diff --git a/.classpath b/.classpath index 4c6e618..6d95e3a 100644 --- a/.classpath +++ b/.classpath @@ -20,7 +20,6 @@ - @@ -28,7 +27,7 @@ - + diff --git a/.settings/org.eclipse.jdt.core.prefs b/.settings/org.eclipse.jdt.core.prefs index 13b3428..6b5aebc 100644 --- a/.settings/org.eclipse.jdt.core.prefs +++ b/.settings/org.eclipse.jdt.core.prefs @@ -1,13 +1,13 @@ eclipse.preferences.version=1 org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled org.eclipse.jdt.core.compiler.codegen.methodParameters=do not generate -org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.8 +org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.7 org.eclipse.jdt.core.compiler.codegen.unusedLocal=preserve -org.eclipse.jdt.core.compiler.compliance=1.8 +org.eclipse.jdt.core.compiler.compliance=1.7 org.eclipse.jdt.core.compiler.debug.lineNumber=generate org.eclipse.jdt.core.compiler.debug.localVariable=generate org.eclipse.jdt.core.compiler.debug.sourceFile=generate org.eclipse.jdt.core.compiler.problem.assertIdentifier=error org.eclipse.jdt.core.compiler.problem.enumIdentifier=error org.eclipse.jdt.core.compiler.problem.forbiddenReference=warning -org.eclipse.jdt.core.compiler.source=1.8 +org.eclipse.jdt.core.compiler.source=1.7 diff --git a/.settings/org.eclipse.wst.common.component b/.settings/org.eclipse.wst.common.component index 9f49a51..38e18cb 100644 --- a/.settings/org.eclipse.wst.common.component +++ b/.settings/org.eclipse.wst.common.component @@ -4,10 +4,6 @@ - - - uses - diff --git a/.settings/org.eclipse.wst.common.project.facet.core.xml b/.settings/org.eclipse.wst.common.project.facet.core.xml index 3f2dd2b..4045d87 100644 --- a/.settings/org.eclipse.wst.common.project.facet.core.xml +++ b/.settings/org.eclipse.wst.common.project.facet.core.xml @@ -3,5 +3,5 @@ - + diff --git a/pom.xml b/pom.xml index 701bd10..aac6184 100644 --- a/pom.xml +++ b/pom.xml @@ -117,18 +117,18 @@ org.gcube.core common-scope-maps - compile + provided org.gcube.core common-encryption - compile + provided org.gcube.common authorization-client - compile + provided 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 2ffa8ab..51c7be9 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,8 +12,7 @@ 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 ManageRevertOperationWidget( - // "random-url-here", 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 85c5e7d..1289679 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 @@ -26,7 +26,7 @@ public interface GRSFManageWidgetService extends RemoteService { /** * Notify product update */ - String notifyProductUpdate(ManageProductBean bean) throws Exception; + void notifyProductUpdate(ManageProductBean bean) throws Exception; /** * Identifier of the record (UUID) 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 8d402e8..5da42f3 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 @@ -15,7 +15,7 @@ import com.google.gwt.user.client.rpc.AsyncCallback; public interface GRSFManageWidgetServiceAsync { void notifyProductUpdate(ManageProductBean bean, - AsyncCallback callback); + AsyncCallback callback); void getProductBeanById(String identifier, 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 95f8730..a073471 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 @@ -2,7 +2,9 @@ package org.gcube.datacatalogue.grsf_manage_widget.client.view; import java.util.ArrayList; import java.util.Arrays; +import java.util.HashSet; import java.util.List; +import java.util.Set; import org.gcube.datacatalogue.common.enums.Status; import org.gcube.datacatalogue.grsf_manage_widget.client.GRSFManageWidgetService; @@ -12,6 +14,8 @@ import org.gcube.datacatalogue.grsf_manage_widget.client.view.subwidgets.Connect import org.gcube.datacatalogue.grsf_manage_widget.client.view.subwidgets.SimilarGRSFRecordWidget; import org.gcube.datacatalogue.grsf_manage_widget.client.view.subwidgets.SourceWidget; import org.gcube.datacatalogue.grsf_manage_widget.client.view.subwidgets.SuggestMerges; +import org.gcube.datacatalogue.grsf_manage_widget.shared.ConnectedBean; +import org.gcube.datacatalogue.grsf_manage_widget.shared.HashTagsOnUpdate; import org.gcube.datacatalogue.grsf_manage_widget.shared.ManageProductBean; import org.gcube.datacatalogue.grsf_manage_widget.shared.SimilarGRSFRecord; import org.gcube.datacatalogue.grsf_manage_widget.shared.SourceRecord; @@ -31,7 +35,6 @@ import com.github.gwtbootstrap.client.ui.TextBox; import com.github.gwtbootstrap.client.ui.constants.AlertType; 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.ClickEvent; import com.google.gwt.event.shared.HandlerManager; import com.google.gwt.uibinder.client.UiBinder; @@ -173,7 +176,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 @@ -222,7 +225,7 @@ public class ManageProductWidget extends Composite{ infoBlock.setVisible(false); // top: more or less fixed information - GRSFNameTexBox.setText(bean.getGrsfName()); + GRSFNameTexBox.setText(bean.getTitle()); shortNameTextBox.setText(bean.getShortName()); semanticIdentifierTextBox.setText(bean.getSemanticIdentifier()); productGrsfType.setText(bean.getGrsfType()); @@ -244,7 +247,7 @@ public class ManageProductWidget extends Composite{ similarGRSFRecordGroup.setVisible(false); // further suggested merges - suggestedMergesPanel = new SuggestMerges(service); + suggestedMergesPanel = new SuggestMerges(service, bean.getDomain()); panelForFurtherMerges.add(suggestedMergesPanel); // prepare "connect" panel @@ -309,11 +312,19 @@ public class ManageProductWidget extends Composite{ @UiHandler("confirmButton") void onSaveButton(ClickEvent ce){ + String report = ""; + Set hashtags = new HashSet<>(); + // if the status has not be changed ... - if(listBoxStatus.getSelectedIndex() <= 0) + if(listBoxStatus.getSelectedIndex() <= 0){ bean.setNewStatus(bean.getCurrentStatus()); - else + report = "-The Status is unchanged"; + } + else{ bean.setNewStatus(Status.fromString(listBoxStatus.getSelectedItemText())); + report = "-The Status has been changed to " + bean.getNewStatus().getOrigName(); + hashtags.add(bean.getNewStatus().getOrigName()); + } manageProductModal.setCloseVisible(false); cancelButton.setEnabled(false); @@ -323,9 +334,26 @@ public class ManageProductWidget extends Composite{ // get short name bean.setShortNameUpdated(shortNameTextBox.getText()); + if(bean.getShortName() != bean.getShortNameUpdated()){ + report += "\n- The GRSF Short Name has been changed to '" + bean.getShortNameUpdated() + "'"; + hashtags.add(HashTagsOnUpdate.SHORTNAME_UPDATED.getString()); + } + // evaluate the connections and the actions on them bean.setConnections(connectWidget.getConnectList()); + // add the connections for the report + if(!bean.getConnections().isEmpty()){ + report += "\n- Suggested connections:"; + hashtags.add(HashTagsOnUpdate.CONNECT.getString()); + for(ConnectedBean cb: bean.getConnections()){ + if(cb.isRemove()) + report += "\n\t - remove connection with record " + cb.getKnowledgeBaseId() + ";"; + else if(cb.isConnect()) + report += "\n\t - add connection with record " + cb.getKnowledgeBaseId() + ";"; + } + } + // update similar records and to connect if(similarRecordPanel != null) bean.setSimilarGrsfRecords(similarRecordPanel.getSimilarRecords()); @@ -337,33 +365,51 @@ public class ManageProductWidget extends Composite{ // set the merge operator on the bean if there is at least one merge to be done for(SimilarGRSFRecord sR: bean.getSimilarGrsfRecords()){ + report += "\n- Suggested merges:"; if(sR.isSuggestedMerge()){ bean.setMergesInvolved(true); - break; + report += "\n\t - merge the current record with record " + sR.getKnowledgeBaseId() + ";"; + } + + if(bean.isMergesInvolved()){ + report += "\n- The update involves a merge operation."; + hashtags.add(HashTagsOnUpdate.MERGE.getString()); } } - + // set new values bean.setAnnotation(new HTML(annotationArea.getText().trim()).getText()); - // traceability flag - bean.setTraceabilityFlag(traceabilityFlag.getValue()); + if(bean.getAnnotation() != null && !bean.getAnnotation().isEmpty()) + report += "\n- Annotation message is: " + bean.getAnnotation(); - service.notifyProductUpdate(bean, new AsyncCallback() { + // traceability flag + Boolean traceabilityNewValue = traceabilityFlag.getValue(); + boolean currentTraceabilitFlag = bean.isTraceabilityFlag(); + if(!traceabilityNewValue.equals(currentTraceabilitFlag)){ + report += "\n- Traceability flag has been changed to: " + traceabilityNewValue; + + if(traceabilityNewValue) + hashtags.add(HashTagsOnUpdate.TRACEABILITY_FLAG_SET.getString()); + else + hashtags.add(HashTagsOnUpdate.TRACEABILITY_FLAG_UNSET.getString()); + } + + bean.setTraceabilityFlag(traceabilityNewValue); + + // set the report + bean.setReport(report); + + GWT.log("Report is:\n" + report); + + service.notifyProductUpdate(bean, new AsyncCallback() { @Override - public void onSuccess(String result) { - - if(result == null){ - showInfo(STATUS_UPDATE_SUCCESS, AlertType.SUCCESS); - confirmButton.removeFromParent(); - formUpdate.setVisible(false); - - }else{ - showInfo(STATUS_UPDATE_ERROR + "(" + result + ")", AlertType.ERROR); - confirmButton.setEnabled(true); - } + public void onSuccess(Void v) { + showInfo(STATUS_UPDATE_SUCCESS, AlertType.SUCCESS); + confirmButton.removeFromParent(); + formUpdate.setVisible(false); manageProductModal.setCloseVisible(true); cancelButton.setEnabled(true); loaderIcon.setVisible(false); 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 index bbbe1f2..592cbf0 100644 --- 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 @@ -17,7 +17,6 @@ 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; @@ -76,15 +75,12 @@ public class ManageRevertOperationWidget extends Composite { 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) { + public ManageRevertOperationWidget(String encryptedUrlOperation) { initWidget(uiBinder.createAndBindUi(this)); - this.eventBus = eventBus; - GWT.log("Encrypted url is " + encryptedUrlOperation); if(encryptedUrlOperation == null || encryptedUrlOperation.isEmpty()) @@ -99,6 +95,8 @@ public class ManageRevertOperationWidget extends Composite { // revertOperationModal.getElement().getStyle().setWidth(60, Unit.PCT); revertOperationModal.show(); + moreInfoAboutOperation.getElement().getStyle().setMarginBottom(50, Unit.PX); + // async request to fetch the product loadModalContent(encryptedUrlOperation); @@ -106,7 +104,6 @@ public class ManageRevertOperationWidget extends Composite { /** * 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) { @@ -123,7 +120,7 @@ public class ManageRevertOperationWidget extends Composite { if(result != null){ revertableOperation = result; String dateString = DateTimeFormat.getFormat("MM/dd/yyyy HH:mm:ss").format(new Date(revertableOperation.getTimestamp())); - requestAuthor.setText(revertableOperation.getAdmin()); + requestAuthor.setText(revertableOperation.getFullNameOriginalAdmin() + "(" + revertableOperation.getUserNameOriginalAdmin() + ")"); requestTypeBox.setText(revertableOperation.getOperation().toString().toUpperCase()); requestRecordUUID.setText(revertableOperation.getUuid()); requestTimestamp.setText(dateString); @@ -133,7 +130,7 @@ public class ManageRevertOperationWidget extends Composite { 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); 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 index ea0db89..cb96584 100644 --- 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 @@ -16,10 +16,6 @@ - - - @@ -75,6 +71,10 @@ + + + { } @@ -92,9 +83,9 @@ public class ConnectToWidget extends Composite{ } // manage the button for manual suggestion - final String acceptedDomain = bean.getGrsfDomain().equalsIgnoreCase(Product_Type.STOCK.getOrigName()) ? + final String acceptedDomain = bean.getDomain().equalsIgnoreCase(Product_Type.STOCK.getOrigName()) ? Product_Type.FISHERY.getOrigName() : Product_Type.STOCK.getOrigName(); - suggestRecord.setTitle("Connect this " + bean.getGrsfDomain() + " record to a " + acceptedDomain + " record "); + suggestRecord.setTitle("Connect this " + bean.getDomain() + " record to a " + acceptedDomain + " record "); suggestRecord.setText("Add Connection"); suggestRecord.setType(ButtonType.LINK); suggestRecord.getElement().getStyle().setFontWeight(FontWeight.BOLD); @@ -190,54 +181,44 @@ public class ConnectToWidget extends Composite{ private Tuple buildWidgetConnect(final ConnectedBean cb, final String acceptedDomain){ VerticalPanel main = new VerticalPanel(); - main.setWidth("100%"); HorizontalPanel hp = new HorizontalPanel(); + main.setWidth("100%"); hp.setWidth("100%"); VerticalPanel vpLeft = new VerticalPanel(); vpLeft.getElement().getStyle().setMarginLeft(15, Unit.PX); vpLeft.setWidth("80%"); - Paragraph semanticIdentifier = new Paragraph("UUID:"); - final TextBox box = new TextBox(); - final Icon icon = new Icon(IconType.OK_SIGN); - icon.setVisible(false); - icon.getElement().getStyle().setMarginLeft(10, Unit.PX); - icon.getElement().getStyle().setMarginTop(5, Unit.PX); + Paragraph identifier = new Paragraph("Record UUID:"); + + // view link final Anchor view = new Anchor(); view.setText("View"); view.setTitle("Click to inspect the record"); view.setTarget("_blank"); view.getElement().getStyle().setFontWeight(FontWeight.BOLD); view.setVisible(false); - - // 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); - } - }); - } - }); - box.addChangeHandler(new ChangeHandler() { - - @Override - public void onChange(ChangeEvent event) { - GWT.log("onChange"); - validateUUID(box, cb, icon, view, acceptedDomain); - } - }); + + // a textbox with a validate button on the right side + AppendButton uuidAndValidateButton = new AppendButton(); + final Button validateUUIDButton = new Button("Validate"); + validateUUIDButton.setEnabled(false); + final PasteAwareTextBox box = new PasteAwareTextBox(validateUUIDButton); box.setWidth("512px"); - box.setPlaceholder("Copy and Paste the Identifier (UUID) of the record to connect here"); - - vpLeft.add(semanticIdentifier); - vpLeft.add(box); + box.setPlaceholder("Copy and Paste the Identifier (UUID) of the record to connect here, then validate"); + validateUUIDButton.addClickHandler(new ClickHandler() { + + @Override + public void onClick(ClickEvent event) { + + validateUUID(box, cb, view, validateUUIDButton, acceptedDomain); + + } + }); + uuidAndValidateButton.add(box); + uuidAndValidateButton.add(validateUUIDButton); + vpLeft.add(identifier); + vpLeft.add(uuidAndValidateButton); + vpLeft.add(view); VerticalPanel vpRight = new VerticalPanel(); vpRight.setWidth("20%"); @@ -274,66 +255,53 @@ public class ConnectToWidget extends Composite{ return new Tuple(cb, main, box); } - protected void validateUUID(final TextBox box, final ConnectedBean c, final Icon icon, final Anchor view, final String acceptedDomain) { - - if(!box.isEnabled()) - return; + protected void validateUUID(final TextBox box, final ConnectedBean c, final Anchor view, final Button validateUUIDButton, final String acceptedDomain) { + validateUUIDButton.setText("Validating..."); + validateUUIDButton.setEnabled(false); + box.setEnabled(false); + view.setVisible(false); + final String currentText = box.getText().trim(); c.setKnowledgeBaseId(null); c.setConnect(false); - icon.setVisible(false); - view.setVisible(false); - icon.setSpin(false); - - if(currentText == null || currentText.isEmpty()) - return; - - if(!currentText.matches(REGEX_UUID)){ - icon.setType(IconType.BAN_CIRCLE); - icon.setTitle("Not a valid UUID"); - icon.setVisible(true); - view.setVisible(false); - return; - } // else check at server side if it exists - GWT.log("Text changed to " + currentText); - box.setEnabled(false); - icon.setIcon(IconType.ROTATE_RIGHT); - icon.setSpin(true); - service.checkIdentifierExistsInDomain(currentText, acceptedDomain, new AsyncCallback() { @Override public void onSuccess(String result) { - icon.setSpin(false); if(result != null){ c.setKnowledgeBaseId(currentText); c.setConnect(true); - icon.setType(IconType.OK_CIRCLE); - icon.setTitle("Accepted"); view.setHref(result); view.setVisible(true); box.setEnabled(false); + validateUUIDButton.setText("Accepted"); + validateUUIDButton.setType(ButtonType.SUCCESS); + validateUUIDButton.setEnabled(false); } else{ - icon.setType(IconType.BAN_CIRCLE); - icon.setTitle("Not a valid UUID"); view.setVisible(false); box.setEnabled(true); + validateUUIDButton.setText("Invalid"); + validateUUIDButton.setType(ButtonType.DANGER); + validateUUIDButton.setEnabled(true); } - icon.setVisible(true); + + } @Override public void onFailure(Throwable caught) { box.setEnabled(true); - icon.setSpin(false); view.setVisible(false); - icon.setType(IconType.BAN_CIRCLE); - icon.setTitle(caught.getMessage()); + validateUUIDButton.setText("Invalid"); + validateUUIDButton.setTitle("Error is " + caught); + validateUUIDButton.setType(ButtonType.DANGER); + view.setVisible(false); + box.setEnabled(true); } }); diff --git a/src/main/java/org/gcube/datacatalogue/grsf_manage_widget/client/view/subwidgets/PasteAwareTextBox.java b/src/main/java/org/gcube/datacatalogue/grsf_manage_widget/client/view/subwidgets/PasteAwareTextBox.java new file mode 100644 index 0000000..dea45bd --- /dev/null +++ b/src/main/java/org/gcube/datacatalogue/grsf_manage_widget/client/view/subwidgets/PasteAwareTextBox.java @@ -0,0 +1,62 @@ +package org.gcube.datacatalogue.grsf_manage_widget.client.view.subwidgets; + +import com.github.gwtbootstrap.client.ui.Button; +import com.github.gwtbootstrap.client.ui.TextBox; +import com.google.gwt.core.shared.GWT; +import com.google.gwt.user.client.Event; + +/** + * A paste aware textbox widget. + * @author Costantino Perciante at ISTI-CNR (costantino.perciante@isti.cnr.it) + */ +public class PasteAwareTextBox extends TextBox { + + private Button toBeEnabled; + private static final String REGEX_UUID = "[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}"; + + public PasteAwareTextBox(Button b) { + super(); + toBeEnabled = b; + sinkEvents(Event.ONPASTE); + sinkEvents(Event.ONCHANGE); + sinkEvents(Event.ONKEYPRESS); + } + + @Override + public void onBrowserEvent(Event event) { + super.onBrowserEvent(event); + switch (event.getTypeInt()) { + case Event.ONPASTE: + onEvent(getClipboardData(event)); + break; + case Event.ONCHANGE: + case Event.ONKEYPRESS: + onEvent(this.getText()); + break; + } + + } + + private void onEvent(String clipboardData) { + GWT.log("Current text is:" + clipboardData); + toBeEnabled.setEnabled(false); + if(clipboardData != null && !clipboardData.isEmpty()){ + final String currentText = clipboardData.trim(); + if(!currentText.matches(REGEX_UUID)) + return; + else + toBeEnabled.setEnabled(true); + } + + } + + /** + * In case of PASTE event + * @param event + * @return + */ + private static native String getClipboardData(Event event) /*-{ + return event.clipboardData.getData('text/plain'); + }-*/; + +} \ No newline at end of file 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 118ee2e..24d607c 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 @@ -7,22 +7,17 @@ import java.util.List; import org.gcube.datacatalogue.grsf_manage_widget.client.GRSFManageWidgetServiceAsync; import org.gcube.datacatalogue.grsf_manage_widget.shared.SimilarGRSFRecord; +import com.github.gwtbootstrap.client.ui.AppendButton; import com.github.gwtbootstrap.client.ui.Button; -import com.github.gwtbootstrap.client.ui.Icon; import com.github.gwtbootstrap.client.ui.Paragraph; 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.dom.client.Style.Float; import com.google.gwt.dom.client.Style.FontWeight; import com.google.gwt.dom.client.Style.Unit; -import com.google.gwt.event.dom.client.ChangeEvent; -import com.google.gwt.event.dom.client.ChangeHandler; import com.google.gwt.event.dom.client.ClickEvent; import com.google.gwt.event.dom.client.ClickHandler; -import com.google.gwt.event.dom.client.KeyPressEvent; -import com.google.gwt.event.dom.client.KeyPressHandler; import com.google.gwt.uibinder.client.UiBinder; import com.google.gwt.uibinder.client.UiField; import com.google.gwt.user.client.rpc.AsyncCallback; @@ -46,14 +41,12 @@ public class SuggestMerges extends Composite { private List extraSimilarRecordsList = new ArrayList(0); - private static final String REGEX_UUID = "[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}"; - interface SuggestMergesUiBinder extends UiBinder { } private GRSFManageWidgetServiceAsync service; - public SuggestMerges(GRSFManageWidgetServiceAsync service) { + public SuggestMerges(GRSFManageWidgetServiceAsync service, final String acceptedDomain) { initWidget(uiBinder.createAndBindUi(this)); this.service = service; @@ -69,7 +62,7 @@ public class SuggestMerges extends Composite { @Override public void onClick(ClickEvent arg0) { SimilarGRSFRecord s = new SimilarGRSFRecord(); - Widget w = buildWidgetForExtraSimilarRecord(s); + Widget w = buildWidgetForExtraSimilarRecord(s, acceptedDomain); extraSimilarRecordsList.add(new Tuple(s, w, null)); similarGrsfRecordsSuggestedPanel.add(w); } @@ -81,7 +74,7 @@ public class SuggestMerges extends Composite { * @param w the widget * @param s the similar record. */ - private Widget buildWidgetForExtraSimilarRecord(final SimilarGRSFRecord s){ + private Widget buildWidgetForExtraSimilarRecord(final SimilarGRSFRecord s, final String acceptedDomain){ VerticalPanel main = new VerticalPanel(); main.getElement().getStyle().setMarginTop(10, Unit.PX); @@ -95,12 +88,9 @@ public class SuggestMerges extends Composite { HorizontalPanel textBoxIconContainer = new HorizontalPanel(); textBoxIconContainer.setWidth("100%"); - Paragraph identifier = new Paragraph("UUID:"); - final TextBox box = new TextBox(); - final Icon icon = new Icon(IconType.OK_SIGN); - icon.setVisible(false); - icon.getElement().getStyle().setMarginLeft(10, Unit.PX); - icon.getElement().getStyle().setMarginTop(5, Unit.PX); + Paragraph identifier = new Paragraph("Record UUID:"); + + // view link final Anchor view = new Anchor(); view.setText("View"); view.setTitle("Click to inspect the record"); @@ -108,28 +98,26 @@ public class SuggestMerges extends Composite { view.getElement().getStyle().setFontWeight(FontWeight.BOLD); view.setVisible(false); - // add a couple of handlers - box.addKeyPressHandler(new KeyPressHandler() { - + // a textbox with a validate button on the right side + AppendButton uuidAndValidateButton = new AppendButton(); + final Button validateUUIDButton = new Button("Validate"); + validateUUIDButton.setEnabled(false); + final PasteAwareTextBox box = new PasteAwareTextBox(validateUUIDButton); + box.setWidth("512px"); + box.setPlaceholder("Copy and Paste the Identifier (UUID) of the record to merge, then validate"); + validateUUIDButton.addClickHandler(new ClickHandler() { + @Override - public void onKeyPress(KeyPressEvent event) { - validateUUID(box, s, icon, view); - + public void onClick(ClickEvent event) { + + validateUUID(box, s, view, validateUUIDButton, acceptedDomain); + } }); - box.addChangeHandler(new ChangeHandler() { - @Override - public void onChange(ChangeEvent event) { - GWT.log("onChange"); - validateUUID(box, s, icon, view); - } - }); - box.setWidth("511px"); - box.setPlaceholder("Insert the Identifier (UUID) of the record to be merged"); + uuidAndValidateButton.add(box); + uuidAndValidateButton.add(validateUUIDButton); vpLeft.add(identifier); - textBoxIconContainer.add(box); - textBoxIconContainer.add(icon); - vpLeft.add(textBoxIconContainer); + vpLeft.add(uuidAndValidateButton); vpLeft.add(view); // the right side @@ -179,68 +167,53 @@ public class SuggestMerges extends Composite { * @param icon * @param view */ - protected void validateUUID(final TextBox box, final SimilarGRSFRecord s, final Icon icon, final Anchor view) { + protected void validateUUID(final TextBox box, final SimilarGRSFRecord s, final Anchor view, final Button validateUUIDButton, final String acceptedDomain) { - if(!box.isEnabled()) - return; + validateUUIDButton.setText("Validating..."); + validateUUIDButton.setEnabled(false); + box.setEnabled(false); + view.setVisible(false); final String currentText = box.getText().trim(); s.setKnowledgeBaseId(null); s.setSuggestedMerge(false); - icon.setVisible(false); - view.setVisible(false); - icon.setSpin(false); - - if(currentText == null || currentText.isEmpty()) - return; - - if(!currentText.matches(REGEX_UUID)){ - icon.setType(IconType.BAN_CIRCLE); - icon.setTitle("Not a valid UUID"); - icon.setVisible(true); - return; - } // else check at server side if it exists - GWT.log("Text changed to " + currentText); - box.setEnabled(false); - icon.setVisible(true); - icon.setIcon(IconType.ROTATE_RIGHT); - icon.setTitle("Checking..."); - icon.setSpin(true); - - service.checkIdentifierExistsInDomain(currentText, s.getDomain(), new AsyncCallback() { + service.checkIdentifierExistsInDomain(currentText, acceptedDomain, new AsyncCallback() { @Override public void onSuccess(String result) { - icon.setSpin(false); if(result != null){ s.setKnowledgeBaseId(currentText); s.setSuggestedMerge(true); - icon.setType(IconType.OK_CIRCLE); - icon.setTitle("Accepted"); view.setHref(result); view.setVisible(true); box.setEnabled(false); + validateUUIDButton.setText("Accepted"); + validateUUIDButton.setType(ButtonType.SUCCESS); + validateUUIDButton.setEnabled(false); } else{ - icon.setType(IconType.BAN_CIRCLE); - icon.setTitle("Not a valid UUID"); view.setVisible(false); box.setEnabled(true); + validateUUIDButton.setText("Invalid"); + validateUUIDButton.setType(ButtonType.DANGER); + validateUUIDButton.setEnabled(true); } - icon.setVisible(true); + + } @Override public void onFailure(Throwable caught) { box.setEnabled(true); - icon.setSpin(false); - icon.setType(IconType.BAN_CIRCLE); - icon.setVisible(true); view.setVisible(false); - icon.setTitle(caught.getMessage()); + validateUUIDButton.setText("Invalid"); + validateUUIDButton.setTitle("Error is " + caught); + validateUUIDButton.setType(ButtonType.DANGER); + view.setVisible(false); + box.setEnabled(true); } }); @@ -257,7 +230,8 @@ public class SuggestMerges extends Composite { SimilarGRSFRecord similarRecord = ((SimilarGRSFRecord)p.getO()); if(similarRecord.getKnowledgeBaseId() == null || similarRecord.getKnowledgeBaseId().isEmpty()) continue; - toReturn.add((SimilarGRSFRecord) p.getO()); + similarRecord.setSuggestedMerge(true); + toReturn.add(similarRecord); } return toReturn; 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 8ea85b0..222e692 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 @@ -28,6 +28,7 @@ import org.gcube.datacatalogue.grsf_manage_widget.shared.SourceRecord; import org.gcube.datacatalogue.grsf_manage_widget.shared.ex.NoGRSFRecordException; import org.gcube.vomanagement.usermanagement.RoleManager; import org.gcube.vomanagement.usermanagement.impl.LiferayRoleManager; +import org.gcube.vomanagement.usermanagement.impl.LiferayUserManager; import org.gcube.vomanagement.usermanagement.model.GCubeTeam; import org.gcube.vomanagement.usermanagement.model.GCubeUser; @@ -85,6 +86,8 @@ public class GRSFNotificationService extends RemoteServiceServlet implements GRS if(!Utils.isIntoPortal()){ + Thread.sleep(2000); + toReturn = new ManageProductBean(); toReturn.setCatalogueIdentifier(UUID.randomUUID().toString()); List connectTo = new ArrayList<>(); @@ -112,12 +115,12 @@ public class GRSFNotificationService extends RemoteServiceServlet implements GRS "Fishery" )); toReturn.setSuggestedByKnowledgeBaseConnections(suggestionsForConnections); - toReturn.setGrsfDomain("Stock"); + toReturn.setDomain("Stock"); toReturn.setGrsfType("Assessment Unit"); - toReturn.setKnowledgeBaseIdentifier("91f1e413-dc9f-3b4e-b1c5-0e8560177253"); + toReturn.setKnowledgeBaseId("91f1e413-dc9f-3b4e-b1c5-0e8560177253"); toReturn.setShortName("Widow rockfish - US West Coast"); toReturn.setShortNameUpdated("Widow rockfish - US West Coast"); - toReturn.setGrsfName("sebastes entomelas FAO 77 FAO 67"); + toReturn.setTitle("sebastes entomelas FAO 77 FAO 67"); toReturn.setTraceabilityFlag(true); toReturn.setCurrentStatus(Status.Pending); toReturn.setSemanticIdentifier("asfis:WRO+fao:67;FAO"); @@ -217,6 +220,7 @@ public class GRSFNotificationService extends RemoteServiceServlet implements GRS Map> extrasWithoutNamespaces = Utils.replaceFieldsKey(extrasAsPairs, fieldsNamespacesMap); String catalogueIdentifier = record.getId(); + String description = record.getNotes(); Status status = Status.fromString(extrasWithoutNamespaces.get(Constants.STATUS_OF_THE_GRSF_RECORD_CUSTOM_KEY).get(0)); String uuidKB = extrasWithoutNamespaces.get(Constants.UUID_KB_CUSTOM_KEY).get(0); String grsfDomain = extrasWithoutNamespaces.get(Constants.DOMAIN_CUSTOM_KEY).get(0); @@ -295,7 +299,7 @@ public class GRSFNotificationService extends RemoteServiceServlet implements GRS // set the values toReturn = new ManageProductBean( semanticId, catalogueIdentifier, uuidKB, grsfType, - grsfDomain, grsfName, shortName, traceabilityFlag, + grsfDomain, shortName, description, grsfName,traceabilityFlag, status, recordUrl, sources, similarRecords, connectedBeans, suggestedConnectionsByKnowledgeBase); @@ -340,9 +344,13 @@ public class GRSFNotificationService extends RemoteServiceServlet implements GRS } @Override - public String notifyProductUpdate(ManageProductBean bean) throws Exception{ + public void notifyProductUpdate(ManageProductBean bean) throws Exception{ logger.info("Creating notification for the bean " + bean + " to send to the knowledge base"); + if(!Utils.isIntoPortal()){ + Thread.sleep(2500); + return; + } try{ String context = Utils.getScopeFromClientUrl(getThreadLocalRequest()); @@ -364,8 +372,8 @@ public class GRSFNotificationService extends RemoteServiceServlet implements GRS String sessionProductKey = ScopeProvider.instance.get() + bean.getCatalogueIdentifier(); threadRequest.getSession().removeAttribute(sessionProductKey); - return Utils.updateRecord(baseUrl, bean, catalogue, username, administratorFullName, threadRequest, - PortalContext.getConfiguration().getCurrentGroupId(threadRequest), context, token, bean.getReport()); + Utils.updateRecord(baseUrl, bean, catalogue, username, administratorFullName, threadRequest, + PortalContext.getConfiguration().getCurrentGroupId(threadRequest), context, token); }catch(Exception e){ logger.error("Unable to update the product", e); @@ -377,12 +385,22 @@ public class GRSFNotificationService extends RemoteServiceServlet implements GRS public RevertableOperationInfo validateRevertOperation(String encryptedUrl) throws Exception { if(!Utils.isIntoPortal()){ + Thread.sleep(2000); + + // random result + boolean throwException = Math.random() > 0.5; + + if(throwException) + throw new Exception("Unable to parse the inserted url"); + String baseUrl = "url of the record here"; String fullName = "Andrea Rossi"; String uuid = UUID.randomUUID().toString(); String adminInUrl = "costantino.perciante"; + String adminInUrlFullName = "Costantino Perciante"; long timestamp = System.currentTimeMillis() - 1000 * ((long)(Math.random() * 10 * 60 * 60)); - return new RevertableOperationInfo(baseUrl, fullName, uuid, adminInUrl, timestamp, RevertableOperations.MERGE); + return new RevertableOperationInfo( + baseUrl, fullName, uuid, adminInUrlFullName, adminInUrl, timestamp, RevertableOperations.MERGE); } PortalContext pContext = PortalContext.getConfiguration(); @@ -403,31 +421,32 @@ public class GRSFNotificationService extends RemoteServiceServlet implements GRS // decrypt the url RevertOperationUrl decryptedUrl = new RevertOperationUrl(encryptedUrl); - String adminInUrl = decryptedUrl.getAdmin(); + String userNameadminInUrl = decryptedUrl.getAdmin(); // this is the username + String fullNameadminInUrl = new LiferayUserManager().getUserByUsername(userNameadminInUrl).getFullname(); // this is the fullname String uuid = decryptedUrl.getUuid(); - logger.info("User " + username + " has requested to invert an operation on record with id " + uuid + " and admin in url is " + adminInUrl); + logger.info("User " + username + " has requested to invert an operation on record with id " + uuid + " and admin in url is " + userNameadminInUrl); // we need to check the timestamp (it has 24h validity) boolean isValidTimestamp = decryptedUrl.isTimestampValid(); if(!isValidTimestamp) throw new Exception("This operation can no longer be reverted (link expired)!"); - + 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) if(isReviewer){ return new RevertableOperationInfo(recordUrl, - fullName, uuid, adminInUrl, decryptedUrl.getTimestamp(), decryptedUrl.getOperation()); + fullName, uuid, fullNameadminInUrl, userNameadminInUrl, decryptedUrl.getTimestamp(), decryptedUrl.getOperation()); }else{ - if(!username.equals(adminInUrl)) + if(!username.equals(userNameadminInUrl)) 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()); + fullName, uuid, fullNameadminInUrl, userNameadminInUrl, decryptedUrl.getTimestamp(), decryptedUrl.getOperation()); } } @@ -435,25 +454,25 @@ public class GRSFNotificationService extends RemoteServiceServlet implements GRS @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); @@ -465,9 +484,10 @@ public class GRSFNotificationService extends RemoteServiceServlet implements GRS 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)); - + + Utils.revertOperation(httpClient, baseUrl, threadRequest, rInfo, token, context, + PortalContext.getConfiguration().getCurrentGroupId(threadRequest)); + } catch(Exception e){ logger.error("Unable to revert operation ", e); diff --git a/src/main/java/org/gcube/datacatalogue/grsf_manage_widget/server/manage/GRSFUpdaterServiceClient.java b/src/main/java/org/gcube/datacatalogue/grsf_manage_widget/server/manage/GRSFUpdaterServiceClient.java index 14e0048..c0a458c 100644 --- a/src/main/java/org/gcube/datacatalogue/grsf_manage_widget/server/manage/GRSFUpdaterServiceClient.java +++ b/src/main/java/org/gcube/datacatalogue/grsf_manage_widget/server/manage/GRSFUpdaterServiceClient.java @@ -98,7 +98,7 @@ public class GRSFUpdaterServiceClient { JSONObject obj = new JSONObject(); obj.put(Constants.ADMINISTRATOR_FULLNAME, fullName); obj.put(Constants.CATALOGUE_ID, bean.getCatalogueIdentifier()); - obj.put(Constants.KB_ID, bean.getKnowledgeBaseIdentifier()); + obj.put(Constants.KB_ID, bean.getKnowledgeBaseId()); obj.put(Constants.NEW_STATUS, bean.getNewStatus().toString().toLowerCase()); obj.put(Constants.OLD_STATUS, bean.getCurrentStatus().toString().toLowerCase()); obj.put(Constants.TRACEABILITY_FLAG, bean.isTraceabilityFlag()); @@ -122,9 +122,9 @@ public class GRSFUpdaterServiceClient { for(ConnectedBean c: connections){ JSONObject cc = new JSONObject(); if(c.isRemove() || (c.isConnect() && !c.isRemove())){ // do not send it if it needs to be unconnected but not removed - cc.put(Constants.SOURCE_KNOWLEDGE_BASE_ID, bean.getKnowledgeBaseIdentifier()); + cc.put(Constants.SOURCE_KNOWLEDGE_BASE_ID, bean.getKnowledgeBaseId()); cc.put(Constants.DEST_KNOWLEDGE_BASE_ID, c.getKnowledgeBaseId()); - cc.put(Constants.SOURCE_DOMAIN, bean.getGrsfDomain()); + cc.put(Constants.SOURCE_DOMAIN, bean.getDomain()); cc.put(Constants.CONNECTION_TO_REMOVE, c.isRemove()); } connectionsJson.add(cc); 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 87cfe3f..efc4afa 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 @@ -7,6 +7,7 @@ import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; +import java.util.Set; import javax.servlet.http.HttpServletRequest; @@ -15,6 +16,7 @@ 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.shared.HashTagsOnUpdate; 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; @@ -34,6 +36,7 @@ import com.fasterxml.jackson.databind.ObjectMapper; import eu.trentorise.opendata.jackan.internal.org.apache.http.HttpEntity; import eu.trentorise.opendata.jackan.internal.org.apache.http.HttpResponse; +import eu.trentorise.opendata.jackan.internal.org.apache.http.client.methods.CloseableHttpResponse; import eu.trentorise.opendata.jackan.internal.org.apache.http.client.methods.HttpPost; import eu.trentorise.opendata.jackan.internal.org.apache.http.entity.StringEntity; import eu.trentorise.opendata.jackan.internal.org.apache.http.impl.client.CloseableHttpClient; @@ -77,76 +80,79 @@ public class SocialCommunications { + "
your request for the record 'PRODUCT_TITLE' has been accepted." + "
It is available here LINK_RECORD."; + private static final String ADD_REPORT = "
This is a summary of the actions proposed:
REPORT_UPDATE
"; + + // revert link 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."; + + "
a revert operation (undo merge) has been requested on record RECORD_URL, by ADMIN_WHO_CHANGED."; - private static final String EMAIL_EDITOR_REVERT = "Dear USER_FULLNAME," - +"
a revert operation (undo merge) has been requested on this RECORD_URL you managed."; + private static final String EMAIL_EDITOR_REVERT = "Dear ORIGINAL_USER," + +"
a revert operation (undo merge) has been requested on this RECORD_URL you managed by ADMIN_WHO_CHANGED."; + + // post on revert + private static final String POST_ON_REVERT = "Dear members," + + "
a merge operation has been reverted on this record RECORD_URL by ADMIN_WHO_CHANGED. The merge was originally proposed by ORIGINAL_USER."; + + private static final String SOCIAL_NETWORKING_BASE_URL_SESSION_KEY = "SOCIAL_NETWORKING_SESSION_KEY"; /** * + * @param httpServletRequest * @param context * @return */ - private static String getBaseUrlSocialService(String context){ + public static String getBaseUrlSocialService(HttpServletRequest httpServletRequest){ - if(context == null || context.isEmpty()) - throw new IllegalArgumentException("A valid context is needed to discover the service"); + String context = ScopeProvider.instance.get(); - String oldContext = ScopeProvider.instance.get(); - ScopeProvider.instance.set(context); + String keyPerContext = SOCIAL_NETWORKING_BASE_URL_SESSION_KEY + context; + String basePath = (String) httpServletRequest.getSession().getAttribute(keyPerContext); - String basePath = null; - try{ + if(basePath == null){ + try{ - SimpleQuery query = queryFor(GCoreEndpoint.class); - query.addCondition(String.format("$resource/Profile/ServiceClass/text() eq '%s'",serviceClass)); - query.addCondition("$resource/Profile/DeploymentData/Status/text() eq 'ready'"); - query.addCondition(String.format("$resource/Profile/ServiceName/text() eq '%s'",serviceName)); - query.setResult("$resource/Profile/AccessPoint/RunningInstanceInterfaces//Endpoint[@EntryName/string() eq \""+resource+"\"]/text()"); + SimpleQuery query = queryFor(GCoreEndpoint.class); + query.addCondition(String.format("$resource/Profile/ServiceClass/text() eq '%s'",serviceClass)); + query.addCondition("$resource/Profile/DeploymentData/Status/text() eq 'ready'"); + query.addCondition(String.format("$resource/Profile/ServiceName/text() eq '%s'",serviceName)); + query.setResult("$resource/Profile/AccessPoint/RunningInstanceInterfaces//Endpoint[@EntryName/string() eq \""+resource+"\"]/text()"); - DiscoveryClient client = client(); - List endpoints = client.submit(query); - if (endpoints == null || endpoints.isEmpty()) - throw new Exception("Cannot retrieve the GCoreEndpoint serviceName: "+serviceName +", serviceClass: " +serviceClass +", in scope: "+context); + DiscoveryClient client = client(); + List endpoints = client.submit(query); + if (endpoints == null || endpoints.isEmpty()) + throw new Exception("Cannot retrieve the GCoreEndpoint serviceName: "+serviceName +", serviceClass: " +serviceClass +", in scope: "+context); - basePath = endpoints.get(0); - if(basePath==null) - throw new Exception("Endpoint:"+resource+", is null for serviceName: "+serviceName +", serviceClass: " +serviceClass +", in scope: "+context); + basePath = endpoints.get(0); + if(basePath==null) + throw new Exception("Endpoint:"+resource+", is null for serviceName: "+serviceName +", serviceClass: " +serviceClass +", in scope: "+context); - }catch(Exception e){ - logger.error("Unable to retrieve such service endpoint information!", e); - }finally{ - if(oldContext != null && !oldContext.equals(context)) - ScopeProvider.instance.set(oldContext); + httpServletRequest.getSession().setAttribute(keyPerContext, basePath); + + }catch(Exception e){ + logger.error("Unable to retrieve such service endpoint information!", e); + } } logger.info("Found base path " + basePath + " for the service"); return basePath; } /** - * Notify the users about the required changes. - * @param bean - * @param url - * @param username - * @param fullName - * @param hashtags - * @param enablePostNotification - * @throws Exception + * Require a proper application token for writing a post and send messages. + * @return + * @throws Exception */ @SuppressWarnings("unchecked") - public static void writeProductPost(ManageProductBean bean, String username, String fullName, String report, boolean enablePostNotification) throws Exception{ + private static String requireApplicationToken(String serviceUrl) throws Exception{ - // 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); + String basePath = serviceUrl; if(basePath == null){ @@ -161,7 +167,9 @@ public class SocialCommunications { // 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 + "\"}"); + JSONObject requestToken = new JSONObject(); + requestToken.put("app_id", APPLICATION_ID_CATALOGUE_MANAGER); + StringEntity input = new StringEntity(requestToken.toJSONString()); input.setContentType(MEDIATYPE_JSON); postRequest.setEntity(input); HttpResponse response = client.execute(postRequest); @@ -182,39 +190,144 @@ public class SocialCommunications { }else{ - String applicationToken = (String)mapResponseGeneratedToken.get("result"); + return (String)mapResponseGeneratedToken.get("result"); - // replace - String message = POST_MESSAGE.replace("PRODUCT_TITLE", bean.getGrsfName()).replace("PRODUCT_URL", bean.getRecordUrl()).replace("USER_FULLNAME", fullName); - - // evaluate hashtags from the report ... TODO - // if(hashtags != null && !hashtags.isEmpty()) - // for (String hashtag : hashtags) { - // String modifiedHashtag = hashtag.replaceAll(" ", "_").replace("_+", "_"); // no empty spaces allowed - // if(modifiedHashtag.endsWith("_")) - // modifiedHashtag = modifiedHashtag.substring(0, modifiedHashtag.length() - 1); - // message += " #" + modifiedHashtag; - // } - - logger.info("The post that is going to be written is -> " + message); - postRequest = new HttpPost(basePath + SOCIAL_SERVICE_WRITE_APPLICATION_POST + "?gcube-token=" + applicationToken); - JSONObject object = new JSONObject(); - object.put("text", message); - object.put("enable_notification", enablePostNotification); - input = new StringEntity(object.toJSONString()); - input.setContentType(MEDIATYPE_JSON); - postRequest.setEntity(input); - response = client.execute(postRequest); - - Map mapResponseWritePost = getResponseEntityAsJSON(response); - - if (response.getStatusLine().getStatusCode() != 201) - throw new RuntimeException("Failed to write application post : HTTP error code : " - + response.getStatusLine().getStatusCode() + mapResponseWritePost.get("message")); } } + }catch(Exception e){ + logger.error("Failed to create a post", e); + throw e; + } + } + } + /** + * Notify the users about the required changes. + * @param bean + * @param url + * @param username + * @param fullName + * @param hashtags + * @param enablePostNotification + * @throws Exception + */ + @SuppressWarnings("unchecked") + public static void writePostOnRevert(String serviceUrl, RevertableOperationInfo rInfo, boolean enablePostNotification) throws Exception{ + + // 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 = serviceUrl; + + 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();){ + + // require url + String applicationToken = requireApplicationToken(serviceUrl); + + // replace + String message = POST_ON_REVERT.replace("RECORD_URL", rInfo.getRecordUrl()).replace("ADMIN_WHO_CHANGED", rInfo.getFullNameCurrentAdmin()).replace("ORIGINAL_USER", rInfo.getFullNameOriginalAdmin()); + + // add hashtag + message +="

"; + message += " #" + HashTagsOnUpdate.REVERTED_MERGE.getString(); + + logger.info("The post that is going to be written is -> " + message); + HttpPost postRequest = new HttpPost(basePath + SOCIAL_SERVICE_WRITE_APPLICATION_POST + "?gcube-token=" + applicationToken); + JSONObject object = new JSONObject(); + object.put("text", message); + object.put("enable_notification", enablePostNotification); + StringEntity input = new StringEntity(object.toJSONString()); + input.setContentType(MEDIATYPE_JSON); + postRequest.setEntity(input); + CloseableHttpResponse response = client.execute(postRequest); + + Map mapResponseWritePost = getResponseEntityAsJSON(response); + + if (response.getStatusLine().getStatusCode() != 201) + throw new RuntimeException("Failed to write application post : HTTP error code : " + + response.getStatusLine().getStatusCode() + mapResponseWritePost.get("message")); + + }catch(Exception e){ + logger.error("Failed to create a post", e); + } + } + } + + /** + * Notify the users about the required changes. + * @param bean + * @param url + * @param username + * @param fullName + * @param hashtags + * @param enablePostNotification + * @throws Exception + */ + @SuppressWarnings("unchecked") + public static void writeProductPost(String serviceUrl, ManageProductBean bean, String username, String fullName, boolean enablePostNotification) throws Exception{ + + // 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 = serviceUrl; + + 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();){ + + // require url + String applicationToken = requireApplicationToken(serviceUrl); + + // replace + String message = POST_MESSAGE.replace("PRODUCT_TITLE", bean.getTitle()).replace("PRODUCT_URL", bean.getUrl()). + replace("USER_FULLNAME", fullName); + if(bean.getReport() != null) + message += ADD_REPORT.replace("REPORT_UPDATE", bean.getReport()); + + Set hashtags = bean.getHashtags(); + if(hashtags != null && !hashtags.isEmpty()){ + message +="

"; + for (String hashtag : hashtags) { + message += " #" + hashtag; + } + } + + logger.info("The post that is going to be written is -> " + message); + HttpPost postRequest = new HttpPost(basePath + SOCIAL_SERVICE_WRITE_APPLICATION_POST + "?gcube-token=" + applicationToken); + JSONObject object = new JSONObject(); + object.put("text", message); + object.put("enable_notification", enablePostNotification); + StringEntity input = new StringEntity(object.toJSONString()); + input.setContentType(MEDIATYPE_JSON); + postRequest.setEntity(input); + CloseableHttpResponse response = client.execute(postRequest); + + Map mapResponseWritePost = getResponseEntityAsJSON(response); + + if (response.getStatusLine().getStatusCode() != 201) + throw new RuntimeException("Failed to write application post : HTTP error code : " + + response.getStatusLine().getStatusCode() + mapResponseWritePost.get("message")); }catch(Exception e){ logger.error("Failed to create a post", e); } @@ -233,6 +346,7 @@ public class SocialCommunications { */ @SuppressWarnings("unchecked") public static void sendEmailAdministratorsOnMerge( + String serviceUrl, ManageProductBean bean, DataCatalogue catalogue, String username, @@ -265,7 +379,7 @@ public class SocialCommunications { String tokenUser = SecurityTokenProvider.instance.get(); logger.info("Current scope for writeProductPost is " + currentScope + " and token is " + tokenUser.substring(0, 10) + "***************"); - String basePath = getBaseUrlSocialService(currentScope); + String basePath = serviceUrl; if(basePath == null){ @@ -278,88 +392,69 @@ public class SocialCommunications { 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 + "\"}"); + /// require url + String applicationToken = requireApplicationToken(serviceUrl); + String revertUrl = getEncodedUrlManage(operation, username, System.currentTimeMillis(), bean.getKnowledgeBaseId(), httpServletRequest); + + String messageToEditor = (EMAIL_MESSAGE_EDITOR + + (isMergeInvolved? REVERT_LINK_PIECE : "")).replace("USER_FULLNAME", fullName).replace("PRODUCT_TITLE", bean.getTitle()). + replace("LINK_RECORD", bean.getUrl()).replace("LINK", revertUrl); + String messageToReviewer = (EMAIL_MESSAGE_REVIEWER+ + (isMergeInvolved? REVERT_LINK_PIECE : "")).replace("USER_FULLNAME", fullName).replace("PRODUCT_TITLE", bean.getTitle()). + replace("LINK_RECORD", bean.getUrl()).replace("LINK", revertUrl); + String subject = "Update request on GRSF Record"; + + // append report + if(bean.getReport() != null){ + messageToEditor += ADD_REPORT.replace("REPORT_UPDATE", bean.getReport()); + messageToReviewer += ADD_REPORT.replace("REPORT_UPDATE", bean.getReport()); + } + + // send email to the editor + logger.info("The message that is going to be send to the editor is\n" + messageToEditor); + HttpPost 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", username); + recipients.add(recipient); + reqMessage.put("recipients", recipients); + StringEntity input = new StringEntity(reqMessage.toJSONString()); input.setContentType(MEDIATYPE_JSON); postRequest.setEntity(input); - HttpResponse response = client.execute(postRequest); + CloseableHttpResponse response = client.execute(postRequest); - logger.debug("Url is " + basePath + SOCIAL_SERVICE_APPLICATION_TOKEN + "?gcube-token=" + tokenUser); + Map mapResponseWritePost = getResponseEntityAsJSON(response); - if (response.getStatusLine().getStatusCode() != 201) { - throw new RuntimeException("Failed to retrieve application token : HTTP error code : " - + response.getStatusLine().getStatusCode()); - }else{ + if (response.getStatusLine().getStatusCode() != 201){ + logger.error("Failed to send message to editor : HTTP error code : " + + response.getStatusLine().getStatusCode() + mapResponseWritePost.get("message")); + } - 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 revertUrl = getEncodedUrlManage(operation, username, System.currentTimeMillis(), bean.getKnowledgeBaseIdentifier(), httpServletRequest); - - String messageToEditor = (EMAIL_MESSAGE_EDITOR + - (isMergeInvolved? REVERT_LINK_PIECE : "")).replace("USER_FULLNAME", fullName).replace("PRODUCT_TITLE", bean.getGrsfName()).replace("LINK_RECORD", bean.getRecordUrl()).replace("LINK", revertUrl); - String messageToReviewer = (EMAIL_MESSAGE_REVIEWER+ - (isMergeInvolved? REVERT_LINK_PIECE : "")).replace("USER_FULLNAME", fullName).replace("PRODUCT_TITLE", bean.getGrsfName()).replace("LINK_RECORD", bean.getRecordUrl()).replace("LINK", revertUrl); - String subject = "Update 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", username); - 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")); - } - - } + // 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){ @@ -369,7 +464,9 @@ public class SocialCommunications { } + @SuppressWarnings("unchecked") public static void sendEmailAdministratorsOnOperationReverted( + String serviceUrl, RevertableOperationInfo rInfo, long groupId ) throws Exception { @@ -386,19 +483,16 @@ public class SocialCommunications { } // if the user is a reviewer, then send the email just once - reviewers.remove(rInfo.getAdmin()); + reviewers.remove(rInfo.getUserNameOriginalAdmin()); 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); + String basePath = serviceUrl; if(basePath == null){ @@ -411,84 +505,59 @@ public class SocialCommunications { 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 + "\"}"); + String applicationToken = requireApplicationToken(serviceUrl); + + String messageToEditor = EMAIL_EDITOR_REVERT.replace("RECORD_URL", rInfo.getRecordUrl()).replace("ORIGINAL_USER", rInfo.getFullNameOriginalAdmin()). + replace("ADMIN_WHO_CHANGED", rInfo.getFullNameCurrentAdmin()); + String messageToReviewer = EMAIL_REVIEWER_REVERT.replace("ADMIN_WHO_CHANGED", rInfo.getFullNameCurrentAdmin()).replace("RECORD_URL", rInfo.getRecordUrl()). + replace("ORIGINAL_USER", rInfo.getFullNameOriginalAdmin()); + 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); + HttpPost 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.getUserNameOriginalAdmin()); + recipients.add(recipient); + reqMessage.put("recipients", recipients); + StringEntity input = new StringEntity(reqMessage.toJSONString()); input.setContentType(MEDIATYPE_JSON); postRequest.setEntity(input); - HttpResponse response = client.execute(postRequest); + CloseableHttpResponse response = client.execute(postRequest); - logger.debug("Url is " + basePath + SOCIAL_SERVICE_APPLICATION_TOKEN + "?gcube-token=" + tokenUser); + Map mapResponseWritePost = getResponseEntityAsJSON(response); - if (response.getStatusLine().getStatusCode() != 201) { - throw new RuntimeException("Failed to retrieve application token : HTTP error code : " - + response.getStatusLine().getStatusCode()); - }else{ + if (response.getStatusLine().getStatusCode() != 201){ + logger.error("Failed to send message to editor : HTTP error code : " + + response.getStatusLine().getStatusCode() + mapResponseWritePost.get("message")); + } - 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")); - } - - } + // 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){ 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 1815514..db119dd 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 @@ -178,17 +178,16 @@ public class Utils { * @param catalogue * @return true on success, false otherwise */ - public static String updateRecord( + public static void updateRecord( String serviceUrl, - ManageProductBean bean, - DataCatalogue catalogue, - String username, - String fullName, - HttpServletRequest httpServletRequest, - long groupId, - String context, - String token, - String report) throws Exception{ + final ManageProductBean bean, + final DataCatalogue catalogue, + final String username, + final String fullName, + final HttpServletRequest httpServletRequest, + final long groupId, + final String context, + final String token) throws Exception{ if(serviceUrl == null) throw new IllegalArgumentException("GRSF Updater service url cannot be null"); @@ -205,35 +204,41 @@ public class Utils { // send update to the knowledge base GRSFUpdaterServiceClient.updateKB(httpClient, serviceUrl, bean, catalogue, username, fullName); + // require social networking url + final String baseUrlSocial = SocialCommunications.getBaseUrlSocialService(httpServletRequest); + // manage interactions through a separated thread but set there security token and context (and then reset them) - new Thread(()->{ + Thread t = new Thread(new Runnable() { - ScopeProvider.instance.set(context); - SecurityTokenProvider.instance.set(token); - try{ + @Override + public void run() { + ScopeProvider.instance.set(context); + SecurityTokenProvider.instance.set(token); + try{ - // send email to Editors and Reviewers - SocialCommunications.sendEmailAdministratorsOnMerge(bean, catalogue, username, fullName, groupId, httpServletRequest, bean.isMergesInvolved()); + // send email to Editors and Reviewers + SocialCommunications.sendEmailAdministratorsOnMerge(baseUrlSocial, bean, catalogue, username, fullName, + groupId, httpServletRequest, bean.isMergesInvolved()); - // create a post about the operation - SocialCommunications.writeProductPost(bean, username, fullName, report, true); + // create a post about the operation + SocialCommunications.writeProductPost(baseUrlSocial, bean, username, fullName, false); - }catch(Exception e){ - logger.error("Something failed while alerting editors/reviewers", e); - }finally{ - ScopeProvider.instance.reset(); - SecurityTokenProvider.instance.reset(); + }catch(Exception e){ + logger.error("Something failed while alerting editors/reviewers", e); + }finally{ + ScopeProvider.instance.reset(); + SecurityTokenProvider.instance.reset(); + } } - - }).start(); + }); + t.start(); }catch(Exception e){ logger.error("Unable to update this Item ", e); throw e; } - return null; } - + /** * Revert operation and alert admins/vre users * @author Costantino Perciante at ISTI-CNR (costantino.perciante@isti.cnr.it) @@ -242,30 +247,40 @@ public class Utils { * @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()); - + public static void revertOperation(CloseableHttpClient httpClient, String baseUrl, HttpServletRequest httpServletRequest, + final RevertableOperationInfo rInfo, final String token, final String context, final long groupId) throws Exception{ + + GRSFUpdaterServiceClient.revertOperation(httpClient, baseUrl, rInfo.getFullNameCurrentAdmin(), rInfo.getUuid()); + + // require social networking url + final String baseUrlSocial = SocialCommunications.getBaseUrlSocialService(httpServletRequest); + // manage interactions through a separated thread but set there security token and context (and then reset them) - new Thread(()->{ + Thread t = new Thread(new Runnable() { - ScopeProvider.instance.set(context); - SecurityTokenProvider.instance.set(token); - try{ + @Override + public void run() { + ScopeProvider.instance.set(context); + SecurityTokenProvider.instance.set(token); + try{ - // create a post about the operation - SocialCommunications.sendEmailAdministratorsOnOperationReverted(rInfo, groupId); + // write post about this + SocialCommunications.writePostOnRevert(baseUrlSocial, rInfo, true); - }catch(Exception e){ - logger.error("Something failed while alerting editors/reviewers", e); - }finally{ - ScopeProvider.instance.reset(); - SecurityTokenProvider.instance.reset(); + // alert who's involved + SocialCommunications.sendEmailAdministratorsOnOperationReverted(baseUrlSocial, rInfo, groupId); + + }catch(Exception e){ + logger.error("Something failed while alerting editors/reviewers", e); + }finally{ + ScopeProvider.instance.reset(); + SecurityTokenProvider.instance.reset(); + } } + + }); + t.start(); - }).start(); - } /** @@ -290,7 +305,7 @@ public class Utils { } // update the current status record - String productId = bean.getKnowledgeBaseIdentifier(); + String productId = bean.getKnowledgeBaseId(); Map> extrasMap = getExtrasAsHashMap(catalogue.getDataset(productId, sysApi).getExtras()); extrasMap.put(Constants.STATUS_OF_THE_GRSF_RECORD_CUSTOM_KEY, Arrays.asList(Status.To_be_Merged.getOrigName())); catalogue.patchProductCustomFields(productId, sysApi, extrasMap); @@ -445,10 +460,10 @@ public class Utils { JSONParser parser = new JSONParser(); JSONObject object = (JSONObject)parser.parse(json); - + String uuid = getDatasetKnowledgeBaseIdFromUrl((String)object.get(Constants.SIMILAR_RECORDS_BEAN_FIELD_URL)); CkanDataset dataset = ctl.getDataset(uuid, apiKey); - + return new SimilarGRSFRecord( uuid, (String)object.get(Constants.SIMILAR_RECORDS_BEAN_FIELD_DESCRIPTION), @@ -507,7 +522,7 @@ public class Utils { String destDomain = extrasWithoutNamespaces.get(Constants.DOMAIN_CUSTOM_KEY).get(0); String shortName = extrasWithoutNamespaces.get(Constants.SHORT_NAME_CUSTOM_KEY).get(0); String description = destDataset.getNotes(); - + return new ConnectedBean( connectedBeanUuid, description, diff --git a/src/main/java/org/gcube/datacatalogue/grsf_manage_widget/shared/HashTagsOnUpdate.java b/src/main/java/org/gcube/datacatalogue/grsf_manage_widget/shared/HashTagsOnUpdate.java new file mode 100644 index 0000000..8f2ea71 --- /dev/null +++ b/src/main/java/org/gcube/datacatalogue/grsf_manage_widget/shared/HashTagsOnUpdate.java @@ -0,0 +1,26 @@ +package org.gcube.datacatalogue.grsf_manage_widget.shared; + +/** + * A list of hastags to send along the update + * @author Costantino Perciante at ISTI-CNR (costantino.perciante@isti.cnr.it) + */ +public enum HashTagsOnUpdate { + + MERGE("merge"), + REVERTED_MERGE("reverted_merge"), + CONNECT("connect"), + SHORTNAME_UPDATED("shortname_updated"), + TRACEABILITY_FLAG_SET("traceability_flag_set"), + TRACEABILITY_FLAG_UNSET("traceability_flag_unset"); + + private String string; + + HashTagsOnUpdate(String asString){ + this.string = asString; + } + + public String getString() { + return string; + } + +} diff --git a/src/main/java/org/gcube/datacatalogue/grsf_manage_widget/shared/ManageProductBean.java b/src/main/java/org/gcube/datacatalogue/grsf_manage_widget/shared/ManageProductBean.java index b2f1159..ae98d60 100644 --- a/src/main/java/org/gcube/datacatalogue/grsf_manage_widget/shared/ManageProductBean.java +++ b/src/main/java/org/gcube/datacatalogue/grsf_manage_widget/shared/ManageProductBean.java @@ -1,8 +1,8 @@ package org.gcube.datacatalogue.grsf_manage_widget.shared; -import java.io.Serializable; import java.util.ArrayList; import java.util.List; +import java.util.Set; import org.gcube.datacatalogue.common.enums.Status; @@ -10,16 +10,11 @@ import org.gcube.datacatalogue.common.enums.Status; * The bean to be managed by GRSF Editors and Reviewers. * @author Costantino Perciante at ISTI-CNR (costantino.perciante@isti.cnr.it) */ -public class ManageProductBean implements Serializable{ +public class ManageProductBean extends GenericRecord{ private static final long serialVersionUID = -4882608487467259326L; - private String semanticIdentifier; // Stock id or Fishery id private String catalogueIdentifier; // Catalogue id - private String knowledgeBaseIdentifier; // GRSF UUID private String grsfType; // Fishery or Stock type (e.g., Assessment_Unit, Marine Resource and so on) - private String grsfDomain; // fishery/stock - private String grsfName; // Fishery name or Stock name - private String shortName; // it is editable ... private String shortNameUpdated; // the updated one, if any private boolean traceabilityFlag; //from false to true etc private Status currentStatus; @@ -32,8 +27,8 @@ public class ManageProductBean implements Serializable{ private List currentConnections; private List connections; // the one to used eventually private boolean mergesInvolved; // important: in this case an email must be sent to the editors/reviewers - private String recordUrl; // this record url private String report; // the report that keeps track of the changes + private Set hashtags; public ManageProductBean() { super(); @@ -44,9 +39,10 @@ public class ManageProductBean implements Serializable{ String catalogueIdentifier, String knowledgeBaseIdentifier, String grsfType, - String grsfDomain, - String grsfName, + String grsfDomain, String shortName, + String description, + String title, boolean traceabilityFlag, Status currentStatus, String recordUrl, @@ -55,14 +51,9 @@ public class ManageProductBean implements Serializable{ List currentConnections, List suggestedByKnowledgeBaseConnections ) { - super(); - this.semanticIdentifier = semanticIdentifier; + super(knowledgeBaseIdentifier, description, shortName, title, recordUrl, semanticIdentifier, grsfDomain); this.catalogueIdentifier = catalogueIdentifier; - this.knowledgeBaseIdentifier = knowledgeBaseIdentifier; this.grsfType = grsfType; - this.grsfDomain = grsfDomain; - this.grsfName = grsfName; - this.shortName = shortName; this.shortNameUpdated = shortName; this.traceabilityFlag = traceabilityFlag; this.currentStatus = currentStatus; @@ -70,15 +61,6 @@ public class ManageProductBean implements Serializable{ this.similarGrsfRecords = similarGrsfRecords; this.currentConnections = currentConnections; this.suggestedByKnowledgeBaseConnections = suggestedByKnowledgeBaseConnections; - this.recordUrl = recordUrl; - } - - public String getSemanticIdentifier() { - return semanticIdentifier; - } - - public void setSemanticIdentifier(String semanticIdentifier) { - this.semanticIdentifier = semanticIdentifier; } public List getSources() { @@ -105,14 +87,6 @@ public class ManageProductBean implements Serializable{ this.catalogueIdentifier = catalogueIdentifier; } - public String getKnowledgeBaseIdentifier() { - return knowledgeBaseIdentifier; - } - - public void setKnowledgeBaseIdentifier(String knowledgeBaseIdentifier) { - this.knowledgeBaseIdentifier = knowledgeBaseIdentifier; - } - public String getGrsfType() { return grsfType; } @@ -121,22 +95,6 @@ public class ManageProductBean implements Serializable{ this.grsfType = grsfType; } - public String getGrsfDomain() { - return grsfDomain; - } - - public void setGrsfDomain(String grsfDomain) { - this.grsfDomain = grsfDomain; - } - - public String getGrsfName() { - return grsfName; - } - - public void setGrsfName(String grsfName) { - this.grsfName = grsfName; - } - public Status getCurrentStatus() { return currentStatus; } @@ -169,14 +127,6 @@ public class ManageProductBean implements Serializable{ this.traceabilityFlag = traceabilityFlag; } - public String getShortName() { - return shortName; - } - - public void setShortName(String shortName) { - this.shortName = shortName; - } - public String getShortNameUpdated() { return shortNameUpdated; } @@ -219,14 +169,6 @@ public class ManageProductBean implements Serializable{ this.mergesInvolved = mergesInvolved; } - public String getRecordUrl() { - return recordUrl; - } - - public void setRecordUrl(String recordUrl) { - this.recordUrl = recordUrl; - } - public List getConnections() { return connections; } @@ -243,23 +185,30 @@ public class ManageProductBean implements Serializable{ this.report = report; } + public Set getHashtags() { + return hashtags; + } + + public void setHashtags(Set hashtags) { + this.hashtags = hashtags; + } + @Override public String toString() { - return "ManageProductBean [semanticIdentifier=" + semanticIdentifier - + ", catalogueIdentifier=" + catalogueIdentifier - + ", knowledgeBaseIdentifier=" + knowledgeBaseIdentifier - + ", grsfType=" + grsfType + ", grsfDomain=" + grsfDomain - + ", grsfName=" + grsfName + ", shortName=" + shortName - + ", shortNameUpdated=" + shortNameUpdated - + ", traceabilityFlag=" + traceabilityFlag + ", currentStatus=" - + currentStatus + ", newStatus=" + newStatus + ", annotation=" - + annotation + ", sources=" + sources + ", similarGrsfRecords=" - + similarGrsfRecords + ", suggestedByKnowledgeBaseConnections=" - + suggestedByKnowledgeBaseConnections + ", suggestdByAdministratorConnections=" + return "ManageProductBean [catalogueIdentifier=" + catalogueIdentifier + + ", grsfType=" + grsfType + ", shortNameUpdated=" + + shortNameUpdated + ", traceabilityFlag=" + traceabilityFlag + + ", currentStatus=" + currentStatus + ", newStatus=" + + newStatus + ", annotation=" + annotation + ", sources=" + + sources + ", similarGrsfRecords=" + similarGrsfRecords + + ", suggestedByKnowledgeBaseConnections=" + + suggestedByKnowledgeBaseConnections + + ", suggestdByAdministratorConnections=" + suggestdByAdministratorConnections + ", currentConnections=" + currentConnections + ", connections=" + connections - + ", mergesInvolved=" + mergesInvolved + ", recordUrl=" - + recordUrl + ", report=" + report + "]"; + + ", mergesInvolved=" + mergesInvolved + ", report=" + report + + ", hashtags=" + hashtags + ", GenericRecord=" + super.toString() + + "]"; } } 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 index 5e61c9e..e2ad818 100644 --- 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 @@ -6,35 +6,33 @@ public class RevertableOperationInfo implements Serializable{ private static final long serialVersionUID = 5274434342849474800L; private String recordUrl; - private String fullName; + private String fullNameCurrentAdmin; // the one who is thinking to revert it private String uuid; - private String admin; + private String fullNameOriginalAdmin; // the original admin in the link (his/her Full Name) + private String userNameOriginalAdmin; // the original admin's username private long timestamp; private RevertableOperations operation; public RevertableOperationInfo() { super(); } - - public RevertableOperationInfo(String recordUrl, String fullName, - String uuid, String admin, long timestamp, RevertableOperations operation) { + public RevertableOperationInfo( + String recordUrl, + String fullNameCurrentAdmin, + String uuid, + String fullNameOriginalAdmin, + String userNameOriginalAdmin, + long timestamp, + RevertableOperations operation) { super(); this.recordUrl = recordUrl; - this.fullName = fullName; + this.fullNameCurrentAdmin = fullNameCurrentAdmin; this.uuid = uuid; - this.admin = admin; + this.fullNameOriginalAdmin = fullNameOriginalAdmin; this.timestamp = timestamp; this.operation = operation; } - public String getAdmin() { - return admin; - } - - public void setAdmin(String admin) { - this.admin = admin; - } - public long getTimestamp() { return timestamp; } @@ -57,23 +55,43 @@ public class RevertableOperationInfo implements Serializable{ public void setRecordUrl(String recordUrl) { this.recordUrl = recordUrl; } - public String getFullName() { - return fullName; + + public String getFullNameCurrentAdmin() { + return fullNameCurrentAdmin; } - public void setFullName(String fullName) { - this.fullName = fullName; + + public void setFullNameCurrentAdmin(String fullNameCurrentAdmin) { + this.fullNameCurrentAdmin = fullNameCurrentAdmin; } + + public String getFullNameOriginalAdmin() { + return fullNameOriginalAdmin; + } + + public void setFullNameOriginalAdmin(String fullNameOriginalAdmin) { + this.fullNameOriginalAdmin = fullNameOriginalAdmin; + } + + public String getUserNameOriginalAdmin() { + return userNameOriginalAdmin; + } + + public void setUserNameOriginalAdmin(String userNameOriginalAdmin) { + this.userNameOriginalAdmin = userNameOriginalAdmin; + } + 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 + return "RevertableOperationInfo [recordUrl=" + recordUrl + + ", fullNameCurrentAdmin=" + fullNameCurrentAdmin + ", uuid=" + + uuid + ", fullNameOriginalAdmin=" + fullNameOriginalAdmin + + ", userNameOriginalAdmin=" + userNameOriginalAdmin + ", timestamp=" + timestamp + ", operation=" + operation + "]"; } } diff --git a/src/main/webapp/images/loader.gif b/src/main/webapp/images/loader.gif new file mode 100644 index 0000000..e661c2f Binary files /dev/null and b/src/main/webapp/images/loader.gif differ diff --git a/src/main/webapp/img/validation_error_icon.png b/src/main/webapp/images/validation_error_icon.png similarity index 100% rename from src/main/webapp/img/validation_error_icon.png rename to src/main/webapp/images/validation_error_icon.png