diff --git a/opensocial/shindig.orng.properties b/opensocial/shindig.orng.properties new file mode 100644 index 000000000..366c26c5e --- /dev/null +++ b/opensocial/shindig.orng.properties @@ -0,0 +1,190 @@ +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. + +################################################################## +# +# Note from UCSF. Compare this to the latest in shindig-common/conf/shindig.properties +# whenever you download a new version of shindig +# +################################################################## + +# Location of feature manifests (comma separated) +shindig.features.default=res://features/features.txt + +# Location of container configurations (comma separated) +#shindig.containers.default=res://containers/default/container.js +shindig.containers.default=res://orng-container.js + +# A file containing blacklisted gadgets. +shindig.blacklist.file= + +### Inbound OAuth support +# The URL base to use for full OAuth support (three-legged) +shindig.oauth.base-url=/oauth/ +shindig.oauth.authorize-action=/WEB-INF/authorize.jsp + +### Outbound OAuth support +shindig.signing.state-key= +shindig.signing.key-name= +shindig.signing.key-file= +shindig.signing.global-callback-url=http://localhost:8080/shindigorng/gadgets/oauthcallback +shindig.signing.enable-signed-callbacks=true + +# Set to true if you want to allow the use of 3-legged OAuth tokens when viewer != owner. +# This setting is not recommeneded for pages that allow user-controlled javascript, since +# that javascript could be used to make unauthorized requests on behalf of the viewer of the page +shindig.signing.viewer-access-tokens-enabled=false + +# If enabled here, configuration values can be found in container configuration files. +shindig.locked-domain.enabled=false + +# TODO: This needs to be moved to container configuration. +# Note by Eric. This is set up to now exclude everything and include nothing +shindig.content-rewrite.only-allow-excludes=false +shindig.content-rewrite.include-urls=.* +shindig.content-rewrite.exclude-urls= +shindig.content-rewrite.include-tags=body,embed,img,input,link,script,style +shindig.content-rewrite.expires=86400 +shindig.content-rewrite.proxy-url=/shindigorng/gadgets/proxy?container=default&url= +shindig.content-rewrite.concat-url=/shindigorng/gadgets/concat?container=default& +shindig.content-rewrite.enable-split-js-concat=false + +# +# Default set of forced libs to allow for better caching +# +# NOTE: setting this causes the EndToEnd test to fail the opensocial-templates test +shindig.gadget-rewrite.default-forced-libs=core:rpc +shindig.gadget-rewrite.default-forced-libs= + +# +# Allow supported JavaScript features required by a gadget to be externalized on demand +shindig.gadget-rewrite.externalize-feature-libs=true + +# Configuration for image rewriter +shindig.image-rewrite.max-inmem-bytes = 1048576 +shindig.image-rewrite.max-palette-size = 256 +shindig.image-rewrite.allow-jpeg-conversion = true +shindig.image-rewrite.jpeg-compression = 0.75 +shindig.image-rewrite.min-threshold-bytes = 200 + +# Configuration for the os:Flash tag +shindig.flash.min-version = 9.0.115 + +# Configuration for template rewriter +shindig.template-rewrite.extension-tag-namespace=http://ns.opensocial.org/2009/extensions + +# These values provide default TTLs for HTTP responses that don't use caching headers. +shindig.cache.http.defaultTtl=3600000 +shindig.cache.http.negativeCacheTtl=60000 + +# A default refresh interval for XML files, since there is no natural way for developers to +# specify this value, and most HTTP responses don't include good cache control headers. +shindig.cache.xml.refreshInterval=300000 + +# Add entries in the form shindig.cache.lru..capacity to specify capacities for different +# caches when using the LruCacheProvider. +# It is highly recommended that the EhCache implementation be used instead of the LRU cache. +shindig.cache.lru.default.capacity=1000 +shindig.cache.lru.expressions.capacity=1000 +shindig.cache.lru.gadgetSpecs.capacity=1000 +shindig.cache.lru.messageBundles.capacity=1000 +shindig.cache.lru.httpResponses.capacity=10000 + +# The location of the EhCache configuration file. +shindig.cache.ehcache.config=res://org/apache/shindig/common/cache/ehcache/ehcacheConfig.xml + +# True to enable JMX integration with cache stats +shindig.cache.ehcache.jmx.enabled=true + +# true to enable JMX stats. +shindig.cache.ehcache.jmx.stats=true + +# true to skip expensive encoding detection. +# if true, will only attempt to validate utf-8. Assumes all other encodings are ISO-8859-1. +shindig.http.fast-encoding-detection=true + +# Configuration for the HttpFetcher +# Connection timeout, in milliseconds, for requests. +shindig.http.client.connection-timeout-ms=5000 + +# Maximum size, in bytes, of the object we fetched, 0 == no limit +shindig.http.client.max-object-size-bytes=0 + +# Strict-mode parsing for proxy and concat URIs ensures that the authority/host and path +# for the URIs match precisely what is found in the container config for it. This is +# useful where statistics and traffic routing patterns, typically in large installations, +# key on hostname (and occasionally path). Enforcing this does come at the cost that +# mismatches break, which in turn mandates that URI generation always happen in consistent +# fashion, ie. by the class itself or tightly controlled code. +shindig.uri.proxy.use-strict-parsing=false +shindig.uri.concat.use-strict-parsing=false + +# Host:port of the proxy to use while fetching urls. Leave blank if proxy is +# not to be used. +org.apache.shindig.gadgets.http.basicHttpFetcherProxy= + +org.apache.shindig.serviceExpirationDurationMinutes=60 + +# +# Older versions of shindig used 'data' in the json-rpc response format +# The spec calls for using 'result' instead, however to avoid breakage we +# allow you to set it back to the old way here +# +# valid values are +# result - new form +# data - old broken form +# both - return both fields for full compatibility +# +shindig.json-rpc.result-field=result + +# Remap "Internal server error"s received from the basicHttpFetcherProxy server to +# "Bad Gateway error"s, so that it is clear to the user that the proxy server is +# the one that threw the exception. +shindig.accelerate.remapInternalServerError=true +shindig.proxy.remapInternalServerError=true + +shindig.signing.key-file=/shindig/openssl/oauthkey.pem +shindig.signing.key-name= + +#################################################################################### +# +# Open Research Networking Gadgets Items +# +##################################################################################### + +# orng.system must be set to Profiles or VIVO +#orng.system = Profiles +orng.system = VIVO + +# orng.dbDriver is likely com.microsoft.sqlserver.jdbc.SQLServerDriver for Profiles and com.mysql.jdbc.Driver for VIVO +#orng.dbDriver = com.microsoft.sqlserver.jdbc.SQLServerDriver +orng.dbDriver = com.mysql.jdbc.Driver +orng.dbURL = jdbc:mysql://localhost/vitrodb +orng.dbUser = vitrodb +orng.dbPassword = vitrodb +orng.tokenservice.port = 8777 +# orng.RDFConverter = elda | babel +orng.RDFConverter = elda +#orng.RDFConverter = babel + +# until Profiles has RDF +orng.profilesXMLService = http://dev-profiles.ucsf.edu/api_100810/ProfileService.svc/ProfileSearch +orng.profilesRDF = true; + + + + diff --git a/opensocial/shindig_example_gadgets.sql b/opensocial/shindig_example_gadgets.sql new file mode 100644 index 000000000..51d404de6 --- /dev/null +++ b/opensocial/shindig_example_gadgets.sql @@ -0,0 +1,28 @@ + +-- Add some gadgets to play with ------------------------ +-- + +INSERT INTO `shindig_apps` (`appid`, `name`, `url`, `PersonFilterID`, `enabled`, `channels`) VALUES +(100, 'Google Search', 'http://dev-profiles.ucsf.edu/apps/GoogleSearch.xml', NULL, 1, NULL), +(101, 'Featured Presentations', 'http://dev-profiles.ucsf.edu/apps/SlideShare.xml', NULL, 1, NULL), +(102, 'Faculty Mentor', 'http://dev-profiles.ucsf.edu/apps/Mentor.xml', NULL, 1, NULL), +(103, 'Websites', 'http://dev-profiles.ucsf.edu/apps/Links.xml', NULL, 1, NULL), +(104, 'Profile List', 'http://dev-profiles.ucsf.edu/apps/ProfileListTool.xml', NULL, 1, 'JSONPersonIds'), +(105, 'Publication Export', 'http://dev-profiles.ucsf.edu/apps/PubExportTool.xml', NULL, 1, 'JSONPubMedIds'), +(106, 'RDF Test Gadget', 'http://dev-profiles.ucsf.edu/gadgets/RDFTest.xml', NULL, 1, NULL); + +INSERT INTO `shindig_app_views` (`appid`, `viewer_req`, `owner_req`, `page`, `view`, `closed_width`, `open_width`, `start_closed`, `chromeId`, `display_order`) VALUES +(100, NULL, NULL, 'search', NULL, 600, 600, 1, 'gadgets-search', NULL), +(101, NULL, 'R', 'individual', 'profile', 291, 590, 1, 'gadgets-view', 3), +(101, NULL, NULL, 'individual-EDIT-MODE', 'home', 700, 700, 1, 'gadgets-edit', NULL), +(102, NULL, 'R', 'individual', 'profile', 291, 590, 1, 'gadgets-view', 2), +(102, NULL, NULL, 'individual-EDIT-MODE', 'home', 700, 700, 1, 'gadgets-edit', NULL), +(103, NULL, NULL, 'individual-EDIT-MODE', 'home', 700, 700, 1, 'gadgets-edit', NULL), +(103, NULL, 'R', 'individual', 'profile', 291, 590, 0, 'gadgets-view', 1), +(104, 'U', NULL, 'search', 'small', 160, 160, 0, 'gadgets-tools', NULL), +(104, 'U', NULL, 'gadgetDetails', 'canvas', 700, 700, 0, 'gadgets-detail', NULL), +(104, 'U', NULL, 'SimilarPeople.aspx', 'small', 160, 160, 0, 'gadgets-tools', NULL), +(104, 'U', NULL, 'individual', 'small', 160, 160, 0, 'gadgets-view', NULL), +(104, 'U', NULL, 'CoAuthors.aspx', 'small', 160, 160, 0, 'gadgets-tools', NULL), +(105, 'U', NULL, 'individual', 'small', 160, 160, 0, 'gadgets-view', NULL), +(105, 'U', NULL, 'gadgetDetails', 'canvas', 700, 700, 0, 'gadgets-detail', NULL); diff --git a/opensocial/shindig_orng_tables.sql b/opensocial/shindig_orng_tables.sql new file mode 100644 index 000000000..ed52ea340 --- /dev/null +++ b/opensocial/shindig_orng_tables.sql @@ -0,0 +1,145 @@ + +-- +-- Table structure for table `shindig_activity` +-- + +CREATE TABLE IF NOT EXISTS `shindig_activity` ( + `activityId` int(11) NOT NULL AUTO_INCREMENT, + `userId` varchar(255) default NULL, + `appId` int(11) default NULL, + `createdDT` datetime default NULL, + `activity` text, + PRIMARY KEY (`activityId`) +) ENGINE=MyISAM DEFAULT CHARSET=latin1; + +-- -------------------------------------------------------- + +-- +-- Table structure for table `shindig_appdata` +-- + +CREATE TABLE IF NOT EXISTS `shindig_appdata` ( + `userId` varchar(255) NOT NULL, + `appId` int(11) NOT NULL, + `keyname` varchar(255) NOT NULL, + `value` varchar(4000) default NULL, + `createdDT` datetime default NULL, + `updatedDT` datetime default NULL, + KEY `userId` (`userId`,`appId`) +) ENGINE=MyISAM DEFAULT CHARSET=latin1; + +-- -------------------------------------------------------- + +-- +-- Table structure for table `shindig_apps` +-- + +CREATE TABLE IF NOT EXISTS `shindig_apps` ( + `appid` int(11) NOT NULL, + `name` varchar(255) NOT NULL, + `url` varchar(255) NOT NULL, + `PersonFilterID` int(11) default NULL, + `enabled` tinyint(1) NOT NULL default '1', + `channels` varchar(255) default NULL, + PRIMARY KEY (`appid`) +) ENGINE=MyISAM DEFAULT CHARSET=latin1; + +-- -------------------------------------------------------- + +-- +-- Table structure for table `shindig_app_registry` +-- + +CREATE TABLE IF NOT EXISTS `shindig_app_registry` ( + `appid` int(11) NOT NULL, + `personId` varchar(255) NOT NULL, + `createdDT` datetime NOT NULL, + PRIMARY KEY (`appid`) +) ENGINE=MyISAM DEFAULT CHARSET=latin1; + +-- -------------------------------------------------------- + +-- +-- Table structure for table `shindig_app_views` +-- + +CREATE TABLE IF NOT EXISTS `shindig_app_views` ( + `appid` int(11) NOT NULL, + `viewer_req` char(1) default NULL, + `owner_req` char(1) default NULL, + `page` varchar(50) default NULL, + `view` varchar(50) default NULL, + `closed_width` int(11) default NULL, + `open_width` int(11) default NULL, + `start_closed` tinyint(1) default NULL, + `chromeId` varchar(50) default NULL, + `display_order` int(11) default NULL +) ENGINE=MyISAM DEFAULT CHARSET=latin1; + +-- -------------------------------------------------------- + +-- +-- Table structure for table `shindig_messages` +-- + +CREATE TABLE IF NOT EXISTS `shindig_messages` ( + `msgId` varchar(255) NOT NULL, + `senderId` varchar(255) default NULL, + `recipientId` varchar(255) default NULL, + `coll` varchar(255) default NULL, + `title` varchar(255) default NULL, + `body` varchar(4000) default NULL, + `createdDT` datetime default NULL, + PRIMARY KEY (`msgId`) +) ENGINE=MyISAM DEFAULT CHARSET=latin1; + +-- -------------------------------------------------------- + + +DELIMITER // +CREATE PROCEDURE shindig_registerAppPerson (uid varchar(255), aid INT, v BOOL) +BEGIN + IF (v) + THEN + INSERT INTO shindig_app_registry (appId, personId, createdDT) values (aid, uid, now()); + ELSE + DELETE FROM shindig_app_registry where appId = aid AND personId = uid; + END IF; +END // +DELIMITER ; + +DELIMITER // +CREATE PROCEDURE shindig_upsertAppData(uid varchar(255), aid INT, kn varchar(255),v varchar(4000)) +BEGIN + DECLARE cnt int; + SELECT count(*) FROM shindig_appdata WHERE userId = uid AND appId = aid and keyname = kn INTO cnt; + IF (cnt > 0) + THEN + UPDATE shindig_appdata set `value` = v, updatedDT = NOW() WHERE userId = uid AND appId = aid and keyname = kn; + ELSE + INSERT INTO shindig_appdata (userId, appId, keyname, `value`) values (uid, aid, kn, v); + END IF; + -- if keyname is VISIBLE, do more + IF (kn = 'VISIBLE' AND v = 'Y') + THEN + CALL shindig_registerAppPerson(uid, aid, 1); + ELSEIF (kn = 'VISIBLE' ) + THEN + CALL shindig_registerAppPerson(uid, aid, 0); + END IF; +END // +DELIMITER ; + +DELIMITER // +CREATE PROCEDURE shindig_deleteAppData(uid varchar(255),aid INT, kn varchar(255)) +BEGIN + DELETE FROM shindig_appdata WHERE userId = uid AND appId = aid and keyname = kn; + -- if keyname is VISIBLE, do more + IF (kn = 'VISIBLE' ) + THEN + CALL shindig_registerAppPerson(uid, aid, 0); + END IF; +END // +DELIMITER ; + + diff --git a/opensocial/shindigorng.war b/opensocial/shindigorng.war new file mode 100644 index 000000000..fa845ad02 Binary files /dev/null and b/opensocial/shindigorng.war differ diff --git a/webapp/config/licenser/known_exceptions.txt b/webapp/config/licenser/known_exceptions.txt index 665d8a46d..2551ba6f7 100644 --- a/webapp/config/licenser/known_exceptions.txt +++ b/webapp/config/licenser/known_exceptions.txt @@ -38,6 +38,7 @@ webapp/web/js/tiny_mce/**/* webapp/web/js/jquery.js webapp/web/js/jquery-ui/* webapp/web/js/jquery_plugins/* +webapp/web/js/jquery.fix.clone.js # See /doc/3rd-party-licenses.txt for LICENSE file webapp/web/dojo.js @@ -173,3 +174,14 @@ webapp/src/edu/cornell/mannlib/vitro/webapp/web/antisamy-vitro-1.4.4.xml # A kluge class derived from JarJar code. See /doc/3rd-party-licenses.txt for LICENSE file utilities/buildutils/src/com/tonicsystems/jarjar/KlugedDepFind.java + +#Public Domain +webapp/web/js/json2.js + +# Part of the OpenSocial integration - What license should apply here? +webapp/src/edu/ucsf/vitro/opensocial/GadgetController.java +webapp/src/edu/ucsf/vitro/opensocial/GadgetSpec.java +webapp/src/edu/ucsf/vitro/opensocial/GadgetViewRequirements.java +webapp/src/edu/ucsf/vitro/opensocial/OpenSocialManager.java +webapp/src/edu/ucsf/vitro/opensocial/PreparedGadget.java +webapp/web/js/openSocial/shindig.js diff --git a/webapp/src/edu/cornell/mannlib/vitro/webapp/controller/edit/DatapropEditController.java b/webapp/src/edu/cornell/mannlib/vitro/webapp/controller/edit/DatapropEditController.java index 8f2f8a172..8b2a9e45b 100644 --- a/webapp/src/edu/cornell/mannlib/vitro/webapp/controller/edit/DatapropEditController.java +++ b/webapp/src/edu/cornell/mannlib/vitro/webapp/controller/edit/DatapropEditController.java @@ -1,44 +1,44 @@ -/* $This file is distributed under the terms of the license in /doc/license.txt$ */ - +/* $This file is distributed under the terms of the license in /doc/license.txt$ */ + package edu.cornell.mannlib.vitro.webapp.controller.edit; - -import java.io.UnsupportedEncodingException; -import java.net.URLEncoder; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.Iterator; -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.controller.BaseEditController; -import edu.cornell.mannlib.vitro.webapp.auth.permissions.SimplePermission; -import edu.cornell.mannlib.vitro.webapp.beans.ApplicationBean; -import edu.cornell.mannlib.vitro.webapp.beans.DataProperty; -import edu.cornell.mannlib.vitro.webapp.beans.Ontology; -import edu.cornell.mannlib.vitro.webapp.beans.PropertyGroup; -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.PropertyGroupDao; + +import java.io.UnsupportedEncodingException; +import java.net.URLEncoder; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.Iterator; +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.controller.BaseEditController; +import edu.cornell.mannlib.vitro.webapp.auth.permissions.SimplePermission; +import edu.cornell.mannlib.vitro.webapp.beans.ApplicationBean; +import edu.cornell.mannlib.vitro.webapp.beans.DataProperty; +import edu.cornell.mannlib.vitro.webapp.beans.Ontology; +import edu.cornell.mannlib.vitro.webapp.beans.PropertyGroup; +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.PropertyGroupDao; public class DatapropEditController extends BaseEditController { private static final Log log = LogFactory.getLog(DatapropEditController.class.getName()); - public void doPost (HttpServletRequest request, HttpServletResponse response) { - if (!isAuthorizedToDisplayPage(request, response, SimplePermission.EDIT_ONTOLOGY.ACTIONS)) { - return; - } - - VitroRequest vreq = new VitroRequest(request); + public void doPost (HttpServletRequest request, HttpServletResponse response) { + if (!isAuthorizedToDisplayPage(request, response, SimplePermission.EDIT_ONTOLOGY.ACTIONS)) { + return; + } + + VitroRequest vreq = new VitroRequest(request); final int NUM_COLS=15; @@ -56,7 +56,7 @@ public class DatapropEditController extends BaseEditController { results.add("range datatype"); results.add("group"); results.add("display tier"); - results.add("display limit"); + results.add("data entry limit"); results.add("example"); results.add("description"); results.add("public description"); @@ -169,8 +169,8 @@ public class DatapropEditController extends BaseEditController { } } request.setAttribute("equivalentProperties", eqProperties); - - ApplicationBean appBean = vreq.getAppBean(); + + ApplicationBean appBean = vreq.getAppBean(); request.setAttribute("epoKey",epo.getKey()); request.setAttribute("datatypeProperty", dp); diff --git a/webapp/src/edu/cornell/mannlib/vitro/webapp/controller/freemarker/ListDatatypePropertiesController.java b/webapp/src/edu/cornell/mannlib/vitro/webapp/controller/freemarker/ListDatatypePropertiesController.java new file mode 100644 index 000000000..6e7bb1dc2 --- /dev/null +++ b/webapp/src/edu/cornell/mannlib/vitro/webapp/controller/freemarker/ListDatatypePropertiesController.java @@ -0,0 +1,160 @@ +/* $This file is distributed under the terms of the license in /doc/license.txt$ */ + +package edu.cornell.mannlib.vitro.webapp.controller.freemarker; + +import java.io.UnsupportedEncodingException; +import java.net.URLEncoder; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Iterator; +import java.util.List; +import java.util.Map; + +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.vitro.webapp.auth.permissions.SimplePermission; +import edu.cornell.mannlib.vitro.webapp.auth.requestedAction.Actions; +import edu.cornell.mannlib.vitro.webapp.beans.DataProperty; +import edu.cornell.mannlib.vitro.webapp.beans.Datatype; +import edu.cornell.mannlib.vitro.webapp.beans.PropertyGroup; +import edu.cornell.mannlib.vitro.webapp.beans.VClass; +import edu.cornell.mannlib.vitro.webapp.controller.Controllers; +import edu.cornell.mannlib.vitro.webapp.controller.VitroRequest; +import edu.cornell.mannlib.vitro.webapp.controller.freemarker.responsevalues.ResponseValues; +import edu.cornell.mannlib.vitro.webapp.controller.freemarker.responsevalues.TemplateResponseValues; +import edu.cornell.mannlib.vitro.webapp.dao.DataPropertyDao; +import edu.cornell.mannlib.vitro.webapp.dao.DatatypeDao; +import edu.cornell.mannlib.vitro.webapp.dao.PropertyGroupDao; +import edu.cornell.mannlib.vitro.webapp.dao.VClassDao; +import edu.cornell.mannlib.vitro.webapp.dao.VitroVocabulary; + +public class ListDatatypePropertiesController extends FreemarkerHttpServlet { + + private static Log log = LogFactory.getLog( ListDatatypePropertiesController.class ); + + private static final String TEMPLATE_NAME = "siteAdmin-objectPropHierarchy.ftl"; + + @Override + protected Actions requiredActions(VitroRequest vreq) { + return SimplePermission.EDIT_ONTOLOGY.ACTIONS; + } + + @Override + protected ResponseValues processRequest(VitroRequest vreq) { + + Map body = new HashMap(); + try { + + body.put("displayOption", "all"); + body.put("pageTitle", "All Data Properties"); + body.put("propertyType", "data"); + + String noResultsMsgStr = "No data properties found"; + + String ontologyUri = vreq.getParameter("ontologyUri"); + + DataPropertyDao dao = vreq.getFullWebappDaoFactory().getDataPropertyDao(); + VClassDao vcDao = vreq.getFullWebappDaoFactory().getVClassDao(); + DatatypeDao dDao = vreq.getFullWebappDaoFactory().getDatatypeDao(); + PropertyGroupDao pgDao = vreq.getFullWebappDaoFactory().getPropertyGroupDao(); + + List props = new ArrayList(); + if (vreq.getParameter("propsForClass") != null) { + noResultsMsgStr = "There are no data properties that apply to this class."; + Collection dataProps = dao.getDataPropertiesForVClass(vreq.getParameter("vclassUri")); + Iterator dataPropIt = dataProps.iterator(); + HashSet propURIs = new HashSet(); + while (dataPropIt.hasNext()) { + DataProperty dp = dataPropIt.next(); + if (!(propURIs.contains(dp.getURI()))) { + propURIs.add(dp.getURI()); + DataProperty prop = dao.getDataPropertyByURI(dp.getURI()); + if (prop != null) { + props.add(prop); + } + } + } + } else { + props = dao.getAllDataProperties(); + } + + if (ontologyUri != null) { + List scratch = new ArrayList(); + for (DataProperty p: props) { + if (p.getNamespace().equals(ontologyUri)) { + scratch.add(p); + } + } + props = scratch; + } + + if (props != null) { + Collections.sort(props); + } + + String json = new String(); + int counter = 0; + + if (props != null) { + if (props.size()==0) { + json = "{ \"name\": \"" + noResultsMsgStr + "\" }"; + } else { + for (DataProperty prop: props) { + if ( counter > 0 ) { + json += ", "; + } + + String nameStr = prop.getPublicName()==null ? prop.getName()==null ? prop.getURI()==null ? "(no name)" : prop.getURI() : prop.getName() : prop.getPublicName(); + try { + json += "{ \"name\": \"" + nameStr + "\", "; + } catch (Exception e) { + json += "{ \"name\": \"" + nameStr + "\", "; + } + + json += "\"data\": { \"internalName\": \"" + prop.getLocalNameWithPrefix() + "\", "; + +/* VClass vc = null; + String domainStr=""; + if (prop.getDomainClassURI() != null) { + vc = vcDao.getVClassByURI(prop.getDomainClassURI()); + if (vc != null) { + try { + domainStr=""+vc.getName()+""; + } catch (UnsupportedEncodingException e) { + e.printStackTrace(); + } + } + } +*/ + VClass vc = (prop.getDomainClassURI() != null) ? vcDao.getVClassByURI(prop.getDomainClassURI()) : null; + String domainStr = (vc != null) ? vc.getLocalNameWithPrefix() : ""; + json += "\"domainVClass\": \"" + domainStr + "\", " ; + + Datatype rangeDatatype = dDao.getDatatypeByURI(prop.getRangeDatatypeURI()); + String rangeDatatypeStr = (rangeDatatype==null)?prop.getRangeDatatypeURI():rangeDatatype.getName(); + json += "\"rangeVClass\": \"" + rangeDatatypeStr + "\", " ; + + if (prop.getGroupURI() != null) { + PropertyGroup pGroup = pgDao.getGroupByURI(prop.getGroupURI()); + json += "\"group\": \"" + ((pGroup == null) ? "unknown group" : pGroup.getName()) + "\" } } " ; + } else { + json += "\"group\": \"unspecified\" } }" ; + } + counter += 1; + } + } + body.put("jsonTree",json); + } + } catch (Throwable t) { + t.printStackTrace(); + } + return new TemplateResponseValues(TEMPLATE_NAME, body); + } +} diff --git a/webapp/src/edu/cornell/mannlib/vitro/webapp/controller/freemarker/ListPropertyWebappsController.java b/webapp/src/edu/cornell/mannlib/vitro/webapp/controller/freemarker/ListPropertyWebappsController.java new file mode 100644 index 000000000..4e13eb87d --- /dev/null +++ b/webapp/src/edu/cornell/mannlib/vitro/webapp/controller/freemarker/ListPropertyWebappsController.java @@ -0,0 +1,190 @@ +/* $This file is distributed under the terms of the license in /doc/license.txt$ */ + +package edu.cornell.mannlib.vitro.webapp.controller.freemarker; + +import java.net.URLEncoder; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Iterator; +import java.util.List; +import java.util.Map; + +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.vitro.webapp.auth.permissions.SimplePermission; +import edu.cornell.mannlib.vitro.webapp.auth.requestedAction.Actions; +import edu.cornell.mannlib.vitro.webapp.beans.ObjectProperty; +import edu.cornell.mannlib.vitro.webapp.beans.Ontology; +import edu.cornell.mannlib.vitro.webapp.beans.PropertyGroup; +import edu.cornell.mannlib.vitro.webapp.beans.PropertyInstance; +import edu.cornell.mannlib.vitro.webapp.beans.VClass; +import edu.cornell.mannlib.vitro.webapp.controller.Controllers; +import edu.cornell.mannlib.vitro.webapp.controller.VitroRequest; +import edu.cornell.mannlib.vitro.webapp.controller.freemarker.responsevalues.ResponseValues; +import edu.cornell.mannlib.vitro.webapp.controller.freemarker.responsevalues.TemplateResponseValues; +import edu.cornell.mannlib.vitro.webapp.dao.ObjectPropertyDao; +import edu.cornell.mannlib.vitro.webapp.dao.OntologyDao; +import edu.cornell.mannlib.vitro.webapp.dao.PropertyGroupDao; +import edu.cornell.mannlib.vitro.webapp.dao.PropertyInstanceDao; +import edu.cornell.mannlib.vitro.webapp.dao.VClassDao; +import edu.cornell.mannlib.vitro.webapp.dao.VitroVocabulary; + +public class ListPropertyWebappsController extends FreemarkerHttpServlet { + private static Log log = LogFactory.getLog( ListPropertyWebappsController.class ); + + private static final String TEMPLATE_NAME = "siteAdmin-objectPropHierarchy.ftl"; + + @Override + protected Actions requiredActions(VitroRequest vreq) { + return SimplePermission.EDIT_ONTOLOGY.ACTIONS; + } + + @Override + protected ResponseValues processRequest(VitroRequest vreq) { + + Map body = new HashMap(); + try { + + body.put("displayOption", "all"); + body.put("pageTitle", "All Object Properties"); + body.put("propertyType", "object"); + + String noResultsMsgStr = "No object properties found"; + + String ontologyUri = vreq.getParameter("ontologyUri"); + + ObjectPropertyDao dao = vreq.getFullWebappDaoFactory().getObjectPropertyDao(); + PropertyInstanceDao piDao = vreq.getFullWebappDaoFactory().getPropertyInstanceDao(); + VClassDao vcDao = vreq.getFullWebappDaoFactory().getVClassDao(); + PropertyGroupDao pgDao = vreq.getFullWebappDaoFactory().getPropertyGroupDao(); + + String vclassURI = vreq.getParameter("vclassUri"); + + List props = new ArrayList(); + if (vreq.getParameter("propsForClass") != null) { + noResultsMsgStr = "There are no object properties that apply to this class."; + + // incomplete list of classes to check, but better than before + List superclassURIs = vcDao.getAllSuperClassURIs(vclassURI); + superclassURIs.add(vclassURI); + superclassURIs.addAll(vcDao.getEquivalentClassURIs(vclassURI)); + + Map propInstMap = new HashMap(); + for (String classURI : superclassURIs) { + Collection propInsts = piDao.getAllPropInstByVClass(classURI); + for (PropertyInstance propInst : propInsts) { + propInstMap.put(propInst.getPropertyURI(), propInst); + } + } + List propInsts = new ArrayList(); + propInsts.addAll(propInstMap.values()); + Collections.sort(propInsts); + + Iterator propInstIt = propInsts.iterator(); + HashSet propURIs = new HashSet(); + while (propInstIt.hasNext()) { + PropertyInstance pi = (PropertyInstance) propInstIt.next(); + if (!(propURIs.contains(pi.getPropertyURI()))) { + propURIs.add(pi.getPropertyURI()); + ObjectProperty prop = (ObjectProperty) dao.getObjectPropertyByURI(pi.getPropertyURI()); + if (prop != null) { + props.add(prop); + } + } + } + } else { + props = (vreq.getParameter("iffRoot")!=null) + ? dao.getRootObjectProperties() + : dao.getAllObjectProperties(); + } + + OntologyDao oDao = vreq.getFullWebappDaoFactory().getOntologyDao(); + HashMap ontologyHash = new HashMap(); + + Iterator propIt = props.iterator(); + List scratch = new ArrayList(); + while (propIt.hasNext()) { + ObjectProperty p = (ObjectProperty) propIt.next(); + if (p.getNamespace()!=null) { + if( !ontologyHash.containsKey( p.getNamespace() )){ + Ontology o = (Ontology)oDao.getOntologyByURI(p.getNamespace()); + if (o==null) { + if (!VitroVocabulary.vitroURI.equals(p.getNamespace())) { + log.debug("doGet(): no ontology object found for the namespace "+p.getNamespace()); + } + } else { + ontologyHash.put(p.getNamespace(), o.getName() == null ? p.getNamespace() : o.getName()); + } + } + if (ontologyUri != null && p.getNamespace().equals(ontologyUri)) { + scratch.add(p); + } + } + } + + if (ontologyUri != null) { + props = scratch; + } + + if (props != null) { + Collections.sort(props, new ShowObjectPropertyHierarchyController.ObjectPropertyAlphaComparator()); + } + + String json = new String(); + int counter = 0; + + if (props != null) { + if (props.size()==0) { + json = "{ \"name\": \"" + noResultsMsgStr + "\" }"; + } else { + Iterator propsIt = props.iterator(); + while (propsIt.hasNext()) { + if ( counter > 0 ) { + json += ", "; + } + ObjectProperty prop = (ObjectProperty) propsIt.next(); + + String propNameStr = ShowObjectPropertyHierarchyController.getDisplayLabel(prop); + try { + json += "{ \"name\": \"" + + propNameStr + "\", "; + } catch (Exception e) { + json += "{ \"name\": \"" + propNameStr + "\", "; + } + + json += "\"data\": { \"internalName\": \"" + prop.getLocalNameWithPrefix() + "\", "; + + VClass vc = (prop.getDomainVClassURI() != null) ? vcDao.getVClassByURI(prop.getDomainVClassURI()) : null; + String domainStr = (vc != null) ? vc.getLocalNameWithPrefix() : ""; + json += "\"domainVClass\": \"" + domainStr + "\", " ; + + vc = (prop.getRangeVClassURI() != null) ? vcDao.getVClassByURI(prop.getRangeVClassURI()) : null; + String rangeStr = (vc != null) ? vc.getLocalNameWithPrefix() : ""; + json += "\"rangeVClass\": \"" + rangeStr + "\", " ; + + if (prop.getGroupURI() != null) { + PropertyGroup pGroup = pgDao.getGroupByURI(prop.getGroupURI()); + json += "\"group\": \"" + ((pGroup == null) ? "unknown group" : pGroup.getName()) + "\" } } " ; + } else { + json += "\"group\": \"unspecified\" } }" ; + } + counter += 1; + } + } + body.put("jsonTree",json); + } + + } catch (Throwable t) { + t.printStackTrace(); + } + + return new TemplateResponseValues(TEMPLATE_NAME, body); + } +} diff --git a/webapp/src/edu/cornell/mannlib/vitro/webapp/controller/freemarker/ShowClassHierarchyController.java b/webapp/src/edu/cornell/mannlib/vitro/webapp/controller/freemarker/ShowClassHierarchyController.java index f91adbb07..40cac772c 100644 --- a/webapp/src/edu/cornell/mannlib/vitro/webapp/controller/freemarker/ShowClassHierarchyController.java +++ b/webapp/src/edu/cornell/mannlib/vitro/webapp/controller/freemarker/ShowClassHierarchyController.java @@ -11,7 +11,6 @@ import java.util.Iterator; import java.util.LinkedList; import java.util.List; -import javax.servlet.RequestDispatcher; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; @@ -30,7 +29,6 @@ import edu.cornell.mannlib.vitro.webapp.beans.ApplicationBean; import edu.cornell.mannlib.vitro.webapp.controller.VitroRequest; import edu.cornell.mannlib.vitro.webapp.controller.freemarker.responsevalues.ResponseValues; import edu.cornell.mannlib.vitro.webapp.controller.freemarker.responsevalues.TemplateResponseValues; -import edu.cornell.mannlib.vitro.webapp.email.FreemarkerEmailFactory; import edu.cornell.mannlib.vitro.webapp.dao.OntologyDao; import edu.cornell.mannlib.vitro.webapp.dao.VClassDao; import edu.cornell.mannlib.vitro.webapp.dao.VClassGroupDao; @@ -47,8 +45,6 @@ public class ShowClassHierarchyController extends FreemarkerHttpServlet { private VClassDao vcDao = null; private int previous_posn = 0; - private int childCount = 0; - private int uriCounter = 0; @Override protected Actions requiredActions(VitroRequest vreq) { @@ -116,7 +112,7 @@ public class ShowClassHierarchyController extends FreemarkerHttpServlet { while (rootIt.hasNext()) { VClass root = (VClass) rootIt.next(); if (root != null) { - json += addChildren(vreq.getFullWebappDaoFactory(), root, 0, ontologyUri,counter,"parent"); + json += addChildren(vreq.getFullWebappDaoFactory(), root, 0, ontologyUri,counter); counter += 1; } } @@ -134,7 +130,7 @@ public class ShowClassHierarchyController extends FreemarkerHttpServlet { return new TemplateResponseValues(TEMPLATE_NAME, body); } - private String addChildren(WebappDaoFactory wadf, VClass parent, int position, String ontologyUri, int counter, String status) { + private String addChildren(WebappDaoFactory wadf, VClass parent, int position, String ontologyUri, int counter) { String rowElts = addVClassDataToResultsList(wadf, parent, position, ontologyUri, counter); int childShift = (rowElts.length() > 0) ? 1 : 0; // if addVClassDataToResultsList filtered out the result, don't shift the children over int length = rowElts.length(); @@ -157,7 +153,7 @@ public class ShowClassHierarchyController extends FreemarkerHttpServlet { Iterator childClassIt = childClasses.iterator(); while (childClassIt.hasNext()) { VClass child = (VClass) childClassIt.next(); - leaves += addChildren(wadf, child, position + childShift, ontologyUri, counter, "child"); + leaves += addChildren(wadf, child, position + childShift, ontologyUri, counter); if (!childClassIt.hasNext()) { if ( ontologyUri == null ) { leaves += " }] "; @@ -166,15 +162,12 @@ public class ShowClassHierarchyController extends FreemarkerHttpServlet { // need this for when we show the classes associated with an ontology String ending = leaves.substring(leaves.length() - 2, leaves.length()); if ( ending.equals("] ") ) { - log.debug("[1] leaves += }]"); leaves += "}]"; } else if ( ending.equals(" [") ){ - log.debug("[2] leaves += ]"); leaves += "] "; } else { - log.debug("[3] leaves += }]"); leaves += "}]"; } } diff --git a/webapp/src/edu/cornell/mannlib/vitro/webapp/controller/freemarker/ShowDataPropertyHierarchyController.java b/webapp/src/edu/cornell/mannlib/vitro/webapp/controller/freemarker/ShowDataPropertyHierarchyController.java new file mode 100644 index 000000000..6cb6b42e6 --- /dev/null +++ b/webapp/src/edu/cornell/mannlib/vitro/webapp/controller/freemarker/ShowDataPropertyHierarchyController.java @@ -0,0 +1,251 @@ +/* $This file is distributed under the terms of the license in /doc/license.txt$ */ + +package edu.cornell.mannlib.vitro.webapp.controller.freemarker; + +import java.net.URLEncoder; +import java.text.Collator; +import java.util.ArrayList; +import java.util.Collections; +import java.util.Comparator; +import java.util.HashMap; +import java.util.Map; +import java.util.Iterator; +import java.util.LinkedList; +import java.util.List; + +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.vitro.webapp.auth.permissions.SimplePermission; +import edu.cornell.mannlib.vitro.webapp.auth.requestedAction.Actions; +import edu.cornell.mannlib.vitro.webapp.beans.DataProperty; +import edu.cornell.mannlib.vitro.webapp.beans.Datatype; +import edu.cornell.mannlib.vitro.webapp.beans.PropertyGroup; +import edu.cornell.mannlib.vitro.webapp.beans.VClass; +import edu.cornell.mannlib.vitro.webapp.controller.Controllers; +import edu.cornell.mannlib.vitro.webapp.controller.VitroRequest; +import edu.cornell.mannlib.vitro.webapp.controller.freemarker.responsevalues.ResponseValues; +import edu.cornell.mannlib.vitro.webapp.controller.freemarker.responsevalues.TemplateResponseValues; +import edu.cornell.mannlib.vitro.webapp.dao.DataPropertyDao; +import edu.cornell.mannlib.vitro.webapp.dao.DatatypeDao; +import edu.cornell.mannlib.vitro.webapp.dao.PropertyGroupDao; +import edu.cornell.mannlib.vitro.webapp.dao.VClassDao; + +public class ShowDataPropertyHierarchyController extends FreemarkerHttpServlet { + + private static final Log log = LogFactory.getLog(ShowDataPropertyHierarchyController.class.getName()); + + private static final String TEMPLATE_NAME = "siteAdmin-objectPropHierarchy.ftl"; + private int MAXDEPTH = 5; + + private DataPropertyDao dpDao = null; + private VClassDao vcDao = null; + private PropertyGroupDao pgDao = null; + private DatatypeDao dDao = null; + + private int previous_posn = 0; + + @Override + protected Actions requiredActions(VitroRequest vreq) { + return SimplePermission.EDIT_ONTOLOGY.ACTIONS; + } + + @Override + protected ResponseValues processRequest(VitroRequest vreq) { + + Map body = new HashMap(); + try { + + String displayOption = ""; + + if ( vreq.getParameter("displayOption") != null ) { + displayOption = vreq.getParameter("displayOption"); + } + else { + displayOption = "hierarchy"; + } + body.put("displayOption", displayOption); + + if ( displayOption.equals("all") ) { + body.put("pageTitle", "All Data Properties"); + } + else { + body.put("pageTitle", "Data Property Hierarchy"); + } + + body.put("propertyType", "data"); + + dpDao = vreq.getAssertionsWebappDaoFactory().getDataPropertyDao(); + vcDao = vreq.getAssertionsWebappDaoFactory().getVClassDao(); + pgDao = vreq.getAssertionsWebappDaoFactory().getPropertyGroupDao(); + dDao = vreq.getAssertionsWebappDaoFactory().getDatatypeDao(); + + String json = new String(); + + String ontologyUri = vreq.getParameter("ontologyUri"); + String startPropertyUri = vreq.getParameter("propertyUri"); + + List roots = null; + + if (startPropertyUri != null) { + roots = new LinkedList(); + roots.add(dpDao.getDataPropertyByURI(startPropertyUri)); + } else { + roots = dpDao.getRootDataProperties(); + if (roots!=null){ + Collections.sort(roots); + } + } + + int counter = 0; + + if (roots!=null) { + Iterator rootIt = roots.iterator(); + if (!rootIt.hasNext()) { + DataProperty dp = new DataProperty(); + dp.setURI(ontologyUri+"fake"); + String notFoundMessage = "No data properties found."; + dp.setName(notFoundMessage); + dp.setName(notFoundMessage); + json += addDataPropertyDataToResultsList(dp, 0, ontologyUri, counter); + } else { + while (rootIt.hasNext()) { + DataProperty root = rootIt.next(); + if ( (ontologyUri==null) || ( (ontologyUri!=null) && (root.getNamespace()!=null) && (ontologyUri.equals(root.getNamespace())) ) ) { + json += addChildren(root, 0, ontologyUri, counter); + counter += 1; + } + } + int length = json.length(); + if ( length > 0 ) { + json += " }"; + } + } + } + + body.put("jsonTree",json); + + } catch (Throwable t) { + t.printStackTrace(); + } + return new TemplateResponseValues(TEMPLATE_NAME, body); + } + + private String addChildren(DataProperty parent, int position, String ontologyUri, int counter) { + if (parent == null) { + return ""; + } + String details = addDataPropertyDataToResultsList(parent, position, ontologyUri, counter); + int length = details.length(); + String leaves = ""; + leaves += details; + List childURIstrs = dpDao.getSubPropertyURIs(parent.getURI()); + if ((childURIstrs.size()>0) && position 0 ) { + // need this for when we show the classes associated with an ontology + String ending = leaves.substring(leaves.length() - 2, leaves.length()); + if ( ending.equals("] ") ) { + leaves += "}]"; + } + else if ( ending.equals(" [") ){ + leaves += "] "; + } + else { + leaves += "}]"; + } + } + } + } + } + else { + if ( ontologyUri == null ) { + leaves += "] "; + } + else if ( ontologyUri != null && length > 0 ) { + leaves += "] "; + } + } + return leaves; + } + + private String addDataPropertyDataToResultsList(DataProperty dp, int position, String ontologyUri, int counter) { + String tempString = ""; + if (dp == null) { + return tempString; + } + if (ontologyUri == null || ( (dp.getNamespace()!=null) && (dp.getNamespace().equals(ontologyUri)) ) ) { + if ( counter < 1 && position < 1 ) { + tempString += "{ \"name\": "; + } + else if ( position == previous_posn ) { + tempString += "}, { \"name\": "; + } + else if ( position > previous_posn ) { + tempString += " { \"name\": "; + } + else if ( position < previous_posn ) { + tempString += "}, { \"name\": "; + } + + String nameStr = dp.getPublicName()==null ? dp.getName()==null ? dp.getURI()==null ? "(no name)" : dp.getURI() : dp.getName() : dp.getPublicName(); + try { + tempString += "\"" + nameStr + "\", "; + } catch (Exception e) { + tempString += "\"" + nameStr + "\", "; + log.error("Unsupported: URLEncoder.encode() with UTF-8"); + } + + tempString += "\"data\": { \"internalName\": \"" + dp.getLocalNameWithPrefix() + "\", "; + + VClass tmp = null; + try { + tempString += "\"domainVClass\": \"" + (((tmp = vcDao.getVClassByURI(dp.getDomainClassURI())) != null && (tmp.getLocalNameWithPrefix() == null)) ? "" : vcDao.getVClassByURI(dp.getDomainClassURI()).getLocalNameWithPrefix()) + "\", " ; + } catch (NullPointerException e) { + tempString += "\"domainVClass\": \"\","; + } + try { + Datatype rangeDatatype = dDao.getDatatypeByURI(dp.getRangeDatatypeURI()); + String rangeDatatypeStr = (rangeDatatype==null)?dp.getRangeDatatypeURI():rangeDatatype.getName(); + tempString += "\"rangeVClass\": \"" + ((rangeDatatypeStr != null) ? rangeDatatypeStr : "") + "\", " ; + } catch (NullPointerException e) { + tempString += "\"rangeVClass\": \"\","; + } + if (dp.getGroupURI() != null) { + PropertyGroup pGroup = pgDao.getGroupByURI(dp.getGroupURI()); + tempString += "\"group\": \"" + ((pGroup == null) ? "unknown group" : pGroup.getName()) + "\" " ; + } else { + tempString += "\"group\": \"unspecified\""; + } + tempString += "}, \"children\": ["; + + previous_posn = position; + } + return tempString; + } + + private class DataPropertyAlphaComparator implements Comparator { + public int compare(Object o1, Object o2) { + return Collator.getInstance().compare( ((DataProperty)o1).getName(), ((DataProperty)o2).getName()); + } + } + +} diff --git a/webapp/src/edu/cornell/mannlib/vitro/webapp/controller/freemarker/ShowObjectPropertyHierarchyController.java b/webapp/src/edu/cornell/mannlib/vitro/webapp/controller/freemarker/ShowObjectPropertyHierarchyController.java new file mode 100644 index 000000000..659ddb97c --- /dev/null +++ b/webapp/src/edu/cornell/mannlib/vitro/webapp/controller/freemarker/ShowObjectPropertyHierarchyController.java @@ -0,0 +1,275 @@ +/* $This file is distributed under the terms of the license in /doc/license.txt$ */ + +package edu.cornell.mannlib.vitro.webapp.controller.freemarker; + +import java.io.UnsupportedEncodingException; +import java.net.URLEncoder; +import java.text.Collator; +import java.util.ArrayList; +import java.util.Collections; +import java.util.Comparator; +import java.util.HashMap; +import java.util.Map; +import java.util.Iterator; +import java.util.LinkedList; +import java.util.List; + +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 edu.cornell.mannlib.vitro.webapp.auth.permissions.SimplePermission; +import edu.cornell.mannlib.vitro.webapp.auth.requestedAction.Actions; +import edu.cornell.mannlib.vitro.webapp.beans.ObjectProperty; +import edu.cornell.mannlib.vitro.webapp.beans.PropertyGroup; +import edu.cornell.mannlib.vitro.webapp.beans.VClass; +import edu.cornell.mannlib.vitro.webapp.controller.Controllers; +import edu.cornell.mannlib.vitro.webapp.controller.VitroRequest; +import edu.cornell.mannlib.vitro.webapp.controller.freemarker.responsevalues.ResponseValues; +import edu.cornell.mannlib.vitro.webapp.controller.freemarker.responsevalues.TemplateResponseValues; +import edu.cornell.mannlib.vitro.webapp.dao.ObjectPropertyDao; +import edu.cornell.mannlib.vitro.webapp.dao.PropertyGroupDao; +import edu.cornell.mannlib.vitro.webapp.dao.VClassDao; + +public class ShowObjectPropertyHierarchyController extends FreemarkerHttpServlet { + + private static final Log log = LogFactory.getLog(ShowObjectPropertyHierarchyController.class.getName()); + + private static final String TEMPLATE_NAME = "siteAdmin-objectPropHierarchy.ftl"; + + private int MAXDEPTH = 5; + + private ObjectPropertyDao opDao = null; + private VClassDao vcDao = null; + private PropertyGroupDao pgDao = null; + + private int previous_posn = 0; + + @Override + protected Actions requiredActions(VitroRequest vreq) { + return SimplePermission.EDIT_ONTOLOGY.ACTIONS; + } + + @Override + protected ResponseValues processRequest(VitroRequest vreq) { + + Map body = new HashMap(); + try { + + String displayOption = ""; + + if ( vreq.getParameter("displayOption") != null ) { + displayOption = vreq.getParameter("displayOption"); + } + else { + displayOption = "hierarchy"; + } + body.put("displayOption", displayOption); + + if ( displayOption.equals("all") ) { + body.put("pageTitle", "All Object Properties"); + } + else { + body.put("pageTitle", "Object Property Hierarchy"); + } + + body.put("propertyType", "object"); + + opDao = vreq.getAssertionsWebappDaoFactory().getObjectPropertyDao(); + vcDao = vreq.getAssertionsWebappDaoFactory().getVClassDao(); + pgDao = vreq.getAssertionsWebappDaoFactory().getPropertyGroupDao(); + + String json = new String(); + + String ontologyUri = vreq.getParameter("ontologyUri"); + String startPropertyUri = vreq.getParameter("propertyUri"); + + List roots = null; + + if (startPropertyUri != null) { + roots = new LinkedList(); + ObjectProperty op = opDao.getObjectPropertyByURI(startPropertyUri); + if (op == null) { + op = new ObjectProperty(); + op.setURI(startPropertyUri); + } + roots.add(op); + } else { + roots = opDao.getRootObjectProperties(); + if (roots!=null){ + Collections.sort(roots, new ObjectPropertyAlphaComparator()); // sorts by domain public + } + } + + int counter = 0; + + if (roots != null) { + Iterator rootIt = roots.iterator(); + if (!rootIt.hasNext()) { + ObjectProperty op = new ObjectProperty(); + op.setURI(ontologyUri+"fake"); + String notFoundMessage = "No object properties found."; + op.setDomainPublic(notFoundMessage); + json += addObjectPropertyDataToResultsList(op, 0, ontologyUri, counter); + } else { + while (rootIt.hasNext()) { + ObjectProperty root = rootIt.next(); + if ( (ontologyUri==null) || + ( (ontologyUri != null) + && (root.getNamespace() != null) + && (ontologyUri.equals(root.getNamespace())) ) ) { + json += addChildren(root, 0, ontologyUri, counter); + counter += 1; + } + } + int length = json.length(); + if ( length > 0 ) { + json += " }"; + } + } + } + + body.put("jsonTree",json); + + } catch (Throwable t) { + t.printStackTrace(); + } + return new TemplateResponseValues(TEMPLATE_NAME, body); + + } + + private String addChildren(ObjectProperty parent, int position, String ontologyUri, int counter) { + String details = addObjectPropertyDataToResultsList(parent, position, ontologyUri, counter); + int length = details.length(); + String leaves = ""; + leaves += details; + List childURIstrs = opDao.getSubPropertyURIs(parent.getURI()); + if ((childURIstrs.size()>0) && position 0 ) { + // need this for when we show the classes associated with an ontology + String ending = leaves.substring(leaves.length() - 2, leaves.length()); + if ( ending.equals("] ") ) { + leaves += "}]"; + } + else if ( ending.equals(" [") ){ + leaves += "] "; + } + else { + leaves += "}]"; + } + } + } + } + } + else { + if ( ontologyUri == null ) { + leaves += "] "; + } + else if ( ontologyUri != null && length > 0 ) { + leaves += "] "; + } + } + return leaves; + } + + private String addObjectPropertyDataToResultsList(ObjectProperty op, int position, String ontologyUri, int counter) { + String tempString = ""; + if (ontologyUri == null || ( (op.getNamespace()!=null) && (op.getNamespace().equals(ontologyUri)) ) ) { + // first if statement ensures that the first class begins with correct format + if ( counter < 1 && position < 1 ) { + tempString += "{ \"name\": "; + } + else if ( position == previous_posn ) { + tempString += "}, { \"name\": "; + } + else if ( position > previous_posn ) { + tempString += " { \"name\": "; + } + else if ( position < previous_posn ) { + tempString += "}, { \"name\": "; + } + + try { + tempString += "\"" + getDisplayLabel(op)+"\", "; + } catch (UnsupportedEncodingException uee) { + tempString += "\"" + getDisplayLabel(op) + "\""; + log.error("Unsupported: URLEncoder.encode() with UTF-8"); + } + + tempString += "\"data\": { \"internalName\": \"" + op.getLocalNameWithPrefix() + "\", "; + + VClass tmp = null; + + try { + tempString += "\"domainVClass\": \"" + (((tmp = vcDao.getVClassByURI(op.getDomainVClassURI())) != null && (tmp.getLocalNameWithPrefix() == null)) ? "" : vcDao.getVClassByURI(op.getDomainVClassURI()).getLocalNameWithPrefix()) + "\", " ; + } catch (NullPointerException e) { + tempString += "\"domainVClass\": \"\","; + } + try { + tempString += "\"rangeVClass\": \"" + (((tmp = vcDao.getVClassByURI(op.getRangeVClassURI())) != null && (tmp.getLocalNameWithPrefix() == null)) ? "" : vcDao.getVClassByURI(op.getRangeVClassURI()).getLocalNameWithPrefix()) + "\", " ; + } catch (NullPointerException e) { + tempString += "\"rangeVClass\": \"\","; + } + if (op.getGroupURI() != null) { + PropertyGroup pGroup = pgDao.getGroupByURI(op.getGroupURI()); + tempString += "\"group\": \"" + ((pGroup == null) ? "unknown group" : pGroup.getName()) + "\" " ; + } else { + tempString += "\"group\": \"unspecified\""; + } + tempString += "}, \"children\": ["; + + previous_posn = position; + } + return tempString; + } + + public static class ObjectPropertyAlphaComparator implements Comparator { + public int compare(ObjectProperty op1, ObjectProperty op2) { + if (op1 == null) { + return 1; + } else if (op2 == null) { + return -1; + } + String propLabel1 = getDisplayLabel(op1); + String propLabel2 = getDisplayLabel(op2); + if (propLabel1 == null) { + return 1; + } else if (propLabel2 == null) { + return -1; + } else { + return Collator.getInstance().compare( propLabel1, propLabel2 ); + } + } + } + + /* + * should never be null + */ + public static String getDisplayLabel(ObjectProperty op) { + String domainPublic = op.getDomainPublic(); + String displayLabel = (domainPublic != null && domainPublic.length() > 0) + ? domainPublic + : op.getLocalName(); + return (displayLabel != null) ? displayLabel : "[object property]" ; + } + +} \ No newline at end of file diff --git a/webapp/src/edu/cornell/mannlib/vitro/webapp/controller/individual/IndividualResponseBuilder.java b/webapp/src/edu/cornell/mannlib/vitro/webapp/controller/individual/IndividualResponseBuilder.java index 6d273f9cc..ddae63f42 100644 --- a/webapp/src/edu/cornell/mannlib/vitro/webapp/controller/individual/IndividualResponseBuilder.java +++ b/webapp/src/edu/cornell/mannlib/vitro/webapp/controller/individual/IndividualResponseBuilder.java @@ -2,9 +2,15 @@ package edu.cornell.mannlib.vitro.webapp.controller.individual; +import java.io.IOException; +import java.sql.SQLException; import java.util.HashMap; import java.util.Map; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.json.JSONException; + import edu.cornell.mannlib.vitro.webapp.auth.permissions.SimplePermission; import edu.cornell.mannlib.vitro.webapp.auth.policy.PolicyHelper; import edu.cornell.mannlib.vitro.webapp.beans.Individual; @@ -22,6 +28,7 @@ import edu.cornell.mannlib.vitro.webapp.dao.WebappDaoFactory; import edu.cornell.mannlib.vitro.webapp.web.beanswrappers.ReadOnlyBeansWrapper; import edu.cornell.mannlib.vitro.webapp.web.templatemodels.individual.IndividualTemplateModel; import edu.cornell.mannlib.vitro.webapp.web.templatemodels.individuallist.ListedIndividual; +import edu.ucsf.vitro.opensocial.OpenSocialManager; import freemarker.ext.beans.BeansWrapper; import freemarker.template.TemplateModel; import freemarker.template.TemplateModelException; @@ -33,6 +40,9 @@ import freemarker.template.TemplateModelException; * TODO clean this up. */ class IndividualResponseBuilder { + private static final Log log = LogFactory + .getLog(IndividualResponseBuilder.class); + private static final Map namespaces = new HashMap() {{ put("display", VitroVocabulary.DISPLAY); put("vitro", VitroVocabulary.vitroURI); @@ -78,6 +88,24 @@ class IndividualResponseBuilder { //If special values required for individuals like menu, include values in template values body.putAll(getSpecialEditingValues()); + // VIVO OpenSocial Extension by UCSF + try { + OpenSocialManager openSocialManager = new OpenSocialManager(vreq, + itm.isEditable() ? "individual-EDIT-MODE" : "individual", itm.isEditable()); + openSocialManager.setPubsubData(OpenSocialManager.JSON_PERSONID_CHANNEL, + OpenSocialManager.buildJSONPersonIds(individual, "1 person found")); + body.put(OpenSocialManager.TAG_NAME, openSocialManager); + if (openSocialManager.isVisible()) { + body.put("bodyOnload", "my.init();"); + } + } catch (JSONException e) { + log.error("JSONException in doTemplate()", e); + } catch (IOException e) { + log.error("IOException in doTemplate()", e); + } catch (SQLException e) { + log.error("SQLException in doTemplate()", e); + } + String template = new IndividualTemplateLocator(vreq, individual).findTemplate(); return new TemplateResponseValues(template, body); diff --git a/webapp/src/edu/cornell/mannlib/vitro/webapp/dao/DataPropertyDao.java b/webapp/src/edu/cornell/mannlib/vitro/webapp/dao/DataPropertyDao.java index 5c3a4281f..ae1bef568 100644 --- a/webapp/src/edu/cornell/mannlib/vitro/webapp/dao/DataPropertyDao.java +++ b/webapp/src/edu/cornell/mannlib/vitro/webapp/dao/DataPropertyDao.java @@ -1,5 +1,5 @@ -/* $This file is distributed under the terms of the license in /doc/license.txt$ */ - +/* $This file is distributed under the terms of the license in /doc/license.txt$ */ + package edu.cornell.mannlib.vitro.webapp.dao; import edu.cornell.mannlib.vitro.webapp.beans.DataProperty; @@ -34,9 +34,11 @@ public interface DataPropertyDao extends PropertyDao { List getRootDataProperties(); - boolean annotateDataPropertyAsExternalIdentifier(String dataPropertyURI); - - public List getDataPropertyList(Individual subject); - + boolean annotateDataPropertyAsExternalIdentifier(String dataPropertyURI); + + public List getDataPropertyList(Individual subject); + public List getDataPropertyList(String subjectUri); + + public String getCustomListViewConfigFileName(DataProperty dataProperty); } \ No newline at end of file diff --git a/webapp/src/edu/cornell/mannlib/vitro/webapp/dao/DataPropertyStatementDao.java b/webapp/src/edu/cornell/mannlib/vitro/webapp/dao/DataPropertyStatementDao.java index ff795fa83..3801f9a67 100644 --- a/webapp/src/edu/cornell/mannlib/vitro/webapp/dao/DataPropertyStatementDao.java +++ b/webapp/src/edu/cornell/mannlib/vitro/webapp/dao/DataPropertyStatementDao.java @@ -4,6 +4,7 @@ package edu.cornell.mannlib.vitro.webapp.dao; import java.util.Collection; import java.util.List; +import java.util.Set; import com.hp.hpl.jena.rdf.model.Literal; @@ -44,5 +45,9 @@ public interface DataPropertyStatementDao { List getDataPropertyValuesForIndividualByProperty(String subjectUri, String propertyUri); + List getDataPropertyValuesForIndividualByProperty(Individual subject, DataProperty property, String queryString, Set constructQueryStrings); + + List getDataPropertyValuesForIndividualByProperty(String subjectUri, String propertyUri, String queryString, Set constructQueryStrings); + } diff --git a/webapp/src/edu/cornell/mannlib/vitro/webapp/dao/DisplayVocabulary.java b/webapp/src/edu/cornell/mannlib/vitro/webapp/dao/DisplayVocabulary.java index 4e23e7cba..708bff318 100644 --- a/webapp/src/edu/cornell/mannlib/vitro/webapp/dao/DisplayVocabulary.java +++ b/webapp/src/edu/cornell/mannlib/vitro/webapp/dao/DisplayVocabulary.java @@ -130,11 +130,14 @@ public class DisplayVocabulary { public static final String HOME_MENU_ITEM = DISPLAY_NS + "HomeMenuItem"; - /* some URIs of properties used with the SPARQL DataGetter */ + /* some URIs of properties used with the SPARQL DataGetter, and save to var is saved for fixedHtml */ public static final String SAVE_TO_VAR = DISPLAY_NS + "saveToVar" ; public static final String QUERY_MODEL = DISPLAY_NS + "queryModel"; public static final String QUERY = DISPLAY_NS + "query"; + /* URI of property for Fixed HTML Generator */ + public static final String FIXED_HTML_VALUE = DISPLAY_NS + "htmlValue"; + //public static final Individual EVENTS = m_model.createIndividual( NS + "Events", PAGE ); //public static final Individual EVENTS_MENU_ITEM = m_model.createIndividual( NS + "EventsMenuItem", NAVIGATION_ELEMENT ); diff --git a/webapp/src/edu/cornell/mannlib/vitro/webapp/dao/filtering/DataPropertyDaoFiltering.java b/webapp/src/edu/cornell/mannlib/vitro/webapp/dao/filtering/DataPropertyDaoFiltering.java index f2f569b62..819f84f65 100644 --- a/webapp/src/edu/cornell/mannlib/vitro/webapp/dao/filtering/DataPropertyDaoFiltering.java +++ b/webapp/src/edu/cornell/mannlib/vitro/webapp/dao/filtering/DataPropertyDaoFiltering.java @@ -213,4 +213,9 @@ class DataPropertyDaoFiltering extends BaseFiltering implements DataPropertyDao{ return innerDataPropertyDao.getDataPropertyList(subjectUri); } + @Override + public String getCustomListViewConfigFileName(DataProperty dataProperty) { + return innerDataPropertyDao.getCustomListViewConfigFileName(dataProperty); + } + } \ No newline at end of file diff --git a/webapp/src/edu/cornell/mannlib/vitro/webapp/dao/filtering/DataPropertyStatementDaoFiltering.java b/webapp/src/edu/cornell/mannlib/vitro/webapp/dao/filtering/DataPropertyStatementDaoFiltering.java index a7f9868dd..d0936e2fd 100644 --- a/webapp/src/edu/cornell/mannlib/vitro/webapp/dao/filtering/DataPropertyStatementDaoFiltering.java +++ b/webapp/src/edu/cornell/mannlib/vitro/webapp/dao/filtering/DataPropertyStatementDaoFiltering.java @@ -7,6 +7,7 @@ import java.util.Collection; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; +import java.util.Set; import com.hp.hpl.jena.rdf.model.Literal; @@ -18,6 +19,7 @@ import edu.cornell.mannlib.vitro.webapp.dao.DataPropertyStatementDao; import edu.cornell.mannlib.vitro.webapp.dao.filtering.filters.VitroFilters; class DataPropertyStatementDaoFiltering extends BaseFiltering implements DataPropertyStatementDao{ + final DataPropertyStatementDao innerDataPropertyStatementDao; final VitroFilters filters; @@ -141,4 +143,48 @@ class DataPropertyStatementDaoFiltering extends BaseFiltering implements DataPro } + @Override + public List getDataPropertyValuesForIndividualByProperty(Individual subject, DataProperty property, String queryString, Set constructQueryStrings) { + return getDataPropertyValuesForIndividualByProperty(subject.getURI(), property.getURI(), queryString, constructQueryStrings); + } + + @Override + public List getDataPropertyValuesForIndividualByProperty(String subjectUri, String propertyUri, String queryString, Set constructQueryStrings) { + List literals = innerDataPropertyStatementDao.getDataPropertyValuesForIndividualByProperty(subjectUri, propertyUri, queryString, constructQueryStrings); + /* Filter the data + * + * Filtering is applied to a list of DataPropertyStatement. Create these statements, mapped + * to the literal that they are built from, apply filtering to the statements, then get + * the associated literals out of the original list. Use a LinkedHashMap to preserve the ordering. + */ + Map stmtsToLiterals = + new LinkedHashMap(literals.size()); + + for (Literal literal : literals) { + String value = literal.getLexicalForm(); + DataPropertyStatement statement = new DataPropertyStatementImpl(subjectUri, propertyUri, value); + statement.setDatatypeURI(literal.getDatatypeURI()); + statement.setLanguage(literal.getLanguage()); + stmtsToLiterals.put(statement, literal); + } + + List stmtList = new ArrayList(stmtsToLiterals.keySet()); + + // Apply the filters to the list of statements + List filteredStatements = filter(stmtList, filters.getDataPropertyStatementFilter()); + + // Get the literals associated with the filtered statements out of the original list + List filteredLiterals = new ArrayList(filteredStatements.size()); + for (DataPropertyStatement dps : filteredStatements) { + if (dps instanceof DataPropertyStatementFiltering) { + dps = ((DataPropertyStatementFiltering)dps).innerStmt; + } + filteredLiterals.add(stmtsToLiterals.get(dps)); + } + + // Return the filtered list of literals + return filteredLiterals; + + } + } \ No newline at end of file diff --git a/webapp/src/edu/cornell/mannlib/vitro/webapp/dao/jena/DataPropertyDaoJena.java b/webapp/src/edu/cornell/mannlib/vitro/webapp/dao/jena/DataPropertyDaoJena.java index 35cdb324c..8a1ce1189 100644 --- a/webapp/src/edu/cornell/mannlib/vitro/webapp/dao/jena/DataPropertyDaoJena.java +++ b/webapp/src/edu/cornell/mannlib/vitro/webapp/dao/jena/DataPropertyDaoJena.java @@ -11,6 +11,7 @@ import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; import java.util.List; +import java.util.Map; import org.apache.commons.lang.StringUtils; import org.apache.commons.logging.Log; @@ -25,6 +26,8 @@ import com.hp.hpl.jena.ontology.ProfileException; import com.hp.hpl.jena.ontology.Restriction; import com.hp.hpl.jena.ontology.SomeValuesFromRestriction; 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; @@ -729,5 +732,52 @@ public class DataPropertyDaoJena extends PropertyDaoJena implements } return properties; } + protected static final String LIST_VIEW_CONFIG_FILE_QUERY_STRING = + "PREFIX display: " + + "SELECT ?property ?filename WHERE { \n" + + " ?property display:listViewConfigFile ?filename . \n" + + "}"; + + protected static Query listViewConfigFileQuery = null; + static { + try { + listViewConfigFileQuery = QueryFactory.create(LIST_VIEW_CONFIG_FILE_QUERY_STRING); + } catch(Throwable th){ + log.error("could not create SPARQL query for LIST_VIEW_CONFIG_FILE_QUERY_STRING " + th.getMessage()); + log.error(LIST_VIEW_CONFIG_FILE_QUERY_STRING); + } + } + + Map customListViewConfigFileMap = null; + + @Override + public String getCustomListViewConfigFileName(DataProperty dp) { + if (customListViewConfigFileMap == null) { + customListViewConfigFileMap = new HashMap(); + OntModel displayModel = getOntModelSelector().getDisplayModel(); + //Get all property to list view config file mappings in the system + QueryExecution qexec = QueryExecutionFactory.create(listViewConfigFileQuery, displayModel); + ResultSet results = qexec.execSelect(); + //Iterate through mappings looking for the current property and setting up a hashmap for subsequent retrieval + while (results.hasNext()) { + QuerySolution soln = results.next(); + String propertyUri = soln.getResource("property").getURI(); + DataProperty prop = getDataPropertyByURI(propertyUri); + if (prop == null) { + //This is a warning only if this property is the one for which we're searching + if(dp.getURI().equals(propertyUri)){ + log.warn("Can't find property for uri " + propertyUri); + } else { + log.debug("Can't find property for uri " + propertyUri); + } + } else { + String filename = soln.getLiteral("filename").getLexicalForm(); + customListViewConfigFileMap.put(prop, filename); + } + } + qexec.close(); + } + return customListViewConfigFileMap.get(dp); + } } diff --git a/webapp/src/edu/cornell/mannlib/vitro/webapp/dao/jena/DataPropertyStatementDaoJena.java b/webapp/src/edu/cornell/mannlib/vitro/webapp/dao/jena/DataPropertyStatementDaoJena.java index 06504e8ca..600d0fa4d 100644 --- a/webapp/src/edu/cornell/mannlib/vitro/webapp/dao/jena/DataPropertyStatementDaoJena.java +++ b/webapp/src/edu/cornell/mannlib/vitro/webapp/dao/jena/DataPropertyStatementDaoJena.java @@ -8,6 +8,7 @@ import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; +import java.util.Set; import com.hp.hpl.jena.datatypes.TypeMapper; import com.hp.hpl.jena.ontology.OntModel; @@ -18,10 +19,15 @@ 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.QuerySolutionMap; import com.hp.hpl.jena.query.ResultSet; +import com.hp.hpl.jena.query.Syntax; import com.hp.hpl.jena.rdf.model.AnonId; import com.hp.hpl.jena.rdf.model.Literal; +import com.hp.hpl.jena.rdf.model.Model; +import com.hp.hpl.jena.rdf.model.ModelFactory; import com.hp.hpl.jena.rdf.model.Property; +import com.hp.hpl.jena.rdf.model.RDFNode; import com.hp.hpl.jena.rdf.model.Resource; import com.hp.hpl.jena.rdf.model.ResourceFactory; import com.hp.hpl.jena.rdf.model.Statement; @@ -38,6 +44,7 @@ import edu.cornell.mannlib.vitro.webapp.controller.edit.ReorderController; import edu.cornell.mannlib.vitro.webapp.dao.DataPropertyStatementDao; import edu.cornell.mannlib.vitro.webapp.dao.VitroVocabulary; import edu.cornell.mannlib.vitro.webapp.dao.jena.event.IndividualUpdateEvent; +import edu.cornell.mannlib.vitro.webapp.dao.jena.ObjectPropertyStatementDaoJena; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; @@ -190,6 +197,7 @@ public class DataPropertyStatementDaoJena extends JenaBaseDao implements DataPro Resource res = ResourceFactory.createResource(entity.getURI()); if (!VitroVocabulary.PSEUDO_BNODE_NS.equals(entity.getNamespace())) { for (Literal lit : this.getDataPropertyValuesForIndividualByProperty(res.getURI(), datapropURI)) { + log.debug("Literal lit = " + lit); DataPropertyStatement ed = new DataPropertyStatementImpl(); fillDataPropertyStatementWithJenaLiteral(ed, lit); ed.setIndividualURI(entity.getURI()); @@ -390,4 +398,126 @@ public class DataPropertyStatementDaoJena extends JenaBaseDao implements DataPro } } + + @Override + public List getDataPropertyValuesForIndividualByProperty( + Individual subject, + DataProperty property, + String queryString, Set constructQueryStrings ) { + return getDataPropertyValuesForIndividualByProperty(subject.getURI(), property.getURI(), queryString, constructQueryStrings ); + } + + @Override + public List getDataPropertyValuesForIndividualByProperty( + String subjectUri, + String propertyUri, + String queryString, Set constructQueryStrings ) { + + Model constructedModel = constructModelForSelectQueries( + subjectUri, propertyUri, constructQueryStrings); + + log.debug("Query string for data property " + propertyUri + ": " + queryString); + + Query query = null; + try { + query = QueryFactory.create(queryString, Syntax.syntaxARQ); + } catch(Throwable th){ + log.error("Could not create SPARQL query for query string. " + th.getMessage()); + log.error(queryString); + return Collections.emptyList(); + } + + QuerySolutionMap initialBindings = new QuerySolutionMap(); + initialBindings.add("subject", ResourceFactory.createResource(subjectUri)); + initialBindings.add("property", ResourceFactory.createResource(propertyUri)); + + // Run the SPARQL query to get the properties + List values = new ArrayList(); + DatasetWrapper w = dwf.getDatasetWrapper(); + Dataset dataset = w.getDataset(); + dataset.getLock().enterCriticalSection(Lock.READ); + QueryExecution qexec = null; + try { + + qexec = (constructedModel == null) + ? QueryExecutionFactory.create( + query, dataset, initialBindings) + : QueryExecutionFactory.create( + query, constructedModel, initialBindings); + + ResultSet results = qexec.execSelect(); + + while (results.hasNext()) { + QuerySolution sol = results.next(); + Literal value = sol.getLiteral("value"); + values.add(value); + } + log.debug("values = " + values); + return values; + + } catch (Exception e) { + log.error("Error getting data property values for subject " + subjectUri + " and property " + propertyUri); + return Collections.emptyList(); + } finally { + dataset.getLock().leaveCriticalSection(); + w.close(); + if (qexec != null) { + qexec.close(); + } + } + + } + private Model constructModelForSelectQueries(String subjectUri, + String propertyUri, + Set constructQueries) { + + if (constructQueries == null) { + return null; + } + + Model constructedModel = ModelFactory.createDefaultModel(); + + for (String queryString : constructQueries) { + + log.debug("CONSTRUCT query string for object property " + + propertyUri + ": " + queryString); + + Query query = null; + try { + query = QueryFactory.create(queryString, Syntax.syntaxARQ); + } catch(Throwable th){ + log.error("Could not create CONSTRUCT SPARQL query for query " + + "string. " + th.getMessage()); + log.error(queryString); + return constructedModel; + } + + QuerySolutionMap initialBindings = new QuerySolutionMap(); + initialBindings.add( + "subject", ResourceFactory.createResource(subjectUri)); + initialBindings.add( + "property", ResourceFactory.createResource(propertyUri)); + + DatasetWrapper w = dwf.getDatasetWrapper(); + Dataset dataset = w.getDataset(); + dataset.getLock().enterCriticalSection(Lock.READ); + QueryExecution qe = null; + try { + qe = QueryExecutionFactory.create( + query, dataset, initialBindings); + qe.execConstruct(constructedModel); + } catch (Exception e) { + log.error("Error getting constructed model for subject " + subjectUri + " and property " + propertyUri); + } finally { + if (qe != null) { + qe.close(); + } + dataset.getLock().leaveCriticalSection(); + w.close(); + } + } + + return constructedModel; + + } } diff --git a/webapp/src/edu/cornell/mannlib/vitro/webapp/dao/jena/RDFServiceGraph.java b/webapp/src/edu/cornell/mannlib/vitro/webapp/dao/jena/RDFServiceGraph.java index 12050e1a6..4b2afdd89 100644 --- a/webapp/src/edu/cornell/mannlib/vitro/webapp/dao/jena/RDFServiceGraph.java +++ b/webapp/src/edu/cornell/mannlib/vitro/webapp/dao/jena/RDFServiceGraph.java @@ -1,6 +1,5 @@ package edu.cornell.mannlib.vitro.webapp.dao.jena; -import java.io.ByteArrayInputStream; import java.io.InputStream; import java.util.ArrayList; import java.util.Collections; @@ -49,8 +48,6 @@ public class RDFServiceGraph implements GraphWithPerform { private PrefixMapping prefixMapping = new PrefixMappingImpl(); private GraphEventManager eventManager; private Reifier reifier = new EmptyReifier(this); - private GraphStatisticsHandler graphStatisticsHandler; - private TransactionHandler transactionHandler; private QueryHandler queryHandler; /** @@ -83,26 +80,6 @@ public class RDFServiceGraph implements GraphWithPerform { public void add(Triple arg0) throws AddDeniedException { performAdd(arg0); } - -// public void executeUpdate(String updateString) { -// try { -// RepositoryConnection conn = getConnection(); -// try { -// Update u = conn.prepareUpdate(QueryLanguage.SPARQL, updateString); -// u.execute(); -// } catch (MalformedQueryException e) { -// throw new RuntimeException(e); -// } catch (UpdateExecutionException e) { -// log.error(e,e); -// log.error("Update command: \n" + updateString); -// throw new RuntimeException(e); -// } finally { -// conn.close(); -// } -// } catch (RepositoryException re) { -// throw new RuntimeException(re); -// } -// } private String serialize(Triple t) { StringBuffer sb = new StringBuffer(); @@ -130,8 +107,8 @@ public class RDFServiceGraph implements GraphWithPerform { public void performDelete(Triple t) { ChangeSet changeSet = rdfService.manufactureChangeSet(); try { - changeSet.addRemoval(new ByteArrayInputStream( - serialize(t).getBytes()), RDFService.ModelSerializationFormat.N3, graphURI); + changeSet.addRemoval(RDFServiceUtils.toInputStream(serialize(t)), + RDFService.ModelSerializationFormat.N3, graphURI); rdfService.changeSetUpdate(changeSet); } catch (RDFServiceException rdfse) { throw new RuntimeException(rdfse); @@ -268,16 +245,8 @@ public class RDFServiceGraph implements GraphWithPerform { findQuery.append("\n}"); String queryString = findQuery.toString(); - //log.info(queryString); - -// //TODO remove me -// if (queryString.contains("individual/AI") && queryString.contains("label")) { -// throw new RuntimeException("break!"); -// } ResultSet rs = execSelect(queryString); - //rs = execSelect(findQuery.toString()); - //rs = execSelect(findQuery.toString()); List triplist = new ArrayList(); while (rs.hasNext()) { @@ -324,9 +293,6 @@ public class RDFServiceGraph implements GraphWithPerform { @Override public Reifier getReifier() { - //if (reifier == null) { - // reifier = new SimpleReifier(this, ReificationStyle.Standard); - //} return reifier; } @@ -354,7 +320,6 @@ public class RDFServiceGraph implements GraphWithPerform { @Override public boolean isIsomorphicWith(Graph arg0) { - log.info("Hey dummy!"); throw new UnsupportedOperationException("isIsomorphicWith() not supported " + "by SPARQL graphs"); } diff --git a/webapp/src/edu/cornell/mannlib/vitro/webapp/dao/jena/RDFServiceGraphBulkUpdater.java b/webapp/src/edu/cornell/mannlib/vitro/webapp/dao/jena/RDFServiceGraphBulkUpdater.java index 3e0928cba..0b8f80622 100644 --- a/webapp/src/edu/cornell/mannlib/vitro/webapp/dao/jena/RDFServiceGraphBulkUpdater.java +++ b/webapp/src/edu/cornell/mannlib/vitro/webapp/dao/jena/RDFServiceGraphBulkUpdater.java @@ -73,7 +73,6 @@ public class RDFServiceGraphBulkUpdater extends SimpleBulkUpdateHandler { @Override public void add(Graph g, boolean arg1) { - log.info("bulk addGraph()"); Model[] model = separateStatementsWithBlankNodes(g); addModel(model[1] /* nonBlankNodeModel */); // replace following call with different method @@ -118,7 +117,6 @@ public class RDFServiceGraphBulkUpdater extends SimpleBulkUpdateHandler { } public void addModel(Model model) { - log.info("bulk addModel()"); ChangeSet changeSet = graph.getRDFService().manufactureChangeSet(); ByteArrayOutputStream out = new ByteArrayOutputStream(); model.write(out, "N-TRIPLE"); @@ -133,7 +131,6 @@ public class RDFServiceGraphBulkUpdater extends SimpleBulkUpdateHandler { } public void deleteModel(Model model) { - log.info("bulk addModel()"); ChangeSet changeSet = graph.getRDFService().manufactureChangeSet(); ByteArrayOutputStream out = new ByteArrayOutputStream(); model.write(out, "N-TRIPLE"); @@ -180,22 +177,7 @@ public class RDFServiceGraphBulkUpdater extends SimpleBulkUpdateHandler { public static void removeAll( Graph g ) { - ExtendedIterator it = GraphUtil.findAll(g); - try { - while (it.hasNext()) { - Triple t = it.next(); - g.delete(t); - it.remove(); - } - } finally { - it.close(); - } - - // get rid of remaining blank nodes using a SPARQL DELETE - if (g instanceof SparqlGraph) { - ((SparqlGraph) g).removeAll(); - } - + g.getBulkUpdateHandler().delete(g); } } 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 4514ba5c1..287099b78 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 @@ -13,6 +13,7 @@ import javax.servlet.http.HttpSession; import com.hp.hpl.jena.ontology.OntModel; 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.edit.n3editing.VTwo.EditConfigurationUtils; import edu.cornell.mannlib.vitro.webapp.edit.n3editing.VTwo.EditConfigurationVTwo; @@ -154,7 +155,7 @@ public class ManagePageGenerator extends BaseEditConfigurationGenerator implemen //For the case of a new page if(subjectUri == null) { //Once added, return to pageList - editConfiguration.setUrlToReturnTo("/pageList"); + editConfiguration.setUrlToReturnTo(UrlBuilder.getUrl("/pageList")); editConfiguration.setEntityToReturnTo("?page"); editConfiguration.setPredicateUri(predicateUri); @@ -190,8 +191,10 @@ public class ManagePageGenerator extends BaseEditConfigurationGenerator implemen //In the case where this is a new page, need to ensure page gets a new private void setNewResources(EditConfigurationVTwo conf) { //null makes default namespace be triggered - conf.addNewResource("page", defaultDisplayNs); - conf.addNewResource("menuItem", defaultDisplayNs); + //conf.addNewResource("page", defaultDisplayNs); + //conf.addNewResource("menuItem", defaultDisplayNs); + conf.addNewResource("page", null); + conf.addNewResource("menuItem", null); } diff --git a/webapp/src/edu/cornell/mannlib/vitro/webapp/edit/n3editing/configuration/preprocessors/ManagePagePreprocessor.java b/webapp/src/edu/cornell/mannlib/vitro/webapp/edit/n3editing/configuration/preprocessors/ManagePagePreprocessor.java index 694fc7322..0030d7038 100644 --- a/webapp/src/edu/cornell/mannlib/vitro/webapp/edit/n3editing/configuration/preprocessors/ManagePagePreprocessor.java +++ b/webapp/src/edu/cornell/mannlib/vitro/webapp/edit/n3editing/configuration/preprocessors/ManagePagePreprocessor.java @@ -25,9 +25,11 @@ import edu.cornell.mannlib.vitro.webapp.edit.n3editing.VTwo.fields.FieldVTwo; import edu.cornell.mannlib.vitro.webapp.edit.n3editing.configuration.generators.ManagePageGenerator; import edu.cornell.mannlib.vitro.webapp.edit.n3editing.configuration.preprocessors.utils.ProcessDataGetterN3; import edu.cornell.mannlib.vitro.webapp.edit.n3editing.configuration.preprocessors.utils.ProcessDataGetterN3Utils; +import net.sf.json.JSON; +import net.sf.json.JSONArray; import net.sf.json.JSONObject; import net.sf.json.JSONSerializer; - +import net.sf.json.util.JSONUtils; public class ManagePagePreprocessor extends BaseEditSubmissionPreprocessorVTwo { @@ -83,7 +85,7 @@ public class ManagePagePreprocessor extends int counter = 0; for(JSONObject jsonObject:pageContentUnitsJSON) { String dataGetterClass = getDataGetterClass(jsonObject); - ProcessDataGetterN3 pn = ProcessDataGetterN3Utils.getDataGetterProcessorN3(dataGetterClass); + ProcessDataGetterN3 pn = ProcessDataGetterN3Utils.getDataGetterProcessorN3(dataGetterClass, jsonObject); //Add n3 required addN3Required(pn, counter); //Add N3 Optional as well @@ -106,7 +108,13 @@ public class ManagePagePreprocessor extends // TODO Auto-generated method stub List newResources = pn.getNewResources(counter); for(String newResource:newResources) { - editConfiguration.addNewResource(newResource, ManagePageGenerator.defaultDisplayNs); + //Will null get us display vocabulary or something else? + + editConfiguration.addNewResource(newResource, null); + //Weirdly enough, the defaultDisplayNS doesn't act as a namespace REALLY + //as it first gets assigned as the URI itself and this lead to an error + //instead of repetitively trying to get another URI + //editConfiguration.addNewResource(newResource, ManagePageGenerator.defaultDisplayNs ); } } @@ -130,23 +138,51 @@ public class ManagePagePreprocessor extends List uriLabels = pn.getUriVarNamesBase(); for(String literalLabel:literalLabels) { - //TODO: Deal with multiple submission values - //This retrieves the value for this particular json object - String literalValue = jsonObject.getString(literalLabel); - //Var names will depend on which data getter object this is on the page, so depends on counter + List literalValues = new ArrayList(); + Object jsonValue = jsonObject.get(literalLabel); + //Var names will depend on which data getter object this is on the page, so depends on counter String submissionLiteralName = pn.getVarName(literalLabel, counter); + //Single value + if(jsonValue instanceof String) { + //TODO: Deal with multiple submission values + //This retrieves the value for this particular json object + literalValues.add(jsonObject.getString(literalLabel)); + } else if(jsonValue instanceof JSONArray) { + JSONArray values = jsonObject.getJSONArray(literalLabel); + literalValues = (List) JSONSerializer.toJava(values); + } + String[] literalValuesSubmission = new String[literalValues.size()]; + literalValuesSubmission = literalValues.toArray(literalValuesSubmission); //This adds literal, connecting the field with - submission.addLiteralToForm(editConfiguration, editConfiguration.getField(submissionLiteralName), submissionLiteralName, new String[]{literalValue}); + submission.addLiteralToForm(editConfiguration, + editConfiguration.getField(submissionLiteralName), + submissionLiteralName, + literalValuesSubmission); } for(String uriLabel:uriLabels) { - //TODO: Deal with multiple submission values - //This retrieves the value for this particular json object - String uriValue = jsonObject.getString(uriLabel); - //Var names will depend on which data getter object this is on the page, so depends on counter + List uriValues = new ArrayList(); + Object jsonValue = jsonObject.get(uriLabel); + //Var names will depend on which data getter object this is on the page, so depends on counter String submissionUriName = pn.getVarName(uriLabel, counter); - //This adds literal, connecting the field with - submission.addLiteralToForm(editConfiguration, editConfiguration.getField(submissionUriName), submissionUriName, new String[]{uriValue}); + //if single value, then, add to values + if(jsonValue instanceof String) { + //Var names will depend on which data getter object this is on the page, so depends on counter + //This retrieves the value for this particular json object and adds to list + uriValues.add(jsonObject.getString(uriLabel)); + + } else if(jsonValue instanceof JSONArray) { + //multiple values + JSONArray values = jsonObject.getJSONArray(uriLabel); + uriValues = (List) JSONSerializer.toJava(values); + } else { + //This may include JSON Objects but no way to deal with these right now + } + String[] uriValuesSubmission = new String[uriValues.size()]; + + uriValuesSubmission = uriValues.toArray(uriValuesSubmission); + //This adds literal, connecting the field with the value + submission.addUriToForm(editConfiguration, submissionUriName, uriValuesSubmission); } //this needs to be different diff --git a/webapp/src/edu/cornell/mannlib/vitro/webapp/edit/n3editing/configuration/preprocessors/utils/ProcessClassGroupDataGetterN3.java b/webapp/src/edu/cornell/mannlib/vitro/webapp/edit/n3editing/configuration/preprocessors/utils/ProcessClassGroupDataGetterN3.java new file mode 100644 index 000000000..fcbc2ff6e --- /dev/null +++ b/webapp/src/edu/cornell/mannlib/vitro/webapp/edit/n3editing/configuration/preprocessors/utils/ProcessClassGroupDataGetterN3.java @@ -0,0 +1,95 @@ +/* $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.List; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashMap; +import java.util.Map; + +import com.hp.hpl.jena.rdf.model.Literal; + +import edu.cornell.mannlib.vitro.webapp.dao.DisplayVocabulary; +import edu.cornell.mannlib.vitro.webapp.edit.n3editing.VTwo.fields.FieldVTwo; + +import net.sf.json.JSONObject; +import net.sf.json.JSONSerializer; +//Returns the appropriate n3 based on data getter +public class ProcessClassGroupDataGetterN3 extends ProcessDataGetterAbstract { + private static String classType = "java:edu.cornell.mannlib.vitro.webapp.utils.dataGetter.ClassGroupPageData"; + private static String classGroupVarBase = "classGroup"; + public ProcessClassGroupDataGetterN3(){ + + } + //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 + public List retrieveN3Required(int counter) { + return this.retrieveN3ForTypeAndClassGroup(counter); + + } + public List retrieveN3Optional(int counter) { + return null; + } + + public List retrieveN3ForTypeAndClassGroup(int counter) { + String n3ForType = this.getN3ForTypePartial(counter); + String n3 = n3ForType +"; \n" + + "<" + DisplayVocabulary.FOR_CLASSGROUP + "> " + getN3VarName(classGroupVarBase, counter) + " ."; + List n3List = new ArrayList(); + n3List.add(getPrefixes() + n3); + return n3List; + } + + public String getN3ForTypePartial(int counter) { + String dataGetterVar = getDataGetterVar(counter); + String n3 = dataGetterVar + " a <" + getClassType() + ">"; + return n3; + } + + //These methods will return the literals and uris expected within the n3 + //and the counter is used to ensure they are numbered correctly + + public List retrieveLiteralsOnForm(int counter) { + //no literals, just the class group URI + List literalsOnForm = new ArrayList(); + return literalsOnForm; + + } + + + public List retrieveUrisOnForm(int counter) { + List urisOnForm = new ArrayList(); + //Class group is a URI + urisOnForm.add(getVarName("classGroup", counter)); + return urisOnForm; + + } + + public List retrieveFields(int counter) { + List fields = new ArrayList(); + fields.add(new FieldVTwo().setName(getVarName("classGroup", counter))); + return fields; + } + + //These var names match the names of the elements within the json object returned with the info required for the data getter + + public List getLiteralVarNamesBase() { + return Arrays.asList(); + } + + //these are for the fields ON the form + public List getUriVarNamesBase() { + return Arrays.asList("classGroup"); + } + + //This class can be extended so returning type here + public String getClassType() { + return classType; + } + +} + + diff --git a/webapp/src/edu/cornell/mannlib/vitro/webapp/edit/n3editing/configuration/preprocessors/utils/ProcessDataGetterAbstract.java b/webapp/src/edu/cornell/mannlib/vitro/webapp/edit/n3editing/configuration/preprocessors/utils/ProcessDataGetterAbstract.java new file mode 100644 index 000000000..f17d8f531 --- /dev/null +++ b/webapp/src/edu/cornell/mannlib/vitro/webapp/edit/n3editing/configuration/preprocessors/utils/ProcessDataGetterAbstract.java @@ -0,0 +1,53 @@ +/* $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.List; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashMap; +import java.util.Map; + +import com.hp.hpl.jena.rdf.model.Literal; + +import edu.cornell.mannlib.vitro.webapp.edit.n3editing.VTwo.fields.FieldVTwo; + +import net.sf.json.JSONObject; +import net.sf.json.JSONSerializer; +//Returns the appropriate n3 based on data getter +public abstract class ProcessDataGetterAbstract implements ProcessDataGetterN3 { + + public ProcessDataGetterAbstract(){ + + } + + //placeholder so need "?" in front of the variable + public String getDataGetterVar(int counter) { + return "?dataGetter" + counter; + } + + public String getPrefixes() { + return "@prefix display: . \n" + + "@prefix rdfs: . \n"; + } + + public String getVarName(String base, int counter) { + return base + counter; + } + + //For use within n3 strings, need a "?" + public String getN3VarName(String base, int counter) { + return "?" + getVarName(base, counter); + } + + //Return name of new resources + public List getNewResources(int counter) { + //Each data getter requires a new resource + List newResources = new ArrayList(); + newResources.add("dataGetter" + counter); + return newResources; + } + +} + + diff --git a/webapp/src/edu/cornell/mannlib/vitro/webapp/edit/n3editing/configuration/preprocessors/utils/ProcessDataGetterN3.java b/webapp/src/edu/cornell/mannlib/vitro/webapp/edit/n3editing/configuration/preprocessors/utils/ProcessDataGetterN3.java index a69417d4c..97ca9df78 100644 --- a/webapp/src/edu/cornell/mannlib/vitro/webapp/edit/n3editing/configuration/preprocessors/utils/ProcessDataGetterN3.java +++ b/webapp/src/edu/cornell/mannlib/vitro/webapp/edit/n3editing/configuration/preprocessors/utils/ProcessDataGetterN3.java @@ -10,7 +10,7 @@ import edu.cornell.mannlib.vitro.webapp.edit.n3editing.VTwo.fields.FieldVTwo; //Returns the appropriate n3 based on data getter public interface ProcessDataGetterN3 { - public List retrieveN3Required(int counter); + public List retrieveN3Required(int counter); public List retrieveN3Optional(int counter); public ListretrieveLiteralsOnForm(int counter); diff --git a/webapp/src/edu/cornell/mannlib/vitro/webapp/edit/n3editing/configuration/preprocessors/utils/ProcessDataGetterN3Utils.java b/webapp/src/edu/cornell/mannlib/vitro/webapp/edit/n3editing/configuration/preprocessors/utils/ProcessDataGetterN3Utils.java index ff4a96daf..2026ce7e9 100644 --- a/webapp/src/edu/cornell/mannlib/vitro/webapp/edit/n3editing/configuration/preprocessors/utils/ProcessDataGetterN3Utils.java +++ b/webapp/src/edu/cornell/mannlib/vitro/webapp/edit/n3editing/configuration/preprocessors/utils/ProcessDataGetterN3Utils.java @@ -2,6 +2,7 @@ package edu.cornell.mannlib.vitro.webapp.edit.n3editing.configuration.preprocessors.utils; +import java.lang.reflect.Constructor; import java.util.HashMap; import java.util.Map; @@ -20,6 +21,7 @@ import com.hp.hpl.jena.rdf.model.StmtIterator; import edu.cornell.mannlib.vitro.webapp.dao.VitroVocabulary; import edu.cornell.mannlib.vitro.webapp.dao.jena.ModelContext; import edu.cornell.mannlib.vitro.webapp.edit.n3editing.configuration.preprocessors.utils.ProcessDataGetterN3; +import edu.cornell.mannlib.vitro.webapp.utils.dataGetter.DataGetter; /* * This class determines what n3 should be returned for a particular data getter and can be overwritten or extended in VIVO. @@ -29,18 +31,20 @@ public class ProcessDataGetterN3Utils { public static HashMap getDataGetterTypeToProcessorMap() { HashMap map = new HashMap(); map.put("edu.cornell.mannlib.vitro.webapp.utils.dataGetter.SparqlQueryDataGetter", "edu.cornell.mannlib.vitro.webapp.edit.n3editing.configuration.preprocessors.utils.ProcessSparqlDataGetterN3"); + 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"); + return map; } - public static ProcessDataGetterN3 getDataGetterProcessorN3(String dataGetterClass) { + public static ProcessDataGetterN3 getDataGetterProcessorN3(String dataGetterClass, JSONObject jsonObject) { HashMap map = getDataGetterTypeToProcessorMap(); // if(map.containsKey(dataGetterClass)) { String processorClass = map.get(dataGetterClass); try { - Class clz = Class.forName(processorClass); - //Don't actually need to pass in json object since that includes the actual submission values - ProcessDataGetterN3 pn = (ProcessDataGetterN3) clz.getConstructor().newInstance(); + ProcessDataGetterN3 pn = instantiateClass(processorClass, jsonObject); return pn; } catch(Exception ex) { log.error("Exception occurred in trying to get processor class for n3 for " + dataGetterClass, ex); @@ -50,4 +54,25 @@ public class ProcessDataGetterN3Utils { return null; } + private static ProcessDataGetterN3 instantiateClass(String processorClass, JSONObject jsonObject) { + ProcessDataGetterN3 pn = null; + try { + Class clz = Class.forName(processorClass); + Constructor[] ctList = clz.getConstructors(); + for (Constructor ct: ctList) { + Class[] parameterTypes =ct.getParameterTypes(); + if(parameterTypes.length > 0 && parameterTypes[0].isAssignableFrom(jsonObject.getClass())) { + pn = (ProcessDataGetterN3) ct.newInstance(jsonObject); + } else { + pn = (ProcessDataGetterN3) ct.newInstance(); + } + } + + } catch(Exception ex) { + log.error("Error occurred instantiating " + processorClass, ex); + } + return pn; + + } + } \ No newline at end of file diff --git a/webapp/src/edu/cornell/mannlib/vitro/webapp/edit/n3editing/configuration/preprocessors/utils/ProcessFixedHTMLN3.java b/webapp/src/edu/cornell/mannlib/vitro/webapp/edit/n3editing/configuration/preprocessors/utils/ProcessFixedHTMLN3.java new file mode 100644 index 000000000..01d8a933f --- /dev/null +++ b/webapp/src/edu/cornell/mannlib/vitro/webapp/edit/n3editing/configuration/preprocessors/utils/ProcessFixedHTMLN3.java @@ -0,0 +1,81 @@ +/* $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.List; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashMap; +import java.util.Map; + +import com.hp.hpl.jena.rdf.model.Literal; + +import edu.cornell.mannlib.vitro.webapp.edit.n3editing.VTwo.fields.FieldVTwo; + +import net.sf.json.JSONObject; +import net.sf.json.JSONSerializer; +//Returns the appropriate n3 based on data getter +public class ProcessFixedHTMLN3 extends ProcessDataGetterAbstract { + private static String classType = "java:edu.cornell.mannlib.vitro.webapp.utils.dataGetter.FixedHTMLDataGetter"; + + public ProcessFixedHTMLN3(){ + + } + //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 + public List retrieveN3Required(int counter) { + String dataGetterVar = getDataGetterVar(counter); + String n3 = dataGetterVar + " a <" + classType + ">; \n" + + "display:saveToVar " + getN3VarName("saveToVar", counter) + "; \n" + + "display:htmlValue " + getN3VarName("htmlValue", 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)); + literalsOnForm.add(getVarName("htmlValue", counter)); + return literalsOnForm; + + } + + + public List retrieveUrisOnForm(int counter) { + List urisOnForm = new ArrayList(); + 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("htmlValue", counter))); + + return fields; + } + + public List getLiteralVarNamesBase() { + return Arrays.asList("saveToVar", "htmlValue"); + } + + //these are for the fields ON the form + public List getUriVarNamesBase() { + return Arrays.asList(); + } + + + +} + + diff --git a/webapp/src/edu/cornell/mannlib/vitro/webapp/edit/n3editing/configuration/preprocessors/utils/ProcessIndividualsForClassesDataGetterN3.java b/webapp/src/edu/cornell/mannlib/vitro/webapp/edit/n3editing/configuration/preprocessors/utils/ProcessIndividualsForClassesDataGetterN3.java new file mode 100644 index 000000000..cd7f25b31 --- /dev/null +++ b/webapp/src/edu/cornell/mannlib/vitro/webapp/edit/n3editing/configuration/preprocessors/utils/ProcessIndividualsForClassesDataGetterN3.java @@ -0,0 +1,139 @@ +/* $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.Collection; +import java.util.List; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashMap; +import java.util.Map; + +import com.hp.hpl.jena.rdf.model.Literal; + +import edu.cornell.mannlib.vitro.webapp.dao.DisplayVocabulary; +import edu.cornell.mannlib.vitro.webapp.edit.n3editing.VTwo.fields.FieldVTwo; + +import net.sf.json.JSONArray; +import net.sf.json.JSONObject; +import net.sf.json.JSONSerializer; +//Returns the appropriate n3 for selection of classes from within class group +public class ProcessIndividualsForClassesDataGetterN3 extends ProcessClassGroupDataGetterN3 { + private static String classType = "java:edu.cornell.mannlib.vitro.webapp.utils.dataGetter.IndividualsForClassesDataGetter"; + protected JSONObject values = null; + int classCount = 0; + protected static String individualClassVarNameBase = "classesSelectedInClassGroup"; + public ProcessIndividualsForClassesDataGetterN3(JSONObject jsonObject){ + this.values = jsonObject; + if(values != null && values.containsKey(individualClassVarNameBase)) { + //Check how many individual classes are in json object + JSONArray ja = values.getJSONArray(individualClassVarNameBase); + classCount = ja.size(); + } + } + //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 + public List retrieveN3Required(int counter) { + + List classGroupN3 = this.retrieveN3ForTypeAndClassGroup(counter); + classGroupN3.addAll(this.addIndividualClassesN3(counter)); + return classGroupN3; + + } + + + protected List addIndividualClassesN3(int counter) { + List classN3 = new ArrayList(); + if(classCount > 0) { + classN3.add(generateIndividualClassN3(counter)); + } + return classN3; + } + + protected String generateIndividualClassN3(int counter) { + String dataGetterVar = getDataGetterVar(counter); + String n3 = dataGetterVar + " <" + DisplayVocabulary.GETINDIVIDUALS_FOR_CLASS + "> "; + //Consider a multi-valued field - in this case single field with multiple values + n3 += getN3VarName(individualClassVarNameBase, counter); + /* + int i; + for(i = 0; i < classCount; i++) { + if(i != 0) { + n3+= ","; + } + n3 += getN3VarName(individualClassVarNameBase + counter, classCount); + }*/ + n3 += " ."; + return n3; + + } + public List retrieveN3Optional(int counter) { + return null; + } + + //These methods will return the literals and uris expected within the n3 + //and the counter is used to ensure they are numbered correctly + + public List retrieveLiteralsOnForm(int counter) { + //no literals, just the class group URI + List literalsOnForm = new ArrayList(); + return literalsOnForm; + + } + + + public List retrieveUrisOnForm(int counter) { + //get class group uris + List urisOnForm = super.retrieveUrisOnForm(counter); + //now get individual classes selected uri + //urisOnForm.addAll(getIndividualClassesVarNames(counter)); + //here again,consider multi-valued + urisOnForm.add(getVarName(individualClassVarNameBase, counter)); + return urisOnForm; + + } + + private List getIndividualClassesVarNames(int counter) { + List individualClassUris = new ArrayList(); + int i; + for(i = 0; i < classCount; i++) { + individualClassUris.add(getVarName(individualClassVarNameBase + counter, classCount)); + } + return individualClassUris; + + } + + public List retrieveFields(int counter) { + List fields = super.retrieveFields(counter); + fields.add(new FieldVTwo().setName(getVarName(individualClassVarNameBase, counter))); + //Add fields for each class selected + /* List classVarNames = getIndividualClassesVarNames(counter); + for(String v:classVarNames) { + fields.add(new FieldVTwo().setName(v)); + + }*/ + return fields; + } + + //These var names match the names of the elements within the json object returned with the info required for the data getter + + public List getLiteralVarNamesBase() { + return Arrays.asList(); + } + + //these are for the fields ON the form + public List getUriVarNamesBase() { + return Arrays.asList("classGroup", individualClassVarNameBase); + } + + @Override + public String getClassType() { + return classType; + } + + +} + + diff --git a/webapp/src/edu/cornell/mannlib/vitro/webapp/edit/n3editing/configuration/preprocessors/utils/ProcessSparqlDataGetterN3.java b/webapp/src/edu/cornell/mannlib/vitro/webapp/edit/n3editing/configuration/preprocessors/utils/ProcessSparqlDataGetterN3.java index 37b1d6c43..132cf5307 100644 --- a/webapp/src/edu/cornell/mannlib/vitro/webapp/edit/n3editing/configuration/preprocessors/utils/ProcessSparqlDataGetterN3.java +++ b/webapp/src/edu/cornell/mannlib/vitro/webapp/edit/n3editing/configuration/preprocessors/utils/ProcessSparqlDataGetterN3.java @@ -15,7 +15,7 @@ import edu.cornell.mannlib.vitro.webapp.edit.n3editing.VTwo.fields.FieldVTwo; import net.sf.json.JSONObject; import net.sf.json.JSONSerializer; //Returns the appropriate n3 based on data getter -public class ProcessSparqlDataGetterN3 implements ProcessDataGetterN3 { +public class ProcessSparqlDataGetterN3 extends ProcessDataGetterAbstract { private static String classType = "java:edu.cornell.mannlib.vitro.webapp.utils.dataGetter.SparqlQueryDataGetter"; public ProcessSparqlDataGetterN3(){ @@ -39,15 +39,6 @@ public class ProcessSparqlDataGetterN3 implements ProcessDataGetterN3 { public List retrieveN3Optional(int counter) { return null; } - //placeholder so need "?" in front of the variable - public String getDataGetterVar(int counter) { - return "?dataGetter" + counter; - } - - private String getPrefixes() { - return "@prefix display: . \n" + - "@prefix rdfs: . \n"; - } //Need to add method sfor returning the fields, literals on form, and all that @@ -105,22 +96,7 @@ public class ProcessSparqlDataGetterN3 implements ProcessDataGetterN3 { return Arrays.asList("queryModel"); } - public String getVarName(String base, int counter) { - return base + counter; - } - - //For use within n3 strings, need a "?" - public String getN3VarName(String base, int counter) { - return "?" + getVarName(base, counter); - } - - //Return name of new resources - public List getNewResources(int counter) { - //Each data getter requires a new resource - List newResources = new ArrayList(); - newResources.add("dataGetter" + counter); - return newResources; - } + } diff --git a/webapp/src/edu/cornell/mannlib/vitro/webapp/rdfservice/impl/sdb/RDFServiceSDB.java b/webapp/src/edu/cornell/mannlib/vitro/webapp/rdfservice/impl/sdb/RDFServiceSDB.java index 1e52016d7..64c3b17de 100644 --- a/webapp/src/edu/cornell/mannlib/vitro/webapp/rdfservice/impl/sdb/RDFServiceSDB.java +++ b/webapp/src/edu/cornell/mannlib/vitro/webapp/rdfservice/impl/sdb/RDFServiceSDB.java @@ -13,16 +13,21 @@ import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import com.hp.hpl.jena.graph.Triple; +import com.hp.hpl.jena.query.DataSource; import com.hp.hpl.jena.query.Dataset; +import com.hp.hpl.jena.query.DatasetFactory; 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.query.ResultSetFormatter; import com.hp.hpl.jena.rdf.listeners.StatementListener; import com.hp.hpl.jena.rdf.model.Model; import com.hp.hpl.jena.rdf.model.ModelFactory; +import com.hp.hpl.jena.rdf.model.RDFNode; +import com.hp.hpl.jena.rdf.model.Resource; import com.hp.hpl.jena.rdf.model.Statement; import com.hp.hpl.jena.rdf.model.StmtIterator; import com.hp.hpl.jena.sdb.SDBFactory; @@ -30,11 +35,6 @@ import com.hp.hpl.jena.sdb.Store; import com.hp.hpl.jena.sdb.StoreDesc; import com.hp.hpl.jena.sdb.sql.SDBConnection; import com.hp.hpl.jena.shared.Lock; -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.dao.jena.DatasetWrapper; import edu.cornell.mannlib.vitro.webapp.dao.jena.SparqlGraph; @@ -141,6 +141,7 @@ public class RDFServiceSDB extends RDFServiceImpl implements RDFService { } private void removeBlankNodesWithSparqlUpdate(Dataset dataset, Model model, String graphURI) { + Model blankNodeModel = ModelFactory.createDefaultModel(); StmtIterator stmtIt = model.listStatements(); while (stmtIt.hasNext()) { @@ -149,7 +150,61 @@ public class RDFServiceSDB extends RDFServiceImpl implements RDFService { blankNodeModel.add(stmt); } } - removeUsingSparqlUpdate(dataset, blankNodeModel, graphURI); + + String rootFinder = "SELECT ?s WHERE { ?s ?p ?o OPTIONAL { ?ss ?pp ?s } FILTER (!bound(?ss)) }"; + Query rootFinderQuery = QueryFactory.create(rootFinder); + QueryExecution qe = QueryExecutionFactory.create(rootFinderQuery, blankNodeModel); + try { + ResultSet rs = qe.execSelect(); + while (rs.hasNext()) { + QuerySolution qs = rs.next(); + Resource s = qs.getResource("s"); + String treeFinder = makeDescribe(s); + Query treeFinderQuery = QueryFactory.create(treeFinder); + QueryExecution qee = QueryExecutionFactory.create(treeFinderQuery, blankNodeModel); + try { + Model tree = qee.execDescribe(); + StmtIterator sit = tree.listStatements(s, null, (RDFNode) null); + while (sit.hasNext()) { + Statement stmt = sit.nextStatement(); + RDFNode n = stmt.getObject(); + Model m2 = ModelFactory.createDefaultModel(); + if (n.isResource()) { + Resource s2 = (Resource) n; + // now run yet another describe query + String smallerTree = makeDescribe(s2); + Query smallerTreeQuery = QueryFactory.create(smallerTree); + QueryExecution qe3 = QueryExecutionFactory.create( + smallerTreeQuery, tree); + try { + qe3.execDescribe(m2); + } finally { + qe3.close(); + } + } + m2.add(stmt); + DataSource ds = DatasetFactory.create(); + ds.addNamedModel(graphURI, dataset.getNamedModel(graphURI)); + removeUsingSparqlUpdate(ds, m2, graphURI); + } + } finally { + qee.close(); + } + } + } finally { + qe.close(); + } + } + + private String makeDescribe(Resource s) { + StringBuffer query = new StringBuffer("DESCRIBE <") ; + if (s.isAnon()) { + query.append("_:" + s.getId().toString()); + } else { + query.append(s.getURI()); + } + query.append(">"); + return query.toString(); } private void removeUsingSparqlUpdate(Dataset dataset, Model model, String graphURI) { @@ -173,11 +228,8 @@ public class RDFServiceSDB extends RDFServiceImpl implements RDFService { } StringBuffer queryBuff = new StringBuffer(); - queryBuff.append("DELETE { " + ((graphURI != null) ? "GRAPH <" + graphURI + "> { " : "" ) + " \n"); + queryBuff.append("CONSTRUCT { \n"); queryBuff.append(patternBuff); - if (graphURI != null) { - queryBuff.append(" } \n"); - } queryBuff.append("} WHERE { \n"); if (graphURI != null) { queryBuff.append(" GRAPH <" + graphURI + "> { \n"); @@ -190,10 +242,23 @@ public class RDFServiceSDB extends RDFServiceImpl implements RDFService { //log.debug(queryBuff.toString()); - GraphStore graphStore = GraphStoreFactory.create(dataset); - UpdateRequest request = UpdateFactory.create(); - request.add(queryBuff.toString()); - UpdateAction.execute(request, graphStore); + Query construct = QueryFactory.create(queryBuff.toString()); + // make a plain dataset to force the query to be run in a way that + // won't overwhelm MySQL with too many joins + DataSource ds = DatasetFactory.create(); + ds.addNamedModel(graphURI, (graphURI != null) + ? dataset.getNamedModel(graphURI) : dataset.getDefaultModel()); + QueryExecution qe = QueryExecutionFactory.create(construct, ds); + try { + Model m = qe.execConstruct(); + if (graphURI != null) { + dataset.getNamedModel(graphURI).remove(m); + } else { + dataset.getDefaultModel().remove(m); + } + } finally { + qe.close(); + } } private Model parseModel(ModelChange modelChange) { diff --git a/webapp/src/edu/cornell/mannlib/vitro/webapp/reasoner/SimpleReasoner.java b/webapp/src/edu/cornell/mannlib/vitro/webapp/reasoner/SimpleReasoner.java index 6cb8e322c..6a03d1a34 100644 --- a/webapp/src/edu/cornell/mannlib/vitro/webapp/reasoner/SimpleReasoner.java +++ b/webapp/src/edu/cornell/mannlib/vitro/webapp/reasoner/SimpleReasoner.java @@ -14,6 +14,7 @@ import com.hp.hpl.jena.ontology.AnnotationProperty; import com.hp.hpl.jena.ontology.OntClass; import com.hp.hpl.jena.ontology.OntModel; import com.hp.hpl.jena.ontology.OntModelSpec; +import com.hp.hpl.jena.ontology.OntProperty; import com.hp.hpl.jena.query.Query; import com.hp.hpl.jena.query.QueryExecution; import com.hp.hpl.jena.query.QueryExecutionFactory; @@ -25,6 +26,7 @@ import com.hp.hpl.jena.rdf.listeners.StatementListener; import com.hp.hpl.jena.rdf.model.Literal; import com.hp.hpl.jena.rdf.model.Model; import com.hp.hpl.jena.rdf.model.ModelFactory; +import com.hp.hpl.jena.rdf.model.Property; import com.hp.hpl.jena.rdf.model.RDFNode; import com.hp.hpl.jena.rdf.model.Resource; import com.hp.hpl.jena.rdf.model.ResourceFactory; @@ -32,6 +34,7 @@ import com.hp.hpl.jena.rdf.model.Statement; import com.hp.hpl.jena.rdf.model.StmtIterator; import com.hp.hpl.jena.shared.JenaException; import com.hp.hpl.jena.shared.Lock; +import com.hp.hpl.jena.util.iterator.ExtendedIterator; import com.hp.hpl.jena.vocabulary.OWL; import com.hp.hpl.jena.vocabulary.RDF; import com.hp.hpl.jena.vocabulary.RDFS; @@ -126,12 +129,13 @@ public class SimpleReasoner extends StatementListener { */ @Override public void addedStatement(Statement stmt) { - try { if (stmt.getPredicate().equals(RDF.type)) { addedABoxTypeAssertion(stmt, inferenceModel, new HashSet()); setMostSpecificTypes(stmt.getSubject(), inferenceModel, new HashSet()); - } + } else { + addedABoxAssertion(stmt, inferenceModel); + } doPlugins(ModelUpdate.Operation.ADD,stmt); @@ -150,8 +154,9 @@ public class SimpleReasoner extends StatementListener { public void removedStatement(Statement stmt) { try { - if (!isInterestedInRemovedStatement(stmt)) { return; } - + // if (!isInterestedInRemovedStatement(stmt)) { return; } + // interested in all of them now that we are doing inverse + // property reasoning handleRemovedStatement(stmt); } catch (Exception e) { @@ -160,7 +165,6 @@ public class SimpleReasoner extends StatementListener { } } - /* * Synchronized part of removedStatement. Interacts * with DeltaComputer. @@ -175,7 +179,9 @@ public class SimpleReasoner extends StatementListener { if (stmt.getPredicate().equals(RDF.type)) { removedABoxTypeAssertion(stmt, inferenceModel); setMostSpecificTypes(stmt.getSubject(), inferenceModel, new HashSet()); - } + } else { + removedABoxAssertion(stmt, inferenceModel); + } doPlugins(ModelUpdate.Operation.RETRACT,stmt); } } @@ -184,29 +190,32 @@ public class SimpleReasoner extends StatementListener { * Performs incremental ABox reasoning based * on changes to the class hierarchy. * - * Handles rdfs:subclassOf, owl:equivalentClass, + * Handles rdfs:subclassOf, owl:equivalentClass, and owl:inverseOf */ public void addedTBoxStatement(Statement stmt) { + try { + if (!(stmt.getPredicate().equals(RDFS.subClassOf) || stmt.getPredicate().equals(OWL.equivalentClass) || stmt.getPredicate().equals(OWL.inverseOf))) { + return; + } - try { - log.debug("added TBox assertion = " + stmt.toString()); + log.debug("added TBox assertion = " + stmt.toString()); - if ( stmt.getPredicate().equals(RDFS.subClassOf) || stmt.getPredicate().equals(OWL.equivalentClass) ) { + if ( stmt.getObject().isResource() && (stmt.getObject().asResource()).getURI() == null ) { + log.warn("The object of this assertion has a null URI: " + stmtString(stmt)); + return; + } + + if ( stmt.getSubject().getURI() == null ) { + log.warn("The subject of this assertion has a null URI: " + stmtString(stmt)); + return; + } + + if (stmt.getPredicate().equals(RDFS.subClassOf) || stmt.getPredicate().equals(OWL.equivalentClass)) { // ignore anonymous classes if (stmt.getSubject().isAnon() || stmt.getObject().isAnon()) { return; } - if ( stmt.getObject().isResource() && (stmt.getObject().asResource()).getURI() == null ) { - log.warn("The object of this assertion has a null URI: " + stmtString(stmt)); - return; - } - - if ( stmt.getSubject().getURI() == null ) { - log.warn("The subject of this assertion has a null URI: " + stmtString(stmt)); - return; - } - OntClass subject = tboxModel.getOntClass((stmt.getSubject()).getURI()); if (subject == null) { log.debug("didn't find subject class in the tbox: " + (stmt.getSubject()).getURI()); @@ -220,13 +229,27 @@ public class SimpleReasoner extends StatementListener { } if (stmt.getPredicate().equals(RDFS.subClassOf)) { - addedSubClass(subject,object,inferenceModel); + addedSubClass(subject,object,inferenceModel); } else { - // equivalent class is the same as subclass in both directions - addedSubClass(subject,object,inferenceModel); - addedSubClass(object,subject,inferenceModel); + // equivalent class is the same as subclass in both directions + addedSubClass(subject,object,inferenceModel); + addedSubClass(object,subject,inferenceModel); + } + } else { + OntProperty prop1 = tboxModel.getOntProperty((stmt.getSubject()).getURI()); + if (prop1 == null) { + log.debug("didn't find subject property in the tbox: " + (stmt.getSubject()).getURI()); + return; + } + + OntProperty prop2 = tboxModel.getOntProperty(((Resource)stmt.getObject()).getURI()); + if (prop2 == null) { + log.debug("didn't find object property in the tbox: " + ((Resource)stmt.getObject()).getURI()); + return; } - } + + addedInverseProperty(prop1, prop2, inferenceModel); + } } catch (Exception e) { // don't stop the edit if there's an exception log.error("Exception while adding inference(s): " + e.getMessage()); @@ -237,29 +260,33 @@ public class SimpleReasoner extends StatementListener { * Performs incremental ABox reasoning based * on changes to the class hierarchy. * - * Handles rdfs:subclassOf, owl:equivalentClass, + * Handles rdfs:subclassOf, owl:equivalentClass, and owl:inverseOf */ - public void removedTBoxStatement(Statement stmt) { - + public void removedTBoxStatement(Statement stmt) { try { - log.debug("removed TBox assertion = " + stmt.toString()); + if (!(stmt.getPredicate().equals(RDFS.subClassOf) || stmt.getPredicate().equals(OWL.equivalentClass) || stmt.getPredicate().equals(OWL.inverseOf))) { + return; + } + log.debug("removed TBox assertion = " + stmt.toString()); + + if ( stmt.getObject().isResource() && (stmt.getObject().asResource()).getURI() == null ) { + log.warn("The object of this assertion has a null URI: " + stmtString(stmt)); + return; + } + + if ( stmt.getSubject().getURI() == null ) { + log.warn("The subject of this assertion has a null URI: " + stmtString(stmt)); + return; + } + if ( stmt.getPredicate().equals(RDFS.subClassOf) || stmt.getPredicate().equals(OWL.equivalentClass) ) { + // ignore anonymous classes if (stmt.getSubject().isAnon() || stmt.getObject().isAnon()) { return; } - if ( stmt.getObject().isResource() && (stmt.getObject().asResource()).getURI() == null ) { - log.warn("The object of this assertion has a null URI: " + stmtString(stmt)); - return; - } - - if ( stmt.getSubject().getURI() == null ) { - log.warn("The subject of this assertion has a null URI: " + stmtString(stmt)); - return; - } - OntClass subject = tboxModel.getOntClass((stmt.getSubject()).getURI()); if (subject == null) { log.debug("didn't find subject class in the tbox: " + (stmt.getSubject()).getURI()); @@ -279,7 +306,21 @@ public class SimpleReasoner extends StatementListener { removedSubClass(subject,object,inferenceModel); removedSubClass(object,subject,inferenceModel); } - } + } else { + OntProperty prop1 = tboxModel.getOntProperty((stmt.getSubject()).getURI()); + if (prop1 == null) { + log.debug("didn't find subject property in the tbox: " + (stmt.getSubject()).getURI()); + return; + } + + OntProperty prop2 = tboxModel.getOntProperty(((Resource)stmt.getObject()).getURI()); + if (prop2 == null) { + log.debug("didn't find object property in the tbox: " + ((Resource)stmt.getObject()).getURI()); + return; + } + + removedInverseProperty(prop1, prop2, inferenceModel); + } } catch (Exception e) { // don't stop the edit if there's an exception log.error("Exception while removing inference(s): " + e.getMessage()); @@ -376,6 +417,45 @@ public class SimpleReasoner extends StatementListener { } } + /* + * Performs incremental property-based reasoning. + * + * Materializes inferences based on the owl:inverseOf relationship. + * + * If it is added that x prop1 y, and prop2 is an inverseOf prop1 + * then add y prop2 x to the inference graph, if it is not already in + * the assertions graph. + */ + public void addedABoxAssertion(Statement stmt, Model inferenceModel) { + + List inverseProperties = getInverseProperties(stmt); + Iterator inverseIter = inverseProperties.iterator(); + + while (inverseIter.hasNext()) { + Property inverseProp = inverseIter.next(); + + Statement infStmt = ResourceFactory.createStatement(stmt.getObject().asResource(), inverseProp, stmt.getSubject()); + + aboxModel.enterCriticalSection(Lock.READ); + try { + inferenceModel.enterCriticalSection(Lock.WRITE); + try { + if (inferenceModel.contains(stmt)) { + inferenceModel.remove(stmt); + } + + if (!inferenceModel.contains(infStmt) && !aboxModel.contains(infStmt) ) { + inferenceModel.add(infStmt); + } + } finally { + inferenceModel.leaveCriticalSection(); + } + } finally { + aboxModel.leaveCriticalSection(); + } + } + } + /* * If it is removed that B is of type A, then for each superclass of A remove * the inferred statement that B is of that type UNLESS it is otherwise entailed @@ -386,8 +466,7 @@ public class SimpleReasoner extends StatementListener { tboxModel.enterCriticalSection(Lock.READ); - try { - + try { OntClass cls = null; if ( (stmt.getObject().asResource()).getURI() != null ) { @@ -453,13 +532,50 @@ public class SimpleReasoner extends StatementListener { } } + /* + * Performs incremental property-based reasoning. + * + * Retracts inferences based on the owl:inverseOf relationship. + * + * If it is removed that x prop1 y, and prop2 is an inverseOf prop1 + * then remove y prop2 x from the inference graph, unless it is + * otherwise entailed by the assertions graph independently of + * this removed statement. + */ + public void removedABoxAssertion(Statement stmt, Model inferenceModel) { + List inverseProperties = getInverseProperties(stmt); + Iterator inverseIter = inverseProperties.iterator(); + + while (inverseIter.hasNext()) { + OntProperty inverseProp = inverseIter.next(); + + Statement infStmt = ResourceFactory.createStatement(stmt.getObject().asResource(), inverseProp, stmt.getSubject()); + + inferenceModel.enterCriticalSection(Lock.WRITE); + try { + if (!entailedInverseStmt(infStmt) && inferenceModel.contains(infStmt)) { + inferenceModel.remove(infStmt); + } + + // if a statement has been removed that is otherwise entailed, + // add it to the inference graph. + if (entailedInverseStmt(stmt) && !inferenceModel.contains(stmt)) { + inferenceModel.add(stmt); + } + } finally { + inferenceModel.leaveCriticalSection(); + } + } + } + // Returns true if it is entailed by class subsumption that // subject is of type cls; otherwise returns false. protected boolean entailedType(Resource subject, OntClass cls) { - aboxModel.enterCriticalSection(Lock.READ); + tboxModel.enterCriticalSection(Lock.READ); + aboxModel.enterCriticalSection(Lock.READ); - try { + try { List subclasses = null; subclasses = (cls.listSubClasses(false)).toList(); subclasses.addAll((cls.listEquivalentClasses()).toList()); @@ -477,12 +593,101 @@ public class SimpleReasoner extends StatementListener { } catch (Exception e) { log.debug("exception in method entailedType: " + e.getMessage()); return false; - } finally { + } finally { aboxModel.leaveCriticalSection(); tboxModel.leaveCriticalSection(); } } + + // Returns true if the triple is entailed by inverse property + // reasoning; otherwise returns false. + protected boolean entailedInverseStmt(Statement stmt) { + ExtendedIterator iter = null; + + tboxModel.enterCriticalSection(Lock.READ); + try { + OntProperty prop = tboxModel.getOntProperty(stmt.getPredicate().asResource().getURI()); + iter = prop.listInverse(); + } finally { + tboxModel.leaveCriticalSection(); + } + + aboxModel.enterCriticalSection(Lock.READ); + try { + while (iter.hasNext()) { + Property invProp = iter.next(); + + // not reasoning on properties in the OWL, RDF or RDFS namespace + if ((invProp.getNameSpace()).equals(OWL.NS) || + (invProp.getNameSpace()).equals(RDFS.getURI()) || + (invProp.getNameSpace()).equals(RDF.getURI())) { + continue; + } + + Statement invStmt = ResourceFactory.createStatement(stmt.getObject().asResource(), invProp, stmt.getSubject()); + if (aboxModel.contains(invStmt)) { + return true; + } + } + return false; + } finally { + aboxModel.leaveCriticalSection(); + } + } + /* + * Returns a list of properties that are inverses of the property + * in the given statement. + */ + protected List getInverseProperties(Statement stmt) { + + List inverses = new ArrayList(); + + if (stmt.getSubject().isAnon() || stmt.getObject().isAnon()) { + return inverses; + } + + tboxModel.enterCriticalSection(Lock.READ); + try { + + OntProperty prop = tboxModel.getOntProperty(stmt.getPredicate().getURI()); + + if (prop != null) { + if (!prop.isObjectProperty()) { + return inverses; + } + + if (!stmt.getObject().isResource()) { + log.warn("The predicate of this statement is an object property, but the object is not a resource."); + return inverses; + } + + // not reasoning on properties in the OWL, RDF or RDFS namespace + if ((prop.getNameSpace()).equals(OWL.NS) || + (prop.getNameSpace()).equals(RDFS.getURI()) || + (prop.getNameSpace()).equals(RDF.getURI())) { + return inverses; + } + + ExtendedIterator iter = prop.listInverse(); + + while (iter.hasNext()) { + OntProperty invProp = iter.next(); + + if ((invProp.getNameSpace()).equals(OWL.NS) || + (invProp.getNameSpace()).equals(RDFS.getURI()) || + (invProp.getNameSpace()).equals(RDF.getURI())) { + continue; + } + inverses.add(invProp); + } + } + } finally { + tboxModel.leaveCriticalSection(); + } + + return inverses; + } /* * If it is added that B is a subClass of A, then for each * individual that is typed as B, either in the ABox or in the @@ -566,6 +771,112 @@ public class SimpleReasoner extends StatementListener { } } + /* + * If it is added that P is an inverse of Q, then: + * 1. For each statement involving predicate P in + * the assertions model add the inverse statement + * to the inference model if that inverse is + * in the assertions model. + * + * 2. Repeat the same for predicate Q. + * + */ + public void addedInverseProperty(OntProperty prop1, OntProperty prop2, Model inferenceModel) { + + if ( !prop1.isObjectProperty() || !prop2.isObjectProperty() ) { + log.warn("The subject and object of the inverseOf statement are not both object properties. No inferencing will be performed. property 1: " + prop1.getURI() + " property 2:" + prop2.getURI()); + return; + } + + Model inferences = ModelFactory.createDefaultModel(); + inferences.add(generateInverseInferences(prop1, prop2)); + inferences.add(generateInverseInferences(prop2, prop1)); + + if (inferences.isEmpty()) return; + + aboxModel.enterCriticalSection(Lock.READ); + try { + StmtIterator iter = inferences.listStatements(); + + while (iter.hasNext()) { + Statement infStmt = iter.next(); + + inferenceModel.enterCriticalSection(Lock.WRITE); + try { + if (!inferenceModel.contains(infStmt) && !aboxModel.contains(infStmt)) { + inferenceModel.add(infStmt); + } + } finally { + inferenceModel.leaveCriticalSection(); + } + } + } finally { + aboxModel.leaveCriticalSection(); + } + } + + public Model generateInverseInferences(OntProperty prop, OntProperty inverseProp) { + Model inferences = ModelFactory.createDefaultModel(); + + aboxModel.enterCriticalSection(Lock.READ); + try { + StmtIterator iter = aboxModel.listStatements((Resource) null, prop, (RDFNode) null); + + while (iter.hasNext()) { + Statement stmt = iter.next(); + if (!stmt.getObject().isResource()) continue; + Statement infStmt = ResourceFactory.createStatement(stmt.getObject().asResource(), inverseProp, stmt.getSubject()); + inferences.add(infStmt); + } + } finally { + aboxModel.leaveCriticalSection(); + } + + return inferences; + } + + /* + * If it is removed that P is an inverse of Q, then: + * 1. For each statement involving predicate P in + * the abox assertions model remove the inverse + * statement from the inference model unless + * that statement is otherwise entailed. + * + * 2. Repeat the same for predicate Q. + */ + public void removedInverseProperty(OntProperty prop1, OntProperty prop2, Model inferenceModel) { + + if ( !prop1.isObjectProperty() || !prop2.isObjectProperty() ) { + log.warn("The subject and object of the inverseOf statement are not both object properties. No inferencing will be performed. property 1: " + prop1.getURI() + " property 2:" + prop2.getURI()); + return; + } + + Model inferences = ModelFactory.createDefaultModel(); + inferences.add(generateInverseInferences(prop1, prop2)); + inferences.add(generateInverseInferences(prop2, prop1)); + + if (inferences.isEmpty()) return; + + StmtIterator iter = inferences.listStatements(); + + while (iter.hasNext()) { + Statement infStmt = iter.next(); + + if (entailedInverseStmt(infStmt)) { + continue; + } + + inferenceModel.enterCriticalSection(Lock.WRITE); + try { + if (inferenceModel.contains(infStmt)) { + inferenceModel.remove(infStmt); + } + } finally { + inferenceModel.leaveCriticalSection(); + } + } + } + /* * Find the most specific types (classes) of an individual and * indicate them for the individual with the mostSpecificType @@ -740,13 +1051,12 @@ public class SimpleReasoner extends StatementListener { * inference models. */ protected synchronized void recomputeABox() { - HashSet unknownTypes = new HashSet(); - + // recompute the inferences inferenceRebuildModel.enterCriticalSection(Lock.WRITE); try { - log.info("Computing class-based ABox inferences."); + log.info("Computing ABox inferences."); inferenceRebuildModel.removeAll(); int numStmts = 0; @@ -784,6 +1094,57 @@ public class SimpleReasoner extends StatementListener { log.info("Still computing class-based ABox inferences..."); } + if (stopRequested) { + log.info("a stopRequested signal was received during recomputeABox. Halting Processing."); + return; + } + } + + log.info("Finished computing class-based ABox inferences"); + + Iterator invStatements = null; + tboxModel.enterCriticalSection(Lock.READ); + try { + invStatements = tboxModel.listStatements((Resource) null, OWL.inverseOf, (Resource) null); + } finally { + tboxModel.leaveCriticalSection(); + } + + while (invStatements.hasNext()) { + Statement stmt = invStatements.next(); + + try { + OntProperty prop1 = tboxModel.getOntProperty((stmt.getSubject()).getURI()); + if (prop1 == null) { + log.debug("didn't find subject property in the tbox: " + (stmt.getSubject()).getURI()); + continue; + } + + OntProperty prop2 = tboxModel.getOntProperty(((Resource)stmt.getObject()).getURI()); + if (prop2 == null) { + log.debug("didn't find object property in the tbox: " + ((Resource)stmt.getObject()).getURI()); + continue; + } + + addedInverseProperty(prop1, prop2, inferenceRebuildModel); + } catch (NullPointerException npe) { + log.error("a NullPointerException was received while recomputing the ABox inferences. Halting inference computation."); + return; + } catch (JenaException je) { + if (je.getMessage().equals("Statement models must no be null")) { + log.error("Exception while recomputing ABox inference model. Halting inference computation.", je); + return; + } + log.error("Exception while recomputing ABox inference model: ", je); + } catch (Exception e) { + log.error("Exception while recomputing ABox inference model: ", e); + } + + numStmts++; + if ((numStmts % 10000) == 0) { + log.info("Still computing property-based ABox inferences..."); + } + if (stopRequested) { log.info("a stopRequested signal was received during recomputeABox. Halting Processing."); return; @@ -798,7 +1159,7 @@ public class SimpleReasoner extends StatementListener { inferenceRebuildModel.leaveCriticalSection(); } - log.info("Finished computing class-based ABox inferences"); + log.info("Finished computing property-based ABox inferences"); // reflect the recomputed inferences into the application // inference model. @@ -983,9 +1344,9 @@ public class SimpleReasoner extends StatementListener { } break; } - } catch (Throwable t) { + } catch (Exception e) { log.error("Exception while processing " + (op == ModelUpdate.Operation.ADD ? "an added" : "a removed") + - " statement in SimpleReasoner plugin:" + plugin.getClass().getName() + " -- " + t.getMessage()); + " statement in SimpleReasoner plugin:" + plugin.getClass().getName() + " -- " + e.getMessage()); } } } diff --git a/webapp/src/edu/cornell/mannlib/vitro/webapp/search/controller/PagedSearchController.java b/webapp/src/edu/cornell/mannlib/vitro/webapp/search/controller/PagedSearchController.java index 7b29e95c0..5323223bd 100644 --- a/webapp/src/edu/cornell/mannlib/vitro/webapp/search/controller/PagedSearchController.java +++ b/webapp/src/edu/cornell/mannlib/vitro/webapp/search/controller/PagedSearchController.java @@ -3,6 +3,7 @@ package edu.cornell.mannlib.vitro.webapp.search.controller; import java.io.IOException; +import java.sql.SQLException; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; @@ -51,6 +52,7 @@ import edu.cornell.mannlib.vitro.webapp.search.beans.VitroQueryFactory; import edu.cornell.mannlib.vitro.webapp.search.solr.SolrSetup; import edu.cornell.mannlib.vitro.webapp.web.templatemodels.LinkTemplateModel; import edu.cornell.mannlib.vitro.webapp.web.templatemodels.searchresult.IndividualSearchResult; +import edu.ucsf.vitro.opensocial.OpenSocialManager; /** * Paged search controller that uses Solr @@ -268,7 +270,24 @@ public class PagedSearchController extends FreemarkerHttpServlet { vreq.getServletPath(), pagingLinkParams)); } - String template = templateTable.get(format).get(Result.PAGED); + // VIVO OpenSocial Extension by UCSF + try { + OpenSocialManager openSocialManager = new OpenSocialManager(vreq, "search"); + // put list of people found onto pubsub channel + List ids = OpenSocialManager.getOpenSocialId(individuals); + openSocialManager.setPubsubData(OpenSocialManager.JSON_PERSONID_CHANNEL, + OpenSocialManager.buildJSONPersonIds(ids, "" + ids.size() + " people found")); + body.put("openSocial", openSocialManager); + if (openSocialManager.isVisible()) { + body.put("bodyOnload", "my.init();"); + } + } catch (IOException e) { + log.error("IOException in doTemplate()", e); + } catch (SQLException e) { + log.error("SQLException in doTemplate()", e); + } + + String template = templateTable.get(format).get(Result.PAGED); return new TemplateResponseValues(template, body); } catch (Throwable e) { diff --git a/webapp/src/edu/cornell/mannlib/vitro/webapp/servlet/setup/UserModelSetup.java b/webapp/src/edu/cornell/mannlib/vitro/webapp/servlet/setup/UserModelSetup.java index 8f0f25621..d7688149c 100644 --- a/webapp/src/edu/cornell/mannlib/vitro/webapp/servlet/setup/UserModelSetup.java +++ b/webapp/src/edu/cornell/mannlib/vitro/webapp/servlet/setup/UserModelSetup.java @@ -23,6 +23,7 @@ import edu.cornell.mannlib.vitro.webapp.startup.StartupStatus; */ public class UserModelSetup extends JenaDataSourceSetupBase implements ServletContextListener { + protected static String AUTHPATH = BASE + "auth/"; private static final Log log = LogFactory.getLog( UserModelSetup.class.getName()); @@ -55,7 +56,12 @@ public class UserModelSetup extends JenaDataSourceSetupBase OntModel userAccountsModel = ModelFactory.createOntologyModel( MEM_ONT_MODEL_SPEC); - userAccountsModel.add(userAccountsDbModel); + // This is used in Selenium testing, when we load user accounts from a file. + if (userAccountsDbModel.isEmpty()) { + readOntologyFilesInPathSet(AUTHPATH, ctx, userAccountsDbModel); + } + + userAccountsModel.add(userAccountsDbModel); userAccountsModel.getBaseModel().register( new ModelSynchronizer(userAccountsDbModel)); ctx.setAttribute("userAccountsOntModel", userAccountsModel); @@ -65,5 +71,4 @@ public class UserModelSetup extends JenaDataSourceSetupBase ss.fatal(this, "Unable to load user accounts model from DB", t); } } - } diff --git a/webapp/src/edu/cornell/mannlib/vitro/webapp/utils/dataGetter/FixedHTMLDataGetter.java b/webapp/src/edu/cornell/mannlib/vitro/webapp/utils/dataGetter/FixedHTMLDataGetter.java new file mode 100644 index 000000000..afa3b2f6e --- /dev/null +++ b/webapp/src/edu/cornell/mannlib/vitro/webapp/utils/dataGetter/FixedHTMLDataGetter.java @@ -0,0 +1,128 @@ +/* $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.ArrayList; +import java.util.Collections; +import java.util.HashMap; +import java.util.Iterator; +import java.util.List; +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.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.QuerySolutionMap; +import com.hp.hpl.jena.query.ResultSet; +import com.hp.hpl.jena.rdf.model.Literal; +import com.hp.hpl.jena.rdf.model.Model; +import com.hp.hpl.jena.rdf.model.RDFNode; +import com.hp.hpl.jena.rdf.model.Resource; +import com.hp.hpl.jena.rdf.model.ResourceFactory; +import com.hp.hpl.jena.shared.Lock; + +import edu.cornell.mannlib.vitro.webapp.controller.VitroRequest; +import edu.cornell.mannlib.vitro.webapp.dao.DisplayVocabulary; + +public class FixedHTMLDataGetter extends DataGetterBase implements DataGetter{ + String dataGetterURI; + String htmlValue; + String saveToVar; + VitroRequest vreq; + ServletContext context; + private final static String defaultTemplate = "menupage--defaultFixedHtml.ftl"; + + final static Log log = LogFactory.getLog(FixedHTMLDataGetter.class); + + /** + * Constructor with display model and data getter URI that will be called by reflection. + */ + public FixedHTMLDataGetter(VitroRequest vreq, Model displayModel, String dataGetterURI){ + this.configure(vreq, displayModel,dataGetterURI); + } + + @Override + public Map getData(Map pageData) { + Map rmap = new HashMap(); + rmap.put("variableName", this.saveToVar); + rmap.put(this.saveToVar, this.htmlValue); + //this is the default template set here - overridden by page level template if there is one + rmap.put("bodyTemplate", defaultTemplate); + return rmap; + } + + /** + * Configure this instance based on the URI and display model. + */ + protected void configure(VitroRequest vreq, Model displayModel, String dataGetterURI) { + if( vreq == null ) + throw new IllegalArgumentException("VitroRequest may not be null."); + if( displayModel == null ) + throw new IllegalArgumentException("Display Model may not be null."); + if( dataGetterURI == null ) + throw new IllegalArgumentException("PageUri may not be null."); + + this.vreq = vreq; + this.context = vreq.getSession().getServletContext(); + this.dataGetterURI = dataGetterURI; + + QuerySolutionMap initBindings = new QuerySolutionMap(); + initBindings.add("dataGetterURI", ResourceFactory.createResource(this.dataGetterURI)); + + int count = 0; + Query dataGetterConfigurationQuery = QueryFactory.create(dataGetterQuery) ; + displayModel.enterCriticalSection(Lock.READ); + try{ + QueryExecution qexec = QueryExecutionFactory.create( + dataGetterConfigurationQuery, displayModel, initBindings) ; + ResultSet res = qexec.execSelect(); + try{ + while( res.hasNext() ){ + count++; + QuerySolution soln = res.next(); + + // is NOT OPTIONAL + Literal value = soln.getLiteral("htmlValue"); + if( dataGetterConfigurationQuery == null ) + log.error("no html value defined for page " + this.dataGetterURI); + else + this.htmlValue = value.getLexicalForm(); + + + //saveToVar is OPTIONAL + Literal saveTo = soln.getLiteral("saveToVar"); + if( saveTo != null && saveTo.isLiteral() ){ + this.saveToVar = saveTo.asLiteral().getLexicalForm(); + }else{ + this.saveToVar = defaultVarNameForResults; + } + } + }finally{ qexec.close(); } + }finally{ displayModel.leaveCriticalSection(); } + } + + + + private static final String saveToVarPropertyURI= "<" + DisplayVocabulary.SAVE_TO_VAR+ ">"; + private static final String htmlValuePropertyURI= "<" + DisplayVocabulary.FIXED_HTML_VALUE+ ">"; + + public static final String defaultVarNameForResults = "results"; + + /** + * Query to get the definition of the SparqlDataGetter for a given URI. + */ + private static final String dataGetterQuery = + "PREFIX display: <" + DisplayVocabulary.DISPLAY_NS +"> \n" + + "SELECT ?saveToVar ?htmlValue WHERE { \n" + + " ?dataGetterURI "+saveToVarPropertyURI+" ?saveToVar . \n" + + " ?dataGetterURI "+htmlValuePropertyURI+" ?htmlValue . \n" + + "}"; + + +} diff --git a/webapp/src/edu/cornell/mannlib/vitro/webapp/utils/dataGetter/IndividualsForClassesDataGetter.java b/webapp/src/edu/cornell/mannlib/vitro/webapp/utils/dataGetter/IndividualsForClassesDataGetter.java index 1d5c10a3d..b6c16a6ff 100644 --- a/webapp/src/edu/cornell/mannlib/vitro/webapp/utils/dataGetter/IndividualsForClassesDataGetter.java +++ b/webapp/src/edu/cornell/mannlib/vitro/webapp/utils/dataGetter/IndividualsForClassesDataGetter.java @@ -47,7 +47,8 @@ public class IndividualsForClassesDataGetter extends DataGetterBase implements D String dataGetterURI; String classGroupURI; Map classIntersectionsMap; - + private final static String defaultTemplate = "page-classgroup.ftl"; + /** * Constructor with display model and data getter URI that will be called by reflection. */ @@ -132,6 +133,8 @@ public class IndividualsForClassesDataGetter extends DataGetterBase implements D data.put("dataServiceUrlIndividualsByVClass", this.getDataServiceUrl()); //this is the class group associated with the data getter utilized for display on menu editing, not the custom one created data.put("classGroupUri",this.classGroupURI); + //default template, overridden at page level if specified in display model + data.put("bodyTemplate", defaultTemplate); } catch(Exception ex) { log.error("An error occurred retrieving Vclass Intersection individuals", ex); } diff --git a/webapp/src/edu/cornell/mannlib/vitro/webapp/utils/dataGetter/SparqlQueryDataGetter.java b/webapp/src/edu/cornell/mannlib/vitro/webapp/utils/dataGetter/SparqlQueryDataGetter.java index ebfe75890..058de06aa 100644 --- a/webapp/src/edu/cornell/mannlib/vitro/webapp/utils/dataGetter/SparqlQueryDataGetter.java +++ b/webapp/src/edu/cornell/mannlib/vitro/webapp/utils/dataGetter/SparqlQueryDataGetter.java @@ -37,9 +37,11 @@ public class SparqlQueryDataGetter extends DataGetterBase implements DataGetter{ String modelURI; VitroRequest vreq; ServletContext context; - + final static Log log = LogFactory.getLog(SparqlQueryDataGetter.class); + //default template + private final static String defaultTemplate = "menupage--defaultSparql.ftl"; /** * Constructor with display model and data getter URI that will be called by reflection. @@ -134,7 +136,9 @@ public class SparqlQueryDataGetter extends DataGetterBase implements DataGetter{ //put results in page data, what key to use for results? Map rmap = new HashMap(); - rmap.put(this.saveToVar, results); + rmap.put(this.saveToVar, results); + //This will be overridden at page level in display model if template specified there + rmap.put("bodyTemplate", defaultTemplate); return rmap; } diff --git a/webapp/src/edu/cornell/mannlib/vitro/webapp/web/templatemodels/customlistview/DataPropertyListConfig.java b/webapp/src/edu/cornell/mannlib/vitro/webapp/web/templatemodels/customlistview/DataPropertyListConfig.java new file mode 100644 index 000000000..b88b9ab38 --- /dev/null +++ b/webapp/src/edu/cornell/mannlib/vitro/webapp/web/templatemodels/customlistview/DataPropertyListConfig.java @@ -0,0 +1,159 @@ +/* $This file is distributed under the terms of the license in /doc/license.txt$ */ + +package edu.cornell.mannlib.vitro.webapp.web.templatemodels.customlistview; + +import java.io.File; +import java.io.FileReader; +import java.io.IOException; +import java.lang.reflect.Constructor; +import java.util.Set; + +import org.apache.commons.lang.StringUtils; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + +import edu.cornell.mannlib.vitro.webapp.beans.DataProperty; +import edu.cornell.mannlib.vitro.webapp.controller.VitroRequest; +import edu.cornell.mannlib.vitro.webapp.dao.WebappDaoFactory; +import edu.cornell.mannlib.vitro.webapp.web.templatemodels.individual.DataPropertyTemplateModel; +import edu.cornell.mannlib.vitro.webapp.web.templatemodels.individual.DataPropertyTemplateModel.ConfigError; +import freemarker.cache.TemplateLoader; +import freemarker.template.Configuration; + +public class DataPropertyListConfig { + private static final Log log = LogFactory.getLog(DataPropertyListConfig.class); + + + private static final String CONFIG_FILE_PATH = "/config/"; + private static final String DEFAULT_CONFIG_FILE_NAME = "listViewConfig-dataDefault.xml"; + + /* NB The default post-processor is not the same as the post-processor for the default view. The latter + * actually defines its own post-processor, whereas the default post-processor is used for custom views + * that don't define a post-processor, to ensure that the standard post-processing applies. + * + * edu.cornell.mannlib.vitro.webapp.web.templatemodels.individual.DefaultObjectPropertyDataPostProcessor + */ + + // TODO Lump these together into the PropertyListConfigContext + private final DataPropertyTemplateModel dptm; + private final VitroRequest vreq; + private final TemplateLoader templateLoader; + + private boolean isDefaultConfig; + private Set constructQueries; + private String selectQuery; + private String templateName; + + public DataPropertyListConfig(DataPropertyTemplateModel dptm, TemplateLoader templateLoader, VitroRequest vreq, + DataProperty dp, boolean editing) + throws InvalidConfigurationException { + + this.dptm = dptm; + this.vreq = vreq; + WebappDaoFactory wadf = vreq.getWebappDaoFactory(); + this.templateLoader = templateLoader; + + // Get the custom config filename + String configFileName = wadf.getDataPropertyDao().getCustomListViewConfigFileName(dp); + if (configFileName == null) { // no custom config; use default config + configFileName = DEFAULT_CONFIG_FILE_NAME; + } + log.debug("Using list view config file " + configFileName + " for data property " + dp.getURI()); + + String configFilePath = getConfigFilePath(configFileName); + + try { + File config = new File(configFilePath); + if ( ! isDefaultConfig(configFileName) && ! config.exists() ) { + log.warn("Can't find config file " + configFilePath + " for data property " + dp.getURI() + "\n" + + ". Using default config file instead."); + configFilePath = getConfigFilePath(DEFAULT_CONFIG_FILE_NAME); + // Should we test for the existence of the default, and throw an error if it doesn't exist? + } + setValuesFromConfigFile(configFilePath, wadf, editing); + + } catch (Exception e) { + log.error("Error processing config file " + configFilePath + " for data property " + dp.getURI(), e); + // What should we do here? + } + + if ( ! isDefaultConfig(configFileName) ) { + ConfigError configError = checkConfiguration(); + if ( configError != null ) { // the configuration contains an error + log.warn("Invalid list view config for data property " + dp.getURI() + + " in " + configFilePath + ":\n" + + configError + " Using default config instead."); + configFilePath = getConfigFilePath(DEFAULT_CONFIG_FILE_NAME); + setValuesFromConfigFile(configFilePath, wadf, editing); + } + } + + isDefaultConfig = isDefaultConfig(configFileName); + } + + private boolean isDefaultConfig(String configFileName) { + return configFileName.equals(DEFAULT_CONFIG_FILE_NAME); + } + + private ConfigError checkConfiguration() { + + ConfigError error = dptm.checkQuery(selectQuery); + if (error != null) { + return error; + } + + if (StringUtils.isBlank(selectQuery)) { + return ConfigError.NO_SELECT_QUERY; + } + + if ( StringUtils.isBlank(templateName)) { + return ConfigError.NO_TEMPLATE; + } + + try { + if ( templateLoader.findTemplateSource(templateName) == null ) { + return ConfigError.TEMPLATE_NOT_FOUND; + } + } catch (IOException e) { + log.error("Error finding template " + templateName, e); + } + + return null; + } + + private void setValuesFromConfigFile(String configFilePath, WebappDaoFactory wdf, + boolean editing) { + try { + FileReader reader = new FileReader(configFilePath); + CustomListViewConfigFile configFileContents = new CustomListViewConfigFile(reader); + + selectQuery = configFileContents.getSelectQuery(false, editing); + templateName = configFileContents.getTemplateName(); + constructQueries = configFileContents.getConstructQueries(); + + } catch (Exception e) { + log.error("Error processing config file " + configFilePath, e); + } + } + + private String getConfigFilePath(String filename) { + return vreq.getSession().getServletContext().getRealPath(CONFIG_FILE_PATH + filename); + } + + public String getSelectQuery() { + return this.selectQuery; + } + + public Set getConstructQueries() { + return this.constructQueries; + } + + public String getTemplateName() { + return this.templateName; + } + + public boolean isDefaultListView() { + return this.isDefaultConfig; + } + +} \ No newline at end of file diff --git a/webapp/src/edu/cornell/mannlib/vitro/webapp/web/templatemodels/individual/DataPropertyStatementTemplateModel.java b/webapp/src/edu/cornell/mannlib/vitro/webapp/web/templatemodels/individual/DataPropertyStatementTemplateModel.java index f9fa1bf1a..47b7bfbac 100644 --- a/webapp/src/edu/cornell/mannlib/vitro/webapp/web/templatemodels/individual/DataPropertyStatementTemplateModel.java +++ b/webapp/src/edu/cornell/mannlib/vitro/webapp/web/templatemodels/individual/DataPropertyStatementTemplateModel.java @@ -25,13 +25,15 @@ public class DataPropertyStatementTemplateModel extends PropertyStatementTemplat private final Literal literalValue; private final String deleteUrl; private final String editUrl; - + private final String templateName; + //Extended to include vitro request to check for special parameters - public DataPropertyStatementTemplateModel(String subjectUri, String propertyUri, - Literal literal, VitroRequest vreq) { + public DataPropertyStatementTemplateModel(String subjectUri, String propertyUri, Literal literal, + String templateName, VitroRequest vreq) { super(subjectUri, propertyUri, vreq); this.literalValue = literal; + this.templateName = templateName; // Do delete url first, since used in building edit url this.deleteUrl = makeDeleteUrl(); @@ -52,6 +54,7 @@ public class DataPropertyStatementTemplateModel extends PropertyStatementTemplat "datapropKey", makeHash(dps), "cmd", "delete"); + params.put("templateName", templateName); params.putAll(UrlBuilder.getModelParams(vreq)); return UrlBuilder.getUrl(EDIT_PATH, params); diff --git a/webapp/src/edu/cornell/mannlib/vitro/webapp/web/templatemodels/individual/DataPropertyTemplateModel.java b/webapp/src/edu/cornell/mannlib/vitro/webapp/web/templatemodels/individual/DataPropertyTemplateModel.java index 2452851ff..f5036463c 100644 --- a/webapp/src/edu/cornell/mannlib/vitro/webapp/web/templatemodels/individual/DataPropertyTemplateModel.java +++ b/webapp/src/edu/cornell/mannlib/vitro/webapp/web/templatemodels/individual/DataPropertyTemplateModel.java @@ -4,7 +4,11 @@ package edu.cornell.mannlib.vitro.webapp.web.templatemodels.individual; import java.util.ArrayList; import java.util.List; +import java.util.Set; +import java.util.regex.Matcher; +import java.util.regex.Pattern; +import org.apache.commons.lang.StringUtils; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; @@ -17,11 +21,15 @@ import edu.cornell.mannlib.vitro.webapp.beans.DataProperty; import edu.cornell.mannlib.vitro.webapp.beans.Individual; import edu.cornell.mannlib.vitro.webapp.beans.Property; import edu.cornell.mannlib.vitro.webapp.controller.VitroRequest; +import edu.cornell.mannlib.vitro.webapp.controller.freemarker.FreemarkerConfigurationLoader; import edu.cornell.mannlib.vitro.webapp.controller.freemarker.UrlBuilder; import edu.cornell.mannlib.vitro.webapp.controller.freemarker.UrlBuilder.ParamMap; import edu.cornell.mannlib.vitro.webapp.controller.freemarker.UrlBuilder.Route; import edu.cornell.mannlib.vitro.webapp.dao.DataPropertyStatementDao; import edu.cornell.mannlib.vitro.webapp.dao.VitroVocabulary; +import edu.cornell.mannlib.vitro.webapp.web.templatemodels.customlistview.InvalidConfigurationException; +import edu.cornell.mannlib.vitro.webapp.web.templatemodels.customlistview.DataPropertyListConfig; +import freemarker.cache.TemplateLoader; public class DataPropertyTemplateModel extends PropertyTemplateModel { @@ -31,6 +39,33 @@ public class DataPropertyTemplateModel extends PropertyTemplateModel { private static final String EDIT_PATH = "editRequestDispatch"; private final List statements; + private static final String KEY_SUBJECT = "subject"; + private static final String KEY_PROPERTY = "property"; + + public static enum ConfigError { + NO_SELECT_QUERY("Missing select query specification"), + NO_TEMPLATE("Missing template specification"), + TEMPLATE_NOT_FOUND("Specified template does not exist"); + + String message; + + ConfigError(String message) { + this.message = message; + } + + public String getMessage() { + return message; + } + + public String toString() { + return getMessage(); + } + } + + private DataPropertyListConfig config; + private String objectKey; + private String queryString; + private Set constructQueries; DataPropertyTemplateModel(DataProperty dp, Individual subject, VitroRequest vreq, boolean editing, List populatedDataPropertyList) { @@ -38,15 +73,25 @@ public class DataPropertyTemplateModel extends PropertyTemplateModel { super(dp, subject, vreq); setName(dp.getPublicName()); + // Get the config for this data property + try { + config = new DataPropertyListConfig(this, getFreemarkerTemplateLoader(), vreq, dp, editing); + } catch (Exception e) { + log.error(e, e); + } + + queryString = getSelectQuery(); + constructQueries = getConstructQueries(); + statements = new ArrayList(); // If the property is populated, get the data property statements via a sparql query if (populatedDataPropertyList.contains(dp)) { log.debug("Getting data for populated data property " + getUri()); DataPropertyStatementDao dpDao = vreq.getWebappDaoFactory().getDataPropertyStatementDao(); - List values = dpDao.getDataPropertyValuesForIndividualByProperty(subject, dp); + List values = dpDao.getDataPropertyValuesForIndividualByProperty(subject, dp, queryString, constructQueries); for (Literal value : values) { - statements.add(new DataPropertyStatementTemplateModel(subjectUri, propertyUri, value, vreq)); + statements.add(new DataPropertyStatementTemplateModel(subjectUri, propertyUri, value, getTemplateName(), vreq)); } } else { log.debug("Data property " + getUri() + " is unpopulated."); @@ -57,7 +102,6 @@ public class DataPropertyTemplateModel extends PropertyTemplateModel { } } - protected void setAddUrl(Property property) { DataProperty dp = (DataProperty) property; @@ -91,6 +135,10 @@ public class DataPropertyTemplateModel extends PropertyTemplateModel { addUrl = UrlBuilder.getUrl(EDIT_PATH, params); } + protected TemplateLoader getFreemarkerTemplateLoader() { + return FreemarkerConfigurationLoader.getConfig(vreq).getTemplateLoader(); + } + @Override protected int getPropertyDisplayTier(Property p) { return ((DataProperty)p).getDisplayTier(); @@ -101,6 +149,37 @@ public class DataPropertyTemplateModel extends PropertyTemplateModel { return Route.DATA_PROPERTY_EDIT; } + public ConfigError checkQuery(String queryString) { + if (StringUtils.isBlank(queryString)) { + return ConfigError.NO_SELECT_QUERY; + } + return null; + } + + private String getSelectQuery() { + return config.getSelectQuery(); + } + + private Set getConstructQueries() { + return config.getConstructQueries(); + } + + protected String getTemplateName() { + return config.getTemplateName(); + } + + protected boolean hasDefaultListView() { + return config.isDefaultListView(); + } + + protected String getObjectKey() { + return objectKey; + } + + protected boolean isEmpty() { + return statements.isEmpty(); + } + /* Template properties */ public String getType() { @@ -111,6 +190,9 @@ public class DataPropertyTemplateModel extends PropertyTemplateModel { return statements; } + public String getTemplate() { + return getTemplateName(); + } /* Template methods */ diff --git a/webapp/src/edu/cornell/mannlib/vitro/webapp/web/templatemodels/individual/ObjectPropertyStatementTemplateModel.java b/webapp/src/edu/cornell/mannlib/vitro/webapp/web/templatemodels/individual/ObjectPropertyStatementTemplateModel.java index 28ff330c3..f68441b96 100644 --- a/webapp/src/edu/cornell/mannlib/vitro/webapp/web/templatemodels/individual/ObjectPropertyStatementTemplateModel.java +++ b/webapp/src/edu/cornell/mannlib/vitro/webapp/web/templatemodels/individual/ObjectPropertyStatementTemplateModel.java @@ -34,7 +34,7 @@ public class ObjectPropertyStatementTemplateModel extends PropertyStatementTempl public ObjectPropertyStatementTemplateModel(String subjectUri, String propertyUri, String objectKey, Map data, String templateName, VitroRequest vreq) { super(subjectUri, propertyUri, vreq); - + this.data = Collections.unmodifiableMap(new HashMap(data)); this.objectUri = data.get(objectKey); this.templateName = templateName; diff --git a/webapp/src/edu/ucsf/vitro/opensocial/GadgetController.java b/webapp/src/edu/ucsf/vitro/opensocial/GadgetController.java new file mode 100644 index 000000000..89d6cea00 --- /dev/null +++ b/webapp/src/edu/ucsf/vitro/opensocial/GadgetController.java @@ -0,0 +1,100 @@ +package edu.ucsf.vitro.opensocial; + +import java.io.IOException; +import java.sql.SQLException; +import java.util.HashMap; +import java.util.Map; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + +import edu.cornell.mannlib.vitro.webapp.config.ConfigurationProperties; +import edu.cornell.mannlib.vitro.webapp.controller.VitroRequest; +import edu.cornell.mannlib.vitro.webapp.controller.freemarker.FreemarkerHttpServlet; +import edu.cornell.mannlib.vitro.webapp.controller.freemarker.responsevalues.ExceptionResponseValues; +import edu.cornell.mannlib.vitro.webapp.controller.freemarker.responsevalues.RedirectResponseValues; +import edu.cornell.mannlib.vitro.webapp.controller.freemarker.responsevalues.ResponseValues; +import edu.cornell.mannlib.vitro.webapp.controller.freemarker.responsevalues.TemplateResponseValues; + +public class GadgetController extends FreemarkerHttpServlet { + + private static final long serialVersionUID = 1L; + private static final Log log = LogFactory.getLog(GadgetController.class); + + @Override + protected ResponseValues processRequest(VitroRequest vreq) { + if (vreq.getServletPath().endsWith("/sandbox")) { + boolean sandbox = "True".equalsIgnoreCase(ConfigurationProperties.getBean(vreq.getSession() + .getServletContext()).getProperty("OpenSocial.sandbox")); + if (!sandbox) { + return new ExceptionResponseValues( new Exception("Sandbox not available")); + } + return processGadgetSandbox(vreq); + } + else { + return processGadgetDetails(vreq); + } + } + + protected ResponseValues processGadgetDetails(VitroRequest vreq) { + try { + Map body = new HashMap(); + + body.put("title", "Gadget Details"); + // VIVO OpenSocial Extension by UCSF + try { + OpenSocialManager openSocialManager = new OpenSocialManager(vreq, "gadgetDetails"); + body.put(OpenSocialManager.TAG_NAME, openSocialManager); + if (openSocialManager.isVisible()) { + body.put("bodyOnload", "my.init();"); + } + } catch (IOException e) { + log.error("IOException in doTemplate()", e); + } catch (SQLException e) { + log.error("SQLException in doTemplate()", e); + } + + return new TemplateResponseValues("gadgetDetails.ftl", body); + + } catch (Throwable e) { + log.error(e, e); + return new ExceptionResponseValues(e); + } + } + + @Override + protected String getTitle(String siteName, VitroRequest vreq) { + return "Gadget Details"; + } + + protected ResponseValues processGadgetSandbox(VitroRequest vreq) { + if ("POST".equalsIgnoreCase(vreq.getMethod())) { + vreq.getSession().setAttribute(OpenSocialManager.OPENSOCIAL_GADGETS, vreq.getParameter("gadgetURLS")); + vreq.getSession().setAttribute(OpenSocialManager.OPENSOCIAL_DEBUG, vreq.getParameter("debug") != null); + vreq.getSession().setAttribute(OpenSocialManager.OPENSOCIAL_NOCACHE, vreq.getParameter("useCache") == null); + return new RedirectResponseValues("/"); + } + + Map body = new HashMap(); + body.put("title", "Gadget Sandbox"); + + try { + OpenSocialManager openSocialManager = new OpenSocialManager(vreq, "gadgetSandbox"); + String gadgetURLS = ""; + for (PreparedGadget gadget : openSocialManager.getVisibleGadgets()) + { + gadgetURLS += gadget.getGadgetURL() + System.getProperty("line.separator"); + } + body.put("gadgetURLS", gadgetURLS); + body.put(OpenSocialManager.TAG_NAME, openSocialManager); + } catch (IOException e) { + log.error("IOException in doTemplate()", e); + } catch (SQLException e) { + log.error("SQLException in doTemplate()", e); + } + + + return new TemplateResponseValues("gadgetLogin.ftl", body); + } + +} diff --git a/webapp/src/edu/ucsf/vitro/opensocial/GadgetSpec.java b/webapp/src/edu/ucsf/vitro/opensocial/GadgetSpec.java new file mode 100644 index 000000000..165fdf1c4 --- /dev/null +++ b/webapp/src/edu/ucsf/vitro/opensocial/GadgetSpec.java @@ -0,0 +1,204 @@ +package edu.ucsf.vitro.opensocial; + +import java.sql.Connection; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.sql.Statement; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import org.apache.commons.dbcp.BasicDataSource; + +public class GadgetSpec { + private String openSocialGadgetURL; + private String name; + private int appId = 0; + private List channels = new ArrayList(); + private boolean unknownGadget = false; + private Map viewRequirements = new HashMap(); + + // For preloading + public GadgetSpec(int appId, String name, String openSocialGadgetURL, + List channels) { + this.appId = appId; + this.name = name; + this.openSocialGadgetURL = openSocialGadgetURL; + this.channels.addAll(channels); + } + + public GadgetSpec(int appId, String name, String openSocialGadgetURL, + String channelsStr) { + this(appId, name, openSocialGadgetURL, Arrays.asList(channelsStr != null + && channelsStr.length() > 0 ? channelsStr.split(" ") : new String[0])); + } + + public GadgetSpec(int appId, String name, String openSocialGadgetURL, + List channels, boolean unknownGadget, BasicDataSource ds) + throws SQLException { + this(appId, name, openSocialGadgetURL, channels); + this.unknownGadget = unknownGadget; + // Load gadgets from the DB first + if (!unknownGadget) { + Connection conn = null; + Statement stmt = null; + ResultSet rset = null; + + try { + String sqlCommand = "select page, viewer_req, owner_req, view, closed_width, open_width, start_closed, chromeId, display_order from shindig_app_views where appId = " + + appId; + conn = ds.getConnection(); + stmt = conn.createStatement(); + rset = stmt.executeQuery(sqlCommand); + while (rset.next()) { + viewRequirements.put( + rset.getString(1), + new GadgetViewRequirements(rset.getString(1), rset + .getString(2), rset.getString(3), rset + .getString(4), rset.getInt(5), rset + .getInt(6), rset.getBoolean(7), rset + .getString(8), rset.getInt(9))); + } + } finally { + try { + if (rset != null) { + rset.close(); + } + } catch (Exception e) { + e.printStackTrace(); + } + try { + if (stmt != null) { + stmt.close(); + } + } catch (Exception e) { + e.printStackTrace(); + } + try { + if (conn != null) { + conn.close(); + } + } catch (Exception e) { + e.printStackTrace(); + } + } + } + } + + public int getAppId() { + return appId; + } + + public String getName() { + return name; + } + + public String getGadgetURL() { + return openSocialGadgetURL; + } + + public List getChannels() { + return channels; + } + + public boolean listensTo(String channel) { // if an unknown gadget just say yes, + // we don't care about + // performance in this situation + return unknownGadget || channels.contains(channel); + } + + public GadgetViewRequirements getGadgetViewRequirements(String page) { + if (viewRequirements.containsKey(page)) { + return viewRequirements.get(page); + } + return null; + } + + public boolean show(String viewerId, String ownerId, String page, + BasicDataSource ds) throws SQLException { + boolean show = true; + // if there are no view requirements, go ahead and show it. We are + // likely testing out a new gadget + // if there are some, turn it off unless this page is + if (viewRequirements.size() > 0) { + show = false; + } + + if (viewRequirements.containsKey(page)) { + show = true; + GadgetViewRequirements req = getGadgetViewRequirements(page); + if ('U' == req.getViewerReq() && viewerId != null) { + show = false; + } else if ('R' == req.getViewerReq()) { + show &= isRegisteredTo(viewerId, ds); + } + if ('R' == req.getOwnerReq()) { + show &= isRegisteredTo(ownerId, ds); + } else if ('S' == req.getOwnerReq()) { + show &= (viewerId == ownerId); + } + } + return show; + } + + public boolean isRegisteredTo(String personId, BasicDataSource ds) + throws SQLException { + int count = 0; + + Connection conn = null; + Statement stmt = null; + ResultSet rset = null; + + try { + String sqlCommand = "select count(*) from shindig_app_registry where appId = " + + getAppId() + " and personId = '" + personId + "';"; + conn = ds.getConnection(); + stmt = conn.createStatement(); + rset = stmt.executeQuery(sqlCommand); + while (rset.next()) { + count = rset.getInt(1); + } + } finally { + try { + if (rset != null) { + rset.close(); + } + } catch (Exception e) { + e.printStackTrace(); + } + try { + if (stmt != null) { + stmt.close(); + } + } catch (Exception e) { + e.printStackTrace(); + } + try { + if (conn != null) { + conn.close(); + } + } catch (Exception e) { + e.printStackTrace(); + } + } + + return (count == 1); + } + + public boolean fromSandbox() { + return unknownGadget; + } + + // who sees it? Return the viewerReq for the ProfileDetails page + public char getVisibleScope() { + GadgetViewRequirements req = getGadgetViewRequirements("/display"); + return req != null ? req.getViewerReq() : ' '; + } + + public String toString() { + return "" + this.appId + ":" + this.name + ":" + this.openSocialGadgetURL; + } + +} diff --git a/webapp/src/edu/ucsf/vitro/opensocial/GadgetViewRequirements.java b/webapp/src/edu/ucsf/vitro/opensocial/GadgetViewRequirements.java new file mode 100644 index 000000000..bedbc3ba9 --- /dev/null +++ b/webapp/src/edu/ucsf/vitro/opensocial/GadgetViewRequirements.java @@ -0,0 +1,67 @@ +package edu.ucsf.vitro.opensocial; + +public class GadgetViewRequirements { + private String page; + private char viewerReq; // U for User or null for no requirement + private char ownerReq; // R for Registered or null for no requirement + private String view; + private int closedWidth; + private int openWidth; + private boolean startClosed; + private String chromeId; + private int display_order; + + public GadgetViewRequirements(String page, char viewerReq, char ownerReq, + String view, int closedWidth, int openWidth, boolean startClosed, + String chromeId, int display_order) { + this.page = page; + this.viewerReq = viewerReq; + this.ownerReq = ownerReq; + this.view = view; + this.closedWidth = closedWidth; + this.openWidth = openWidth; + this.startClosed = startClosed; + this.chromeId = chromeId; + this.display_order = display_order; + } + + public GadgetViewRequirements(String page, String viewerReq, + String ownerReq, String view, int closedWidth, int openWidth, + boolean startClosed, String chromeId, int display_order) { + this(page, viewerReq != null ? viewerReq.charAt(0) : ' ', + ownerReq != null ? ownerReq.charAt(0) : ' ', view, closedWidth, + openWidth, startClosed, chromeId, display_order); + } + + public char getViewerReq() { + return viewerReq; + } + + public char getOwnerReq() { + return ownerReq; + } + + public String getView() { + return view; + } + + public int getClosedWidth() { + return closedWidth; + } + + public int getOpenWidth() { + return openWidth; + } + + public boolean getStartClosed() { + return startClosed; + } + + public String getChromeId() { + return chromeId; + } + + int getDisplayOrder() { + return display_order; + } +} diff --git a/webapp/src/edu/ucsf/vitro/opensocial/OpenSocialManager.java b/webapp/src/edu/ucsf/vitro/opensocial/OpenSocialManager.java new file mode 100644 index 000000000..16b35a5d0 --- /dev/null +++ b/webapp/src/edu/ucsf/vitro/opensocial/OpenSocialManager.java @@ -0,0 +1,483 @@ +package edu.ucsf.vitro.opensocial; + +import java.io.IOException; +import java.net.Socket; +import java.net.URLEncoder; +import java.sql.Connection; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.sql.Statement; +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import org.apache.commons.dbcp.BasicDataSource; +import org.json.JSONException; +import org.json.JSONObject; + +import edu.cornell.mannlib.vedit.beans.LoginStatusBean; +import edu.cornell.mannlib.vitro.webapp.beans.Individual; +import edu.cornell.mannlib.vitro.webapp.beans.UserAccount; +import edu.cornell.mannlib.vitro.webapp.config.ConfigurationProperties; +import edu.cornell.mannlib.vitro.webapp.controller.VitroRequest; +import edu.cornell.mannlib.vitro.webapp.controller.individual.IndividualRequestAnalysisContextImpl; +import edu.cornell.mannlib.vitro.webapp.controller.individual.IndividualRequestAnalyzer; +import edu.cornell.mannlib.vitro.webapp.controller.individual.IndividualRequestInfo; + +public class OpenSocialManager { + public static final String SHINDIG_URL_PROP = "OpenSocial.shindigURL"; + + public static final String OPENSOCIAL_DEBUG = "OPENSOCIAL_DEBUG"; + public static final String OPENSOCIAL_NOCACHE = "OPENSOCIAL_NOCACHE"; + public static final String OPENSOCIAL_GADGETS = "OPENSOCIAL_GADGETS"; + + public static final String JSON_PERSONID_CHANNEL = "JSONPersonIds"; + public static final String JSON_PMID_CHANNEL = "JSONPubMedIds"; + public static final String TAG_NAME = "openSocial"; + + private static final String DEFAULT_DRIVER = "com.mysql.jdbc.Driver"; + + private List gadgets = new ArrayList(); + private Map pubsubdata = new HashMap(); + private String viewerId = null; + private String ownerId = null; + private boolean isDebug = false; + private boolean noCache = false; + private String pageName; + private ConfigurationProperties configuration; + + private BasicDataSource dataSource; + + public OpenSocialManager(VitroRequest vreq, String pageName) throws SQLException, IOException { + this(vreq, pageName, false); + } + + public OpenSocialManager(VitroRequest vreq, String pageName, boolean editMode) throws SQLException, IOException { + this.isDebug = vreq.getSession() != null + && Boolean.TRUE.equals(vreq.getSession().getAttribute(OPENSOCIAL_DEBUG)); + this.noCache = vreq.getSession() != null + && Boolean.TRUE.equals(vreq.getSession().getAttribute(OPENSOCIAL_NOCACHE)); + this.pageName = pageName; + + configuration = ConfigurationProperties.getBean(vreq.getSession() + .getServletContext()); + + if (configuration.getProperty(SHINDIG_URL_PROP) == null) { + // do nothing + return; + } + + // Analyze the request to figure out whose page we are viewing. + this.ownerId = figureOwnerId(vreq); + + // in editMode we need to set the viewer to be the same as the owner + // otherwise, the gadget will not be able to save appData correctly + if (editMode) { + this.viewerId = ownerId; + } + else { + UserAccount viewer = LoginStatusBean.getCurrentUser(vreq); + this.viewerId = viewer != null ? viewer.getUri() : null; + } + + boolean gadgetSandbox = "gadgetSandbox".equals(pageName); + String requestAppId = vreq.getParameter("appId"); + + Map dbApps = new HashMap(); + Map officialApps = new HashMap(); + + dataSource = new BasicDataSource(); + dataSource.setDriverClassName(DEFAULT_DRIVER); + dataSource.setUsername(configuration + .getProperty("VitroConnection.DataSource.username")); + dataSource.setPassword(configuration + .getProperty("VitroConnection.DataSource.password")); + dataSource.setUrl(configuration + .getProperty("VitroConnection.DataSource.url")); + + // Load gadgets from the DB first + Connection conn = null; + Statement stmt = null; + ResultSet rset = null; + try { + + String sqlCommand = "select appId, name, url, channels, enabled from shindig_apps"; + // if a specific app is requested, only grab it + if (requestAppId != null) { + sqlCommand += " where appId = " + requestAppId; + } + conn = dataSource.getConnection(); + stmt = conn.createStatement(); + rset = stmt.executeQuery(sqlCommand); + + while (rset.next()) { + GadgetSpec spec = new GadgetSpec(rset.getInt(1), + rset.getString(2), rset.getString(3), rset.getString(4)); + String gadgetFileName = getGadgetFileNameFromURL(rset + .getString(3)); + + dbApps.put(gadgetFileName, spec); + if (requestAppId != null || rset.getBoolean(5)) { + officialApps.put(gadgetFileName, spec); + } + } + } finally { + try { + if (rset != null) { + rset.close(); + } + } catch (Exception e) { + e.printStackTrace(); + } + try { + if (stmt != null) { + stmt.close(); + } + } catch (Exception e) { + e.printStackTrace(); + } + try { + if (conn != null) { + conn.close(); + } + } catch (Exception e) { + e.printStackTrace(); + } + } + + // Add manual gadgets if there are any + // Note that this block of code only gets executed after someone fills in the + // gadget/sandbox form! + int moduleId = 0; + if (vreq.getSession() != null + && vreq.getSession().getAttribute(OPENSOCIAL_GADGETS) != null) { + String openSocialGadgetURLS = (String) vreq.getSession() + .getAttribute(OPENSOCIAL_GADGETS); + String[] urls = openSocialGadgetURLS.split(System.getProperty("line.separator")); + for (String openSocialGadgetURL : urls) { + if (openSocialGadgetURL.length() == 0) + continue; + int appId = 0; // if URL matches one in the DB, use DB provided + // appId, otherwise generate one + String gadgetFileName = getGadgetFileNameFromURL(openSocialGadgetURL); + String name = gadgetFileName; + List channels = new ArrayList(); + boolean unknownGadget = true; + if (dbApps.containsKey(gadgetFileName)) { + appId = dbApps.get(gadgetFileName).getAppId(); + name = dbApps.get(gadgetFileName).getName(); + channels = dbApps.get(gadgetFileName).getChannels(); + unknownGadget = false; + } else { + appId = openSocialGadgetURL.hashCode(); + } + // if they asked for a specific one, only let it in + if (requestAppId != null + && Integer.getInteger(requestAppId) != appId) { + continue; + } + GadgetSpec gadget = new GadgetSpec(appId, name, + openSocialGadgetURL, channels, unknownGadget, dataSource); + // only add ones that are visible in this context! + if (unknownGadget + || gadget.show(viewerId, ownerId, pageName, dataSource)) { + String securityToken = socketSendReceive(viewerId, ownerId, + "" + gadget.getAppId()); + gadgets.add(new PreparedGadget(gadget, this, moduleId++, + securityToken)); + } + } + } + + // if no manual one were added, use the ones from the DB + if (gadgets.size() == 0) { + // Load DB gadgets + if (gadgetSandbox) { + officialApps = dbApps; + } + for (GadgetSpec spec : officialApps.values()) { + GadgetSpec gadget = new GadgetSpec(spec.getAppId(), + spec.getName(), spec.getGadgetURL(), + spec.getChannels(), false, dataSource); + // only add ones that are visible in this context! + if (gadgetSandbox + || gadget.show(viewerId, ownerId, pageName, dataSource)) { + String securityToken = socketSendReceive(viewerId, ownerId, + "" + gadget.getAppId()); + gadgets.add(new PreparedGadget(gadget, this, moduleId++, + securityToken)); + } + } + } + + // sort the gadgets + Collections.sort(gadgets); + } + + private String figureOwnerId(VitroRequest vreq) { + IndividualRequestAnalyzer requestAnalyzer = new IndividualRequestAnalyzer(vreq, + new IndividualRequestAnalysisContextImpl(vreq)); + IndividualRequestInfo requestInfo = requestAnalyzer.analyze(); + Individual owner = requestInfo.getIndividual(); + return owner != null ? owner.getURI() : null; + } + + private String getGadgetFileNameFromURL(String url) { + String[] urlbits = url.split("/"); + return urlbits[urlbits.length - 1]; + } + + public boolean isDebug() { + return isDebug; + } + + public boolean noCache() { + return noCache; + } + + public String getOwnerId() { + return ownerId; + } + + public boolean hasGadgetListeningTo(String channel) { + for (PreparedGadget gadget : getVisibleGadgets()) { + if (gadget.getGadgetSpec().listensTo(channel)) { + return true; + } + } + return false; + } + + public static List getOpenSocialId(List individuals) { + List personIds = new ArrayList(); + for (Individual ind : individuals) { + personIds.add(ind.getURI()); + } + return personIds; + } + + // JSON Helper Functions + public static String buildJSONPersonIds(List personIds, + String message) throws JSONException { + JSONObject json = new JSONObject(); + json.put("message", message); + json.put("personIds", personIds); + return json.toString(); + } + + public static String buildJSONPersonIds(String personId, String message) throws JSONException { + List personIds = new ArrayList(); + personIds.add(personId); + return buildJSONPersonIds(personIds, message); + } + + public static String buildJSONPersonIds(Individual ind, String message) throws JSONException { + List personIds = new ArrayList(); + personIds.add(ind.getURI()); + return buildJSONPersonIds(personIds, message); + } + /**** + * public static String BuildJSONPubMedIds(Person person) { List + * pubIds = new List(); foreach (Publication pub in + * person.PublicationList) { foreach (PublicationSource pubSource in + * pub.PublicationSourceList) { if ("PubMed".Equals(pubSource.Name)) { + * pubIds.Add(Int32.Parse(pubSource.ID)); } } } Dictionary + * foundPubs = new Dictionary(); foundPubs.Add("pubIds", + * pubIds); foundPubs.Add("message", "PubMedIDs for " + + * person.Name.FullName); JavaScriptSerializer serializer = new + * JavaScriptSerializer(); return serializer.Serialize(foundPubs); } + ***/ + + public void setPubsubData(String key, String value) { + if (pubsubdata.containsKey(key)) { + pubsubdata.remove(key); + } + if (value != null && !value.isEmpty()) { + pubsubdata.put(key, value); + } + } + + public Map getPubsubData() { + return pubsubdata; + } + + public void removePubsubGadgetsWithoutData() { + // if any visible gadgets depend on pubsub data that isn't present, + // throw them out + List removedGadgets = new ArrayList(); + for (PreparedGadget gadget : gadgets) { + for (String channel : gadget.getGadgetSpec().getChannels()) { + if (!pubsubdata.containsKey(channel)) { + removedGadgets.add(gadget); + break; + } + } + } + for (PreparedGadget gadget : removedGadgets) { + gadgets.remove(gadget); + } + } + + public void removeGadget(String name) { + // if any visible gadgets depend on pubsub data that isn't present, + // throw them out + PreparedGadget gadgetToRemove = null; + for (PreparedGadget gadget : gadgets) { + if (name.equals(gadget.getName())) { + gadgetToRemove = gadget; + break; + } + } + gadgets.remove(gadgetToRemove); + } + + public String getPageName() { + return pageName; + } + + public String getIdToUrlMapJavascript() { + String retval = "var idToUrlMap = {"; + for (PreparedGadget gadget : gadgets) { + // retval += gadget.GetAppId() + ":'" + gadget.GetGadgetURL() + + // "', "; + retval += "'remote_iframe_" + gadget.getAppId() + "':'" + + gadget.getGadgetURL() + "', "; + } + return retval.substring(0, retval.length() - 2) + "};"; + } + + public boolean isVisible() { + // always have turned on for ProfileDetails.aspx because we want to + // generate the "profile was viewed" in Javascript (bot proof) + // regardless of any gadgets being visible, and we need this to be True + // for the shindig javascript libraries to load + return (configuration.getProperty(SHINDIG_URL_PROP) != null + && (getVisibleGadgets().size() > 0) || getPageName().equals( + "/display")); + } + + public List getVisibleGadgets() { + return gadgets; + } + + public void postActivity(int userId, String title) throws SQLException { + postActivity(userId, title, null, null, null); + } + + public void postActivity(int userId, String title, String body) throws SQLException { + postActivity(userId, title, body, null, null); + } + + public void postActivity(int userId, String title, String body, + String xtraId1Type, String xtraId1Value) throws SQLException { + Connection conn = null; + Statement stmt = null; + String sqlCommand = "INSERT INTO shindig_activity (userId, activity, xtraId1Type, xtraId1Value) VALUES ('" + + userId + "','" + + System.currentTimeMillis() + "" + title + "" + + (body != null ? "" + body + "" : "") + "','" + + xtraId1Type + "','" + xtraId1Value + "');"; + try { + conn = dataSource.getConnection(); + stmt = conn.createStatement(); + stmt.executeUpdate(sqlCommand); + } finally { + try { + if (stmt != null) { + stmt.close(); + } + } catch (Exception e) { + e.printStackTrace(); + } + try { + if (conn != null) { + conn.close(); + } + } catch (Exception e) { + e.printStackTrace(); + } + } + + } + + private String socketSendReceive(String viewer, String owner, String gadget) + throws IOException { + // These keys need to match what you see in + // edu.ucsf.orng.shindig.service.SecureTokenGeneratorService in + // Shindig + String[] tokenService = configuration.getProperty( + "OpenSocial.tokenService").split(":"); + String request = "c=default" + (viewer != null ? "&v=" + URLEncoder.encode(viewer, "UTF-8") : "") + + (owner != null ? "&o=" + URLEncoder.encode(owner, "UTF-8") : "") + "&g=" + gadget + "\r\n"; + + // Create a socket connection with the specified server and port. + Socket s = new Socket(tokenService[0], + Integer.parseInt(tokenService[1])); + + // Send request to the server. + s.getOutputStream().write(request.getBytes()); + + // Receive the encoded content. + int bytes = 0; + String page = ""; + byte[] bytesReceived = new byte[256]; + + // The following will block until the page is transmitted. + while ((bytes = s.getInputStream().read(bytesReceived)) > 0) { + page += new String(bytesReceived, 0, bytes); + } + + return page; + } + + public String getContainerJavascriptSrc() { + return configuration.getProperty(SHINDIG_URL_PROP) + + "/gadgets/js/core:dynamic-height:osapi:pubsub:rpc:views:shindig-container.js?c=1" + + (isDebug ? "&debug=1" : ""); + } + + public String getGadgetJavascript() { + String lineSeparator = System.getProperty("line.separator"); + String gadgetScriptText = lineSeparator + + "var my = {};" + + lineSeparator + + "my.gadgetSpec = function(appId, name, url, secureToken, view, closed_width, open_width, start_closed, chrome_id, visible_scope) {" + + lineSeparator + "this.appId = appId;" + lineSeparator + + "this.name = name;" + lineSeparator + "this.url = url;" + + lineSeparator + "this.secureToken = secureToken;" + + lineSeparator + "this.view = view || 'default';" + + lineSeparator + "this.closed_width = closed_width;" + + lineSeparator + "this.open_width = open_width;" + + lineSeparator + "this.start_closed = start_closed;" + + lineSeparator + "this.chrome_id = chrome_id;" + lineSeparator + + "this.visible_scope = visible_scope;" + lineSeparator + "};" + + lineSeparator + "my.pubsubData = {};" + lineSeparator; + for (String key : getPubsubData().keySet()) { + gadgetScriptText += "my.pubsubData['" + key + "'] = '" + + getPubsubData().get(key) + "';" + lineSeparator; + } + gadgetScriptText += "my.openSocialURL = '" + + configuration.getProperty(SHINDIG_URL_PROP) + "';" + + lineSeparator + "my.debug = " + (isDebug() ? "1" : "0") + ";" + + lineSeparator + "my.noCache = " + (noCache() ? "1" : "0") + + ";" + lineSeparator + "my.gadgets = ["; + for (PreparedGadget gadget : getVisibleGadgets()) { + gadgetScriptText += "new my.gadgetSpec(" + gadget.getAppId() + ",'" + + gadget.getName() + "','" + gadget.getGadgetURL() + "','" + + gadget.getSecurityToken() + "','" + gadget.getView() + + "'," + gadget.getClosedWidth() + "," + + gadget.getOpenWidth() + "," + + (gadget.getStartClosed() ? "1" : "0") + ",'" + + gadget.getChromeId() + "','" + + gadget.getGadgetSpec().getVisibleScope() + "'), "; + } + gadgetScriptText = gadgetScriptText.substring(0, + gadgetScriptText.length() - 2) + + "];" + + lineSeparator; + + return gadgetScriptText; + } +} diff --git a/webapp/src/edu/ucsf/vitro/opensocial/PreparedGadget.java b/webapp/src/edu/ucsf/vitro/opensocial/PreparedGadget.java new file mode 100644 index 000000000..caab0b18b --- /dev/null +++ b/webapp/src/edu/ucsf/vitro/opensocial/PreparedGadget.java @@ -0,0 +1,123 @@ +package edu.ucsf.vitro.opensocial; + +import java.io.UnsupportedEncodingException; +import java.net.URLEncoder; + +public class PreparedGadget implements Comparable { + private GadgetSpec gadgetSpec; + private OpenSocialManager helper; + private int moduleId; + private String securityToken; + + public PreparedGadget(GadgetSpec gadgetSpec, OpenSocialManager helper, + int moduleId, String securityToken) { + this.gadgetSpec = gadgetSpec; + this.helper = helper; + this.moduleId = moduleId; + this.securityToken = securityToken; + } + + public int compareTo(PreparedGadget other) { + GadgetViewRequirements gvr1 = this.getGadgetViewRequirements(); + GadgetViewRequirements gvr2 = other.getGadgetViewRequirements(); + return ("" + this.getView() + (gvr1 != null ? gvr1.getDisplayOrder() + : Integer.MAX_VALUE)).compareTo("" + other.getView() + + (gvr2 != null ? gvr2.getDisplayOrder() : Integer.MAX_VALUE)); + } + + public GadgetSpec getGadgetSpec() { + return gadgetSpec; + } + + public String getSecurityToken() { + return securityToken; + } + + public int getAppId() { + return gadgetSpec.getAppId(); + } + + public String getName() { + return gadgetSpec.getName(); + } + + public int getModuleId() { + return moduleId; + } + + public String getGadgetURL() { + return gadgetSpec.getGadgetURL(); + } + + GadgetViewRequirements getGadgetViewRequirements() { + return gadgetSpec.getGadgetViewRequirements(helper.getPageName()); + } + + public String getView() { + GadgetViewRequirements reqs = getGadgetViewRequirements(); + if (reqs != null) { + return reqs.getView(); + } + // default behavior that will get invoked when there is no reqs. Useful + // for sandbox gadgets + else if (helper.getPageName().equals("individual-EDIT-MODE")) { + return "home"; + } else if (helper.getPageName().equals("individual")) { + return "profile"; + } else if (helper.getPageName().equals("gadgetDetails")) { + return "canvas"; + } else if (gadgetSpec.getGadgetURL().contains("Tool")) { + return "small"; + } else { + return null; + } + } + + public int getOpenWidth() { + GadgetViewRequirements reqs = getGadgetViewRequirements(); + return reqs != null ? reqs.getOpenWidth() : 0; + } + + public int getClosedWidth() { + GadgetViewRequirements reqs = getGadgetViewRequirements(); + return reqs != null ? reqs.getClosedWidth() : 0; + } + + public boolean getStartClosed() { + GadgetViewRequirements reqs = getGadgetViewRequirements(); + // if the page specific reqs are present, honor those. Otherwise defaut + // to true for regular gadgets, false for sandbox gadgets + return reqs != null ? reqs.getStartClosed() : !gadgetSpec.fromSandbox(); + } + + public String getChromeId() { + GadgetViewRequirements reqs = getGadgetViewRequirements(); + if (reqs != null) { + return reqs.getChromeId(); + } + // default behavior that will get invoked when there is no reqs. Useful + // for sandbox gadgets + else if (gadgetSpec.getGadgetURL().contains("Tool")) { + return "gadgets-tools"; + } else if (helper.getPageName().equals("individual-EDIT-MODE")) { + return "gadgets-edit"; + } else if (helper.getPageName().equals("individual")) { + return "gadgets-view"; + } else if (helper.getPageName().equals("gadgetDetails")) { + return "gadgets-detail"; + } else if (helper.getPageName().equals("search")) { + return "gadgets-search"; + } else { + return null; + } + } + + public String getCanvasURL() throws UnsupportedEncodingException { + return "~/gadget?appId=" + getAppId() + "&Person=" + + URLEncoder.encode(helper.getOwnerId(), "UTF-8"); + } + + public String toString() { + return "" + this.moduleId + ", (" + this.gadgetSpec.toString() + ")"; + } +} diff --git a/webapp/test/edu/cornell/mannlib/vitro/webapp/reasoner/SimpleReasonerPropertyTest.java b/webapp/test/edu/cornell/mannlib/vitro/webapp/reasoner/SimpleReasonerPropertyTest.java new file mode 100644 index 000000000..7276828f8 --- /dev/null +++ b/webapp/test/edu/cornell/mannlib/vitro/webapp/reasoner/SimpleReasonerPropertyTest.java @@ -0,0 +1,488 @@ +/* $This file is distributed under the terms of the license in /doc/license.txt$ */ + +package edu.cornell.mannlib.vitro.webapp.reasoner; + +import org.apache.log4j.Level; +import org.junit.Assert; +import org.junit.Before; +import org.junit.Test; +import org.mindswap.pellet.jena.PelletReasonerFactory; + +import com.hp.hpl.jena.ontology.OntModel; +import com.hp.hpl.jena.ontology.OntModelSpec; +import com.hp.hpl.jena.ontology.OntProperty; +import com.hp.hpl.jena.rdf.model.Model; +import com.hp.hpl.jena.rdf.model.ModelFactory; +import com.hp.hpl.jena.rdf.model.Resource; + +import edu.cornell.mannlib.vitro.testing.AbstractTestClass; +import edu.cornell.mannlib.vitro.webapp.utils.threads.VitroBackgroundThread; + +public class SimpleReasonerPropertyTest extends AbstractTestClass { + + long delay = 50; + + @Before + public void suppressErrorOutput() { + suppressSyserr(); + //Turn off log messages to console + setLoggerLevel(SimpleReasoner.class, Level.OFF); + setLoggerLevel(SimpleReasonerTBoxListener.class, Level.OFF); + } + + /* + * basic scenarios around adding abox data + * + * Create a Tbox with property P inverseOf property Q. + * Pellet will compute TBox inferences. Add a statement + * a P b, and verify that b Q a is inferred. + * Add a statement c Q d and verify that d Q c + * is inferred. + */ + @Test + public void addABoxAssertion1() { + + OntModel tBox = ModelFactory.createOntologyModel(PelletReasonerFactory.THE_SPEC); + + OntProperty P = tBox.createOntProperty("http://test.vivo/P"); + P.setLabel("property P", "en-US"); + + OntProperty Q = tBox.createOntProperty("http://test.vivo/Q"); + Q.setLabel("property Q", "en-US"); + + P.addInverseOf(Q); + + // this is the model to receive inferences + Model inf = ModelFactory.createDefaultModel(); + + // create an ABox and register the SimpleReasoner listener with it + OntModel aBox = ModelFactory.createOntologyModel(OntModelSpec.OWL_MEM); + aBox.register(new SimpleReasoner(tBox, aBox, inf)); + + // Individuals a, b, c and d + Resource a = aBox.createResource("http://test.vivo/a"); + Resource b = aBox.createResource("http://test.vivo/b"); + Resource c = aBox.createResource("http://test.vivo/c"); + Resource d = aBox.createResource("http://test.vivo/d"); + + // b Q a is inferred from a P b + aBox.add(a,P,b); + Assert.assertTrue(inf.contains(b,Q,a)); + + // d P c is inferred from c Q d. + aBox.add(c,Q,d); + Assert.assertTrue(inf.contains(d,P,c)); + } + + /* + * don't infer if it's already in the abox + */ + @Test + public void addABoxAssertion2() { + + OntModel tBox = ModelFactory.createOntologyModel(PelletReasonerFactory.THE_SPEC); + + OntProperty P = tBox.createOntProperty("http://test.vivo/P"); + P.setLabel("property P", "en-US"); + OntProperty Q = tBox.createOntProperty("http://test.vivo/Q"); + Q.setLabel("property Q", "en-US"); + P.addInverseOf(Q); + + // this is the model to receive inferences + Model inf = ModelFactory.createDefaultModel(); + + // create an ABox and add statement b Q a + OntModel aBox = ModelFactory.createOntologyModel(OntModelSpec.OWL_MEM); + // Individuals a, b, c and d + Resource a = aBox.createResource("http://test.vivo/a"); + Resource b = aBox.createResource("http://test.vivo/b"); + aBox.add(b,Q,a); + + // register SimpleReasoner + aBox.register(new SimpleReasoner(tBox, aBox, inf)); + + // b Q a is inferred from a P b, but it is already in the abox + aBox.add(a,P,b); + Assert.assertFalse(inf.contains(b,Q,a)); + } + + /* + * don't infer if it's already in the abox + */ + @Test + public void addABoxAssertion3() { + OntModel tBox = ModelFactory.createOntologyModel(PelletReasonerFactory.THE_SPEC); + + OntProperty P = tBox.createOntProperty("http://test.vivo/P"); + P.setLabel("property P", "en-US"); + OntProperty Q = tBox.createOntProperty("http://test.vivo/Q"); + Q.setLabel("property Q", "en-US"); + P.addInverseOf(Q); + + // this is the model to receive inferences + Model inf = ModelFactory.createDefaultModel(); + + // create an ABox and register SimpleReasoner + OntModel aBox = ModelFactory.createOntologyModel(OntModelSpec.OWL_MEM); + aBox.register(new SimpleReasoner(tBox, aBox, inf)); + + // Individuals a, b, c and d + Resource a = aBox.createResource("http://test.vivo/a"); + Resource b = aBox.createResource("http://test.vivo/b"); + + // b Q a is inferred from a P b, but it is already in the abox + aBox.add(a,P,b); + aBox.add(b,Q,a); + Assert.assertFalse(inf.contains(b,Q,a)); + } + + /* + * adding abox data where the property has an inverse and + * and equivalent property. + */ + @Test + public void addABoxAssertion4() { + + OntModel tBox = ModelFactory.createOntologyModel(PelletReasonerFactory.THE_SPEC); + + OntProperty P = tBox.createOntProperty("http://test.vivo/P"); + P.setLabel("property P", "en-US"); + OntProperty R = tBox.createOntProperty("http://test.vivo/R"); + R.setLabel("property R", "en-US"); + OntProperty Q = tBox.createOntProperty("http://test.vivo/Q"); + Q.setLabel("property Q", "en-US"); + + R.addEquivalentProperty(P); + P.addEquivalentProperty(R); + P.addInverseOf(Q); + + // this is the model to receive inferences + Model inf = ModelFactory.createDefaultModel(); + + // create an ABox and register the SimpleReasoner listener with it + OntModel aBox = ModelFactory.createOntologyModel(OntModelSpec.OWL_MEM); + aBox.register(new SimpleReasoner(tBox, aBox, inf)); + + // a, b, c and d + Resource a = aBox.createResource("http://test.vivo/a"); + Resource b = aBox.createResource("http://test.vivo/b"); + Resource c = aBox.createResource("http://test.vivo/c"); + Resource d = aBox.createResource("http://test.vivo/d"); + + // b Q a is inferred from a R b. + aBox.add(a,R,b); + Assert.assertTrue(inf.contains(b,Q,a)); + + // d P c is inferred from c Q d. + aBox.add(c,Q,d); + Assert.assertTrue(inf.contains(d,P,c)); + Assert.assertTrue(inf.contains(d,R,c)); + } + + /* + * basic scenarios around removing abox data + */ + @Test + public void removedABoxAssertion1() { + OntModel tBox = ModelFactory.createOntologyModel(PelletReasonerFactory.THE_SPEC); + + OntProperty P = tBox.createOntProperty("http://test.vivo/P"); + P.setLabel("property P", "en-US"); + OntProperty Q = tBox.createOntProperty("http://test.vivo/Q"); + Q.setLabel("property Q", "en-US"); + OntProperty T = tBox.createOntProperty("http://test.vivo/T"); + Q.setLabel("property T", "en-US"); + P.addInverseOf(Q); + P.addInverseOf(T); + + // this is the model to receive inferences + Model inf = ModelFactory.createDefaultModel(); + + // create an ABox and register the SimpleReasoner listener with it + OntModel aBox = ModelFactory.createOntologyModel(OntModelSpec.OWL_MEM); + aBox.register(new SimpleReasoner(tBox, aBox, inf)); + + // Individuals a, b, c and d + Resource a = aBox.createResource("http://test.vivo/a"); + Resource b = aBox.createResource("http://test.vivo/b"); + Resource c = aBox.createResource("http://test.vivo/c"); + Resource d = aBox.createResource("http://test.vivo/d"); + + // b Q a is inferred from a P b. + aBox.add(a,P,b); + Assert.assertTrue(inf.contains(b,Q,a)); + + // d P c is inferred from c Q d and from c T d + aBox.add(c,Q,d); + aBox.add(c,T,d); + Assert.assertTrue(inf.contains(d,P,c)); + + aBox.remove(a,P,b); + Assert.assertFalse(inf.contains(b,Q,a)); + + aBox.remove(c,Q,d); + Assert.assertTrue(inf.contains(d,P,c)); + } + + /* + * removing abox data with equivalent and inverse properties + */ + @Test + public void removedABoxAssertion2() { + OntModel tBox = ModelFactory.createOntologyModel(PelletReasonerFactory.THE_SPEC); + + OntProperty P = tBox.createOntProperty("http://test.vivo/P"); + P.setLabel("property P", "en-US"); + OntProperty Q = tBox.createOntProperty("http://test.vivo/Q"); + Q.setLabel("property Q", "en-US"); + OntProperty T = tBox.createOntProperty("http://test.vivo/T"); + Q.setLabel("property T", "en-US"); + P.addInverseOf(Q); + Q.addInverseOf(P); + P.addEquivalentProperty(T); + T.addEquivalentProperty(P); + + // this is the model to receive inferences + Model inf = ModelFactory.createDefaultModel(); + + // create an ABox and register the SimpleReasoner listener with it + OntModel aBox = ModelFactory.createOntologyModel(OntModelSpec.OWL_MEM); + aBox.register(new SimpleReasoner(tBox, aBox, inf)); + + // Individuals a, b, c and d + Resource a = aBox.createResource("http://test.vivo/a"); + Resource b = aBox.createResource("http://test.vivo/b"); + + // b Q a is inferred from a P b and a T b. + aBox.add(a,P,b); + aBox.add(a,T,b); + Assert.assertTrue(inf.contains(b,Q,a)); + Assert.assertFalse(inf.contains(a,P,b)); + + aBox.remove(a,P,b); + Assert.assertTrue(inf.contains(b,Q,a)); + } + + /* + * removing abox data with equivalent and inverse properties + */ + @Test + public void removedABoxAssertion3() { + OntModel tBox = ModelFactory.createOntologyModel(PelletReasonerFactory.THE_SPEC); + + OntProperty P = tBox.createOntProperty("http://test.vivo/P"); + P.setLabel("property P", "en-US"); + OntProperty Q = tBox.createOntProperty("http://test.vivo/Q"); + Q.setLabel("property Q", "en-US"); + P.addInverseOf(Q); + + // this is the model to receive inferences + Model inf = ModelFactory.createDefaultModel(); + + // create an ABox and register the SimpleReasoner listener with it + OntModel aBox = ModelFactory.createOntologyModel(OntModelSpec.OWL_MEM); + Resource a = aBox.createResource("http://test.vivo/a"); + Resource b = aBox.createResource("http://test.vivo/b"); + aBox.add(a,P,b); + + aBox.register(new SimpleReasoner(tBox, aBox, inf)); + + aBox.add(b,Q,a); + + Assert.assertFalse(inf.contains(b,Q,a)); + Assert.assertFalse(inf.contains(a,P,b)); + + aBox.remove(a,P,b); + Assert.assertTrue(inf.contains(a,P,b)); + } + + /* + * Basic scenario around adding an inverseOf assertion to the + * TBox + */ + @Test + public void addTBoxInverseAssertion1() throws InterruptedException { + + // Create TBox, ABox and Inference models and register + // the ABox reasoner listeners with the ABox and TBox + // Pellet will compute TBox inferences + + OntModel tBox = ModelFactory.createOntologyModel(PelletReasonerFactory.THE_SPEC); + + OntProperty P = tBox.createOntProperty("http://test.vivo/P"); + P.setLabel("property P", "en-US"); + + OntProperty Q = tBox.createOntProperty("http://test.vivo/Q"); + Q.setLabel("property Q", "en-US"); + + OntModel aBox = ModelFactory.createOntologyModel(OntModelSpec.OWL_MEM); + Model inf = ModelFactory.createDefaultModel(); + + SimpleReasoner simpleReasoner = new SimpleReasoner(tBox, aBox, inf); + aBox.register(simpleReasoner); + SimpleReasonerTBoxListener simpleReasonerTBoxListener = getTBoxListener(simpleReasoner); + tBox.register(simpleReasonerTBoxListener); + + // Individuals a, b, c and d + Resource a = aBox.createResource("http://test.vivo/a"); + Resource b = aBox.createResource("http://test.vivo/b"); + Resource c = aBox.createResource("http://test.vivo/c"); + Resource d = aBox.createResource("http://test.vivo/d"); + + // abox statements + aBox.add(a,P,b); + aBox.add(c,P,d); + aBox.add(b,Q,a); + + // Assert P and Q as inverses and wait for SimpleReasoner TBox + // thread to end + + Q.addInverseOf(P); + + tBox.rebind(); + tBox.prepare(); + + while (!VitroBackgroundThread.getLivingThreads().isEmpty()) { + Thread.sleep(delay); + } + + // Verify inferences + Assert.assertTrue(inf.contains(d,Q,c)); + Assert.assertFalse(inf.contains(b,Q,a)); + + simpleReasonerTBoxListener.setStopRequested(); + } + + /* + * Basic scenario around removing an inverseOf assertion to the + * TBox + */ + @Test + public void removeTBoxInverseAssertion1() throws InterruptedException { + + // Create TBox, ABox and Inference models and register + // the ABox reasoner listeners with the ABox and TBox + // Pellet will compute TBox inferences + + OntModel tBox = ModelFactory.createOntologyModel(PelletReasonerFactory.THE_SPEC); + + // set up TBox and Abox + + OntProperty P = tBox.createOntProperty("http://test.vivo/propP"); + P.setLabel("property P", "en-US"); + + OntProperty Q = tBox.createOntProperty("http://test.vivo/propQ"); + Q.setLabel("property Q", "en-US"); + + Q.addInverseOf(P); + + OntModel aBox = ModelFactory.createOntologyModel(OntModelSpec.OWL_MEM); + Model inf = ModelFactory.createDefaultModel(); + + SimpleReasoner simpleReasoner = new SimpleReasoner(tBox, aBox, inf); + aBox.register(simpleReasoner); + SimpleReasonerTBoxListener simpleReasonerTBoxListener = getTBoxListener(simpleReasoner); + tBox.register(simpleReasonerTBoxListener); + // Individuals a, b, c and d + Resource c = aBox.createResource("http://test.vivo/c"); + Resource d = aBox.createResource("http://test.vivo/d"); + + // abox statements + aBox.add(c,P,d); + + Assert.assertTrue(inf.contains(d,Q,c)); + + // Remove P and Q inverse relationship and wait for + // SimpleReasoner TBox thread to end. + + Q.removeInverseProperty(P); + + tBox.rebind(); + tBox.prepare(); + + while (!VitroBackgroundThread.getLivingThreads().isEmpty()) { + Thread.sleep(delay); + } + + // Verify inferences + Assert.assertFalse(inf.contains(d,Q,c)); + + simpleReasonerTBoxListener.setStopRequested(); + } + + /* + * Basic scenario around recomputing the ABox inferences + */ + @Test + public void recomputeABox1() throws InterruptedException { + + // Create TBox, ABox and Inference models and register + // the ABox reasoner listeners with the ABox and TBox + // Pellet will compute TBox inferences + OntModel tBox = ModelFactory.createOntologyModel(PelletReasonerFactory.THE_SPEC); + + // set up TBox and Abox + OntProperty P = tBox.createOntProperty("http://test.vivo/propP"); + P.setLabel("property P", "en-US"); + OntProperty Q = tBox.createOntProperty("http://test.vivo/propQ"); + Q.setLabel("property Q", "en-US"); + Q.addInverseOf(P); + + OntProperty X = tBox.createOntProperty("http://test.vivo/propX"); + P.setLabel("property X", "en-US"); + OntProperty Y = tBox.createOntProperty("http://test.vivo/propY"); + Q.setLabel("property Y", "en-US"); + X.addInverseOf(Y); + + OntModel aBox = ModelFactory.createOntologyModel(OntModelSpec.OWL_MEM); + Model inf = ModelFactory.createDefaultModel(); + + SimpleReasoner simpleReasoner = new SimpleReasoner(tBox, aBox, inf); + aBox.register(simpleReasoner); + SimpleReasonerTBoxListener simpleReasonerTBoxListener = getTBoxListener(simpleReasoner); + tBox.register(simpleReasonerTBoxListener); + // Individuals a, b, c and d + Resource a = aBox.createResource("http://test.vivo/a"); + Resource b = aBox.createResource("http://test.vivo/b"); + Resource c = aBox.createResource("http://test.vivo/c"); + Resource d = aBox.createResource("http://test.vivo/d"); + + // abox statements + aBox.add(a,P,b); + aBox.add(c,X,d); + + simpleReasoner.recompute(); + + while (simpleReasoner.isRecomputing()) { + Thread.sleep(delay); + } + + // Verify inferences + Assert.assertTrue(inf.contains(b,Q,a)); + Assert.assertTrue(inf.contains(d,Y,c)); + + simpleReasonerTBoxListener.setStopRequested(); + } + + //==================================== Utility methods ==================== + SimpleReasonerTBoxListener getTBoxListener(SimpleReasoner simpleReasoner) { + return new SimpleReasonerTBoxListener(simpleReasoner, new Exception().getStackTrace()[1].getMethodName()); + } + + // To help in debugging the unit test + void printModel(Model model, String modelName) { + + System.out.println("\nThe " + modelName + " model has " + model.size() + " statements:"); + System.out.println("---------------------------------------------------------------------"); + model.write(System.out); + } + + // To help in debugging the unit test + void printModel(OntModel ontModel, String modelName) { + + System.out.println("\nThe " + modelName + " model has " + ontModel.size() + " statements:"); + System.out.println("---------------------------------------------------------------------"); + ontModel.writeAll(System.out,"N3",null); + + } +} \ No newline at end of file diff --git a/webapp/test/stubs/edu/cornell/mannlib/vitro/webapp/dao/DataPropertyDaoStub.java b/webapp/test/stubs/edu/cornell/mannlib/vitro/webapp/dao/DataPropertyDaoStub.java index 53faa0201..15c6c81f2 100644 --- a/webapp/test/stubs/edu/cornell/mannlib/vitro/webapp/dao/DataPropertyDaoStub.java +++ b/webapp/test/stubs/edu/cornell/mannlib/vitro/webapp/dao/DataPropertyDaoStub.java @@ -26,6 +26,7 @@ public class DataPropertyDaoStub implements DataPropertyDao { // ---------------------------------------------------------------------- private final Map dpMap = new HashMap(); + private final Map configFilesMap = new HashMap(); public void addDataProperty(DataProperty dataProperty) { if (dataProperty == null) { @@ -40,6 +41,18 @@ public class DataPropertyDaoStub implements DataPropertyDao { dpMap.put(uri, dataProperty); } + public void setCustomListViewConfigFileName(DataProperty property, String filename) { + if (property == null) { + throw new NullPointerException("property may not be null."); + } + + String uri = property.getURI(); + if (uri == null) { + throw new NullPointerException("uri may not be null."); + } + + configFilesMap.put(uri, filename); + } // ---------------------------------------------------------------------- // Stub methods // ---------------------------------------------------------------------- @@ -49,6 +62,17 @@ public class DataPropertyDaoStub implements DataPropertyDao { return dpMap.get(dataPropertyURI); } + @Override + public String getCustomListViewConfigFileName(DataProperty dataProperty) { + if (dataProperty == null) { + return null; + } + String uri = dataProperty.getURI(); + if (uri == null) { + return null; + } + return configFilesMap.get(uri); + } // ---------------------------------------------------------------------- // Un-implemented methods // ---------------------------------------------------------------------- diff --git a/webapp/test/stubs/edu/cornell/mannlib/vitro/webapp/dao/VClassDaoStub.java b/webapp/test/stubs/edu/cornell/mannlib/vitro/webapp/dao/VClassDaoStub.java index 3c912c72e..e17fd5b68 100644 --- a/webapp/test/stubs/edu/cornell/mannlib/vitro/webapp/dao/VClassDaoStub.java +++ b/webapp/test/stubs/edu/cornell/mannlib/vitro/webapp/dao/VClassDaoStub.java @@ -1,3 +1,5 @@ +/* $This file is distributed under the terms of the license in /doc/license.txt$ */ + package stubs.edu.cornell.mannlib.vitro.webapp.dao; import java.util.HashMap; diff --git a/webapp/web/WEB-INF/ontologies/app/ApplicationConfiguration.n3 b/webapp/web/WEB-INF/ontologies/app/ApplicationConfiguration.n3 index b543e9f6c..6087d35c8 100644 --- a/webapp/web/WEB-INF/ontologies/app/ApplicationConfiguration.n3 +++ b/webapp/web/WEB-INF/ontologies/app/ApplicationConfiguration.n3 @@ -111,14 +111,6 @@ :configContextFor rdf:type owl:ObjectProperty ; rdfs:domain :ConfigContext . -:inheritingConfigContextFor rdf:type owl:ObjectProperty ; - rdfs:domain :ConfigContext ; - rdfs:subPropertyOf :configContextFor . - -:nonInheritingConfigContextFor rdf:type owl:ObjectProperty ; - rdfs:domain :ConfigContext ; - rdfs:subPropertyOf :configContextFor . - :qualifiedBy rdf:type owl:ObjectProperty ; rdfs:domain :ConfigContext . @@ -130,6 +122,14 @@ rdfs:domain :ConfigContext ; rdfs:subPropertyOf :qualifiedBy . +:inheritingConfigurationFor rdf:type owl:ObjectProperty ; + rdfs:domain :ConfigContext ; + rdfs:subPropertyOf :configContextFor . + +:nonInheritingConfigurationFor rdf:type owl:ObjectProperty ; + rdfs:domain :ConfigContext ; + rdfs:subPropertyOf :configContextFor . + :hasListView rdf:type owl:ObjectProperty ; rdfs:range :ListDisplayView ; rdfs:domain :PropertyDisplayConfig ; @@ -246,15 +246,15 @@ #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ :defer rdf:type :OfferEditOption , - owl:NamedIndividual . + owl:NamedIndividual . -:false rdf:type :OfferEditOption , - owl:NamedIndividual . +:doNotOfferForEdit rdf:type :OfferEditOption , + owl:NamedIndividual . :ifStatement rdf:type :OfferEditOption , - owl:NamedIndividual . + owl:NamedIndividual . -:true rdf:type :OfferEditOption , +:offerForEdit rdf:type :OfferEditOption , owl:NamedIndividual . #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ diff --git a/webapp/web/WEB-INF/ontologies/app/menuload/displayTBOX.n3 b/webapp/web/WEB-INF/ontologies/app/menuload/displayTBOX.n3 index 5ba172b5a..656e1aad7 100644 --- a/webapp/web/WEB-INF/ontologies/app/menuload/displayTBOX.n3 +++ b/webapp/web/WEB-INF/ontologies/app/menuload/displayTBOX.n3 @@ -112,7 +112,10 @@ owl:versionInfo a owl:DatatypeProperty. - a owl:DatatypeProperty. + a owl:DatatypeProperty. + + + a owl:DatatypeProperty. ######### Object Properties######### ###Basic diff --git a/webapp/web/WEB-INF/web.xml b/webapp/web/WEB-INF/web.xml index 0287959bd..61b95f719 100644 --- a/webapp/web/WEB-INF/web.xml +++ b/webapp/web/WEB-INF/web.xml @@ -786,38 +786,38 @@ - ObjectPropertyHierarchyListingController - edu.cornell.mannlib.vitro.webapp.controller.edit.listing.ObjectPropertyHierarchyListingController + ShowObjectPropertyHierarchyController + edu.cornell.mannlib.vitro.webapp.controller.freemarker.ShowObjectPropertyHierarchyController - ObjectPropertyHierarchyListingController + ShowObjectPropertyHierarchyController /showObjectPropertyHierarchy - DataPropertyHierarchyListingController - edu.cornell.mannlib.vitro.webapp.controller.edit.listing.DataPropertyHierarchyListingController + ShowDataPropertyHierarchyController + edu.cornell.mannlib.vitro.webapp.controller.freemarker.ShowDataPropertyHierarchyController - DataPropertyHierarchyListingController + ShowDataPropertyHierarchyController /showDataPropertyHierarchy - PropertyWebappsListingController - edu.cornell.mannlib.vitro.webapp.controller.edit.listing.PropertyWebappsListingController + ListPropertyWebappsController + edu.cornell.mannlib.vitro.webapp.controller.freemarker.ListPropertyWebappsController - PropertyWebappsListingController + ListPropertyWebappsController /listPropertyWebapps - DatatypePropertiesListingController - edu.cornell.mannlib.vitro.webapp.controller.edit.listing.DatatypePropertiesListingController + ListDatatypePropertiesController + edu.cornell.mannlib.vitro.webapp.controller.freemarker.ListDatatypePropertiesController - DatatypePropertiesListingController + ListDatatypePropertiesController /listDatatypeProperties @@ -1300,6 +1300,20 @@ /admin/getObjectClasses + + + GadgetController + edu.ucsf.vitro.opensocial.GadgetController + + + GadgetController + /gadget + + + GadgetController + /gadget/sandbox + + diff --git a/webapp/web/css/classHierarchy.css b/webapp/web/css/classHierarchy.css index f1f92b5bb..618c4d28d 100644 --- a/webapp/web/css/classHierarchy.css +++ b/webapp/web/css/classHierarchy.css @@ -65,6 +65,11 @@ span#expandAll { float: right; font-size: 0.85em; } +span.rangeClass { + color:#064D68; + padding-left:22px; + padding-right:8px; +} div#expandLink { margin-top:-10px; width:100%; diff --git a/webapp/web/css/menupage/pageList.css b/webapp/web/css/menupage/pageList.css index 7b124d062..a5e32fb5e 100644 --- a/webapp/web/css/menupage/pageList.css +++ b/webapp/web/css/menupage/pageList.css @@ -1,3 +1,5 @@ +/* $This file is distributed under the terms of the license in /doc/license.txt$ */ + .menuFlag { width:67px; height:18px; diff --git a/webapp/web/js/jquery.fix.clone.js b/webapp/web/js/jquery.fix.clone.js new file mode 100644 index 000000000..b7011aaac --- /dev/null +++ b/webapp/web/js/jquery.fix.clone.js @@ -0,0 +1,26 @@ +// Textarea and select clone() bug workaround | Spencer Tipping +// Licensed under the terms of the MIT source code license + +// Motivation. +// jQuery's clone() method works in most cases, but it fails to copy the value of textareas and select elements. This patch replaces jQuery's clone() method with a wrapper that fills in the +// values after the fact. + +// An interesting error case submitted by Piotr Przybył: If two box itself rather than relying on jQuery's value-based val(). + +(function (original) { + jQuery.fn.clone = function () { + var result = original.apply(this, arguments), + my_textareas = this.find('textarea').add(this.filter('textarea')), + result_textareas = result.find('textarea').add(this.filter('textarea')), + my_selects = this.find('select').add(this.filter('select')), + result_selects = result.find('select').add(this.filter('select')); + + for (var i = 0, l = my_textareas.length; i < l; ++i) $(result_textareas[i]).val($(my_textareas[i]).val()); + for (var i = 0, l = my_selects.length; i < l; ++i) result_selects[i].selectedIndex = my_selects[i].selectedIndex; + + return result; + }; +}) (jQuery.fn.clone); + +// Generated by SDoc \ No newline at end of file diff --git a/webapp/web/js/menupage/pageManagementUtils.js b/webapp/web/js/menupage/pageManagementUtils.js index 8ccb35a2d..77a41e0ad 100644 --- a/webapp/web/js/menupage/pageManagementUtils.js +++ b/webapp/web/js/menupage/pageManagementUtils.js @@ -1,18 +1,34 @@ /* $This file is distributed under the terms of the license in /doc/license.txt$ */ var pageManagementUtils = { - + dataGetterLabelToURI:null,//initialized by custom data + processDataGetterUtils:processDataGetterUtils,//an external class that should exist before this one + dataGetterMap:null, // on initial page setup onLoad:function(){ if (this.disableFormInUnsupportedBrowsers()) { return; } this.mixIn(); + this.initDataGetterProcessors(), this.initObjects(); this.bindEventListeners(); this.initDisplay(); }, + initDataGetterProcessors:function() { + //data getter processor map should come in from custom data + //Go through each and initialize with their class + + if(pageManagementUtils.processDataGetterUtils != null) { + var dataGetterProcessorMap = pageManagementUtils.dataGetterProcessorMap = pageManagementUtils.processDataGetterUtils.dataGetterProcessorMap; + $.each(dataGetterProcessorMap, function(key, dataGetterProcessorObject) { + //passes class name from data getter label to uri to processor + dataGetterProcessorObject.initProcessor(pageManagementUtils.dataGetterLabelToURI[key]); + }) + } + + }, disableFormInUnsupportedBrowsers: function() { var disableWrapper = $('#ie67DisableWrapper'); @@ -28,6 +44,7 @@ var pageManagementUtils = { }, mixIn: function() { + //Data getter process list input should be retrieved from the custom data // Mix in the custom form utility methods $.extend(this, vitro.customFormUtils); // Get the custom form data from the page @@ -35,11 +52,12 @@ var pageManagementUtils = { }, initObjects:function(){ this.counter = 0; - this.contentTypeSelect = $("#typeSelect"); + this.contentTypeSelect = $("select#typeSelect"); //list of options this.contentTypeSelectOptions = $('select#typeSelect option'); - this.classGroupSection = $("section#classGroup"); - this.nonClassGroupSection = $("section#nonClassGroup"); + this.classGroupSection = $("section#browseClassGroup"); + this.sparqlQuerySection = $("section#sparqlQuery"); + this.fixedHTMLSection = $("section#fixedHtml"); //From original menu management edit this.defaultTemplateRadio = $('input.default-template'); this.customTemplateRadio = $('input.custom-template'); @@ -48,12 +66,19 @@ var pageManagementUtils = { // this.changeContentType = $('#changeContentType'); this.selectContentType = $('#selectContentType'); // this.existingContentType = $('#existingContentType'); - this.selectClassGroupDropdown = $('#selectClassGroup'); - this.classesForClassGroup = $('#classesInSelectedGroup'); + this.selectClassGroupDropdown = $('select#selectClassGroup'); + this.classesForClassGroup = $('section#classesInSelectedGroup'); this.selectedGroupForPage = $('#selectedContentTypeValue'); this.allClassesSelectedCheckbox = $('#allSelected'); this.displayInternalMessage = $('#internal-class label em'); this.pageContentSubmissionInputs = $("#pageContentSubmissionInputs"); + this.headerBar = $("section#headerBar"); + this.moreContentButton = $("input#moreContent"); + this.isMenuCheckbox = $("input#menuCheckbox"); + this.menuSection = $("section#menu"); + this.submitButton = $("input#submit"); + this.leftSideDiv = $("div#leftSide"); + this.rightSideDiv = $("div#rightSide") }, initDisplay: function(){ //right side components @@ -62,125 +87,120 @@ var pageManagementUtils = { //Why would you want to hide this? This hides everything // $("section#pageDetails").hide(); - $("section#headerBar").hide(); + this.headerBar.hide(); this.classGroupSection.hide(); - this.nonClassGroupSection.hide(); - $("section#classesInSelectedGroup").addClass('hidden'); - $("input#moreContent").hide(); + this.sparqlQuerySection.hide(); + this.fixedHTMLSection.hide(); + this.classesForClassGroup.addClass('hidden'); + this.moreContentButton.hide(); //left side components - $("input.default-template").attr('checked',true); - $("input#menuCheckbox").attr('checked',false); - $("section#menu").hide(); + this.defaultTemplateRadio.attr('checked',true); + this.isMenuCheckbox.attr('checked',false); + this.menuSection.hide(); }, bindEventListeners:function(){ - $("input.default-template").click( function() { - $("section#custom-template").addClass('hidden'); + + this.defaultTemplateRadio.click( function() { + pageManagementUtils.customTemplate.addClass('hidden'); + //Also clear custom template value so as not to submit it + pageManagementUtils.clearInputs(pageManagementUtils.customTemplate); }); - $("input.custom-template").click( function() { - $("section#custom-template").removeClass('hidden'); + this.customTemplateRadio.click( function() { + pageManagementUtils.customTemplate.removeClass('hidden'); }); - $("input#menuCheckbox").click( function() { - if ( $("section#menu").is(':hidden') ) { - $("section#menu").show(); + this.isMenuCheckbox.click( function() { + if ( pageManagementUtils.menuSection.is(':hidden') ) { + pageManagementUtils.menuSection.show(); } else { - $("section#menu").hide(); + pageManagementUtils.menuSection.hide(); } }); - $("input#submit").click( function() { + this.submitButton.click( function() { $("section#pageDetails").show(); }); //Collapses the current content and creates a new section of content //Resets the content to be cloned to default settings - $("input#moreContent").click( function() { + this.moreContentButton.click( function() { var selectedType = pageManagementUtils.contentTypeSelect.val(); var selectedTypeText = $("#typeSelect option:selected").text(); - //Not sure why selected group here? This won't always be true for more content - //var selectedGroup = $('select#selectClassGroup').val(); - //Aren't these already hidden? - //Hide both sections - $("section#classGroup").hide(); - $("section#nonClassGroup").hide(); - - //Reset class group - pageManagementUtils.resetClassGroupSection(); + //Hide all sections + pageManagementUtils.classGroupSection.hide(); + pageManagementUtils.fixedHTMLSection.hide(); + pageManagementUtils.sparqlQuerySection.hide(); + //Reset main content type drop-down pageManagementUtils.contentTypeSelectOptions.eq(0).attr('selected', 'selected'); - $("input#moreContent").hide(); - if ( $("div#leftSide").css("height") != undefined ) { - $("div#leftSide").css("height",""); - if ( $("div#leftSide").height() < $("div#rightSide").height() ) { - $("div#leftSide").css("height",$("div#rightSide").height() + "px"); + pageManagementUtils.moreContentButton.hide(); + if ( pageManagementUtils.leftSideDiv.css("height") != undefined ) { + pageManagementUtils.leftSideDiv.css("height",""); + if ( pageManagementUtils.leftSideDiv.height() < pageManagementUtils.rightSideDiv.height() ) { + pageManagementUtils.leftSideDiv.css("height",pageManagementUtils.rightSideDiv.height() + "px"); } } - $("section#headerBar").hide(); - $("section#headerBar").text(""); - //pageManagementUtils.cloneContentArea(selectedType,selectedGroup); - pageManagementUtils.cloneContentArea(selectedType, selectedTypeText); - pageManagementUtils.contentTypeSelect.focus(); + pageManagementUtils.headerBar.hide(); + pageManagementUtils.headerBar.text(""); + pageManagementUtils.cloneContentArea(selectedType, selectedTypeText); + //Reset class group section AFTER cloning not before + pageManagementUtils.resetClassGroupSection(); + //Clear all inputs values + pageManagementUtils.clearSourceTemplateValues(); + pageManagementUtils.contentTypeSelect.focus(); }); //replacing with menu management edit version which is extended with some of the logic below this.selectClassGroupDropdown.change(function() { pageManagementUtils.chooseClassGroup(); }); - /* - $("select#selectClassGroup").change( function() { - if ( $("select#selectClassGroup").val() == "" ) { - $("section#classesInSelectedGroup").addClass('hidden'); - $("div#leftSide").css("height",""); - $("input#moreContent").hide(); - + + this.contentTypeSelect.change( function() { + _this = pageManagementUtils; + pageManagementUtils.clearSourceTemplateValues(); + if ( _this.contentTypeSelect.val() == "browseClassGroup" ) { + pageManagementUtils.classGroupSection.show(); + pageManagementUtils.fixedHTMLSection.hide(); + pageManagementUtils.sparqlQuerySection.hide(); + pageManagementUtils.moreContentButton.hide(); + pageManagementUtils.headerBar.text("Browse Class Group - "); + pageManagementUtils.headerBar.show(); } - else { - $("section#classesInSelectedGroup").removeClass('hidden'); - $("input#moreContent").show(); - if ( $("div#leftSide").height() < $("div#rightSide").height() ) { - $("div#leftSide").css("height",$("div#rightSide").height() + "px"); - } - } - });*/ - - $("select#typeSelect").change( function() { - $('input#variable').val(""); - $('textarea#textArea').val(""); - if ( $("#typeSelect").val() == "browseClassGroup" ) { - $("section#classGroup").show(); - $("section#nonClassGroup").hide(); - $("input#moreContent").hide(); - $("section#headerBar").text("Browse Class Group - "); - $("section#headerBar").show(); - } - if ( $("#typeSelect").val() == "fixedHtml" || $("#typeSelect").val() == "sparqlQuery" ) { - $("section#classGroup").hide(); - if ( $("#typeSelect").val() == "fixedHtml" ) { - $('span#taSpan').text("Enter fixed HTML here"); - $("section#headerBar").text("Fixed HTML - "); + if ( _this.contentTypeSelect.val() == "fixedHtml" || _this.contentTypeSelect.val() == "sparqlQuery" ) { + pageManagementUtils.classGroupSection.hide(); + //if fixed html show that, otherwise show sparq + if ( _this.contentTypeSelect.val() == "fixedHtml" ) { + pageManagementUtils.headerBar.text("Fixed HTML - "); + pageManagementUtils.fixedHTMLSection.show(); + pageManagementUtils.sparqlQuerySection.hide(); } else { - $('span#taSpan').text("Enter SPARQL query here"); - $("section#headerBar").text("SPARQL Query Results - "); + pageManagementUtils.headerBar.text("SPARQL Query Results - "); + pageManagementUtils.sparqlQuerySection.show(); + pageManagementUtils.fixedHTMLSection.hide(); } - $("section#nonClassGroup").show(); - $("section#headerBar").show(); - $('select#selectClassGroup option').eq(0).attr('selected', 'selected'); - $("section#classesInSelectedGroup").addClass('hidden'); - $("input#moreContent").show(); + + pageManagementUtils.headerBar.show(); + //$('select#selectClassGroup option').eq(0).attr('selected', 'selected'); + pageManagementUtils.classesForClassGroup.addClass('hidden'); + pageManagementUtils.moreContentButton.show(); } - if ( $("#typeSelect").val() == "" ) { - $("section#classGroup").hide(); - $("section#nonClassGroup").hide(); - $("input#moreContent").hide(); - $('select#selectClassGroup option').eq(0).attr('selected', 'selected'); - $("section#classesInSelectedGroup").addClass('hidden'); - $("section#headerBar").hide(); - $("section#headerBar").text(""); + if ( _this.contentTypeSelect.val() == "" ) { + pageManagementUtils.classGroupSection.hide(); + pageManagementUtils.fixedHTMLSection.hide(); + pageManagementUtils.sparqlQuerySection.hide(); + + pageManagementUtils.moreContentButton.hide(); + + //$('select#selectClassGroup option').eq(0).attr('selected', 'selected'); + + pageManagementUtils.classesForClassGroup.addClass('hidden'); + pageManagementUtils.headerBar.hide(); + pageManagementUtils.headerBar.text(""); } pageManagementUtils.adjustSaveButtonHeight(); }); @@ -194,13 +214,17 @@ var pageManagementUtils = { });*/ //Submission: validate as well as create appropriate hidden json inputs - $("form").submit(function () { + $("form").submit(function (event) { var validationError = pageManagementUtils.validateMenuItemForm(); if (validationError == "") { //Create the appropriate json objects pageManagementUtils.createPageContentForSubmission(); + //return true; + //For testing, not submitting anything + //event.preventDefault(); return true; } else{ + $('#error-alert').removeClass('hidden'); $('#error-alert p').html(validationError); //TODO: Check why scrolling appears to be a problem @@ -209,69 +233,48 @@ var pageManagementUtils = { } }); + }, + //Clear values in content areas that are cloned to create the page content type specific sections + //i.e. reset sparql query/class group areas + //TODO: Check if reset is more what we need here? + clearSourceTemplateValues:function() { + //inputs, textareas + pageManagementUtils.clearInputs(pageManagementUtils.fixedHTMLSection); + pageManagementUtils.clearInputs(pageManagementUtils.sparqlQuerySection); + pageManagementUtils.clearInputs(pageManagementUtils.classGroupSection); + + }, + clearInputs:function($el) { + //jquery selector :input selects all input text area select and button elements + $el.find("input").val(""); + $el.find("textarea").val(""); + //resetting class group section as well so selection is reset if type changes + $el.find("select option:eq(0)").attr("selected", "selected"); + }, //Clone content area cloneContentArea: function(contentType, contentTypeLabel) { var ctr = pageManagementUtils.counter; var counter = pageManagementUtils.counter; - var varOrClas; + var varOrClass; + //Clone the object, renaming ids and copying text area values as well + $newContentObj = pageManagementUtils.createCloneObject(contentType, counter); - if ( contentType == "fixedHTML" || contentType == "sparqlQuery" ) { - var taValue = $('textarea#textArea').val(); - alert("original text area value is " + taValue); - var $newContentObj = $('section#nonClassGroup').clone(); - $newContentObj.addClass("pageContent"); - varOrClass = $newContentObj.find('input').val(); - $newContentObj.show(); - //Save content type - $newContentObj.attr("contentType", contentType); - $newContentObj.attr("id", contentType + counter); - $newContentObj.find('input#variable').attr("id","variable" + counter); - $newContentObj.find('textarea#textArea').attr("id","textArea" + counter); - $newContentObj.find('label#variableLabel').attr("id","variableLabel" + counter); - $newContentObj.find('label#taLabel').attr("id","taLabel" + counter); - - //Keep the name of the inputs the same - // $newContentObj.find('input#variable').attr("name","variable" + counter); - // $newContentObj.find('textarea#textArea').attr("name","textArea" + counter); - // There's a jquery bug when cloning textareas: the value doesn't - // get cloned. So - // copy the value "manually." - $newContentObj.find("textarea[name='textArea']").val(taValue); - } + if ( contentType == "sparqlQuery" || contentType == "fixedHtml") { + varOrClass = $newContentObj.find('input[name="saveToVar"]').val(); + } else if ( contentType == "browseClassGroup" ) { - - var $newContentObj = $('section#classGroup').clone(); - - $newContentObj.addClass("pageContent"); - $newContentObj.show(); - $newContentObj.attr("contentType", contentType); - $newContentObj.attr("id", "classGroup" + counter); - $newContentObj.find('section#selectContentType').attr("id", "selectContentType" + counter); - $newContentObj.find('select#selectClassGroup').val(groupValue); - $newContentObj.find('select#selectClassGroup').attr("id","selectClassGroup" + counter); - $newContentObj.find('select#selectClassGroup' + counter).attr("name","selectClassGroup" + counter); - $newContentObj.find('section#classesInSelectedGroup').attr("id","classesInSelectedGroup" + counter); - $newContentObj.find('section#classesInSelectedGroup' + counter).removeClass('hidden'); - $newContentObj.find('p#selectClassesMessage').attr("id","selectClassesMessage" + counter); - // Will need to uncomment this and find a way to apply the css style -// $newContentObj.find('section#internal-class').attr("id","internal-class" + -// counter); - $newContentObj.find("input[name='display-internalClass']").attr("name","display-internalClass" + counter); - $newContentObj.find('ul#selectedClasses').attr("id","selectedClasses" + counter); - $newContentObj.find('ul#selectedClasses' + counter).attr("name","selectedClasses" + counter); - - $newContentObj.find('ul#selectedClasses' + counter).children("li").each( function(i,abc) { - var $theCheckbox = $(this).find('input'); - $theCheckbox.attr("name", $theCheckbox.attr("name") + counter); - }); - - varOrClass = $newContentObj.find('select#selectClassGroup' + counter + ' option:selected').text(); + $newContentObj.find('section#classesInSelectedGroup' + counter).removeClass('hidden'); + varOrClass = $newContentObj.find('select#selectClassGroup' + counter + ' option:selected').text(); } + pageManagementUtils.createClonedContentContainer($newContentObj, counter, contentTypeLabel, varOrClass); + //previously increased by 10, just increasing by 1 here + pageManagementUtils.counter++; + }, + createClonedContentContainer:function($newContentObj, counter, contentTypeLabel, varOrClass) { //Create the container for the new content - $newDivContainer = $("
", { id: "divContainer" + counter, "class": "pageContentContainer", @@ -281,10 +284,20 @@ var pageManagementUtils = { + "' class='pageContentWrapper'>" }); - var $clickableSpan = $newDivContainer.children('span#clickable' + counter); + //Hide inner div var $innerDiv = $newDivContainer.children('div#innerContainer' + counter); $innerDiv.hide(); - //Expand/collapse toggle + //Bind event listers for the new content for display/removal etc. + pageManagementUtils.bindClonedContentContainerEvents($newDivContainer, counter); + //Append the new container to the section storing these containers + $newDivContainer.appendTo($('section#contentDivs')); + //place new content object + $newContentObj.prependTo($innerDiv); + }, + bindClonedContentContainerEvents:function($newDivContainer, counter) { + var $clickableSpan = $newDivContainer.children('span#clickable' + counter); + var $innerDiv = $newDivContainer.children('div#innerContainer' + counter); + //Expand/collapse toggle $clickableSpan.click(function() { if ( $innerDiv.is(':visible') ) { $innerDiv.slideUp(222); @@ -302,26 +315,20 @@ var pageManagementUtils = { } window.setTimeout('pageManagementUtils.adjustSaveButtonHeight()', 223); }); + //remove button $newRemoveButton = $innerDiv.find('input#remove' + counter); // this will have to disable submitted fields as well as hide them. $newRemoveButton.click(function() { $innerDiv.parent("div").css("display","none"); pageManagementUtils.adjustSaveButtonHeight(); }); - - $newDivContainer.appendTo($('section#contentDivs')); - $newContentObj.prependTo($innerDiv); - counter = counter + 10; }, resetClassGroupSection:function() { - $('select#selectClassGroup option').eq(0).attr('selected', 'selected'); - $("section#classesInSelectedGroup").addClass('hidden'); - }, - //finalize later, but basically use same attribute across page content and use attribute instead of id - //Attribute would be what keeps track of content, so contentCounter or something like that - toggleArrow:function() { - - + //doing this in clear inputs instead which will be triggered + //every time content type is changed AS well as on more content button after + //original content is cloned and stored + //$('select#selectClassGroup option').eq(0).attr('selected', 'selected'); + pageManagementUtils.classesForClassGroup.addClass('hidden'); }, //Adjust save button height adjustSaveButtonHeight:function() { @@ -345,7 +352,6 @@ var pageManagementUtils = { } else { //update existing content type with correct class group name and hide class group select again - var _this = pageManagementUtils; // pageManagementUtils.hideClassGroups(); pageManagementUtils.selectedGroupForPage.html(results.classGroupName); @@ -372,14 +378,14 @@ var pageManagementUtils = { //From NEW code if (pageManagementUtils.selectClassGroupDropdown.val() == "" ) { - $("section#classesInSelectedGroup").addClass('hidden'); + pageManagementUtils.classesForClassGroup.addClass('hidden'); $("div#leftSide").css("height",""); - $("input#moreContent").hide(); + pageManagementUtils.moreContentButton.hide(); } else { - $("section#classesInSelectedGroup").removeClass('hidden'); - $("input#moreContent").show(); + pageManagementUtils.classesForClassGroup.removeClass('hidden'); + pageManagementUtils.moreContentButton.show(); if ( $("div#leftSide").height() < $("div#rightSide").height() ) { $("div#leftSide").css("height",$("div#rightSide").height() + "px"); } @@ -390,39 +396,24 @@ var pageManagementUtils = { }); }, - /* - showClassGroups: function() { //User has clicked change content type - //Show the section with the class group dropdown - this.selectContentType.removeClass("hidden"); - //Hide the "change content type" section which shows the selected class group - this.existingContentType.addClass("hidden"); - //Hide the checkboxes for classes within the class group - this.classesForClassGroup.addClass("hidden"); - }, - hideClassGroups: function() { //User has selected class group/content type, page should show classes for class group and 'existing' type with change link - //Hide the class group dropdown - this.selectContentType.addClass("hidden"); - //Show the "change content type" section which shows the selected class group - this.existingContentType.removeClass("hidden"); - //Show the classes in the class group - this.classesForClassGroup.removeClass("hidden"); - - },*/ toggleClassSelection: function() { // Check/unckeck all classes for selection $('input:checkbox[name=allSelected]').click(function(){ if ( this.checked ) { - // if checked, select all the checkboxes - $('input:checkbox[name=classInClassGroup]').attr('checked','checked'); + // if checked, select all the checkboxes for this particular section + $(this).closest("ul").find('input:checkbox[name=classInClassGroup]').attr('checked','checked'); + //$('input:checkbox[name=classInClassGroup]').attr('checked','checked'); } else { // if not checked, deselect all the checkboxes - $('input:checkbox[name=classInClassGroup]').removeAttr('checked'); + $(this).closest("ul").find('input:checkbox[name=classInClassGroup]').removeAttr('checked'); + + // $('input:checkbox[name=classInClassGroup]').removeAttr('checked'); } }); $('input:checkbox[name=classInClassGroup]').click(function(){ - $('input:checkbox[name=allSelected]').removeAttr('checked'); + $(this).closest(ul).find('input:checkbox[name=allSelected]').removeAttr('checked'); }); }, //This is SPECIFIC to VIVO so should be moved there updateInternalClassMessage:function(classGroupName) { //User has changed content type @@ -482,29 +473,63 @@ var pageManagementUtils = { //Create a new hidden input with a specific name and assign value per page content pageManagementUtils.createPageContentInputForSubmission(jsonObjectString); }); - //in this case it's a sparql data getter, but what about the others - /* - var len = pageContentSections.length; - var i; - for(i = 0; i < len; i++) { - var pageContentSection = $(pageContentSections[i]); - var pageContentSectionId = pageContentSection.attr("id"); - var jsonObject = pageManagementUtils.processPageContentSection(pageContentSection); - var jsonObjectString = JSON.stringify(jsonObject); - //Create a new hidden input with a specific name and assign value per page content - pageManagementUtils.createPageContentInputForSubmission(jsonObjectString); - }*/ }, createPageContentInputForSubmission: function(inputValue) { $("").appendTo(pageManagementUtils.pageContentSubmissionInputs); }, + //returns a json object with the data getter information required processPageContentSection:function(pageContentSection) { - - var variableValue = pageContentSection.find("input[name='variable']").val(); - var queryValue = pageContentSection.find("textarea[name='textArea']").val(); - var returnObject = {saveToVar:variableValue, query:queryValue, dataGetterClass:pageManagementUtils.dataGetterLabelToURI["sparqlDataGetter"], queryModel:"vitro:contextDisplayModel"}; - return returnObject; + //This processing should be specific to the type and so that content type's specific processor will + //return the json object required + if(pageManagementUtils.processDataGetterUtils != null) { + var dataGetterType = pageManagementUtils.processDataGetterUtils.selectDataGetterType(pageContentSection); + if(pageManagementUtils.dataGetterProcessorMap != null) { + var dataGetterProcessor = pageManagementUtils.dataGetterProcessorMap[dataGetterType]; + //the content type specific processor will create the json object to be returned + var jsonObject = dataGetterProcessor.processPageContentSection(pageContentSection); + return jsonObject; + } else { + //ERROR handling + alert("An error has occurred and the map of processors for this content is missing. Please contact the administrator"); + return null; + } + } + alert("An error has occurred and the code for processing this content is missing a component. Please contact the administrator."); + //Error handling here + return null; + }, + //clones and returns cloned object for content type + createCloneObject:function(contentType, counter) { + var originalObjectPath = 'section#' + contentType; + var $newContentObj = $(originalObjectPath).clone(); + $newContentObj.addClass("pageContent"); + //Save content type + $newContentObj.attr("contentType", contentType); + //Set id for object + $newContentObj.attr("id", contentType + counter); + $newContentObj.show(); + pageManagementUtils.renameIdsInClone($newContentObj, counter); + // pageManagementUtils.cloneTextAreaValues(originalObjectPath, $newContentObj); + return $newContentObj; + }, + //This is specifically for cloning text area values + //May not need this if implementing the jquery fix + ///would need a similar function for select as well + cloneTextAreaValues:function(originalAncestorPath, $newAncestorObject) { + $(originalAncestorPath + " textarea").each(function(index, el) { + var originalTextAreaValue = $(this).val(); + var originalTextAreaName = $(this).attr("name"); + $newAncestorObject.find("textarea[name='" + originalTextAreaName + "']").val(originalTextAreaValue); + }); + }, + //given an object and a counter, rename all the ids + renameIdsInClone:function($newContentObj, counter) { + $newContentObj.find("[id]").each(function(index, el) { + var originalId = $(this).attr("id"); + var newId = originalId + counter; + $(this).attr("id", newId); + }); } }; diff --git a/webapp/web/js/menupage/processClassGroupDataGetterContent.js b/webapp/web/js/menupage/processClassGroupDataGetterContent.js new file mode 100644 index 000000000..9462eedd0 --- /dev/null +++ b/webapp/web/js/menupage/processClassGroupDataGetterContent.js @@ -0,0 +1,21 @@ +/* $This file is distributed under the terms of the license in /doc/license.txt$ */ + +//Process sparql data getter and provide a json object with the necessary information +//Depending on what is included here, a different type of data getter might be used +var processClassGroupDataGetterContent = { + dataGetterClass:null, + //can use this if expect to initialize from elsewhere + initProcessor:function(dataGetterClassInput) { + this.dataGetterClass = dataGetterClassInput; + }, + //Do we need a separate content type for each of the others? + processPageContentSection:function(pageContentSection) { + //Will look at classes etc. + var classGroup = pageContentSection.find("select[name='selectClassGroup']").val(); + //query model should also be an input, ensure class group URI is saved as URI and not string + var returnObject = {classGroup:classGroup, dataGetterClass:this.dataGetterClass}; + return returnObject; + } + + +} \ No newline at end of file diff --git a/webapp/web/js/menupage/processDataGetterUtils.js b/webapp/web/js/menupage/processDataGetterUtils.js new file mode 100644 index 000000000..9744bbe99 --- /dev/null +++ b/webapp/web/js/menupage/processDataGetterUtils.js @@ -0,0 +1,29 @@ +/* $This file is distributed under the terms of the license in /doc/license.txt$ */ + +//This class is responsible for the product-specific form processing/content selection that might be possible +//Overrides the usual behavior of selecting the specific JavaScript class needed to convert the form inputs +//into a JSON object for submission based on the page content type + +//This will need to be overridden or extended, what have you.. in VIVO +var processDataGetterUtils = { + dataGetterProcessorMap:{"browseClassGroup": processClassGroupDataGetterContent, + "sparqlQuery": processSparqlDataGetterContent, + "fixedHtml":processFixedHTMLDataGetterContent, + "individualsForClasses":processIndividualsForClassesDataGetterContent}, + selectDataGetterType:function(pageContentSection) { + var contentType = pageContentSection.attr("contentType"); + //The form can provide "browse class group" as content type but need to check + //whether this is in fact individuals for classes instead + if(contentType == "browseClassGroup") { + //Is ALL NOT selected and there are other classes, pick one + //this SHOULD be an array + var allClassesSelected = pageContentSection.find("input[name='allSelected']:checked"); + //If all NOT selected then need to pick a different content type + if(allClassesSelected.length == 0) { + contentType = "individualsForClasses"; + } + } + + return contentType; + } +}; \ No newline at end of file diff --git a/webapp/web/js/menupage/processFixedHTMLDataGetterContent.js b/webapp/web/js/menupage/processFixedHTMLDataGetterContent.js new file mode 100644 index 000000000..577a0e719 --- /dev/null +++ b/webapp/web/js/menupage/processFixedHTMLDataGetterContent.js @@ -0,0 +1,20 @@ +/* $This file is distributed under the terms of the license in /doc/license.txt$ */ + +//Process sparql data getter and provide a json object with the necessary information +var processFixedHTMLDataGetterContent = { + dataGetterClass:null, + //can use this if expect to initialize from elsewhere + initProcessor:function(dataGetterClass) { + this.dataGetterClass =dataGetterClass; + }, + //requires variable and text area + processPageContentSection:function(pageContentSection) { + var saveToVarValue = pageContentSection.find("input[name='saveToVar']").val(); + var htmlValue = pageContentSection.find("textarea[name='htmlValue']").val(); + //query model should also be an input + var returnObject = {saveToVar:saveToVarValue, htmlValue:htmlValue, dataGetterClass:this.dataGetterClass}; + return returnObject; + } + + +} \ No newline at end of file diff --git a/webapp/web/js/menupage/processIndividualsForClassesDataGetterContent.js b/webapp/web/js/menupage/processIndividualsForClassesDataGetterContent.js new file mode 100644 index 000000000..290ca11b3 --- /dev/null +++ b/webapp/web/js/menupage/processIndividualsForClassesDataGetterContent.js @@ -0,0 +1,24 @@ +/* $This file is distributed under the terms of the license in /doc/license.txt$ */ + +var processIndividualsForClassesDataGetterContent = { + dataGetterClass:null, + //can use this if expect to initialize from elsewhere + initProcessor:function(dataGetterClassInput) { + this.dataGetterClass = dataGetterClassInput; + }, + //Do we need a separate content type for each of the others? + processPageContentSection:function(pageContentSection) { + //Will look at classes etc. + var classGroup = pageContentSection.find("select[name='selectClassGroup']").val(); + //query model should also be an input + //Get classes selected + var classesSelected = []; + pageContentSection.find("input[name='classInClassGroup']:checked").each(function(){ + //Need to make sure that the class is also saved as a URI + classesSelected.push($(this).val()); + }); + var returnObject = {classGroup:classGroup, classesSelectedInClassGroup:classesSelected, dataGetterClass:this.dataGetterClass}; + return returnObject; + } + +} \ No newline at end of file diff --git a/webapp/web/js/menupage/processSparqlDataGetterContent.js b/webapp/web/js/menupage/processSparqlDataGetterContent.js new file mode 100644 index 000000000..fb00769a6 --- /dev/null +++ b/webapp/web/js/menupage/processSparqlDataGetterContent.js @@ -0,0 +1,23 @@ +/* $This file is distributed under the terms of the license in /doc/license.txt$ */ + +//Process sparql data getter and provide a json object with the necessary information +var processSparqlDataGetterContent = { + 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 queryValue = pageContentSection.find("textarea[name='query']").val(); + var queryModel = pageContentSection.find("input[name='queryModel']").val(); + + //query model should also be an input + //set query model to query model here - vitro:contentDisplayModel + var returnObject = {saveToVar:variableValue, query:queryValue, dataGetterClass:this.dataGetterClass, queryModel:queryModel}; + return returnObject; + } + + +}; \ No newline at end of file diff --git a/webapp/web/js/openSocial/shindig.js b/webapp/web/js/openSocial/shindig.js new file mode 100644 index 000000000..7809a3b7c --- /dev/null +++ b/webapp/web/js/openSocial/shindig.js @@ -0,0 +1,449 @@ +/* + Profiles Shindig Helper functions for gadget-to-container commands + + */ + + // dummy function so google analytics does not break for institutions who do not use it + +_gaq = {}; +_gaq.push = function(data) { // + }; + +// pubsub +gadgets.pubsubrouter.init(function(id) { + return my.gadgets[shindig.container.gadgetService.getGadgetIdFromModuleId(id)].url; + }, { + onSubscribe: function(sender, channel) { + setTimeout("my.onSubscribe('" + sender + "', '" + channel + "')", 3000); + // return true to reject the request. + return false; + }, + onUnsubscribe: function(sender, channel) { + //alert(sender + " unsubscribes from channel '" + channel + "'"); + // return true to reject the request. + return false; + }, + onPublish: function(sender, channel, message) { + // return true to reject the request. + + // track with google analytics + if (sender != '..' ) { + var moduleId = shindig.container.gadgetService.getGadgetIdFromModuleId(sender); + } + + if (channel == 'VISIBLE') { + var statusId = document.getElementById(sender + '_status'); + if (statusId) { + // only act on these in HOME view since they are only meant to be seen when viewer=owner + if (my.gadgets[moduleId].view != 'home') { + return true; + } + if (message == 'Y') { + statusId.style.color = 'GREEN'; + statusId.innerHTML = 'This section is VISIBLE'; + if (my.gadgets[moduleId].visible_scope == 'U') { + statusId.innerHTML += ' to UCSF'; + } + else { + statusId.innerHTML += ' to the public'; + } + } + else { + statusId.style.color = '#CC0000'; + statusId.innerHTML = 'This section is HIDDEN'; + if (my.gadgets[moduleId].visible_scope == 'U') { + statusId.innerHTML += ' from UCSF'; + } + else { + statusId.innerHTML += ' from the public'; + } + } + } + } + else if (channel == 'added' && my.gadgets[moduleId].view == 'home') { + if (message == 'Y') { + _gaq.push(['_trackEvent', my.gadgets[moduleId].name, 'SHOW', 'profile_edit_view']); + osapi.activities.create( + { 'userId': gadgets.util.getUrlParameters()['Person'], + 'appId': my.gadgets[moduleId].appId, + 'activity': {'postedTime': new Date().getTime(), 'title': 'added a gadget', 'body': 'added the ' + my.gadgets[moduleId].name + ' gadget to their profile' } + }).execute(function(response){}); + } + else { + _gaq.push(['_trackEvent', my.gadgets[moduleId].name, 'HIDE', 'profile_edit_view']); + } + } + else if (channel == 'status') { + // message should be of the form 'COLOR:Message Content' + var statusId = document.getElementById(sender + '_status'); + if (statusId) { + var messageSplit = message.split(':'); + if (messageSplit.length == 2) { + statusId.style.color = messageSplit[0]; + statusId.innerHTML = messageSplit[1]; + } + else { + statusId.innerHTML = message; + } + } + } + else if (channel == 'analytics') { + // publish to google analytics + // message should be JSON encoding object with required action and optional label and value + // as documented here: http://code.google.com/apis/analytics/docs/tracking/eventTrackerGuide.html + // note that event category will be set to the gadget name automatically by this code + // Note: message will be already converted to an object + if (message.hasOwnProperty('value')) { + _gaq.push(['_trackEvent', my.gadgets[moduleId].name, message.action, message.label, message.value]); + } + else if (message.hasOwnProperty('label')) { + _gaq.push(['_trackEvent', my.gadgets[moduleId].name, message.action, message.label]); + } + else { + _gaq.push(['_trackEvent', my.gadgets[moduleId].name, message.action]); + } + } + else if (channel == 'profile') { + _gaq.push(['_trackEvent', my.gadgets[moduleId].name, 'go_to_profile', message]); + document.location.href = '/' + location.pathname.split('/')[1] + '/display/n' + message; + } + else if (channel == 'JSONPersonIds' || channel == 'JSONPubMedIds') { + // do nothing, no need to alert + } + else { + alert(sender + " publishes '" + message + "' to channel '" + channel + "'"); + } + return false; + } +}); + +// helper functions +my.findGadgetsAttachingTo = function(chromeId) { + var retval = []; + for (var i = 0; i < my.gadgets.length; i++) { + if (my.gadgets[i].chrome_id == chromeId) { + retval[retval.length] = my.gadgets[i]; + } + } + return retval; +}; + +my.removeGadgets = function(gadgetsToRemove) { + for (var i = 0; i < gadgetsToRemove.length; i++) { + for (var j = 0; j < my.gadgets.length; j++) { + if (gadgetsToRemove[i].url == my.gadgets[j].url) { + my.gadgets.splice(j, 1); + break; + } + } + } +}; + +my.onSubscribe = function(sender, channel) { + // lookup pubsub data based on channel and if a match is found, publish the data to that channel after a delay + if (my.pubsubData[channel]) { + gadgets.pubsubrouter.publish(channel, my.pubsubData[channel]); + } + else { + alert(sender + " subscribes to channel '" + channel + "'"); + } + //PageMethods.onSubscribe(sender, channel, my.pubsubHint, my.CallSuccess, my.CallFailed); +}; + +my.removeParameterFromURL = function(url, parameter) { + var urlparts= url.split('?'); // prefer to use l.search if you have a location/link object + if (urlparts.length>=2) { + var prefix= encodeURIComponent(parameter)+'='; + var pars= urlparts[1].split(/[&;]/g); + for (var i= pars.length; i-->0;) //reverse iteration as may be destructive + if (pars[i].lastIndexOf(prefix, 0)!==-1) //idiom for string.startsWith + pars.splice(i, 1); + url= urlparts[0]+'?'+pars.join('&'); + } + return url; +}; + + // publish the people +my.CallSuccess = function(result) { + gadgets.pubsubrouter.publish('person', result); +}; + + // alert message on some failure +my.CallFailed = function(error) { + alert(error.get_message()); +}; + +my.requestGadgetMetaData = function(view, opt_callback) { + var request = { + context: { + country: "default", + language: "default", + view: view, + ignoreCache : my.noCache, + container: "default" + }, + gadgets: [] + }; + + for (var moduleId = 0; moduleId < my.gadgets.length; moduleId++) { + // only add those with matching views + if (my.gadgets[moduleId].view == view) { + request.gadgets[request.gadgets.length] = {'url': my.gadgets[moduleId].url, 'moduleId': moduleId}; + } + } + + var makeRequestParams = { + "CONTENT_TYPE" : "JSON", + "METHOD" : "POST", + "POST_DATA" : gadgets.json.stringify(request)}; + + gadgets.io.makeNonProxiedRequest(my.openSocialURL + "/gadgets/metadata", + function(data) { + data = data.data; + if (opt_callback) { + opt_callback(data); + } + }, + makeRequestParams, + "application/javascript" + ); +}; + +my.renderableGadgets = []; + +my.generateGadgets = function(metadata) { + // put them in moduleId order + for (var i = 0; i < metadata.gadgets.length; i++) { + var moduleId = metadata.gadgets[i].moduleId; + // Notes by Eric. Not sure if I should have to calculate this myself, but I will. + var height = metadata.gadgets[i].height; + var width = metadata.gadgets[i].width; + var viewPrefs = metadata.gadgets[i].views[my.gadgets[moduleId].view]; + if (viewPrefs) { + height = viewPrefs.preferredHeight || height; + width = viewPrefs.preferredWidth || width; + } + my.renderableGadgets[moduleId] = shindig.container.createGadget({'specUrl': metadata.gadgets[i].url, 'secureToken': my.gadgets[moduleId].secureToken, + 'title': metadata.gadgets[i].title, 'userPrefs': metadata.gadgets[i].userPrefs, + 'height': height, 'width': width, 'debug': my.debug}); + // set the metadata for easy access + my.renderableGadgets[moduleId].setMetadata(metadata.gadgets[i]); + } + // this will be called multiple times, only render when all gadgets have been processed + var ready = my.renderableGadgets.length == my.gadgets.length; + for (var i = 0; ready && i < my.renderableGadgets.length; i++) { + if (!my.renderableGadgets[i]) { + ready = false; + } + } + + if (ready) { + shindig.container.addGadgets(my.renderableGadgets ); + shindig.container.renderGadgets(); + } +}; + +my.init = function() { + // overwrite this RPC function. Do it at this level so that rpc.f (this.f) is accessible for getting module ID +// gadgets.rpc.register('requestNavigateTo', doProfilesNavigation); + + shindig.container.gadgetClass = ProfilesGadget; + shindig.container.layoutManager = new ProfilesLayoutManager(); + shindig.container.setNoCache(my.noCache); + shindig.container.gadgetService = new ProfilesGadgetService(); + + // since we render multiple views, we need to do somethign fancy by swapping out this value in getIframeUrl + shindig.container.setView('REPLACE_THIS_VIEW'); + + // do multiple times as needed if we have multiple views + // find out what views are being used and call requestGadgetMetaData for each one + var views = {}; + for (var moduleId = 0; moduleId < my.gadgets.length; moduleId++) { + var view = my.gadgets[moduleId].view; + if (!views[view]) { + views[view] = view; + my.requestGadgetMetaData(view, my.generateGadgets); + } + } +}; + +// ProfilesGadgetService + +ProfilesGadgetService = function() { + shindig.IfrGadgetService.call(this); +}; + +ProfilesGadgetService.inherits(shindig.IfrGadgetService); + +ProfilesGadgetService.prototype.requestNavigateTo = function(view, opt_params) { + var urlTemplate = gadgets.config.get('views')[view].urlTemplate; + var url = urlTemplate || 'OpenSocial.aspx?'; + + url += window.location.search.substring(1); + + // remove appId if present + url = my.removeParameterFromURL(url, 'appId'); + + // Add appId if the URL Template begins with the word 'gadget' + if (urlTemplate.toLowerCase().indexOf('gadget') == 0) { + var moduleId = shindig.container.gadgetService.getGadgetIdFromModuleId(this.f); + var appId = my.gadgets[moduleId].appId; + url += (url.indexOf('?') != url.length - 1 ? '&' : '') + 'appId=' + appId; + } + + if (opt_params) { + var paramStr = gadgets.json.stringify(opt_params); + if (paramStr.length > 0) { + url += (url.indexOf('?') != url.length - 1 ? '&' : '') + 'appParams=' + encodeURIComponent(paramStr); + } + } + if (url && document.location.href.indexOf(url) == -1) { + document.location.href = url; + } +}; + +// ProfilesGadget + +ProfilesGadget = function(opt_params) { + shindig.Gadget.call(this, opt_params); + this.debug = my.debug; + this.serverBase_ = my.openSocialURL + "/gadgets/"; + var gadget = this; + var subClass = shindig.IfrGadget; + this.metadata = {}; + for (var name in subClass) if (subClass.hasOwnProperty(name)) { + if (name == 'getIframeUrl') { + // we need to keep this old one + gadget['originalGetIframeUrl'] = subClass[name]; + } + else if (name != 'finishRender') { + gadget[name] = subClass[name]; + } + } +}; + +ProfilesGadget.inherits(shindig.BaseIfrGadget); + +ProfilesGadget.prototype.setMetadata = function(metadata) { + this.metadata = metadata; +}; + +ProfilesGadget.prototype.hasFeature = function(feature) { + for (var i = 0; i < this.metadata.features.length; i++) { + if (this.metadata.features[i] == feature) { + return true; + } + } + return false; +}; + +ProfilesGadget.prototype.getAdditionalParams = function() { + var params = ''; + for (var key in my.gadgets[this.id].additionalParams) { + params += '&' + key + '=' + my.gadgets[this.id].additionalParams[key]; + } + return params; +}; + +ProfilesGadget.prototype.finishRender = function(chrome) { + window.frames[this.getIframeId()].location = this.getIframeUrl(); + if (my.gadgets[this.id].start_closed) { + this.handleToggle(); + } + else if (chrome) { + // set the gadget box width, and remember that we always render as open + chrome.style.width = (my.gadgets[this.id].open_width || 600) + 'px'; + } +}; + +ProfilesGadget.prototype.getIframeUrl = function() { + var url = this.originalGetIframeUrl(); + return url.replace('REPLACE_THIS_VIEW', my.gadgets[this.id].view); +}; + +ProfilesGadget.prototype.handleToggle = function() { + var gadgetIframe = document.getElementById(this.getIframeId()); + if (gadgetIframe) { + var gadgetContent = gadgetIframe.parentNode; + var gadgetImg = document.getElementById('gadgets-gadget-title-image-' + this.id); + if (gadgetContent.style.display) { + //OPEN + gadgetContent.parentNode.style.width = (my.gadgets[this.id].open_width || 600) + 'px'; + gadgetContent.style.display = ''; + gadgetImg.src = '/' + location.pathname.split('/')[1] + '/themes/opensocial/images/openSocial/icon_squareDownArrow.gif'; + // refresh if certain features require so + //if (this.hasFeature('dynamic-height')) { + if (my.gadgets[this.id].chrome_id == 'gadgets-search') { + this.refresh(); + document.getElementById(this.getIframeId()).contentWindow.location.reload(true); + } + + if (my.gadgets[this.id].view == 'home') { + // record in google analytics + _gaq.push(['_trackEvent', my.gadgets[this.id].name, 'OPEN_IN_EDIT', 'profile_edit_view']); + } + else { + osapi.activities.create( + { 'userId': gadgets.util.getUrlParameters()['Person'], + 'appId': my.gadgets[this.id].appId, + 'activity': {'postedTime': new Date().getTime(), 'title': 'gadget viewed', 'body': my.gadgets[this.id].name + ' gadget was viewed' } + }).execute(function(response){}); + // record in google analytics + _gaq.push(['_trackEvent', my.gadgets[this.id].name, 'OPEN']); + } + } + else { + //CLOSE + gadgetContent.parentNode.style.width = (my.gadgets[this.id].closed_width || 600) + 'px'; + gadgetContent.style.display = 'none'; + gadgetImg.src = '/' + location.pathname.split('/')[1] + '/themes/opensocial/images/openSocial/icon_squareArrow.gif'; + if (my.gadgets[this.id].view == 'home') { + // record in google analytics + _gaq.push(['_trackEvent', my.gadgets[this.id].name, 'CLOSE_IN_EDIT', 'profile_edit_view']); + } + else { + // record in google analytics + _gaq.push(['_trackEvent', my.gadgets[this.id].name, 'CLOSE']); + } + } + } +}; + +ProfilesGadget.prototype.getTitleBarContent = function(continuation) { + if (my.gadgets[this.id].view == 'canvas') { + document.getElementById("gadgets-title").innerHTML = (this.title ? this.title : 'Gadget'); + continuation(''); + } + else { + continuation( + ''); + } +}; + +// ProfilesLayoutManager. Creates a FloatLeftLayoutManager for every chromeId in our gadgets +ProfilesLayoutManager = function() { + shindig.LayoutManager.call(this); + // find out what chromeId's are being used, create a FloatLeftLayoutManager for each + this.layoutManagers = {}; + for (var moduleId = 0; moduleId < my.gadgets.length; moduleId++) { + var chromeId = my.gadgets[moduleId].chrome_id; + if (!this.layoutManagers[chromeId]) { + this.layoutManagers[chromeId] = new shindig.FloatLeftLayoutManager(chromeId); + } + } +}; + +ProfilesLayoutManager.inherits(shindig.LayoutManager); + +ProfilesLayoutManager.prototype.getGadgetChrome = function(gadget) { + return this.layoutManagers[my.gadgets[gadget.id].chrome_id].getGadgetChrome(gadget); +}; \ No newline at end of file diff --git a/webapp/web/js/siteAdmin/classHierarchyUtils.js b/webapp/web/js/siteAdmin/classHierarchyUtils.js index da516c282..55d8ca7fc 100644 --- a/webapp/web/js/siteAdmin/classHierarchyUtils.js +++ b/webapp/web/js/siteAdmin/classHierarchyUtils.js @@ -32,6 +32,7 @@ this.form = $('form#classHierarchyForm'); this.select = $('select#displayOption'); this.addClass = $('input#addClass'); + this.addGroup = $('input#addGroup'); }, bindEventListeners: function() { @@ -49,6 +50,10 @@ classHierarchyUtils.form.attr("action", "vclass_retry"); classHierarchyUtils.form.submit(); }); + this.addGroup.click(function() { + classHierarchyUtils.form.attr("action", "editForm?controller=Classgroup"); + classHierarchyUtils.form.submit(); + }); }, buildClassHierarchyHtml: function() { @@ -96,6 +101,7 @@ getTheChildren: function(node) { var childDetails = ""; + var subclassString = " "; var ctr = 0 $.each(node.children, function() { if ( ctr == 0 ) { @@ -105,12 +111,18 @@ else { childDetails += "" ; } - + + if ( this.children.length == 1 ) { + subclassString += " (1 subclass)"; + } + else if ( this.children.length > 1 ) { + subclassString += " (" + this.children.length + " subclasses)"; + } childDetails += " " - + this.name + ""; - + subclassString = " "; classHierarchyUtils.clickableSpans.push('subclassExpand' + classHierarchyUtils.expandCounter); classHierarchyUtils.expandCounter += 1; @@ -141,13 +153,13 @@ $clickableHeader.click(function() { if ( $clickableHeader.attr('view') == "less" ) { $clickableHeader.addClass("headerSpanMinus"); - $('table#classHierarchy' + ctr).find('span').addClass("subclassExpandMinus"); + $('table#classHierarchy' + ctr).find('span.subclassExpandPlus').addClass("subclassExpandMinus"); $('table#classHierarchy' + ctr).find('table.subclassTable').show(); $clickableHeader.attr('view', 'more' ); } else { $clickableHeader.removeClass("headerSpanMinus"); - $('table#classHierarchy' + ctr).find('span').removeClass("subclassExpandMinus"); + $('table#classHierarchy' + ctr).find('span.subclassExpandPlus').removeClass("subclassExpandMinus"); $('table#classHierarchy' + ctr).find('table.subclassTable').hide(); $clickableHeader.attr('view', 'less' ); } @@ -180,14 +192,14 @@ if ( classHierarchyUtils.expandAll.text() == "expand all" ) { classHierarchyUtils.expandAll.text("collapse all"); $('span.headerSpanPlus').addClass("headerSpanMinus"); - $('table.classHierarchy').find('span').addClass("subclassExpandMinus"); + $('table.classHierarchy').find('span.subclassExpandPlus').addClass("subclassExpandMinus"); $('table.classHierarchy').find('table.subclassTable').show(); $('section#container').find('span.headerSpanPlus').attr('view','more'); } else { classHierarchyUtils.expandAll.text("expand all"); $('span.headerSpanPlus').removeClass("headerSpanMinus"); - $('table.classHierarchy').find('span').removeClass("subclassExpandMinus"); + $('table.classHierarchy').find('span.subclassExpandPlus').removeClass("subclassExpandMinus"); $('table.classHierarchy').find('table.subclassTable').hide(); $('section#container').find('span.headerSpanPlus').attr('view','less'); } diff --git a/webapp/web/js/siteAdmin/objectPropertyHierarchyUtils.js b/webapp/web/js/siteAdmin/objectPropertyHierarchyUtils.js new file mode 100644 index 000000000..de0b96db8 --- /dev/null +++ b/webapp/web/js/siteAdmin/objectPropertyHierarchyUtils.js @@ -0,0 +1,293 @@ +/* $This file is distributed under the terms of the license in /doc/license.txt$ */ + + var objectPropHierarchyUtils = { + onLoad: function(urlBase,displayOption,type) { + this.imagePath = urlBase + "/images/"; + this.propType = type; + this.initObjects(); + this.expandAll.hide(); + this.checkJsonTree(); + + if ( noProps ) { + this.buildNoPropsHtml(); + } + else if ( displayOption == "all" ) { + this.buildAllPropsHtml(); + } + else { + this.buildPropertyHierarchyHtml(); + this.wireExpandLink(); + } + + if ( displayOption == "hierarchy" ) { + this.expandAll.show(); + } + this.bindEventListeners(); + }, + + initObjects: function() { + this.expandAll = $('span#expandAll').find('a'); + this.classCounter = 1; + this.expandCounter = 1; + this.classHtml = ""; + this.clickableSpans = [] ; + this.form = $('form#classHierarchyForm'); + this.select = $('select#displayOption'); + this.addProperty = $('input#addProperty'); + noProps = new Boolean; + }, + + bindEventListeners: function() { + if ( this.propType == "object" ) { + this.select.change(function() { + if ( objectPropHierarchyUtils.select.val() == "all") { + objectPropHierarchyUtils.form.attr("action", "listPropertyWebapps"); + } + else { + objectPropHierarchyUtils.form.attr("action", "showObjectPropertyHierarchy"); + } + objectPropHierarchyUtils.form.submit(); + }); + + this.addProperty.click(function() { + objectPropHierarchyUtils.form.attr("action", "editForm?controller=Property"); + objectPropHierarchyUtils.form.submit(); + }); + } + else { + this.select.change(function() { + if ( objectPropHierarchyUtils.select.val() == "all") { + objectPropHierarchyUtils.form.attr("action", "listDatatypeProperties"); + } + else { + objectPropHierarchyUtils.form.attr("action", "showDataPropertyHierarchy"); + } + objectPropHierarchyUtils.form.submit(); + }); + + this.addProperty.click(function() { + objectPropHierarchyUtils.form.attr("action", "editForm?controller=Dataprop"); + objectPropHierarchyUtils.form.submit(); + }); + } + }, + + checkJsonTree: function() { + if ( json.length == 1 ) { + $.each(json, function() { + // check to see whether we have a 'no properties' message or an actual json tree + if ( this.name.indexOf("properties") != -1 && this.data == undefined ) { + noProps = true; + } + else { + noProps = false; + } + }); + } + else { + noProps = false; + } + }, + + buildPropertyHierarchyHtml: function() { + + $.each(json, function() { + $newClassSection = jQuery("
", { + id: "classContainer" + objectPropHierarchyUtils.classCounter + }); + var descendants = ""; + var headerSpan = ""; + + if ( this.children.length ) { + descendants = objectPropHierarchyUtils.getTheChildren(this); + headerSpan = " "; + } + + objectPropHierarchyUtils.classHtml += "
" + this.name + headerSpan + "
" + "
" ; + + objectPropHierarchyUtils.classHtml += ""; + + objectPropHierarchyUtils.classHtml += ""; + + objectPropHierarchyUtils.classHtml += ""; + + + objectPropHierarchyUtils.classHtml += descendants; + + objectPropHierarchyUtils.classHtml += "
Local Name:" + + (this.data.internalName.length > 0 ? this.data.internalName : "none" ) + "
Group:" + + (this.data.group.length > 0 ? this.data.group : "unspecified" ) + "
Domain Class:" + + (this.data.domainVClass.length > 0 ? this.data.domainVClass : "none" ) + " "; + + objectPropHierarchyUtils.classHtml += "Range Class:" + + (this.data.rangeVClass.length > 0 ? this.data.rangeVClass : "none" ) + "
"; + // alert(objectPropHierarchyUtils.classHtml); + $newClassSection.html(objectPropHierarchyUtils.classHtml); + $newClassSection.appendTo($('section#container')); + objectPropHierarchyUtils.makeHeaderSpansClickable(objectPropHierarchyUtils.classCounter); + objectPropHierarchyUtils.makeSubpropertySpansClickable(); + objectPropHierarchyUtils.clickableSpans = [] ; + objectPropHierarchyUtils.classHtml = ""; + objectPropHierarchyUtils.classCounter += 1; + }); + }, + + getTheChildren: function(node) { + var childDetails = ""; + var subclassString = " "; + var ctr = 0 + $.each(node.children, function() { + if ( ctr == 0 ) { + childDetails += "Subproperties:"; + ctr = ctr + 1; + } + else { + childDetails += "" ; + } + + if ( this.children.length == 1 ) { + subclassString += " (1 subclass)"; + } + else if ( this.children.length > 1 ) { + subclassString += " (" + this.children.length + " subclasses)"; + } + + childDetails += " " + + this.name + subclassString + ""; + + subclassString = " "; + + objectPropHierarchyUtils.clickableSpans.push('subclassExpand' + objectPropHierarchyUtils.expandCounter); + + objectPropHierarchyUtils.expandCounter += 1; + + childDetails += ""; + + childDetails += ""; + + childDetails += ""; + + if ( this.children ) { + var grandChildren = objectPropHierarchyUtils.getTheChildren(this); + childDetails += grandChildren; + } + }); + childDetails += "
Local Name:" + + (this.data.internalName.length > 0 ? this.data.internalName : "none" ) + "
Group:" + + (this.data.group.length > 0 ? this.data.group : "unspecified" ) + "
Domain Class:" + + (this.data.domainVClass.length > 0 ? this.data.domainVClass : "none" ) + " "; + + childDetails += "Range Class:" + + (this.data.rangeVClass.length > 0 ? this.data.rangeVClass : "none" ) + "
"; + return childDetails; + }, + + makeHeaderSpansClickable: function(ctr) { + + var $clickableHeader = $('section#classContainer' + ctr).find('span.headerSpanPlus'); + + $clickableHeader.click(function() { + if ( $clickableHeader.attr('view') == "less" ) { + $clickableHeader.addClass("headerSpanMinus"); + $('table#classHierarchy' + ctr).find('span.subclassExpandPlus').addClass("subclassExpandMinus"); + $('table#classHierarchy' + ctr).find('table.subclassTable').show(); + $clickableHeader.attr('view', 'more' ); + } + else { + $clickableHeader.removeClass("headerSpanMinus"); + $('table#classHierarchy' + ctr).find('span.subclassExpandPlus').removeClass("subclassExpandMinus"); + $('table#classHierarchy' + ctr).find('table.subclassTable').hide(); + $clickableHeader.attr('view', 'less' ); + } + }); + },// $('myOjbect').css('background-image', 'url(' + imageUrl + ')'); + + makeSubpropertySpansClickable: function() { + $.each(objectPropHierarchyUtils.clickableSpans, function() { + var currentSpan = this; + var $clickableSpan = $('section#container').find('span#' + currentSpan); + var $subclassTable = $('section#container').find('table#subclassTable' + currentSpan.replace("subclassExpand","")); + + $clickableSpan.click(function() { + if ( $subclassTable.is(':visible') ) { + $subclassTable.hide(); + $subclassTable.find('table.subclassTable').hide(); + $subclassTable.find('span').removeClass("subclassExpandMinus"); + $clickableSpan.removeClass("subclassExpandMinus"); + } + else { + $subclassTable.show(); + $clickableSpan.addClass("subclassExpandMinus"); + } + }); + }); + }, + + wireExpandLink: function() { + this.expandAll.click(function() { + if ( objectPropHierarchyUtils.expandAll.text() == "expand all" ) { + objectPropHierarchyUtils.expandAll.text("collapse all"); + $('span.headerSpanPlus').addClass("headerSpanMinus"); + $('table.classHierarchy').find('span.subclassExpandPlus').addClass("subclassExpandMinus"); + $('table.classHierarchy').find('table.subclassTable').show(); + $('section#container').find('span.headerSpanPlus').attr('view','more'); + } + else { + objectPropHierarchyUtils.expandAll.text("expand all"); + $('span.headerSpanPlus').removeClass("headerSpanMinus"); + $('table.classHierarchy').find('span.subclassExpandPlus').removeClass("subclassExpandMinus"); + $('table.classHierarchy').find('table.subclassTable').hide(); + $('section#container').find('span.headerSpanPlus').attr('view','less'); + } + }); + }, + + buildAllPropsHtml: function() { + + $.each(json, function() { + $newClassSection = jQuery("
", { + id: "classContainer" + objectPropHierarchyUtils.classCounter + }); + + objectPropHierarchyUtils.classHtml += "
" + this.name + "
" + "" ; + + objectPropHierarchyUtils.classHtml += ""; + + objectPropHierarchyUtils.classHtml += ""; + + objectPropHierarchyUtils.classHtml += ""; + + objectPropHierarchyUtils.classHtml += "
Local Name:" + + (this.data.internalName.length > 0 ? this.data.internalName : "none" ) + "
Group:" + + (this.data.group.length > 0 ? this.data.group : "unspecified" ) + "
Domain Class:" + + (this.data.domainVClass.length > 0 ? this.data.domainVClass : "none" ) + " "; + + objectPropHierarchyUtils.classHtml += "Range Class:" + + (this.data.rangeVClass.length > 0 ? this.data.rangeVClass : "none" ) + "
"; + + $newClassSection.html(objectPropHierarchyUtils.classHtml); + $newClassSection.appendTo($('section#container')); + objectPropHierarchyUtils.classHtml = ""; + objectPropHierarchyUtils.classCounter += 1; + }); + }, + + buildNoPropsHtml: function() { + + $.each(json, function() { + $newClassSection = jQuery("
", { + id: "classContainer" + objectPropHierarchyUtils.classCounter + }); + + objectPropHierarchyUtils.classHtml = "

" + this.name + "

"; + $newClassSection.html(objectPropHierarchyUtils.classHtml); + $newClassSection.appendTo($('section#container')); + objectPropHierarchyUtils.classHtml = ""; + }); + } +} diff --git a/webapp/web/templates/edit/specific/dataprop_retry.jsp b/webapp/web/templates/edit/specific/dataprop_retry.jsp index e13eb1288..c0ff71c19 100644 --- a/webapp/web/templates/edit/specific/dataprop_retry.jsp +++ b/webapp/web/templates/edit/specific/dataprop_retry.jsp @@ -1,16 +1,16 @@ <%-- $This file is distributed under the terms of the license in /doc/license.txt$ --%> <%@ taglib prefix="form" uri="http://vitro.mannlib.cornell.edu/edit/tags" %> -<%@ taglib uri="http://java.sun.com/jstl/core" prefix="c" %> -<%-- colspan set to 4 in DatapropRetryController.java --%> - - - Public Name
as will display on public pages
+<%@ taglib uri="http://java.sun.com/jstl/core" prefix="c" %> +<%-- colspan set to 4 in DatapropRetryController.java --%> + + + Public Name
as will display on public pages
"/> - + Property Group
@@ -27,10 +27,10 @@ Update Level
(specify least restrictive level allowed)
- + - - + + Ontology
@@ -45,42 +45,42 @@ - - - + + + Local Name* -
Change only via the "change URI" button on the previous screen
+
Change only via the "change URI" button on the previous screen
" disabled="disabled"/>
(must be a valid XML name)
startLowercaseAndUseCamelStyle

"/>
-
+ - + - - - - - Domain Class
- - - - - - - Range Datatype
- + + + + + Domain Class
+ + + + + + + Range Datatype
+ @@ -92,7 +92,7 @@ ${functionalLabel} - + @@ -104,17 +104,17 @@ - - - - - Description for ontology editors
+ + + + + Description for ontology editors
- - + + @@ -126,24 +126,24 @@ - - - - - Display Tier
+ + + + + Display Tier
"/> - - - - Display Limit
+ + + + Data Entry Limit
"/> - + Optional: custom entry form
@@ -155,4 +155,4 @@ - + diff --git a/webapp/web/templates/freemarker/body/menupage/menupage--defaultFixedHtml.ftl b/webapp/web/templates/freemarker/body/menupage/menupage--defaultFixedHtml.ftl new file mode 100644 index 000000000..d44ca480e --- /dev/null +++ b/webapp/web/templates/freemarker/body/menupage/menupage--defaultFixedHtml.ftl @@ -0,0 +1,15 @@ +<#-- $This file is distributed under the terms of the license in /doc/license.txt$ --> + +<#--Save to variable indicated in generator --> + +<#assign htmlExists = false/> +<#if variableName?has_content> + <#assign htmlExists = true /> + +<#if htmlExists> + ${.globals[variableName]} +<#else> + No HTML specified. + + + 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 c4ffd52b4..b8e91690a 100644 --- a/webapp/web/templates/freemarker/body/partials/individual/individual-properties.ftl +++ b/webapp/web/templates/freemarker/body/partials/individual/individual-properties.ftl @@ -6,6 +6,7 @@ <#assign subjectUri = individual.controlPanelUrl()?split("=") > <#list propertyGroups.all as group> <#assign groupName = group.getName(nameForOtherGroup)> + <#assign verbose = (verbosePropertySwitch.currentValue)!false>