diff --git a/pom.xml b/pom.xml index d2354e2..2c17480 100644 --- a/pom.xml +++ b/pom.xml @@ -121,6 +121,11 @@ aslcore provided + + org.gcube.portal + social-networking-library + provided + org.gcube.portal custom-portal-handler diff --git a/src/main/java/org/gcube/portlets/widgets/ckandatapublisherwidget/client/CKanMetadataPublisher.java b/src/main/java/org/gcube/portlets/widgets/ckandatapublisherwidget/client/CKanMetadataPublisher.java index ed59355..9361fab 100644 --- a/src/main/java/org/gcube/portlets/widgets/ckandatapublisherwidget/client/CKanMetadataPublisher.java +++ b/src/main/java/org/gcube/portlets/widgets/ckandatapublisherwidget/client/CKanMetadataPublisher.java @@ -5,19 +5,18 @@ import java.util.List; import org.gcube.portlets.widgets.ckandatapublisherwidget.client.ui.CreateDatasetForm; import org.gcube.portlets.widgets.ckandatapublisherwidget.client.ui.MetaDataFieldSkeleton; -import org.gcube.portlets.widgets.ckandatapublisherwidget.client.ui.TwinColumnSelection.TwinColumnSelectionMainPanel; import org.gcube.portlets.widgets.ckandatapublisherwidget.shared.DataType; import org.gcube.portlets.widgets.ckandatapublisherwidget.shared.MetadataFieldWrapper; -import org.gcube.portlets.widgets.ckandatapublisherwidget.shared.ResourceElementBean; import com.github.gwtbootstrap.client.ui.Button; +import com.github.gwtbootstrap.client.ui.ListBox; import com.google.gwt.core.client.EntryPoint; import com.google.gwt.core.shared.GWT; +import com.google.gwt.dom.client.SelectElement; import com.google.gwt.event.dom.client.ClickEvent; import com.google.gwt.event.dom.client.ClickHandler; import com.google.gwt.event.shared.HandlerManager; import com.google.gwt.user.client.Window; -import com.google.gwt.user.client.rpc.AsyncCallback; import com.google.gwt.user.client.ui.RootPanel; import com.google.gwt.user.client.ui.VerticalPanel; @@ -40,7 +39,35 @@ public class CKanMetadataPublisher implements EntryPoint { //startExample(); //testMetadata(); //testSelectionPanel(); + //testHideOption(); + } + @SuppressWarnings("unused") + private void testHideOption() { + + ListBox listBox = new ListBox(true); + listBox.addItem("A"); + listBox.addItem("B"); + listBox.addItem("C"); + listBox.addItem("D"); + listBox.addItem("E"); + listBox.addItem("F"); + + List toHide = new ArrayList(); + toHide.add("A"); + toHide.add("D"); + + RootPanel.get("ckan-metadata-publisher-div").add(listBox); + SelectElement se = listBox.getElement().cast(); + + // hide + for (int i = 0; i < listBox.getItemCount(); i++) { + if(toHide.contains(listBox.getItemText(i))){ + GWT.log("to hide " + listBox.getItemText(i)); + se.getOptions().getItem(i).getStyle().setProperty("display", "none"); + } + } + } @SuppressWarnings("unused") @@ -73,24 +100,24 @@ public class CKanMetadataPublisher implements EntryPoint { // - String folderId = "e87bfc7d-4fb0-4795-9c79-0c495500ca9c"; - ckanServices.getTreeFolder(folderId, new AsyncCallback() { - - - @Override - public void onSuccess(ResourceElementBean result) { - if(result != null){ - RootPanel.get("ckan-metadata-publisher-div").add(new TwinColumnSelectionMainPanel(result)); - } - } - - @Override - public void onFailure(Throwable caught) { - - Window.alert("Failed to retrieve ResourceElementBean"); - - } - }); + // String folderId = "e87bfc7d-4fb0-4795-9c79-0c495500ca9c"; + // ckanServices.getTreeFolder(folderId, new AsyncCallback() { + // + // + // @Override + // public void onSuccess(ResourceElementBean result) { + // if(result != null){ + // RootPanel.get("ckan-metadata-publisher-div").add(new TwinColumnSelectionMainPanel(result)); + // } + // } + // + // @Override + // public void onFailure(Throwable caught) { + // + // Window.alert("Failed to retrieve ResourceElementBean"); + // + // } + // }); } @SuppressWarnings("unused") diff --git a/src/main/java/org/gcube/portlets/widgets/ckandatapublisherwidget/client/CKanPublisherService.java b/src/main/java/org/gcube/portlets/widgets/ckandatapublisherwidget/client/CKanPublisherService.java index b9b30cb..96fd6be 100644 --- a/src/main/java/org/gcube/portlets/widgets/ckandatapublisherwidget/client/CKanPublisherService.java +++ b/src/main/java/org/gcube/portlets/widgets/ckandatapublisherwidget/client/CKanPublisherService.java @@ -67,17 +67,17 @@ public interface CKanPublisherService extends RemoteService { * @return true if it exists, false otherwise */ boolean datasetIdAlreadyExists(String title); - + /** * Retrieve the list of groups the user can choose to associate this product with. * @return a list of groups' beans */ List getUserGroups(); - /** - * Return a tree object representing the whole folder hierarchy - * @param folderId - * @return ResourceElementBean - */ - ResourceElementBean getTreeFolder(String folderId); + // /** + // * Return a tree object representing the whole folder hierarchy + // * @param folderId + // * @return ResourceElementBean + // */ + // ResourceElementBean getTreeFolder(String folderId); } diff --git a/src/main/java/org/gcube/portlets/widgets/ckandatapublisherwidget/client/CKanPublisherServiceAsync.java b/src/main/java/org/gcube/portlets/widgets/ckandatapublisherwidget/client/CKanPublisherServiceAsync.java index 269999d..9a2c6a9 100644 --- a/src/main/java/org/gcube/portlets/widgets/ckandatapublisherwidget/client/CKanPublisherServiceAsync.java +++ b/src/main/java/org/gcube/portlets/widgets/ckandatapublisherwidget/client/CKanPublisherServiceAsync.java @@ -68,18 +68,17 @@ public interface CKanPublisherServiceAsync { */ void datasetIdAlreadyExists(String title, AsyncCallback callback); - /** - * Return a tree object representing the whole folder hierarchy - * @param folderId - * @return ResourceElementBean - */ - void getTreeFolder(String folderId, - AsyncCallback callback); + // /** + // * Return a tree object representing the whole folder hierarchy + // * @param folderId + // * @return ResourceElementBean + // */ + // void getTreeFolder(String folderId, + // AsyncCallback callback); /** * Retrieve the list of groups the user can choose to associate this product with. * @return a list of groups' beans */ void getUserGroups(AsyncCallback> callback); - } diff --git a/src/main/java/org/gcube/portlets/widgets/ckandatapublisherwidget/client/ui/CreateDatasetForm.java b/src/main/java/org/gcube/portlets/widgets/ckandatapublisherwidget/client/ui/CreateDatasetForm.java index a718216..71adb23 100644 --- a/src/main/java/org/gcube/portlets/widgets/ckandatapublisherwidget/client/ui/CreateDatasetForm.java +++ b/src/main/java/org/gcube/portlets/widgets/ckandatapublisherwidget/client/ui/CreateDatasetForm.java @@ -43,6 +43,7 @@ import com.github.gwtbootstrap.client.ui.constants.AlertType; import com.github.gwtbootstrap.client.ui.constants.ControlGroupType; import com.github.gwtbootstrap.client.ui.resources.Bootstrap.Tabs; import com.google.gwt.core.client.GWT; +import com.google.gwt.dom.client.SelectElement; import com.google.gwt.dom.client.Style.Unit; import com.google.gwt.event.dom.client.ChangeEvent; import com.google.gwt.event.dom.client.ChangeHandler; @@ -165,8 +166,8 @@ public class CreateDatasetForm extends Composite{ // error/info messages protected static final String ERROR_PRODUCT_CREATION = "There was an error while trying to publish your product, sorry.. Retry later"; - protected static final String PRODUCT_CREATED_OK = "Product correctly created!"; - private static final String TRYING_TO_CREATE_PRODUCT = "Trying to create product, please wait"; + protected static final String PRODUCT_CREATED_OK = "Product correctly published!"; + private static final String TRYING_TO_CREATE_PRODUCT = "Trying to publish the product, please wait"; // tab panel private TabPanel tabPanel; @@ -209,9 +210,7 @@ public class CreateDatasetForm extends Composite{ * @param eventBus the event bus */ public CreateDatasetForm(HandlerManager eventBus) { - createDatasetFormBody(false, null, eventBus); - } /** @@ -220,40 +219,30 @@ public class CreateDatasetForm extends Composite{ * @param eventBus the event bus */ public CreateDatasetForm(String idFolderWorkspace, HandlerManager eventBus) { - createDatasetFormBody(true, idFolderWorkspace, eventBus); - } /** * Bind on events */ private void bind() { - // when a custom field is removed, remove it from the list eventBus.addHandler(DeleteCustomFieldEvent.TYPE, new DeleteCustomFieldEventHandler() { @Override public void onRemoveEntry(DeleteCustomFieldEvent event) { - customFieldEntriesList.remove(event.getRemovedEntry()); customFields.remove(event.getRemovedEntry()); - } }); // on close form eventBus.addHandler(CloseCreationFormEvent.TYPE, new CloseCreationFormEventHandler() { - @Override public void onClose(CloseCreationFormEvent event) { - InfoIconsLabels.closeDialogBox(popupOpenedIds); - } - }); - } /** @@ -284,7 +273,7 @@ public class CreateDatasetForm extends Composite{ @Override public void onFailure(Throwable caught) { - setAlertBlock("Error while retrieving information, try to refresh the page", AlertType.ERROR, true); + setAlertBlock("Error while retrieving information, try to refresh the page and retry", AlertType.ERROR, true); } @@ -293,7 +282,7 @@ public class CreateDatasetForm extends Composite{ if(bean == null){ - setAlertBlock("Error while retrieving information, try to refresh the page", AlertType.ERROR, true); + setAlertBlock("Error while retrieving information, try to refresh the page and retry", AlertType.ERROR, true); } else{ @@ -381,14 +370,14 @@ public class CreateDatasetForm extends Composite{ } @Override - public void onSuccess(List result) { + public void onSuccess(final List profiles) { - if(result == null){ + if(profiles == null){ setAlertBlock("Error while retrieving profiles, try later", AlertType.ERROR, true); } else{ - receivedBean.setMetadataList(result); + receivedBean.setMetadataList(profiles); prepareMetadataList(receivedBean); organizationsListbox.setEnabled(true); metadataProfilesFormatListbox.setEnabled(true); @@ -429,17 +418,21 @@ public class CreateDatasetForm extends Composite{ ckanServices.getUserGroups(new AsyncCallback>() { @Override - public void onSuccess(List result) { + public void onSuccess(List groups) { - if(result == null){ + if(groups == null){ setAlertBlock("Error while retrieving groups, try later", AlertType.ERROR, true); }else{ - if(result.isEmpty()) + if(groups.isEmpty()) return; else{ - for (GroupBean groups : result) { - groupsListbox.addItem(groups.getGroupTitle(), groups.getGroupName()); + + // add groups + for (GroupBean group : groups) { + groupsListbox.addItem(group.getGroupTitle(), group.getGroupName()); } + + hideGroupsAlreadyInProfile(profiles); groupsControlGroup.setVisible(true); } // everything went ok @@ -469,16 +462,14 @@ public class CreateDatasetForm extends Composite{ } + /** - * When the organization name is changed we need to retrieve the list of profiles + * When the organization name is changed we need to retrieve the list of profiles and groups */ private void organizationsListboxChangeHandlerBody() { // remove any other product profiles - int presentItems = metadataProfilesFormatListbox.getItemCount(); - for (int i = presentItems - 1; i >= 0; i--) { - metadataProfilesFormatListbox.removeItem(i); - } + metadataProfilesFormatListbox.clear(); // add "none" item again metadataProfilesFormatListbox.addItem(NONE_PROFILE); @@ -494,24 +485,61 @@ public class CreateDatasetForm extends Composite{ setAlertBlock("Retrieving profiles, please wait...", AlertType.INFO, true); // disable the list of organizations name so that the user doesn't change it again + // also disable the profiles and the list of groups organizationsListbox.setEnabled(false); metadataProfilesFormatListbox.setEnabled(false); + groupsListbox.setEnabled(false); + groupsControlGroup.setVisible(false); // perform remote request of profiles for the selected organization ckanServices.getProfiles(orgName, new AsyncCallback>() { @Override - public void onSuccess(List result) { + public void onSuccess(final List profiles) { - if(result != null){ + if(profiles != null){ - receivedBean.setMetadataList(result); + receivedBean.setMetadataList(profiles); prepareMetadataList(receivedBean); organizationsListbox.setEnabled(true); metadataProfilesFormatListbox.setEnabled(true); + groupsListbox.setEnabled(true); - // everything went ok - setAlertBlock("", AlertType.DEFAULT, false); + // try to retrieve the licenses + setAlertBlock("Retrieving groups, please wait...", AlertType.INFO, true); + + // request groups + ckanServices.getUserGroups(new AsyncCallback>() { + + @Override + public void onSuccess(List groups) { + + if(groups == null){ + setAlertBlock("Error while retrieving groups, try later", AlertType.ERROR, true); + }else{ + if(groups.isEmpty()) + return; + else{ + + // add groups + for (GroupBean group : groups) { + groupsListbox.addItem(group.getGroupTitle(), group.getGroupName()); + } + + hideGroupsAlreadyInProfile(profiles); + groupsListbox.setEnabled(true); + groupsControlGroup.setVisible(true); + } + // everything went ok + setAlertBlock("", AlertType.ERROR, false); + } + } + + @Override + public void onFailure(Throwable caught) { + setAlertBlock("Error while retrieving groups, try later", AlertType.ERROR, true); + } + }); }else setAlertBlock("Error while retrieving profiles, sorry", AlertType.ERROR, true); @@ -534,10 +562,10 @@ public class CreateDatasetForm extends Composite{ */ private void prepareMetadataList(final DatasetMetadataBean receivedBean) { - List beans = receivedBean.getMetadataList(); + List profiles = receivedBean.getMetadataList(); - if(beans != null && !beans.isEmpty()){ - for(MetaDataProfileBean metadataBean: beans){ + if(profiles != null && !profiles.isEmpty()){ + for(MetaDataProfileBean metadataBean: profiles){ metadataProfilesFormatListbox.addItem(metadataBean.getType().getName()); @@ -547,21 +575,37 @@ public class CreateDatasetForm extends Composite{ @Override public void onChange(ChangeEvent event) { - String selectedItem = metadataProfilesFormatListbox.getSelectedItemText(); + String selectedItemText = metadataProfilesFormatListbox.getSelectedItemText(); - if(selectedItem.equals(NONE_PROFILE)){ - // hide the panel + if(selectedItemText.equals(NONE_PROFILE)){ metadataFieldsPanel.clear(); metadataFieldsPanel.setVisible(false); receivedBean.setChosenProfile(null); }else{ - receivedBean.setChosenProfile(selectedItem); + receivedBean.setChosenProfile(selectedItemText); metadataFieldsPanel.clear(); - addFields(selectedItem); + addFields(selectedItemText); } } }); } + + // hide elements or show them if needed (groups in profiles cannot be present again in groups listbox) + if(groupsControlGroup.isVisible()){ + List groupsToHide = new ArrayList(); + for(MetaDataProfileBean profile: profiles) + groupsToHide.add(profile.getType().toString()); + + SelectElement se = groupsListbox.getElement().cast(); + + for (int i = 0; i < groupsListbox.getItemCount(); i++) { + if(groupsToHide.contains(groupsListbox.getItemText(i))){ + se.getOptions().getItem(i).getStyle().setProperty("display", "none"); + }else + se.getOptions().getItem(i).getStyle().setProperty("display", ""); + } + } + metadataProfilesControlGroup.setVisible(true); }else{ // just hide this listbox @@ -635,23 +679,16 @@ public class CreateDatasetForm extends Composite{ @Override public void onSuccess(Boolean result) { - if(result){ - alertOnContinue("Sorry but a product with such title already exists, try to change it", AlertType.WARNING); - }else{ - actionsAfterOnContinue(); - } } @Override public void onFailure(Throwable caught) { - alertOnContinue("Sorry but there was a problem while checking if the inserted data are correct", AlertType.ERROR); - } }); } @@ -1228,6 +1265,7 @@ public class CreateDatasetForm extends Composite{ organizationsListbox.setEnabled(false); addCustomFieldButton.setEnabled(false); metadataProfilesFormatListbox.setEnabled(false); + groupsListbox.setEnabled(false); for(CustomFieldEntry ce: customFieldEntriesList) ce.freeze(); @@ -1290,4 +1328,23 @@ public class CreateDatasetForm extends Composite{ } + /** + * Hide the groups that are already listed in the profiles page + * @param profiles + */ + private void hideGroupsAlreadyInProfile(List profiles) { + + List groupsToHide = new ArrayList(); + for(MetaDataProfileBean profile: profiles) + groupsToHide.add(profile.getType().getName()); + + SelectElement se = groupsListbox.getElement().cast(); + + for (int i = 0; i < groupsListbox.getItemCount(); i++) { + if(groupsToHide.contains(groupsListbox.getItemText(i))){ + se.getOptions().getItem(i).getStyle().setProperty("display", "none"); + } + } + + } } diff --git a/src/main/java/org/gcube/portlets/widgets/ckandatapublisherwidget/server/AssociationToGroupThread.java b/src/main/java/org/gcube/portlets/widgets/ckandatapublisherwidget/server/AssociationToGroupThread.java deleted file mode 100644 index df8242e..0000000 --- a/src/main/java/org/gcube/portlets/widgets/ckandatapublisherwidget/server/AssociationToGroupThread.java +++ /dev/null @@ -1,87 +0,0 @@ -package org.gcube.portlets.widgets.ckandatapublisherwidget.server; - -import java.util.List; - -import org.gcube.datacatalogue.ckanutillibrary.DataCatalogue; -import org.gcube.datacatalogue.ckanutillibrary.models.RolesCkanGroupOrOrg; -import org.gcube.portlets.widgets.ckandatapublisherwidget.shared.GroupBean; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import eu.trentorise.opendata.jackan.model.CkanGroup; - -/** - * Associate the dataset to a group. - * @author Costantino Perciante at ISTI-CNR (costantino.perciante@isti.cnr.it) - */ -public class AssociationToGroupThread extends Thread { - - private static final Logger logger = LoggerFactory.getLogger(AssociationToGroupThread.class); - - private String groupTitle; - private String datasetId; - private String username; - private DataCatalogue catalogue; - private String organization; - private List groups; - - /** - * @param list - * @param groupTitle - * @param datasetId - * @param username - * @param catalogue - */ - public AssociationToGroupThread(List groups, String groupTitle, String datasetId, - String username, DataCatalogue catalogue, String organization) { - this.groups = groups; - this.groupTitle = groupTitle; - this.datasetId = datasetId; - this.username = username; - this.catalogue = catalogue; - this.organization = organization; - } - - @Override - public void run() { - - logger.info("Association thread started to put the dataset with id = "+ datasetId + " into group with title " + groupTitle + " for user " + username); - - // create the group - CkanGroup group = catalogue.createGroup(groupTitle, groupTitle, ""); - - if(group == null){ - - logger.warn("The group doesn't exist!!! Unable to perform such association"); - - }else{ - - logger.debug("Group exists, going to add the user " + username + " as its admin..."); - - // retrieve the role to be assigned according the one the user has into the organization of the dataset - RolesCkanGroupOrOrg role = RolesCkanGroupOrOrg.valueOf(catalogue.getRoleOfUserInOrganization(username, organization, catalogue.getApiKeyFromUsername(username)).toUpperCase()); - - if(!role.equals(RolesCkanGroupOrOrg.ADMIN)) - role = RolesCkanGroupOrOrg.MEMBER; // decrease the role to member if it is not an admin - - boolean assigned = catalogue.checkRoleIntoGroup(username, groupTitle, role); - - if(assigned){ - - logger.debug("Admin/editor role was assigned for this group, going to associate the product to the group"); - boolean putIntoGroup = catalogue.assignDatasetToGroup(groupTitle, datasetId, catalogue.getApiKeyFromUsername(username)); - logger.info("Was product put into group? " + putIntoGroup); - - } - } - - logger.info("Other groups to which the product should be associate are " + groups); - - for (GroupBean groupBean : groups) { - boolean putIntoGroup = catalogue.assignDatasetToGroup(groupBean.getGroupTitle(), datasetId, catalogue.getApiKeyFromUsername(username)); - logger.info("Was product put into group" + groupBean + "? " + putIntoGroup); - } - - } - -} diff --git a/src/main/java/org/gcube/portlets/widgets/ckandatapublisherwidget/server/CKANPublisherServicesImpl.java b/src/main/java/org/gcube/portlets/widgets/ckandatapublisherwidget/server/CKANPublisherServicesImpl.java index 84d3406..3085555 100644 --- a/src/main/java/org/gcube/portlets/widgets/ckandatapublisherwidget/server/CKANPublisherServicesImpl.java +++ b/src/main/java/org/gcube/portlets/widgets/ckandatapublisherwidget/server/CKANPublisherServicesImpl.java @@ -12,8 +12,6 @@ import javax.servlet.http.HttpSession; import org.gcube.application.framework.core.session.ASLSession; import org.gcube.application.framework.core.session.SessionManager; -import org.gcube.common.homelibrary.home.HomeLibrary; -import org.gcube.common.homelibrary.home.workspace.Workspace; import org.gcube.datacatalogue.ckanutillibrary.DataCatalogue; import org.gcube.datacatalogue.ckanutillibrary.DataCatalogueFactory; import org.gcube.datacatalogue.ckanutillibrary.models.ResourceBean; @@ -21,6 +19,8 @@ import org.gcube.datacatalogue.ckanutillibrary.utils.SessionCatalogueAttributes; import org.gcube.datacatalogue.ckanutillibrary.utils.UtilMethods; import org.gcube.portal.custom.scopemanager.scopehelper.ScopeHelper; import org.gcube.portlets.widgets.ckandatapublisherwidget.client.CKanPublisherService; +import org.gcube.portlets.widgets.ckandatapublisherwidget.server.threads.AssociationToGroupAndNotifyThread; +import org.gcube.portlets.widgets.ckandatapublisherwidget.server.threads.WritePostCatalogueManagerThread; import org.gcube.portlets.widgets.ckandatapublisherwidget.server.utils.Utils; import org.gcube.portlets.widgets.ckandatapublisherwidget.server.utils.WorkspaceUtils; import org.gcube.portlets.widgets.ckandatapublisherwidget.shared.DatasetMetadataBean; @@ -52,7 +52,6 @@ public class CKANPublisherServicesImpl extends RemoteServiceServlet implements C public static final String TEST_SCOPE = "/gcube"; public static final String TEST_USER = "test.user"; - private final static String TEST_SEC_TOKEN = "a1e19695-467f-42b8-966d-bf83dd2382ef"; // map private ConcurrentHashMap mapOrganizationScope = new ConcurrentHashMap(); @@ -71,7 +70,7 @@ public class CKANPublisherServicesImpl extends RemoteServiceServlet implements C logger.debug("Discovering ckan instance into scope " + scopeInWhichDiscover); instance = DataCatalogueFactory.getFactory().getUtilsPerScope(scopeInWhichDiscover); }catch(Exception e){ - logger.warn("Unable to retrieve ckan utils in scope " + scopeInWhichDiscover + ". Error is " + e.toString()); + logger.warn("Unable to retrieve ckan utils in scope " + scopeInWhichDiscover + ". Error is " + e.getLocalizedMessage()); } return instance; } @@ -102,54 +101,6 @@ public class CKANPublisherServicesImpl extends RemoteServiceServlet implements C return user; } - /** - * Get current user's token (for a given scope) - * @param scope if it is not specified it will be retrieved from the asl - * @return String the ckan user's token - */ - private String getUserCKanTokenFromSession(String scope){ - - String token = null; - - if(!isWithinPortal()){ - logger.warn("You are running outside the portal"); - token = TEST_SEC_TOKEN; - }else{ - - ASLSession aslSession = getASLSession(); - String username = aslSession.getUsername(); - logger.debug("User in session is " + username); - if(username.equals(TEST_USER)){ - - logger.warn("Session expired, returning null token"); - token = null; - - }else{ - try{ - - HttpSession httpSession = getThreadLocalRequest().getSession(); - String scopeInWhichDiscover = (scope != null && !scope.isEmpty()) ? scope : getASLSession().getScope(); - String keyPerScope = UtilMethods.concatenateSessionKeyScope(SessionCatalogueAttributes.CKAN_TOKEN_KEY, scopeInWhichDiscover); - - if(httpSession.getAttribute(keyPerScope) != null){ - token = (String)httpSession.getAttribute(keyPerScope); - logger.debug("Found ckan token into session"); - } - else{ - token = getCatalogue(scopeInWhichDiscover).getApiKeyFromUsername(username); - httpSession.setAttribute(keyPerScope, token); - logger.debug("Ckan token has been set for user " + username); - } - logger.debug("Found ckan token " + token.substring(0, 3) + "************************" + - " for user " + username + " into scope " + scopeInWhichDiscover); - }catch(Exception e){ - logger.error("Error while retrieving the key" , e); - } - } - } - return token; - } - /** * Retrieve the list of organizations in which the user can publish (roles ADMIN) * @param username @@ -367,8 +318,9 @@ public class CKANPublisherServicesImpl extends RemoteServiceServlet implements C logger.debug("The user wants to publish in organization with name " + organizationNameOrId); String scope = getScopeFromOrgName(organizationNameOrId); DataCatalogue utils = getCatalogue(scope); + String userApiKey = utils.getApiKeyFromUsername(userName); - String datasetId = utils.createCKanDataset(getUserCKanTokenFromSession(scope), title, null, organizationNameOrId, author, + String datasetId = utils.createCKanDataset(userApiKey, title, null, organizationNameOrId, author, authorMail, maintainer, maintainerMail, version, description, licenseId, listOfTags, customFields, resources, setPublic); @@ -378,20 +330,23 @@ public class CKANPublisherServicesImpl extends RemoteServiceServlet implements C toCreate.setId(datasetId); // retrieve the url - String datasetUrl = utils.getPortletUrl() + "?" + URLEncoder.encode("path=" + utils.getUrlFromDatasetIdOrName(getUserCKanTokenFromSession(scope), datasetId, true), "UTF-8"); + String datasetUrl = utils.getPortletUrl() + "?" + URLEncoder.encode("path=" + utils.getUrlFromDatasetIdOrName(userApiKey, datasetId, true), "UTF-8"); toCreate.setSource(datasetUrl); // start a thread that will associate this dataset with the group if(toCreate.getChosenProfile() != null || toCreate.getGroups() != null){ - AssociationToGroupThread threadAssociationToGroup = - new AssociationToGroupThread( + AssociationToGroupAndNotifyThread threadAssociationToGroup = + new AssociationToGroupAndNotifyThread( toCreate.getGroups(), toCreate.getChosenProfile(), - datasetId, + datasetId, + toCreate.getTitle(), + aslSession.getUserFullName(), userName, utils, - organizationNameOrId + organizationNameOrId, + getThreadLocalRequest() ); threadAssociationToGroup.start(); @@ -406,7 +361,7 @@ public class CKANPublisherServicesImpl extends RemoteServiceServlet implements C datasetUrl, false, // send notification to other people toCreate.getTags(), - toCreate.getAuthorFullName() + aslSession.getUserFullName() ); threadWritePost.start(); @@ -452,7 +407,8 @@ public class CKANPublisherServicesImpl extends RemoteServiceServlet implements C // get the scope in which we should discover the ckan instance given the organization name in which the dataset was created String scope = getScopeFromOrgName(resource.getOrganizationNameDatasetParent()); - String resourceId = getCatalogue(scope).addResourceToDataset(resourceBean, getUserCKanTokenFromSession(scope)); + DataCatalogue catalogue = getCatalogue(scope); + String resourceId = catalogue.addResourceToDataset(resourceBean, catalogue.getApiKeyFromUsername(username)); if(resourceId != null){ logger.debug("Resource " + resource.getName() + " is now available"); @@ -490,8 +446,9 @@ public class CKANPublisherServicesImpl extends RemoteServiceServlet implements C try{ // get the scope in which we should discover the ckan instance given the organization name in which the dataset was created String scope = getScopeFromOrgName(resource.getOrganizationNameDatasetParent()); - deleted = getCatalogue(scope). - deleteResourceFromDataset(resource.getOriginalIdInWorkspace(), getUserCKanTokenFromSession(scope)); + DataCatalogue catalogue = getCatalogue(scope); + deleted = catalogue. + deleteResourceFromDataset(resource.getOriginalIdInWorkspace(), catalogue.getApiKeyFromUsername(username)); if(deleted){ logger.info("Resource described by " + resource + " deleted"); }else @@ -562,46 +519,46 @@ public class CKANPublisherServicesImpl extends RemoteServiceServlet implements C return toReturn; } - @Override - public ResourceElementBean getTreeFolder(String folderId) { - - if(folderId == null || folderId.isEmpty()){ - logger.warn("Empty folder id or null, returning"); - return null; - } - ASLSession session = getASLSession(); - try{ - if(!isWithinPortal()){ - logger.warn("Running outside the portal"); - Workspace ws = getFakeWS(); - ResourceElementBean toReturn = WorkspaceUtils.getTreeFromFolder(folderId, ws); - logger.debug("Returning " + toReturn); - return toReturn; - }else{ - if(session.getUsername().equals(TEST_USER)){ - return null; - }else{ - // TODO - return null; - } - } - }catch(Exception e){ - logger.error("Failed to build the tree", e); - } - return null; - } - - /** - * Retrieve the workspace for the development user - * @return - * @throws Exception - */ - private Workspace getFakeWS() throws Exception{ - return HomeLibrary - .getHomeManagerFactory() - .getHomeManager() - .getHome(getDevelopmentUser()).getWorkspace(); - } + // @Override + // public ResourceElementBean getTreeFolder(String folderId) { + // + // if(folderId == null || folderId.isEmpty()){ + // logger.warn("Empty folder id or null, returning"); + // return null; + // } + // ASLSession session = getASLSession(); + // try{ + // if(!isWithinPortal()){ + // logger.warn("Running outside the portal"); + // Workspace ws = getFakeWS(); + // ResourceElementBean toReturn = WorkspaceUtils.getTreeFromFolder(folderId, ws); + // logger.debug("Returning " + toReturn); + // return toReturn; + // }else{ + // if(session.getUsername().equals(TEST_USER)){ + // return null; + // }else{ + // // TODO + // return null; + // } + // } + // }catch(Exception e){ + // logger.error("Failed to build the tree", e); + // } + // return null; + // } + // + // /** + // * Retrieve the workspace for the development user + // * @return + // * @throws Exception + // */ + // private Workspace getFakeWS() throws Exception{ + // return HomeLibrary + // .getHomeManagerFactory() + // .getHomeManager() + // .getHome(getDevelopmentUser()).getWorkspace(); + // } @Override public List getUserGroups() { @@ -611,6 +568,7 @@ public class CKANPublisherServicesImpl extends RemoteServiceServlet implements C if(isWithinPortal()){ String username = session.getUsername(); + if(username.equals(TEST_USER)){ logger.warn("Session expired"); return null; @@ -623,10 +581,16 @@ public class CKANPublisherServicesImpl extends RemoteServiceServlet implements C String scope = (String)httpSession.getAttribute(SessionCatalogueAttributes.SCOPE_CLIENT_PORTLET_URL); DataCatalogue catalogue = getCatalogue(scope); List ckanGroups = catalogue.getGroups(); + + String apiKey = catalogue.getApiKeyFromUsername(username); - // TODO check role - + // Members/Admin of the group for (CkanGroup ckanGroup : ckanGroups) { + String role = catalogue.getRoleOfUserInGroup(username, ckanGroup.getName(), apiKey); + + if(role == null) + continue; + toReturn.add(new GroupBean(ckanGroup.getTitle(), ckanGroup.getName())); } logger.debug("List of groups to return is " + toReturn); diff --git a/src/main/java/org/gcube/portlets/widgets/ckandatapublisherwidget/server/threads/AssociationToGroupAndNotifyThread.java b/src/main/java/org/gcube/portlets/widgets/ckandatapublisherwidget/server/threads/AssociationToGroupAndNotifyThread.java new file mode 100644 index 0000000..a5648ae --- /dev/null +++ b/src/main/java/org/gcube/portlets/widgets/ckandatapublisherwidget/server/threads/AssociationToGroupAndNotifyThread.java @@ -0,0 +1,166 @@ +package org.gcube.portlets.widgets.ckandatapublisherwidget.server.threads; + +import java.util.ArrayList; +import java.util.Iterator; +import java.util.List; +import java.util.Map; + +import javax.servlet.http.HttpServletRequest; + +import org.gcube.common.portal.mailing.EmailNotification; +import org.gcube.datacatalogue.ckanutillibrary.DataCatalogue; +import org.gcube.datacatalogue.ckanutillibrary.models.RolesCkanGroupOrOrg; +import org.gcube.datacatalogue.ckanutillibrary.utils.UtilMethods; +import org.gcube.portlets.widgets.ckandatapublisherwidget.shared.GroupBean; +import org.gcube.vomanagement.usermanagement.UserManager; +import org.gcube.vomanagement.usermanagement.impl.LiferayUserManager; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import eu.trentorise.opendata.jackan.model.CkanGroup; + +/** + * Associate the dataset to a group and send notifications to group's admins. + * @author Costantino Perciante at ISTI-CNR (costantino.perciante@isti.cnr.it) + */ +public class AssociationToGroupAndNotifyThread extends Thread { + + private static final Logger logger = LoggerFactory.getLogger(AssociationToGroupAndNotifyThread.class); + private static final String PRODUCT_ASSOCIATED_TO_GROUP_SUBJECT = "Product $PRODUCT added to group $GROUP"; + private static final String PRODUCT_ASSOCIATED_TO_GROUP_BODY = "Dear user,'\n'a new product named $TITLE has been just published to the Data Catalogue group $GROUP by $USER_FULLNAME."; + + private String groupTitle; + private String datasetId; + private String username; + private String datasetTitle; + private String userFullName; + private DataCatalogue catalogue; + private String organization; + private List groups; + private HttpServletRequest request; + + /** + * @param list + * @param groupTitle + * @param datasetId + * @param username + * @param catalogue + */ + public AssociationToGroupAndNotifyThread(List groups, String groupTitle, String datasetId, String datasetTitle, String userFullName, + String username, DataCatalogue catalogue, String organization, HttpServletRequest request) { + this.request = request; + this.groups = groups; + this.groupTitle = groupTitle; + this.datasetId = datasetId; + this.username = username; + this.catalogue = catalogue; + this.organization = organization; + this.datasetTitle = datasetTitle; + this.userFullName = userFullName; + } + + @Override + public void run() { + + logger.info("Association thread started to put the dataset with id = "+ datasetId + " into group with title " + groupTitle + " for user " + username); + + // create the group + CkanGroup group = catalogue.createGroup(groupTitle, groupTitle, ""); + + if(group == null){ + + logger.warn("The group doesn't exist! Unable to perform such association"); + + }else{ + + logger.debug("Group exists, going to add the user " + username + " as its admin..."); + + // retrieve the role to be assigned according the one the user has into the organization of the dataset + RolesCkanGroupOrOrg role = RolesCkanGroupOrOrg.valueOf(catalogue.getRoleOfUserInOrganization(username, organization, catalogue.getApiKeyFromUsername(username)).toUpperCase()); + + if(!role.equals(RolesCkanGroupOrOrg.ADMIN)) + role = RolesCkanGroupOrOrg.MEMBER; // decrease the role to member if it is not an admin + + boolean assigned = catalogue.checkRoleIntoGroup(username, groupTitle, role); + + if(assigned){ + + logger.debug("Admin/editor role was assigned for this group, going to associate the product to the group"); + boolean putIntoGroup = catalogue.assignDatasetToGroup(groupTitle, datasetId, catalogue.getApiKeyFromUsername(username)); + logger.info("Was product put into group? " + putIntoGroup); + + if(putIntoGroup) + notifyGroupAdmins(catalogue, groupTitle, username); + + } + } + + logger.info("Other groups to which the product should be associate are " + groups); + + for (GroupBean groupBean : groups) { + boolean putIntoGroup = catalogue.assignDatasetToGroup(groupBean.getGroupTitle(), datasetId, catalogue.getApiKeyFromUsername(username)); + logger.info("Was product put into group" + groupBean.getGroupTitle() + "? " + putIntoGroup); + + if(putIntoGroup) + notifyGroupAdmins(catalogue, groupBean.getGroupTitle(), username); + } + + } + + /** + * Send a notification to the group admin(s) about the just added product + * @param username + * @param groupTitle + * @param catalogue + */ + private void notifyGroupAdmins(DataCatalogue catalogue, String groupTitle, String username){ + + // get the groups admin + Map> userAndRoles = catalogue.getRolesAndUsersGroup(groupTitle); + + if(userAndRoles.containsKey(RolesCkanGroupOrOrg.ADMIN)){ + + List admins = userAndRoles.get(RolesCkanGroupOrOrg.ADMIN); + List adminsEmails = new ArrayList(); + + for(int i = 0; i < admins.size(); i++){ + String convertedName = UtilMethods.fromCKanUsernameToUsername(admins.get(i)); + admins.set(i, convertedName); + } + + // remove the same user who published the product if he/she is an admin of the group + int indexOfUser = admins.indexOf(username); + if(indexOfUser >= 0) + admins.remove(indexOfUser); + + // further cleaning of the list (for users that are only in ckan... sysadmin for example) + UserManager um = new LiferayUserManager(); + Iterator adminIt = admins.iterator(); + + while (adminIt.hasNext()) { + String admin = (String) adminIt.next(); + try{ + adminsEmails.add(um.getUserByUsername(admin).getEmail()); + }catch(Exception e){ + logger.error("User with username " + admin + " doesn't exist in Liferay"); + adminIt.remove(); + } + } + + logger.info("The list of admins for group " + groupTitle + " is " + admins); + + if(admins.isEmpty()) + return; + + // send the email + EmailNotification mailToSend = new EmailNotification( + adminsEmails, + PRODUCT_ASSOCIATED_TO_GROUP_SUBJECT.replace("$TITLE", datasetTitle).replace("$GROUP", groupTitle), + PRODUCT_ASSOCIATED_TO_GROUP_BODY.replace("$TITLE", datasetTitle).replace("$GROUP", groupTitle).replace("$USER_FULLNAME", userFullName), + request); + mailToSend.sendEmail(); + + }else + logger.warn("It seems there is no user with role Admin in group " + groupTitle); + } +} diff --git a/src/main/java/org/gcube/portlets/widgets/ckandatapublisherwidget/server/WritePostCatalogueManagerThread.java b/src/main/java/org/gcube/portlets/widgets/ckandatapublisherwidget/server/threads/WritePostCatalogueManagerThread.java similarity index 87% rename from src/main/java/org/gcube/portlets/widgets/ckandatapublisherwidget/server/WritePostCatalogueManagerThread.java rename to src/main/java/org/gcube/portlets/widgets/ckandatapublisherwidget/server/threads/WritePostCatalogueManagerThread.java index 21198c3..ab708a0 100644 --- a/src/main/java/org/gcube/portlets/widgets/ckandatapublisherwidget/server/WritePostCatalogueManagerThread.java +++ b/src/main/java/org/gcube/portlets/widgets/ckandatapublisherwidget/server/threads/WritePostCatalogueManagerThread.java @@ -1,12 +1,8 @@ -package org.gcube.portlets.widgets.ckandatapublisherwidget.server; +package org.gcube.portlets.widgets.ckandatapublisherwidget.server.threads; -import static org.gcube.common.authorization.client.Constants.authorizationService; - -import java.util.ArrayList; import java.util.List; import org.gcube.common.authorization.library.provider.SecurityTokenProvider; -import org.gcube.common.authorization.library.provider.UserInfo; import org.gcube.common.scope.api.ScopeProvider; import org.gcube.portlets.widgets.ckandatapublisherwidget.server.utils.Utils; import org.slf4j.Logger; @@ -58,7 +54,12 @@ public class WritePostCatalogueManagerThread extends Thread { try{ // evaluate user's token for this scope - String token = authorizationService().generateUserToken(new UserInfo(username, new ArrayList()), scope); + String token = Utils.tryGetElseCreateToken(username, scope); + + if(token == null){ + logger.warn("Unable to proceed, user's token is not available"); + return; + } logger.info("Started request to write application post " + "for new product created. Scope is " + scope + " and " @@ -80,10 +81,8 @@ public class WritePostCatalogueManagerThread extends Thread { }catch(Exception e){ logger.error("Failed to write the post because of the following error ", e); }finally{ - // remove token and scope SecurityTokenProvider.instance.reset(); ScopeProvider.instance.reset(); } } - -} +} \ No newline at end of file diff --git a/src/main/java/org/gcube/portlets/widgets/ckandatapublisherwidget/server/utils/Utils.java b/src/main/java/org/gcube/portlets/widgets/ckandatapublisherwidget/server/utils/Utils.java index 938ca73..7347953 100644 --- a/src/main/java/org/gcube/portlets/widgets/ckandatapublisherwidget/server/utils/Utils.java +++ b/src/main/java/org/gcube/portlets/widgets/ckandatapublisherwidget/server/utils/Utils.java @@ -1,5 +1,7 @@ package org.gcube.portlets.widgets.ckandatapublisherwidget.server.utils; +import static org.gcube.common.authorization.client.Constants.authorizationService; + import java.io.BufferedReader; import java.io.InputStream; import java.io.InputStreamReader; @@ -11,7 +13,9 @@ import java.util.Map; import javax.servlet.http.HttpSession; import org.gcube.application.framework.core.session.ASLSession; +import org.gcube.common.authorization.client.exceptions.ObjectNotFound; import org.gcube.common.authorization.library.provider.SecurityTokenProvider; +import org.gcube.common.authorization.library.provider.UserInfo; import org.gcube.common.homelibrary.home.exceptions.InternalErrorException; import org.gcube.common.homelibrary.home.workspace.WorkspaceItem; import org.gcube.common.homelibrary.home.workspace.folder.items.GCubeItem; @@ -479,4 +483,28 @@ public class Utils { return toReturn; } + + /** + * First check to retrieve the token, else create it + * @param username + * @param context + * @return the user token for the context + */ + public static String tryGetElseCreateToken(String username, String context) { + String token = null; + try{ + try{ + logger.debug("Checking if token for user " + username + " in context " + context + " already exists..."); + token = authorizationService().resolveTokenByUserAndContext(username, context); + logger.debug("It exists!"); + }catch(ObjectNotFound e){ + logger.info("Creating token for user " + username + " and context " + context); + token = authorizationService().generateUserToken(new UserInfo(username, new ArrayList()), context); + logger.debug("received token: "+ token.substring(0, 5) + "***********************"); + } + }catch(Exception e){ + logger.error("Failed both token retrieval and creation", e); + } + return token; + } } \ No newline at end of file diff --git a/src/main/java/org/gcube/portlets/widgets/ckandatapublisherwidget/server/utils/WorkspaceUtils.java b/src/main/java/org/gcube/portlets/widgets/ckandatapublisherwidget/server/utils/WorkspaceUtils.java index 57aa4bb..bad7378 100644 --- a/src/main/java/org/gcube/portlets/widgets/ckandatapublisherwidget/server/utils/WorkspaceUtils.java +++ b/src/main/java/org/gcube/portlets/widgets/ckandatapublisherwidget/server/utils/WorkspaceUtils.java @@ -180,7 +180,7 @@ public class WorkspaceUtils { } /** - * Replaces the "/" char with a custom one + * Replaces the "/" char with a custom one and return an editable name for the user * @param rootElem * @param pathSeparatorInWs */ diff --git a/src/main/java/org/gcube/portlets/widgets/ckandatapublisherwidget/shared/GroupBean.java b/src/main/java/org/gcube/portlets/widgets/ckandatapublisherwidget/shared/GroupBean.java index 7a202ad..d7aae98 100644 --- a/src/main/java/org/gcube/portlets/widgets/ckandatapublisherwidget/shared/GroupBean.java +++ b/src/main/java/org/gcube/portlets/widgets/ckandatapublisherwidget/shared/GroupBean.java @@ -41,5 +41,11 @@ public class GroupBean implements Serializable { public void setGroupName(String groupName) { this.groupName = groupName; } - + + @Override + public String toString() { + return "GroupBean [groupTitle=" + groupTitle + ", groupName=" + + groupName + "]"; + } + }