diff --git a/.classpath b/.classpath
index 6d6f9dd..4c6e618 100644
--- a/.classpath
+++ b/.classpath
@@ -12,7 +12,7 @@
-
+
@@ -23,7 +23,7 @@
-
+
diff --git a/pom.xml b/pom.xml
index a2b7261..701bd10 100644
--- a/pom.xml
+++ b/pom.xml
@@ -83,10 +83,6 @@
portal-manager
provided
-
- org.gcube.portal
- notifications-common-library
-
org.gcube.portlets.user
gcube-url-shortener
@@ -121,18 +117,18 @@
org.gcube.core
common-scope-maps
- provided
+ compile
org.gcube.core
common-encryption
- provided
+ compile
org.gcube.common
authorization-client
- provided
+ compile
junit
diff --git a/src/main/java/org/gcube/datacatalogue/grsf_manage_widget/client/GRSFManageWidget.java b/src/main/java/org/gcube/datacatalogue/grsf_manage_widget/client/GRSFManageWidget.java
index c9c6d1d..2ffa8ab 100644
--- a/src/main/java/org/gcube/datacatalogue/grsf_manage_widget/client/GRSFManageWidget.java
+++ b/src/main/java/org/gcube/datacatalogue/grsf_manage_widget/client/GRSFManageWidget.java
@@ -12,6 +12,8 @@ public class GRSFManageWidget implements EntryPoint {
*/
public void onModuleLoad(){
// HandlerManager eventBus = new HandlerManager(null);
- // RootPanel.get("manageDiv").add(new ManageProductWidget("fffb6167-b570-42a8-92b9-5be28549c3b8", eventBus));
+ //RootPanel.get("manageDiv").add(new ManageProductWidget("fffb6167-b570-42a8-92b9-5be28549c3b8", eventBus));
+ // RootPanel.get("manageDiv").add(new ManageRevertOperationWidget(
+ // "random-url-here", eventBus));
}
}
diff --git a/src/main/java/org/gcube/datacatalogue/grsf_manage_widget/client/GRSFManageWidgetService.java b/src/main/java/org/gcube/datacatalogue/grsf_manage_widget/client/GRSFManageWidgetService.java
index 9b93aa8..85c5e7d 100644
--- a/src/main/java/org/gcube/datacatalogue/grsf_manage_widget/client/GRSFManageWidgetService.java
+++ b/src/main/java/org/gcube/datacatalogue/grsf_manage_widget/client/GRSFManageWidgetService.java
@@ -1,6 +1,7 @@
package org.gcube.datacatalogue.grsf_manage_widget.client;
import org.gcube.datacatalogue.grsf_manage_widget.shared.ManageProductBean;
+import org.gcube.datacatalogue.grsf_manage_widget.shared.RevertableOperationInfo;
import com.google.gwt.user.client.rpc.RemoteService;
import com.google.gwt.user.client.rpc.RemoteServiceRelativePath;
@@ -45,10 +46,18 @@ public interface GRSFManageWidgetService extends RemoteService {
String checkIdentifierExistsInDomain(String id, String domain) throws Exception;
/**
- * Check if the given url for reverting the operation is valid and send the request to the knowledge base
+ * Check if the given url for reverting the operation is valid and get back the needed info to proceed
* @param url
* @throws Exception
*/
- void validateRevertOperation(String url) throws Exception;
+ RevertableOperationInfo validateRevertOperation(String url) throws Exception;
+
+ /**
+ * Perform a revert operation
+ * @param rInfo
+ * @return
+ * @throws Exception
+ */
+ Boolean performRevertOperation(RevertableOperationInfo rInfo) throws Exception;
}
diff --git a/src/main/java/org/gcube/datacatalogue/grsf_manage_widget/client/GRSFManageWidgetServiceAsync.java b/src/main/java/org/gcube/datacatalogue/grsf_manage_widget/client/GRSFManageWidgetServiceAsync.java
index 481cffd..8d402e8 100644
--- a/src/main/java/org/gcube/datacatalogue/grsf_manage_widget/client/GRSFManageWidgetServiceAsync.java
+++ b/src/main/java/org/gcube/datacatalogue/grsf_manage_widget/client/GRSFManageWidgetServiceAsync.java
@@ -4,6 +4,7 @@
package org.gcube.datacatalogue.grsf_manage_widget.client;
import org.gcube.datacatalogue.grsf_manage_widget.shared.ManageProductBean;
+import org.gcube.datacatalogue.grsf_manage_widget.shared.RevertableOperationInfo;
import com.google.gwt.user.client.rpc.AsyncCallback;
@@ -27,6 +28,9 @@ public interface GRSFManageWidgetServiceAsync {
void checkIdentifierExistsInDomain(String id,
String domain, AsyncCallback callback);
- void validateRevertOperation(String url, AsyncCallback callback);
+ void validateRevertOperation(String url, AsyncCallback callback);
+
+ void performRevertOperation(RevertableOperationInfo rInfo,
+ AsyncCallback callback);
}
diff --git a/src/main/java/org/gcube/datacatalogue/grsf_manage_widget/client/view/ManageProductWidget.java b/src/main/java/org/gcube/datacatalogue/grsf_manage_widget/client/view/ManageProductWidget.java
index c0c0e8c..95f8730 100644
--- a/src/main/java/org/gcube/datacatalogue/grsf_manage_widget/client/view/ManageProductWidget.java
+++ b/src/main/java/org/gcube/datacatalogue/grsf_manage_widget/client/view/ManageProductWidget.java
@@ -52,8 +52,7 @@ public class ManageProductWidget extends Composite{
private static GRSFManageWidgetServiceAsync service = GWT.create(GRSFManageWidgetService.class);
- private static ManageProductWidgetUiBinder uiBinder = GWT
- .create(ManageProductWidgetUiBinder.class);
+ private static ManageProductWidgetUiBinder uiBinder = GWT.create(ManageProductWidgetUiBinder.class);
interface ManageProductWidgetUiBinder extends
UiBinder {
@@ -174,7 +173,7 @@ public class ManageProductWidget extends Composite{
// show modal
manageProductModal.addStyleName("management-metadata-modal-style");
- // manageProductModal.getElement().getStyle().setWidth(60, Unit.PCT);
+ manageProductModal.getElement().getStyle().setWidth(60, Unit.PCT);
manageProductModal.show();
// async request to fetch the product
@@ -331,11 +330,19 @@ public class ManageProductWidget extends Composite{
if(similarRecordPanel != null)
bean.setSimilarGrsfRecords(similarRecordPanel.getSimilarRecords());
else
- bean.setSimilarGrsfRecords(new ArrayList(0));
+ bean.setSimilarGrsfRecords(new ArrayList(0));
// add the suggested ones, if any
bean.getSimilarGrsfRecords().addAll(suggestedMergesPanel.getSimilarRecords());
+ // set the merge operator on the bean if there is at least one merge to be done
+ for(SimilarGRSFRecord sR: bean.getSimilarGrsfRecords()){
+ if(sR.isSuggestedMerge()){
+ bean.setMergesInvolved(true);
+ break;
+ }
+ }
+
// set new values
bean.setAnnotation(new HTML(annotationArea.getText().trim()).getText());
diff --git a/src/main/java/org/gcube/datacatalogue/grsf_manage_widget/client/view/ManageRevertOperationWidget.java b/src/main/java/org/gcube/datacatalogue/grsf_manage_widget/client/view/ManageRevertOperationWidget.java
new file mode 100644
index 0000000..50f28a6
--- /dev/null
+++ b/src/main/java/org/gcube/datacatalogue/grsf_manage_widget/client/view/ManageRevertOperationWidget.java
@@ -0,0 +1,198 @@
+package org.gcube.datacatalogue.grsf_manage_widget.client.view;
+
+import java.util.Date;
+
+import org.gcube.datacatalogue.grsf_manage_widget.client.GRSFManageWidgetService;
+import org.gcube.datacatalogue.grsf_manage_widget.client.GRSFManageWidgetServiceAsync;
+import org.gcube.datacatalogue.grsf_manage_widget.shared.RevertableOperationInfo;
+
+import com.github.gwtbootstrap.client.ui.AlertBlock;
+import com.github.gwtbootstrap.client.ui.Button;
+import com.github.gwtbootstrap.client.ui.Icon;
+import com.github.gwtbootstrap.client.ui.Image;
+import com.github.gwtbootstrap.client.ui.Modal;
+import com.github.gwtbootstrap.client.ui.TextArea;
+import com.github.gwtbootstrap.client.ui.constants.AlertType;
+import com.google.gwt.core.client.GWT;
+import com.google.gwt.dom.client.Style.FontWeight;
+import com.google.gwt.dom.client.Style.Unit;
+import com.google.gwt.event.dom.client.ClickEvent;
+import com.google.gwt.event.shared.HandlerManager;
+import com.google.gwt.i18n.client.DateTimeFormat;
+import com.google.gwt.uibinder.client.UiBinder;
+import com.google.gwt.uibinder.client.UiField;
+import com.google.gwt.uibinder.client.UiHandler;
+import com.google.gwt.user.client.rpc.AsyncCallback;
+import com.google.gwt.user.client.ui.Anchor;
+import com.google.gwt.user.client.ui.Composite;
+import com.google.gwt.user.client.ui.VerticalPanel;
+import com.google.gwt.user.client.ui.Widget;
+
+public class ManageRevertOperationWidget extends Composite {
+
+ private static ManageRevertOperationWidgetUiBinder uiBinder = GWT
+ .create(ManageRevertOperationWidgetUiBinder.class);
+
+ interface ManageRevertOperationWidgetUiBinder extends
+ UiBinder {
+ }
+
+ public ManageRevertOperationWidget() {
+ initWidget(uiBinder.createAndBindUi(this));
+ }
+
+ @UiField
+ VerticalPanel moreInfoAboutOperation;
+
+ @UiField
+ Modal revertOperationModal;
+
+ @UiField
+ Icon loaderIcon;
+
+ @UiField
+ Image loadingImage;
+
+ @UiField
+ AlertBlock infoBlock;
+
+ @UiField
+ Button revertButton;
+
+ @UiField
+ Button cancelButton;
+
+ @UiField
+ TextArea requestAuthor;
+
+ @UiField
+ TextArea requestTypeBox;
+
+ @UiField
+ TextArea requestRecordUUID;
+
+ @UiField
+ TextArea requestTimestamp;
+
+ private static GRSFManageWidgetServiceAsync service = GWT.create(GRSFManageWidgetService.class);
+
+ private HandlerManager eventBus;
+ public static final String LOADING_IMAGE_URL = GWT.getModuleBaseURL() + "../images/loader.gif";
+ private RevertableOperationInfo revertableOperation = null;
+
+ public ManageRevertOperationWidget(String encryptedUrlOperation, HandlerManager eventBus) {
+ initWidget(uiBinder.createAndBindUi(this));
+
+ this.eventBus = eventBus;
+
+ GWT.log("Encrypted url is " + encryptedUrlOperation);
+
+ if(encryptedUrlOperation == null || encryptedUrlOperation.isEmpty())
+ return;
+
+ // start loader service
+ loadingImage.setUrl(LOADING_IMAGE_URL);
+ loadingImage.setVisible(true);
+
+ // show modal
+ revertOperationModal.addStyleName("management-metadata-modal-style");
+ // revertOperationModal.getElement().getStyle().setWidth(60, Unit.PCT);
+ revertOperationModal.show();
+
+ // async request to fetch the product
+ loadModalContent(encryptedUrlOperation);
+
+ }
+
+ /**
+ * Validate the parameters of the url and ask the editor/reviewer what he/she wants to do.
+ * @author Costantino Perciante at ISTI-CNR (costantino.perciante@isti.cnr.it)
+ * @param encryptedUrlOperation
+ */
+ private void loadModalContent(String encryptedUrlOperation) {
+
+ revertButton.setEnabled(false);
+
+ service.validateRevertOperation(encryptedUrlOperation, new AsyncCallback() {
+
+ @Override
+ public void onSuccess(RevertableOperationInfo result) {
+
+ loadingImage.setVisible(false);
+
+ if(result != null){
+ revertableOperation = result;
+ String dateString = DateTimeFormat.getFormat("HH:mm:ss").format(new Date(revertableOperation.getTimestamp()));
+ requestAuthor.setText(revertableOperation.getAdmin());
+ requestTypeBox.setText(revertableOperation.getOperation().toString().toUpperCase());
+ requestRecordUUID.setText(revertableOperation.getUuid());
+ requestTimestamp.setText(dateString);
+
+ Anchor viewRecord = new Anchor();
+ viewRecord.setText("View Record");
+ viewRecord.getElement().getStyle().setFontWeight(FontWeight.BOLD);
+ viewRecord.setHref(revertableOperation.getRecordUrl());
+ viewRecord.setTarget("_blank");
+ viewRecord.getElement().getStyle().setMarginBottom(20, Unit.PX);
+ moreInfoAboutOperation.add(viewRecord);
+ moreInfoAboutOperation.setVisible(true);
+ revertButton.setEnabled(true);
+ }else
+ displayError(null);
+ }
+
+ @Override
+ public void onFailure(Throwable caught) {
+ loadingImage.setVisible(false);
+ displayError(caught);
+ }
+ });
+
+ }
+
+ @UiHandler("revertButton")
+ void onSaveButton(ClickEvent ce){
+
+ loaderIcon.setVisible(true);
+ revertButton.setEnabled(false);
+
+ service.performRevertOperation(revertableOperation, new AsyncCallback() {
+
+ @Override
+ public void onSuccess(Boolean result) {
+
+ revertButton.setEnabled(true);
+ loaderIcon.setVisible(false);
+
+ if(!result)
+ displayError(null);
+ else{
+ infoBlock.setVisible(true);
+ infoBlock.setType(AlertType.SUCCESS);
+ infoBlock.setText("The request has been processed successfully!");
+ revertButton.removeFromParent();
+ cancelButton.removeFromParent();
+ }
+ }
+
+ @Override
+ public void onFailure(Throwable caught) {
+ displayError(caught);
+ }
+ });
+
+ }
+
+ @UiHandler("cancelButton")
+ void onCancelButton(ClickEvent ce){
+ revertOperationModal.hide();
+ }
+
+ protected void displayError(Throwable caught) {
+
+ infoBlock.setVisible(true);
+ infoBlock.setType(AlertType.ERROR);
+ infoBlock.setText("Unable to perform this operation. " + (caught != null ? "Error was " + caught : ""));
+
+ }
+}
diff --git a/src/main/java/org/gcube/datacatalogue/grsf_manage_widget/client/view/ManageRevertOperationWidget.ui.xml b/src/main/java/org/gcube/datacatalogue/grsf_manage_widget/client/view/ManageRevertOperationWidget.ui.xml
new file mode 100644
index 0000000..ea0db89
--- /dev/null
+++ b/src/main/java/org/gcube/datacatalogue/grsf_manage_widget/client/view/ManageRevertOperationWidget.ui.xml
@@ -0,0 +1,87 @@
+
+
+
+ .loader-image {
+ display: block;
+ margin: auto auto;
+ }
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Request Type:
+
+
+
+
+
+
+
+
+ Request was performed by:
+
+
+
+
+
+
+
+
+ Request was performed on Record:
+
+
+
+
+
+
+
+
+ Request was performed at:
+
+
+
+
+
+
+
+
+
+
+
+
+ Cancel
+ Revert
+
+
+
+
\ No newline at end of file
diff --git a/src/main/java/org/gcube/datacatalogue/grsf_manage_widget/client/view/subwidgets/ConnectToWidget.java b/src/main/java/org/gcube/datacatalogue/grsf_manage_widget/client/view/subwidgets/ConnectToWidget.java
index bf12942..508a2a1 100644
--- a/src/main/java/org/gcube/datacatalogue/grsf_manage_widget/client/view/subwidgets/ConnectToWidget.java
+++ b/src/main/java/org/gcube/datacatalogue/grsf_manage_widget/client/view/subwidgets/ConnectToWidget.java
@@ -234,7 +234,7 @@ public class ConnectToWidget extends Composite{
}
});
box.setWidth("512px");
- box.setPlaceholder("Insert the Identifier (UUID) of the record to connect and press ENTER");
+ box.setPlaceholder("Copy and Paste the Identifier (UUID) of the record to connect here");
vpLeft.add(semanticIdentifier);
vpLeft.add(box);
@@ -276,6 +276,9 @@ public class ConnectToWidget extends Composite{
protected void validateUUID(final TextBox box, final ConnectedBean c, final Icon icon, final Anchor view, final String acceptedDomain) {
+ if(!box.isEnabled())
+ return;
+
final String currentText = box.getText().trim();
c.setKnowledgeBaseId(null);
c.setConnect(false);
@@ -306,7 +309,6 @@ public class ConnectToWidget extends Composite{
public void onSuccess(String result) {
icon.setSpin(false);
-
if(result != null){
c.setKnowledgeBaseId(currentText);
c.setConnect(true);
@@ -314,15 +316,15 @@ public class ConnectToWidget extends Composite{
icon.setTitle("Accepted");
view.setHref(result);
view.setVisible(true);
+ box.setEnabled(false);
}
else{
icon.setType(IconType.BAN_CIRCLE);
icon.setTitle("Not a valid UUID");
view.setVisible(false);
+ box.setEnabled(true);
}
-
icon.setVisible(true);
- box.setEnabled(true);
}
@Override
diff --git a/src/main/java/org/gcube/datacatalogue/grsf_manage_widget/client/view/subwidgets/SuggestMerges.java b/src/main/java/org/gcube/datacatalogue/grsf_manage_widget/client/view/subwidgets/SuggestMerges.java
index fa8696f..118ee2e 100644
--- a/src/main/java/org/gcube/datacatalogue/grsf_manage_widget/client/view/subwidgets/SuggestMerges.java
+++ b/src/main/java/org/gcube/datacatalogue/grsf_manage_widget/client/view/subwidgets/SuggestMerges.java
@@ -14,8 +14,6 @@ import com.github.gwtbootstrap.client.ui.TextBox;
import com.github.gwtbootstrap.client.ui.constants.ButtonType;
import com.github.gwtbootstrap.client.ui.constants.IconType;
import com.google.gwt.core.client.GWT;
-import com.google.gwt.core.client.Scheduler;
-import com.google.gwt.core.client.Scheduler.ScheduledCommand;
import com.google.gwt.dom.client.Style.Float;
import com.google.gwt.dom.client.Style.FontWeight;
import com.google.gwt.dom.client.Style.Unit;
@@ -112,21 +110,14 @@ public class SuggestMerges extends Composite {
// add a couple of handlers
box.addKeyPressHandler(new KeyPressHandler() {
-
+
@Override
public void onKeyPress(KeyPressEvent event) {
- GWT.log("onKeyPress " + event.getNativeEvent().getKeyCode());
- Scheduler.get().scheduleDeferred(new ScheduledCommand() {
-
- @Override
- public void execute() {
- box.setFocus(false);
- }
- });
+ validateUUID(box, s, icon, view);
+
}
});
box.addChangeHandler(new ChangeHandler() {
-
@Override
public void onChange(ChangeEvent event) {
GWT.log("onChange");
@@ -190,6 +181,9 @@ public class SuggestMerges extends Composite {
*/
protected void validateUUID(final TextBox box, final SimilarGRSFRecord s, final Icon icon, final Anchor view) {
+ if(!box.isEnabled())
+ return;
+
final String currentText = box.getText().trim();
s.setKnowledgeBaseId(null);
s.setSuggestedMerge(false);
@@ -199,7 +193,7 @@ public class SuggestMerges extends Composite {
if(currentText == null || currentText.isEmpty())
return;
-
+
if(!currentText.matches(REGEX_UUID)){
icon.setType(IconType.BAN_CIRCLE);
icon.setTitle("Not a valid UUID");
@@ -220,7 +214,7 @@ public class SuggestMerges extends Composite {
@Override
public void onSuccess(String result) {
icon.setSpin(false);
-
+
if(result != null){
s.setKnowledgeBaseId(currentText);
s.setSuggestedMerge(true);
@@ -228,14 +222,15 @@ public class SuggestMerges extends Composite {
icon.setTitle("Accepted");
view.setHref(result);
view.setVisible(true);
+ box.setEnabled(false);
}
else{
icon.setType(IconType.BAN_CIRCLE);
icon.setTitle("Not a valid UUID");
view.setVisible(false);
+ box.setEnabled(true);
}
icon.setVisible(true);
- box.setEnabled(true);
}
@Override
diff --git a/src/main/java/org/gcube/datacatalogue/grsf_manage_widget/server/manage/GRSFNotificationService.java b/src/main/java/org/gcube/datacatalogue/grsf_manage_widget/server/manage/GRSFNotificationService.java
index 795f4a1..8ea85b0 100644
--- a/src/main/java/org/gcube/datacatalogue/grsf_manage_widget/server/manage/GRSFNotificationService.java
+++ b/src/main/java/org/gcube/datacatalogue/grsf_manage_widget/server/manage/GRSFNotificationService.java
@@ -21,6 +21,8 @@ import org.gcube.datacatalogue.common.enums.Status;
import org.gcube.datacatalogue.grsf_manage_widget.client.GRSFManageWidgetService;
import org.gcube.datacatalogue.grsf_manage_widget.shared.ConnectedBean;
import org.gcube.datacatalogue.grsf_manage_widget.shared.ManageProductBean;
+import org.gcube.datacatalogue.grsf_manage_widget.shared.RevertableOperationInfo;
+import org.gcube.datacatalogue.grsf_manage_widget.shared.RevertableOperations;
import org.gcube.datacatalogue.grsf_manage_widget.shared.SimilarGRSFRecord;
import org.gcube.datacatalogue.grsf_manage_widget.shared.SourceRecord;
import org.gcube.datacatalogue.grsf_manage_widget.shared.ex.NoGRSFRecordException;
@@ -372,7 +374,16 @@ public class GRSFNotificationService extends RemoteServiceServlet implements GRS
}
@Override
- public void validateRevertOperation(String encryptedUrl) throws Exception {
+ public RevertableOperationInfo validateRevertOperation(String encryptedUrl) throws Exception {
+
+ if(!Utils.isIntoPortal()){
+ String baseUrl = "url of the record here";
+ String fullName = "Andrea Rossi";
+ String uuid = UUID.randomUUID().toString();
+ String adminInUrl = "costantino.perciante";
+ long timestamp = System.currentTimeMillis() - 1000 * ((long)(Math.random() * 10 * 60 * 60));
+ return new RevertableOperationInfo(baseUrl, fullName, uuid, adminInUrl, timestamp, RevertableOperations.MERGE);
+ }
PortalContext pContext = PortalContext.getConfiguration();
String context = Utils.getScopeFromClientUrl(getThreadLocalRequest());
@@ -402,35 +413,69 @@ public class GRSFNotificationService extends RemoteServiceServlet implements GRS
if(!isValidTimestamp)
throw new Exception("This operation can no longer be reverted (link expired)!");
-
- String keyPerContext = UtilMethods.concatenateSessionKeyScope(Constants.GRSF_UPDATER_SERVICE, context);
- String baseUrl = (String)getThreadLocalRequest().getSession().getAttribute(keyPerContext);
- if(baseUrl == null || baseUrl.isEmpty()){
- baseUrl = GRSFUpdaterServiceClient.discoverEndPoint(context);
- getThreadLocalRequest().getSession().setAttribute(keyPerContext, baseUrl);
- }
-
- if(baseUrl == null || baseUrl.isEmpty())
- throw new Exception("Unable to discover grsf-updater service!");
+
+ DataCatalogue catalogue = getCatalogue(context);
+ String recordUrl = catalogue.getDataset(uuid, catalogue.getApiKeyFromUsername(username)).getExtrasAsHashMap().get(Constants.ITEM_URL_FIELD);
// check if it is a reviewer, than he can do what he wants (no matter the admin)
- try(CloseableHttpClient httpClient = HttpClientBuilder.create().build();){
- if(isReviewer){
- GRSFUpdaterServiceClient.revertOperation(httpClient, baseUrl, fullName, uuid);
- }else{
+ if(isReviewer){
+ return new RevertableOperationInfo(recordUrl,
+ fullName, uuid, adminInUrl, decryptedUrl.getTimestamp(), decryptedUrl.getOperation());
+ }else{
- if(!username.equals(adminInUrl))
- throw new Exception("You are not the editor allowed to perform this operation!");
- else
- GRSFUpdaterServiceClient.revertOperation(httpClient, baseUrl, fullName, uuid);
- }
- }catch(Exception e){
- logger.error("Unable to update this Item ", e);
- throw e;
+ if(!username.equals(adminInUrl))
+ throw new Exception("You are not the editor allowed to perform this operation!");
+ else
+ return new RevertableOperationInfo(recordUrl,
+ fullName, uuid, adminInUrl, decryptedUrl.getTimestamp(), decryptedUrl.getOperation());
}
}
+ @Override
+ public Boolean performRevertOperation(RevertableOperationInfo rInfo)
+ throws Exception {
+
+ if(!Utils.isIntoPortal()){
+ // random result
+ boolean toReturn = Math.random() > 0.5;
+
+ if(toReturn){
+
+ boolean throwException = Math.random() > 0.5;
+ if(throwException)
+ throw new Exception("Unable to execute request for XYZ");
+
+ }
+ return toReturn;
+ }
+
+ HttpServletRequest threadRequest = getThreadLocalRequest();
+ String context = Utils.getScopeFromClientUrl(threadRequest);
+ String token = SecurityTokenProvider.instance.get();
+
+ try(CloseableHttpClient httpClient = HttpClientBuilder.create().build();){
+
+ String keyPerContext = UtilMethods.concatenateSessionKeyScope(Constants.GRSF_UPDATER_SERVICE, context);
+ String baseUrl = (String)getThreadLocalRequest().getSession().getAttribute(keyPerContext);
+ if(baseUrl == null || baseUrl.isEmpty()){
+ baseUrl = GRSFUpdaterServiceClient.discoverEndPoint(context);
+ getThreadLocalRequest().getSession().setAttribute(keyPerContext, baseUrl);
+ }
+
+ if(baseUrl == null || baseUrl.isEmpty())
+ throw new Exception("Unable to discover grsf-updater service!");
+
+ Utils.revertOperation(httpClient, baseUrl, rInfo, token, context, PortalContext.getConfiguration().getCurrentGroupId(threadRequest));
+
+ }
+ catch(Exception e){
+ logger.error("Unable to revert operation ", e);
+ throw e;
+ }
+ return true;
+ }
+
/**
* Check if the current user is an editor
* @param username
@@ -484,21 +529,21 @@ public class GRSFNotificationService extends RemoteServiceServlet implements GRS
@Override
public String checkIdentifierExistsInDomain(String id,
String domain) throws Exception {
-
+
if(!Utils.isIntoPortal()){
-
+
boolean throwException = Math.random() > 0.5;
-
+
// simulate some delay...
Thread.sleep(2500);
-
+
if(throwException)
throw new Exception("The suggested record is not a GRSF record");
-
+
return "http://data.d4science.org/catalogue/grsf_admin/" + id;
-
+
}
-
+
String scopePerCurrentUrl = Utils.getScopeFromClientUrl(getThreadLocalRequest());
DataCatalogue catalogue = getCatalogue(scopePerCurrentUrl);
String username = Utils.getCurrentUser(getThreadLocalRequest()).getUsername();
diff --git a/src/main/java/org/gcube/datacatalogue/grsf_manage_widget/server/manage/RevertOperationUrl.java b/src/main/java/org/gcube/datacatalogue/grsf_manage_widget/server/manage/RevertOperationUrl.java
index 4b20e0b..33013aa 100644
--- a/src/main/java/org/gcube/datacatalogue/grsf_manage_widget/server/manage/RevertOperationUrl.java
+++ b/src/main/java/org/gcube/datacatalogue/grsf_manage_widget/server/manage/RevertOperationUrl.java
@@ -4,6 +4,7 @@ import java.net.URLDecoder;
import java.net.URLEncoder;
import org.gcube.common.encryption.StringEncrypter;
+import org.gcube.datacatalogue.grsf_manage_widget.shared.RevertableOperations;
import org.gcube.portlets.user.urlshortener.UrlShortener;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -24,32 +25,11 @@ public class RevertOperationUrl {
public static final String OPERATION_REVERT_QUERY_PARAM = "operation_revert";
public static final long TTL = 1000 * 60 * 60 * 24;
- /**
- * For now only Merge can be reverted
- * @author Costantino Perciante at ISTI-CNR (costantino.perciante@isti.cnr.it)
- */
- public enum Operation {
-
- MERGE("merge");
- // DISSECT("dissect");
- private String name;
-
- private Operation(String name) {
- this.name = name;
- }
-
- @Override
- public String toString() {
- return name;
- }
-
- }
-
private String baseUrl;
private String admin;
private long timestamp;
private String uuid;
- private Operation operation;
+ private RevertableOperations operation;
/**
* @param admin
@@ -59,7 +39,7 @@ public class RevertOperationUrl {
* @param op
*/
public RevertOperationUrl(String baseUrl, String admin, long timestamp, String uuid,
- Operation operation) {
+ RevertableOperations operation) {
super();
this.baseUrl = baseUrl;
this.admin = admin;
@@ -133,7 +113,7 @@ public class RevertOperationUrl {
this.uuid = value;
break;
case OPERATION_REVERT_QUERY_PARAM:
- this.operation = Operation.valueOf(value);
+ this.operation = RevertableOperations.valueOf(value);
break;
default:
break;
@@ -189,12 +169,12 @@ public class RevertOperationUrl {
}
- public Operation getOperation() {
+ public RevertableOperations getOperation() {
return operation;
}
- public void setOperation(Operation operation) {
+ public void setOperation(RevertableOperations operation) {
this.operation = operation;
}
diff --git a/src/main/java/org/gcube/datacatalogue/grsf_manage_widget/server/manage/SocialCommunications.java b/src/main/java/org/gcube/datacatalogue/grsf_manage_widget/server/manage/SocialCommunications.java
index 293d602..87cfe3f 100644
--- a/src/main/java/org/gcube/datacatalogue/grsf_manage_widget/server/manage/SocialCommunications.java
+++ b/src/main/java/org/gcube/datacatalogue/grsf_manage_widget/server/manage/SocialCommunications.java
@@ -15,8 +15,9 @@ import org.gcube.common.resources.gcore.GCoreEndpoint;
import org.gcube.common.scope.api.ScopeProvider;
import org.gcube.datacatalogue.ckanutillibrary.server.DataCatalogue;
import org.gcube.datacatalogue.common.Constants;
-import org.gcube.datacatalogue.grsf_manage_widget.server.manage.RevertOperationUrl.Operation;
import org.gcube.datacatalogue.grsf_manage_widget.shared.ManageProductBean;
+import org.gcube.datacatalogue.grsf_manage_widget.shared.RevertableOperationInfo;
+import org.gcube.datacatalogue.grsf_manage_widget.shared.RevertableOperations;
import org.gcube.resources.discovery.client.api.DiscoveryClient;
import org.gcube.resources.discovery.client.queries.api.SimpleQuery;
import org.gcube.vomanagement.usermanagement.RoleManager;
@@ -78,6 +79,13 @@ public class SocialCommunications {
private static final String REVERT_LINK_PIECE = "
The request involves a merge operation. You can reject the merge by exploiting this link LINK in the following 24 hours.";
+ // on revert operation
+ private static final String EMAIL_REVIEWER_REVERT = "Dear GRSF Reviewer,"
+ + "
a revert operation (undo merge) has been requested on record RECORD_URL, by USERNAME.";
+
+ private static final String EMAIL_EDITOR_REVERT = "Dear USER_FULLNAME,"
+ +"
a revert operation (undo merge) has been requested on this RECORD_URL you managed.";
+
/**
*
* @param context
@@ -128,9 +136,10 @@ public class SocialCommunications {
* @param fullName
* @param hashtags
* @param enablePostNotification
+ * @throws Exception
*/
@SuppressWarnings("unchecked")
- public static void writeProductPost(ManageProductBean bean, String username, String fullName, String report, boolean enablePostNotification){
+ public static void writeProductPost(ManageProductBean bean, String username, String fullName, String report, boolean enablePostNotification) throws Exception{
// discover service endpoint for the social networking library
String currentScope = ScopeProvider.instance.get();
@@ -142,6 +151,7 @@ public class SocialCommunications {
if(basePath == null){
logger.error("Unable to write a post because there is no social networking service available");
+ throw new Exception("Unable to discover the social networking service");
}else{
@@ -222,7 +232,7 @@ public class SocialCommunications {
* @throws Exceptio
*/
@SuppressWarnings("unchecked")
- public static void sendEmailAdministrators(
+ public static void sendEmailAdministratorsOnMerge(
ManageProductBean bean,
DataCatalogue catalogue,
String username,
@@ -248,7 +258,7 @@ public class SocialCommunications {
logger.info("List of " + Constants.GRSF_CATALOGUE_REVIEWER_ROLE + " is " + reviewers);
// build the url that allows to revert the operation
- Operation operation = Operation.MERGE;
+ RevertableOperations operation = RevertableOperations.MERGE;
// discover service endpoint for the social networking library
String currentScope = ScopeProvider.instance.get();
@@ -260,6 +270,7 @@ public class SocialCommunications {
if(basePath == null){
logger.error("Unable to write a post because there is no social networking service available");
+ throw new Exception("Unable to discover the social networking service");
}else{
@@ -352,7 +363,137 @@ public class SocialCommunications {
}
}catch(Exception e){
- logger.error("Failed to create a post", e);
+ logger.error("Failed to send messages", e);
+ }
+ }
+
+ }
+
+ public static void sendEmailAdministratorsOnOperationReverted(
+ RevertableOperationInfo rInfo,
+ long groupId
+ ) throws Exception {
+
+ // get the list of GRSF Reviewers to alert them as well
+ RoleManager roleManager = new LiferayRoleManager();
+ List teamRoles = roleManager.listTeamsByGroup(groupId);
+ List reviewers = new ArrayList<>();
+ UserManager um = new LiferayUserManager();
+
+ for(GCubeTeam tr: teamRoles){
+ if(tr.getTeamName().equals(Constants.GRSF_CATALOGUE_REVIEWER_ROLE))
+ reviewers.add(um.getUserById(tr.getUserId()).getUsername());
+ }
+
+ // if the user is a reviewer, then send the email just once
+ reviewers.remove(rInfo.getAdmin());
+
+ logger.info("List of " + Constants.GRSF_CATALOGUE_REVIEWER_ROLE + " is " + reviewers);
+
+ // build the url that allows to revert the operation
+ RevertableOperations operation = RevertableOperations.MERGE;
+
+ // discover service endpoint for the social networking library
+ String currentScope = ScopeProvider.instance.get();
+ String tokenUser = SecurityTokenProvider.instance.get();
+
+ logger.info("Current scope for writeProductPost is " + currentScope + " and token is " + tokenUser.substring(0, 10) + "***************");
+ String basePath = getBaseUrlSocialService(currentScope);
+
+ if(basePath == null){
+
+ logger.error("Unable to write a post because there is no social networking service available");
+ throw new Exception("Unable to discover the social networking service");
+
+ }else{
+
+ basePath = basePath.endsWith("/") ? basePath : basePath + "/";
+
+ try(CloseableHttpClient client = HttpClientBuilder.create().setRedirectStrategy(new LaxRedirectStrategy()).build();){
+
+ // ask token application
+ HttpPost postRequest = new HttpPost(basePath + SOCIAL_SERVICE_APPLICATION_TOKEN + "?gcube-token=" + tokenUser);
+ StringEntity input = new StringEntity("{\"app_id\":\"" + APPLICATION_ID_CATALOGUE_MANAGER + "\"}");
+ input.setContentType(MEDIATYPE_JSON);
+ postRequest.setEntity(input);
+ HttpResponse response = client.execute(postRequest);
+
+ logger.debug("Url is " + basePath + SOCIAL_SERVICE_APPLICATION_TOKEN + "?gcube-token=" + tokenUser);
+
+ if (response.getStatusLine().getStatusCode() != 201) {
+ throw new RuntimeException("Failed to retrieve application token : HTTP error code : "
+ + response.getStatusLine().getStatusCode());
+ }else{
+
+ Map mapResponseGeneratedToken = getResponseEntityAsJSON(response);
+ boolean successGeneratedToken = (boolean)mapResponseGeneratedToken.get("success");
+ if(!successGeneratedToken){
+
+ throw new RuntimeException("Failed to generate the token for the application!"
+ + " Error message is " + mapResponseGeneratedToken.get("message"));
+
+ }else{
+
+ String applicationToken = (String)mapResponseGeneratedToken.get("result");
+
+ String messageToEditor = EMAIL_EDITOR_REVERT.replace("RECORD_URL", rInfo.getRecordUrl()).replace("USER_FULLNAME", rInfo.getFullName());
+ String messageToReviewer = EMAIL_REVIEWER_REVERT.replace("USER_FULLNAME", rInfo.getFullName()).replace("RECORD_URL", rInfo.getRecordUrl());
+ String subject = "Revert merge request on GRSF Record";
+
+ // send email to the editor
+ logger.info("The message that is going to be send to the editor is\n" + messageToEditor);
+ postRequest = new HttpPost(basePath + SOCIAL_SEND_EMAIL + "?gcube-token=" + applicationToken);
+ JSONObject reqMessage = new JSONObject();
+ reqMessage.put("subject", subject);
+ reqMessage.put("body", messageToEditor);
+ JSONArray recipients = new JSONArray();
+ JSONObject recipient = new JSONObject();
+ recipient.put("id", rInfo.getAdmin());
+ recipients.add(recipient);
+ reqMessage.put("recipients", recipients);
+ input = new StringEntity(reqMessage.toJSONString());
+ input.setContentType(MEDIATYPE_JSON);
+ postRequest.setEntity(input);
+ response = client.execute(postRequest);
+
+ Map mapResponseWritePost = getResponseEntityAsJSON(response);
+
+ if (response.getStatusLine().getStatusCode() != 201){
+ logger.error("Failed to send message to editor : HTTP error code : "
+ + response.getStatusLine().getStatusCode() + mapResponseWritePost.get("message"));
+ }
+
+ // send email to the reviewers
+ logger.info("The message that is going to be send to the reviewers is\n" + messageToReviewer);
+ postRequest = new HttpPost(basePath + SOCIAL_SEND_EMAIL + "?gcube-token=" + applicationToken);
+ reqMessage = new JSONObject();
+ reqMessage.put("subject", subject);
+ reqMessage.put("body", messageToReviewer);
+ recipients = new JSONArray();
+ for(String reviewer: reviewers){
+ JSONObject recip = new JSONObject();
+ recip.put("id", reviewer);
+ recipients.add(recip);
+ }
+ reqMessage.put("recipients", recipients);
+ input = new StringEntity(reqMessage.toJSONString());
+ input.setContentType(MEDIATYPE_JSON);
+ postRequest.setEntity(input);
+ response = client.execute(postRequest);
+ mapResponseWritePost = getResponseEntityAsJSON(response);
+
+ if (response.getStatusLine().getStatusCode() != 201){
+ logger.error("Failed to send message to editor : HTTP error code : "
+ + response.getStatusLine().getStatusCode() + mapResponseWritePost.get("message"));
+ }
+
+ }
+
+ }
+
+ }catch(Exception e){
+ logger.error("Failed to send messages", e);
+ throw new Exception(e);
}
}
@@ -364,7 +505,7 @@ public class SocialCommunications {
* @return
* @throws Exception
*/
- public static String getEncodedUrlManage(Operation operation, String administrator, long timestamp, String uuid, HttpServletRequest httpServletRequest) throws Exception{
+ public static String getEncodedUrlManage(RevertableOperations operation, String administrator, long timestamp, String uuid, HttpServletRequest httpServletRequest) throws Exception{
String clientUrl = Utils.getCurrentClientUrl(httpServletRequest).split("\\?")[0]; // ignore other parameters
RevertOperationUrl operationUrl = new RevertOperationUrl(clientUrl, administrator, timestamp, uuid, operation);
String shortUrl = operationUrl.getShortUrl();
diff --git a/src/main/java/org/gcube/datacatalogue/grsf_manage_widget/server/manage/Utils.java b/src/main/java/org/gcube/datacatalogue/grsf_manage_widget/server/manage/Utils.java
index 2f4c523..1815514 100644
--- a/src/main/java/org/gcube/datacatalogue/grsf_manage_widget/server/manage/Utils.java
+++ b/src/main/java/org/gcube/datacatalogue/grsf_manage_widget/server/manage/Utils.java
@@ -28,6 +28,7 @@ import org.gcube.datacatalogue.common.enums.Product_Type;
import org.gcube.datacatalogue.common.enums.Status;
import org.gcube.datacatalogue.grsf_manage_widget.shared.ConnectedBean;
import org.gcube.datacatalogue.grsf_manage_widget.shared.ManageProductBean;
+import org.gcube.datacatalogue.grsf_manage_widget.shared.RevertableOperationInfo;
import org.gcube.datacatalogue.grsf_manage_widget.shared.SimilarGRSFRecord;
import org.gcube.resources.discovery.client.api.DiscoveryClient;
import org.gcube.resources.discovery.client.queries.api.Query;
@@ -212,7 +213,7 @@ public class Utils {
try{
// send email to Editors and Reviewers
- SocialCommunications.sendEmailAdministrators(bean, catalogue, username, fullName, groupId, httpServletRequest, bean.isMergesInvolved());
+ SocialCommunications.sendEmailAdministratorsOnMerge(bean, catalogue, username, fullName, groupId, httpServletRequest, bean.isMergesInvolved());
// create a post about the operation
SocialCommunications.writeProductPost(bean, username, fullName, report, true);
@@ -232,6 +233,40 @@ public class Utils {
}
return null;
}
+
+ /**
+ * Revert operation and alert admins/vre users
+ * @author Costantino Perciante at ISTI-CNR (costantino.perciante@isti.cnr.it)
+ * @param httpClient
+ * @param baseUrl
+ * @param fullName
+ * @param uuid
+ */
+ public static void revertOperation(CloseableHttpClient httpClient, String baseUrl,
+ RevertableOperationInfo rInfo, String token, String context, long groupId) throws Exception{
+
+ GRSFUpdaterServiceClient.revertOperation(httpClient, baseUrl, rInfo.getFullName(), rInfo.getUuid());
+
+ // manage interactions through a separated thread but set there security token and context (and then reset them)
+ new Thread(()->{
+
+ ScopeProvider.instance.set(context);
+ SecurityTokenProvider.instance.set(token);
+ try{
+
+ // create a post about the operation
+ SocialCommunications.sendEmailAdministratorsOnOperationReverted(rInfo, groupId);
+
+ }catch(Exception e){
+ logger.error("Something failed while alerting editors/reviewers", e);
+ }finally{
+ ScopeProvider.instance.reset();
+ SecurityTokenProvider.instance.reset();
+ }
+
+ }).start();
+
+ }
/**
* Update the status of the involved records to "to be merged"
@@ -396,33 +431,6 @@ public class Utils {
return null;
}
- // /**
- // * Get extra information to show in the management panel, if any
- // * @param extrasAsPairs
- // */
- // public static void getExtrasToShow(List extrasAsPairs, ){
- //
- // Set extrasToShow = getLookedUpExtrasKeys();
- // if(extrasToShow != null && !extrasToShow.isEmpty()){
- // Map extrasKeyValuePair = new HashMap();
- // = product.getExtras();
- // for (CkanPair ckanPair : extrasAsPairs) {
- // String key = ckanPair.getKey();
- // String value = ckanPair.getValue();
- //
- // if(extrasToShow.contains(key)){
- // String currentValueInMap = extrasKeyValuePair.get(key);
- // if(currentValueInMap == null)
- // currentValueInMap = value;
- // else
- // currentValueInMap += ", " + value;
- // extrasKeyValuePair.put(key, currentValueInMap);
- // }
- // }
- // toReturn.setExtrasIfAvailable(extrasKeyValuePair);
- // }
- //
- // }
/**
* Get a {@link SimilarGRSFRecord} from a json string
diff --git a/src/main/java/org/gcube/datacatalogue/grsf_manage_widget/shared/RevertableOperationInfo.java b/src/main/java/org/gcube/datacatalogue/grsf_manage_widget/shared/RevertableOperationInfo.java
new file mode 100644
index 0000000..5e61c9e
--- /dev/null
+++ b/src/main/java/org/gcube/datacatalogue/grsf_manage_widget/shared/RevertableOperationInfo.java
@@ -0,0 +1,79 @@
+package org.gcube.datacatalogue.grsf_manage_widget.shared;
+
+import java.io.Serializable;
+
+public class RevertableOperationInfo implements Serializable{
+
+ private static final long serialVersionUID = 5274434342849474800L;
+ private String recordUrl;
+ private String fullName;
+ private String uuid;
+ private String admin;
+ private long timestamp;
+ private RevertableOperations operation;
+
+ public RevertableOperationInfo() {
+ super();
+ }
+
+ public RevertableOperationInfo(String recordUrl, String fullName,
+ String uuid, String admin, long timestamp, RevertableOperations operation) {
+ super();
+ this.recordUrl = recordUrl;
+ this.fullName = fullName;
+ this.uuid = uuid;
+ this.admin = admin;
+ this.timestamp = timestamp;
+ this.operation = operation;
+ }
+
+ public String getAdmin() {
+ return admin;
+ }
+
+ public void setAdmin(String admin) {
+ this.admin = admin;
+ }
+
+ public long getTimestamp() {
+ return timestamp;
+ }
+
+ public void setTimestamp(long timestamp) {
+ this.timestamp = timestamp;
+ }
+
+ public RevertableOperations getOperation() {
+ return operation;
+ }
+
+ public void setOperation(RevertableOperations operation) {
+ this.operation = operation;
+ }
+
+ public String getRecordUrl() {
+ return recordUrl;
+ }
+ public void setRecordUrl(String recordUrl) {
+ this.recordUrl = recordUrl;
+ }
+ public String getFullName() {
+ return fullName;
+ }
+ public void setFullName(String fullName) {
+ this.fullName = fullName;
+ }
+ public String getUuid() {
+ return uuid;
+ }
+ public void setUuid(String uuid) {
+ this.uuid = uuid;
+ }
+
+ @Override
+ public String toString() {
+ return "RevertableOperationInfo [recordUrl=" + recordUrl + ", fullName="
+ + fullName + ", uuid=" + uuid + ", admin=" + admin
+ + ", timestamp=" + timestamp + ", operation=" + operation + "]";
+ }
+}
diff --git a/src/main/java/org/gcube/datacatalogue/grsf_manage_widget/shared/RevertableOperations.java b/src/main/java/org/gcube/datacatalogue/grsf_manage_widget/shared/RevertableOperations.java
new file mode 100644
index 0000000..ca0b7a8
--- /dev/null
+++ b/src/main/java/org/gcube/datacatalogue/grsf_manage_widget/shared/RevertableOperations.java
@@ -0,0 +1,22 @@
+package org.gcube.datacatalogue.grsf_manage_widget.shared;
+
+/**
+ * For now only Merge can be reverted
+ * @author Costantino Perciante at ISTI-CNR (costantino.perciante@isti.cnr.it)
+ */
+public enum RevertableOperations {
+
+ MERGE("merge");
+ // DISSECT("dissect");
+ private String name;
+
+ private RevertableOperations(String name) {
+ this.name = name;
+ }
+
+ @Override
+ public String toString() {
+ return name;
+ }
+
+}
\ No newline at end of file