From 4fdbb71fee96d87ed9a124fba00a47bbaea67f8d Mon Sep 17 00:00:00 2001 From: Georgy Litvinov Date: Wed, 17 Feb 2021 14:15:10 +0100 Subject: [PATCH] Added code for deleting individuals --- .../DeleteIndividualController.java | 201 ++++++++++++++++++ .../generators/DefaultDeleteGenerator.java | 18 +- .../edit/EditConfigurationTemplateModel.java | 4 + .../rdf/display/firsttime/application.owl | 1 + .../rdf/displayTbox/everytime/displayTBOX.n3 | 3 + webapp/src/main/webapp/i18n/all.properties | 1 + .../forms/confirmDeleteIndividualForm.ftl | 26 +++ 7 files changed, 252 insertions(+), 2 deletions(-) create mode 100644 api/src/main/java/edu/cornell/mannlib/vitro/webapp/controller/freemarker/DeleteIndividualController.java create mode 100644 webapp/src/main/webapp/templates/freemarker/edit/forms/confirmDeleteIndividualForm.ftl diff --git a/api/src/main/java/edu/cornell/mannlib/vitro/webapp/controller/freemarker/DeleteIndividualController.java b/api/src/main/java/edu/cornell/mannlib/vitro/webapp/controller/freemarker/DeleteIndividualController.java new file mode 100644 index 000000000..f7bdaaa41 --- /dev/null +++ b/api/src/main/java/edu/cornell/mannlib/vitro/webapp/controller/freemarker/DeleteIndividualController.java @@ -0,0 +1,201 @@ +package edu.cornell.mannlib.vitro.webapp.controller.freemarker; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.InputStream; +import java.nio.charset.StandardCharsets; +import java.util.HashMap; + +import javax.servlet.annotation.WebServlet; +import javax.servlet.http.HttpServletResponse; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.apache.jena.query.Query; +import org.apache.jena.query.QueryExecution; +import org.apache.jena.query.QueryExecutionFactory; +import org.apache.jena.query.QueryFactory; +import org.apache.jena.query.QuerySolution; +import org.apache.jena.query.QuerySolutionMap; +import org.apache.jena.query.ResultSet; +import org.apache.jena.rdf.model.Model; +import org.apache.jena.rdf.model.ResourceFactory; +import org.apache.jena.shared.Lock; + +import edu.cornell.mannlib.vitro.webapp.auth.permissions.SimplePermission; +import edu.cornell.mannlib.vitro.webapp.auth.requestedAction.AuthorizationRequest; +import edu.cornell.mannlib.vitro.webapp.controller.VitroRequest; +import edu.cornell.mannlib.vitro.webapp.controller.freemarker.responsevalues.RedirectResponseValues; +import edu.cornell.mannlib.vitro.webapp.controller.freemarker.responsevalues.ResponseValues; +import edu.cornell.mannlib.vitro.webapp.controller.freemarker.responsevalues.TemplateResponseValues; +import edu.cornell.mannlib.vitro.webapp.dao.DisplayVocabulary; +import edu.cornell.mannlib.vitro.webapp.dao.jena.QueryUtils; +import edu.cornell.mannlib.vitro.webapp.dao.jena.event.BulkUpdateEvent; +import edu.cornell.mannlib.vitro.webapp.modelaccess.ModelNames; +import edu.cornell.mannlib.vitro.webapp.rdfservice.ChangeSet; +import edu.cornell.mannlib.vitro.webapp.rdfservice.RDFService; +import edu.cornell.mannlib.vitro.webapp.rdfservice.RDFServiceException; +import edu.cornell.mannlib.vitro.webapp.rdfservice.impl.RDFServiceUtils; + +@WebServlet(name="DeleteIndividualController",urlPatterns="/deleteIndividualController") +public class DeleteIndividualController extends FreemarkerHttpServlet{ + + private static final Log log = LogFactory.getLog(DeleteIndividualController.class); + private static final boolean BEGIN = true; + private static final boolean END = !BEGIN; + + private static String TYPE_QUERY_START = "" + + "PREFIX vitro: " + + "SELECT ?type " + + "WHERE" + + "{ <"; + private static String TYPE_QUERY_END = "> vitro:mostSpecificType ?type ." + + "}"; + private static String queryForDeleteQuery = + "PREFIX display: <" + DisplayVocabulary.DISPLAY_NS +"> \n" + + "SELECT ?deleteQueryText WHERE { ?associatedURI display:hasDeleteQuery ?deleteQueryText }"; + + private static final String DEFAULT_DELETE_QUERY_TEXT = "DESCRIBE ?associatedURI"; + + @Override + protected AuthorizationRequest requiredActions(VitroRequest vreq) { + return SimplePermission.DO_FRONT_END_EDITING.ACTION; + } + + protected ResponseValues processRequest(VitroRequest vreq) { + String errorMessage = handleErrors(vreq); + if (!errorMessage.isEmpty()) { + return prepareErrorMessage(errorMessage); + } + String individualUri = vreq.getParameter("individualUri"); + String type = getObjectMostSpecificType(individualUri, vreq); + Model displayModel = vreq.getDisplayModel(); + + String delteQueryText = getDeleteQueryForType(type, displayModel); + byte[] toRemove = getIndividualsToDelete(individualUri, delteQueryText, vreq); + if (toRemove.length > 0) { + deleteIndividuals(toRemove,vreq); + } + String redirectUrl = getRedirectUrl(vreq); + + return new RedirectResponseValues(redirectUrl, HttpServletResponse.SC_SEE_OTHER); + } + + private String getRedirectUrl(VitroRequest vreq) { + String redirectUrl = vreq.getParameter("redirectUrl"); + if (redirectUrl != null) { + return redirectUrl; + } + return "/"; + } + + + private TemplateResponseValues prepareErrorMessage(String errorMessage) { + HashMap map = new HashMap(); + map.put("errorMessage", errorMessage); + return new TemplateResponseValues("error-message.ftl", map); + } + + private String handleErrors(VitroRequest vreq) { + String uri = vreq.getParameter("individualUri"); + if ( uri == null) { + return "Individual uri is null. No object to delete."; + } + if (uri.contains(">")) { + return "Individual uri shouldn't contain >"; + } + return ""; + } + + private static String getDeleteQueryForType(String typeURI,Model displayModel) { + + String deleteQueryText = DEFAULT_DELETE_QUERY_TEXT; + + Query queryForTypeSpecificDeleteQuery = QueryFactory.create(queryForDeleteQuery); + + QuerySolutionMap initialBindings = new QuerySolutionMap(); + initialBindings.add("associatedURI", ResourceFactory.createResource( typeURI )); + + displayModel.enterCriticalSection(Lock.READ); + try{ + QueryExecution qexec = QueryExecutionFactory.create(queryForTypeSpecificDeleteQuery,displayModel,initialBindings ); + try{ + ResultSet results = qexec.execSelect(); + while (results.hasNext()) { + QuerySolution solution = results.nextSolution(); + deleteQueryText = solution.get("deleteQueryText").toString(); + } + }finally{ qexec.close(); } + }finally{ displayModel.leaveCriticalSection(); } + + if (!deleteQueryText.equals(DEFAULT_DELETE_QUERY_TEXT)) { + log.debug("For " + typeURI + " found delete query \n" + deleteQueryText); + } else { + log.debug("For " + typeURI + " delete query not found. Using defalut query \n" + deleteQueryText); + } + return deleteQueryText; + } + + private String getObjectMostSpecificType(String individualURI, VitroRequest vreq) { + String type = ""; + try { + ResultSet results = QueryUtils.getLanguageNeutralQueryResults(makeTypeQuery(individualURI), vreq); + while (results.hasNext()) { + QuerySolution solution = results.nextSolution(); + type = solution.get("type").toString(); + log.debug(type); + } + } catch (Exception e) { + log.error("Failed to get type for individual URI " + individualURI); + log.error(e, e); + } + return type; + } + + private byte[] getIndividualsToDelete(String targetIndividual, String deleteQuery,VitroRequest vreq) { + ByteArrayOutputStream out = new ByteArrayOutputStream(); + Query queryForTypeSpecificDeleteQuery = QueryFactory.create(deleteQuery); + QuerySolutionMap initialBindings = new QuerySolutionMap(); + initialBindings.add("individualURI", ResourceFactory.createResource( targetIndividual )); + Model ontModel = vreq.getJenaOntModel(); + try { + QueryExecution qexec = QueryExecutionFactory.create(queryForTypeSpecificDeleteQuery,ontModel,initialBindings ); + Model results = qexec.execDescribe(); + results.write(out,"N3"); + + } catch (Exception e) { + log.error("Query raised an error \n" + deleteQuery); + log.error(e, e); + } + return out.toByteArray(); + } + + private String makeTypeQuery(String objectURI) { + return TYPE_QUERY_START + objectURI + TYPE_QUERY_END; + } + + private void deleteIndividuals(byte[] toRemove, VitroRequest vreq) { + String removingString = new String(toRemove, StandardCharsets.UTF_8); + RDFService rdfService = vreq.getRDFService(); + ChangeSet cs = makeChangeSet(rdfService); + InputStream in = new ByteArrayInputStream(toRemove); + cs.addRemoval(in, RDFServiceUtils.getSerializationFormatFromJenaString("N3"), ModelNames.ABOX_ASSERTIONS); + try { + rdfService.changeSetUpdate(cs); + } catch (RDFServiceException e) { + log.error("Got error while removing\n" + removingString); + throw new RuntimeException(e); + } + } + + private ChangeSet makeChangeSet(RDFService rdfService) { + ChangeSet cs = rdfService.manufactureChangeSet(); + cs.addPreChangeEvent(new BulkUpdateEvent(null, BEGIN)); + cs.addPostChangeEvent(new BulkUpdateEvent(null, END)); + return cs; +} + + + + +} diff --git a/api/src/main/java/edu/cornell/mannlib/vitro/webapp/edit/n3editing/configuration/generators/DefaultDeleteGenerator.java b/api/src/main/java/edu/cornell/mannlib/vitro/webapp/edit/n3editing/configuration/generators/DefaultDeleteGenerator.java index 7b77786d4..11ef2d455 100644 --- a/api/src/main/java/edu/cornell/mannlib/vitro/webapp/edit/n3editing/configuration/generators/DefaultDeleteGenerator.java +++ b/api/src/main/java/edu/cornell/mannlib/vitro/webapp/edit/n3editing/configuration/generators/DefaultDeleteGenerator.java @@ -28,7 +28,9 @@ public class DefaultDeleteGenerator extends BaseEditConfigurationGenerator imple private Integer dataHash = 0; private DataPropertyStatement dps = null; private String dataLiteral = null; - private String template = "confirmDeletePropertyForm.ftl"; + private String propertyTemplate = "confirmDeletePropertyForm.ftl"; + private String individualTemplate = "confirmDeleteIndividualForm.ftl"; + //In this case, simply return the edit configuration currently saved in session //Since this is forwarding from another form, an edit configuration should already exist in session @@ -43,12 +45,24 @@ public class DefaultDeleteGenerator extends BaseEditConfigurationGenerator imple if(editConfiguration == null) { editConfiguration = setupEditConfiguration(vreq, session); } - editConfiguration.setTemplate(template); //prepare update? prepare(vreq, editConfiguration); + if (editConfiguration.getPredicateUri() == null && editConfiguration.getSubjectUri() == null) { + editConfiguration.setTemplate(individualTemplate); + addRedirectUrl(vreq, editConfiguration); + }else { + editConfiguration.setTemplate(propertyTemplate); + } return editConfiguration; } + private void addRedirectUrl(VitroRequest vreq, EditConfigurationVTwo editConfiguration) { + String redirectUrl = vreq.getParameter("redirectUrl"); + if (redirectUrl != null) { + editConfiguration.addFormSpecificData("redirectUrl", redirectUrl); + } + } + private EditConfigurationVTwo setupEditConfiguration(VitroRequest vreq, HttpSession session) { EditConfigurationVTwo editConfiguration = new EditConfigurationVTwo(); initProcessParameters(vreq, session, editConfiguration); diff --git a/api/src/main/java/edu/cornell/mannlib/vitro/webapp/web/templatemodels/edit/EditConfigurationTemplateModel.java b/api/src/main/java/edu/cornell/mannlib/vitro/webapp/web/templatemodels/edit/EditConfigurationTemplateModel.java index 7f88848ec..05a9c676f 100644 --- a/api/src/main/java/edu/cornell/mannlib/vitro/webapp/web/templatemodels/edit/EditConfigurationTemplateModel.java +++ b/api/src/main/java/edu/cornell/mannlib/vitro/webapp/web/templatemodels/edit/EditConfigurationTemplateModel.java @@ -705,6 +705,10 @@ public class EditConfigurationTemplateModel extends BaseTemplateModel { public String getDeleteProcessingUrl() { return vreq.getContextPath() + "/deletePropertyController"; } + + public String getDeleteIndividualProcessingUrl() { + return vreq.getContextPath() + "/deleteIndividualController"; + } //TODO: Check if this logic is correct and delete prohibited does not expect a specific value public boolean isDeleteProhibited() { diff --git a/home/src/main/resources/rdf/display/firsttime/application.owl b/home/src/main/resources/rdf/display/firsttime/application.owl index 02dbd5736..3a48d3ef5 100644 --- a/home/src/main/resources/rdf/display/firsttime/application.owl +++ b/home/src/main/resources/rdf/display/firsttime/application.owl @@ -128,6 +128,7 @@ + diff --git a/home/src/main/resources/rdf/displayTbox/everytime/displayTBOX.n3 b/home/src/main/resources/rdf/displayTbox/everytime/displayTBOX.n3 index d5fcf9ec2..07478b771 100644 --- a/home/src/main/resources/rdf/displayTbox/everytime/displayTBOX.n3 +++ b/home/src/main/resources/rdf/displayTbox/everytime/displayTBOX.n3 @@ -204,6 +204,9 @@ vitro:additionalLink display:hasElement a owl:ObjectProperty . +display:hasDeleteQuery + a owl:DataProperty . + display:excludeClass a owl:ObjectProperty . diff --git a/webapp/src/main/webapp/i18n/all.properties b/webapp/src/main/webapp/i18n/all.properties index 1909545ad..5f7be8861 100644 --- a/webapp/src/main/webapp/i18n/all.properties +++ b/webapp/src/main/webapp/i18n/all.properties @@ -720,6 +720,7 @@ there_are_no_entries_for_selection = There are no entries in the system from whi the_range_class_does_not_exist= The range class for this property does not exist in the system. editing_prohibited = This property is currently configured to prohibit editing. confirm_entry_deletion_from = Are you sure you want to delete the following entry from +confirm_individual_deletion = Are you sure you want to delete the following individual? edit_date_time_value = Edit Date/Time Value create_date_time_value = Create Date/Time Value diff --git a/webapp/src/main/webapp/templates/freemarker/edit/forms/confirmDeleteIndividualForm.ftl b/webapp/src/main/webapp/templates/freemarker/edit/forms/confirmDeleteIndividualForm.ftl new file mode 100644 index 000000000..01d28ec62 --- /dev/null +++ b/webapp/src/main/webapp/templates/freemarker/edit/forms/confirmDeleteIndividualForm.ftl @@ -0,0 +1,26 @@ +<#-- $This file is distributed under the terms of the license in LICENSE$ --> +<#if editConfiguration.pageData.redirectUrl??> + <#assign redirectUrl = editConfiguration.pageData.redirectUrl /> +<#else> + <#assign redirectUrl = "/" /> + +<#assign statement = editConfiguration.objectStatementDisplay /> +<#assign deletionTemplateName = editConfiguration.deleteTemplate/> + +
+

${i18n().confirm_individual_deletion}

+ + + + <#assign deletionTemplateName = editConfiguration.deleteTemplate/> + + <#if statement?has_content> + <#include deletionTemplateName /> + +
+

+ + or + ${i18n().cancel_link} +

+