diff --git a/pom.xml b/pom.xml index 3ea0936..3b0fe63 100644 --- a/pom.xml +++ b/pom.xml @@ -93,14 +93,10 @@ org.slf4j slf4j-log4j12 - 1.6.4 - provided org.slf4j slf4j-api - 1.6.4 - provided org.gcube.common.portal @@ -176,6 +172,11 @@ gcube-widgets provided + + net.htmlparser.jericho + jericho-html + 3.3 + diff --git a/src/main/java/org/gcube/portlets/widgets/ckandatapublisherwidget/client/CKanMetadataPublisher.java b/src/main/java/org/gcube/portlets/widgets/ckandatapublisherwidget/client/CKanMetadataPublisher.java index 4ca55fe..d0d9a56 100644 --- a/src/main/java/org/gcube/portlets/widgets/ckandatapublisherwidget/client/CKanMetadataPublisher.java +++ b/src/main/java/org/gcube/portlets/widgets/ckandatapublisherwidget/client/CKanMetadataPublisher.java @@ -1,6 +1,6 @@ package org.gcube.portlets.widgets.ckandatapublisherwidget.client; -import org.gcube.portlets.widgets.ckandatapublisherwidget.client.ui.EditMetadataForm; +import org.gcube.portlets.widgets.ckandatapublisherwidget.client.ui.CreateDatasetForm; import com.google.gwt.core.client.EntryPoint; import com.google.gwt.user.client.ui.RootPanel; @@ -24,7 +24,7 @@ public class CKanMetadataPublisher implements EntryPoint { String idFolderWorkspace = "8b67fc1f-940d-4512-b783-105fba11c270"; String owner = "costantino.perciante"; - RootPanel.get("ckan-metadata-publisher-div").add(new EditMetadataForm(owner)); + RootPanel.get("ckan-metadata-publisher-div").add(new CreateDatasetForm(owner)); } } diff --git a/src/main/java/org/gcube/portlets/widgets/ckandatapublisherwidget/client/CKanPublisherService.java b/src/main/java/org/gcube/portlets/widgets/ckandatapublisherwidget/client/CKanPublisherService.java index 94d62d9..493a315 100644 --- a/src/main/java/org/gcube/portlets/widgets/ckandatapublisherwidget/client/CKanPublisherService.java +++ b/src/main/java/org/gcube/portlets/widgets/ckandatapublisherwidget/client/CKanPublisherService.java @@ -32,17 +32,26 @@ public interface CKanPublisherService extends RemoteService { /** * Try to create such dataset starting from the information contained into the toCreate bean. * @param toCreate + * @param isWorkspaceRequest if the call comes from the workspace * @return the identifier of the created dataset or null on error */ - String createCKanDataset(DatasetMetadataBean toCreate); + String createCKanDataset(DatasetMetadataBean toCreate, boolean isWorkspaceRequest); /** * Add this resource to the dataset whose id is datasetId * @param resource * @param datasetId * @param owner of the dataset - * @param callback */ - boolean addResourceToDataset(ResourceBean resource, String datasetId, String owner); + ResourceBean addResourceToDataset(ResourceBean resource, String datasetId, String owner); + + /** + * Delete this resource from the dataset with id datasetId + * @param resource + * @param datasetId + * @param owner of the dataset + * @return true on success, false otherwise + */ + boolean deleteResourceFromDataset(ResourceBean resource, String owner); } diff --git a/src/main/java/org/gcube/portlets/widgets/ckandatapublisherwidget/client/CKanPublisherServiceAsync.java b/src/main/java/org/gcube/portlets/widgets/ckandatapublisherwidget/client/CKanPublisherServiceAsync.java index a2eb722..d07202b 100644 --- a/src/main/java/org/gcube/portlets/widgets/ckandatapublisherwidget/client/CKanPublisherServiceAsync.java +++ b/src/main/java/org/gcube/portlets/widgets/ckandatapublisherwidget/client/CKanPublisherServiceAsync.java @@ -31,9 +31,10 @@ public interface CKanPublisherServiceAsync { /** * Try to create such dataset starting from the information contained into the toCreate bean. * @param toCreate + * @param isWorkspaceRequest if the call comes from the workspace * @return true on success, false otherwise */ - void createCKanDataset(DatasetMetadataBean toCreate, + void createCKanDataset(DatasetMetadataBean toCreate, boolean isWorkspaceRequest, AsyncCallback callback); /** @@ -44,6 +45,15 @@ public interface CKanPublisherServiceAsync { * @param callback */ void addResourceToDataset(ResourceBean resource, String datasetId, String owner, - AsyncCallback callback); + AsyncCallback callback); + + /** + * Delete this resource from the dataset with id datasetId + * @param resource + * @param owner of the dataset + * @return true on success, false otherwise + */ + void deleteResourceFromDataset(ResourceBean resource, + String owner, AsyncCallback callback); } diff --git a/src/main/java/org/gcube/portlets/widgets/ckandatapublisherwidget/client/events/DeleteResourceEvent.java b/src/main/java/org/gcube/portlets/widgets/ckandatapublisherwidget/client/events/DeleteResourceEvent.java new file mode 100644 index 0000000..63178a5 --- /dev/null +++ b/src/main/java/org/gcube/portlets/widgets/ckandatapublisherwidget/client/events/DeleteResourceEvent.java @@ -0,0 +1,34 @@ +package org.gcube.portlets.widgets.ckandatapublisherwidget.client.events; + +import org.gcube.portlets.widgets.ckandatapublisherwidget.shared.ResourceBean; + +import com.google.gwt.event.shared.GwtEvent; + +/** + * Deleted resource event. + * @author Costantino Perciante at ISTI-CNR (costantino.perciante@isti.cnr.it) + */ +public class DeleteResourceEvent extends GwtEvent { + public static Type TYPE = new Type(); + + private ResourceBean resource; + + public DeleteResourceEvent(ResourceBean resource) { + this.resource = resource; + } + + public ResourceBean getResource() { + return resource; + } + + @Override + public Type getAssociatedType() { + return TYPE; + } + + @Override + protected void dispatch(DeleteResourceEventHandler handler) { + handler.onDeletedResource(this); + } + +} diff --git a/src/main/java/org/gcube/portlets/widgets/ckandatapublisherwidget/client/events/DeleteResourceEventHandler.java b/src/main/java/org/gcube/portlets/widgets/ckandatapublisherwidget/client/events/DeleteResourceEventHandler.java new file mode 100644 index 0000000..02f6f95 --- /dev/null +++ b/src/main/java/org/gcube/portlets/widgets/ckandatapublisherwidget/client/events/DeleteResourceEventHandler.java @@ -0,0 +1,11 @@ +package org.gcube.portlets.widgets.ckandatapublisherwidget.client.events; + +import com.google.gwt.event.shared.EventHandler; + +/** + * The delete event handler + * @author Costantino Perciante at ISTI-CNR (costantino.perciante@isti.cnr.it) + */ +public interface DeleteResourceEventHandler extends EventHandler{ + void onDeletedResource(DeleteResourceEvent deleteResourceEvent); +} diff --git a/src/main/java/org/gcube/portlets/widgets/ckandatapublisherwidget/client/ui/AddResourceToDataset.java b/src/main/java/org/gcube/portlets/widgets/ckandatapublisherwidget/client/ui/AddResourceToDataset.java index 453b12e..61a7d9b 100644 --- a/src/main/java/org/gcube/portlets/widgets/ckandatapublisherwidget/client/ui/AddResourceToDataset.java +++ b/src/main/java/org/gcube/portlets/widgets/ckandatapublisherwidget/client/ui/AddResourceToDataset.java @@ -36,10 +36,10 @@ public class AddResourceToDataset extends Composite{ // bus to alert the dataset form about this new resource private HandlerManager eventBus; - + // the dataset id private String datasetId; - + // the owner private String owner; @@ -57,10 +57,10 @@ public class AddResourceToDataset extends Composite{ // save bus this.eventBus = eventBus; - + // save dataset id (it is needed when we will add resources) this.datasetId = datasetId; - + // the owner of the dataset/files this.owner = owner; } @@ -71,44 +71,45 @@ public class AddResourceToDataset extends Composite{ infoBlock.setVisible(false); // validation - if(resourceUrlTextBox.getText().isEmpty()){ + if(resourceUrlTextBox.getText().isEmpty() || resourceNameTextBox.getText().isEmpty()){ - infoBlock.setType(AlertType.ERROR); - infoBlock.setText("URL cannot be empty"); - infoBlock.setVisible(true); + showAlert("Url and name fields cannot be empty", AlertType.ERROR); return; } - - // remove html tags into description - //String description = convert(resourceDescriptionTextArea.getText()); TODO - + // collect data and build up the bean final ResourceBean resource = new ResourceBean(resourceUrlTextBox.getText(), resourceNameTextBox.getText(), resourceDescriptionTextArea.getText()); // disable add button addResourceButton.setEnabled(false); - + // try to create - ckanServices.addResourceToDataset(resource, datasetId, owner, new AsyncCallback() { - + ckanServices.addResourceToDataset(resource, datasetId, owner, new AsyncCallback() { + @Override - public void onSuccess(Boolean result) { - - if(result){ + public void onSuccess(ResourceBean result) { + + if(result != null){ showAlert("Resource created correctly", AlertType.SUCCESS); - eventBus.fireEvent(new AddResourceEvent(resource)); + eventBus.fireEvent(new AddResourceEvent(result)); + + // remove data + resourceUrlTextBox.setText(""); + resourceNameTextBox.setText(""); + resourceDescriptionTextArea.setText(""); + } else - showAlert("Unable to add this resource. Check the url is correct", AlertType.ERROR); - + showAlert("Unable to add this resource. Check that the url is correct", AlertType.ERROR); + } - + @Override public void onFailure(Throwable caught) { - + showAlert("Unable to add this resource, sorry", AlertType.ERROR); - + } }); @@ -120,7 +121,7 @@ public class AddResourceToDataset extends Composite{ * @param type */ protected void showAlert(String text, AlertType type) { - + infoBlock.setText(text); infoBlock.setType(type); infoBlock.setVisible(true); diff --git a/src/main/java/org/gcube/portlets/widgets/ckandatapublisherwidget/client/ui/AddResourceToDataset.ui.xml b/src/main/java/org/gcube/portlets/widgets/ckandatapublisherwidget/client/ui/AddResourceToDataset.ui.xml index 15eb527..59a13e8 100644 --- a/src/main/java/org/gcube/portlets/widgets/ckandatapublisherwidget/client/ui/AddResourceToDataset.ui.xml +++ b/src/main/java/org/gcube/portlets/widgets/ckandatapublisherwidget/client/ui/AddResourceToDataset.ui.xml @@ -37,15 +37,6 @@ .add-resource-button { float: right; } - - .info-markdown { - width: 95%; - background-color: #ebebeb; - border-bottom: 1px thin; - border-left: 1px thin; - border-right: 1px thin; - padding: 3px; - } + * Name: @@ -89,34 +81,16 @@ - - You can use - - Markdown formatting - - - - Add + Add diff --git a/src/main/java/org/gcube/portlets/widgets/ckandatapublisherwidget/client/ui/AddedResourcesSummary.java b/src/main/java/org/gcube/portlets/widgets/ckandatapublisherwidget/client/ui/AddedResourcesSummary.java index b338e85..e52ff7b 100644 --- a/src/main/java/org/gcube/portlets/widgets/ckandatapublisherwidget/client/ui/AddedResourcesSummary.java +++ b/src/main/java/org/gcube/portlets/widgets/ckandatapublisherwidget/client/ui/AddedResourcesSummary.java @@ -3,24 +3,31 @@ package org.gcube.portlets.widgets.ckandatapublisherwidget.client.ui; import java.util.ArrayList; import java.util.List; +import org.gcube.portlets.widgets.ckandatapublisherwidget.client.CKanPublisherService; +import org.gcube.portlets.widgets.ckandatapublisherwidget.client.CKanPublisherServiceAsync; import org.gcube.portlets.widgets.ckandatapublisherwidget.client.events.AddResourceEvent; import org.gcube.portlets.widgets.ckandatapublisherwidget.client.events.AddResourceEventHandler; +import org.gcube.portlets.widgets.ckandatapublisherwidget.client.events.DeleteResourceEvent; +import org.gcube.portlets.widgets.ckandatapublisherwidget.client.events.DeleteResourceEventHandler; import org.gcube.portlets.widgets.ckandatapublisherwidget.shared.ResourceBean; +import com.github.gwtbootstrap.client.ui.Accordion; +import com.github.gwtbootstrap.client.ui.AccordionGroup; import com.github.gwtbootstrap.client.ui.Button; -import com.github.gwtbootstrap.client.ui.constants.ButtonType; +import com.github.gwtbootstrap.client.ui.Paragraph; import com.google.gwt.core.client.GWT; import com.google.gwt.event.dom.client.ClickEvent; import com.google.gwt.event.dom.client.ClickHandler; import com.google.gwt.event.shared.HandlerManager; import com.google.gwt.uibinder.client.UiBinder; import com.google.gwt.uibinder.client.UiField; +import com.google.gwt.user.client.rpc.AsyncCallback; import com.google.gwt.user.client.ui.Composite; import com.google.gwt.user.client.ui.VerticalPanel; import com.google.gwt.user.client.ui.Widget; /** - * A summary of the added resources by the user. + * A summary of the resources added by the user. * @author Costantino Perciante at ISTI-CNR (costantino.perciante@isti.cnr.it) */ public class AddedResourcesSummary extends Composite{ @@ -32,20 +39,29 @@ public class AddedResourcesSummary extends Composite{ UiBinder { } + //Create a remote service proxy to talk to the server-side ckan service. + private final CKanPublisherServiceAsync ckanServices = GWT.create(CKanPublisherService.class); + // Event bus private HandlerManager eventBus; - // list of added resources + // list of added resources (beans) List addedResources; + // the owner + private String owner; + @UiField VerticalPanel addResourcesPanel; - public AddedResourcesSummary(HandlerManager eventBus) { + public AddedResourcesSummary(HandlerManager eventBus, String owner) { initWidget(uiBinder.createAndBindUi(this)); // save bus this.eventBus = eventBus; + // save owner + this.owner = owner; + // bind on add resource event bind(); @@ -53,6 +69,9 @@ public class AddedResourcesSummary extends Composite{ addedResources = new ArrayList(); } + /** + * Bind on add/delete resource event + */ private void bind() { // when a new resource is added @@ -64,71 +83,84 @@ public class AddedResourcesSummary extends Composite{ // get the resource final ResourceBean justAddedResource = addResourceEvent.getResource(); - // check if a resource with this id already exists - for (ResourceBean resource : addedResources){ + // Build an accordion to show resource info + Accordion accordion = new Accordion(); + AccordionGroup accordionGroup = new AccordionGroup(); + accordionGroup.setHeading("- " + justAddedResource.getName()); + accordion.add(accordionGroup); - if(resource.getId().equals(justAddedResource.getId())){ + // add sub-info such as url and description + Paragraph pUrl = new Paragraph(); + pUrl.setText("Url : " + justAddedResource.getUrl()); + Paragraph pDescription = new Paragraph(); + pDescription.setText("Description : " + justAddedResource.getDescription()); + + // button to delete the resource + Button deleteButton = new Button(); + deleteButton.setText("Delete"); + deleteButton.addClickHandler(new ClickHandler() { + + @Override + public void onClick(ClickEvent event) { + + eventBus.fireEvent(new DeleteResourceEvent(justAddedResource)); - // clear list and rebuild - rebuildSummary(); - return; } - } + }); - Button associatedButton = new Button(); - associatedButton.setType(ButtonType.LINK); - associatedButton.setText("-" + justAddedResource.getName()); + // fill accordion + accordionGroup.add(pUrl); + accordionGroup.add(pDescription); + accordionGroup.add(deleteButton); // add to the list addedResources.add(justAddedResource); // add to the panel - addResourcesPanel.add(associatedButton); - - // add handler to swap tab on click - associatedButton.addClickHandler(new ClickHandler() { + addResourcesPanel.add(accordion); + } + }); - @Override - public void onClick(ClickEvent event) { + // when the user wants to delete a resource + eventBus.addHandler(DeleteResourceEvent.TYPE, new DeleteResourceEventHandler() { - // TODO show information below this link and swap panel + @Override + public void onDeletedResource(DeleteResourceEvent deleteResourceEvent) { + // to delete + ResourceBean toDelete = deleteResourceEvent.getResource(); + + // find it + for(int i = 0; i < addedResources.size(); i++){ + + if(addedResources.get(i).getId().equals(toDelete.getId())){ + + // get the associated widget and remove it + final Widget widget = addResourcesPanel.getWidget(i); + + // remote call to remove it from the dataset + ckanServices.deleteResourceFromDataset(toDelete, owner, new AsyncCallback() { + + @Override + public void onSuccess(Boolean result) { + + if(result) + widget.removeFromParent(); + } + + @Override + public void onFailure(Throwable caught) { + + } + }); + + break; } - }); - + } + + // remove from the list + addedResources.remove(toDelete); } }); } - - /** - * Rebuild the summary list - */ - protected void rebuildSummary() { - - addResourcesPanel.clear(); - - for (final ResourceBean resource : addedResources){ - - Button associatedButton = new Button(); - associatedButton.setType(ButtonType.LINK); - associatedButton.setText("-" + resource.getName()); - - // add to the list - addedResources.add(resource); - - // add to the panel - addResourcesPanel.add(associatedButton); - - // add handler to swap tab on click - associatedButton.addClickHandler(new ClickHandler() { - - @Override - public void onClick(ClickEvent event) { - - // TODO show information below this link and swap panel - - } - }); - } - } } diff --git a/src/main/java/org/gcube/portlets/widgets/ckandatapublisherwidget/client/ui/AddedResourcesSummary.ui.xml b/src/main/java/org/gcube/portlets/widgets/ckandatapublisherwidget/client/ui/AddedResourcesSummary.ui.xml index 79ee8b8..3311d54 100644 --- a/src/main/java/org/gcube/portlets/widgets/ckandatapublisherwidget/client/ui/AddedResourcesSummary.ui.xml +++ b/src/main/java/org/gcube/portlets/widgets/ckandatapublisherwidget/client/ui/AddedResourcesSummary.ui.xml @@ -1,11 +1,8 @@ - - - -

Added Resources

- +

Added Resources

+
\ No newline at end of file diff --git a/src/main/java/org/gcube/portlets/widgets/ckandatapublisherwidget/client/ui/EditMetadataForm.java b/src/main/java/org/gcube/portlets/widgets/ckandatapublisherwidget/client/ui/CreateDatasetForm.java similarity index 85% rename from src/main/java/org/gcube/portlets/widgets/ckandatapublisherwidget/client/ui/EditMetadataForm.java rename to src/main/java/org/gcube/portlets/widgets/ckandatapublisherwidget/client/ui/CreateDatasetForm.java index dc7e066..d0af196 100644 --- a/src/main/java/org/gcube/portlets/widgets/ckandatapublisherwidget/client/ui/EditMetadataForm.java +++ b/src/main/java/org/gcube/portlets/widgets/ckandatapublisherwidget/client/ui/CreateDatasetForm.java @@ -7,6 +7,7 @@ import java.util.List; import java.util.Map; import java.util.Map.Entry; +import org.gcube.datacatalogue.ckanutillibrary.models.MetaDataBean; import org.gcube.portlets.user.gcubewidgets.client.elements.Span; import org.gcube.portlets.widgets.ckandatapublisherwidget.client.CKanPublisherService; import org.gcube.portlets.widgets.ckandatapublisherwidget.client.CKanPublisherServiceAsync; @@ -33,6 +34,8 @@ import com.google.gwt.event.dom.client.ClickHandler; import com.google.gwt.event.dom.client.KeyCodes; import com.google.gwt.event.dom.client.KeyDownEvent; import com.google.gwt.event.shared.HandlerManager; +import com.google.gwt.regexp.shared.MatchResult; +import com.google.gwt.regexp.shared.RegExp; import com.google.gwt.uibinder.client.UiBinder; import com.google.gwt.uibinder.client.UiField; import com.google.gwt.uibinder.client.UiHandler; @@ -47,7 +50,7 @@ import com.google.gwt.user.client.ui.Widget; * Edit metadata form for ckan dataset. * @author Costantino Perciante at ISTI-CNR (costantino.perciante@isti.cnr.it) */ -public class EditMetadataForm extends Composite{ +public class CreateDatasetForm extends Composite{ /** * Create a remote service proxy to talk to the server-side ckan service. @@ -58,7 +61,7 @@ public class EditMetadataForm extends Composite{ .create(EditMetadataFormUiBinder.class); interface EditMetadataFormUiBinder extends - UiBinder { + UiBinder { } @UiField TextBox titleTextBox; @@ -83,12 +86,14 @@ public class EditMetadataForm extends Composite{ @UiField AlertBlock infoBlock; @UiField AlertBlock onCreateAlertBlock; @UiField VerticalPanel resourcesPanel; + @UiField VerticalPanel metadataPanel; + @UiField ListBox metadataFormatListbox; // tab panel private TabPanel tabPanel; // add resource form - AddResourceToDataset resourceForm; + private AddResourceToDataset resourceForm; // tags list private List tagsList = new ArrayList(); @@ -108,11 +113,14 @@ public class EditMetadataForm extends Composite{ // the owner private String owner; + // workspace request? + private boolean isWorkspaceRequest = false; + /** * Invoked in the most general case * @param owner */ - public EditMetadataForm(String owner) { + public CreateDatasetForm(String owner) { initWidget(uiBinder.createAndBindUi(this)); this.owner = owner; @@ -144,6 +152,7 @@ public class EditMetadataForm extends Composite{ authorEmailTextbox.setText(bean.getAuthorEmail()); maintainerTextbox.setText(bean.getMaintainer()); maintainerEmailTextbox.setText(bean.getMaintainerEmail()); + prepareMetadataList(receivedBean); // set organizations List organizations = bean.getOrganizationList(); @@ -206,16 +215,35 @@ public class EditMetadataForm extends Composite{ } + /** + * Add the items to the listbox and put data into the metadataPanel + * @param receivedBean + */ + protected void prepareMetadataList(DatasetMetadataBean receivedBean) { + + List beans = receivedBean.getMetadataList(); + + for(MetaDataBean metadataBean: beans){ + + //metadataFormatListbox.addItem(metadataBean.getType().getName()); + + } + + } + /** * Invoked when the workspace is used. * @param idFolderWorkspace * @param owner */ - public EditMetadataForm(String idFolderWorkspace, String owner) { + public CreateDatasetForm(String idFolderWorkspace, String owner) { initWidget(uiBinder.createAndBindUi(this)); this.owner = owner; + // workspace request + isWorkspaceRequest = true; + // bind on events bind(); @@ -245,6 +273,7 @@ public class EditMetadataForm extends Composite{ authorEmailTextbox.setText(bean.getAuthorEmail()); maintainerTextbox.setText(bean.getMaintainer()); maintainerEmailTextbox.setText(bean.getMaintainerEmail()); + prepareMetadataList(receivedBean); // set organizations List organizations = bean.getOrganizationList(); @@ -364,11 +393,16 @@ public class EditMetadataForm extends Composite{ @UiHandler("createButton") void createDatasetEvent(ClickEvent e){ - // validate data TODO + // validate data boolean areDataValid = validateData(); // Collect current data and send them to the server - if(areDataValid){ + if(!areDataValid){ + + alertOnCreate("Please check inserted data ", AlertType.ERROR); + + } + else{ String title = titleTextBox.getValue(); String description = descriptionTextarea.getText(); @@ -413,10 +447,9 @@ public class EditMetadataForm extends Composite{ createButton.setEnabled(false); resetButton.setEnabled(false); - onCreateAlertBlock.setText("Trying to create dataset, please wait"); - onCreateAlertBlock.setVisible(true); + alertOnCreate("Trying to create dataset, please wait", AlertType.INFO); - ckanServices.createCKanDataset(receivedBean, new AsyncCallback() { + ckanServices.createCKanDataset(receivedBean, isWorkspaceRequest, new AsyncCallback() { @Override public void onSuccess(final String datasetId) { @@ -459,10 +492,10 @@ public class EditMetadataForm extends Composite{ // tab for the added resources Tab addedResources = new Tab(); - addedResources.add(new AddedResourcesSummary(eventBus)); + addedResources.add(new AddedResourcesSummary(eventBus, owner)); addedResources.setHeading("Added Resource"); tabPanel.add(addedResources); - + // add tabs to resources panel tabPanel.selectTab(0); resourcesPanel.add(tabPanel); @@ -518,14 +551,38 @@ public class EditMetadataForm extends Composite{ /** * Validate data - * @return true on success + * @return true on success, false otherwise */ private boolean validateData() { + + boolean correct = true; - // TODO - return true; + if(titleTextBox.getText().isEmpty()) + correct = false; + + // email reg expression + String regexMail = "\\b[\\w.%-]+@[-.\\w]+\\.[A-Za-z]{2,4}\\b"; + + correct &= validateByRegExpression(maintainerEmailTextbox.getText(), regexMail); + + // name reg expression + String regexName = "^[a-zA-Z\\s]+"; + + correct &= validateByRegExpression(maintainerTextbox.getText(), regexName); + + return correct; } + private boolean validateByRegExpression(String textToValidate, String regex){ + + RegExp pattern = RegExp.compile(regex); + MatchResult matcher = pattern.exec(textToValidate); + + GWT.log("Matcher is " + matcher); + + return (matcher != null); + } + @UiHandler("resetButton") void resetFormEvent(ClickEvent e){ @@ -533,8 +590,6 @@ public class EditMetadataForm extends Composite{ titleTextBox.setText(""); descriptionTextarea.setText(""); versionTextbox.setText(""); - authorTextbox.setText(""); - authorEmailTextbox.setText(""); maintainerTextbox.setText(""); maintainerEmailTextbox.setText(""); removeTags(); @@ -548,14 +603,14 @@ public class EditMetadataForm extends Composite{ } /** - * Disable dataset editable fields + * Disable dataset editable fields once the dataset has been + * Successfully created. */ protected void disableDatasetFields() { + titleTextBox.setEnabled(false); descriptionTextarea.setEnabled(false); versionTextbox.setEnabled(false); - authorTextbox.setEnabled(false); - authorEmailTextbox.setEnabled(false); maintainerTextbox.setEnabled(false); maintainerEmailTextbox.setEnabled(false); visibilityListbox.setEnabled(false); @@ -563,6 +618,17 @@ public class EditMetadataForm extends Composite{ licenseListbox.setEnabled(false); organizationsListbox.setEnabled(false); addCustomFieldButton.setEnabled(false); + + // freeze tags + for(int i = 0; i < tagsList.size(); i++){ + + // get tag widget + ListItem tagWidget = (ListItem)tagsPanel.getWidget(i); + + // get the "x" span + tagWidget.getWidget(1).removeFromParent(); + + } } /** @@ -604,19 +670,19 @@ public class EditMetadataForm extends Composite{ final String value = itemBox.getValue(); final ListItem displayItem = new ListItem(); displayItem.setStyleName("tag-style"); - Span p = new Span(itemBox.getValue()); + Span tagText = new Span(itemBox.getValue()); - Span span = new Span("x"); - span.setTitle("Remove this tag"); - span.addClickHandler(new ClickHandler() { + Span tagRemove = new Span("x"); + tagRemove.setTitle("Remove this tag"); + tagRemove.addClickHandler(new ClickHandler() { public void onClick(ClickEvent clickEvent) { - removeListItem(displayItem, value); + removeTag(displayItem, value); } }); - span.setStyleName("tag-style-x"); - displayItem.add(p); - displayItem.add(span); + tagRemove.setStyleName("tag-style-x"); + displayItem.add(tagText); + displayItem.add(tagRemove); itemBox.setValue(""); itemBox.setFocus(true); tagsPanel.add(displayItem); @@ -640,7 +706,7 @@ public class EditMetadataForm extends Composite{ span.setTitle("Remove this tag"); span.addClickHandler(new ClickHandler() { public void onClick(ClickEvent clickEvent) { - removeListItem(displayItem, tag); + removeTag(displayItem, tag); } }); @@ -655,10 +721,11 @@ public class EditMetadataForm extends Composite{ * Remove a tag from the list * @param displayItem */ - private void removeListItem(ListItem displayItem, String value) { - GWT.log("Removing: " + displayItem.getWidget(0).getElement().getInnerHTML(), null); + private void removeTag(ListItem displayItem, String value) { + tagsList.remove(value); tagsPanel.remove(displayItem); + } /** diff --git a/src/main/java/org/gcube/portlets/widgets/ckandatapublisherwidget/client/ui/EditMetadataForm.ui.xml b/src/main/java/org/gcube/portlets/widgets/ckandatapublisherwidget/client/ui/CreateDatasetForm.ui.xml similarity index 87% rename from src/main/java/org/gcube/portlets/widgets/ckandatapublisherwidget/client/ui/EditMetadataForm.ui.xml rename to src/main/java/org/gcube/portlets/widgets/ckandatapublisherwidget/client/ui/CreateDatasetForm.ui.xml index 66d0cb7..dfdbcf0 100644 --- a/src/main/java/org/gcube/portlets/widgets/ckandatapublisherwidget/client/ui/EditMetadataForm.ui.xml +++ b/src/main/java/org/gcube/portlets/widgets/ckandatapublisherwidget/client/ui/CreateDatasetForm.ui.xml @@ -33,15 +33,6 @@ .tagsPanelStyle { display: inline-block; } - - .info-markdown { - width: 95%; - background-color: #ebebeb; - border-bottom: 1px thin; - border-left: 1px thin; - border-right: 1px thin; - padding: 3px; - } - - You can use - - Markdown formatting - - @@ -188,7 +160,7 @@ + enabled="false" b:id="author" title="Dataset author" ui:field="authorTextbox" /> @@ -199,7 +171,7 @@ + enabled="false" b:id="email" title="Dataset author" ui:field="authorEmailTextbox" /> @@ -223,6 +195,20 @@ + + Metadata + Format: + + + + + + + + + Custom Field(s): @@ -236,7 +222,8 @@ dataset. You have to choose a unique key for the field and a value for this. You - can remove them at any time. + can remove them at any time until you create the + dataset. @@ -261,12 +248,13 @@ - - Add Resources + + Add Resources - + Create diff --git a/src/main/java/org/gcube/portlets/widgets/ckandatapublisherwidget/server/CKANPublisherServicesImpl.java b/src/main/java/org/gcube/portlets/widgets/ckandatapublisherwidget/server/CKANPublisherServicesImpl.java index a41f983..107cd1e 100644 --- a/src/main/java/org/gcube/portlets/widgets/ckandatapublisherwidget/server/CKANPublisherServicesImpl.java +++ b/src/main/java/org/gcube/portlets/widgets/ckandatapublisherwidget/server/CKANPublisherServicesImpl.java @@ -10,6 +10,10 @@ import java.util.List; import java.util.Map; import java.util.Map.Entry; +import net.htmlparser.jericho.Renderer; +import net.htmlparser.jericho.Segment; +import net.htmlparser.jericho.Source; + import org.gcube.common.homelibrary.home.HomeLibrary; import org.gcube.common.homelibrary.home.exceptions.InternalErrorException; import org.gcube.common.homelibrary.home.workspace.Workspace; @@ -18,6 +22,7 @@ import org.gcube.common.homelibrary.home.workspace.folder.FolderItem; import org.gcube.common.homelibrary.home.workspace.folder.items.GCubeItem; import org.gcube.common.scope.api.ScopeProvider; import org.gcube.datacatalogue.ckanutillibrary.CKanUtilsFactory; +import org.gcube.datacatalogue.ckanutillibrary.models.MetaDataBean; import org.gcube.portlets.widgets.ckandatapublisherwidget.client.CKanPublisherService; import org.gcube.portlets.widgets.ckandatapublisherwidget.shared.DatasetMetadataBean; import org.gcube.portlets.widgets.ckandatapublisherwidget.shared.LicensesBean; @@ -149,6 +154,27 @@ public class CKANPublisherServicesImpl extends RemoteServiceServlet implements C return null; } + + /** + * Retrieve the list of metadata beans + * @return + */ + private List getMetaDataBeanList(){ + + logger.debug("Request for metadata beans list"); + + try{ + + String currentScope = getCurrentScope(); + return CKanUtilsFactory.getInstance().getCkanUtilsForScope(currentScope).getMetadataList(); + + }catch(Exception e){ + logger.error("Unable to retrieve metadata", e); + } + + return null; + + } @Override public LicensesBean getLicenses() { @@ -187,7 +213,7 @@ public class CKANPublisherServicesImpl extends RemoteServiceServlet implements C bean = new DatasetMetadataBean(); bean.setId(folderId); - bean.setOwnerFolderInWorkspace(owner); + bean.setOwnerIdentifier(owner); bean.setVersion(1); bean.setAuthor(userOwner.getFullname()); bean.setAuthorEmail(userOwner.getEmail()); @@ -195,6 +221,7 @@ public class CKANPublisherServicesImpl extends RemoteServiceServlet implements C bean.setMaintainerEmail(userOwner.getEmail()); bean.setOrganizationList(getUserOrganizationsList(owner)); + // if the request comes from the workspace if(folderId != null && !folderId.isEmpty()){ @@ -222,6 +249,11 @@ public class CKANPublisherServicesImpl extends RemoteServiceServlet implements C } bean.setResourcesIds(childrenIds); + + // retrieve the metadata + List metadataBeans = getMetaDataBeanList(); + bean.setMetadataList(metadataBeans); + } }catch(Exception e){ @@ -241,7 +273,7 @@ public class CKANPublisherServicesImpl extends RemoteServiceServlet implements C bean.setMaintainer("Costantino Perciante"); bean.setMaintainerEmail("costantino.perciante@isti.cnr.it"); bean.setOrganizationList(getUserOrganizationsList(owner)); - bean.setOwnerFolderInWorkspace(owner); + bean.setOwnerIdentifier(owner); if(folderId != null && !folderId.isEmpty()){ ScopeProvider.instance.set("/gcube"); @@ -267,6 +299,11 @@ public class CKANPublisherServicesImpl extends RemoteServiceServlet implements C bean.setResourcesIds(childrenIds); } + + // retrieve the metadata + List metadataBeans = getMetaDataBeanList(); + bean.setMetadataList(metadataBeans); + }catch(Exception e){ logger.error("Error while building bean into dev mode", e); } @@ -302,13 +339,13 @@ public class CKANPublisherServicesImpl extends RemoteServiceServlet implements C } @Override - public String createCKanDataset(DatasetMetadataBean toCreate) { + public String createCKanDataset(DatasetMetadataBean toCreate, boolean isWorkspaceRequest) { // retrieve ckan's catalog url String ckanPortalUrl = getCatalogueUrl(); - // retrieve the owner of the original folder into the workspace - String owner = toCreate.getOwnerFolderInWorkspace(); + // retrieve the owner identifier + String owner = toCreate.getOwnerIdentifier(); // retrieve the api key for this user String apiKey = getCKANApikeyFromUser(owner); @@ -335,7 +372,16 @@ public class CKANPublisherServicesImpl extends RemoteServiceServlet implements C dataset.setMaintainer(toCreate.getMaintainer()); dataset.setMaintainerEmail(toCreate.getMaintainerEmail()); dataset.setVersion(String.valueOf(toCreate.getVersion())); - dataset.setNotes(toCreate.getDescription()); + + // description must be escaped + Source description = new Source(toCreate.getDescription()); + Segment htmlSeg = new Segment(description, 0, description.length()); + Renderer htmlRend = new Renderer(htmlSeg); + dataset.setNotes(htmlRend.toString()); + + logger.debug("Description (escaped is ) " + htmlRend.toString()); + + // visibility TODO dataset.setOpen(toCreate.getVisibility()); // iterate over the licenses to find the id of the chosen one @@ -374,7 +420,7 @@ public class CKANPublisherServicesImpl extends RemoteServiceServlet implements C } // check if we need to add the resources - if(toCreate.isAddResources()){ + if(toCreate.isAddResources() && isWorkspaceRequest){ logger.debug("We need to add resources to the dataset"); @@ -384,7 +430,7 @@ public class CKANPublisherServicesImpl extends RemoteServiceServlet implements C Workspace ws = HomeLibrary .getHomeManagerFactory() .getHomeManager() - .getHome(toCreate.getOwnerFolderInWorkspace()).getWorkspace(); + .getHome(toCreate.getOwnerIdentifier()).getWorkspace(); List resources = new ArrayList(); @@ -420,8 +466,7 @@ public class CKANPublisherServicesImpl extends RemoteServiceServlet implements C }catch(Exception e){ // try to update - logger.error("Error while creating the dataset, probably it already exists. Trying to update it..", e); - res = client.updateDataset(dataset); + logger.error("Error while creating the dataset, probably it already exists.", e); } @@ -459,7 +504,7 @@ public class CKANPublisherServicesImpl extends RemoteServiceServlet implements C HttpURLConnection.setFollowRedirects(true); HttpURLConnection con = (HttpURLConnection) new URL(URLName).openConnection(); con.setRequestMethod("HEAD"); - logger.info("Return code is " + con.getResponseCode()); + logger.debug("Return code is " + con.getResponseCode()); return (con.getResponseCode() == HttpURLConnection.HTTP_OK); } catch (Exception e) { @@ -469,45 +514,81 @@ public class CKANPublisherServicesImpl extends RemoteServiceServlet implements C } @Override - public boolean addResourceToDataset(ResourceBean resourceBean, String datasetId, String owner) { - - logger.info("Incoming request for creating new resource for dataset with id " + datasetId); - logger.info("Owner is " + owner + " and resource is " + resourceBean); - + public ResourceBean addResourceToDataset(ResourceBean resourceBean, String datasetId, String owner) { + + logger.debug("Incoming request for creating new resource for dataset with id " + datasetId); + logger.debug("Owner is " + owner + " and resource is " + resourceBean); + // of course, if it exists if(exists(resourceBean.getUrl())){ - + try{ - // retrieve ckan's catalog url - String ckanPortalUrl = getCatalogueUrl(); - - // retrieve the api key for this user - String apiKey = getCKANApikeyFromUser(owner); - - CkanResource resource = new CkanResource(ckanPortalUrl, datasetId); - resource.setName(resourceBean.getName()); - resource.setDescription(resourceBean.getDescription()); - resource.setUrl(resourceBean.getUrl()); - resource.setOwner(owner); - - // Checked client - CheckedCkanClient client = new CheckedCkanClient(ckanPortalUrl, apiKey); - CkanResource createdRes = client.createResource(resource); - - if(createdRes != null){ - - logger.info("Resource " + createdRes.getName() + " is now available"); - return true; - - } - + // retrieve ckan's catalog url + String ckanPortalUrl = getCatalogueUrl(); + + // retrieve the api key for this user + String apiKey = getCKANApikeyFromUser(owner); + + CkanResource resource = new CkanResource(ckanPortalUrl, datasetId); + resource.setName(resourceBean.getName()); + + // escape description + Source description = new Source(resourceBean.getDescription()); + Segment htmlSeg = new Segment(description, 0, description.length()); + Renderer htmlRend = new Renderer(htmlSeg); + + resource.setDescription(htmlRend.toString()); + resource.setUrl(resourceBean.getUrl()); + resource.setOwner(owner); + + // Checked client + CheckedCkanClient client = new CheckedCkanClient(ckanPortalUrl, apiKey); + CkanResource createdRes = client.createResource(resource); + + if(createdRes != null){ + + logger.debug("Resource " + createdRes.getName() + " is now available"); + + // set its id and turn it to the client + resourceBean.setId(createdRes.getId()); + return resourceBean; + + } + }catch(Exception e){ logger.error("Unable to create new resource", e); } } - - logger.info("No resource created"); - + + logger.debug("No resource created"); + + return null; + } + + @Override + public boolean deleteResourceFromDataset(ResourceBean resource, + String owner) { + logger.debug("Request for deleting resource " + resource); + + try{ + + // retrieve ckan's catalog url + String ckanPortalUrl = getCatalogueUrl(); + + // retrieve the api key for this user + String apiKey = getCKANApikeyFromUser(owner); + + // Checked client + CheckedCkanClient client = new CheckedCkanClient(ckanPortalUrl, apiKey); + client.deleteResource(resource.getId()); + + return true; + + }catch(Exception e){ + logger.error("Unable to delete such resource", e); + } + return false; } + } diff --git a/src/main/java/org/gcube/portlets/widgets/ckandatapublisherwidget/shared/DatasetMetadataBean.java b/src/main/java/org/gcube/portlets/widgets/ckandatapublisherwidget/shared/DatasetMetadataBean.java index 66084fb..98e5268 100644 --- a/src/main/java/org/gcube/portlets/widgets/ckandatapublisherwidget/shared/DatasetMetadataBean.java +++ b/src/main/java/org/gcube/portlets/widgets/ckandatapublisherwidget/shared/DatasetMetadataBean.java @@ -4,6 +4,8 @@ import java.io.Serializable; import java.util.List; import java.util.Map; +import org.gcube.datacatalogue.ckanutillibrary.models.MetaDataBean; + /** * This bean will contain during ckan metadata creation the following information * (related to the workspace folder that represents a dataset) @@ -19,6 +21,7 @@ import java.util.Map; *
  • custom fields -> gcube items couple *
  • organizationsList -> list of organizations to which the user belong (and in which * he wants to publish) + *
  • list of metadata, that is custom fields per vre * * @author Costantino Perciante at ISTI-CNR * (costantino.perciante@isti.cnr.it) @@ -39,11 +42,12 @@ public class DatasetMetadataBean implements Serializable { private String authorEmail; // folder's email owner private String maintainer; private String maintainerEmail; - private String ownerFolderInWorkspace; // owner of the folder into the workspace + private String ownerIdentifier; // owner of the folder into the workspace (e.g., andrea.rossi) private List organizationList; // list of organization in which the user is present and could create the dataset private String selectedOrganization; private List resourcesIds; // in case of workspace, this is the list of children's ids private boolean addResources; // if true, add these files as resources to the dataset + private List metadataList; public DatasetMetadataBean(){ super(); @@ -63,17 +67,21 @@ public class DatasetMetadataBean implements Serializable { * @param authorEmail * @param maintainer * @param maintainerEmail - * @param ownerFolderInWorkspace + * @param ownerIdentifier * @param organizationList * @param selectedOrganization + * @param resourcesIds + * @param addResources + * @param metadataList */ public DatasetMetadataBean(String id, String title, String description, Map customFields, List tags, - String license, boolean visibility, - String source, long version, String author, String authorEmail, - String maintainer, String maintainerEmail, - String ownerFolderInWorkspace, List organizationList, - String selectedOrganization) { + String license, boolean visibility, String source, long version, + String author, String authorEmail, String maintainer, + String maintainerEmail, String ownerIdentifier, + List organizationList, String selectedOrganization, + List resourcesIds, boolean addResources, + List metadataList) { super(); this.id = id; this.title = title; @@ -88,9 +96,20 @@ public class DatasetMetadataBean implements Serializable { this.authorEmail = authorEmail; this.maintainer = maintainer; this.maintainerEmail = maintainerEmail; - this.ownerFolderInWorkspace = ownerFolderInWorkspace; + this.ownerIdentifier = ownerIdentifier; this.organizationList = organizationList; this.selectedOrganization = selectedOrganization; + this.resourcesIds = resourcesIds; + this.addResources = addResources; + this.metadataList = metadataList; + } + + public List getMetadataList() { + return metadataList; + } + + public void setMetadataList(List metadataList) { + this.metadataList = metadataList; } public String getId() { @@ -101,12 +120,12 @@ public class DatasetMetadataBean implements Serializable { this.id = id; } - public void setOwnerFolderInWorkspace(String ownerFolderInWorkspace) { - this.ownerFolderInWorkspace = ownerFolderInWorkspace; + public String getOwnerIdentifier() { + return ownerIdentifier; } - public String getOwnerFolderInWorkspace(){ - return ownerFolderInWorkspace; + public void setOwnerIdentifier(String ownerIdentifier) { + this.ownerIdentifier = ownerIdentifier; } public String getTitle() { @@ -242,14 +261,14 @@ public class DatasetMetadataBean implements Serializable { return "DatasetMetadataBean [id=" + id + ", title=" + title + ", description=" + description + ", customFields=" + customFields + ", tags=" + tags + ", license=" + license - + ", visibility=" + visibility - + ", source=" + source + ", version=" + version + ", author=" - + author + ", authorEmail=" + authorEmail + ", maintainer=" - + maintainer + ", maintainerEmail=" + maintainerEmail - + ", ownerFolderInWorkspace=" + ownerFolderInWorkspace - + ", organizationList=" + organizationList + + ", visibility=" + visibility + ", source=" + source + + ", version=" + version + ", author=" + author + + ", authorEmail=" + authorEmail + ", maintainer=" + maintainer + + ", maintainerEmail=" + maintainerEmail + ", ownerIdentifier=" + + ownerIdentifier + ", organizationList=" + organizationList + ", selectedOrganization=" + selectedOrganization + ", resourcesIds=" + resourcesIds + ", addResources=" - + addResources + "]"; + + addResources + ", metadataList=" + metadataList + "]"; } + } diff --git a/src/main/resources/org/gcube/portlets/widgets/ckandatapublisherwidget/CKanMetadataPublisher.gwt.xml b/src/main/resources/org/gcube/portlets/widgets/ckandatapublisherwidget/CKanMetadataPublisher.gwt.xml index fa37e4f..f36e359 100644 --- a/src/main/resources/org/gcube/portlets/widgets/ckandatapublisherwidget/CKanMetadataPublisher.gwt.xml +++ b/src/main/resources/org/gcube/portlets/widgets/ckandatapublisherwidget/CKanMetadataPublisher.gwt.xml @@ -10,7 +10,7 @@ - + diff --git a/src/main/webapp/CKanMetadataPublisher.html b/src/main/webapp/CKanMetadataPublisher.html index 7fa2fb4..f0b17e1 100644 --- a/src/main/webapp/CKanMetadataPublisher.html +++ b/src/main/webapp/CKanMetadataPublisher.html @@ -5,8 +5,6 @@ - diff --git a/src/main/webapp/js/showdown.js b/src/main/webapp/js/showdown.js deleted file mode 100644 index 7f8d073..0000000 --- a/src/main/webapp/js/showdown.js +++ /dev/null @@ -1,325 +0,0 @@ -/** - * Created by Tivie on 06-01-2015. - */ - -// Private properties -var showdown = {}, - parsers = {}, - extensions = {}, - globalOptions = getDefaultOpts(true), - flavor = { - github: { - omitExtraWLInCodeBlocks: true, - prefixHeaderId: 'user-content-', - simplifiedAutoLink: true, - literalMidWordUnderscores: true, - strikethrough: true, - tables: true, - tablesHeaderId: true, - ghCodeBlocks: true, - tasklists: true - }, - vanilla: getDefaultOpts(true) - }; - -/** - * helper namespace - * @type {{}} - */ -showdown.helper = {}; - -/** - * TODO LEGACY SUPPORT CODE - * @type {{}} - */ -showdown.extensions = {}; - -/** - * Set a global option - * @static - * @param {string} key - * @param {*} value - * @returns {showdown} - */ -showdown.setOption = function (key, value) { - 'use strict'; - globalOptions[key] = value; - return this; -}; - -/** - * Get a global option - * @static - * @param {string} key - * @returns {*} - */ -showdown.getOption = function (key) { - 'use strict'; - return globalOptions[key]; -}; - -/** - * Get the global options - * @static - * @returns {{}} - */ -showdown.getOptions = function () { - 'use strict'; - return globalOptions; -}; - -/** - * Reset global options to the default values - * @static - */ -showdown.resetOptions = function () { - 'use strict'; - globalOptions = getDefaultOpts(true); -}; - -/** - * Set the flavor showdown should use as default - * @param {string} name - */ -showdown.setFlavor = function (name) { - 'use strict'; - if (flavor.hasOwnProperty(name)) { - var preset = flavor[name]; - for (var option in preset) { - if (preset.hasOwnProperty(option)) { - globalOptions[option] = preset[option]; - } - } - } -}; - -/** - * Get the default options - * @static - * @param {boolean} [simple=true] - * @returns {{}} - */ -showdown.getDefaultOptions = function (simple) { - 'use strict'; - return getDefaultOpts(simple); -}; - -/** - * Get or set a subParser - * - * subParser(name) - Get a registered subParser - * subParser(name, func) - Register a subParser - * @static - * @param {string} name - * @param {function} [func] - * @returns {*} - */ -showdown.subParser = function (name, func) { - 'use strict'; - if (showdown.helper.isString(name)) { - if (typeof func !== 'undefined') { - parsers[name] = func; - } else { - if (parsers.hasOwnProperty(name)) { - return parsers[name]; - } else { - throw Error('SubParser named ' + name + ' not registered!'); - } - } - } -}; - -/** - * Gets or registers an extension - * @static - * @param {string} name - * @param {object|function=} ext - * @returns {*} - */ -showdown.extension = function (name, ext) { - 'use strict'; - - if (!showdown.helper.isString(name)) { - throw Error('Extension \'name\' must be a string'); - } - - name = showdown.helper.stdExtName(name); - - // Getter - if (showdown.helper.isUndefined(ext)) { - if (!extensions.hasOwnProperty(name)) { - throw Error('Extension named ' + name + ' is not registered!'); - } - return extensions[name]; - - // Setter - } else { - // Expand extension if it's wrapped in a function - if (typeof ext === 'function') { - ext = ext(); - } - - // Ensure extension is an array - if (!showdown.helper.isArray(ext)) { - ext = [ext]; - } - - var validExtension = validate(ext, name); - - if (validExtension.valid) { - extensions[name] = ext; - } else { - throw Error(validExtension.error); - } - } -}; - -/** - * Gets all extensions registered - * @returns {{}} - */ -showdown.getAllExtensions = function () { - 'use strict'; - return extensions; -}; - -/** - * Remove an extension - * @param {string} name - */ -showdown.removeExtension = function (name) { - 'use strict'; - delete extensions[name]; -}; - -/** - * Removes all extensions - */ -showdown.resetExtensions = function () { - 'use strict'; - extensions = {}; -}; - -/** - * Validate extension - * @param {array} extension - * @param {string} name - * @returns {{valid: boolean, error: string}} - */ -function validate(extension, name) { - 'use strict'; - - var errMsg = (name) ? 'Error in ' + name + ' extension->' : 'Error in unnamed extension', - ret = { - valid: true, - error: '' - }; - - if (!showdown.helper.isArray(extension)) { - extension = [extension]; - } - - for (var i = 0; i < extension.length; ++i) { - var baseMsg = errMsg + ' sub-extension ' + i + ': ', - ext = extension[i]; - if (typeof ext !== 'object') { - ret.valid = false; - ret.error = baseMsg + 'must be an object, but ' + typeof ext + ' given'; - return ret; - } - - if (!showdown.helper.isString(ext.type)) { - ret.valid = false; - ret.error = baseMsg + 'property "type" must be a string, but ' + typeof ext.type + ' given'; - return ret; - } - - var type = ext.type = ext.type.toLowerCase(); - - // normalize extension type - if (type === 'language') { - type = ext.type = 'lang'; - } - - if (type === 'html') { - type = ext.type = 'output'; - } - - if (type !== 'lang' && type !== 'output' && type !== 'listener') { - ret.valid = false; - ret.error = baseMsg + 'type ' + type + ' is not recognized. Valid values: "lang/language", "output/html" or "listener"'; - return ret; - } - - if (type === 'listener') { - if (showdown.helper.isUndefined(ext.listeners)) { - ret.valid = false; - ret.error = baseMsg + '. Extensions of type "listener" must have a property called "listeners"'; - return ret; - } - } else { - if (showdown.helper.isUndefined(ext.filter) && showdown.helper.isUndefined(ext.regex)) { - ret.valid = false; - ret.error = baseMsg + type + ' extensions must define either a "regex" property or a "filter" method'; - return ret; - } - } - - if (ext.listeners) { - if (typeof ext.listeners !== 'object') { - ret.valid = false; - ret.error = baseMsg + '"listeners" property must be an object but ' + typeof ext.listeners + ' given'; - return ret; - } - for (var ln in ext.listeners) { - if (ext.listeners.hasOwnProperty(ln)) { - if (typeof ext.listeners[ln] !== 'function') { - ret.valid = false; - ret.error = baseMsg + '"listeners" property must be an hash of [event name]: [callback]. listeners.' + ln + - ' must be a function but ' + typeof ext.listeners[ln] + ' given'; - return ret; - } - } - } - } - - if (ext.filter) { - if (typeof ext.filter !== 'function') { - ret.valid = false; - ret.error = baseMsg + '"filter" must be a function, but ' + typeof ext.filter + ' given'; - return ret; - } - } else if (ext.regex) { - if (showdown.helper.isString(ext.regex)) { - ext.regex = new RegExp(ext.regex, 'g'); - } - if (!ext.regex instanceof RegExp) { - ret.valid = false; - ret.error = baseMsg + '"regex" property must either be a string or a RegExp object, but ' + typeof ext.regex + ' given'; - return ret; - } - if (showdown.helper.isUndefined(ext.replace)) { - ret.valid = false; - ret.error = baseMsg + '"regex" extensions must implement a replace string or function'; - return ret; - } - } - } - return ret; -} - -/** - * Validate extension - * @param {object} ext - * @returns {boolean} - */ -showdown.validateExtension = function (ext) { - 'use strict'; - - var validateExtension = validate(ext, null); - if (!validateExtension.valid) { - console.warn(validateExtension.error); - return false; - } - return true; -};