Compare commits

...
Sign in to create a new pull request.

1 commit

Author SHA1 Message Date
595e122043 Added code for deleting individuals 2021-02-18 19:06:34 +01:00
10 changed files with 283 additions and 3 deletions

View file

@ -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: <http://vitro.mannlib.cornell.edu/ns/vitro/0.7#>"
+ "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 ?individualURI";
@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<String,Object> map = new HashMap<String,Object>();
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;
}
}

View file

@ -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);

View file

@ -355,7 +355,7 @@ public class EditRequestDispatchController extends FreemarkerHttpServlet {
String predicateUri = EditConfigurationUtils.getPredicateUri(vreq);
String formParam = getFormParam(vreq);
//if no form parameter, then predicate uri and subject uri must both be populated
if (formParam == null || "".equals(formParam)) {
if ((formParam == null || "".equals(formParam)) && !isDeleteForm(vreq)) {
if ((predicateUri == null || predicateUri.trim().length() == 0)) {
return true;
}

View file

@ -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() {

View file

@ -7,6 +7,7 @@ import static edu.cornell.mannlib.vitro.webapp.auth.requestedAction.RequestedAct
import static edu.cornell.mannlib.vitro.webapp.auth.requestedAction.RequestedAction.SOME_URI;
import java.util.Collection;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
@ -27,6 +28,7 @@ import edu.cornell.mannlib.vitro.webapp.beans.VClass;
import edu.cornell.mannlib.vitro.webapp.config.ConfigurationProperties;
import edu.cornell.mannlib.vitro.webapp.controller.VitroRequest;
import edu.cornell.mannlib.vitro.webapp.controller.freemarker.UrlBuilder;
import edu.cornell.mannlib.vitro.webapp.controller.freemarker.UrlBuilder.ParamMap;
import edu.cornell.mannlib.vitro.webapp.controller.freemarker.UrlBuilder.Route;
import edu.cornell.mannlib.vitro.webapp.dao.ObjectPropertyStatementDao;
import edu.cornell.mannlib.vitro.webapp.dao.VClassDao;
@ -37,6 +39,7 @@ import edu.cornell.mannlib.vitro.webapp.web.templatemodels.BaseTemplateModel;
public abstract class BaseIndividualTemplateModel extends BaseTemplateModel {
private static final Log log = LogFactory.getLog(BaseIndividualTemplateModel.class);
private static final String EDIT_PATH = "editRequestDispatch";
protected final Individual individual;
protected final LoginStatusBean loginStatusBean;
@ -148,6 +151,23 @@ public abstract class BaseIndividualTemplateModel extends BaseTemplateModel {
public String getName() {
return individual.getName();
}
public String getDeleteUrl() {
Collection<String> types = getMostSpecificTypes();
ParamMap params = new ParamMap(
"objectUri", individual.getURI(),
"cmd", "delete",
"statement_label",getNameStatement().getValue(),
"statement_object",individual.getURI()
);
Iterator<String> typesIterator = types.iterator();
if (types.iterator().hasNext()) {
String type = typesIterator.next();
params.put("statement_type", type);
}
return UrlBuilder.getUrl(EDIT_PATH, params);
}
public Collection<String> getMostSpecificTypes() {
ObjectPropertyStatementDao opsDao = vreq.getWebappDaoFactory().getObjectPropertyStatementDao();

View file

@ -128,6 +128,7 @@
<owl:ObjectProperty rdf:about="&display;restrictResultsByClass"/>
<owl:ObjectProperty rdf:about="&display;getIndividualsForClass"/>
<owl:ObjectProperty rdf:about="&display;hasDataGetter"/>
<owl:DataProperty rdf:about="&display;hasDeleteQuery"/>
<owl:ObjectProperty rdf:about="&display;requiresAction">
</owl:ObjectProperty>

View file

@ -204,6 +204,9 @@ vitro:additionalLink
display:hasElement
a owl:ObjectProperty .
display:hasDeleteQuery
a owl:DataProperty .
display:excludeClass
a owl:ObjectProperty .

View file

@ -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

View file

@ -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 = "/" />
</#if>
<#assign statement = editConfiguration.objectStatementDisplay />
<#assign deletionTemplateName = editConfiguration.deleteTemplate/>
<form action="${editConfiguration.deleteIndividualProcessingUrl}" method="get">
<h2>${i18n().confirm_individual_deletion} </h2>
<input type="hidden" name="individualUri" value="${editConfiguration.objectUri}" role="input" />
<input type="hidden" name="redirectUrl" value="${redirectUrl}" role="input" />
<#assign deletionTemplateName = editConfiguration.deleteTemplate/>
<#if statement?has_content>
<#include deletionTemplateName />
</#if>
<br />
<p class="submit">
<input type="submit" id="submit" value="${i18n().delete_button}" role="button"/>
or
<a class="cancel" title="${i18n().cancel_title}" href="${editConfiguration.cancelUrl}">${i18n().cancel_link}</a>
</p>
</form>

View file

@ -203,6 +203,16 @@ name will be used as the label. -->
<a class="edit-${propertyLocalName}" href="${url}" title="${i18n().edit_entry}"><img class="edit-individual" data-range="${rangeUri}" src="${urls.images}/individual/editIcon.gif" alt="${i18n().edit_entry}" /></a>
</#macro>
<#macro deleteIndividualLink individual redirectUrl="/">
<#local url = individual.deleteUrl + "&redirectUrl=" + "${redirectUrl}">
<@showDeleteIndividualLink url />
</#macro>
<#macro showDeleteIndividualLink url>
<a class="delete-individual" href="${url}" title="${i18n().delete_entry}"><img class="delete-individual" src="${urls.images}/individual/deleteIcon.gif" alt="${i18n().delete_entry}" /></a>
</#macro>
<#macro deleteLink propertyLocalName propertyName statement rangeUri="">
<#local url = statement.deleteUrl>
<#if url?has_content>