Added code for deleting individuals (#213)
* Added code for deleting individuals * Allow admins delete individuals * Fixed delete individual url params * Improved query safety * refact: fixed constants' names, fixed mistakes, removed useless code * refact: refactored sparql queries * fix: fixed error logging mistakes * fix: use CONSTRUCT instead of DESCRIBE as less potentionally problematic, lookup for mostSpecificTypes * feat: add delete link in vitro individual profile if individual is editable * fix: renamed individualURI to individualUri and added safety check * fix: use ABox model to construct triples to delete
This commit is contained in:
parent
c41853440f
commit
ff285fb80d
11 changed files with 327 additions and 9 deletions
|
@ -0,0 +1,211 @@
|
|||
package edu.cornell.mannlib.vitro.webapp.controller.freemarker;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.InputStream;
|
||||
import java.io.StringWriter;
|
||||
import java.util.HashMap;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
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.ModelFactory;
|
||||
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.beans.Individual;
|
||||
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 static edu.cornell.mannlib.vitro.webapp.dao.DisplayVocabulary.HAS_DELETE_QUERY;
|
||||
|
||||
import edu.cornell.mannlib.vitro.webapp.dao.jena.event.BulkUpdateEvent;
|
||||
import edu.cornell.mannlib.vitro.webapp.modelaccess.ModelAccess;
|
||||
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.impl.RDFServiceUtils;
|
||||
|
||||
@WebServlet(name = "DeleteIndividualController", urlPatterns = "/deleteIndividualController")
|
||||
public class DeleteIndividualController extends FreemarkerHttpServlet {
|
||||
|
||||
private static final String INDIVIDUAL_URI = "individualUri";
|
||||
private static final long serialVersionUID = 1L;
|
||||
private static final Log log = LogFactory.getLog(DeleteIndividualController.class);
|
||||
private static final boolean BEGIN = true;
|
||||
private static final boolean END = !BEGIN;
|
||||
|
||||
private static String queryForDeleteQuery = ""
|
||||
+ "SELECT ?deleteQueryText WHERE { "
|
||||
+ "?associatedUri <" + HAS_DELETE_QUERY + "> ?deleteQueryText ."
|
||||
+ "}";
|
||||
|
||||
private static final String DEFAULT_DELETE_QUERY_TEXT = ""
|
||||
+ "CONSTRUCT { ?individualUri ?p1 ?o1 . ?s2 ?p2 ?individualUri . } "
|
||||
+ "WHERE {"
|
||||
+ " { ?individualUri ?p1 ?o1 . } UNION { ?s2 ?p2 ?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(INDIVIDUAL_URI);
|
||||
List<String> types = getObjectMostSpecificTypes(individualUri, vreq);
|
||||
Model displayModel = vreq.getDisplayModel();
|
||||
|
||||
String deleteQueryText = getDeleteQueryForTypes(types, displayModel);
|
||||
Model toRemove = getIndividualsToDelete(individualUri, deleteQueryText, vreq);
|
||||
if (toRemove.size() > 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(INDIVIDUAL_URI);
|
||||
if (uri == null) {
|
||||
return "Individual uri is null. No object to delete.";
|
||||
}
|
||||
if (uri.contains("<") || uri.contains(">")) {
|
||||
return "Individual IRI shouldn't contain '<' or '>";
|
||||
}
|
||||
return "";
|
||||
}
|
||||
|
||||
private static String getDeleteQueryForTypes(List<String> types, Model displayModel) {
|
||||
String deleteQueryText = DEFAULT_DELETE_QUERY_TEXT;
|
||||
String foundType = "";
|
||||
for ( String type: types) {
|
||||
Query queryForTypeSpecificDeleteQuery = QueryFactory.create(queryForDeleteQuery);
|
||||
QuerySolutionMap initialBindings = new QuerySolutionMap();
|
||||
initialBindings.add("associatedURI", ResourceFactory.createResource(type));
|
||||
displayModel.enterCriticalSection(Lock.READ);
|
||||
try {
|
||||
QueryExecution qexec = QueryExecutionFactory.create(queryForTypeSpecificDeleteQuery, displayModel,
|
||||
initialBindings);
|
||||
try {
|
||||
ResultSet results = qexec.execSelect();
|
||||
if (results.hasNext()) {
|
||||
QuerySolution solution = results.nextSolution();
|
||||
deleteQueryText = solution.get("deleteQueryText").toString();
|
||||
foundType = type;
|
||||
}
|
||||
} finally {
|
||||
qexec.close();
|
||||
}
|
||||
} finally {
|
||||
displayModel.leaveCriticalSection();
|
||||
}
|
||||
if (!foundType.isEmpty()) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!foundType.isEmpty()) {
|
||||
log.debug("For " + foundType + " found delete query \n" + deleteQueryText);
|
||||
if (!deleteQueryText.contains(INDIVIDUAL_URI)){
|
||||
log.error("Safety check failed. Delete query text should contain " + INDIVIDUAL_URI + ", "
|
||||
+ "but it didn't. To prevent bad consequences query was rejected.");
|
||||
log.error("Delete query which caused the error: \n" + deleteQueryText);
|
||||
deleteQueryText = DEFAULT_DELETE_QUERY_TEXT;
|
||||
}
|
||||
} else {
|
||||
log.debug("For most specific types: " + types.stream().collect(Collectors.joining(",")) + " no delete query was found. Using default query \n" + deleteQueryText);
|
||||
}
|
||||
return deleteQueryText;
|
||||
}
|
||||
|
||||
private List<String> getObjectMostSpecificTypes(String individualUri, VitroRequest vreq) {
|
||||
List<String> types = new LinkedList<String>();
|
||||
Individual individual = vreq.getWebappDaoFactory().getIndividualDao().getIndividualByURI(individualUri);
|
||||
if (individual != null) {
|
||||
types = individual.getMostSpecificTypeURIs();
|
||||
}
|
||||
if (types.isEmpty()) {
|
||||
log.error("Failed to get most specific type for individual Uri " + individualUri);
|
||||
}
|
||||
return types;
|
||||
}
|
||||
|
||||
private Model getIndividualsToDelete(String targetIndividual, String deleteQuery, VitroRequest vreq) {
|
||||
try {
|
||||
Query queryForTypeSpecificDeleteQuery = QueryFactory.create(deleteQuery);
|
||||
QuerySolutionMap bindings = new QuerySolutionMap();
|
||||
bindings.add(INDIVIDUAL_URI, ResourceFactory.createResource(targetIndividual));
|
||||
Model ontModel = ModelAccess.on(vreq).getOntModelSelector().getABoxModel();
|
||||
QueryExecution qexec = QueryExecutionFactory.create(queryForTypeSpecificDeleteQuery, ontModel, bindings);
|
||||
Model results = qexec.execConstruct();
|
||||
return results;
|
||||
|
||||
} catch (Exception e) {
|
||||
log.error("Query raised an error \n" + deleteQuery);
|
||||
log.error(e, e);
|
||||
}
|
||||
return ModelFactory.createDefaultModel();
|
||||
}
|
||||
|
||||
private void deleteIndividuals(Model model, VitroRequest vreq) {
|
||||
RDFService rdfService = vreq.getRDFService();
|
||||
ChangeSet cs = makeChangeSet(rdfService);
|
||||
try {
|
||||
ByteArrayOutputStream out = new ByteArrayOutputStream();
|
||||
model.write(out, "N3");
|
||||
InputStream in = new ByteArrayInputStream(out.toByteArray());
|
||||
cs.addRemoval(in, RDFServiceUtils.getSerializationFormatFromJenaString("N3"), ModelNames.ABOX_ASSERTIONS);
|
||||
rdfService.changeSetUpdate(cs);
|
||||
} catch (Exception e) {
|
||||
StringWriter sw = new StringWriter();
|
||||
model.write(sw, "N3");
|
||||
log.error("Got " + e.getClass().getSimpleName() + " while removing\n" + sw.toString());
|
||||
log.error(e,e);
|
||||
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;
|
||||
}
|
||||
|
||||
}
|
|
@ -53,6 +53,8 @@ public class DisplayVocabulary {
|
|||
//specific case for internal class, value is true or false
|
||||
public static final String RESTRICT_RESULTS_BY_INTERNAL = NS + "restrictResultsByInternalClass";
|
||||
|
||||
public static final String HAS_DELETE_QUERY = NS + "hasDeleteQuery";
|
||||
|
||||
|
||||
/* Data Properties */
|
||||
public static final DatatypeProperty URL_MAPPING = m_model.createDatatypeProperty(NS + "urlMapping");
|
||||
|
|
|
@ -2,8 +2,6 @@
|
|||
|
||||
package edu.cornell.mannlib.vitro.webapp.edit.n3editing.configuration.generators;
|
||||
|
||||
import java.util.HashMap;
|
||||
|
||||
import javax.servlet.http.HttpSession;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
|
@ -21,14 +19,16 @@ import edu.cornell.mannlib.vitro.webapp.edit.n3editing.VTwo.EditConfigurationVTw
|
|||
*/
|
||||
public class DefaultDeleteGenerator extends BaseEditConfigurationGenerator implements EditConfigurationGenerator {
|
||||
|
||||
private Log log = LogFactory.getLog(DefaultObjectPropertyFormGenerator.class);
|
||||
private static final Log log = LogFactory.getLog(DefaultObjectPropertyFormGenerator.class);
|
||||
private static final String PROPERTY_TEMPLATE = "confirmDeletePropertyForm.ftl";
|
||||
private static final String INDIVIDUAL_TEMPLATE = "confirmDeleteIndividualForm.ftl";
|
||||
|
||||
private String subjectUri = null;
|
||||
private String predicateUri = null;
|
||||
private String objectUri = null;
|
||||
private Integer dataHash = 0;
|
||||
private DataPropertyStatement dps = null;
|
||||
private String dataLiteral = null;
|
||||
private String template = "confirmDeletePropertyForm.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,17 +43,37 @@ 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(INDIVIDUAL_TEMPLATE);
|
||||
addDeleteParams(vreq, editConfiguration);
|
||||
}else {
|
||||
editConfiguration.setTemplate(PROPERTY_TEMPLATE);
|
||||
}
|
||||
return editConfiguration;
|
||||
}
|
||||
|
||||
private void addDeleteParams(VitroRequest vreq, EditConfigurationVTwo editConfiguration) {
|
||||
String redirectUrl = vreq.getParameter("redirectUrl");
|
||||
if (redirectUrl != null) {
|
||||
editConfiguration.addFormSpecificData("redirectUrl", redirectUrl);
|
||||
}
|
||||
String individualName = vreq.getParameter("individualName");
|
||||
if (redirectUrl != null) {
|
||||
editConfiguration.addFormSpecificData("individualName", individualName);
|
||||
}
|
||||
String individualType = vreq.getParameter("individualType");
|
||||
if (redirectUrl != null) {
|
||||
editConfiguration.addFormSpecificData("individualType", individualType);
|
||||
}
|
||||
}
|
||||
|
||||
private EditConfigurationVTwo setupEditConfiguration(VitroRequest vreq, HttpSession session) {
|
||||
EditConfigurationVTwo editConfiguration = new EditConfigurationVTwo();
|
||||
initProcessParameters(vreq, session, editConfiguration);
|
||||
//set edit key for this as well
|
||||
editConfiguration.setEditKey(editConfiguration.newEditKey(session));
|
||||
editConfiguration.setEditKey(EditConfigurationVTwo.newEditKey(session));
|
||||
return editConfiguration;
|
||||
|
||||
}
|
||||
|
|
|
@ -78,6 +78,9 @@ public class EditRequestDispatchController extends FreemarkerHttpServlet {
|
|||
} else if(MANAGE_MENUS_FORM.equals(vreq.getParameter("editForm"))) {
|
||||
return SimplePermission.MANAGE_MENUS.ACTION;
|
||||
}
|
||||
if (isIndividualDeletion(vreq)) {
|
||||
return SimplePermission.DO_BACK_END_EDITING.ACTION;
|
||||
}
|
||||
// Check if this statement can be edited here and return unauthorized if not
|
||||
String subjectUri = EditConfigurationUtils.getSubjectUri(vreq);
|
||||
String predicateUri = EditConfigurationUtils.getPredicateUri(vreq);
|
||||
|
@ -106,6 +109,16 @@ public class EditRequestDispatchController extends FreemarkerHttpServlet {
|
|||
return isAuthorized? SimplePermission.DO_FRONT_END_EDITING.ACTION: AuthorizationRequest.UNAUTHORIZED;
|
||||
}
|
||||
|
||||
private boolean isIndividualDeletion(VitroRequest vreq) {
|
||||
String subjectUri = EditConfigurationUtils.getSubjectUri(vreq);
|
||||
String predicateUri = EditConfigurationUtils.getPredicateUri(vreq);
|
||||
String objectUri = EditConfigurationUtils.getObjectUri(vreq);
|
||||
if (objectUri != null && subjectUri == null && predicateUri == null && isDeleteForm(vreq)) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected ResponseValues processRequest(VitroRequest vreq) {
|
||||
|
||||
|
@ -363,7 +376,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;
|
||||
}
|
||||
|
|
|
@ -718,6 +718,10 @@ public class EditConfigurationTemplateModel extends BaseTemplateModel {
|
|||
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() {
|
||||
String deleteProhibited = vreq.getParameter("deleteProhibited");
|
||||
|
|
|
@ -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;
|
||||
|
@ -149,6 +152,22 @@ public abstract class BaseIndividualTemplateModel extends BaseTemplateModel {
|
|||
return individual.getName();
|
||||
}
|
||||
|
||||
public String getDeleteUrl() {
|
||||
Collection<String> types = getMostSpecificTypes();
|
||||
ParamMap params = new ParamMap(
|
||||
"objectUri", individual.getURI(),
|
||||
"cmd", "delete",
|
||||
"individualName",getNameStatement().getValue()
|
||||
);
|
||||
Iterator<String> typesIterator = types.iterator();
|
||||
if (types.iterator().hasNext()) {
|
||||
String type = typesIterator.next();
|
||||
params.put("individualType", type);
|
||||
}
|
||||
|
||||
return UrlBuilder.getUrl(EDIT_PATH, params);
|
||||
}
|
||||
|
||||
public Collection<String> getMostSpecificTypes() {
|
||||
ObjectPropertyStatementDao opsDao = vreq.getWebappDaoFactory().getObjectPropertyStatementDao();
|
||||
Map<String, String> types = opsDao.getMostSpecificTypesInClassgroupsForIndividual(getUri());
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -204,6 +204,9 @@ vitro:additionalLink
|
|||
display:hasElement
|
||||
a owl:ObjectProperty .
|
||||
|
||||
display:hasDeleteQuery
|
||||
a owl:DataProperty .
|
||||
|
||||
display:excludeClass
|
||||
a owl:ObjectProperty .
|
||||
|
||||
|
|
|
@ -49,7 +49,9 @@
|
|||
<h1 class="fn" itemprop="name">
|
||||
<#-- Label -->
|
||||
<@p.label individual editable labelCount localesCount languageCount/>
|
||||
|
||||
<#if editable>
|
||||
<@p.deleteIndividualLink individual />
|
||||
</#if>
|
||||
<#-- Most-specific types -->
|
||||
<@p.mostSpecificTypes individual />
|
||||
<span id="iconControlsVitro"><img id="uriIcon" title="${individual.uri}" class="middle" src="${urls.images}/individual/uriIcon.gif" alt="uri icon"/></span>
|
||||
|
|
|
@ -0,0 +1,33 @@
|
|||
<#-- $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>
|
||||
<#if editConfiguration.pageData.individualName??>
|
||||
<#assign individualName = editConfiguration.pageData.individualName />
|
||||
</#if>
|
||||
<#if editConfiguration.pageData.individualType??>
|
||||
<#assign individualType = editConfiguration.pageData.individualType />
|
||||
</#if>
|
||||
|
||||
<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" />
|
||||
<p>
|
||||
<#if individualType??>
|
||||
${individualType}
|
||||
</#if>
|
||||
<#if individualName??>
|
||||
${individualName}
|
||||
</#if>
|
||||
</p>
|
||||
<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>
|
|
@ -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>
|
||||
|
|
Loading…
Add table
Reference in a new issue