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