diff --git a/utilities/testrunner/src/edu/cornell/mannlib/vitro/utilities/testrunner/test-user-model.owl b/utilities/testrunner/src/edu/cornell/mannlib/vitro/utilities/testrunner/test-user-model.owl
index c6e9a3523..a84b13707 100644
--- a/utilities/testrunner/src/edu/cornell/mannlib/vitro/utilities/testrunner/test-user-model.owl
+++ b/utilities/testrunner/src/edu/cornell/mannlib/vitro/utilities/testrunner/test-user-model.owl
@@ -16,7 +16,7 @@
ACTIVE
1
0
-
+
@@ -28,7 +28,7 @@
ACTIVE
1
0
-
+
@@ -40,7 +40,7 @@
ACTIVE
1
0
-
+
@@ -52,7 +52,7 @@
ACTIVE
1
0
-
+
diff --git a/webapp/rdf/auth/everytime/permission_config.n3 b/webapp/rdf/auth/everytime/permission_config.n3
index c4a88018a..e0b1c1960 100644
--- a/webapp/rdf/auth/everytime/permission_config.n3
+++ b/webapp/rdf/auth/everytime/permission_config.n3
@@ -12,6 +12,7 @@ auth:ADMIN
# ADMIN-only permissions
auth:hasPermission simplePermission:AccessSpecialDataModels ;
+ auth:hasPermission simplePermission:EnableDeveloperPanel ;
auth:hasPermission simplePermission:LoginDuringMaintenance ;
auth:hasPermission simplePermission:ManageMenus ;
auth:hasPermission simplePermission:ManageProxies ;
@@ -23,8 +24,12 @@ auth:ADMIN
auth:hasPermission simplePermission:UseAdvancedDataToolsPages ;
auth:hasPermission simplePermission:UseMiscellaneousAdminPages ;
auth:hasPermission simplePermission:UseSparqlQueryPage ;
- auth:hasPermission simplePermission:PageViewableAdmin ;
- auth:hasPermission simplePermission:EnableDeveloperPanel ;
+ auth:hasPermission simplePermission:PageViewableAdmin ;
+
+ # Uncomment the following permission line to enable the SPARQL update API.
+ # Before enabling, be sure that the URL api/sparqlUpdate is secured by SSH,
+ # so passwords will not be sent in clear text.
+# auth:hasPermission simplePermission:UseSparqlUpdateApi ;
# permissions for CURATOR and above.
auth:hasPermission simplePermission:EditOntology ;
diff --git a/webapp/src/edu/cornell/mannlib/vitro/webapp/auth/permissions/SimplePermission.java b/webapp/src/edu/cornell/mannlib/vitro/webapp/auth/permissions/SimplePermission.java
index 33c609c5b..0484638bd 100644
--- a/webapp/src/edu/cornell/mannlib/vitro/webapp/auth/permissions/SimplePermission.java
+++ b/webapp/src/edu/cornell/mannlib/vitro/webapp/auth/permissions/SimplePermission.java
@@ -38,6 +38,8 @@ public class SimplePermission extends Permission {
NAMESPACE + "EditOwnAccount");
public static final SimplePermission EDIT_SITE_INFORMATION = new SimplePermission(
NAMESPACE + "EditSiteInformation");
+ public static final SimplePermission ENABLE_DEVELOPER_PANEL = new SimplePermission(
+ NAMESPACE + "EnableDeveloperPanel");
public static final SimplePermission LOGIN_DURING_MAINTENANCE = new SimplePermission(
NAMESPACE + "LoginDuringMaintenance");
public static final SimplePermission MANAGE_MENUS = new SimplePermission(
@@ -76,9 +78,8 @@ public class SimplePermission extends Permission {
NAMESPACE + "UseAdvancedDataToolsPages");
public static final SimplePermission USE_SPARQL_QUERY_PAGE = new SimplePermission(
NAMESPACE + "UseSparqlQueryPage");
- public static final SimplePermission ENABLE_DEVELOPER_PANEL = new SimplePermission(
- NAMESPACE + "EnableDeveloperPanel");
-
+ public static final SimplePermission USE_SPARQL_UPDATE_API = new SimplePermission(
+ NAMESPACE + "UseSparqlUpdateApi");
// ----------------------------------------------------------------------
// These instances are "catch all" permissions to cover poorly defined
diff --git a/webapp/src/edu/cornell/mannlib/vitro/webapp/auth/policy/PolicyHelper.java b/webapp/src/edu/cornell/mannlib/vitro/webapp/auth/policy/PolicyHelper.java
index d8a606efb..b1250a855 100644
--- a/webapp/src/edu/cornell/mannlib/vitro/webapp/auth/policy/PolicyHelper.java
+++ b/webapp/src/edu/cornell/mannlib/vitro/webapp/auth/policy/PolicyHelper.java
@@ -83,11 +83,11 @@ public class PolicyHelper {
}
try{
- Authenticator basicAuth = new BasicAuthenticator(req);
- UserAccount user = basicAuth.getAccountForInternalAuth( email );
+ Authenticator auth = Authenticator.getInstance(req);
+ UserAccount user = auth.getAccountForInternalAuth( email );
log.debug("userAccount is " + user==null?"null":user.getUri() );
- if( ! basicAuth.isCurrentPassword( user, password ) ){
+ if( ! auth.isCurrentPassword( user, password ) ){
log.debug(String.format("UNAUTHORIZED, password not accepted for %s, account URI: %s",
user.getEmailAddress(), user.getUri()));
return false;
diff --git a/webapp/src/edu/cornell/mannlib/vitro/webapp/controller/api/SparqlUpdateApiController.java b/webapp/src/edu/cornell/mannlib/vitro/webapp/controller/api/SparqlUpdateApiController.java
new file mode 100644
index 000000000..60a0e6af0
--- /dev/null
+++ b/webapp/src/edu/cornell/mannlib/vitro/webapp/controller/api/SparqlUpdateApiController.java
@@ -0,0 +1,201 @@
+/* $This file is distributed under the terms of the license in /doc/license.txt$ */
+
+package edu.cornell.mannlib.vitro.webapp.controller.api;
+
+import static javax.servlet.http.HttpServletResponse.SC_BAD_REQUEST;
+import static javax.servlet.http.HttpServletResponse.SC_FORBIDDEN;
+import static javax.servlet.http.HttpServletResponse.SC_INTERNAL_SERVER_ERROR;
+import static javax.servlet.http.HttpServletResponse.SC_OK;
+
+import java.io.IOException;
+import java.io.PrintWriter;
+
+import javax.servlet.ServletContext;
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpServlet;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.apache.commons.lang.StringUtils;
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
+import com.hp.hpl.jena.query.Dataset;
+import com.hp.hpl.jena.update.GraphStore;
+import com.hp.hpl.jena.update.GraphStoreFactory;
+import com.hp.hpl.jena.update.UpdateAction;
+import com.hp.hpl.jena.update.UpdateFactory;
+import com.hp.hpl.jena.update.UpdateRequest;
+
+import edu.cornell.mannlib.vitro.webapp.auth.permissions.SimplePermission;
+import edu.cornell.mannlib.vitro.webapp.auth.policy.PolicyHelper;
+import edu.cornell.mannlib.vitro.webapp.auth.requestedAction.Actions;
+import edu.cornell.mannlib.vitro.webapp.beans.UserAccount;
+import edu.cornell.mannlib.vitro.webapp.controller.VitroRequest;
+import edu.cornell.mannlib.vitro.webapp.controller.authenticate.Authenticator;
+import edu.cornell.mannlib.vitro.webapp.dao.jena.RDFServiceDataset;
+import edu.cornell.mannlib.vitro.webapp.search.indexing.IndexBuilder;
+
+/**
+ * This extends HttpServlet instead of VitroHttpServlet because we want to have
+ * full control over the response:
+ *
+ * - No redirecting to the login page if not authorized
+ * - No redirecting to the home page on insufficient authorization
+ * - No support for GET or HEAD requests, only POST.
+ *
+ *
+ * So these responses will be produced:
+ *
+ *
+ * 200 Success
+ * 400 Failed to parse SPARQL update
+ * 400 SPARQL update must specify a GRAPH URI.
+ * 403 username/password combination is not valid
+ * 403 Account is not authorized
+ * 405 Method not allowed
+ * 500 Unknown error
+ *
+ */
+public class SparqlUpdateApiController extends HttpServlet {
+ private static final Log log = LogFactory
+ .getLog(SparqlUpdateApiController.class);
+
+ private static final Actions REQUIRED_ACTIONS = SimplePermission.USE_SPARQL_UPDATE_API.ACTIONS;
+
+ @Override
+ protected void doPost(HttpServletRequest req, HttpServletResponse resp)
+ throws ServletException, IOException {
+ try {
+ checkAuthorization(req);
+ UpdateRequest parsed = parseUpdateString(req);
+ executeUpdate(req, parsed);
+ do200response(resp);
+ } catch (AuthException e) {
+ do403response(resp, e);
+ } catch (ParseException e) {
+ do400response(resp, e);
+ } catch (Exception e) {
+ do500response(resp, e);
+ }
+ }
+
+ private void checkAuthorization(HttpServletRequest req)
+ throws AuthException {
+ String email = req.getParameter("email");
+ String password = req.getParameter("password");
+
+ Authenticator auth = Authenticator.getInstance(req);
+ UserAccount account = auth.getAccountForInternalAuth(email);
+ if (!auth.isCurrentPassword(account, password)) {
+ log.debug("Invalid: '" + email + "'/'" + password + "'");
+ throw new AuthException("email/password combination is not valid");
+ }
+
+ if (!PolicyHelper.isAuthorizedForActions(req, email, password,
+ REQUIRED_ACTIONS)) {
+ log.debug("Not authorized: '" + email + "'");
+ throw new AuthException("Account is not authorized");
+ }
+
+ log.debug("Authorized for '" + email + "'");
+ }
+
+ private UpdateRequest parseUpdateString(HttpServletRequest req)
+ throws ParseException {
+ String update = req.getParameter("update");
+ if (StringUtils.isBlank(update)) {
+ log.debug("No update parameter.");
+ throw new ParseException("No 'update' parameter.");
+ }
+
+ if (!StringUtils.containsIgnoreCase(update, "GRAPH")) {
+ if (log.isDebugEnabled()) {
+ log.debug("No GRAPH uri in '" + update + "'");
+ }
+ throw new ParseException("SPARQL update must specify a GRAPH URI.");
+ }
+
+ try {
+ return UpdateFactory.create(update);
+ } catch (Exception e) {
+ log.debug("Problem parsing", e);
+ throw new ParseException("Failed to parse SPARQL update", e);
+ }
+ }
+
+ private void executeUpdate(HttpServletRequest req, UpdateRequest parsed) {
+ ServletContext ctx = req.getSession().getServletContext();
+ VitroRequest vreq = new VitroRequest(req);
+
+ IndexBuilder.getBuilder(ctx).pause();
+ try {
+ Dataset ds = new RDFServiceDataset(vreq.getUnfilteredRDFService());
+ GraphStore graphStore = GraphStoreFactory.create(ds);
+ UpdateAction.execute(parsed, graphStore);
+ } finally {
+ IndexBuilder.getBuilder(ctx).unpause();
+ }
+ }
+
+ private void do200response(HttpServletResponse resp) throws IOException {
+ doResponse(resp, SC_OK, "SPARQL update accepted.");
+ }
+
+ private void do403response(HttpServletResponse resp, AuthException e)
+ throws IOException {
+ doResponse(resp, SC_FORBIDDEN, e.getMessage());
+ }
+
+ private void do400response(HttpServletResponse resp, ParseException e)
+ throws IOException {
+ if (e.getCause() == null) {
+ doResponse(resp, SC_BAD_REQUEST, e.getMessage());
+ } else {
+ doResponse(resp, SC_BAD_REQUEST, e.getMessage(), e.getCause());
+ }
+ }
+
+ private void do500response(HttpServletResponse resp, Exception e)
+ throws IOException {
+ doResponse(resp, SC_INTERNAL_SERVER_ERROR, "Unknown error", e);
+ }
+
+ private void doResponse(HttpServletResponse resp, int statusCode,
+ String message) throws IOException {
+ resp.setStatus(statusCode);
+ PrintWriter writer = resp.getWriter();
+ writer.println("" + statusCode + " " + message + "
");
+ }
+
+ private void doResponse(HttpServletResponse resp, int statusCode,
+ String message, Throwable e) throws IOException {
+ resp.setStatus(statusCode);
+ PrintWriter writer = resp.getWriter();
+ writer.println("" + statusCode + " " + message + "
");
+ writer.println("");
+ e.printStackTrace(writer);
+ writer.println("
");
+ }
+
+ // ----------------------------------------------------------------------
+ // Helper classes
+ // ----------------------------------------------------------------------
+
+ private static class AuthException extends Exception {
+ private AuthException(String message) {
+ super(message);
+ }
+ }
+
+ private static class ParseException extends Exception {
+ private ParseException(String message) {
+ super(message);
+ }
+
+ private ParseException(String message, Throwable cause) {
+ super(message, cause);
+ }
+ }
+
+}
diff --git a/webapp/src/edu/cornell/mannlib/vitro/webapp/controller/edit/ExternalIdRetryController.java b/webapp/src/edu/cornell/mannlib/vitro/webapp/controller/edit/ExternalIdRetryController.java
deleted file mode 100644
index 51509e580..000000000
--- a/webapp/src/edu/cornell/mannlib/vitro/webapp/controller/edit/ExternalIdRetryController.java
+++ /dev/null
@@ -1,120 +0,0 @@
-/* $This file is distributed under the terms of the license in /doc/license.txt$ */
-
-package edu.cornell.mannlib.vitro.webapp.controller.edit;
-
-import java.util.Collections;
-import java.util.HashMap;
-import java.util.LinkedList;
-import java.util.List;
-
-import javax.servlet.RequestDispatcher;
-import javax.servlet.http.HttpServletRequest;
-import javax.servlet.http.HttpServletResponse;
-
-import org.apache.commons.logging.Log;
-import org.apache.commons.logging.LogFactory;
-
-import edu.cornell.mannlib.vedit.beans.EditProcessObject;
-import edu.cornell.mannlib.vedit.beans.FormObject;
-import edu.cornell.mannlib.vedit.beans.Option;
-import edu.cornell.mannlib.vedit.controller.BaseEditController;
-import edu.cornell.mannlib.vedit.util.FormUtils;
-import edu.cornell.mannlib.vitro.webapp.auth.permissions.SimplePermission;
-import edu.cornell.mannlib.vitro.webapp.beans.DataPropertyStatement;
-import edu.cornell.mannlib.vitro.webapp.beans.DataPropertyStatementImpl;
-import edu.cornell.mannlib.vitro.webapp.beans.Individual;
-import edu.cornell.mannlib.vitro.webapp.controller.Controllers;
-import edu.cornell.mannlib.vitro.webapp.controller.VitroRequest;
-import edu.cornell.mannlib.vitro.webapp.dao.DataPropertyDao;
-import edu.cornell.mannlib.vitro.webapp.dao.DataPropertyStatementDao;
-import edu.cornell.mannlib.vitro.webapp.dao.IndividualDao;
-
-public class ExternalIdRetryController extends BaseEditController {
-
- private static final Log log = LogFactory.getLog(ExternalIdRetryController.class.getName());
-
- public void doPost (HttpServletRequest request, HttpServletResponse response) {
- if (!isAuthorizedToDisplayPage(request, response,
- SimplePermission.DO_BACK_END_EDITING.ACTIONS)) {
- return;
- }
-
- VitroRequest vreq = new VitroRequest(request);
-
- //create an EditProcessObject for this and put it in the session
- EditProcessObject epo = super.createEpo(request);
-
- String action = "insert";
-
- DataPropertyDao dpDao = vreq.getUnfilteredWebappDaoFactory().getDataPropertyDao();
- DataPropertyStatementDao edDao = vreq.getUnfilteredWebappDaoFactory().getDataPropertyStatementDao();
- epo.setDataAccessObject(edDao);
- epo.setBeanClass(DataPropertyStatement.class);
-
- IndividualDao eDao = vreq.getUnfilteredWebappDaoFactory().getIndividualDao();
-
- DataPropertyStatement eidForEditing = null;
- if (!epo.getUseRecycledBean()){
- eidForEditing = new DataPropertyStatementImpl();
- populateBeanFromParams(eidForEditing,vreq);
- if (vreq.getParameter(MULTIPLEXED_PARAMETER_NAME) != null) {
- action = "update";
- }
- epo.setOriginalBean(eidForEditing);
- } else {
- eidForEditing = (DataPropertyStatement) epo.getNewBean();
- }
-
- //make a simple mask for the class's id
- Object[] simpleMaskPair = new Object[2];
- simpleMaskPair[0]="Id";
- //simpleMaskPair[1]=Integer.valueOf(eidForEditing.getId());
- epo.getSimpleMask().add(simpleMaskPair);
-
- //set up any listeners
-
- FormObject foo = new FormObject();
- HashMap OptionMap = new HashMap();
- List entityList = new LinkedList();
- if (eidForEditing.getIndividualURI() != null) {
- Individual individual = eDao.getIndividualByURI(eidForEditing.getIndividualURI());
- entityList.add(new Option(individual.getURI(),individual.getName(),true));
- } else {
- entityList.add(new Option ("-1", "Error: the entity must be specified", true));
- }
- OptionMap.put("IndividualURI",entityList);
- // TOOD change following DAO call to getAllExternalIdDataProperties once implemented
- List allExternalIdDataProperties = dpDao.getAllExternalIdDataProperties();
- Collections.sort(allExternalIdDataProperties);
- OptionMap.put("DatapropURI", FormUtils.makeOptionListFromBeans(allExternalIdDataProperties, "URI", "PublicName", eidForEditing.getDatapropURI(),""));
- foo.setOptionLists(OptionMap);
- foo.setErrorMap(epo.getErrMsgMap());
-
- epo.setFormObject(foo);
-
- FormUtils.populateFormFromBean(eidForEditing,action,foo,epo.getBadValueMap());
-
- RequestDispatcher rd = request.getRequestDispatcher(Controllers.BASIC_JSP);
- request.setAttribute("bodyJsp","/templates/edit/formBasic.jsp");
- request.setAttribute("formJsp","/templates/edit/specific/externalIds_retry.jsp");
- request.setAttribute("scripts","/templates/edit/formBasic.js");
- request.setAttribute("title","External Id Editing Form");
- request.setAttribute("_action",action);
- request.setAttribute("unqualifiedClassName","External Id");
- setRequestAttributes(request,epo);
-
- try {
- rd.forward(request, response);
- } catch (Exception e) {
- log.error("ExternalIdRetryController could not forward to view.");
- log.error(e.getMessage());
- log.error(e.getStackTrace());
- }
-
- }
-
- public void doGet (HttpServletRequest request, HttpServletResponse response) {
- doPost(request, response);
- }
-
-}
diff --git a/webapp/src/edu/cornell/mannlib/vitro/webapp/controller/edit/VclassEditController.java b/webapp/src/edu/cornell/mannlib/vitro/webapp/controller/edit/VclassEditController.java
index 83ac3b051..69960f546 100644
--- a/webapp/src/edu/cornell/mannlib/vitro/webapp/controller/edit/VclassEditController.java
+++ b/webapp/src/edu/cornell/mannlib/vitro/webapp/controller/edit/VclassEditController.java
@@ -143,24 +143,25 @@ public class VclassEditController extends BaseEditController {
// if supported, we want to show only the asserted superclasses and subclasses.
VClassDao vcDao = ModelAccess.on(getServletContext()).getBaseWebappDaoFactory().getVClassDao();
+ VClassDao displayVcDao = ModelAccess.on(getServletContext()).getWebappDaoFactory().getVClassDao();
List superVClasses = getVClassesForURIList(
- vcDao.getSuperClassURIs(vcl.getURI(),false), vcDao);
+ vcDao.getSuperClassURIs(vcl.getURI(),false), displayVcDao);
sortForPickList(superVClasses, request);
request.setAttribute("superclasses",superVClasses);
List subVClasses = getVClassesForURIList(
- vcDao.getSubClassURIs(vcl.getURI()), vcDao);
+ vcDao.getSubClassURIs(vcl.getURI()), displayVcDao);
sortForPickList(subVClasses, request);
request.setAttribute("subclasses",subVClasses);
List djVClasses = getVClassesForURIList(
- vcDao.getDisjointWithClassURIs(vcl.getURI()), vcDao);
+ vcDao.getDisjointWithClassURIs(vcl.getURI()), displayVcDao);
sortForPickList(djVClasses, request);
request.setAttribute("disjointClasses",djVClasses);
List eqVClasses = getVClassesForURIList(
- vcDao.getEquivalentClassURIs(vcl.getURI()), vcDao);
+ vcDao.getEquivalentClassURIs(vcl.getURI()), displayVcDao);
sortForPickList(eqVClasses, request);
request.setAttribute("equivalentClasses",eqVClasses);
diff --git a/webapp/src/edu/cornell/mannlib/vitro/webapp/controller/freemarker/PageController.java b/webapp/src/edu/cornell/mannlib/vitro/webapp/controller/freemarker/PageController.java
index 785bb031b..fe08c7d95 100644
--- a/webapp/src/edu/cornell/mannlib/vitro/webapp/controller/freemarker/PageController.java
+++ b/webapp/src/edu/cornell/mannlib/vitro/webapp/controller/freemarker/PageController.java
@@ -58,15 +58,16 @@ public class PageController extends FreemarkerHttpServlet{
if( pageActs == null && dgActs == null){
return Actions.AUTHORIZED;
- }else if( pageActs == null && dgActs != null ){
+ }else if( pageActs == null ){
return dgActs;
+ }else if( dgActs == null ){
+ return pageActs;
}else{
- return pageActs;
+ return pageActs.and(dgActs);
}
} catch (Exception e) {
- // TODO Auto-generated catch block
- log.debug(e);
+ log.warn(e);
return Actions.UNAUTHORIZED;
}
}
diff --git a/webapp/src/edu/cornell/mannlib/vitro/webapp/dao/jena/PageDaoJena.java b/webapp/src/edu/cornell/mannlib/vitro/webapp/dao/jena/PageDaoJena.java
index 23e300013..fd333560b 100644
--- a/webapp/src/edu/cornell/mannlib/vitro/webapp/dao/jena/PageDaoJena.java
+++ b/webapp/src/edu/cornell/mannlib/vitro/webapp/dao/jena/PageDaoJena.java
@@ -537,13 +537,14 @@ public class PageDaoJena extends JenaBaseDao implements PageDao {
List actions = new ArrayList();
Model dModel = getOntModelSelector().getDisplayModel();
+ dModel.enterCriticalSection(false);
try{
QueryExecution qe =
QueryExecutionFactory.create( requiredActionsQuery, dModel, initialBindings);
actions = executeQueryToList( qe );
qe.close();
}finally{
- dModel.enterCriticalSection(false);
+ dModel.leaveCriticalSection();
}
return actions;
}
diff --git a/webapp/src/edu/cornell/mannlib/vitro/webapp/edit/n3editing/configuration/generators/DefaultObjectPropertyFormGenerator.java b/webapp/src/edu/cornell/mannlib/vitro/webapp/edit/n3editing/configuration/generators/DefaultObjectPropertyFormGenerator.java
index 11cf0210d..4d29a23d1 100644
--- a/webapp/src/edu/cornell/mannlib/vitro/webapp/edit/n3editing/configuration/generators/DefaultObjectPropertyFormGenerator.java
+++ b/webapp/src/edu/cornell/mannlib/vitro/webapp/edit/n3editing/configuration/generators/DefaultObjectPropertyFormGenerator.java
@@ -112,7 +112,7 @@ public class DefaultObjectPropertyFormGenerator implements EditConfigurationGene
Individual subject = EditConfigurationUtils.getSubjectIndividual(vreq);
String predicateUri = EditConfigurationUtils.getPredicateUri(vreq);
String rangeUri = EditConfigurationUtils.getRangeUri(vreq);
- if (rangeUri != null) {
+ if (rangeUri != null && !rangeUri.isEmpty()) {
VClass rangeVClass = ctxDaoFact.getVClassDao().getVClassByURI(rangeUri);
if (!rangeVClass.isUnion()) {
types.add(rangeVClass);
@@ -125,18 +125,23 @@ public class DefaultObjectPropertyFormGenerator implements EditConfigurationGene
}
WebappDaoFactory wDaoFact = vreq.getWebappDaoFactory();
//Get all vclasses applicable to subject
- List vClasses = subject.getVClasses();
- HashMap typesHash = new HashMap();
- for(VClass vclass: vClasses) {
- List rangeVclasses = wDaoFact.getVClassDao().getVClassesForProperty(vclass.getURI(),predicateUri);
- if(rangeVclasses != null) {
- for(VClass range: rangeVclasses) {
- //a hash will keep a unique list of types and so prevent duplicates
- typesHash.put(range.getURI(), range);
+ if(subject != null) {
+ List vClasses = subject.getVClasses();
+ HashMap typesHash = new HashMap();
+ for(VClass vclass: vClasses) {
+ List rangeVclasses = wDaoFact.getVClassDao().getVClassesForProperty(vclass.getURI(),predicateUri);
+ if(rangeVclasses != null) {
+ for(VClass range: rangeVclasses) {
+ //a hash will keep a unique list of types and so prevent duplicates
+ typesHash.put(range.getURI(), range);
+ }
}
- }
- }
- types.addAll(typesHash.values());
+ }
+ types.addAll(typesHash.values());
+ } else {
+ log.error("Subject individual was null for");
+ }
+
return types;
}
diff --git a/webapp/src/edu/cornell/mannlib/vitro/webapp/freemarker/config/FreemarkerConfigurationImpl.java b/webapp/src/edu/cornell/mannlib/vitro/webapp/freemarker/config/FreemarkerConfigurationImpl.java
index 617a09f6c..7d2c05c9d 100644
--- a/webapp/src/edu/cornell/mannlib/vitro/webapp/freemarker/config/FreemarkerConfigurationImpl.java
+++ b/webapp/src/edu/cornell/mannlib/vitro/webapp/freemarker/config/FreemarkerConfigurationImpl.java
@@ -3,6 +3,7 @@
package edu.cornell.mannlib.vitro.webapp.freemarker.config;
import java.io.IOException;
+import java.lang.ref.WeakReference;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
@@ -35,28 +36,52 @@ import freemarker.template.utility.DeepUnwrap;
* Extend the Freemarker Configuration class to include some information that is
* particular to the current request.
*
- * Takes advantage of the fact that each servlet request runs in a separate
- * thread. Stores the request-based information in a ThreadLocal. Override any
- * methods that should return that information instead of (or in addition to)
- * the common info.
+ * A reference to the current request is not always available to the Freemarker
+ * Configuration, so we take advantage of the fact that each request runs in a
+ * separate thread, and store a reference to that request in a ThreadLocal
+ * object.
+ *
+ * Then, we override any methods that should return that request-based
+ * information instead of (or in addition to) the common info.
*
* Only the getters are overridden, not the setters. So if you call
* setAllSharedVariables(), for example, it will have no effect on the
* request-based information.
+ *
+ * Notice that the reference to the current request is actually stored through a
+ * WeakReference. This is because the ThreadLocal will not be cleared when the
+ * webapp is stopped, so none of the references from that ThreadLocal are
+ * eligible for garbage collection. If any of those references is an instance of
+ * a class that is loaded by the webapp, then the webapp ClassLoader is not
+ * eligible for garbage collection. This would be a huge memory leak.
+ *
+ * Thanks to the WeakReference, the request is eligible for garbage collection
+ * if nothing else refers to it. In theory, this means that the WeakReference
+ * could return a null, but if the garbage collector has taken the request, then
+ * who is invoking this object?
*/
public class FreemarkerConfigurationImpl extends Configuration {
private static final Log log = LogFactory
.getLog(FreemarkerConfigurationImpl.class);
- private final ThreadLocal rbiRef = new ThreadLocal<>();
+ private static final String ATTRIBUTE_NAME = RequestBasedInformation.class
+ .getName();
+
+ private final ThreadLocal> reqRef = new ThreadLocal<>();
void setRequestInfo(HttpServletRequest req) {
- rbiRef.set(new RequestBasedInformation(req, this));
+ reqRef.set(new WeakReference<>(req));
+ req.setAttribute(ATTRIBUTE_NAME, new RequestBasedInformation(req, this));
+ }
+
+ private RequestBasedInformation getRequestInfo() {
+ HttpServletRequest req = reqRef.get().get();
+ return (RequestBasedInformation) req.getAttribute(ATTRIBUTE_NAME);
}
@Override
public Object getCustomAttribute(String name) {
- Map attribs = rbiRef.get().getCustomAttributes();
+ Map attribs = getRequestInfo().getCustomAttributes();
if (attribs.containsKey(name)) {
return attribs.get(name);
} else {
@@ -66,13 +91,13 @@ public class FreemarkerConfigurationImpl extends Configuration {
@Override
public String[] getCustomAttributeNames() {
- Set rbiNames = rbiRef.get().getCustomAttributes().keySet();
+ Set rbiNames = getRequestInfo().getCustomAttributes().keySet();
return joinNames(rbiNames, super.getCustomAttributeNames());
}
@Override
public TemplateModel getSharedVariable(String name) {
- Map vars = rbiRef.get().getSharedVariables();
+ Map vars = getRequestInfo().getSharedVariables();
if (vars.containsKey(name)) {
return vars.get(name);
} else {
@@ -82,7 +107,7 @@ public class FreemarkerConfigurationImpl extends Configuration {
@Override
public Set getSharedVariableNames() {
- Set rbiNames = rbiRef.get().getSharedVariables().keySet();
+ Set rbiNames = getRequestInfo().getSharedVariables().keySet();
@SuppressWarnings("unchecked")
Set superNames = super.getSharedVariableNames();
@@ -94,7 +119,7 @@ public class FreemarkerConfigurationImpl extends Configuration {
@Override
public Locale getLocale() {
- return rbiRef.get().getReq().getLocale();
+ return getRequestInfo().getReq().getLocale();
}
private String[] joinNames(Set nameSet, String[] nameArray) {
diff --git a/webapp/src/edu/cornell/mannlib/vitro/webapp/freemarker/loader/FreemarkerTemplateLoader.java b/webapp/src/edu/cornell/mannlib/vitro/webapp/freemarker/loader/FreemarkerTemplateLoader.java
index 2ae556862..0bef38b30 100644
--- a/webapp/src/edu/cornell/mannlib/vitro/webapp/freemarker/loader/FreemarkerTemplateLoader.java
+++ b/webapp/src/edu/cornell/mannlib/vitro/webapp/freemarker/loader/FreemarkerTemplateLoader.java
@@ -285,7 +285,7 @@ public class FreemarkerTemplateLoader implements TemplateLoader {
}
public boolean fileQualifies(Path path) {
- return Files.isRegularFile(path) && Files.isReadable(path);
+ return Files.isReadable(path) && !Files.isDirectory(path);
}
public SortedSet getMatches() {
diff --git a/webapp/src/edu/cornell/mannlib/vitro/webapp/servlet/setup/ThemeInfoSetup.java b/webapp/src/edu/cornell/mannlib/vitro/webapp/servlet/setup/ThemeInfoSetup.java
index 6b917c43f..5ccea3bfd 100644
--- a/webapp/src/edu/cornell/mannlib/vitro/webapp/servlet/setup/ThemeInfoSetup.java
+++ b/webapp/src/edu/cornell/mannlib/vitro/webapp/servlet/setup/ThemeInfoSetup.java
@@ -73,8 +73,8 @@ public class ThemeInfoSetup implements ServletContextListener {
ApplicationBean.themeInfo = new ThemeInfo(themesBaseDir,
defaultThemeName, themeNames);
- ss.info(this, ", current theme: " + currentThemeName
- + "default theme: " + defaultThemeName + ", available themes: "
+ ss.info(this, "current theme: " + currentThemeName
+ + ", default theme: " + defaultThemeName + ", available themes: "
+ themeNames);
}
diff --git a/webapp/src/edu/cornell/mannlib/vitro/webapp/utils/dataGetter/SparqlUpdate.java b/webapp/src/edu/cornell/mannlib/vitro/webapp/utils/dataGetter/SparqlUpdate.java
deleted file mode 100644
index 7bb33194b..000000000
--- a/webapp/src/edu/cornell/mannlib/vitro/webapp/utils/dataGetter/SparqlUpdate.java
+++ /dev/null
@@ -1,96 +0,0 @@
-/* $This file is distributed under the terms of the license in /doc/license.txt$ */
-
-package edu.cornell.mannlib.vitro.webapp.utils.dataGetter;
-
-import java.util.HashMap;
-import java.util.Map;
-
-import javax.servlet.ServletContext;
-
-import org.apache.commons.logging.Log;
-import org.apache.commons.logging.LogFactory;
-
-import com.hp.hpl.jena.query.Dataset;
-import com.hp.hpl.jena.rdf.model.Model;
-import com.hp.hpl.jena.update.GraphStore;
-import com.hp.hpl.jena.update.GraphStoreFactory;
-import com.hp.hpl.jena.update.UpdateAction;
-
-import edu.cornell.mannlib.vitro.webapp.auth.permissions.SimplePermission;
-import edu.cornell.mannlib.vitro.webapp.auth.policy.PolicyHelper;
-import edu.cornell.mannlib.vitro.webapp.auth.requestedAction.Actions;
-import edu.cornell.mannlib.vitro.webapp.auth.requestedAction.ifaces.RequiresActions;
-import edu.cornell.mannlib.vitro.webapp.controller.VitroRequest;
-import edu.cornell.mannlib.vitro.webapp.dao.jena.RDFServiceDataset;
-import edu.cornell.mannlib.vitro.webapp.search.indexing.IndexBuilder;
-
-/**
- * Handle a SPARQL Update request. This uses Jena ARQ and the RDFServiceDataset to
- * evaluate a SPARQL Update with the RDFService.
- *
- * The reason to make this a DataGettere was to allow configuration in RDF of this
- * service.
- */
-public class SparqlUpdate implements DataGetter, RequiresActions{
-
- private static final Log log = LogFactory.getLog(SparqlUpdate.class);
-
- VitroRequest vreq;
- ServletContext context;
-
- public SparqlUpdate(
- VitroRequest vreq, Model displayModel, String dataGetterURI ) {
- if( vreq == null )
- throw new IllegalArgumentException("VitroRequest may not be null.");
- this.vreq = vreq;
- this.context = vreq.getSession().getServletContext();
- }
-
-
- /**
- * Gets the update from the request and then executes it on
- * the RDFService.
- */
- @Override
- public Map getData( Map valueMap ) {
- HashMap data = new HashMap();
-
- String update = vreq.getParameter("update");
-
- if( update != null && !update.trim().isEmpty()){
- try{
- IndexBuilder.getBuilder(context).pause();
- Dataset ds = new RDFServiceDataset( vreq.getUnfilteredRDFService() );
- GraphStore graphStore = GraphStoreFactory.create(ds);
- log.warn("The SPARQL update is '"+vreq.getParameter("update")+"'");
- UpdateAction.parseExecute( vreq.getParameter("update") , graphStore );
- }finally{
- IndexBuilder.getBuilder(context).unpause();
- }
-
- }
-
- data.put("bodyTemplate", "page-sparqlUpdateTest.ftl");
- return data;
- }
-
-
- /**
- * Check if this request is authorized by the email/password.
- * If not do normal authorization.
- */
- @Override
- public Actions requiredActions(VitroRequest vreq) {
- String email = vreq.getParameter("email");
- String password = vreq.getParameter("password");
-
- boolean isAuth = PolicyHelper.isAuthorizedForActions(vreq,
- email, password, SimplePermission.MANAGE_SEARCH_INDEX.ACTIONS);
-
- if( isAuth )
- return Actions.AUTHORIZED;
- else
- return SimplePermission.MANAGE_SEARCH_INDEX.ACTIONS;
- }
-
-}
diff --git a/webapp/src/edu/cornell/mannlib/vitro/webapp/utils/developer/DeveloperSettings.java b/webapp/src/edu/cornell/mannlib/vitro/webapp/utils/developer/DeveloperSettings.java
index 169491372..f883281f9 100644
--- a/webapp/src/edu/cornell/mannlib/vitro/webapp/utils/developer/DeveloperSettings.java
+++ b/webapp/src/edu/cornell/mannlib/vitro/webapp/utils/developer/DeveloperSettings.java
@@ -179,7 +179,7 @@ public class DeveloperSettings {
// The factory
// ----------------------------------------------------------------------
- private static final String ATTRIBUTE_NAME = DeveloperSettings.class
+ protected static final String ATTRIBUTE_NAME = DeveloperSettings.class
.getName();
public static DeveloperSettings getBean(HttpServletRequest req) {
@@ -203,7 +203,7 @@ public class DeveloperSettings {
private final Map settings = new EnumMap<>(Keys.class);
- private DeveloperSettings(ServletContext ctx) {
+ protected DeveloperSettings(ServletContext ctx) {
updateFromFile(ctx);
}
diff --git a/webapp/src/edu/cornell/mannlib/vitro/webapp/web/templatemodels/edit/EditConfigurationTemplateModel.java b/webapp/src/edu/cornell/mannlib/vitro/webapp/web/templatemodels/edit/EditConfigurationTemplateModel.java
index 8a3e273ca..651cfb6b0 100644
--- a/webapp/src/edu/cornell/mannlib/vitro/webapp/web/templatemodels/edit/EditConfigurationTemplateModel.java
+++ b/webapp/src/edu/cornell/mannlib/vitro/webapp/web/templatemodels/edit/EditConfigurationTemplateModel.java
@@ -547,11 +547,14 @@ public class EditConfigurationTemplateModel extends BaseTemplateModel {
rangeVClasses.addAll(rangeClass.getUnionComponents());
}
for(VClass rangeVClass : rangeVClasses) {
- vclasses.add(rangeVClass);
+ if(rangeVClass.getGroupURI() != null) {
+ vclasses.add(rangeVClass);
+ }
List subURIs = wdf.getVClassDao().getAllSubClassURIs(rangeVClass.getURI());
for (String subClassURI : subURIs) {
VClass subClass = wdf.getVClassDao().getVClassByURI(subClassURI);
- if (subClass != null) {
+ //if the subclass exists and also belongs to a particular class group
+ if (subClass != null && subClass.getGroupURI() != null) {
vclasses.add(subClass);
}
}
@@ -567,7 +570,8 @@ public class EditConfigurationTemplateModel extends BaseTemplateModel {
//add range vclass to hash
if(rangeVclasses != null) {
for(VClass v: rangeVclasses) {
- if(!vclassesURIs.contains(v.getURI())) {
+ //Need to make sure any class added will belong to a class group
+ if(!vclassesURIs.contains(v.getURI()) && v.getGroupURI() != null) {
vclassesURIs.add(v.getURI());
vclasses.add(v);
}
@@ -577,7 +581,13 @@ public class EditConfigurationTemplateModel extends BaseTemplateModel {
}
//if each subject vclass resulted in null being returned for range vclasses, then size of vclasses would be zero
if(vclasses.size() == 0) {
- vclasses = wdf.getVClassDao().getAllVclasses();
+ List allVClasses = wdf.getVClassDao().getAllVclasses();
+ //Since these are all vclasses, we should check whether vclasses included are in a class group
+ for(VClass v:allVClasses) {
+ if(v.getGroupURI() != null) {
+ vclasses.add(v);
+ }
+ }
}
diff --git a/webapp/src/edu/cornell/mannlib/vitro/webapp/web/templatemodels/individual/GroupedPropertyList.java b/webapp/src/edu/cornell/mannlib/vitro/webapp/web/templatemodels/individual/GroupedPropertyList.java
index 1240285c3..8235202a6 100644
--- a/webapp/src/edu/cornell/mannlib/vitro/webapp/web/templatemodels/individual/GroupedPropertyList.java
+++ b/webapp/src/edu/cornell/mannlib/vitro/webapp/web/templatemodels/individual/GroupedPropertyList.java
@@ -81,7 +81,7 @@ public class GroupedPropertyList extends BaseTemplateModel {
// save applicable ranges before deduping to filter later
populatedObjectPropertyList = dedupe(populatedObjectPropertyList);
-
+
Collection additions = ApplicationConfigurationOntologyUtils
.getAdditionalFauxSubpropertiesForList(
populatedObjectPropertyList, subject, vreq);
@@ -121,13 +121,7 @@ public class GroupedPropertyList extends BaseTemplateModel {
if (editing) {
mergeAllPossibleDataProperties(propertyList);
}
-
-// Not currently necessary since the language-specific version is now added
-// during the merge
-// if (editing) {
-// propertyList = correctLanguageForProperties(propertyList);
-// }
-
+
sort(propertyList);
// Put the list into groups
@@ -176,35 +170,6 @@ public class GroupedPropertyList extends BaseTemplateModel {
return filteredAdditions;
}
- // Use the language-filtering WebappDaoFactory to get the right version of
- // each property. When editing, the methods that add to the property list
- // are blissfully (and intentionally) language-unaware.
- private List correctLanguageForProperties(List properties) {
- List languageCorrectedProps = new ArrayList();
- for (Property p : properties) {
- Property correctedProp = null;
- if (p instanceof ObjectProperty) {
- ObjectProperty op = (ObjectProperty) p;
- correctedProp = wdf.getObjectPropertyDao()
- .getObjectPropertyByURIs(op.getURI(),
- op.getDomainVClassURI(), op.getRangeVClassURI());
- } else if (p instanceof DataProperty) {
- correctedProp = wdf.getDataPropertyDao()
- .getDataPropertyByURI(((DataProperty) p).getURI());
- } else {
- log.warn("Ignoring " + p.getURI() + " which is neither an " +
- "ObjectProperty nor a DatatypeProperty.");
- }
- if (correctedProp != null) {
- languageCorrectedProps.add(correctedProp);
- } else {
- log.error("Unable to retrieve property " + p.getURI() +
- " using the WebappDaoFactory associated with the request.");
- }
- }
- return languageCorrectedProps;
- }
-
// It's possible that an object property retrieved in the call to getPopulatedObjectPropertyList()
// is now empty of statements, because if not editing, some statements without a linked individual
// are not retrieved by the query. (See elements in queries.)
@@ -281,16 +246,17 @@ public class GroupedPropertyList extends BaseTemplateModel {
continue;
}
boolean addToList = true;
- int opIndex = 0;
for(ObjectProperty op : populatedObjectPropertyList) {
- if(redundant(op, piOp)) {
+ RedundancyReason reason = redundant(op, piOp);
+ if(reason != null) {
addToList = false;
- if (moreRestrictiveRange(piOp, op, wadf)) {
- propertyList = replaceOpWithPiOpInList(piOp, op, opIndex, propertyList);
+ if (reason == RedundancyReason.LABEL_AND_URI_MATCH
+ && moreRestrictiveRange(piOp, op, wadf)) {
+ op.setRangeVClassURI(piOp.getRangeVClassURI());
+ op.setRangeVClass(piOp.getRangeVClass());
}
break;
}
- opIndex++;
}
if(addToList) {
propertyList.add(piOp);
@@ -315,6 +281,10 @@ public class GroupedPropertyList extends BaseTemplateModel {
return propertyList;
}
+ private enum RedundancyReason {
+ LABEL_AND_URI_MATCH, LABEL_URI_DOMAIN_AND_RANGE_MATCH
+ }
+
private boolean moreRestrictiveRange(ObjectProperty piOp, ObjectProperty op,
WebappDaoFactory wadf) {
if(piOp.getRangeVClassURI() == null) {
@@ -327,25 +297,9 @@ public class GroupedPropertyList extends BaseTemplateModel {
}
}
- private List replaceOpWithPiOpInList(ObjectProperty piOp,
- ObjectProperty op, int opIndex, List propertyList) {
-
- List returnList = new ArrayList();
- int index = 0;
- for(Property p : propertyList) {
- if(index == opIndex /* p.equals(op) */) {
- returnList.add(piOp);
- } else {
- returnList.add(p);
- }
- index++;
- }
- return returnList;
- }
-
- private boolean redundant(ObjectProperty op, ObjectProperty op2) {
+ private RedundancyReason redundant(ObjectProperty op, ObjectProperty op2) {
if (op2.getURI() == null) {
- return false;
+ return null;
}
boolean uriMatches = (op.getURI() != null
&& op.getURI().equals(op2.getURI()));
@@ -360,7 +314,7 @@ public class GroupedPropertyList extends BaseTemplateModel {
labelMatches = true;
}
if(uriMatches && labelMatches) {
- return true;
+ return RedundancyReason.LABEL_AND_URI_MATCH;
}
if(op.getDomainVClassURI() == null) {
if(op2.getDomainVClassURI() == null) {
@@ -377,9 +331,9 @@ public class GroupedPropertyList extends BaseTemplateModel {
rangeMatches = true;
}
if (uriMatches && domainMatches && rangeMatches) {
- return true;
+ return RedundancyReason.LABEL_URI_DOMAIN_AND_RANGE_MATCH;
}
- return false;
+ return null;
}
private void addObjectPropertyToPropertyList(String propertyUri, String domainUri, String rangeUri,
diff --git a/webapp/test/stubs/edu/cornell/mannlib/vitro/webapp/utils/developer/DeveloperSettingsStub.java b/webapp/test/stubs/edu/cornell/mannlib/vitro/webapp/utils/developer/DeveloperSettingsStub.java
new file mode 100644
index 000000000..6e2871596
--- /dev/null
+++ b/webapp/test/stubs/edu/cornell/mannlib/vitro/webapp/utils/developer/DeveloperSettingsStub.java
@@ -0,0 +1,33 @@
+/* $This file is distributed under the terms of the license in /doc/license.txt$ */
+
+package stubs.edu.cornell.mannlib.vitro.webapp.utils.developer;
+
+import javax.servlet.ServletContext;
+
+import edu.cornell.mannlib.vitro.webapp.utils.developer.DeveloperSettings;
+
+/**
+ * Do everything that a standard DeveloperSettings would do, except loading from
+ * a properties file.
+ *
+ * That way, we don't require ConfigurationProperties to find the Vitro home
+ * directory, so we don't throw errors if there is no ConfigurationProperties.
+ */
+public class DeveloperSettingsStub extends DeveloperSettings {
+ /**
+ * Factory method. Create the stub and set it into the ServletContext.
+ */
+ public static void set(ServletContext ctx) {
+ ctx.setAttribute(ATTRIBUTE_NAME, new DeveloperSettingsStub(ctx));
+ }
+
+ protected DeveloperSettingsStub(ServletContext ctx) {
+ super(ctx);
+ }
+
+ @Override
+ protected void updateFromFile(ServletContext ctx) {
+ // Don't bother.
+ }
+
+}
diff --git a/webapp/test/stubs/javax/servlet/ServletContextStub.java b/webapp/test/stubs/javax/servlet/ServletContextStub.java
index 46e592d60..eb1e5eab7 100644
--- a/webapp/test/stubs/javax/servlet/ServletContextStub.java
+++ b/webapp/test/stubs/javax/servlet/ServletContextStub.java
@@ -21,6 +21,8 @@ import javax.servlet.ServletException;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
+import stubs.edu.cornell.mannlib.vitro.webapp.utils.developer.DeveloperSettingsStub;
+
/**
* A simple stand-in for the {@link ServletContext}, for use in unit tests.
*/
@@ -36,6 +38,11 @@ public class ServletContextStub implements ServletContext {
private final Map mockResources = new HashMap();
private final Map realPaths = new HashMap();
+ public ServletContextStub() {
+ // Assume that unit tests won't want to use Developer mode.
+ DeveloperSettingsStub.set(this);
+ }
+
public void setContextPath(String contextPath) {
if (contextPath == null) {
throw new NullPointerException("contextPath may not be null.");
diff --git a/webapp/web/WEB-INF/web.xml b/webapp/web/WEB-INF/web.xml
index 3a5377bda..1d393a7b9 100644
--- a/webapp/web/WEB-INF/web.xml
+++ b/webapp/web/WEB-INF/web.xml
@@ -1025,6 +1025,16 @@
/admin/sparqlquery
+
+ SparqlUpdateApi
+ edu.cornell.mannlib.vitro.webapp.controller.api.SparqlUpdateApiController
+
+
+
+ SparqlUpdateApi
+ /api/sparqlUpdate
+
+
primitiveRdfEdit
edu.cornell.mannlib.vitro.webapp.controller.edit.PrimitiveRdfEdit
diff --git a/webapp/web/templates/edit/specific/ents_edit.jsp b/webapp/web/templates/edit/specific/ents_edit.jsp
index 10e1764cb..f6f6ba9b2 100644
--- a/webapp/web/templates/edit/specific/ents_edit.jsp
+++ b/webapp/web/templates/edit/specific/ents_edit.jsp
@@ -99,11 +99,6 @@
-
\ No newline at end of file