From 06bdb86f8aafe0bd534bd6f02ca2ae9914d999d0 Mon Sep 17 00:00:00 2001
From: brianjlowe
Date: Mon, 14 Oct 2013 11:49:21 -0400
Subject: [PATCH 1/9] fixes bug in display hiding for qualified properties
---
.../webapp/auth/permissions/DisplayByRolePermission.java | 8 +++++---
1 file changed, 5 insertions(+), 3 deletions(-)
diff --git a/webapp/src/edu/cornell/mannlib/vitro/webapp/auth/permissions/DisplayByRolePermission.java b/webapp/src/edu/cornell/mannlib/vitro/webapp/auth/permissions/DisplayByRolePermission.java
index 74917860b..74ff5c584 100644
--- a/webapp/src/edu/cornell/mannlib/vitro/webapp/auth/permissions/DisplayByRolePermission.java
+++ b/webapp/src/edu/cornell/mannlib/vitro/webapp/auth/permissions/DisplayByRolePermission.java
@@ -15,6 +15,7 @@ import edu.cornell.mannlib.vitro.webapp.auth.requestedAction.display.DisplayObje
import edu.cornell.mannlib.vitro.webapp.auth.requestedAction.ifaces.RequestedAction;
import edu.cornell.mannlib.vitro.webapp.beans.BaseResourceBean.RoleLevel;
import edu.cornell.mannlib.vitro.webapp.beans.DataPropertyStatement;
+import edu.cornell.mannlib.vitro.webapp.beans.ObjectProperty;
import edu.cornell.mannlib.vitro.webapp.beans.ObjectPropertyStatement;
import edu.cornell.mannlib.vitro.webapp.beans.Property;
@@ -112,11 +113,12 @@ public class DisplayByRolePermission extends Permission {
*/
private boolean isAuthorized(DisplayObjectPropertyStatement action) {
ObjectPropertyStatement stmt = action.getObjectPropertyStatement();
- String subjectUri = stmt.getSubjectURI();
- String predicateUri = stmt.getPropertyURI();
+ String subjectUri = stmt.getSubjectURI();
String objectUri = stmt.getObjectURI();
+ Property op = (stmt.getProperty() != null)
+ ? stmt.getProperty() : new Property(stmt.getPropertyURI());
return canDisplayResource(subjectUri)
- && canDisplayPredicate(new Property(predicateUri))
+ && canDisplayPredicate(op)
&& canDisplayResource(objectUri);
}
From 9e09da9871d8d085a7c67e46223f53f9de98e4b9 Mon Sep 17 00:00:00 2001
From: brianjlowe
Date: Mon, 14 Oct 2013 15:28:59 -0400
Subject: [PATCH 2/9] various migration improvements
---
.../ontology/update/KnowledgeBaseUpdater.java | 41 +------------------
.../webapp/ontology/update/TBoxUpdater.java | 7 +---
.../servlet/setup/SimpleReasonerSetup.java | 4 +-
.../servlet/setup/UpdateKnowledgeBase.java | 5 +--
4 files changed, 8 insertions(+), 49 deletions(-)
diff --git a/webapp/src/edu/cornell/mannlib/vitro/webapp/ontology/update/KnowledgeBaseUpdater.java b/webapp/src/edu/cornell/mannlib/vitro/webapp/ontology/update/KnowledgeBaseUpdater.java
index 2561f1f9d..b4f211c97 100644
--- a/webapp/src/edu/cornell/mannlib/vitro/webapp/ontology/update/KnowledgeBaseUpdater.java
+++ b/webapp/src/edu/cornell/mannlib/vitro/webapp/ontology/update/KnowledgeBaseUpdater.java
@@ -64,7 +64,7 @@ public class KnowledgeBaseUpdater {
}
long startTime = System.currentTimeMillis();
- log.info("Migrating the knowledge base");
+ log.info("Performing any necessary data migration");
logger.log("Started knowledge base migration");
try {
@@ -104,12 +104,6 @@ public class KnowledgeBaseUpdater {
log.error(e,e);
}
- try {
- migrateMigrationMetadata(servletContext);
- logger.log("Migrated migration metadata");
- } catch (Exception e) {
- log.error("unable to migrate migration metadata " + e.getMessage());
- }
}
// update ABox data any time
@@ -264,39 +258,6 @@ public class KnowledgeBaseUpdater {
aboxUpdater.processClassChanges(changes.getAtomicClassChanges());
}
- // special for 1.5 - temporary code
- // migrate past migration indicators to not use blank nodes and move them to app metadata model
- // changing structure for pre 1.5 ones in the process
- private void migrateMigrationMetadata(ServletContext servletContext) throws Exception {
-
- String baseResourceURI = "http://vitro.mannlib.cornell.edu/ns/vitro/metadata/migration/";
- String queryFile = "MigrationData.sparql";
-
- RDFService rdfService = RDFServiceUtils.getRDFServiceFactory(servletContext).getRDFService();
-
- String fmQuery = FileUtils.readFileToString(new File(settings.getSparqlConstructDeletionsDir() + "/" + queryFile));
- Model toRemove = ModelFactory.createDefaultModel();
- toRemove.read(rdfService.sparqlConstructQuery(fmQuery, RDFService.ModelSerializationFormat.RDFXML), null);
-
- String cmQuery = FileUtils.readFileToString(new File(settings.getSparqlConstructAdditionsDir() + "/" + queryFile));
- Model toAdd = ModelFactory.createDefaultModel();
- toAdd.read(rdfService.sparqlConstructQuery(cmQuery, RDFService.ModelSerializationFormat.RDFXML), null);
-
- ByteArrayOutputStream outAdd = new ByteArrayOutputStream();
- toAdd.write(outAdd);
- InputStream inAdd = new ByteArrayInputStream(outAdd.toByteArray());
- ChangeSet addChangeSet = rdfService.manufactureChangeSet();
- addChangeSet.addAddition(inAdd, RDFService.ModelSerializationFormat.RDFXML, JenaDataSourceSetupBase.JENA_APPLICATION_METADATA_MODEL);
- rdfService.changeSetUpdate(addChangeSet);
-
- ByteArrayOutputStream outRemove = new ByteArrayOutputStream();
- toRemove.write(outRemove);
- InputStream inRemove = new ByteArrayInputStream(outRemove.toByteArray());
- ChangeSet removeChangeSet = rdfService.manufactureChangeSet();
- removeChangeSet.addRemoval(inRemove, RDFService.ModelSerializationFormat.RDFXML, JenaDataSourceSetupBase.JENA_DB_MODEL);
- rdfService.changeSetUpdate(removeChangeSet);
- }
-
private void updateTBoxAnnotations() {
TBoxUpdater tboxUpdater = new TBoxUpdater(settings, logger, record);
try {
diff --git a/webapp/src/edu/cornell/mannlib/vitro/webapp/ontology/update/TBoxUpdater.java b/webapp/src/edu/cornell/mannlib/vitro/webapp/ontology/update/TBoxUpdater.java
index a9616cefa..cf81d4092 100644
--- a/webapp/src/edu/cornell/mannlib/vitro/webapp/ontology/update/TBoxUpdater.java
+++ b/webapp/src/edu/cornell/mannlib/vitro/webapp/ontology/update/TBoxUpdater.java
@@ -88,10 +88,6 @@ public class TBoxUpdater {
public void modifyPropertyQualifications() throws IOException {
}
-
- private Model mergeConfigurations(Model oldConfig, Model newConfig) {
- return null;
- }
public void updateDefaultAnnotationValues() throws IOException {
updateDefaultAnnotationValues(null);
@@ -528,7 +524,8 @@ public class TBoxUpdater {
" OPTIONAL { <" + oldPropertyURI + "> vitro:inPropertyGroupAnnot ?group . \n" +
" ?configuration config:propertyGroup ?rgroup } \n" +
" OPTIONAL { <" + oldPropertyURI + "> <" + RDFS.label.getURI() + "> ?label . \n" +
- " ?configuration config:displayName ?rlabel } \n " +
+ " ?configuration config:displayName ?rlabel . \n " +
+ " FILTER(?rlabel != ?label) } \n " +
" OPTIONAL { <" + oldPropertyURI + "> vitro:displayRankAnnot ?displayRank . \n" +
" ?configuration vitro:displayRantAnnot ?rdisplayRank } \n " +
" OPTIONAL { <" + oldPropertyURI + "> vitro:customEntryFormAnnot ?customForm . \n" +
diff --git a/webapp/src/edu/cornell/mannlib/vitro/webapp/servlet/setup/SimpleReasonerSetup.java b/webapp/src/edu/cornell/mannlib/vitro/webapp/servlet/setup/SimpleReasonerSetup.java
index c5b6cbf3d..43f6fa7d2 100644
--- a/webapp/src/edu/cornell/mannlib/vitro/webapp/servlet/setup/SimpleReasonerSetup.java
+++ b/webapp/src/edu/cornell/mannlib/vitro/webapp/servlet/setup/SimpleReasonerSetup.java
@@ -81,6 +81,8 @@ public class SimpleReasonerSetup implements ServletContextListener {
log.info("Pellet reasoner connected for the TBox");
+ waitForTBoxReasoning(sce);
+
// set up simple reasoning for the ABox
RDFService rdfService = RDFServiceUtils.getRDFServiceFactory(ctx).getRDFService();
@@ -131,7 +133,7 @@ public class SimpleReasonerSetup implements ServletContextListener {
int sleeps = 0;
// sleep at least once to make sure the TBox reasoning gets started
while ((0 == sleeps) || ((sleeps < 1000) && pelletListener.isReasoning())) {
- if ((sleeps % 10) == 0) { // print message at 10 second intervals
+ if (((sleeps - 1) % 10) == 0) { // print message at 10 second intervals
log.info("Waiting for initial TBox reasoning to complete");
}
Thread.sleep(1000);
diff --git a/webapp/src/edu/cornell/mannlib/vitro/webapp/servlet/setup/UpdateKnowledgeBase.java b/webapp/src/edu/cornell/mannlib/vitro/webapp/servlet/setup/UpdateKnowledgeBase.java
index d68ca6433..817ce52db 100644
--- a/webapp/src/edu/cornell/mannlib/vitro/webapp/servlet/setup/UpdateKnowledgeBase.java
+++ b/webapp/src/edu/cornell/mannlib/vitro/webapp/servlet/setup/UpdateKnowledgeBase.java
@@ -141,7 +141,6 @@ public class UpdateKnowledgeBase implements ServletContextListener {
if(!JenaDataSourceSetupBase.isFirstStartup()) {
try {
ctx.setAttribute(KBM_REQURIED_AT_STARTUP, Boolean.TRUE);
- log.info("Data migration required");
migrationChangesMade = ontologyUpdater.update(ctx);
if (tryMigrateDisplay) {
try {
@@ -221,8 +220,8 @@ public class UpdateKnowledgeBase implements ServletContextListener {
StartupStatus.getBean(ctx).info(this, "Updating knowledge base: reports are in '" + dataDir + "'");
Path changedDir = createDirectory(dataDir, "changedData");
- settings.setAddedDataFile(changedDir.resolve("addedData.n3").toString());
- settings.setRemovedDataFile(changedDir.resolve("removedData.n3").toString());
+ settings.setAddedDataFile(changedDir.resolve(timestampedFileName("addedData", "n3")).toString());
+ settings.setRemovedDataFile(changedDir.resolve(timestampedFileName("removedData", "n3")).toString());
Path logDir = createDirectory(dataDir, "logs");
settings.setLogFile(logDir.resolve(timestampedFileName("knowledgeBaseUpdate", "log")).toString());
From 29f41f61c7e812844089265b132e517659353a77 Mon Sep 17 00:00:00 2001
From: hudajkhan
Date: Mon, 14 Oct 2013 15:46:47 -0400
Subject: [PATCH 3/9] adding solr data getter and some other changes
---
.../controller/json/GetAllVClasses.java | 59 ++++++
.../webapp/controller/json/JsonServlet.java | 2 +
.../DefaultObjectPropertyFormGenerator.java | 6 +-
.../generators/ManagePageGenerator.java | 36 ++++
.../utils/ProcessDataGetterN3Map.java | 1 +
.../ProcessSolrIndividualsDataGetterN3.java | 179 ++++++++++++++++++
webapp/web/i18n/all.properties | 9 +-
webapp/web/js/menupage/pageManagementUtils.js | 67 ++++++-
.../web/js/menupage/processDataGetterUtils.js | 3 +-
.../menupage/processSolrDataGetterContent.js | 86 +++++++++
.../pageManagement--contentTemplates.ftl | 3 +-
.../pageManagement--customDataScript.ftl | 3 +-
.../forms/pageManagement--solrIndividuals.ftl | 33 ++++
.../freemarker/edit/forms/pageManagement.ftl | 10 +-
14 files changed, 480 insertions(+), 17 deletions(-)
create mode 100644 webapp/src/edu/cornell/mannlib/vitro/webapp/controller/json/GetAllVClasses.java
create mode 100644 webapp/src/edu/cornell/mannlib/vitro/webapp/edit/n3editing/configuration/preprocessors/utils/ProcessSolrIndividualsDataGetterN3.java
create mode 100644 webapp/web/js/menupage/processSolrDataGetterContent.js
create mode 100644 webapp/web/templates/freemarker/edit/forms/pageManagement--solrIndividuals.ftl
diff --git a/webapp/src/edu/cornell/mannlib/vitro/webapp/controller/json/GetAllVClasses.java b/webapp/src/edu/cornell/mannlib/vitro/webapp/controller/json/GetAllVClasses.java
new file mode 100644
index 000000000..14f67a50d
--- /dev/null
+++ b/webapp/src/edu/cornell/mannlib/vitro/webapp/controller/json/GetAllVClasses.java
@@ -0,0 +1,59 @@
+/* $This file is distributed under the terms of the license in /doc/license.txt$ */
+
+package edu.cornell.mannlib.vitro.webapp.controller.json;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.json.JSONObject;
+
+import edu.cornell.mannlib.vitro.webapp.beans.VClass;
+import edu.cornell.mannlib.vitro.webapp.beans.VClassGroup;
+import edu.cornell.mannlib.vitro.webapp.controller.VitroRequest;
+import edu.cornell.mannlib.vitro.webapp.dao.VClassGroupsForRequest;
+import edu.cornell.mannlib.vitro.webapp.dao.jena.VClassGroupCache;
+
+/**
+ *This class will get all the vclasses in the system.
+ */
+public class GetAllVClasses extends JsonObjectProducer {
+ private static final Log log = LogFactory
+ .getLog(GetAllVClasses.class);
+
+ public GetAllVClasses(VitroRequest vreq) {
+ super(vreq);
+ }
+
+ @Override
+ protected JSONObject process() throws Exception {
+ JSONObject map = new JSONObject();
+ //Get all VClassGroups
+ List vclasses = new ArrayList();
+ VClassGroupsForRequest vcgc = VClassGroupCache.getVClassGroups(vreq);
+ List groups = vcgc.getGroups();
+ for(VClassGroup vcg: groups) {
+ for( VClass vc : vcg){
+ vclasses.add(vc);
+ }
+
+ }
+
+ //Sort vclass by name
+ Collections.sort(vclasses);
+ ArrayList classes = new ArrayList(vclasses.size());
+
+ for(VClass vc: vclasses) {
+ JSONObject vcObj = new JSONObject();
+ vcObj.put("name", vc.getName());
+ vcObj.put("URI", vc.getURI());
+ classes.add(vcObj);
+ }
+ map.put("classes", classes);
+
+ return map;
+ }
+
+}
diff --git a/webapp/src/edu/cornell/mannlib/vitro/webapp/controller/json/JsonServlet.java b/webapp/src/edu/cornell/mannlib/vitro/webapp/controller/json/JsonServlet.java
index 3f516e109..baf72ec82 100644
--- a/webapp/src/edu/cornell/mannlib/vitro/webapp/controller/json/JsonServlet.java
+++ b/webapp/src/edu/cornell/mannlib/vitro/webapp/controller/json/JsonServlet.java
@@ -83,6 +83,8 @@ public class JsonServlet extends VitroHttpServlet {
new GetRenderedSolrIndividualsByVClass(vreq).process(resp);
}else if( vreq.getParameter("getRandomSolrIndividualsByVClass") != null ){
new GetRandomSolrIndividualsByVClass(vreq).process(resp);
+ } else if( vreq.getParameter("getAllVClasses") != null ){
+ new GetAllVClasses(vreq).process(resp);
}
}
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 69ae7f40a..583a97fd1 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
@@ -104,8 +104,10 @@ public class DefaultObjectPropertyFormGenerator implements EditConfigurationGene
// funny business because it needs to be able to retrieve anonymous union
// classes by their "pseudo-bnode URIs".
// Someday we'll need to figure out a different way of doing this.
- WebappDaoFactory ctxDaoFact = ModelAccess.on(
- vreq.getSession().getServletContext()).getWebappDaoFactory();
+ //WebappDaoFactory ctxDaoFact = ModelAccess.on(
+ // vreq.getSession().getServletContext()).getWebappDaoFactory();
+ WebappDaoFactory ctxDaoFact = vreq.getLanguageNeutralWebappDaoFactory();
+
List types = new ArrayList();
Individual subject = EditConfigurationUtils.getSubjectIndividual(vreq);
String predicateUri = EditConfigurationUtils.getPredicateUri(vreq);
diff --git a/webapp/src/edu/cornell/mannlib/vitro/webapp/edit/n3editing/configuration/generators/ManagePageGenerator.java b/webapp/src/edu/cornell/mannlib/vitro/webapp/edit/n3editing/configuration/generators/ManagePageGenerator.java
index bdb18cafd..42e85f8e7 100644
--- a/webapp/src/edu/cornell/mannlib/vitro/webapp/edit/n3editing/configuration/generators/ManagePageGenerator.java
+++ b/webapp/src/edu/cornell/mannlib/vitro/webapp/edit/n3editing/configuration/generators/ManagePageGenerator.java
@@ -4,6 +4,7 @@ package edu.cornell.mannlib.vitro.webapp.edit.n3editing.configuration.generators
import java.util.ArrayList;
import java.util.Arrays;
+import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
@@ -30,10 +31,14 @@ import com.hp.hpl.jena.rdf.model.Resource;
import com.hp.hpl.jena.rdf.model.ResourceFactory;
import com.hp.hpl.jena.vocabulary.XSD;
+import edu.cornell.mannlib.vitro.webapp.beans.VClass;
+import edu.cornell.mannlib.vitro.webapp.beans.VClassGroup;
import edu.cornell.mannlib.vitro.webapp.controller.VitroRequest;
import edu.cornell.mannlib.vitro.webapp.controller.freemarker.UrlBuilder;
import edu.cornell.mannlib.vitro.webapp.dao.DisplayVocabulary;
import edu.cornell.mannlib.vitro.webapp.dao.ModelAccess;
+import edu.cornell.mannlib.vitro.webapp.dao.VClassGroupsForRequest;
+import edu.cornell.mannlib.vitro.webapp.dao.jena.VClassGroupCache;
import edu.cornell.mannlib.vitro.webapp.edit.n3editing.VTwo.EditConfigurationUtils;
import edu.cornell.mannlib.vitro.webapp.edit.n3editing.VTwo.EditConfigurationVTwo;
import edu.cornell.mannlib.vitro.webapp.edit.n3editing.VTwo.fields.FieldVTwo;
@@ -521,6 +526,8 @@ private String getExistingIsSelfContainedTemplateQuery() {
MenuManagementDataUtils.includeRequiredSystemData(vreq.getSession().getServletContext(), data);
data.put("classGroup", new ArrayList());
data.put("classGroups", DataGetterUtils.getClassGroups(vreq));
+ //for solr individuals data get getter
+ data.put("classes", this.getAllVClasses(vreq));
data.put("availablePermissions", this.getAvailablePermissions(vreq));
data.put("availablePermissionOrderedList", this.getAvailablePermissonsOrderedURIs());
}
@@ -664,4 +671,33 @@ private String getExistingIsSelfContainedTemplateQuery() {
return query;
}
+ //Get all vclasses for the list of vclasses for solr
+ //Originally considered using an ajax request to get the vclasses list which is fine for adding a new content type
+ //but for an existing solr content type, would need to make yet another ajax request which seems too much
+ private List> getAllVClasses(VitroRequest vreq) {
+ List vclasses = new ArrayList();
+ VClassGroupsForRequest vcgc = VClassGroupCache.getVClassGroups(vreq);
+ List groups = vcgc.getGroups();
+ for(VClassGroup vcg: groups) {
+ for( VClass vc : vcg){
+ vclasses.add(vc);
+ }
+
+ }
+
+ //Sort vclass by name
+ Collections.sort(vclasses);
+ ArrayList> classes = new ArrayList>(vclasses.size());
+
+
+ for(VClass vc: vclasses) {
+ HashMap vcObj = new HashMap();
+ vcObj.put("name", vc.getName());
+ vcObj.put("URI", vc.getURI());
+ classes.add(vcObj);
+ }
+ return classes;
+ }
+
+
}
diff --git a/webapp/src/edu/cornell/mannlib/vitro/webapp/edit/n3editing/configuration/preprocessors/utils/ProcessDataGetterN3Map.java b/webapp/src/edu/cornell/mannlib/vitro/webapp/edit/n3editing/configuration/preprocessors/utils/ProcessDataGetterN3Map.java
index 730774c70..0ca8d4f65 100644
--- a/webapp/src/edu/cornell/mannlib/vitro/webapp/edit/n3editing/configuration/preprocessors/utils/ProcessDataGetterN3Map.java
+++ b/webapp/src/edu/cornell/mannlib/vitro/webapp/edit/n3editing/configuration/preprocessors/utils/ProcessDataGetterN3Map.java
@@ -18,6 +18,7 @@ public class ProcessDataGetterN3Map {
map.put("edu.cornell.mannlib.vitro.webapp.utils.dataGetter.ClassGroupPageData", "edu.cornell.mannlib.vitro.webapp.edit.n3editing.configuration.preprocessors.utils.ProcessClassGroupDataGetterN3");
map.put("edu.cornell.mannlib.vitro.webapp.utils.dataGetter.IndividualsForClassesDataGetter", "edu.cornell.mannlib.vitro.webapp.edit.n3editing.configuration.preprocessors.utils.ProcessIndividualsForClassesDataGetterN3");
map.put("edu.cornell.mannlib.vitro.webapp.utils.dataGetter.FixedHTMLDataGetter", "edu.cornell.mannlib.vitro.webapp.edit.n3editing.configuration.preprocessors.utils.ProcessFixedHTMLN3");
+ map.put("edu.cornell.mannlib.vitro.webapp.utils.dataGetter.SolrIndividualsDataGetter", "edu.cornell.mannlib.vitro.webapp.edit.n3editing.configuration.preprocessors.utils.ProcessSolrIndividualsDataGetterN3");
return map;
}
diff --git a/webapp/src/edu/cornell/mannlib/vitro/webapp/edit/n3editing/configuration/preprocessors/utils/ProcessSolrIndividualsDataGetterN3.java b/webapp/src/edu/cornell/mannlib/vitro/webapp/edit/n3editing/configuration/preprocessors/utils/ProcessSolrIndividualsDataGetterN3.java
new file mode 100644
index 000000000..eae59bb9c
--- /dev/null
+++ b/webapp/src/edu/cornell/mannlib/vitro/webapp/edit/n3editing/configuration/preprocessors/utils/ProcessSolrIndividualsDataGetterN3.java
@@ -0,0 +1,179 @@
+/* $This file is distributed under the terms of the license in /doc/license.txt$ */
+
+package edu.cornell.mannlib.vitro.webapp.edit.n3editing.configuration.preprocessors.utils;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+import javax.servlet.ServletContext;
+
+import net.sf.json.JSONObject;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
+import com.hp.hpl.jena.ontology.OntModel;
+import com.hp.hpl.jena.query.Query;
+import com.hp.hpl.jena.query.QueryExecution;
+import com.hp.hpl.jena.query.QueryExecutionFactory;
+import com.hp.hpl.jena.query.QueryFactory;
+import com.hp.hpl.jena.query.QuerySolution;
+import com.hp.hpl.jena.query.ResultSet;
+import com.hp.hpl.jena.rdf.model.Literal;
+import com.hp.hpl.jena.rdf.model.Resource;
+
+import edu.cornell.mannlib.vitro.webapp.edit.n3editing.VTwo.fields.FieldVTwo;
+//Returns the appropriate n3 based on data getter
+public class ProcessSolrIndividualsDataGetterN3 extends ProcessDataGetterAbstract {
+ private static String classType = "java:edu.cornell.mannlib.vitro.webapp.utils.dataGetter.SolrIndividualsDataGetter";
+ private Log log = LogFactory.getLog(ProcessFixedHTMLN3.class);
+
+ public ProcessSolrIndividualsDataGetterN3(){
+
+ }
+ //Pass in variable that represents the counter
+
+ //TODO: ensure correct model returned
+ //We shouldn't use the ACTUAL values here but generate the n3 required
+ //?dataGetter a FixedHTMLDataGetter ; display:saveToVar ?saveToVar; display:htmlValue ?htmlValue .
+ public List retrieveN3Required(int counter) {
+ String dataGetterVar = getDataGetterVar(counter);
+ //UPDATE: Using variable for class type
+ String classTypeVar = getN3VarName(classTypeVarBase, counter);
+ String n3 = dataGetterVar + " a " + classTypeVar + "; \n" +
+ "display:saveToVar " + getN3VarName("saveToVar", counter) + "; \n" +
+ "display:hasVClassId " + getN3VarName("vclassUri", counter) + " .";
+ List requiredList = new ArrayList();
+ requiredList.add(getPrefixes() + n3);
+ return requiredList;
+
+ }
+ public List retrieveN3Optional(int counter) {
+ return null;
+ }
+
+
+ public List retrieveLiteralsOnForm(int counter) {
+ List literalsOnForm = new ArrayList();
+ literalsOnForm.add(getVarName("saveToVar",counter));
+ return literalsOnForm;
+
+ }
+
+
+ public List retrieveUrisOnForm(int counter) {
+ List urisOnForm = new ArrayList();
+ //UPDATE: adding class type as uri on form
+ urisOnForm.add(getVarName("vclassUri", counter));
+
+ urisOnForm.add(getVarName(classTypeVarBase, counter));
+ return urisOnForm;
+
+ }
+
+ public List retrieveFields(int counter) {
+ List fields = new ArrayList();
+
+ //fields.add(new FieldVTwo().setName(getVarName("dataGetter", counter)));
+ fields.add(new FieldVTwo().setName(getVarName("saveToVar", counter)));
+ fields.add(new FieldVTwo().setName(getVarName("vclassUri", counter)));
+ //UPDATE: adding class type to the uris on the form
+ fields.add(new FieldVTwo().setName(getVarName(classTypeVarBase, counter)));
+ return fields;
+ }
+
+ public List getLiteralVarNamesBase() {
+ return Arrays.asList("saveToVar");
+ }
+
+ //these are for the fields ON the form
+ public List getUriVarNamesBase() {
+ return Arrays.asList(classTypeVarBase, "vclassUri");
+ }
+
+ //For Existing Values in case of editing
+
+ //Execute populate before retrieval
+ public void populateExistingValues(String dataGetterURI, int counter, OntModel queryModel) {
+ //First, put dataGetterURI within scope as well
+ this.populateExistingDataGetterURI(dataGetterURI, counter);
+ //Put in type
+ this.populateExistingClassType(this.getClassType(), counter);
+ //Sparql queries for values to be executed
+ //And then placed in the correct place/literal or uri
+ String querystr = getExistingValuesSparqlQuery(dataGetterURI);
+ QueryExecution qe = null;
+ try{
+ Query query = QueryFactory.create(querystr);
+ qe = QueryExecutionFactory.create(query, queryModel);
+ ResultSet results = qe.execSelect();
+ while( results.hasNext()){
+ QuerySolution qs = results.nextSolution();
+ Literal saveToVarLiteral = qs.getLiteral("saveToVar");
+ Resource vclassUriResource = qs.getResource("vclassUri");
+ //Put both literals in existing literals
+ existingLiteralValues.put(this.getVarName("saveToVar", counter),
+ new ArrayList(Arrays.asList(saveToVarLiteral)));
+ existingUriValues.put(this.getVarName("vclassUri", counter),
+ new ArrayList(Arrays.asList(vclassUriResource.getURI())));
+ }
+ } catch(Exception ex) {
+ log.error("Exception occurred in retrieving existing values with query " + querystr, ex);
+ }
+
+
+ }
+
+
+ //?dataGetter a FixedHTMLDataGetter ; display:saveToVar ?saveToVar; display:htmlValue ?htmlValue .
+ protected String getExistingValuesSparqlQuery(String dataGetterURI) {
+ String query = this.getSparqlPrefix() + " SELECT ?saveToVar ?vclassUri WHERE {" +
+ "<" + dataGetterURI + "> display:saveToVar ?saveToVar . \n" +
+ "<" + dataGetterURI + "> display:hasVClassId ?vclassUri . \n" +
+ "}";
+ return query;
+ }
+
+
+ //Method to create a JSON object with existing values to return to form
+ //There may be a better way to do this without having to run the query twice
+ //TODO: Refactor code if required
+ public JSONObject getExistingValuesJSON(String dataGetterURI, OntModel queryModel, ServletContext context) {
+ JSONObject jObject = new JSONObject();
+ jObject.element("dataGetterClass", classType);
+ jObject.element(classTypeVarBase, classType);
+ String querystr = getExistingValuesSparqlQuery(dataGetterURI);
+ QueryExecution qe = null;
+ try{
+ Query query = QueryFactory.create(querystr);
+ qe = QueryExecutionFactory.create(query, queryModel);
+ ResultSet results = qe.execSelect();
+ while( results.hasNext()){
+ QuerySolution qs = results.nextSolution();
+ Literal saveToVarLiteral = qs.getLiteral("saveToVar");
+ Resource vclassUriResource = qs.getResource("vclassUri");
+ String vclassUriString = vclassUriResource.getURI();
+ jObject.element("saveToVar", saveToVarLiteral.getString());
+ //TODO: Handle single and double quotes within string and escape properlyu
+ jObject.element("vclassUri", vclassUriString);
+ }
+ } catch(Exception ex) {
+ log.error("Exception occurred in retrieving existing values with query " + querystr, ex);
+ }
+ return jObject;
+
+ }
+
+ //Escape single and double quotes for html string to be returned to form
+ public String replaceQuotes(String inputStr) {
+ return inputStr.replaceAll("\'", "'").replaceAll("\"", """);
+
+ }
+
+ //This class can be extended so returning type here
+ public String getClassType() {
+ return classType;
+ }
+}
+
+
diff --git a/webapp/web/i18n/all.properties b/webapp/web/i18n/all.properties
index 62fd42caa..193c8a2e7 100644
--- a/webapp/web/i18n/all.properties
+++ b/webapp/web/i18n/all.properties
@@ -605,7 +605,8 @@ page_text = page text
sparql_query_results = Sparql Query Results
no_results_returned = No results were returned.
-
+solr_individual_results = Solr Class Individuals
+select_vclass_uri = Select VClass
#
# manage proxies templates ( /templates/freemarker/body/manageproxies )
#
@@ -849,9 +850,9 @@ delete = delete
map_processor_error = An error has occurred and the map of processors for this content is missing. Please contact the administrator
code_processing_error = An error has occurred and the code for processing this content is missing a component. Please contact the administrator.
-supply_class_group = You must supply a class group
-select_classes_to_display = You must select the classes to display
-
+supply_class_group = You must supply a class group.
+select_classes_to_display = You must select the classes to display.
+select_class_for_solr = You must select a class to display its individuals.
supply_variable_name = You must supply a variable to save HTML content.
apostrophe_not_allowed = The variable name should not have an apostrophe.
double_quote_note_allowed = The variable name should not have a double quote.
diff --git a/webapp/web/js/menupage/pageManagementUtils.js b/webapp/web/js/menupage/pageManagementUtils.js
index f8c3e4a7d..20b95899b 100644
--- a/webapp/web/js/menupage/pageManagementUtils.js
+++ b/webapp/web/js/menupage/pageManagementUtils.js
@@ -104,6 +104,7 @@ var pageManagementUtils = {
this.classGroupSection = $("section#browseClassGroup");
this.sparqlQuerySection = $("section#sparqlQuery");
this.fixedHTMLSection = $("section#fixedHtml");
+ this.solrIndividualsSection = $("section#solrIndividuals");
//From original menu management edit
this.defaultTemplateRadio = $('input.default-template');
this.customTemplateRadio = $('input.custom-template');
@@ -117,6 +118,7 @@ var pageManagementUtils = {
this.classesForClassGroup = $('section#classesInSelectedGroup');
this.selectedGroupForPage = $('#selectedContentTypeValue');
this.allClassesSelectedCheckbox = $('#allSelected');
+
this.displayInternalMessage = $('#internal-class label em');
this.pageContentSubmissionInputs = $("#pageContentSubmissionInputs");
this.headerBar = $("section#headerBar");
@@ -131,6 +133,8 @@ var pageManagementUtils = {
this.rightSideDiv = $("div#rightSide");
//contentDivs container where content added/existing placed
this.savedContentDivs = $("section#contentDivs");
+ //for solr individuals data getter
+ this.solrAllClassesDropdown = $("select#vclassUri");
},
initDisplay: function(){
//right side components
@@ -143,6 +147,7 @@ var pageManagementUtils = {
this.classGroupSection.hide();
this.sparqlQuerySection.hide();
this.fixedHTMLSection.hide();
+ this.solrIndividualsSection.hide();
this.classesForClassGroup.addClass('hidden');
//left side components
//These depend on whether or not this is an existing item or not
@@ -155,8 +160,44 @@ var pageManagementUtils = {
this.menuSection.hide();
}
}
-
+ //populates the dropdown of classes for the solr individuals template
+ //dropdown now populated in template/from form specific data instead of ajax request
+ //this.populateClassForSolrDropdown();
},
+ //this method can be utilized if using an ajax request to get the vclasses
+ /*
+ //for solr individuals - remember this populates the template class dropdown
+ populateClassForSolrDropdown:function() {
+
+ //Run ajax query
+ var url = "dataservice?getAllVClasses=1";
+
+ //Make ajax call to retrieve vclasses
+ $.getJSON(url, function(results) {
+ //Moved the function to processClassGroupDataGetterContent
+ //Should probably remove this entire method and copy there
+ pageManagementUtils.displayAllClassesForSolrDropdown(results);
+ });
+ },
+ displayAllClassesForSolrDropdown:function(results) {
+ if ( results.classes.length == 0 ) {
+
+ } else {
+ var appendHtml = "";
+ $.each(results.classes, function(i, item) {
+ var thisClass = results.classes[i];
+ var thisClassName = thisClass.name;
+ //Create options for the dropdown
+ appendHtml += "" + thisClassName + " ";
+ });
+
+ //if there are options to add
+ if(appendHtml != "") {
+ pageManagementUtils.solrAllClassesDropdown.html(appendHtml);
+ }
+
+ }
+ },*/
bindEventListeners:function(){
this.defaultTemplateRadio.click( function() {
@@ -206,10 +247,13 @@ var pageManagementUtils = {
pageManagementUtils.checkSelfContainedRadio();
});
//replacing with menu management edit version which is extended with some of the logic below
+ //This is technically content specific and should be moved into the individual processor classes somehow
this.selectClassGroupDropdown.change(function() {
pageManagementUtils.chooseClassGroup();
});
+
+
this.contentTypeSelect.change( function() {
pageManagementUtils.handleContentTypeSelect();
});
@@ -229,6 +273,7 @@ var pageManagementUtils = {
pageManagementUtils.classGroupSection.hide();
pageManagementUtils.fixedHTMLSection.hide();
pageManagementUtils.sparqlQuerySection.hide();
+ pageManagementUtils.solrIndividualsSection.hide();
//Reset main content type drop-down
pageManagementUtils.contentTypeSelectOptions.eq(0).attr('selected', 'selected');
if ( pageManagementUtils.leftSideDiv.css("height") != undefined ) {
@@ -291,7 +336,8 @@ var pageManagementUtils = {
}
},
- //Select content type
+ //Select content type - this is content type specific
+ //TODO: Find better way to refactor this and/or see if any of this display functionality can be moved into content type processing
handleContentTypeSelect:function() {
_this = pageManagementUtils;
pageManagementUtils.clearSourceTemplateValues();
@@ -299,22 +345,31 @@ var pageManagementUtils = {
pageManagementUtils.classGroupSection.show();
pageManagementUtils.fixedHTMLSection.hide();
pageManagementUtils.sparqlQuerySection.hide();
+ pageManagementUtils.solrIndividualsSection.hide();
pageManagementUtils.headerBar.text(pageManagementUtils.browseClassGroup + " - ");
pageManagementUtils.headerBar.show();
$('div#selfContainedDiv').hide();
}
- if ( _this.contentTypeSelect.val() == "fixedHtml" || _this.contentTypeSelect.val() == "sparqlQuery" ) {
+ if ( _this.contentTypeSelect.val() == "fixedHtml" || _this.contentTypeSelect.val() == "sparqlQuery" || _this.contentTypeSelect.val() == "solrIndividuals") {
pageManagementUtils.classGroupSection.hide();
//if fixed html show that, otherwise show sparql results
if ( _this.contentTypeSelect.val() == "fixedHtml" ) {
pageManagementUtils.headerBar.text(pageManagementUtils.fixedHtml + " - ");
pageManagementUtils.fixedHTMLSection.show();
pageManagementUtils.sparqlQuerySection.hide();
+ pageManagementUtils.solrIndividualsSection.hide();
}
- else {
+ else if (_this.contentTypeSelect.val() == "sparqlQuery"){
pageManagementUtils.headerBar.text(pageManagementUtils.sparqlResults + " - ");
pageManagementUtils.sparqlQuerySection.show();
pageManagementUtils.fixedHTMLSection.hide();
+ pageManagementUtils.solrIndividualsSection.hide();
+ } else {
+ //solr individuals
+ pageManagementUtils.headerBar.text(pageManagementUtils.solrIndividuals + " - ");
+ pageManagementUtils.sparqlQuerySection.hide();
+ pageManagementUtils.fixedHTMLSection.hide();
+ pageManagementUtils.solrIndividualsSection.show();
}
pageManagementUtils.headerBar.show();
@@ -325,6 +380,7 @@ var pageManagementUtils = {
pageManagementUtils.classGroupSection.hide();
pageManagementUtils.fixedHTMLSection.hide();
pageManagementUtils.sparqlQuerySection.hide();
+ pageManagementUtils.solrIndividualsSection.hide();
pageManagementUtils.classesForClassGroup.addClass('hidden');
pageManagementUtils.headerBar.hide();
pageManagementUtils.headerBar.text("");
@@ -359,6 +415,7 @@ var pageManagementUtils = {
pageManagementUtils.clearInputs(pageManagementUtils.fixedHTMLSection);
pageManagementUtils.clearInputs(pageManagementUtils.sparqlQuerySection);
pageManagementUtils.clearInputs(pageManagementUtils.classGroupSection);
+ pageManagementUtils.clearInputs(pageManagementUtils.solrIndividualsSection);
},
clearInputs:function($el) {
@@ -387,7 +444,7 @@ var pageManagementUtils = {
// Get rid of the cancel link; it'll be replaced by a delete link
$newContentObj.find('span#cancelContent' + counter).html('');
- if ( contentType == "sparqlQuery" || contentType == "fixedHtml") {
+ if ( contentType == "sparqlQuery" || contentType == "fixedHtml" || contentType == "solrIndividuals") {
varOrClass = $newContentObj.find('input[name="saveToVar"]').val();
}
else if ( contentType == "browseClassGroup" ) {
diff --git a/webapp/web/js/menupage/processDataGetterUtils.js b/webapp/web/js/menupage/processDataGetterUtils.js
index d6132a6c7..e7b9cd0c9 100644
--- a/webapp/web/js/menupage/processDataGetterUtils.js
+++ b/webapp/web/js/menupage/processDataGetterUtils.js
@@ -9,7 +9,8 @@ var processDataGetterUtils = {
dataGetterProcessorMap:{"browseClassGroup": processClassGroupDataGetterContent,
"sparqlQuery": processSparqlDataGetterContent,
"fixedHtml":processFixedHTMLDataGetterContent,
- "individualsForClasses":processIndividualsForClassesDataGetterContent},
+ "individualsForClasses":processIndividualsForClassesDataGetterContent,
+ "solrIndividuals":processSolrDataGetterContent},
selectDataGetterType:function(pageContentSection) {
var contentType = pageContentSection.attr("contentType");
//The form can provide "browse class group" as content type but need to check
diff --git a/webapp/web/js/menupage/processSolrDataGetterContent.js b/webapp/web/js/menupage/processSolrDataGetterContent.js
new file mode 100644
index 000000000..0e1026b5e
--- /dev/null
+++ b/webapp/web/js/menupage/processSolrDataGetterContent.js
@@ -0,0 +1,86 @@
+/* $This file is distributed under the terms of the license in /doc/license.txt$ */
+
+$.extend(this, i18nStringsSolrIndividuals);
+
+//Process sparql data getter and provide a json object with the necessary information
+var processSolrDataGetterContent = {
+ dataGetterClass:null,
+ //can use this if expect to initialize from elsewhere
+ initProcessor:function(dataGetterClass) {
+ this.dataGetterClass =dataGetterClass;
+
+ },
+
+ processPageContentSection:function(pageContentSection) {
+
+ var variableValue = pageContentSection.find("input[name='saveToVar']").val();
+ var vclassUriValue = pageContentSection.find("select[name='vclassUri']").val();
+
+
+ //query model should also be an input
+ //set query model to query model here - vitro:contentDisplayModel
+ var returnObject = {saveToVar:variableValue, vclassUri:vclassUriValue, dataGetterClass:this.dataGetterClass};
+ return returnObject;
+ },
+ //For an existing set of content where form is already set, fill in the values
+ populatePageContentSection:function(existingContentObject, pageContentSection) {
+ var saveToVarValue = existingContentObject["saveToVar"];
+ var vclassUriValue = existingContentObject["vclassUri"];
+
+
+ //Now find and set value
+ pageContentSection.find("input[name='saveToVar']").val(saveToVarValue);
+ //set value of solr query
+ pageContentSection.find("select[name='vclassUri']").val(vclassUriValue);
+
+ },
+ //For the label of the content section for editing, need to add additional value
+ retrieveContentLabel:function() {
+ return i18nStringsSolrIndividuals.solrIndividuals;
+ },
+ //For the label of the content section for editing, need to add additional value
+ retrieveAdditionalLabelText:function(existingContentObject) {
+ var saveToVarValue = existingContentObject["saveToVar"];
+ return saveToVarValue;
+ },
+ //Validation on form submit: Check to see that class group has been selected
+ validateFormSubmission: function(pageContentSection, pageContentSectionLabel) {
+ var validationError = "";
+ //Check that vclassuri and saveToVar have been input
+ var variableValue = pageContentSection.find("input[name='saveToVar']").val();
+ if(variableValue == "") {
+ validationError += pageContentSectionLabel + ": " + i18nStringsSolrIndividuals.supplyQueryVariable + " "
+ }
+ if(processSolrDataGetterContent.stringHasSingleQuote(variableValue)) {
+ validationError += pageContentSectionLabel + ": " + i18nStringsSolrIndividuals.noApostrophes + " ";
+ }
+ if(processSolrDataGetterContent.stringHasDoubleQuote(variableValue)) {
+ validationError += pageContentSectionLabel + ": " + i18nStringsSolrIndividuals.noDoubleQuotes + " ";
+ }
+
+ //validation for solr individuals
+
+ var vclassUriValue = pageContentSection.find("select[name='vclassUri']").val();
+ if(vclassUriValue == "") {
+ validationError += pageContentSectionLabel + ": " + i18nStringsSolrIndividuals.selectClass + " ";
+ }
+ return validationError;
+ },
+ encodeQuotes:function(inputStr) {
+ return inputStr.replace(/'/g, ''').replace(/"/g, '"');
+ },
+ //For the variable name, no single quote should be allowed
+ //This can be extended for other special characters
+ stringHasSingleQuote:function(inputStr) {
+ return(inputStr.indexOf("'") != -1);
+ },
+ stringHasDoubleQuote:function(inputStr) {
+ return(inputStr.indexOf("\"") != -1);
+ },
+ replaceEncodedWithEscapedQuotes: function(inputStr) {
+
+ return inputStr.replace(/'/g, "\'").replace(/"/g, "\"");
+ }
+
+
+};
\ No newline at end of file
diff --git a/webapp/web/templates/freemarker/edit/forms/pageManagement--contentTemplates.ftl b/webapp/web/templates/freemarker/edit/forms/pageManagement--contentTemplates.ftl
index 68984cbfa..6caf3c23b 100644
--- a/webapp/web/templates/freemarker/edit/forms/pageManagement--contentTemplates.ftl
+++ b/webapp/web/templates/freemarker/edit/forms/pageManagement--contentTemplates.ftl
@@ -2,4 +2,5 @@
<#--These are the different content templates that will be cloned and used within page management-->
<#include "pageManagement--browseClassGroups.ftl">
<#include "pageManagement--sparqlQuery.ftl">
-<#include "pageManagement--fixedHtml.ftl">
\ No newline at end of file
+<#include "pageManagement--fixedHtml.ftl">
+<#include "pageManagement--solrIndividuals.ftl">
\ No newline at end of file
diff --git a/webapp/web/templates/freemarker/edit/forms/pageManagement--customDataScript.ftl b/webapp/web/templates/freemarker/edit/forms/pageManagement--customDataScript.ftl
index 313c6e74f..9500289aa 100644
--- a/webapp/web/templates/freemarker/edit/forms/pageManagement--customDataScript.ftl
+++ b/webapp/web/templates/freemarker/edit/forms/pageManagement--customDataScript.ftl
@@ -12,7 +12,8 @@ scripts list.-->
"browseClassGroup": "java:edu.cornell.mannlib.vitro.webapp.utils.dataGetter.ClassGroupPageData",
"individualsForClasses": "java:edu.cornell.mannlib.vitro.webapp.utils.dataGetter.IndividualsForClassesDataGetter",
"sparqlQuery":"java:edu.cornell.mannlib.vitro.webapp.utils.dataGetter.SparqlQueryDataGetter",
- "fixedHtml":"java:edu.cornell.mannlib.vitro.webapp.utils.dataGetter.FixedHTMLDataGetter"
+ "fixedHtml":"java:edu.cornell.mannlib.vitro.webapp.utils.dataGetter.FixedHTMLDataGetter",
+ "solrIndividuals":"java:edu.cornell.mannlib.vitro.webapp.utils.dataGetter.SolrIndividualsDataGetter"
}
};
\ No newline at end of file
diff --git a/webapp/web/templates/freemarker/edit/forms/pageManagement--solrIndividuals.ftl b/webapp/web/templates/freemarker/edit/forms/pageManagement--solrIndividuals.ftl
new file mode 100644
index 000000000..38610fd11
--- /dev/null
+++ b/webapp/web/templates/freemarker/edit/forms/pageManagement--solrIndividuals.ftl
@@ -0,0 +1,33 @@
+<#-- $This file is distributed under the terms of the license in /doc/license.txt$ -->
+<#--This contains the template for the Solr Class individuals content type that is to be cloned and used in page management-->
+<#assign classGroup = pageData.classGroup />
+<#assign classGroups = pageData.classGroups />
+<#assign classes = pageData.classes />
+
+
+${scripts.add('')}
diff --git a/webapp/web/templates/freemarker/edit/forms/pageManagement.ftl b/webapp/web/templates/freemarker/edit/forms/pageManagement.ftl
index 2d9d8a26c..108caeb5b 100644
--- a/webapp/web/templates/freemarker/edit/forms/pageManagement.ftl
+++ b/webapp/web/templates/freemarker/edit/forms/pageManagement.ftl
@@ -80,7 +80,8 @@
${i18n().select_type}
${i18n().browse_class_group}
${i18n().fixed_html}
- ${i18n().sparql_query_results}
+ ${i18n().sparql_query_results}
+ ${i18n().solr_individual_results}
${i18n().add_types}
@@ -136,8 +137,10 @@
- ${i18n().page_select_permission}
+
+
+ ${i18n().page_select_permission}
+
${i18n().page_select_permission_option}
<#list pageAvailablePermissionsURIsList as permissionURI>
@@ -176,6 +179,7 @@
browseClassGroup: '${i18n().browse_class_group}',
fixedHtml: '${i18n().fixed_html}',
sparqlResults: '${i18n().sparql_query_results}',
+ solrIndividuals: '${i18n().solr_individual_results}',
orString: '${i18n().or}',
deleteString: '${i18n().delete}',
allCapitalized: '${i18n().all_capitalized}',
From e33070b3a8c245f659d5649700feb8817cb8efbf Mon Sep 17 00:00:00 2001
From: tworrall
Date: Mon, 14 Oct 2013 16:29:01 -0400
Subject: [PATCH 4/9] VIVO-359 and VIVO-345
---
doc/install.html | 24 ++++++++++++++
.../individual/individual-properties.ftl | 2 ++
.../individual-property-group-tabs.ftl | 2 ++
.../freemarker/lib/lib-properties.ftl | 33 ++++++++++++-------
4 files changed, 50 insertions(+), 11 deletions(-)
diff --git a/doc/install.html b/doc/install.html
index cdba8cb20..c409b190c 100644
--- a/doc/install.html
+++ b/doc/install.html
@@ -250,6 +250,30 @@
support websites.
+
+ The following browsers are supported for this release
+
+
+
+ Mac:
+
+ Chrome 30.0.1599.69 and above
+ FireFox 3.6.28, 10.0.12, 24
+ Opera 12.02
+ Safari 5.0.3
+
+
+
+ PC:
+
+ Chrome 25.1364.2 and above
+ FireFox 10.0.12, 24
+ Internet Explorer 8, 9, 10
+ Opera 12.02
+
+
+
+
II. Create an empty MySQL database
Decide on a database name, username, and password. Log into your
diff --git a/webapp/web/templates/freemarker/body/partials/individual/individual-properties.ftl b/webapp/web/templates/freemarker/body/partials/individual/individual-properties.ftl
index 35c5e5666..610d4b409 100644
--- a/webapp/web/templates/freemarker/body/partials/individual/individual-properties.ftl
+++ b/webapp/web/templates/freemarker/body/partials/individual/individual-properties.ftl
@@ -32,6 +32,8 @@
<#elseif rangeClass == "Name" && property.statements?has_content && editable >
${property.name} <@p.verboseDisplay property />
+ <#elseif rangeClass == "Title" && property.statements?has_content && editable >
+ ${property.name} <@p.verboseDisplay property />
<#else>
${property.name} <@p.addLink property editable /> <@p.verboseDisplay property />
#if>
diff --git a/webapp/web/templates/freemarker/body/partials/individual/individual-property-group-tabs.ftl b/webapp/web/templates/freemarker/body/partials/individual/individual-property-group-tabs.ftl
index 3b77322c8..bb9bd9dc8 100644
--- a/webapp/web/templates/freemarker/body/partials/individual/individual-property-group-tabs.ftl
+++ b/webapp/web/templates/freemarker/body/partials/individual/individual-property-group-tabs.ftl
@@ -10,6 +10,7 @@
<#list propertyGroups.all as groupTabs>
+ <#if ( groupTabs.properties?size > 0 ) >
<#assign groupName = groupTabs.getName(nameForOtherGroup)>
<#if groupName?has_content>
<#--the function replaces spaces in the name with underscores, also called for the property group menu-->
@@ -26,6 +27,7 @@
${groupName?capitalize}
#if>
+ #if>
#list>
<#if (propertyGroups.all?size > 1) >
${i18n().view_all_capitalized}
diff --git a/webapp/web/templates/freemarker/lib/lib-properties.ftl b/webapp/web/templates/freemarker/lib/lib-properties.ftl
index 7e1fa765b..fb69a6a8a 100644
--- a/webapp/web/templates/freemarker/lib/lib-properties.ftl
+++ b/webapp/web/templates/freemarker/lib/lib-properties.ftl
@@ -122,17 +122,22 @@ name will be used as the label. -->
#if>
#macro>
-<#macro addLink property editable label="${property.name}">
+<#macro addLink property editable label="${property.name}">
+ <#if property.rangeUri?? >
+ <#local rangeUri = property.rangeUri />
+ <#else>
+ <#local rangeUri = "" />
+ #if>
<#if editable>
<#local url = property.addUrl>
<#if url?has_content>
- <@showAddLink property.localName property.name label url />
+ <@showAddLink property.localName label url rangeUri/>
#if>
#if>
#macro>
-<#macro showAddLink propertyLocalName propertyName label url>
- <#if propertyName == "authors" || propertyName == "webpage" || propertyLocalName == "hasResearchArea">
+<#macro showAddLink propertyLocalName label url rangeUri>
+ <#if rangeUri?contains("Authorship") || rangeUri?contains("URL") || rangeUri?contains("Editorship") || label == "hasResearchArea">
<#else>
@@ -147,17 +152,23 @@ name will be used as the label. -->
<#macro propertyListItem property statement editable >
+ <#if property.rangeUri?? >
+ <#local rangeUri = property.rangeUri />
+ <#else>
+ <#local rangeUri = "" />
+ #if>
<#nested>
- <@editingLinks "${property.localName}" "${property.name}" statement editable/>
+ <@editingLinks "${property.localName}" "${property.name}" statement editable rangeUri/>
#macro>
-<#macro editingLinks propertyLocalName propertyName statement editable>
- <#if editable && (propertyName != "authors" && propertyName != "webpage" && propertyLocalName != "hasResearchArea")>
- <@editLink propertyLocalName propertyName statement />
- <@deleteLink propertyLocalName propertyName statement />
-
+<#macro editingLinks propertyLocalName propertyName statement editable rangeUri="">
+ <#if editable >
+ <#if (!rangeUri?contains("Authorship") && !rangeUri?contains("URL") && !rangeUri?contains("Editorship") && propertyLocalName != "hasResearchArea")>
+ <@editLink propertyLocalName propertyName statement />
+ <@deleteLink propertyLocalName propertyName statement />
+ #if>
#if>
#macro>
<#macro editLink propertyLocalName propertyName statement>
@@ -250,7 +261,7 @@ name will be used as the label. -->
<#local label = individual.nameStatement>
${label.value}
<#if useEditLink>
- <@editingLinks "label" "" label editable />
+ <@editingLinks "label" "" label editable ""/>
<#elseif (editable && (labelCount > 0)) || (languageCount > 1)>
<#--We display the link even when the user is not logged in case of multiple labels with different languages-->
<#assign labelLink = ""/>
From dfc3163ac13fa3a59b4fcfd06595f475b98067bb Mon Sep 17 00:00:00 2001
From: tworrall
Date: Tue, 15 Oct 2013 10:27:31 -0400
Subject: [PATCH 5/9] js file needed to reference jQuery instead of just $
---
webapp/web/js/vitroUtils.js | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/webapp/web/js/vitroUtils.js b/webapp/web/js/vitroUtils.js
index ee9f88e08..e910efa74 100644
--- a/webapp/web/js/vitroUtils.js
+++ b/webapp/web/js/vitroUtils.js
@@ -5,7 +5,7 @@ $(document).ready(function(){
// Use jQuery() instead of $() alias, because dwr/util.js, loaded on back end editing
// pages, overwrites $.
// fade out welcome-message when user logs in
- $.extend(this, i18nStrings);
+ jQuery.extend(this, i18nStrings);
jQuery('section#welcome-message').css('display', 'block').delay(2000).fadeOut(1500);
From 3b6d65176d82ddf485ee35514ff826cbe49f2b1d Mon Sep 17 00:00:00 2001
From: j2blake
Date: Tue, 15 Oct 2013 12:59:57 -0400
Subject: [PATCH 6/9] VIVO-363 Write the exception to the log, first thing.
---
webapp/web/error.jsp | 12 ++++++++++++
1 file changed, 12 insertions(+)
diff --git a/webapp/web/error.jsp b/webapp/web/error.jsp
index 07a8a2e37..566e53673 100755
--- a/webapp/web/error.jsp
+++ b/webapp/web/error.jsp
@@ -5,7 +5,19 @@
<%@ page import="com.oreilly.servlet.ServletUtils,edu.cornell.mannlib.vitro.webapp.web.*" %>
<%@page import="edu.cornell.mannlib.vitro.webapp.controller.VitroRequest"%>
<%@page import="edu.cornell.mannlib.vitro.webapp.beans.ApplicationBean"%>
+<%@page import="org.apache.commons.logging.Log"%>
+<%@page import="org.apache.commons.logging.LogFactory"%>
<%
+ // We have seen that this page can throw its own error.
+ // Before it does so, be sure that we have written the original error to the log.
+ Object c = request.getAttribute("javax.servlet.jsp.jspException");
+ if (c instanceof Throwable) {
+ Throwable cause = (Throwable) c;
+ Log log = LogFactory.getLog(this.getClass());
+ log.error("Error: ", cause);
+ }
+
+
VitroRequest vreq = new VitroRequest(request);
ApplicationBean appBean = vreq.getAppBean();
String themeDir = appBean.getThemeDir();
From 7fc2f07a7701c7253897a6b5fecac44d9bbdd8b9 Mon Sep 17 00:00:00 2001
From: j2blake
Date: Tue, 15 Oct 2013 15:43:23 -0400
Subject: [PATCH 7/9] Get language-correct labels whether editing or not.
---
.../web/templatemodels/individual/GroupedPropertyList.java | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
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 00fd7e13b..cedb9bde0 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
@@ -108,9 +108,9 @@ public class GroupedPropertyList extends BaseTemplateModel {
if (editing) {
mergeAllPossibleDataProperties(propertyList);
- propertyList = correctLanguageForProperties(propertyList);
}
+ propertyList = correctLanguageForProperties(propertyList);
sort(propertyList);
// Put the list into groups
From cd28028fb3f696add28431f16fed0c8e6a18c487 Mon Sep 17 00:00:00 2001
From: brianjlowe
Date: Tue, 15 Oct 2013 15:48:25 -0400
Subject: [PATCH 8/9] VIVO-366 change to annotation-to-PropertyConfig converter
to deal with multiple values
---
.../ontology/update/KnowledgeBaseUpdater.java | 24 ++--
.../webapp/ontology/update/TBoxUpdater.java | 123 +++++++++---------
2 files changed, 70 insertions(+), 77 deletions(-)
diff --git a/webapp/src/edu/cornell/mannlib/vitro/webapp/ontology/update/KnowledgeBaseUpdater.java b/webapp/src/edu/cornell/mannlib/vitro/webapp/ontology/update/KnowledgeBaseUpdater.java
index b4f211c97..90707edca 100644
--- a/webapp/src/edu/cornell/mannlib/vitro/webapp/ontology/update/KnowledgeBaseUpdater.java
+++ b/webapp/src/edu/cornell/mannlib/vitro/webapp/ontology/update/KnowledgeBaseUpdater.java
@@ -93,18 +93,6 @@ public class KnowledgeBaseUpdater {
List rawChanges = getAtomicOntologyChanges();
AtomicOntologyChangeLists changes = new AtomicOntologyChangeLists(rawChanges,settings.getNewTBoxModel(),settings.getOldTBoxModel());
-
- // Only modify the TBox and migration metadata the first time
- if(updateRequired(servletContext)) {
- //process the TBox before the ABox
- try {
- log.debug("\tupdating tbox annotations");
- updateTBoxAnnotations();
- } catch (Exception e) {
- log.error(e,e);
- }
-
- }
// update ABox data any time
log.info("performing SPARQL CONSTRUCT additions");
@@ -124,6 +112,18 @@ public class KnowledgeBaseUpdater {
performSparqlConstructs(settings.getSparqlConstructDeletionsDir() + "/post/",
settings.getRDFService(), RETRACT);
+
+ // Only modify the TBox and migration metadata the first time
+ if(updateRequired(servletContext)) {
+ //process the TBox before the ABox
+ try {
+ log.debug("\tupdating tbox annotations");
+ updateTBoxAnnotations();
+ } catch (Exception e) {
+ log.error(e,e);
+ }
+
+ }
}
diff --git a/webapp/src/edu/cornell/mannlib/vitro/webapp/ontology/update/TBoxUpdater.java b/webapp/src/edu/cornell/mannlib/vitro/webapp/ontology/update/TBoxUpdater.java
index cf81d4092..9c37e1544 100644
--- a/webapp/src/edu/cornell/mannlib/vitro/webapp/ontology/update/TBoxUpdater.java
+++ b/webapp/src/edu/cornell/mannlib/vitro/webapp/ontology/update/TBoxUpdater.java
@@ -450,90 +450,69 @@ public class TBoxUpdater {
}
public void renameProperty(AtomicOntologyChange changeObj) throws IOException {
- Dataset dataset = new RDFServiceDataset(settings.getRDFService());
- Model userAnnotationsModel = dataset.getNamedModel(
- JenaDataSourceSetupBase.JENA_TBOX_ASSERTIONS_MODEL);
if(changeObj.getNotes() != null && changeObj.getNotes().startsWith("cc:")) {
- mergePropertyAnnotationsToPropertyConfig(changeObj, userAnnotationsModel);
+ mergePropertyAnnotationsToPropertyConfig(changeObj, siteModel);
}
- Resource renamedProperty = userAnnotationsModel.getResource(changeObj.getSourceURI());
- userAnnotationsModel.removeAll(renamedProperty, null, (RDFNode) null);
- userAnnotationsModel.removeAll(null, null, renamedProperty);
+ Resource renamedProperty = siteModel.getResource(changeObj.getSourceURI());
+ siteModel.removeAll(renamedProperty, null, (RDFNode) null);
+ siteModel.removeAll(null, null, renamedProperty);
}
private void mergePropertyAnnotationsToPropertyConfig(AtomicOntologyChange changeObj,
Model userAnnotationsModel) throws IOException {
String contextURI = VitroVocabulary.PROPERTY_CONFIG_DATA + changeObj.getNotes().substring(3);
String oldPropertyURI = changeObj.getSourceURI();
-
Model oldAnnotationsModel = settings.getOldTBoxAnnotationsModel();
String propertyAnnotationsQuery =
"PREFIX config: <" + VitroVocabulary.configURI + "> \n" +
"PREFIX vitro: <" + VitroVocabulary.vitroURI + "> \n" +
"CONSTRUCT { \n" +
- " <" + oldPropertyURI + "> vitro:inPropertyGroupAnnot ?group . \n" +
" <" + oldPropertyURI + "> <" + RDFS.label.getURI() + "> ?label . \n" +
- " <" + oldPropertyURI + "> vitro:displayRankAnnot ?displayRank . \n" +
- " <" + oldPropertyURI + "> vitro:customEntryFormAnnot ?customForm . \n" +
- " <" + oldPropertyURI + "> vitro:hiddenFromDisplayBelowRoleLevelAnnot ?displayLevel . \n" +
- " <" + oldPropertyURI + "> vitro:prohibitedFromUpdateBelowRoleLevelAnnot ?updateLevel . \n " +
+ " <" + oldPropertyURI + "> ?vitroProp ?vitroValue \n" +
"} WHERE { \n" +
- " { <" + oldPropertyURI + "> vitro:inPropertyGroupAnnot ?group } \n" +
- " UNION { <" + oldPropertyURI + "> <" + RDFS.label.getURI() + "> ?label } \n" +
- " UNION { <" + oldPropertyURI + "> vitro:displayRankAnnot ?displayRank } \n" +
- " UNION { <" + oldPropertyURI + "> vitro:customEntryFormAnnot ?customForm } \n" +
- " UNION { <" + oldPropertyURI + "> vitro:hiddenFromDisplayBelowRoleLevelAnnot ?displayLevel } \n" +
- " UNION { <" + oldPropertyURI + "> vitro:prohibitedFromUpdateBelowRoleLevelAnnot ?updateLevel } \n " +
+ " { <" + oldPropertyURI + "> <" + RDFS.label.getURI() + "> ?label } \n" +
+ " UNION { <" + oldPropertyURI + "> ?vitroProp ?vitroValue \n" +
+ " FILTER (regex(str(?vitroProp), \"" + VitroVocabulary.vitroURI + "\")) } \n" +
"} \n" ;
Model userChangesModel = construct(
propertyAnnotationsQuery, userAnnotationsModel).difference(
construct(propertyAnnotationsQuery, oldAnnotationsModel));
- String addQuery = "PREFIX config: <" + VitroVocabulary.configURI + "> \n" +
- "PREFIX vitro: <" + VitroVocabulary.vitroURI + "> \n" +
- "CONSTRUCT { \n" +
- " ?configuration config:propertyGroup ?group . \n" +
- " ?configuration config:displayName ?label . \n" +
- " ?configuration vitro:displayRankAnnot ?displayRank . \n" +
- " ?configuration vitro:customEntryFormAnnot ?customForm . \n" +
- " ?configuration vitro:hiddenFromDisplayBelowRoleLevelAnnot ?displayLevel . \n" +
- " ?configuration vitro:prohibitedFromUpdateBelowRoleLevelAnnot ?updateLevel . \n " +
- "} WHERE { \n" +
- " <" + contextURI + "> config:hasConfiguration ?configuration . \n" +
- " OPTIONAL { <" + oldPropertyURI + "> vitro:inPropertyGroupAnnot ?group } \n" +
- " OPTIONAL { <" + oldPropertyURI + "> <" + RDFS.label.getURI() + "> ?label } \n" +
- " OPTIONAL { <" + oldPropertyURI + "> vitro:displayRankAnnot ?displayRank } \n" +
- " OPTIONAL { <" + oldPropertyURI + "> vitro:customEntryFormAnnot ?customForm } \n" +
- " OPTIONAL { <" + oldPropertyURI + "> vitro:hiddenFromDisplayBelowRoleLevelAnnot ?displayLevel } \n" +
- " OPTIONAL { <" + oldPropertyURI + "> vitro:prohibitedFromUpdateBelowRoleLevelAnnot ?updateLevel } \n " +
- "} \n" ;
+ if(userChangesModel.size() == 0) {
+ return;
+ } else {
+ log.info("Updating PropertyConfig.n3 to include locally-changed " +
+ "settings from old property " + oldPropertyURI);
+ }
- String retractQuery = "PREFIX config: <" + VitroVocabulary.configURI + "> \n" +
+ String newQuery = "PREFIX config: <" + VitroVocabulary.configURI + "> \n" +
"PREFIX vitro: <" + VitroVocabulary.vitroURI + "> \n" +
"CONSTRUCT { \n" +
- " <" + oldPropertyURI + "> config:propertyGroup ?rgroup . \n" +
- " ?configuration config:displayName ?rlabel . \n" +
- " ?configuration vitro:displayRankAnnot ?rdisplayRank . \n" +
- " ?configuration vitro:customEntryFormAnnot ?rcustomForm . \n" +
- " ?configuration vitro:hiddenFromDisplayBelowRoleLevelAnnot ?rdisplayLevel . \n" +
- " ?configuration vitro:prohibitedFromUpdateBelowRoleLevelAnnot ?rupdateLevel . \n " +
+ " ?configuration config:propertyGroup ?group . \n" +
+ " ?configuration config:displayName ?label . \n" +
+ " ?configuration ?vitroProp ?vitroValue . \n" +
"} WHERE { \n" +
" <" + contextURI + "> config:hasConfiguration ?configuration . \n" +
- " OPTIONAL { <" + oldPropertyURI + "> vitro:inPropertyGroupAnnot ?group . \n" +
- " ?configuration config:propertyGroup ?rgroup } \n" +
- " OPTIONAL { <" + oldPropertyURI + "> <" + RDFS.label.getURI() + "> ?label . \n" +
- " ?configuration config:displayName ?rlabel . \n " +
- " FILTER(?rlabel != ?label) } \n " +
- " OPTIONAL { <" + oldPropertyURI + "> vitro:displayRankAnnot ?displayRank . \n" +
- " ?configuration vitro:displayRantAnnot ?rdisplayRank } \n " +
- " OPTIONAL { <" + oldPropertyURI + "> vitro:customEntryFormAnnot ?customForm . \n" +
- " ?configuration vitro:customEntryFormAnnot ?rcustomForm } \n" +
- " OPTIONAL { <" + oldPropertyURI + "> vitro:hiddenFromDisplayBelowRoleLevelAnnot ?displayLevel . \n" +
- " ?configuration vitro:hiddenFromDisplayBelowRoleLevelAnnot ?rdisplayLevel } \n" +
- " OPTIONAL { <" + oldPropertyURI + "> vitro:prohibitedFromUpdateBelowRoleLevelAnnot ?updateLevel . \n " +
- " ?configuration vitro:prohibitedFromUpdateBelowRoleLevelAnnot ?updateLevel } " +
+ " OPTIONAL { <" + oldPropertyURI + "> vitro:inPropertyGroupAnnot ?group } \n" +
+ " OPTIONAL { <" + oldPropertyURI + "> <" + RDFS.label.getURI() + "> ?label } \n" +
+ " OPTIONAL { <" + oldPropertyURI + "> ?vitroProp ?vitroValue \n" +
+ " FILTER (regex(str(?vitroProp), \"" + VitroVocabulary.vitroURI + "\")) } \n" +
+ "} \n" ;
+
+ String existingQuery = "PREFIX config: <" + VitroVocabulary.configURI + "> \n" +
+ "PREFIX vitro: <" + VitroVocabulary.vitroURI + "> \n" +
+ "CONSTRUCT { \n" +
+ " ?configuration config:propertyGroup ?group . \n" +
+ " ?configuration config:displayName ?label . \n" +
+ " ?configuration ?vitroProp ?vitroValue . \n" +
+ "} WHERE { \n" +
+ " <" + contextURI + "> config:hasConfiguration ?configuration . \n" +
+ " OPTIONAL { ?configuration config:propertyGroup ?group } \n" +
+ " OPTIONAL { ?configuration config:displayName ?label } \n" +
+ " OPTIONAL { ?configuration ?vitroProp ?vitroValue \n" +
+ " FILTER (regex(str(?vitroProp), \"" + VitroVocabulary.vitroURI + "\")) } \n" +
"} \n" ;
Model configModel = ModelFactory.createDefaultModel();
@@ -542,17 +521,31 @@ public class TBoxUpdater {
FileInputStream fis = new FileInputStream(file);
configModel.read(fis, null, "N3");
- Model union = ModelFactory.createUnion(configModel,
- userChangesModel);
+ Model currentUnion = ModelFactory.createUnion(configModel,
+ userAnnotationsModel);
- Model additions = construct(addQuery, union);
- Model retractions = construct(retractQuery, union);
+ Model userAnnotationsAsConfig = construct(newQuery, currentUnion);
+ Model currentDefaultConfig = construct(existingQuery, currentUnion);
- if (additions.size() > 0 || retractions.size() > 0) {
- configModel.remove(retractions);
- log.info("Removing " + retractions.size() + " statements from " + contextURI);
+ Model additions = userAnnotationsAsConfig.difference(currentDefaultConfig);
+ Model retractions = currentDefaultConfig.difference(userAnnotationsAsConfig);
+
+ // filter the retractions so we won't remove a value for a given predicate
+ // unless the additions model contains at least one value for the same predicate
+ Model filteredRetractions = ModelFactory.createDefaultModel();
+ StmtIterator retractIt = retractions.listStatements();
+ while(retractIt.hasNext()) {
+ Statement candidate = retractIt.nextStatement();
+ if(additions.contains(null, candidate.getPredicate(), (RDFNode) null)) {
+ filteredRetractions.add(candidate);
+ }
+ }
+
+ if (additions.size() > 0 || filteredRetractions.size() > 0) {
+ configModel.remove(filteredRetractions);
+ log.debug("Removing " + filteredRetractions.size() + " statements from " + contextURI);
configModel.add(additions);
- log.info("Adding " + additions.size() + " statements from " + contextURI);
+ log.debug("Adding " + additions.size() + " statements from " + contextURI);
FileOutputStream fos = new FileOutputStream(file);
configModel.write(fos, "N3");
}
From 5aa1d10028817ad1af627d40cd563bb20866a135 Mon Sep 17 00:00:00 2001
From: tworrall
Date: Tue, 15 Oct 2013 16:04:14 -0400
Subject: [PATCH 9/9] VIVO-367
---
webapp/web/css/individual/individual.css | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/webapp/web/css/individual/individual.css b/webapp/web/css/individual/individual.css
index 2725db1c7..92d24ec93 100644
--- a/webapp/web/css/individual/individual.css
+++ b/webapp/web/css/individual/individual.css
@@ -106,7 +106,8 @@ h1.fn .display-title {
margin-left: 10px;
}
#individual-info h2 {
- padding-bottom: 10px;
+ padding-bottom: 6px;
+ padding-top: 18px;
}
#individual-info nav {
float: left;