Added Delete Item

This commit is contained in:
Francesco Mangiacrapa 2024-02-19 16:08:09 +01:00
parent 2ff3e3b262
commit 9d7efed441
9 changed files with 392 additions and 28 deletions

View File

@ -10,6 +10,8 @@ This project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.htm
- Implemented the Edit facility [#26640]
- Added spinner during resource creation [#25433]
- Implemented the Delete facility [#26793]
## [v2.1.2] - 2023-03-10

View File

@ -43,7 +43,7 @@ public interface CKanPublisherService extends RemoteService {
* @return @return a DatasetMetadataBean on success, <b>null</b> on error.
* @throws Exception the exception
*/
DatasetBean getDatasetBean(String folderIdOrFileId) throws Exception;
DatasetBean buildPartialDatasetBean(String folderIdOrFileId) throws Exception;
/**
* Try to create such dataset starting from the information contained into the
@ -172,4 +172,22 @@ public interface CKanPublisherService extends RemoteService {
* @throws Exception the exception
*/
String getPublicLinkForFileItemId(String itemId, boolean shortenUrl) throws Exception;
/**
* Gets the basic dataset bean.
*
* @param datasetIdOrName the dataset id or name
* @return the basic dataset bean
* @throws Exception the exception
*/
DatasetBean getBasicDatasetBean(String datasetIdOrName) throws Exception;
/**
* Delete item.
*
* @param datasetBean the dataset bean
* @return true, if successful
* @throws Exception the exception
*/
boolean deleteItem(DatasetBean datasetBean) throws Exception;
}

View File

@ -9,6 +9,7 @@ import org.gcube.portlets.widgets.ckandatapublisherwidget.shared.ResourceElement
import org.gcube.portlets.widgets.mpformbuilder.shared.license.LicenseBean;
import org.gcube.portlets.widgets.mpformbuilder.shared.metadata.MetaDataProfileBean;
import com.google.gwt.core.client.GWT;
import com.google.gwt.user.client.rpc.AsyncCallback;
/**
@ -18,6 +19,36 @@ import com.google.gwt.user.client.rpc.AsyncCallback;
*/
public interface CKanPublisherServiceAsync {
/**
* The Class Util.
*
* @author Francesco Mangiacrapa at ISTI-CNR francesco.mangiacrapa@isti.cnr.it
*
* Feb 19, 2024
*/
public static final class Util {
private static CKanPublisherServiceAsync instance;
/**
* Gets the single instance of Util.
*
* @return single instance of Util
*/
public static final CKanPublisherServiceAsync getInstance() {
if (instance == null) {
instance = (CKanPublisherServiceAsync) GWT.create(CKanPublisherService.class);
}
return instance;
}
/**
* Instantiates a new util.
*/
private Util() {
// Utility class should not be instantiated
}
}
/**
* Retrieve the list of licenses to show to the user.
*
@ -33,7 +64,7 @@ public interface CKanPublisherServiceAsync {
* @param callback the callback
* @return @return a DatasetMetadataBean on success, <b>null</b> on error.
*/
void getDatasetBean(String folderIdOrFileId, AsyncCallback<DatasetBean> callback);
void buildPartialDatasetBean(String folderIdOrFileId, AsyncCallback<DatasetBean> callback);
/**
* Try to create such dataset starting from the information contained into the
@ -141,9 +172,9 @@ public interface CKanPublisherServiceAsync {
/**
* Gets the dataset bean.
*
* @param itemID the item ID
* @param itemID the item ID
* @param callback the callback
* @return the dataset bean
* @throws Exception the exception
*/
void getDatasetBeanForUpdate(String itemID, AsyncCallback<DatasetBean> callback);
@ -164,7 +195,6 @@ public interface CKanPublisherServiceAsync {
*
* @param toUpdate the to update
* @param callaback the callaback
* @throws Exception the exception
*/
void updateCKANDataset(DatasetBean toUpdate, AsyncCallback<DatasetBean> callaback);
@ -175,7 +205,23 @@ public interface CKanPublisherServiceAsync {
* @param shortenUrl the shorten url
* @param callaback the callaback
* @return the public link for file item id
* @throws Exception the exception
*/
void getPublicLinkForFileItemId(String itemId, boolean shortenUrl, AsyncCallback<String> callaback);
/**
* Gets the basic dataset bean.
*
* @param datasetIdOrName the dataset id or name
* @param callaback the callaback
* @return the basic dataset bean
*/
void getBasicDatasetBean(String datasetIdOrName, AsyncCallback<DatasetBean> callaback);
/**
* Delete item.
*
* @param datasetBean the dataset bean
* @param callaback the callaback
*/
void deleteItem(DatasetBean datasetBean, AsyncCallback<Boolean> callaback);
}

View File

@ -0,0 +1,167 @@
package org.gcube.portlets.widgets.ckandatapublisherwidget.client.ui.action;
import org.gcube.portlets.widgets.ckandatapublisherwidget.client.CKanPublisherServiceAsync;
import org.gcube.portlets.widgets.ckandatapublisherwidget.shared.DatasetBean;
import org.gcube.portlets.widgets.mpformbuilder.client.ui.utils.LoaderIcon;
import com.github.gwtbootstrap.client.ui.AlertBlock;
import com.github.gwtbootstrap.client.ui.Button;
import com.github.gwtbootstrap.client.ui.Icon;
import com.github.gwtbootstrap.client.ui.constants.AlertType;
import com.github.gwtbootstrap.client.ui.constants.IconType;
import com.google.gwt.core.client.GWT;
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;
import com.google.gwt.uibinder.client.UiField;
import com.google.gwt.uibinder.client.UiHandler;
import com.google.gwt.user.client.rpc.AsyncCallback;
import com.google.gwt.user.client.ui.Composite;
import com.google.gwt.user.client.ui.FlowPanel;
import com.google.gwt.user.client.ui.HTML;
import com.google.gwt.user.client.ui.HTMLPanel;
import com.google.gwt.user.client.ui.Widget;
public class DeleteItemPanel extends Composite {
private static DeleteItemPanelUiBinder uiBinder = GWT.create(DeleteItemPanelUiBinder.class);
@UiField
Button buttonDelete;
@UiField
AlertBlock infoBlock;
@UiField
HTMLPanel deleteMessage;
@UiField
FlowPanel panelConfirm;
private FlowPanel alertPanel = new FlowPanel();
private Icon iconSpinner = new Icon(IconType.SPINNER);
private HandlerManager mainApplicationBusRef;
private final CKanPublisherServiceAsync ckanServices = CKanPublisherServiceAsync.Util.getInstance();
private String datasetIdOrName;
protected static final String MISSING_PUBLISH_RIGHTS = "It seems you are not authorized to delete this item. Either you must be the author item or Catalogue Admininistrator in the organization";
interface DeleteItemPanelUiBinder extends UiBinder<Widget, DeleteItemPanel> {
}
private DatasetBean selectedDataset;
public DeleteItemPanel(HandlerManager mainApplicationBusRef, String datasetIdOrName) {
initWidget(uiBinder.createAndBindUi(this));
this.datasetIdOrName = datasetIdOrName;
this.mainApplicationBusRef = mainApplicationBusRef;
iconSpinner.setSpin(true);
iconSpinner.getElement().getStyle().setMarginLeft(5, Unit.PX);
iconSpinner.getElement().getStyle().setProperty("animation", "spin 1s infinite linear");
alertPanel.add(iconSpinner);
infoBlock.add(alertPanel);
infoBlock.setVisible(true);
LoaderIcon loaderIcon = new LoaderIcon("Checking your permissions...");
setAlertBlock(null, loaderIcon, AlertType.INFO, true);
ckanServices.isPublisherOwnerOrAdminUser(this.datasetIdOrName, new AsyncCallback<Boolean>() {
@Override
public void onFailure(Throwable caught) {
setAlertBlock(MISSING_PUBLISH_RIGHTS + ". Error: " + caught.getMessage(), null, AlertType.ERROR, true);
}
@Override
public void onSuccess(Boolean result) {
setAlertBlock("", null, null, false);
if (result) {
LoaderIcon loaderIcon = new LoaderIcon("Loading the item....");
setAlertBlock(null, loaderIcon, AlertType.INFO, true);
ckanServices.getBasicDatasetBean(datasetIdOrName, new AsyncCallback<DatasetBean>() {
@Override
public void onFailure(Throwable caught) {
setAlertBlock(caught.getMessage(), null, AlertType.ERROR, true);
}
@Override
public void onSuccess(DatasetBean result) {
setAlertBlock("", null, null, false);
panelConfirm.setVisible(true);
selectedDataset = result;
deleteMessage.add(new HTML("<div><i>" + result.getTitle() + "</i></div>"));
}
});
} else {
setAlertBlock(MISSING_PUBLISH_RIGHTS, null, AlertType.WARNING, true);
}
}
});
}
@UiHandler("buttonDelete")
void onClick(ClickEvent e) {
LoaderIcon loaderIcon = new LoaderIcon("Deleting the item....");
setAlertBlock(null, loaderIcon, AlertType.INFO, true);
ckanServices.deleteItem(selectedDataset, new AsyncCallback<Boolean>() {
@Override
public void onFailure(Throwable caught) {
setAlertBlock("Sorry, an error occurred on deleting the item. Error: " + caught.getMessage(), null,
AlertType.ERROR, true);
}
@Override
public void onSuccess(Boolean result) {
if (result) {
setAlertBlock("Item deleted correclty", null, AlertType.INFO, true);
} else {
setAlertBlock("Sorry, I cannot delete the item. Please contact the support", null, AlertType.INFO,
true);
}
}
});
}
/**
* change alert block behavior.
*
* @param textToShow the text to show
* @param type the type
* @param visible the visible
*/
private void setAlertBlock(String textToShow, LoaderIcon loader, AlertType type, boolean visible) {
alertPanel.clear();
if (loader != null)
alertPanel.add(loader);
if (type != null)
infoBlock.setType(type);
if (textToShow != null)
alertPanel.add(new HTML(textToShow));
infoBlock.setVisible(visible);
}
}

View File

@ -0,0 +1,36 @@
<!DOCTYPE ui:UiBinder SYSTEM "http://dl.google.com/gwt/DTD/xhtml.ent">
<ui:UiBinder xmlns:ui="urn:ui:com.google.gwt.uibinder"
xmlns:g="urn:import:com.google.gwt.user.client.ui"
xmlns:b="urn:import:com.github.gwtbootstrap.client.ui">
<ui:style>
.block-alert-style {
margin-top: 10px;
padding: 10px;
margin-bottom: 10px;
}
.float-right {
float: right;
}
.delete-message-panel {
margin: 20px;
padding: 5px;
margin-bottom: 30px;
font-size: 14px;
}
</ui:style>
<g:FlowPanel>
<b:AlertBlock type="INFO" close="false" animation="true"
visible="false" ui:field="infoBlock"
styleName="{style.block-alert-style}"></b:AlertBlock>
<g:FlowPanel ui:field="panelConfirm" visible="false">
<g:HTMLPanel ui:field="deleteMessage"
addStyleNames="{style.delete-message-panel}">
Do you want to delete the item?
</g:HTMLPanel>
<b:Button type="PRIMARY" ui:field="buttonDelete"
addStyleNames="{style.float-right}">Delete</b:Button>
</g:FlowPanel>
</g:FlowPanel>
</ui:UiBinder>

View File

@ -11,7 +11,6 @@ import java.util.Map.Entry;
import java.util.Set;
import org.gcube.portlets.widgets.ckandatapublisherwidget.client.CKanMetadataPublisher;
import org.gcube.portlets.widgets.ckandatapublisherwidget.client.CKanPublisherService;
import org.gcube.portlets.widgets.ckandatapublisherwidget.client.CKanPublisherServiceAsync;
import org.gcube.portlets.widgets.ckandatapublisherwidget.client.events.CloseCreationFormEvent;
import org.gcube.portlets.widgets.ckandatapublisherwidget.client.events.CloseCreationFormEventHandler;
@ -255,7 +254,7 @@ public class CreateDatasetForm extends Composite {
private Icon iconSpinner = new Icon(IconType.SPINNER);
// Create a remote service proxy to talk to the server-side ckan service.
private final CKanPublisherServiceAsync ckanServices = GWT.create(CKanPublisherService.class);
private final CKanPublisherServiceAsync ckanServices = CKanPublisherServiceAsync.Util.getInstance();
// private static final String REGEX_TITLE_PRODUCT_SUBWORD = "[^a-zA-Z0-9_.-]";
private static final String REGEX_MAIL = "\\b[\\w.%-]+@[-.\\w]+\\.[A-Za-z]{2,4}\\b";
@ -386,6 +385,7 @@ public class CreateDatasetForm extends Composite {
iconSpinner.setSpin(true);
iconSpinner.getElement().getStyle().setMarginLeft(5, Unit.PX);
iconSpinner.getElement().getStyle().setProperty("animation", "spin 1s infinite linear");
List<String> listOfSteps = null;
if (isWorkspaceRequest) {
@ -425,7 +425,7 @@ public class CreateDatasetForm extends Composite {
setAlertBlock(null, loader, AlertType.INFO, true);
// get back the licenses and the metadata information
ckanServices.getDatasetBean(idFolderOrFileWorkspace, new AsyncCallback<DatasetBean>() {
ckanServices.buildPartialDatasetBean(idFolderOrFileWorkspace, new AsyncCallback<DatasetBean>() {
@Override
public void onFailure(Throwable caught) {
@ -532,7 +532,7 @@ public class CreateDatasetForm extends Composite {
// force tags
LoaderIcon loaderT = new LoaderIcon("Checking for tags vocabulary, please wait...");
setAlertBlock(null, loader, AlertType.INFO, true);
setAlertBlock(null, loaderT, AlertType.INFO, true);
ckanServices.getTagsForOrganization(orgName, new AsyncCallback<List<String>>() {
@Override

View File

@ -11,7 +11,6 @@ import java.util.Map.Entry;
import java.util.Set;
import org.gcube.portlets.widgets.ckandatapublisherwidget.client.CKanMetadataPublisher;
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.CloseCreationFormEvent;
@ -275,7 +274,7 @@ public class UpdateDatasetForm extends Composite {
private Icon iconSpinner = new Icon(IconType.SPINNER);
// Create a remote service proxy to talk to the server-side ckan service.
private final CKanPublisherServiceAsync ckanServices = GWT.create(CKanPublisherService.class);
private final CKanPublisherServiceAsync ckanServices = CKanPublisherServiceAsync.Util.getInstance();
// private static final String REGEX_TITLE_PRODUCT_SUBWORD = "[^a-zA-Z0-9_.-]";
private static final String REGEX_MAIL = "\\b[\\w.%-]+@[-.\\w]+\\.[A-Za-z]{2,4}\\b";
@ -391,6 +390,7 @@ public class UpdateDatasetForm extends Composite {
iconSpinner.setSpin(true);
iconSpinner.getElement().getStyle().setMarginLeft(5, Unit.PX);
iconSpinner.getElement().getStyle().setProperty("animation", "spin 1s infinite linear");
infoBlock.add(alertPanel);
@ -682,7 +682,7 @@ public class UpdateDatasetForm extends Composite {
});
} else {
setAlertBlock(MISSING_PUBLISH_RIGHTS, null, AlertType.ERROR, true);
setAlertBlock(MISSING_PUBLISH_RIGHTS, null, AlertType.WARNING, true);
}
}

View File

@ -210,13 +210,6 @@ public class CKANPublisherServicesImpl extends RemoteServiceServlet implements C
String keyPerScope = CatalogueUtilMethods
.concatenateSessionKeyScope(SessionCatalogueAttributes.CKAN_LICENSES_KEY, scope);
// if(!isWithinPortal()){
// logger.info("DEV MODE returning funny licenses...");
// List<LicenseBean> licenses = new ArrayList<LicenseBean>();
// licenses.add(new LicenseBean("AFL-3.0", "https://opensource.org/licenses/AFL-3.0"));
// return licenses;
// }
List<LicenseBean> licensesBean = null;
if (httpSession.getAttribute(keyPerScope) != null) {
licensesBean = (List<LicenseBean>) httpSession.getAttribute(keyPerScope);
@ -243,14 +236,14 @@ public class CKANPublisherServicesImpl extends RemoteServiceServlet implements C
}
/**
* Gets the dataset bean.
* Builds the partial dataset bean.
*
* @param folderId the folder id
* @return the dataset bean
* @throws Exception the exception
*/
@Override
public DatasetBean getDatasetBean(String folderId) throws Exception {
public DatasetBean buildPartialDatasetBean(String folderId) throws Exception {
DatasetBean bean = null;
String userName = GenericUtils.getCurrentUser(getThreadLocalRequest()).getUsername();
@ -273,7 +266,7 @@ public class CKANPublisherServicesImpl extends RemoteServiceServlet implements C
logger.debug("Building bean");
bean = new DatasetBean();
bean.setId(folderId);
bean.setWSFolderId(folderId);
bean.setOwnerIdentifier(userName);
bean.setVersion(1);
bean.setAuthorName(userOwner.getFirstName());
@ -334,6 +327,71 @@ public class CKANPublisherServicesImpl extends RemoteServiceServlet implements C
return bean;
}
/**
* Gets the basic dataset bean.
*
* @param datasetIdOrName the dataset id or name
* @return the basic dataset bean
* @throws Exception the exception
*/
@Override
public DatasetBean getBasicDatasetBean(String datasetIdOrName) throws Exception {
logger.debug("getBasicDatasetBean called for {}", datasetIdOrName);
DatasetBean bean = null;
String userName = GenericUtils.getCurrentUser(getThreadLocalRequest()).getUsername();
logger.info("DatasetBeanForUpdate request for " + datasetIdOrName + " and " + userName);
String scopePerCurrentUrl = GenericUtils.getScopeFromClientUrl(getThreadLocalRequest());
DataCatalogue utils = getCatalogue(scopePerCurrentUrl);
CkanDataset dataset = utils.getDataset(datasetIdOrName, userName);
if (dataset == null) {
// the user cannot read the item, so he/she is not the owner nor the admin
throw new Exception("Dataset with id " + datasetIdOrName + " not found for user " + userName);
}
logger.debug("Building bean");
bean = new DatasetBean();
// Basic info
bean.setId(datasetIdOrName);
bean.setCkanName(dataset.getName());
bean.setTitle(dataset.getTitle());
bean.setDescription(dataset.getNotes());
bean.setLicense(dataset.getLicenseTitle());
bean.setVisibile(dataset.isPriv());
long version = 1;
try {
version = Long.parseLong(dataset.getVersion());
} catch (Exception e) {
// TODO: handle exception
}
bean.setVersion(version);
// Other basic info
bean.setOwnerIdentifier(dataset.getCreatorUserId());
bean.setAuthorFullName(dataset.getAuthor());
bean.setAuthorEmail(dataset.getAuthorEmail());
bean.setMaintainer(dataset.getMaintainer());
bean.setMaintainerEmail(dataset.getMaintainerEmail());
// Organization
CkanOrganization ckanOrganization = dataset.getOrganization();
final OrganizationBean ckanOrganizationBean = new OrganizationBean(ckanOrganization.getTitle(),
ckanOrganization.getName(), true);
bean.setOrganizationList(Arrays.asList(ckanOrganizationBean));
bean.setSelectedOrganization(ckanOrganization.getName());
logger.debug("Returning minimal bean " + bean);
logger.info("Returning minimal bean for dataset title: {}" + bean.getTitle());
return bean;
}
/**
* Gets the dataset bean for update.
*
@ -978,6 +1036,30 @@ public class CKANPublisherServicesImpl extends RemoteServiceServlet implements C
}
}
/**
* Delete item.
*
* @param datasetBean the dataset bean
* @return true, if successful
* @throws Exception the exception
*/
@Override
public boolean deleteItem(DatasetBean datasetBean) throws Exception {
logger.debug("delete item called: {}" + datasetBean);
if (datasetBean == null)
throw new Exception("Error: the dataset is null");
try {
String scopeFromOrgName = getScopeFromOrgName(datasetBean.getSelectedOrganization());
logger.debug("Going to delete dataset with name: {}", datasetBean.getCkanName());
return getCatalogue(scopeFromOrgName).deleteDataset(datasetBean.getCkanName());
} catch (Exception e) {
logger.error("Unable to check if such a dataset id already exists", e);
throw new Exception("Unable to check if such a dataset id already exists " + e.getMessage());
}
}
/**
* The method tries to retrieve the scope related to the organization using the
* map first, if no match is found, it retrieves such information by using
@ -1268,7 +1350,7 @@ public class CKANPublisherServicesImpl extends RemoteServiceServlet implements C
/**
* Gets the public link for file item id.
*
* @param itemId the item id
* @param itemId the item id
* @param shortenUrl the shorten url
* @return the public link for file item id
* @throws Exception the exception
@ -1276,7 +1358,8 @@ public class CKANPublisherServicesImpl extends RemoteServiceServlet implements C
@Override
public String getPublicLinkForFileItemId(String itemId, boolean shortenUrl) throws Exception {
logger.debug("get Public Link For ItemId: " + itemId);
//String scopePerCurrentUrl = GenericUtils.getScopeFromClientUrl(getThreadLocalRequest());
// String scopePerCurrentUrl =
// GenericUtils.getScopeFromClientUrl(getThreadLocalRequest());
String theLink = null;
try {
GenericUtils.getCurrentContext(getThreadLocalRequest(), true);
@ -1284,7 +1367,7 @@ public class CKANPublisherServicesImpl extends RemoteServiceServlet implements C
Map<String, String> params = new HashMap<String, String>();
params.put("id", itemId);
theLink = resolver.getLink(params, true);
logger.info("Returning public link: "+theLink);
logger.info("Returning public link: " + theLink);
} catch (UriResolverMapException e) {
logger.error("UriResolverMapException", e);
throw new Exception("Sorry an error occurred on getting the link " + e.getMessage());
@ -1294,8 +1377,8 @@ public class CKANPublisherServicesImpl extends RemoteServiceServlet implements C
} catch (Exception e) {
logger.error("Failed to check the user's role", e);
throw new Exception("Sorry an error occurred on getting the link " + e.getMessage());
}
}
return theLink;
}

View File

@ -57,6 +57,7 @@ public class DatasetBean implements Serializable, IsSerializable {
private List<OrganizationBean> groups;
private List<OrganizationBean> groupsForceCreation;
private List<ResourceElementBean> resources;
private String wsFolderId;
/**
* Instantiates a new dataset bean.
@ -569,6 +570,15 @@ public class DatasetBean implements Serializable, IsSerializable {
return resources;
}
public void setWSFolderId(String folderId) {
this.wsFolderId = folderId;
}
public String getWsFolderId() {
return wsFolderId;
}
@Override
public String toString() {
StringBuilder builder = new StringBuilder();
@ -622,6 +632,8 @@ public class DatasetBean implements Serializable, IsSerializable {
builder.append(groups);
builder.append(", groupsForceCreation=");
builder.append(groupsForceCreation);
builder.append(", wsFolderId=");
builder.append(wsFolderId);
builder.append("]");
return builder.toString();
}