use of org.apache.ofbiz.security.Security in project ofbiz-framework by apache.
the class ContentManagementEvents method updatePublishLinks.
public static String updatePublishLinks(HttpServletRequest request, HttpServletResponse response) {
HttpSession session = request.getSession();
Security security = (Security) request.getAttribute("security");
GenericValue userLogin = (GenericValue) session.getAttribute("userLogin");
String webSiteId = WebSiteWorker.getWebSiteId(request);
Delegator delegator = (Delegator) request.getAttribute("delegator");
LocalDispatcher dispatcher = (LocalDispatcher) request.getAttribute("dispatcher");
Map<String, Object> paramMap = UtilHttp.getParameterMap(request);
// The content to be linked to one or more sites
String targContentId = (String) paramMap.get("contentId");
String roles = null;
String authorId = null;
GenericValue authorContent = ContentManagementWorker.getAuthorContent(delegator, targContentId);
if (authorContent != null) {
authorId = authorContent.getString("contentId");
} else {
request.setAttribute("_ERROR_MESSAGE_", "authorContent is empty.");
return "error";
}
// Determine if user is owner of target content
String userLoginId = userLogin.getString("userLoginId");
List<String> roleTypeList = null;
if (authorId != null && userLoginId != null && authorId.equals(userLoginId)) {
roles = "OWNER";
roleTypeList = StringUtil.split(roles, "|");
}
List<String> targetOperationList = UtilMisc.<String>toList("CONTENT_PUBLISH");
// TODO check the purpose of this list and if we want to make use of it. Else remove
// UtilMisc.toList("ARTICLE");
List<String> contentPurposeList = null;
// The content to be linked to one or more sites
String permittedAction = (String) paramMap.get("permittedAction");
// The content to be linked to one or more sites
String permittedOperations = (String) paramMap.get("permittedOperations");
if (UtilValidate.isEmpty(targContentId)) {
request.setAttribute("_ERROR_MESSAGE_", "targContentId is empty.");
return "error";
}
// Get all the subSites that the user is permitted to link to
List<Object[]> origPublishedLinkList = null;
try {
// TODO: this needs to be given author userLogin
EntityQuery.use(delegator).from("UserLogin").where("userLoginId", authorId).cache().queryOne();
origPublishedLinkList = ContentManagementWorker.getPublishedLinks(delegator, targContentId, webSiteId, userLogin, security, permittedAction, permittedOperations, roles);
} catch (GenericEntityException e) {
request.setAttribute("_ERROR_MESSAGE_", e.getMessage());
return "error";
} catch (GeneralException e2) {
request.setAttribute("_ERROR_MESSAGE_", e2.getMessage());
return "error";
}
// make a map of the values that are passed in using the top subSite as the key.
// Content can only be linked to one subsite under a top site (ends with "_MASTER")
Map<String, String> siteIdLookup = new HashMap<String, String>();
for (Entry<String, Object> entry : paramMap.entrySet()) {
String param = entry.getKey();
int pos = param.indexOf("select_");
if (pos >= 0) {
String siteId = param.substring(7);
String subSiteVal = (String) paramMap.get(param);
siteIdLookup.put(siteId, subSiteVal);
}
}
// Loop thru all the possible subsites
Timestamp nowTimestamp = UtilDateTime.nowTimestamp();
boolean statusIdUpdated = false;
Map<String, Object> result = new HashMap<>();
for (Object[] arr : origPublishedLinkList) {
// main (2nd level) site id
String contentId = (String) arr[0];
String origSubContentId = null;
List<Object[]> origSubList = UtilGenerics.checkList(arr[1]);
Timestamp origFromDate = null;
for (Object[] pubArr : origSubList) {
Timestamp fromDate = (Timestamp) pubArr[2];
origSubContentId = null;
if (fromDate != null) {
origSubContentId = (String) pubArr[0];
origFromDate = fromDate;
break;
}
}
String currentSubContentId = siteIdLookup.get(contentId);
try {
if (UtilValidate.isNotEmpty(currentSubContentId)) {
if (!currentSubContentId.equals(origSubContentId)) {
// disable existing link
if (UtilValidate.isNotEmpty(origSubContentId) && origFromDate != null) {
List<GenericValue> oldActiveValues = EntityQuery.use(delegator).from("ContentAssoc").where("contentId", targContentId, "contentIdTo", origSubContentId, "contentAssocTypeId", "PUBLISH_LINK", "thruDate", null).queryList();
for (GenericValue cAssoc : oldActiveValues) {
cAssoc.set("thruDate", nowTimestamp);
cAssoc.store();
}
}
// create new link
Map<String, Object> serviceIn = new HashMap<String, Object>();
serviceIn.put("userLogin", userLogin);
serviceIn.put("contentId", targContentId);
serviceIn.put("contentAssocTypeId", "PUBLISH_LINK");
serviceIn.put("fromDate", nowTimestamp);
serviceIn.put("contentIdTo", currentSubContentId);
serviceIn.put("roleTypeList", roleTypeList);
serviceIn.put("targetOperationList", targetOperationList);
// TODO check if this should be removed (see above)
serviceIn.put("contentPurposeList", contentPurposeList);
result = dispatcher.runSync("createContentAssoc", serviceIn);
if (ServiceUtil.isError(result)) {
String errorMessage = ServiceUtil.getErrorMessage(result);
request.setAttribute("_ERROR_MESSAGE_", errorMessage);
Debug.logError(errorMessage, module);
return "error";
}
serviceIn = new HashMap<String, Object>();
serviceIn.put("userLogin", userLogin);
serviceIn.put("contentId", targContentId);
serviceIn.put("contentAssocTypeId", "PUBLISH_LINK");
serviceIn.put("fromDate", nowTimestamp);
serviceIn.put("contentIdTo", contentId);
serviceIn.put("roleTypeList", roleTypeList);
serviceIn.put("targetOperationList", targetOperationList);
// TODO check if this should be removed (see above)
serviceIn.put("contentPurposeList", contentPurposeList);
result = dispatcher.runSync("createContentAssoc", serviceIn);
if (ServiceUtil.isError(result)) {
String errorMessage = ServiceUtil.getErrorMessage(result);
request.setAttribute("_ERROR_MESSAGE_", errorMessage);
Debug.logError(errorMessage, module);
return "error";
}
if (!statusIdUpdated) {
try {
GenericValue targContent = EntityQuery.use(delegator).from("Content").where("contentId", targContentId).queryOne();
targContent.set("statusId", "CTNT_PUBLISHED");
targContent.store();
statusIdUpdated = true;
} catch (GenericEntityException e) {
Debug.logError(e.getMessage(), module);
request.setAttribute("_ERROR_MESSAGE_", e.getMessage());
return "error";
}
}
}
} else if (UtilValidate.isNotEmpty(origSubContentId)) {
// if no current link is passed in, look to see if there is an existing link(s) that must be disabled
List<GenericValue> oldActiveValues = EntityQuery.use(delegator).from("ContentAssoc").where("contentId", targContentId, "contentIdTo", origSubContentId, "contentAssocTypeId", "PUBLISH_LINK", "thruDate", null).queryList();
for (GenericValue cAssoc : oldActiveValues) {
cAssoc.set("thruDate", nowTimestamp);
cAssoc.store();
}
}
} catch (GenericEntityException e) {
Debug.logError(e.getMessage(), module);
request.setAttribute("_ERROR_MESSAGE_", e.getMessage());
return "error";
} catch (GenericServiceException e2) {
Debug.logError(e2, module);
request.setAttribute("_ERROR_MESSAGE_", e2.getMessage());
return "error";
}
}
return "success";
}
use of org.apache.ofbiz.security.Security in project ofbiz-framework by apache.
the class ContentManagementEvents method updateStaticValues.
public static String updateStaticValues(HttpServletRequest request, HttpServletResponse response) {
HttpSession session = request.getSession();
Security security = (Security) request.getAttribute("security");
GenericValue userLogin = (GenericValue) session.getAttribute("userLogin");
String webSiteId = WebSiteWorker.getWebSiteId(request);
Delegator delegator = (Delegator) request.getAttribute("delegator");
LocalDispatcher dispatcher = (LocalDispatcher) request.getAttribute("dispatcher");
Map<String, Object> paramMap = UtilHttp.getParameterMap(request);
Map<String, Object> result = new HashMap<>();
String parentPlaceholderId = (String) paramMap.get("ph");
if (UtilValidate.isEmpty(parentPlaceholderId)) {
request.setAttribute("_ERROR_MESSAGE_", "ParentPlaceholder is empty.");
return "error";
}
List<GenericValue> allPublishPointList = null;
List<String[]> permittedPublishPointList = null;
List<Map<String, Object>> valueList = null;
try {
allPublishPointList = ContentManagementWorker.getAllPublishPoints(delegator, webSiteId);
permittedPublishPointList = ContentManagementWorker.getPermittedPublishPoints(delegator, allPublishPointList, userLogin, security, "_ADMIN", null, null);
valueList = ContentManagementWorker.getStaticValues(delegator, parentPlaceholderId, permittedPublishPointList);
} catch (GeneralException e) {
Debug.logError(e.getMessage(), module);
request.setAttribute("_ERROR_MESSAGE_", e.getMessage());
return "error";
}
int counter = 0;
for (Map<String, Object> map : valueList) {
String contentId = (String) map.get("contentId");
for (String[] pubArr : permittedPublishPointList) {
String pubContentId = pubArr[0];
String pubValue = (String) map.get(pubContentId);
String paramName = Integer.toString(counter) + "_" + pubContentId;
String paramValue = (String) paramMap.get(paramName);
Map<String, Object> serviceIn = new HashMap<String, Object>();
serviceIn.put("userLogin", userLogin);
serviceIn.put("contentIdTo", contentId);
serviceIn.put("contentId", pubContentId);
serviceIn.put("contentAssocTypeId", "SUBSITE");
try {
if (UtilValidate.isNotEmpty(paramValue)) {
if (!paramValue.equals(pubValue)) {
if ("Y".equalsIgnoreCase(paramValue)) {
serviceIn.put("fromDate", UtilDateTime.nowTimestamp());
result = dispatcher.runSync("createContentAssoc", serviceIn);
if (ServiceUtil.isError(result)) {
String errorMessage = ServiceUtil.getErrorMessage(result);
request.setAttribute("_ERROR_MESSAGE_", errorMessage);
Debug.logError(errorMessage, module);
return "error";
}
} else if ("N".equalsIgnoreCase(paramValue) && "Y".equalsIgnoreCase(pubValue)) {
serviceIn.put("thruDate", UtilDateTime.nowTimestamp());
Timestamp fromDate = (Timestamp) map.get(pubContentId + "FromDate");
serviceIn.put("fromDate", fromDate);
result = dispatcher.runSync("updateContentAssoc", serviceIn);
if (ServiceUtil.isError(result)) {
String errorMessage = ServiceUtil.getErrorMessage(result);
request.setAttribute("_ERROR_MESSAGE_", errorMessage);
Debug.logError(errorMessage, module);
return "error";
}
}
}
} else if (UtilValidate.isNotEmpty(pubValue)) {
if ("Y".equalsIgnoreCase(pubValue)) {
serviceIn.put("thruDate", UtilDateTime.nowTimestamp());
Timestamp fromDate = (Timestamp) map.get(pubContentId + "FromDate");
serviceIn.put("fromDate", fromDate);
result = dispatcher.runSync("updateContentAssoc", serviceIn);
if (ServiceUtil.isError(result)) {
String errorMessage = ServiceUtil.getErrorMessage(result);
request.setAttribute("_ERROR_MESSAGE_", errorMessage);
Debug.logError(errorMessage, module);
return "error";
}
}
}
} catch (GenericServiceException e) {
Debug.logError(e.getMessage(), module);
request.setAttribute("_ERROR_MESSAGE_", e.getMessage());
return "error";
}
}
counter++;
}
return "success";
}
use of org.apache.ofbiz.security.Security in project ofbiz-framework by apache.
the class ContentEvents method updateAllContentKeywords.
/**
* Updates/adds keywords for all contents
*
* @param request HTTPRequest object for the current request
* @param response HTTPResponse object for the current request
* @return String specifying the exit status of this event
*/
public static String updateAllContentKeywords(HttpServletRequest request, HttpServletResponse response) {
Delegator delegator = (Delegator) request.getAttribute("delegator");
Security security = (Security) request.getAttribute("security");
String updateMode = "CREATE";
String errMsg = null;
String doAll = request.getParameter("doAll");
// check permissions before moving on...
if (!security.hasEntityPermission("CONTENTMGR", "_" + updateMode, request.getSession())) {
Map<String, String> messageMap = UtilMisc.toMap("updateMode", updateMode);
errMsg = UtilProperties.getMessage(resource, "contentevents.not_sufficient_permissions", messageMap, UtilHttp.getLocale(request));
request.setAttribute("_ERROR_MESSAGE_", errMsg);
return "error";
}
int numConts = 0;
int errConts = 0;
boolean beganTx = false;
EntityQuery contentQuery = EntityQuery.use(delegator).from("Content");
try {
// begin the transaction
beganTx = TransactionUtil.begin(7200);
if (Debug.infoOn()) {
long count = contentQuery.queryCount();
Debug.logInfo("========== Found " + count + " contents to index ==========", module);
}
GenericValue content;
try (EntityListIterator entityListIterator = contentQuery.queryIterator()) {
while ((content = entityListIterator.next()) != null) {
ContentKeywordIndex.indexKeywords(content, "Y".equals(doAll));
numConts++;
if (numConts % 500 == 0) {
Debug.logInfo("Keywords indexed for " + numConts + " so far", module);
}
}
} catch (GenericEntityException e) {
errMsg = "[ContentEvents.updateAllContentKeywords] Could not create content-keyword (write error); message: " + e.getMessage();
Debug.logWarning(errMsg, module);
errConts++;
request.setAttribute("_ERROR_MESSAGE_", errMsg);
}
} catch (GenericEntityException gee) {
Debug.logWarning(gee, gee.getMessage(), module);
Map<String, String> messageMap = UtilMisc.toMap("gee", gee.toString());
errMsg = UtilProperties.getMessage(resource, "contentevents.error_getting_content_list", messageMap, UtilHttp.getLocale(request));
request.setAttribute("_ERROR_MESSAGE_", errMsg);
try {
TransactionUtil.rollback(beganTx, gee.getMessage(), gee);
} catch (GenericTransactionException e1) {
Debug.logError(e1, module);
}
return "error";
}
// commit the transaction
try {
TransactionUtil.commit(beganTx);
} catch (GenericTransactionException e) {
Debug.logError(e, module);
}
if (errConts == 0) {
Map<String, String> messageMap = UtilMisc.toMap("numConts", Integer.toString(numConts));
errMsg = UtilProperties.getMessage(resource, "contentevents.keyword_creation_complete_for_contents", messageMap, UtilHttp.getLocale(request));
request.setAttribute("_EVENT_MESSAGE_", errMsg);
return "success";
} else {
Map<String, String> messageMap = UtilMisc.toMap("numConts", Integer.toString(numConts));
messageMap.put("errConts", Integer.toString(errConts));
errMsg = UtilProperties.getMessage(resource, "contentevents.keyword_creation_complete_for_contents_with_errors", messageMap, UtilHttp.getLocale(request));
request.setAttribute("_ERROR_MESSAGE_", errMsg);
return "error";
}
}
use of org.apache.ofbiz.security.Security in project ofbiz-framework by apache.
the class ContentPermissionServices method checkContentPermission.
/**
* checkContentPermission
*
*@param dctx The DispatchContext that this service is operating in
*@param context Map containing the input parameters
*@return Map with the result of the service, the output parameters
*
* This service goes thru a series of test to determine if the user has
* authority to performed anyone of the passed in target operations.
*
* It expects a Content entity in "currentContent"
* It expects a list of contentOperationIds in "targetOperationList" rather
* than a scalar because it is thought that sometimes more than one operation
* would fit the situation.
* Similarly, it expects a list of contentPurposeTypeIds in "contentPurposeList".
* Again, normally there will just be one, but it is possible that a Content
* entity could have multiple purposes associated with it.
* The userLogin GenericValue is also required.
* A list of roleTypeIds is also possible.
*
* The basic sequence of testing events is:
* First the ContentPurposeOperation table is checked to see if there are any
* entries with matching purposes (and operations) with no roleTypeId (ie. _NA_).
* This is done because it would be the most common scenario and is quick to check.
*
* Secondly, the CONTENTMGR permission is checked.
*
* Thirdly, the ContentPurposeOperation table is rechecked to see if there are
* any conditions with roleTypeIds that match associated ContentRoles tied to the
* user.
* If a Party of "PARTY_GROUP" type is found, the PartyRelationship table is checked
* to see if the current user is linked to that group.
*
* If no match is found to this point and the current Content entity has a value for
* ownerContentId, then the last step is recusively applied, using the ContentRoles
* associated with the ownerContent entity.
*/
public static Map<String, Object> checkContentPermission(DispatchContext dctx, Map<String, ? extends Object> context) {
Debug.logWarning(new Exception(), "This service has been depricated in favor of [genericContentPermission]", module);
Security security = dctx.getSecurity();
Delegator delegator = dctx.getDelegator();
// TODO this parameters is still not used but this service need to be replaced by genericContentPermission
// String statusId = (String) context.get("statusId");
// TODO this parameters is still not used but this service need to be replaced by genericContentPermission
// String privilegeEnumId = (String) context.get("privilegeEnumId");
GenericValue content = (GenericValue) context.get("currentContent");
Boolean bDisplayFailCond = (Boolean) context.get("displayFailCond");
boolean displayFailCond = false;
if (bDisplayFailCond != null && bDisplayFailCond.booleanValue()) {
displayFailCond = true;
}
Debug.logInfo("displayFailCond(0):" + displayFailCond, "");
Boolean bDisplayPassCond = (Boolean) context.get("displayPassCond");
boolean displayPassCond = false;
if (bDisplayPassCond != null && bDisplayPassCond.booleanValue()) {
displayPassCond = true;
}
Debug.logInfo("displayPassCond(0):" + displayPassCond, "");
Map<String, Object> results = new HashMap<String, Object>();
GenericValue userLogin = (GenericValue) context.get("userLogin");
String partyId = (String) context.get("partyId");
if (UtilValidate.isEmpty(partyId)) {
String passedUserLoginId = (String) context.get("userLoginId");
if (UtilValidate.isNotEmpty(passedUserLoginId)) {
try {
userLogin = EntityQuery.use(delegator).from("UserLogin").where("userLoginId", passedUserLoginId).cache().queryOne();
if (userLogin != null) {
partyId = userLogin.getString("partyId");
}
} catch (GenericEntityException e) {
return ServiceUtil.returnError(e.getMessage());
}
}
}
if (UtilValidate.isEmpty(partyId) && userLogin != null) {
partyId = userLogin.getString("partyId");
}
// Do entity permission check. This will pass users with administrative permissions.
boolean passed = false;
// I realized, belatedly, that I wanted to be able to pass parameters in as
// strings so this service could be used in an action event directly,
// so I had to write this code to handle both list and strings
List<String> passedPurposes = UtilGenerics.checkList(context.get("contentPurposeList"));
String contentPurposeString = (String) context.get("contentPurposeString");
if (UtilValidate.isNotEmpty(contentPurposeString)) {
List<String> purposesFromString = StringUtil.split(contentPurposeString, "|");
if (passedPurposes == null) {
passedPurposes = new LinkedList<String>();
}
passedPurposes.addAll(purposesFromString);
}
EntityPermissionChecker.StdAuxiliaryValueGetter auxGetter = new EntityPermissionChecker.StdAuxiliaryValueGetter("ContentPurpose", "contentPurposeTypeId", "contentId");
// Sometimes permissions need to be checked before an entity is created, so
// there needs to be a method for setting a purpose list
auxGetter.setList(passedPurposes);
List<String> targetOperations = UtilGenerics.checkList(context.get("targetOperationList"));
String targetOperationString = (String) context.get("targetOperationString");
if (UtilValidate.isNotEmpty(targetOperationString)) {
List<String> operationsFromString = StringUtil.split(targetOperationString, "|");
if (targetOperations == null) {
targetOperations = new LinkedList<String>();
}
targetOperations.addAll(operationsFromString);
}
EntityPermissionChecker.StdPermissionConditionGetter permCondGetter = new EntityPermissionChecker.StdPermissionConditionGetter("ContentPurposeOperation", "contentOperationId", "roleTypeId", "statusId", "contentPurposeTypeId", "privilegeEnumId");
permCondGetter.setOperationList(targetOperations);
EntityPermissionChecker.StdRelatedRoleGetter roleGetter = new EntityPermissionChecker.StdRelatedRoleGetter("Content", "roleTypeId", "contentId", "partyId", "ownerContentId", "ContentRole");
List<String> passedRoles = UtilGenerics.checkList(context.get("roleTypeList"));
if (passedRoles == null)
passedRoles = new LinkedList<String>();
String roleTypeString = (String) context.get("roleTypeString");
if (UtilValidate.isNotEmpty(roleTypeString)) {
List<String> rolesFromString = StringUtil.split(roleTypeString, "|");
passedRoles.addAll(rolesFromString);
}
roleGetter.setList(passedRoles);
String entityAction = (String) context.get("entityOperation");
if (entityAction == null)
entityAction = "_ADMIN";
if (userLogin != null) {
passed = security.hasEntityPermission("CONTENTMGR", entityAction, userLogin);
}
StringBuilder errBuf = new StringBuilder();
String permissionStatus = null;
List<Object> entityIds = new LinkedList<Object>();
if (passed) {
results.put("permissionStatus", "granted");
permissionStatus = "granted";
if (displayPassCond) {
errBuf.append("\n hasEntityPermission(" + entityAction + "): PASSED");
}
} else {
if (displayFailCond) {
errBuf.append("\n hasEntityPermission(" + entityAction + "): FAILED");
}
if (content != null)
entityIds.add(content);
String quickCheckContentId = (String) context.get("quickCheckContentId");
if (UtilValidate.isNotEmpty(quickCheckContentId)) {
List<String> quickList = StringUtil.split(quickCheckContentId, "|");
if (UtilValidate.isNotEmpty(quickList))
entityIds.addAll(quickList);
}
try {
boolean check = EntityPermissionChecker.checkPermissionMethod(delegator, partyId, "Content", entityIds, auxGetter, roleGetter, permCondGetter);
if (check) {
results.put("permissionStatus", "granted");
} else {
results.put("permissionStatus", "rejected");
}
} catch (GenericEntityException e) {
return ServiceUtil.returnError(e.getMessage());
}
permissionStatus = (String) results.get("permissionStatus");
errBuf.append("\n permissionStatus:");
errBuf.append(permissionStatus);
}
if (("granted".equals(permissionStatus) && displayPassCond) || ("rejected".equals(permissionStatus) && displayFailCond)) {
// Don't show this if passed on 'hasEntityPermission'
if (displayFailCond || displayPassCond) {
if (!passed) {
errBuf.append("\n targetOperations:");
errBuf.append(targetOperations);
String errMsg = permCondGetter.dumpAsText();
errBuf.append("\n");
errBuf.append(errMsg);
errBuf.append("\n partyId:");
errBuf.append(partyId);
errBuf.append("\n entityIds:");
errBuf.append(entityIds);
errBuf.append("\n auxList:");
errBuf.append(auxGetter.getList());
errBuf.append("\n roleList:");
errBuf.append(roleGetter.getList());
}
}
}
Debug.logInfo("displayPass/FailCond(0), errBuf:" + errBuf.toString(), "");
results.put(ModelService.ERROR_MESSAGE, errBuf.toString());
return results;
}
use of org.apache.ofbiz.security.Security in project ofbiz-framework by apache.
the class PortalPageWorker method userIsAllowedToConfigure.
/**
* Checks if the user is allowed to configure the PortalPage.
* PortalPage configuration is allowed if he is the PortalPage owner or he has got the PORTALPAGE_ADMIN permission
*/
public static Boolean userIsAllowedToConfigure(String portalPageId, Map<String, Object> context) {
Boolean userIsAllowed = false;
if (UtilValidate.isNotEmpty(portalPageId)) {
GenericValue userLogin = (GenericValue) context.get("userLogin");
if (userLogin != null) {
String userLoginId = (String) userLogin.get("userLoginId");
Security security = (Security) context.get("security");
Boolean hasPortalAdminPermission = security.hasPermission("PORTALPAGE_ADMIN", userLogin);
try {
Delegator delegator = WidgetWorker.getDelegator(context);
GenericValue portalPage = EntityQuery.use(delegator).from("PortalPage").where("portalPageId", portalPageId).queryOne();
if (portalPage != null) {
String ownerUserLoginId = (String) portalPage.get("ownerUserLoginId");
// Users with PORTALPAGE_ADMIN permission can configure every Portal Page
userIsAllowed = (ownerUserLoginId.equals(userLoginId) || hasPortalAdminPermission);
}
} catch (GenericEntityException e) {
return false;
}
}
}
return userIsAllowed;
}
Aggregations