package org.gcube.portlets.widgets.ckancontentmoderator.server; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import org.gcube.common.authorization.library.provider.SecurityTokenProvider; import org.gcube.common.scope.api.ScopeProvider; import org.gcube.datacatalogue.utillibrary.gcat.GCatCaller; import org.gcube.datacatalogue.utillibrary.server.DataCatalogueImpl; import org.gcube.datacatalogue.utillibrary.server.cms.CatalogueContentModeratorSystem; import org.gcube.datacatalogue.utillibrary.shared.ItemStatus; import org.gcube.datacatalogue.utillibrary.shared.jackan.model.CkanDataset; import org.gcube.portlets.widgets.ckancontentmoderator.client.CkanContentModeratorService; import org.gcube.portlets.widgets.ckancontentmoderator.client.ContentModeratorWidgetConstants; import org.gcube.portlets.widgets.ckancontentmoderator.server.CkanContentModeratorUtil.QUERY_OPERATOR; import org.gcube.portlets.widgets.ckancontentmoderator.shared.CMSUserRole; import org.gcube.portlets.widgets.ckancontentmoderator.shared.CatalogueDataset; import org.gcube.portlets.widgets.ckancontentmoderator.shared.ModerationUserRole; import org.gcube.portlets.widgets.ckancontentmoderator.shared.OperationReport; import org.gcube.portlets.widgets.ckancontentmoderator.shared.SearchedData; import org.gcube.vomanagement.usermanagement.model.GCubeUser; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import com.google.gwt.user.server.rpc.RemoteServiceServlet; /** * The Class CkanContentModeratorServiceImpl. * * @author Francesco Mangiacrapa at ISTI-CNR francesco.mangiacrapa@isti.cnr.it * * Jan 11, 2022 * @return the boolean */ @SuppressWarnings("serial") public class CkanContentModeratorServiceImpl extends RemoteServiceServlet implements CkanContentModeratorService { private static Logger LOG = LoggerFactory.getLogger(CkanContentModeratorServiceImpl.class); /** * Checks if is moderation enabled. * * @param reloadConfig the reload config * @return the boolean */ @Override public Boolean isModerationEnabled(boolean reloadConfig) { LOG.info("called isContentModeratorEnabled"); String scope = setContexts(); boolean isModerationEnabled = false; try { DataCatalogueImpl catalogueImpl = CatalogueCMSFactory.getFactory().getCatalogueImplPerScope(scope); CatalogueContentModeratorSystem cmsInstance = catalogueImpl.getCatalogueContentModerator(); isModerationEnabled = cmsInstance.isModerationEnabled(reloadConfig); LOG.info("is ModerationEnabled? " + isModerationEnabled); } catch (Exception e) { LOG.error("Error occured on checking isContentModeratorEnabled, so returning false", e); return false; } return isModerationEnabled; } /** * Checks if is moderator role is assigned to working user in the context. * * @return the moderation user role * @throws Exception the exception */ @Override public Boolean isModeratorRoleAssigned() throws Exception { LOG.info("called isModeratorRoleAssigned"); ModerationUserRole userRole = getCMSRolesForUserInTheContext(); boolean isCatalogueModerator = false; if (userRole != null && userRole.getRoles() != null && userRole.getRoles().contains(CMSUserRole.CATALOGUE_MODERATOR)) { LOG.info("called isModeratorRoleAssigned"); isCatalogueModerator = true; } LOG.info("is " + CMSUserRole.CATALOGUE_MODERATOR.getRoleName() + " assigned? " + isCatalogueModerator); return isCatalogueModerator; } /** * Exists my item in moderation. * * @return true if the user has at least one item moderatorated or in * moderation. * @throws Exception the exception */ @Override public Boolean existsMyItemInModeration() throws Exception { LOG.info("called existsMyItemInModeration"); try { /* * if(!GcubeContextUtil.isWithinPortal()) { * LOG.warn("#### DEV MODE ENABLED #######"); boolean defaultValue = true; * LOG.info("DEV MODE: existsMyItemInModeration returning: " + defaultValue); * return defaultValue; } */ String scope = setContexts(); DataCatalogueImpl catalogueImpl = CatalogueCMSFactory.getFactory().getCatalogueImplPerScope(scope); GCubeUser user = GcubeContextUtil.getCurrentUser(getThreadLocalRequest()); String valueOfQueryEmails = CkanContentModeratorUtil.userSOLRQueryForEmails(user, QUERY_OPERATOR.OR); StringBuilder emailsValuesBuilder = new StringBuilder(); // QUERYING AUTHOR_MAIL. IT MUST BE IN LIST OF EMAILs emailsValuesBuilder .append(ContentModeratorWidgetConstants.CKAN_FIELD_NAME_AUTHOR_MAIL + ":" + valueOfQueryEmails); Map params = new HashMap(1); params.put("q", emailsValuesBuilder.toString()); // QUERYING THE ITEM HAVING ANY MODERATIONS STATUS params.put(GCatCaller.MODERATOR_ITEM_STATUS_PARAMETER, GCatCaller.MODERATOR_ITEM_ANY_STATUS_VALUE); List listItems = catalogueImpl.performQueryForItems(params); LOG.debug("existsMyItemInModeration query returned items: " + listItems); boolean haveItemInModeration = false; if (listItems != null && listItems.size() > 0) { haveItemInModeration = true; } LOG.info("existsMyItemInModeration returning: " + haveItemInModeration); return haveItemInModeration; } catch (Exception e) { LOG.warn("Error occurred on checking existsMyItemInModeration, so returning false"); return false; } } /** * Gets the CMS roles for user in the context. * * @return the CMS roles for user in the context * @throws Exception the exception */ @Override public ModerationUserRole getCMSRolesForUserInTheContext() throws Exception { LOG.info("called getCMSRolesForUserInTheContext"); setContexts(); List roles = GcubeContextUtil.getCMSRoleForUserInTheScope(getThreadLocalRequest()); GcubeContextUtil.getCurrentUser(getThreadLocalRequest()); ModerationUserRole userRole = new ModerationUserRole(getServletInfo(), roles); LOG.info("return: " + userRole); return userRole; } /** * Sets the contexts. * * @return the scope operating in */ private String setContexts() { String scope = GcubeContextUtil.getCurrentScope(this.getThreadLocalRequest()); ScopeProvider.instance.set(scope); GCubeUser user = GcubeContextUtil.getCurrentUser(this.getThreadLocalRequest()); if (user != null) { String token = GcubeContextUtil.getCurrentToken(scope, user.getUsername()); SecurityTokenProvider.instance.set(token); } return scope; } /** * Gets the list items for status. * * @param theStatus the the status * @param offset the offset * @param limit the limit * @param allFields the all fields. If true returns the all * fields of an item * @param restrictedToLoggedInUser if true restricts the list of items to logged * in user * @param sortForField the sort for field * @return the list items for status * @throws Exception the exception */ @Override public List getListItemsForStatus(ItemStatus theStatus, int offset, int limit, boolean allFields, boolean restrictedToLoggedInUser, String sortForField) throws Exception { LOG.info("called getListItemsForStatus with [status: " + theStatus + ", offset: " + offset + ", limit: " + limit + ", restrictedToLoggedInUser: " + restrictedToLoggedInUser + "]"); List datasetList = null; try { String scope = setContexts(); DataCatalogueImpl catalogueImpl = CatalogueCMSFactory.getFactory().getCatalogueImplPerScope(scope); CatalogueContentModeratorSystem cmsInstance = catalogueImpl.getCatalogueContentModerator(); Map filters = null; if (restrictedToLoggedInUser) { filters = new HashMap(1); GCubeUser user = GcubeContextUtil.getCurrentUser(getThreadLocalRequest()); String valueOfQueryEmails = CkanContentModeratorUtil.userSOLRQueryForEmails(user, QUERY_OPERATOR.OR); filters.put(ContentModeratorWidgetConstants.CKAN_FIELD_NAME_AUTHOR_MAIL, valueOfQueryEmails); } List datasets = cmsInstance.getListItemsForStatus(theStatus, limit, offset, allFields, filters, sortForField); if (datasets != null) { int size = datasets.size(); datasetList = new ArrayList(size); LOG.info("datasetList for input parameters returned by CMS has size: " + size); for (CkanDataset ckanDataset : datasets) { CatalogueDataset ds = toPatchedCatalogueDataset(ckanDataset, catalogueImpl.getCatalogueUrl()); LOG.trace("converted dataset is: " + ds); datasetList.add(ds); } } } catch (Exception e) { LOG.error("Error occurred on reading items for status: " + theStatus, e); throw e; } LOG.info("returning " + datasetList.size() + " dataset"); return datasetList; } private CatalogueDataset toPatchedCatalogueDataset(CkanDataset ckanDataset, String catalogueURL) { if (ckanDataset == null) return null; CatalogueDataset ds = CatalogueBeansConverter.toCatalogueDataset.apply(ckanDataset); String datasetURL = String.format("%s/dataset/%s", catalogueURL, ds.getName()); ds.setUrl(datasetURL); return ds; } /** * Gets the item for name. * * @param itemName the item name * @return the item for name * @throws Exception the exception */ @Override public CatalogueDataset getItemForName(String itemName) throws Exception { LOG.info("called getItemForName for: " + itemName); CatalogueDataset ds = null; try { String scope = setContexts(); DataCatalogueImpl catalogueImpl = CatalogueCMSFactory.getFactory().getCatalogueImplPerScope(scope); GCubeUser user = GcubeContextUtil.getCurrentUser(this.getThreadLocalRequest()); boolean moderationCheckPassed = false; if (user != null) { CkanDataset ckanDataset = catalogueImpl.getDataset(itemName, user.getUsername()); ds = null; if (ckanDataset != null) { ds = toPatchedCatalogueDataset(ckanDataset, catalogueImpl.getCatalogueUrl()); Boolean userModerator = isModeratorRoleAssigned(); moderationCheckPassed = userModerator ? true : false; LOG.info("Moderation check: is the user a Moderator? " + moderationCheckPassed); // The user is not a Moderator, yes otherwise if (!moderationCheckPassed) { String datasetAuthorMail = ds.getAuthorEmail(); String userMail = user.getEmail(); if (datasetAuthorMail != null && userMail != null && datasetAuthorMail.compareTo(userMail) == 0) { // The user is the owner of the dataset, so he/she can view the dataset // (moderation check passed) moderationCheckPassed = true; } LOG.info("Moderation check: is the user the owner of the dataset? " + moderationCheckPassed); } } } if (!moderationCheckPassed) { LOG.info("Moderation ckeck not passed, returning null"); ds = null; } } catch (Exception e) { LOG.error("Error occurred on reading item for name: " + itemName, e); throw e; } LOG.info("getItemForName " + itemName + ", returning: " + ds); return ds; } /** * Sets the status. * * @param theStatus the the status * @param itemNames the item names * @param message the message to send to Moderators. If null no messange is sent * @return the operation report * @throws Exception the exception */ @Override public OperationReport setStatus(ItemStatus theStatus, List itemNames, String message) throws Exception { LOG.info("Called set status " + theStatus + " for items with name: " + itemNames + ". Is message null?: "+(message==null)); try { String scope = setContexts(); DataCatalogueImpl catalogueImpl = CatalogueCMSFactory.getFactory().getCatalogueImplPerScope(scope); CatalogueContentModeratorSystem cmsInstance = catalogueImpl.getCatalogueContentModerator(); Map errorMapItems = new HashMap(); List changedStatusListItems = new ArrayList(); for (String itemName : itemNames) { try { catalogueImpl.refreshDataset(itemName); LOG.info("refresh dataset done"); changedStatusListItems.add(itemName); if(message!=null && !message.isEmpty()) { LOG.debug("Sending message: "+message); cmsInstance.messageItem(itemName, message); } } catch (Exception e) { LOG.warn("Error when setting status (updating) the itemName: " + itemName, e); errorMapItems.put(itemName, e.getMessage()); } } return new OperationReport(theStatus.getLabel(), changedStatusListItems, errorMapItems); } catch (Exception e) { LOG.error(e.getMessage(), e); throw new Exception("Error occurred on updating the status for item/s: " + itemNames + ". Caused by: " + e.getMessage()); } } /** * Approve item. * * @param itemNames the item names * @param moderatorMessage the moderator message * @return the operation report * @throws Exception the exception */ @Override public OperationReport approveItem(List itemNames, String moderatorMessage) throws Exception { LOG.info("Called approveItem with name/s: " + itemNames); try { String scope = setContexts(); DataCatalogueImpl catalogueImpl = CatalogueCMSFactory.getFactory().getCatalogueImplPerScope(scope); CatalogueContentModeratorSystem cmsInstance = catalogueImpl.getCatalogueContentModerator(); Map errorMapItems = new HashMap(); List approvedListItems = new ArrayList(); for (String itemName : itemNames) { try { cmsInstance.approveItem(itemName, moderatorMessage); approvedListItems.add(itemName); } catch (Exception e) { LOG.warn("Error when approving itemName: " + itemName, e); errorMapItems.put(itemName, e.getMessage()); } } return new OperationReport(ItemStatus.APPROVED.getLabel(), approvedListItems, errorMapItems); } catch (Exception e) { LOG.error(e.getMessage(), e); throw new Exception("Error occurred on approving item/s: " + itemNames + ". Caused by: " + e.getMessage()); } } /** * Reject item. * * @param itemNames the item names * @param permanentlyDelete the permanently delete * @param reasonMsg the reason msg * @return the operation report * @throws Exception the exception */ @Override public OperationReport rejectItem(List itemNames, boolean permanentlyDelete, String reasonMsg) throws Exception { LOG.info("Called rejectItem with name/s: " + itemNames + ", permanentlyDelete: " + permanentlyDelete); try { String scope = setContexts(); DataCatalogueImpl catalogueImpl = CatalogueCMSFactory.getFactory().getCatalogueImplPerScope(scope); CatalogueContentModeratorSystem cmsInstance = catalogueImpl.getCatalogueContentModerator(); Map errorMapItems = new HashMap(); List passedListItems = new ArrayList(); for (String itemName : itemNames) { try { cmsInstance.rejectItem(itemName, permanentlyDelete, reasonMsg); passedListItems.add(itemName); } catch (Exception e) { LOG.warn("Error when rejecting itemName: " + itemName, e); errorMapItems.put(itemName, e.getMessage()); } } return new OperationReport(ItemStatus.REJECTED.getLabel(), passedListItems, errorMapItems); } catch (Exception e) { LOG.error(e.getMessage(), e); throw new Exception("Error occurred on rejecting item/s: " + itemNames + ". Caused by: " + e.getMessage()); } } /** * Permanently delete. * * @param itemNames the item names * @return the operation report * @throws Exception the exception */ @Override public OperationReport permanentlyDelete(List itemNames) throws Exception { LOG.info("Called permanently delete Items with name/s: " + itemNames); try { String scope = setContexts(); DataCatalogueImpl catalogueImpl = CatalogueCMSFactory.getFactory().getCatalogueImplPerScope(scope); CatalogueContentModeratorSystem cmsInstance = catalogueImpl.getCatalogueContentModerator(); Map errorMapItems = new HashMap(); List passedListItems = new ArrayList(); for (String itemName : itemNames) { try { cmsInstance.permanentlyDelete(itemName); passedListItems.add(itemName); } catch (Exception e) { LOG.warn("Error when deleting permanently the itemName: " + itemName, e); errorMapItems.put(itemName, e.getMessage()); } } return new OperationReport("Permanently Delete", passedListItems, errorMapItems); } catch (Exception e) { LOG.error(e.getMessage(), e); throw new Exception( "Error occurred on permanently delete item/s: " + itemNames + ". Caused by: " + e.getMessage()); } } /** * Gets the data for status. * * @param status the status * @param offset the offset * @param limit the limit * @param serverStartIndex the server start index * @param restrictedToLoggedInUser the restricted to logged in user * @param sortForField the sort for field * @return the data for status * @throws Exception the exception */ @Override public SearchedData getDataForStatus(ItemStatus status, int offset, int limit, int serverStartIndex, boolean restrictedToLoggedInUser, String sortForField) throws Exception { LOG.info("called getDataForStatus [status: " + status + ", offset: " + offset + ", limit: " + limit + ", serverIndex: " + serverStartIndex); String scope = setContexts(); int searchStartIndex = offset; CatalogueContentModeratorSystem cmsInstance = CatalogueCMSFactory.getFactory().getCMSPerScope(scope); SearchedData searchedData = new SearchedData(offset, limit, searchStartIndex, false); Map filters = null; if (restrictedToLoggedInUser) { filters = new HashMap(1); GCubeUser user = GcubeContextUtil.getCurrentUser(getThreadLocalRequest()); String valueOfQueryEmails = CkanContentModeratorUtil.userSOLRQueryForEmails(user, QUERY_OPERATOR.OR); filters.put(ContentModeratorWidgetConstants.CKAN_FIELD_NAME_AUTHOR_MAIL, valueOfQueryEmails); } long totalItemsForStatus = cmsInstance.countListItemsForStatus(status, filters); LOG.info("totalItemsForStatus " + status + " are : " + totalItemsForStatus); List listDataset = new ArrayList(); try { LOG.debug("getListItemsForStatus with searchStartIndex: " + searchStartIndex + ", limit: " + limit); listDataset = getListItemsForStatus(status, searchStartIndex, limit, true, restrictedToLoggedInUser, sortForField); } catch (Exception e) { String error = "Error occurred on getting items for status: " + status; LOG.error(error, e); throw new Exception(error + ". Cause: " + e.getMessage()); } int listDatasetSize = listDataset.size(); LOG.debug("Returned " + listDatasetSize + " with above parameters"); searchedData.setData(listDataset); searchedData.setTotalItems(totalItemsForStatus); if (listDatasetSize == limit || listDatasetSize == 0) { LOG.debug("Page completed returning " + listDatasetSize + " items"); int newOffset = searchStartIndex + offset; searchedData.setServerSearchFinished(newOffset > totalItemsForStatus || listDatasetSize == 0); LOG.debug("is Search finished: " + searchedData.isServerSearchFinished()); return searchedData; } LOG.debug("Returning: " + searchedData); return searchedData; } }