diff --git a/webapp/config/web.xml b/webapp/config/web.xml index d93d5fe3f..e08e3e656 100644 --- a/webapp/config/web.xml +++ b/webapp/config/web.xml @@ -1020,18 +1020,6 @@ /dwr/* - - - sendmail - edu.cornell.mannlib.vitro.webapp.controller.ContactMailServlet - 5 - - - sendmail - /sendmail - - - sendMail edu.cornell.mannlib.vitro.webapp.controller.freemarker.ContactMailController diff --git a/webapp/src/edu/cornell/mannlib/vitro/webapp/auth/identifier/RequestIdentifiers.java b/webapp/src/edu/cornell/mannlib/vitro/webapp/auth/identifier/RequestIdentifiers.java index 9eff6e6f2..d94433bc5 100644 --- a/webapp/src/edu/cornell/mannlib/vitro/webapp/auth/identifier/RequestIdentifiers.java +++ b/webapp/src/edu/cornell/mannlib/vitro/webapp/auth/identifier/RequestIdentifiers.java @@ -46,4 +46,11 @@ public class RequestIdentifiers { return (IdentifierBundle) obj; } + /** + * The login status has changed, so discard the cached Identifiers. + */ + public static void resetIdentifiers(ServletRequest request) { + request.removeAttribute(ATTRIBUTE_ID_BUNDLE); + } + } diff --git a/webapp/src/edu/cornell/mannlib/vitro/webapp/auth/identifier/common/CommonIdentifierBundleFactory.java b/webapp/src/edu/cornell/mannlib/vitro/webapp/auth/identifier/common/CommonIdentifierBundleFactory.java index b2353e6ae..df0ffd012 100644 --- a/webapp/src/edu/cornell/mannlib/vitro/webapp/auth/identifier/common/CommonIdentifierBundleFactory.java +++ b/webapp/src/edu/cornell/mannlib/vitro/webapp/auth/identifier/common/CommonIdentifierBundleFactory.java @@ -24,7 +24,6 @@ import edu.cornell.mannlib.vitro.webapp.beans.Individual; import edu.cornell.mannlib.vitro.webapp.beans.SelfEditingConfiguration; import edu.cornell.mannlib.vitro.webapp.beans.UserAccount; import edu.cornell.mannlib.vitro.webapp.dao.IndividualDao; -import edu.cornell.mannlib.vitro.webapp.dao.UserAccountsDao; import edu.cornell.mannlib.vitro.webapp.dao.WebappDaoFactory; /** @@ -73,7 +72,7 @@ public class CommonIdentifierBundleFactory implements IdentifierBundleFactory { private Collection createRootUserIdentifiers( HttpServletRequest req) { UserAccount user = LoginStatusBean.getCurrentUser(req); - if (isRootUser(user)) { + if ((user != null) && user.isRootUser()) { return Collections.singleton(new IsRootUser()); } else { return Collections.emptySet(); @@ -143,25 +142,6 @@ public class CommonIdentifierBundleFactory implements IdentifierBundleFactory { return individuals; } - /** - * Is this user a root user? - */ - private boolean isRootUser(UserAccount user) { - if (user == null) { - return false; - } - - WebappDaoFactory wdf = (WebappDaoFactory) context - .getAttribute("webappDaoFactory"); - if (wdf == null) { - log.error("Could not get a WebappDaoFactory from the ServletContext"); - return false; - } - - UserAccountsDao uaDao = wdf.getUserAccountsDao(); - return uaDao.isRootUser(user); - } - @Override public String toString() { return this.getClass().getSimpleName() + " - " + hashCode(); diff --git a/webapp/src/edu/cornell/mannlib/vitro/webapp/auth/policy/RootUserPolicy.java b/webapp/src/edu/cornell/mannlib/vitro/webapp/auth/policy/RootUserPolicy.java index 85e069d12..6ee79648c 100644 --- a/webapp/src/edu/cornell/mannlib/vitro/webapp/auth/policy/RootUserPolicy.java +++ b/webapp/src/edu/cornell/mannlib/vitro/webapp/auth/policy/RootUserPolicy.java @@ -9,11 +9,6 @@ import javax.servlet.ServletContextListener; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; -import com.hp.hpl.jena.ontology.OntModel; -import com.hp.hpl.jena.rdf.model.Resource; -import com.hp.hpl.jena.shared.Lock; -import com.hp.hpl.jena.vocabulary.RDF; - import edu.cornell.mannlib.vitro.webapp.auth.identifier.IdentifierBundle; import edu.cornell.mannlib.vitro.webapp.auth.identifier.common.IsRootUser; import edu.cornell.mannlib.vitro.webapp.auth.policy.ifaces.Authorization; @@ -25,9 +20,7 @@ import edu.cornell.mannlib.vitro.webapp.beans.UserAccount.Status; import edu.cornell.mannlib.vitro.webapp.config.ConfigurationProperties; import edu.cornell.mannlib.vitro.webapp.controller.authenticate.Authenticator; import edu.cornell.mannlib.vitro.webapp.dao.UserAccountsDao; -import edu.cornell.mannlib.vitro.webapp.dao.VitroVocabulary; import edu.cornell.mannlib.vitro.webapp.dao.WebappDaoFactory; -import edu.cornell.mannlib.vitro.webapp.dao.jena.ModelContext; import edu.cornell.mannlib.vitro.webapp.servlet.setup.AbortStartup; /** @@ -73,10 +66,11 @@ public class RootUserPolicy implements PolicyIface { try { UserAccountsDao uaDao = getUserAccountsDao(ctx); - OntModel userAccountsModel = getUserAccountsModel(ctx); + + checkForWrongRootUser(ctx, uaDao); if (!rootUserExists(uaDao)) { - createRootUser(ctx, uaDao, userAccountsModel); + createRootUser(ctx, uaDao); } ServletPolicyList.addPolicy(ctx, new RootUserPolicy()); @@ -98,26 +92,41 @@ public class RootUserPolicy implements PolicyIface { return wadf.getUserAccountsDao(); } - private OntModel getUserAccountsModel(ServletContext ctx) { - return ModelContext.getBaseOntModelSelector(ctx) - .getUserAccountsModel(); + private void checkForWrongRootUser(ServletContext ctx, + UserAccountsDao uaDao) { + UserAccount root = getRootUser(uaDao); + if (root == null) { + return; + } + String actualRootEmail = root.getEmailAddress(); + + String configRootEmail = ConfigurationProperties.getBean(ctx) + .getProperty(PROPERTY_ROOT_USER_EMAIL); + if (actualRootEmail.equals(configRootEmail)) { + return; + } + + log.warn("Root user '" + actualRootEmail + "' already exists."); } private boolean rootUserExists(UserAccountsDao uaDao) { + return (getRootUser(uaDao) != null); + } + + private UserAccount getRootUser(UserAccountsDao uaDao) { for (UserAccount ua : uaDao.getAllUserAccounts()) { - if (uaDao.isRootUser(ua)) { - return true; + if (ua.isRootUser()) { + return ua; } } - return false; + return null; } /** * TODO The first and last name should be left blank, so the user will * be forced to edit them. However, that's not in place yet. */ - private void createRootUser(ServletContext ctx, UserAccountsDao uaDao, - OntModel userAccountsModel) { + private void createRootUser(ServletContext ctx, UserAccountsDao uaDao) { String emailAddress = ConfigurationProperties.getBean(ctx) .getProperty(PROPERTY_ROOT_USER_EMAIL); if (emailAddress == null) { @@ -147,19 +156,10 @@ public class RootUserPolicy implements PolicyIface { .applyMd5Encoding(ROOT_USER_INITIAL_PASSWORD)); ua.setPasswordChangeRequired(true); ua.setStatus(Status.ACTIVE); + ua.setRootUser(true); uaDao.insertUserAccount(ua); - userAccountsModel.enterCriticalSection(Lock.WRITE); - try { - Resource r = userAccountsModel.getResource(ua.getUri()); - Resource t = userAccountsModel - .getResource(VitroVocabulary.USERACCOUNT_ROOT_USER); - userAccountsModel.add(r, RDF.type, t); - } finally { - userAccountsModel.leaveCriticalSection(); - } - log.info("Created root user as '" + emailAddress + "'"); } diff --git a/webapp/src/edu/cornell/mannlib/vitro/webapp/beans/UserAccount.java b/webapp/src/edu/cornell/mannlib/vitro/webapp/beans/UserAccount.java index 55e6e2f4b..217648473 100644 --- a/webapp/src/edu/cornell/mannlib/vitro/webapp/beans/UserAccount.java +++ b/webapp/src/edu/cornell/mannlib/vitro/webapp/beans/UserAccount.java @@ -60,6 +60,8 @@ public class UserAccount { /** This may be empty, but should never be null. */ private Set permissionSetUris = Collections.emptySet(); + private boolean rootUser = false; + public String getUri() { return uri; } @@ -172,10 +174,18 @@ public class UserAccount { this.permissionSetUris = new HashSet(permissionSetUris); } + public boolean isRootUser() { + return rootUser; + } + + public void setRootUser(boolean rootUser) { + this.rootUser = rootUser; + } + private T nonNull(T value, T defaultValue) { return (value == null) ? defaultValue : value; } - + private String limitStringLength(int limit, String s) { if (s == null) { return ""; @@ -196,6 +206,7 @@ public class UserAccount { + (", passwordChangeRequired=" + passwordChangeRequired) + (", loginCount=" + loginCount) + (", status=" + status) + (", externalAuthId=" + externalAuthId) + + (", rootUser=" + rootUser) + (", permissionSetUris=" + permissionSetUris) + "]"; } } diff --git a/webapp/src/edu/cornell/mannlib/vitro/webapp/controller/ContactMailServlet.java b/webapp/src/edu/cornell/mannlib/vitro/webapp/controller/ContactMailServlet.java deleted file mode 100644 index 134717ed7..000000000 --- a/webapp/src/edu/cornell/mannlib/vitro/webapp/controller/ContactMailServlet.java +++ /dev/null @@ -1,352 +0,0 @@ -/* $This file is distributed under the terms of the license in /doc/license.txt$ */ - -package edu.cornell.mannlib.vitro.webapp.controller; - -import java.io.FileWriter; -import java.io.IOException; -import java.io.PrintWriter; -import java.util.Calendar; -import java.util.Date; - -import javax.mail.Message; -import javax.mail.MessagingException; -import javax.mail.SendFailedException; -import javax.mail.Session; -import javax.mail.Transport; -import javax.mail.internet.AddressException; -import javax.mail.internet.InternetAddress; -import javax.mail.internet.MimeMessage; -import javax.servlet.ServletException; -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.beans.ApplicationBean; -import edu.cornell.mannlib.vitro.webapp.email.FreemarkerEmailFactory; - -public class ContactMailServlet extends VitroHttpServlet { - - private static final Log log = LogFactory.getLog(ContactMailServlet.class); - - private final static String CONFIRM_PAGE = "/templates/parts/thankyou.jsp"; - private final static String ERR_PAGE = "/templates/parts/contact_err.jsp"; - private final static String SPAM_MESSAGE = "Your message was flagged as spam."; - private final static String EMAIL_BACKUP_FILE_PATH = "/WEB-INF/LatestMessage.html"; - - private final static String WEB_USERNAME_PARAM = "webusername"; - private final static String WEB_USEREMAIL_PARAM = "webuseremail"; - private final static String COMMENTS_PARAM = "s34gfd88p9x1"; - - @Override - public void doGet( HttpServletRequest request, HttpServletResponse response ) - throws ServletException, IOException { - - VitroRequest vreq = new VitroRequest(request); - - ApplicationBean appBean = vreq.getAppBean(); - - String statusMsg = null; // holds the error status - - if (!FreemarkerEmailFactory.isConfigured(vreq)) { - statusMsg = "This application has not yet been configured to send mail. " - + "Email properties must be specified in the configuration properties file."; - redirectToError(response, statusMsg); - return; - } - - String webusername = vreq.getParameter(WEB_USERNAME_PARAM); - String webuseremail = vreq.getParameter(WEB_USEREMAIL_PARAM); - String comments = vreq.getParameter(COMMENTS_PARAM); - - String validationMessage = validateInput(webusername, webuseremail, - comments); - if (validationMessage != null) { - redirectToError(response, validationMessage); - return; - } - webusername = webusername.trim(); - webuseremail = webuseremail.trim(); - comments = comments.trim(); - - String spamReason = null; - - String originalReferer = (String) request.getSession() - .getAttribute("commentsFormReferer"); - if (originalReferer != null) { - request.getSession().removeAttribute("commentsFormReferer"); - /* does not support legitimate clients that don't send the Referer header - String referer = request.getHeader("Referer"); - if (referer == null || - (referer.indexOf("comments") <0 - && referer.indexOf("correction") <0) ) { - spamReason = "The form was not submitted from the " + - "Contact Us or Corrections page."; - statusMsg = SPAM_MESSAGE; - } - */ - } else { - originalReferer = "none"; - } - - if (spamReason == null) { - spamReason = checkForSpam(comments); - if (spamReason != null) { - statusMsg = SPAM_MESSAGE; - } - } - - String formType = vreq.getParameter("DeliveryType"); - String[] deliverToArray = null; - int recipientCount = 0; - String deliveryfrom = null; - - if ("comment".equals(formType)) { - if (appBean.getContactMail() == null || appBean.getContactMail().trim().length()==0) { - log.error("No contact mail address defined in current application"); - throw new Error( - "To establish the Contact Us mail capability the system administrators must " - + "specify an email address in the current portal."); - } else { - deliverToArray = appBean.getContactMail().split(","); - } - deliveryfrom = "Message from the "+appBean.getApplicationName()+" Contact Form"; - } else if ("correction".equals(formType)) { - if (appBean.getCorrectionMail() == null || appBean.getCorrectionMail().trim().length()==0) { - log.error("Expecting one or more correction email addresses to be specified in current application; will attempt to use contact mail address"); - if (appBean.getContactMail() == null || appBean.getContactMail().trim().length()==0) { - log.error("No contact mail address or correction mail address defined in current application"); - } else { - deliverToArray = appBean.getContactMail().split(","); - } - } else { - deliverToArray = appBean.getCorrectionMail().split(","); - } - deliveryfrom = "Message from the "+appBean.getApplicationName()+" Correction Form (ARMANN-nospam)"; - } else { - deliverToArray = appBean.getContactMail().split(","); - statusMsg = SPAM_MESSAGE ; - spamReason = "The form specifies no delivery type."; - } - recipientCount=(deliverToArray == null) ? 0 : deliverToArray.length; - if (recipientCount == 0) { - log.error("recipientCount is 0 when DeliveryType specified as \""+formType+"\""); - throw new Error( - "To establish the Contact Us mail capability the system administrators must " - + "specify at least one email address in the current portal."); - } - - String msgText = composeEmail(webusername, webuseremail, comments, - deliveryfrom, originalReferer, request.getRemoteAddr()); - - // debugging - PrintWriter outFile = new PrintWriter - (new FileWriter(request.getSession().getServletContext() - .getRealPath(EMAIL_BACKUP_FILE_PATH),true)); //autoflush - writeBackupCopy(outFile, msgText, spamReason); - - Session s = FreemarkerEmailFactory.getEmailSession(vreq); - //s.setDebug(true); - try { - - if (spamReason == null) { - sendMessage(s, webuseremail, deliverToArray, deliveryfrom, - recipientCount, msgText); - } - - } catch (AddressException e) { - statusMsg = "Please supply a valid email address."; - outFile.println( statusMsg ); - outFile.println( e.getMessage() ); - } catch (SendFailedException e) { - statusMsg = "The system was unable to deliver your mail. Please try again later. [SEND FAILED]"; - outFile.println( statusMsg ); - outFile.println( e.getMessage() ); - } catch (MessagingException e) { - statusMsg = "The system was unable to deliver your mail. Please try again later. [MESSAGING]"; - outFile.println( statusMsg ); - outFile.println( e.getMessage() ); - e.printStackTrace(); - } - - outFile.flush(); - outFile.close(); - - // Redirect to the appropriate confirmation page - if (statusMsg == null && spamReason == null) { - // message was sent successfully - redirectToConfirmation(response, statusMsg); - } else { - // exception occurred - redirectToError( response, statusMsg); - } - - } - - @Override - public void doPost( HttpServletRequest request, HttpServletResponse response ) - throws ServletException, IOException - { - doGet( request, response ); - } - - private void redirectToConfirmation(HttpServletResponse response, - String statusMsg) throws IOException { - response.sendRedirect( "test?bodyJsp=" + CONFIRM_PAGE + "&home=" ); - } - - private void redirectToError(HttpServletResponse response, String statusMsg) - throws IOException { - response.sendRedirect("test?bodyJsp=" + ERR_PAGE + "&ERR=" + statusMsg); - } - - /** Intended to mangle url so it can get through spam filtering - * http://host/dir/servlet?param=value -> host: dir/servlet?param=value */ - public String stripProtocol( String in ){ - if( in == null ) - return ""; - else - return in.replaceAll("http://", "host: " ); - } - - private String composeEmail(String webusername, String webuseremail, - String comments, String deliveryfrom, - String originalReferer, String ipAddr) { - - StringBuffer msgBuf = new StringBuffer(); - // contains the intro copy for the body of the email message - - String lineSeparator = System.getProperty("line.separator"); - // \r\n on windows, \n on unix - - // from MyLibrary - msgBuf.setLength(0); - msgBuf.append("Content-Type: text/html; charset='us-ascii'" + lineSeparator); - msgBuf.append("" + lineSeparator ); - msgBuf.append("" + lineSeparator ); - msgBuf.append("" + lineSeparator ); - msgBuf.append("" + deliveryfrom + "" + lineSeparator ); - msgBuf.append("" + lineSeparator ); - msgBuf.append("" + lineSeparator ); - msgBuf.append("

" + deliveryfrom + "

" + lineSeparator ); - msgBuf.append("

From: "+webusername +" (" + webuseremail + ")" + - " at IP address " + ipAddr + "

"+lineSeparator); - - if (!(originalReferer == null || originalReferer.equals("none"))){ - //The spam filter that is being used by the listsrv is rejecting likely viewing page " + stripProtocol(originalReferer) ); - msgBuf.append("

likely viewing page " + originalReferer ); - } - - msgBuf.append(lineSeparator + "

Comments:

" + lineSeparator ); - if (comments==null || comments.equals("")) { - msgBuf.append("

BLANK MESSAGE

"); - } else { - msgBuf.append("

"+comments+"

"); - } - msgBuf.append("" + lineSeparator ); - msgBuf.append("" + lineSeparator ); - - return msgBuf.toString(); - - } - - private void writeBackupCopy(PrintWriter outFile, String msgText, - String spamReason) { - Calendar cal = Calendar.getInstance(); - outFile.println("
"); - outFile.println(); - outFile.println("

"+cal.getTime()+"

"); - outFile.println(); - if (spamReason != null) { - outFile.println("

REJECTED - SPAM

"); - outFile.println("

"+spamReason+"

"); - outFile.println(); - } - outFile.print( msgText ); - outFile.println(); - outFile.println(); - outFile.flush(); - // outFile.close(); - } - - private void sendMessage(Session s, String webuseremail, - String[] deliverToArray, String deliveryfrom, int recipientCount, - String msgText) - throws AddressException, SendFailedException, MessagingException { - // Construct the message - MimeMessage msg = new MimeMessage( s ); - //System.out.println("trying to send message from servlet"); - - // Set the from address - msg.setFrom( new InternetAddress( webuseremail )); - - // Set the recipient address - - if (recipientCount>0){ - InternetAddress[] address=new InternetAddress[recipientCount]; - for (int i=0; i -1 - || comments.indexOf("[/URL]") > -1 - || comments.indexOf("[url=") > -1 - || comments.indexOf("[URL=") > -1)) { - return "The message contained blog link markup."; - } - - /* if message is absurdly short, treat as blog spam */ - if (comments.length()<15) { - return "The message was too short."; - } - - return null; - - } -} diff --git a/webapp/src/edu/cornell/mannlib/vitro/webapp/controller/GrefinePropertyListServlet.java b/webapp/src/edu/cornell/mannlib/vitro/webapp/controller/GrefinePropertyListServlet.java new file mode 100644 index 000000000..7a5baf6f5 --- /dev/null +++ b/webapp/src/edu/cornell/mannlib/vitro/webapp/controller/GrefinePropertyListServlet.java @@ -0,0 +1,753 @@ +/* $This file is distributed under the terms of the license in /doc/license.txt$ */ + +package edu.cornell.mannlib.vitro.webapp.controller; + +import java.io.IOException; +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.LinkedList; +import java.util.List; +import java.util.Map; + +import javax.servlet.ServletContext; +import javax.servlet.ServletException; +import javax.servlet.ServletOutputStream; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.apache.lucene.analysis.Analyzer; +import org.apache.lucene.document.Document; +import org.apache.lucene.document.Field; +import org.apache.lucene.index.Term; +import org.apache.lucene.queryParser.ParseException; +import org.apache.lucene.queryParser.QueryParser; +import org.apache.lucene.search.BooleanClause; +import org.apache.lucene.search.BooleanQuery; +import org.apache.lucene.search.IndexSearcher; +import org.apache.lucene.search.Query; +import org.apache.lucene.search.TermQuery; +import org.apache.lucene.search.TopDocs; +import org.apache.lucene.search.WildcardQuery; +import org.json.JSONArray; +import org.json.JSONException; +import org.json.JSONObject; + +import com.hp.hpl.jena.vocabulary.OWL; + +import edu.cornell.mannlib.vitro.webapp.beans.DataProperty; +import edu.cornell.mannlib.vitro.webapp.beans.Datatype; +import edu.cornell.mannlib.vitro.webapp.beans.Individual; +import edu.cornell.mannlib.vitro.webapp.beans.Ontology; +import edu.cornell.mannlib.vitro.webapp.beans.PropertyGroup; +import edu.cornell.mannlib.vitro.webapp.beans.VClass; +import edu.cornell.mannlib.vitro.webapp.beans.VClassGroup; +import edu.cornell.mannlib.vitro.webapp.config.ConfigurationProperties; +import edu.cornell.mannlib.vitro.webapp.dao.DataPropertyDao; +import edu.cornell.mannlib.vitro.webapp.dao.DatatypeDao; +import edu.cornell.mannlib.vitro.webapp.dao.IndividualDao; +import edu.cornell.mannlib.vitro.webapp.dao.OntologyDao; +import edu.cornell.mannlib.vitro.webapp.dao.VClassDao; +import edu.cornell.mannlib.vitro.webapp.dao.VClassGroupDao; +import edu.cornell.mannlib.vitro.webapp.dao.WebappDaoFactory; +import edu.cornell.mannlib.vitro.webapp.search.SearchException; +import edu.cornell.mannlib.vitro.webapp.search.lucene.Entity2LuceneDoc; +import edu.cornell.mannlib.vitro.webapp.search.lucene.Entity2LuceneDoc.VitroLuceneTermNames; +import edu.cornell.mannlib.vitro.webapp.search.lucene.LuceneIndexFactory; +import edu.cornell.mannlib.vitro.webapp.search.lucene.LuceneSetup; + +/** + * This servlet is for servicing Google Refine's + * "Add columns from VIVO" requests. + * + * @author Eliza Chan (elc2013@med.cornell.edu) + * + */ +public class GrefinePropertyListServlet extends VitroHttpServlet { + + private int MAXDEPTH = 7; + private int NUM_COLS = 9; + private static String QUERY_PARAMETER_NAME = "term"; + public static final int MAX_QUERY_LENGTH = 500; + private static final Log log = LogFactory.getLog(GrefinePropertyListServlet.class.getName()); + + + @Override + protected void doPost(HttpServletRequest req, HttpServletResponse resp) + throws ServletException, IOException { + //resp.setContentType("application/json"); + super.doPost(req, resp); + } + + @Override + protected void doGet(HttpServletRequest req, HttpServletResponse resp) + throws ServletException, IOException { + super.doGet(req, resp); + resp.setContentType("application/json"); + VitroRequest vreq = new VitroRequest(req); + + try { + + String callbackStr = (vreq.getParameter("callback") == null) ? "" + : vreq.getParameter("callback"); + ServletOutputStream out = resp.getOutputStream(); + + + // ******* + // methodology adopted from DatatypePropertiesListingController and ClassHierarchyListingController + // ******* + VClassDao vcDao = vreq.getFullWebappDaoFactory().getVClassDao(); + DataPropertyDao dao = vreq.getFullWebappDaoFactory().getDataPropertyDao(); + String topUri = vreq.getParameter("type"); + VClass topClass = vcDao.getVClassByURI(topUri); + HashSet propURIs = new HashSet(); + HashMap> classPropertiesMap = + populateClassPropertiesMap(vcDao, dao, topUri, propURIs); + + + // Construct json String + JSONObject completeJson = new JSONObject(); + JSONArray propertiesJsonArr = new JSONArray(); + if (classPropertiesMap.size() > 0) { + for (Iterator iter = classPropertiesMap.keySet().iterator(); iter.hasNext();) { // add results to schema + VClass vc = (VClass) iter.next(); + System.out.println("vc uri: " + vc.getURI()); + System.out.println("vc name: " + vc.getName()); + + ArrayList vcProps = (ArrayList)classPropertiesMap.get(vc); + for (DataProperty prop: vcProps) { + String nameStr = prop.getPublicName()==null ? prop.getName()==null ? null : prop.getName() : prop.getPublicName(); + System.out.println("--- uri: " + prop.getURI()); + System.out.println("--- name: " + nameStr); + // top level + JSONObject propertiesItemJson = new JSONObject(); + JSONObject rootSchemaJson = new JSONObject(); + rootSchemaJson.put("id", vc.getURI()); + rootSchemaJson.put("name", vc.getName()); + rootSchemaJson.put("alias", new JSONArray()); + propertiesItemJson.put("schema", rootSchemaJson); + // second level + propertiesItemJson.put("id", prop.getURI()); + propertiesItemJson.put("name", nameStr); + propertiesItemJson.put("alias", new JSONArray()); + + JSONObject expectsJson = new JSONObject(); + expectsJson.put("id", prop.getURI()); + expectsJson.put("name", nameStr); + expectsJson.put("alias", new JSONArray()); + propertiesItemJson.put("expects", expectsJson); + + propertiesJsonArr.put(propertiesItemJson); + } + } + } + + + // get data properties from subclasses + List lvl2Classes = new ArrayList(); + List roots = null; + String requestType = vreq.getParameter("type"); + if (requestType != null) { + roots = new LinkedList(); + roots.add(vcDao.getVClassByURI(requestType)); + } + + if (roots != null) { + String ontologyUri = null; + Collections.sort(roots); + Iterator rootIt = roots.iterator(); + if (rootIt.hasNext()) { + while (rootIt.hasNext()) { + VClass root = (VClass) rootIt.next(); + if (root != null) { + List lvl2ChildClasses = new ArrayList(); + addChildren(vcDao, vreq.getFullWebappDaoFactory(), root, lvl2ChildClasses, 0, ontologyUri); + lvl2Classes.addAll(lvl2ChildClasses); + } + } + } + } + + + for (VClass lvl2Class: lvl2Classes) { + HashMap> lvl2ClassPropertiesMap = + populateClassPropertiesMap(vcDao, dao, lvl2Class.getURI(), propURIs); + if (lvl2ClassPropertiesMap.size() > 0) { + for (Iterator iter = lvl2ClassPropertiesMap.keySet().iterator(); iter.hasNext();) { // add results to schema + VClass vc = (VClass) iter.next(); + ArrayList vcProps = (ArrayList)lvl2ClassPropertiesMap.get(vc); + for (DataProperty prop: vcProps) { + String nameStr = prop.getPublicName()==null ? prop.getName()==null ? null : prop.getName() : prop.getPublicName(); + // top level + JSONObject propertiesItemJson = new JSONObject(); + + JSONObject rootSchemaJson = new JSONObject(); + rootSchemaJson.put("id", topClass.getURI()); + rootSchemaJson.put("name", topClass.getName()); + rootSchemaJson.put("alias", new JSONArray()); + propertiesItemJson.put("schema", rootSchemaJson); + + // second level + propertiesItemJson.put("id", vc.getURI()); + propertiesItemJson.put("name", vc.getName()); + propertiesItemJson.put("alias", new JSONArray()); + + propertiesItemJson.put("id2", prop.getURI()); + propertiesItemJson.put("name2", nameStr); + propertiesItemJson.put("alias2", new JSONArray()); + + JSONObject expectsJson = new JSONObject(); + expectsJson.put("id", prop.getURI()); + expectsJson.put("name", nameStr); + expectsJson.put("alias", new JSONArray()); + propertiesItemJson.put("expects", expectsJson); + + propertiesJsonArr.put(propertiesItemJson); + } + } + + } + } + + completeJson.put("properties", propertiesJsonArr); + out.print(callbackStr + "(" + completeJson.toString() + ")"); + + + + } catch (Exception ex) { + log.warn(ex, ex); + } + } + + + private HashMap> populateClassPropertiesMap ( + VClassDao vcDao, + DataPropertyDao dao, + String uri, + HashSet propURIs) { + + HashMap> classPropertiesMap = new HashMap>(); + List props = new ArrayList(); + VClass topVc = vcDao.getVClassByURI(uri); + Collection dataProps = dao.getDataPropertiesForVClass(uri); + Iterator dataPropIt = dataProps.iterator(); + 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); + } + } + } + + + if (props.size() > 0) { + + Collections.sort(props); + for (DataProperty prop: props) { + String nameStr = prop.getPublicName()==null ? prop.getName()==null ? null : prop.getName() : prop.getPublicName(); + if (nameStr != null) { + if (prop.getDomainClassURI() != null) { + VClass vc = vcDao.getVClassByURI(prop.getDomainClassURI()); + if (classPropertiesMap.get(vc) != null) { + ArrayList existingList = (ArrayList)classPropertiesMap.get(vc); + existingList.add(prop); + } else { + ArrayList newList = new ArrayList(); + newList.add(prop); + classPropertiesMap.put(vc, newList); + } + + } else { // some properties have no domain, belong to top vc by default + if (classPropertiesMap.get(topVc) != null) { + ArrayList existingList = (ArrayList)classPropertiesMap.get(topVc); + existingList.add(prop); + } else { + ArrayList newList = new ArrayList(); + newList.add(prop); + classPropertiesMap.put(topVc, newList); + } + } + } + } + } + return classPropertiesMap; + } + + private void addChildren(VClassDao vcDao, WebappDaoFactory wadf, VClass parent, List list, int position, String ontologyUri) { + List rowElts = addVClassDataToResultsList(wadf, parent, position, ontologyUri); + int childShift = (rowElts.size() > 0) ? 1 : 0; // if addVClassDataToResultsList filtered out the result, don't shift the children over + list.addAll(rowElts); + List childURIstrs = vcDao.getSubClassURIs(parent.getURI()); + if ((childURIstrs.size()>0) && position addVClassDataToResultsList(WebappDaoFactory wadf, VClass vcw, int position, String ontologyUri) { + List results = new ArrayList(); + if (ontologyUri == null || ( (vcw.getNamespace()!=null) && (vcw.getNamespace().equals(ontologyUri)) ) ) { + results.add(vcw); + +/* + for (int i=0; i"+vcw.getLocalNameWithPrefix()+"
"), results, numCols); + } catch (Exception e) { + numCols = addColToResults(((vcw.getLocalNameWithPrefix() == null) ? "" : vcw.getLocalNameWithPrefix()), results, numCols); // column 2 + } + numCols = addColToResults(((vcw.getShortDef() == null) ? "" : vcw.getShortDef()), results, numCols); // column 3 + numCols = addColToResults(((vcw.getExample() == null) ? "" : vcw.getExample()), results, numCols); // column 4 + + // Get group name if it exists + VClassGroupDao groupDao= wadf.getVClassGroupDao(); + String groupURI = vcw.getGroupURI(); + String groupName = null; + VClassGroup classGroup = null; + if(groupURI != null) { + classGroup = groupDao.getGroupByURI(groupURI); + if (classGroup != null) { + groupName = classGroup.getPublicName(); + } + } + numCols = addColToResults(((groupName == null) ? "" : groupName), results, numCols); // column 5 + + // Get ontology name + String ontName = null; + try { + OntologyDao ontDao = wadf.getOntologyDao(); + Ontology ont = ontDao.getOntologyByURI(vcw.getNamespace()); + ontName = ont.getName(); + } catch (Exception e) {} + numCols = addColToResults(((ontName == null) ? "" : ontName), results, numCols); // column 6 + + numCols = addColToResults(vcw.getHiddenFromDisplayBelowRoleLevel() == null ? "unspecified" : vcw.getHiddenFromDisplayBelowRoleLevel().getShorthand(), results, numCols); // column 7 + numCols = addColToResults(vcw.getProhibitedFromUpdateBelowRoleLevel() == null ? "unspecified" : vcw.getProhibitedFromUpdateBelowRoleLevel().getShorthand(), results, numCols); // column 8 + + results.add("XX"); // column 9 +*/ + } + return results; + } + + private Integer addColToResults (String value, List results, Integer colIndex) { + if (colIndex>0) { + results.add(value); + } + return colIndex-1; + } + + protected JSONObject getResult(VitroRequest vreq, HttpServletRequest req, + HttpServletResponse resp) throws ServletException { + + HashMap searchWithTypeMap = new HashMap(); + HashMap searchNoTypeMap = new HashMap(); + ArrayList queries = new ArrayList(); + Object qObj = vreq.getParameter("queries"); + + if (qObj == null) { + qObj = vreq.getParameter("query"); + } + + if (qObj != null && qObj instanceof String) { + // e.g. + // {"q0":{"query":"Cathleen","type":"http://xmlns.com/foaf/0.1/Person","type_strict":"should"}, + // "q1":{"query":"Geoffrey","type":"http://xmlns.com/foaf/0.1/Person","type_strict":"should"}, + // "q2":{"query":"Dina","type":"http://xmlns.com/foaf/0.1/Person","type_strict":"should"}} + String qStr = (String) qObj; + queries.add(qStr); + } + + try { + for (int i = 0; i < queries.size(); i++) { + String queryStr = (String) queries.get(i); + JSONObject json = new JSONObject(queryStr); + + if (json.has("query")) { // single query + if (json.has("type")) { + searchWithTypeMap.put("query", json); + } else { + // user did not specify a type + searchNoTypeMap.put("query", json); + } + } else { // multiple queries + for (Iterator iter = json.keys(); iter.hasNext();) { + ArrayList jsonList = new ArrayList(); + String key = (String) iter.next(); + Object obj = json.get(key); + JSONObject jsonLvl2 = (JSONObject) obj; + if (jsonLvl2.has("query")) { + if (jsonLvl2.has("type")) { + searchWithTypeMap.put(key, jsonLvl2); + } else { + // user did not specify a type + searchNoTypeMap.put(key, jsonLvl2); + } + } + } + } + } + } catch (JSONException ex) { + System.err.println("JSONReconcileServlet JSONException: " + ex); + throw new ServletException("JSONReconcileServlet JSONException: " + + ex); + } + + // Run index search + JSONObject qJson = null; + if (searchWithTypeMap.size() > 0) { + qJson = runSearch(searchWithTypeMap, vreq); + } else { + qJson = runSearch(searchNoTypeMap, vreq); + } + return qJson; + } + + /** + * Returns a default JSON response. + * + * @param req + * @param resp + * @return + * @throws ServletException + */ + protected JSONObject getMetadata(HttpServletRequest req, HttpServletResponse resp, String defaultNamespace, + String defaultTypeList, String serverName, int serverPort) throws ServletException { + + JSONObject json = new JSONObject(); + try { + json.put("name", "VIVO Reconciliation Service"); + if (defaultNamespace != null) { + json.put("identifierSpace", defaultNamespace); + json.put("schemaSpace", defaultNamespace); + } + JSONObject viewJson = new JSONObject(); + StringBuffer urlBuf = new StringBuffer(); + urlBuf.append("http://" + serverName); + if (serverPort == 8080) { + urlBuf.append(":" + serverPort); + } + if (req.getContextPath() != null) { + urlBuf.append(req.getContextPath()); + } + viewJson.put("url", urlBuf.toString() + "/individual?uri={{id}}"); + json.put("view", viewJson); + + // parse defaultTypeList from deploy.properties + if (defaultTypeList != null) { + String[] splitList = defaultTypeList.split(";"); + String[][] idNameArray = new String[splitList.length][splitList.length]; + for(int i = 0; i currMap, + VitroRequest vreq) throws ServletException { + JSONObject qJson = new JSONObject(); + try { + Analyzer analyzer = getAnalyzer(getServletContext()); + IndexSearcher searcherForRequest = LuceneIndexFactory + .getIndexSearcher(getServletContext()); + + for (Map.Entry entry : currMap.entrySet()) { + JSONObject resultAllJson = new JSONObject(); + String key = entry.getKey(); + JSONObject json = (JSONObject) entry.getValue(); + String queryVal = json.getString("query"); + + // continue with properties list + String searchType = null; + int limit = 3; // default + String typeStrict = "should"; // default + ArrayList propertiesList = new ArrayList(); + + if (json.has("type")) { + searchType = json.getString("type"); + } + if (json.has("limit")) { + limit = json.getInt("limit"); + } + if (json.has("type_strict")) { // Not sure what this variable + // represents. Skip for now. + typeStrict = json.getString("type_strict"); + } + if (json.has("properties")) { + JSONArray properties = json.getJSONArray("properties"); + for (int i = 0; i < properties.length(); i++) { + String[] pvPair = new String[2]; + JSONObject jsonProperty = properties.getJSONObject(i); + String pid = jsonProperty.getString("pid"); + String v = jsonProperty.getString("v"); + pvPair[0] = pid; + pvPair[1] = v; + propertiesList.add(pvPair); + } + } + + // begin search + JSONArray resultJsonArr = new JSONArray(); + Query query = getReconcileQuery(vreq, analyzer, + queryVal, searchType, propertiesList); + + TopDocs topDocs = searcherForRequest.search(query, null, limit); + if (topDocs != null && topDocs.scoreDocs != null) { + int hitsLength = topDocs.scoreDocs.length; + if (hitsLength > 0) { + for (int i = 0; i < topDocs.scoreDocs.length; i++) { + JSONObject resultJson = new JSONObject(); + float score = topDocs.scoreDocs[i].score; + resultJson.put("score", score); + + Document doc = searcherForRequest + .doc(topDocs.scoreDocs[i].doc); + String uri = doc.get(Entity2LuceneDoc.term.URI); + IndividualDao iDao = vreq.getWebappDaoFactory() + .getIndividualDao(); + Individual ind = iDao.getIndividualByURI(uri); + if (ind != null) { + String name = ind.getName(); + // encode # to %23 + String modUri = uri.replace("#", "%23"); + resultJson.put("id", modUri); + resultJson.put("name", name); + } + List fields = doc.getFields(); + JSONArray typesJsonArr = new JSONArray(); + for (int j = 0; j < fields.size(); j++) { + Field field = (Field) fields.get(j); + String fieldName = field.name(); + if ("type".equals(fieldName)) { + // e.g. http://aims.fao.org/aos/geopolitical.owl#area + String type = field.stringValue(); + int lastIndex2 = type.lastIndexOf('/') + 1; + String typeName = type + .substring(lastIndex2); + typeName = typeName.replace("#", ":"); + JSONObject typesJson = new JSONObject(); + typesJson.put("id", type); + typesJson.put("name", typeName); + typesJsonArr.put(typesJson); + } + } + resultJson.put("type", typesJsonArr); + resultJson.put("match", "false"); + resultJsonArr.put(resultJson); + } + } + } + resultAllJson.put("result", resultJsonArr); + qJson.put(key, resultAllJson); + } + + } catch (JSONException ex) { + System.err.println("JSONReconcileServlet JSONException: " + ex); + throw new ServletException("JSONReconcileServlet JSONException: " + + ex); + } catch (SearchException ex) { + System.err.println("JSONReconcileServlet SearchException: " + ex); + throw new ServletException("JSONReconcileServlet SearchException: " + + ex); + } catch (IOException ex) { + System.err.println("JSONReconcileServlet IOException: " + ex); + throw new ServletException("JSONReconcileServlet IOException: " + + ex); + } + + return qJson; + } + + private Analyzer getAnalyzer(ServletContext servletContext) + throws SearchException { + Object obj = servletContext.getAttribute(LuceneSetup.ANALYZER); + if (obj == null || !(obj instanceof Analyzer)) + throw new SearchException("Could not get anlyzer"); + else + return (Analyzer) obj; + } + + private Query makeReconcileNameQuery(String querystr, Analyzer analyzer, HttpServletRequest request) { + + /* Original code + String tokenizeParam = (String) request.getParameter("tokenize"); + boolean tokenize = "true".equals(tokenizeParam); + + // Note: Stemming is only relevant if we are tokenizing: an untokenized name + // query will not be stemmed. So we don't look at the stem parameter until we get to + // makeTokenizedNameQuery(). + if (tokenize) { + return makeTokenizedNameQuery(querystr, analyzer, request); + } else { + return makeUntokenizedNameQuery(querystr); + } + */ + + // modified code for reconciliation service + request.setAttribute("stem", true); + return makeTokenizedNameQuery(querystr, analyzer, request); + } + + private Query makeTokenizedNameQuery(String querystr, Analyzer analyzer, HttpServletRequest request) { + + String stemParam = (String) request.getParameter("stem"); + boolean stem = "true".equals(stemParam); + String termName = stem ? VitroLuceneTermNames.NAME_STEMMED : VitroLuceneTermNames.NAME_UNSTEMMED; + + BooleanQuery boolQuery = new BooleanQuery(); + + // Use the query parser to analyze the search term the same way the indexed text was analyzed. + // For example, text is lowercased, and function words are stripped out. + QueryParser parser = getQueryParser(termName, analyzer); + + // The wildcard query doesn't play well with stemming. Query term name:tales* doesn't match + // "tales", which is indexed as "tale", while query term name:tales does. Obviously we need + // the wildcard for name:tal*, so the only way to get them all to match is use a disjunction + // of wildcard and non-wildcard queries. The query will look have only an implicit disjunction + // operator: e.g., +(name:tales name:tales*) + try { + log.debug("Adding non-wildcard query for " + querystr); + Query query = parser.parse(querystr); + boolQuery.add(query, BooleanClause.Occur.SHOULD); + + // Prevent ParseException here when adding * after a space. + // If there's a space at the end, we don't need the wildcard query. + if (! querystr.endsWith(" ")) { + log.debug("Adding wildcard query for " + querystr); + Query wildcardQuery = parser.parse(querystr + "*"); + boolQuery.add(wildcardQuery, BooleanClause.Occur.SHOULD); + } + + log.debug("Name query is: " + boolQuery.toString()); + } catch (ParseException e) { + log.warn(e, e); + } + + + return boolQuery; + } + + private Query makeUntokenizedNameQuery(String querystr) { + + querystr = querystr.toLowerCase(); + String termName = VitroLuceneTermNames.NAME_LOWERCASE; + BooleanQuery query = new BooleanQuery(); + log.debug("Adding wildcard query on unanalyzed name"); + query.add( + new WildcardQuery(new Term(termName, querystr + "*")), + BooleanClause.Occur.MUST); + + return query; + } + + private QueryParser getQueryParser(String searchField, Analyzer analyzer){ + // searchField indicates which field to search against when there is no term + // indicated in the query string. + // The analyzer is needed so that we use the same analyzer on the search queries as + // was used on the text that was indexed. + QueryParser qp = new QueryParser(searchField,analyzer); + //this sets the query parser to AND all of the query terms it finds. + qp.setDefaultOperator(QueryParser.AND_OPERATOR); + return qp; + } + + private Query getReconcileQuery(VitroRequest request, Analyzer analyzer, + String querystr, String typeParam, ArrayList propertiesList) throws SearchException{ + + Query query = null; + try { + if( querystr == null){ + log.error("There was no Parameter '"+ QUERY_PARAMETER_NAME + +"' in the request."); + return null; + }else if( querystr.length() > MAX_QUERY_LENGTH ){ + log.debug("The search was too long. The maximum " + + "query length is " + MAX_QUERY_LENGTH ); + return null; + } + + + query = makeReconcileNameQuery(querystr, analyzer, request); + + + // filter by type + if (typeParam != null) { + BooleanQuery boolQuery = new BooleanQuery(); + boolQuery.add( new TermQuery( + new Term(VitroLuceneTermNames.RDFTYPE, + typeParam)), + BooleanClause.Occur.MUST); + boolQuery.add(query, BooleanClause.Occur.MUST); + query = boolQuery; + } + + // if propertiesList has elements, add extra queries to query + Iterator it = propertiesList.iterator(); + while (it.hasNext()) { + String[] pvPair = it.next(); + Query extraQuery = makeReconcileNameQuery(pvPair[1], analyzer, request); + if (!"".equals(pvPair[0]) && pvPair[0] != null) { + BooleanQuery boolQuery = new BooleanQuery(); + boolQuery.add(new TermQuery(new Term( + VitroLuceneTermNames.RDFTYPE, pvPair[0])), + BooleanClause.Occur.MUST); + boolQuery.add(extraQuery, BooleanClause.Occur.MUST); + extraQuery = boolQuery; + } + ((BooleanQuery)query).add(extraQuery, BooleanClause.Occur.MUST); + } + } catch (Exception ex) { + throw new SearchException(ex.getMessage()); + } + + return query; + } + +} + diff --git a/webapp/src/edu/cornell/mannlib/vitro/webapp/controller/JSONReconcileServlet.java b/webapp/src/edu/cornell/mannlib/vitro/webapp/controller/JSONReconcileServlet.java index fe4b9f63b..fb863888b 100644 --- a/webapp/src/edu/cornell/mannlib/vitro/webapp/controller/JSONReconcileServlet.java +++ b/webapp/src/edu/cornell/mannlib/vitro/webapp/controller/JSONReconcileServlet.java @@ -15,6 +15,7 @@ import javax.servlet.ServletOutputStream; 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 org.apache.lucene.analysis.Analyzer; @@ -53,7 +54,8 @@ import edu.cornell.mannlib.vitro.webapp.search.lucene.LuceneSetup; */ public class JSONReconcileServlet extends VitroHttpServlet { - private static String QUERY_PARAMETER_NAME = "term"; + private static final long serialVersionUID = 1L; + private static String QUERY_PARAMETER_NAME = "term"; public static final int MAX_QUERY_LENGTH = 500; private static final Log log = LogFactory.getLog(JSONReconcileServlet.class.getName()); @@ -70,14 +72,14 @@ public class JSONReconcileServlet extends VitroHttpServlet { super.doGet(req, resp); resp.setContentType("application/json"); VitroRequest vreq = new VitroRequest(req); - System.out.println("vreq"); - System.out.println(vreq.getWebappDaoFactory()); + //log.debug("vreq"); + //log.debug(vreq.getWebappDaoFactory()); try { if (vreq.getParameter("query") != null || vreq.getParameter("queries") != null) { JSONObject qJson = getResult(vreq, req, resp); - System.out.println("result: " + qJson.toString()); + log.debug("result: " + qJson.toString()); String responseStr = (vreq.getParameter("callback") == null) ? qJson .toString() : vreq.getParameter("callback") + "(" + qJson.toString() + ")"; @@ -124,8 +126,7 @@ public class JSONReconcileServlet extends VitroHttpServlet { // "q2":{"query":"Dina","type":"http://xmlns.com/foaf/0.1/Person","type_strict":"should"}} String qStr = (String) qObj; queries.add(qStr); - System.out.println(); - System.out.println("query: " + qStr + "\n"); + log.debug("\nquery: " + qStr + "\n"); } try { @@ -158,7 +159,7 @@ public class JSONReconcileServlet extends VitroHttpServlet { } } } catch (JSONException ex) { - System.err.println("JSONReconcileServlet JSONException: " + ex); + log.error("JSONException: " + ex); throw new ServletException("JSONReconcileServlet JSONException: " + ex); } @@ -327,15 +328,15 @@ public class JSONReconcileServlet extends VitroHttpServlet { } } catch (JSONException ex) { - System.err.println("JSONReconcileServlet JSONException: " + ex); + log.error("JSONException: " + ex); throw new ServletException("JSONReconcileServlet JSONException: " + ex); } catch (SearchException ex) { - System.err.println("JSONReconcileServlet SearchException: " + ex); + log.error("SearchException: " + ex); throw new ServletException("JSONReconcileServlet SearchException: " + ex); } catch (IOException ex) { - System.err.println("JSONReconcileServlet IOException: " + ex); + log.error("IOException: " + ex); throw new ServletException("JSONReconcileServlet IOException: " + ex); } @@ -354,30 +355,12 @@ public class JSONReconcileServlet extends VitroHttpServlet { private Query makeReconcileNameQuery(String querystr, Analyzer analyzer, HttpServletRequest request) { - /* Original code - String tokenizeParam = (String) request.getParameter("tokenize"); - boolean tokenize = "true".equals(tokenizeParam); - - // Note: Stemming is only relevant if we are tokenizing: an untokenized name - // query will not be stemmed. So we don't look at the stem parameter until we get to - // makeTokenizedNameQuery(). - if (tokenize) { - return makeTokenizedNameQuery(querystr, analyzer, request); - } else { - return makeUntokenizedNameQuery(querystr); - } - */ - - // modified code for reconciliation service - request.setAttribute("stem", true); return makeTokenizedNameQuery(querystr, analyzer, request); } private Query makeTokenizedNameQuery(String querystr, Analyzer analyzer, HttpServletRequest request) { - - String stemParam = (String) request.getParameter("stem"); - boolean stem = "true".equals(stemParam); - String termName = stem ? VitroLuceneTermNames.NAME_STEMMED : VitroLuceneTermNames.NAME_UNSTEMMED; + + String termName = VitroLuceneTermNames.NAME_STEMMED; BooleanQuery boolQuery = new BooleanQuery(); @@ -407,23 +390,9 @@ public class JSONReconcileServlet extends VitroHttpServlet { } catch (ParseException e) { log.warn(e, e); } - - + return boolQuery; } - - private Query makeUntokenizedNameQuery(String querystr) { - - querystr = querystr.toLowerCase(); - String termName = VitroLuceneTermNames.NAME_LOWERCASE; - BooleanQuery query = new BooleanQuery(); - log.debug("Adding wildcard query on unanalyzed name"); - query.add( - new WildcardQuery(new Term(termName, querystr + "*")), - BooleanClause.Occur.MUST); - - return query; - } private QueryParser getQueryParser(String searchField, Analyzer analyzer){ // searchField indicates which field to search against when there is no term @@ -450,10 +419,8 @@ public class JSONReconcileServlet extends VitroHttpServlet { "query length is " + MAX_QUERY_LENGTH ); return null; } - - + query = makeReconcileNameQuery(querystr, analyzer, request); - // filter by type if (typeParam != null) { @@ -471,7 +438,7 @@ public class JSONReconcileServlet extends VitroHttpServlet { while (it.hasNext()) { String[] pvPair = it.next(); Query extraQuery = makeReconcileNameQuery(pvPair[1], analyzer, request); - if (!"".equals(pvPair[0]) && pvPair[0] != null) { + if ( ! StringUtils.isEmpty(pvPair[0]) ) { BooleanQuery boolQuery = new BooleanQuery(); boolQuery.add(new TermQuery(new Term( VitroLuceneTermNames.RDFTYPE, pvPair[0])), @@ -489,3 +456,4 @@ public class JSONReconcileServlet extends VitroHttpServlet { } } + diff --git a/webapp/src/edu/cornell/mannlib/vitro/webapp/controller/accounts/UserAccountsSelector.java b/webapp/src/edu/cornell/mannlib/vitro/webapp/controller/accounts/UserAccountsSelector.java index 9c4c51cb1..315f8408a 100644 --- a/webapp/src/edu/cornell/mannlib/vitro/webapp/controller/accounts/UserAccountsSelector.java +++ b/webapp/src/edu/cornell/mannlib/vitro/webapp/controller/accounts/UserAccountsSelector.java @@ -39,7 +39,7 @@ public class UserAccountsSelector { + "PREFIX fn: \n" + "PREFIX auth: \n"; - private static final String ALL_VARIABLES = "?uri ?email ?firstName ?lastName ?pwd ?expire ?count ?status"; + private static final String ALL_VARIABLES = "?uri ?email ?firstName ?lastName ?pwd ?expire ?count ?status ?isRoot"; private static final String COUNT_VARIABLE = "?uri"; @@ -169,7 +169,8 @@ public class UserAccountsSelector { + " OPTIONAL { ?uri auth:md5password ?pwd } \n" + " OPTIONAL { ?uri auth:passwordChangeExpires ?expire } \n" + " OPTIONAL { ?uri auth:loginCount ?count } \n" - + " OPTIONAL { ?uri auth:status ?status }"; + + " OPTIONAL { ?uri auth:status ?status } \n" + + " OPTIONAL { ?uri ?isRoot auth:RootUserAccount }"; } private String filterClauses() { @@ -201,11 +202,10 @@ public class UserAccountsSelector { } /** - * Escape any regex special characters in the string. + * Escape any regex special characters in the string. * - * Note that the SPARQL - * parser requires two backslashes, in order to pass a single backslash to - * the REGEX function. + * Note that the SPARQL parser requires two backslashes, in order to pass a + * single backslash to the REGEX function. */ private String escapeForRegex(String raw) { StringBuilder clean = new StringBuilder(); @@ -327,6 +327,7 @@ public class UserAccountsSelector { user.setPasswordLinkExpires(ifLongPresent(solution, "expire", 0L)); user.setLoginCount(ifIntPresent(solution, "count", 0)); user.setStatus(parseStatus(solution, "status", null)); + user.setRootUser(solution.contains("isRoot")); return user; } diff --git a/webapp/src/edu/cornell/mannlib/vitro/webapp/controller/accounts/admin/UserAccountsAddPage.java b/webapp/src/edu/cornell/mannlib/vitro/webapp/controller/accounts/admin/UserAccountsAddPage.java index 51c723459..2add74003 100644 --- a/webapp/src/edu/cornell/mannlib/vitro/webapp/controller/accounts/admin/UserAccountsAddPage.java +++ b/webapp/src/edu/cornell/mannlib/vitro/webapp/controller/accounts/admin/UserAccountsAddPage.java @@ -22,6 +22,7 @@ import edu.cornell.mannlib.vitro.webapp.controller.freemarker.responsevalues.Tem public class UserAccountsAddPage extends UserAccountsPage { private static final String PARAMETER_SUBMIT = "submitAdd"; private static final String PARAMETER_EMAIL_ADDRESS = "emailAddress"; + private static final String PARAMETER_EXTERNAL_AUTH_ID = "externalAuthId"; private static final String PARAMETER_FIRST_NAME = "firstName"; private static final String PARAMETER_LAST_NAME = "lastName"; private static final String PARAMETER_ROLE = "role"; @@ -30,6 +31,7 @@ public class UserAccountsAddPage extends UserAccountsPage { private static final String ERROR_NO_EMAIL = "errorEmailIsEmpty"; private static final String ERROR_EMAIL_IN_USE = "errorEmailInUse"; private static final String ERROR_EMAIL_INVALID_FORMAT = "errorEmailInvalidFormat"; + private static final String ERROR_EXTERNAL_AUTH_ID_IN_USE = "errorExternalAuthIdInUse"; private static final String ERROR_NO_FIRST_NAME = "errorFirstNameIsEmpty"; private static final String ERROR_NO_LAST_NAME = "errorLastNameIsEmpty"; private static final String ERROR_NO_ROLE = "errorNoRoleSelected"; @@ -41,6 +43,7 @@ public class UserAccountsAddPage extends UserAccountsPage { /* The request parameters */ private boolean submit; private String emailAddress = ""; + private String externalAuthId = ""; private String firstName = ""; private String lastName = ""; private String selectedRoleUri = ""; @@ -68,6 +71,7 @@ public class UserAccountsAddPage extends UserAccountsPage { private void parseRequestParameters() { submit = isFlagOnRequest(PARAMETER_SUBMIT); emailAddress = getStringParameter(PARAMETER_EMAIL_ADDRESS, ""); + externalAuthId = getStringParameter(PARAMETER_EXTERNAL_AUTH_ID, ""); firstName = getStringParameter(PARAMETER_FIRST_NAME, ""); lastName = getStringParameter(PARAMETER_LAST_NAME, ""); selectedRoleUri = getStringParameter(PARAMETER_ROLE, ""); @@ -88,6 +92,8 @@ public class UserAccountsAddPage extends UserAccountsPage { errorCode = ERROR_EMAIL_IN_USE; } else if (!isEmailValidFormat()) { errorCode = ERROR_EMAIL_INVALID_FORMAT; + } else if (isExternalAuthIdInUse()) { + errorCode = ERROR_EXTERNAL_AUTH_ID_IN_USE; } else if (firstName.isEmpty()) { errorCode = ERROR_NO_FIRST_NAME; } else if (lastName.isEmpty()) { @@ -103,10 +109,17 @@ public class UserAccountsAddPage extends UserAccountsPage { return userAccountsDao.getUserAccountByEmail(emailAddress) != null; } + private boolean isExternalAuthIdInUse() { + if (externalAuthId.isEmpty()) { + return false; + } + return userAccountsDao.getUserAccountByExternalAuthId(externalAuthId) != null; + } + private boolean isEmailValidFormat() { return Authenticator.isValidEmailAddress(emailAddress); } - + public boolean isValid() { return errorCode.isEmpty(); } @@ -116,7 +129,7 @@ public class UserAccountsAddPage extends UserAccountsPage { u.setEmailAddress(emailAddress); u.setFirstName(firstName); u.setLastName(lastName); - u.setExternalAuthId(""); + u.setExternalAuthId(externalAuthId); u.setMd5Password(""); u.setOldPassword(""); @@ -139,6 +152,7 @@ public class UserAccountsAddPage extends UserAccountsPage { Map body = new HashMap(); body.put("emailAddress", emailAddress); + body.put("externalAuthId", externalAuthId); body.put("firstName", firstName); body.put("lastName", lastName); body.put("selectedRole", selectedRoleUri); diff --git a/webapp/src/edu/cornell/mannlib/vitro/webapp/controller/accounts/admin/UserAccountsAddPageStrategy.java b/webapp/src/edu/cornell/mannlib/vitro/webapp/controller/accounts/admin/UserAccountsAddPageStrategy.java index 9061030e7..fe1e1c489 100644 --- a/webapp/src/edu/cornell/mannlib/vitro/webapp/controller/accounts/admin/UserAccountsAddPageStrategy.java +++ b/webapp/src/edu/cornell/mannlib/vitro/webapp/controller/accounts/admin/UserAccountsAddPageStrategy.java @@ -57,6 +57,7 @@ public abstract class UserAccountsAddPageStrategy extends UserAccountsPage { private static class EmailStrategy extends UserAccountsAddPageStrategy { public static final String CREATE_PASSWORD_URL = "/accounts/createPassword"; + private static final String EMAIL_TEMPLATE = "userAccounts-acctCreatedEmail.ftl"; private boolean sentEmail; @@ -91,15 +92,14 @@ public abstract class UserAccountsAddPageStrategy extends UserAccountsPage { Map body = new HashMap(); body.put("userAccount", page.getAddedAccount()); body.put("passwordLink", buildCreatePasswordLink()); - body.put("subjectLine", "Your VIVO account has been created."); FreemarkerEmailMessage email = FreemarkerEmailFactory .createNewMessage(vreq); email.addRecipient(TO, page.getAddedAccount().getEmailAddress()); email.setSubject("Your VIVO account has been created."); - email.setHtmlTemplate("userAccounts-acctCreatedEmail-html.ftl"); - email.setTextTemplate("userAccounts-acctCreatedEmail-text.ftl"); + email.setTemplate(EMAIL_TEMPLATE); email.setBodyMap(body); + email.processTemplate(); email.send(); sentEmail = true; diff --git a/webapp/src/edu/cornell/mannlib/vitro/webapp/controller/accounts/admin/UserAccountsAdminController.java b/webapp/src/edu/cornell/mannlib/vitro/webapp/controller/accounts/admin/UserAccountsAdminController.java index b17a01c32..f21d4c340 100644 --- a/webapp/src/edu/cornell/mannlib/vitro/webapp/controller/accounts/admin/UserAccountsAdminController.java +++ b/webapp/src/edu/cornell/mannlib/vitro/webapp/controller/accounts/admin/UserAccountsAdminController.java @@ -57,9 +57,9 @@ public class UserAccountsAdminController extends FreemarkerHttpServlet { if (page.isSubmit() && page.isValid()) { page.createNewAccount(); - UserAccountsListPage listPage = new UserAccountsListPage(vreq); - return listPage.showPageWithNewAccount(page.getAddedAccount(), - page.wasPasswordEmailSent()); + UserAccountsListPage.Message.showNewAccount(vreq, + page.getAddedAccount(), page.wasPasswordEmailSent()); + return redirectToList(); } else { return page.showPage(); } @@ -71,9 +71,10 @@ public class UserAccountsAdminController extends FreemarkerHttpServlet { return showHomePage(vreq, page.getBogusMessage()); } else if (page.isSubmit() && page.isValid()) { page.updateAccount(); - UserAccountsListPage listPage = new UserAccountsListPage(vreq); - return listPage.showPageWithUpdatedAccount( + + UserAccountsListPage.Message.showUpdatedAccount(vreq, page.getUpdatedAccount(), page.wasPasswordEmailSent()); + return redirectToList(); } else { return page.showPage(); } @@ -83,8 +84,8 @@ public class UserAccountsAdminController extends FreemarkerHttpServlet { UserAccountsDeleter deleter = new UserAccountsDeleter(vreq); Collection deletedUris = deleter.delete(); - return new UserAccountsListPage(vreq) - .showPageWithDeletions(deletedUris); + UserAccountsListPage.Message.showDeletions(vreq, deletedUris); + return redirectToList(); } private ResponseValues handleListRequest(VitroRequest vreq) { @@ -92,6 +93,14 @@ public class UserAccountsAdminController extends FreemarkerHttpServlet { return page.showPage(); } + /** + * After an successful change, redirect to the list instead of forwarding. + * That way, a browser "refresh" won't try to repeat the operation. + */ + private ResponseValues redirectToList() { + return new RedirectResponseValues("/accountsAdmin/list"); + } + private ResponseValues showHomePage(VitroRequest vreq, String message) { DisplayMessage.setMessage(vreq, message); return new RedirectResponseValues("/"); diff --git a/webapp/src/edu/cornell/mannlib/vitro/webapp/controller/accounts/admin/UserAccountsEditPage.java b/webapp/src/edu/cornell/mannlib/vitro/webapp/controller/accounts/admin/UserAccountsEditPage.java index c939dfe24..8b88daf94 100644 --- a/webapp/src/edu/cornell/mannlib/vitro/webapp/controller/accounts/admin/UserAccountsEditPage.java +++ b/webapp/src/edu/cornell/mannlib/vitro/webapp/controller/accounts/admin/UserAccountsEditPage.java @@ -28,6 +28,7 @@ public class UserAccountsEditPage extends UserAccountsPage { private static final String PARAMETER_SUBMIT = "submitEdit"; private static final String PARAMETER_USER_URI = "editAccount"; private static final String PARAMETER_EMAIL_ADDRESS = "emailAddress"; + private static final String PARAMETER_EXTERNAL_AUTH_ID = "externalAuthId"; private static final String PARAMETER_FIRST_NAME = "firstName"; private static final String PARAMETER_LAST_NAME = "lastName"; private static final String PARAMETER_ROLE = "role"; @@ -36,6 +37,7 @@ public class UserAccountsEditPage extends UserAccountsPage { private static final String ERROR_NO_EMAIL = "errorEmailIsEmpty"; private static final String ERROR_EMAIL_IN_USE = "errorEmailInUse"; private static final String ERROR_EMAIL_INVALID_FORMAT = "errorEmailInvalidFormat"; + private static final String ERROR_EXTERNAL_AUTH_ID_IN_USE = "errorExternalAuthIdInUse"; private static final String ERROR_NO_FIRST_NAME = "errorFirstNameIsEmpty"; private static final String ERROR_NO_LAST_NAME = "errorLastNameIsEmpty"; private static final String ERROR_NO_ROLE = "errorNoRoleSelected"; @@ -48,6 +50,7 @@ public class UserAccountsEditPage extends UserAccountsPage { private boolean submit; private String userUri = ""; private String emailAddress = ""; + private String externalAuthId = ""; private String firstName = ""; private String lastName = ""; private String selectedRoleUri = ""; @@ -79,6 +82,7 @@ public class UserAccountsEditPage extends UserAccountsPage { submit = isFlagOnRequest(PARAMETER_SUBMIT); userUri = getStringParameter(PARAMETER_USER_URI, ""); emailAddress = getStringParameter(PARAMETER_EMAIL_ADDRESS, ""); + externalAuthId = getStringParameter(PARAMETER_EXTERNAL_AUTH_ID, ""); firstName = getStringParameter(PARAMETER_FIRST_NAME, ""); lastName = getStringParameter(PARAMETER_LAST_NAME, ""); selectedRoleUri = getStringParameter(PARAMETER_ROLE, ""); @@ -117,11 +121,13 @@ public class UserAccountsEditPage extends UserAccountsPage { errorCode = ERROR_EMAIL_IN_USE; } else if (!isEmailValidFormat()) { errorCode = ERROR_EMAIL_INVALID_FORMAT; + } else if (externalAuthIdIsChanged() && isExternalAuthIdInUse()) { + errorCode = ERROR_EXTERNAL_AUTH_ID_IN_USE; } else if (firstName.isEmpty()) { errorCode = ERROR_NO_FIRST_NAME; } else if (lastName.isEmpty()) { errorCode = ERROR_NO_LAST_NAME; - } else if (selectedRoleUri.isEmpty()) { + } else if (!isRootUser() && selectedRoleUri.isEmpty()) { errorCode = ERROR_NO_ROLE; } else { errorCode = strategy.additionalValidations(); @@ -139,7 +145,22 @@ public class UserAccountsEditPage extends UserAccountsPage { private boolean isEmailValidFormat() { return Authenticator.isValidEmailAddress(emailAddress); } - + + private boolean externalAuthIdIsChanged() { + return !externalAuthId.equals(userAccount.getExternalAuthId()); + } + + private boolean isExternalAuthIdInUse() { + if (externalAuthId.isEmpty()) { + return false; + } + return userAccountsDao.getUserAccountByExternalAuthId(externalAuthId) != null; + } + + private boolean isRootUser() { + return ((userAccount != null) && userAccount.isRootUser()); + } + public boolean isValid() { return errorCode.isEmpty(); } @@ -149,16 +170,22 @@ public class UserAccountsEditPage extends UserAccountsPage { if (isSubmit()) { body.put("emailAddress", emailAddress); + body.put("externalAuthId", externalAuthId); body.put("firstName", firstName); body.put("lastName", lastName); body.put("selectedRole", selectedRoleUri); } else { body.put("emailAddress", userAccount.getEmailAddress()); + body.put("externalAuthId", userAccount.getExternalAuthId()); body.put("firstName", userAccount.getFirstName()); body.put("lastName", userAccount.getLastName()); body.put("selectedRole", getExistingRoleUri()); } - body.put("roles", buildRolesList()); + + if (!isRootUser()) { + body.put("roles", buildRolesList()); + } + if (associateWithProfile) { body.put("associate", Boolean.TRUE); } @@ -192,9 +219,14 @@ public class UserAccountsEditPage extends UserAccountsPage { userAccount.setEmailAddress(emailAddress); userAccount.setFirstName(firstName); userAccount.setLastName(lastName); + userAccount.setExternalAuthId(externalAuthId); - userAccount - .setPermissionSetUris(Collections.singleton(selectedRoleUri)); + if (isRootUser()) { + userAccount.setPermissionSetUris(Collections. emptySet()); + } else { + userAccount.setPermissionSetUris(Collections + .singleton(selectedRoleUri)); + } strategy.setAdditionalProperties(userAccount); diff --git a/webapp/src/edu/cornell/mannlib/vitro/webapp/controller/accounts/admin/UserAccountsEditPageStrategy.java b/webapp/src/edu/cornell/mannlib/vitro/webapp/controller/accounts/admin/UserAccountsEditPageStrategy.java index c174f8288..b043c2422 100644 --- a/webapp/src/edu/cornell/mannlib/vitro/webapp/controller/accounts/admin/UserAccountsEditPageStrategy.java +++ b/webapp/src/edu/cornell/mannlib/vitro/webapp/controller/accounts/admin/UserAccountsEditPageStrategy.java @@ -9,8 +9,8 @@ import java.net.URL; import java.util.HashMap; import java.util.Map; +import edu.cornell.mannlib.vitro.webapp.beans.ApplicationBean; import edu.cornell.mannlib.vitro.webapp.beans.UserAccount; -import edu.cornell.mannlib.vitro.webapp.beans.UserAccount.Status; import edu.cornell.mannlib.vitro.webapp.controller.VitroRequest; import edu.cornell.mannlib.vitro.webapp.controller.accounts.UserAccountsPage; import edu.cornell.mannlib.vitro.webapp.controller.authenticate.Authenticator; @@ -57,7 +57,8 @@ public abstract class UserAccountsEditPageStrategy extends UserAccountsPage { private static class EmailStrategy extends UserAccountsEditPageStrategy { private static final String PARAMETER_RESET_PASSWORD = "resetPassword"; - + private static final String EMAIL_TEMPLATE = "userAccounts-resetPasswordEmail.ftl"; + public static final String RESET_PASSWORD_URL = "/accounts/resetPassword"; private boolean resetPassword; @@ -102,19 +103,23 @@ public abstract class UserAccountsEditPageStrategy extends UserAccountsPage { Map body = new HashMap(); body.put("userAccount", page.getUpdatedAccount()); body.put("passwordLink", buildResetPasswordLink()); - body.put("subjectLine", "Reset password request"); + body.put("siteName", getSiteName()); FreemarkerEmailMessage email = FreemarkerEmailFactory .createNewMessage(vreq); email.addRecipient(TO, page.getUpdatedAccount().getEmailAddress()); - email.setSubject("Reset password request"); - email.setHtmlTemplate("userAccounts-resetPasswordEmail-html.ftl"); - email.setTextTemplate("userAccounts-resetPasswordEmail-text.ftl"); + email.setTemplate(EMAIL_TEMPLATE); email.setBodyMap(body); + email.processTemplate(); email.send(); sentEmail = true; } + + private String getSiteName() { + ApplicationBean appBean = vreq.getAppBean(); + return appBean.getApplicationName(); + } private String buildResetPasswordLink() { try { diff --git a/webapp/src/edu/cornell/mannlib/vitro/webapp/controller/accounts/admin/UserAccountsListPage.java b/webapp/src/edu/cornell/mannlib/vitro/webapp/controller/accounts/admin/UserAccountsListPage.java index 08476691d..3da82e412 100644 --- a/webapp/src/edu/cornell/mannlib/vitro/webapp/controller/accounts/admin/UserAccountsListPage.java +++ b/webapp/src/edu/cornell/mannlib/vitro/webapp/controller/accounts/admin/UserAccountsListPage.java @@ -11,6 +11,9 @@ import java.util.HashMap; import java.util.List; import java.util.Map; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpSession; + import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; @@ -19,12 +22,12 @@ import edu.cornell.mannlib.vitro.webapp.beans.UserAccount; import edu.cornell.mannlib.vitro.webapp.beans.UserAccount.Status; import edu.cornell.mannlib.vitro.webapp.controller.VitroRequest; import edu.cornell.mannlib.vitro.webapp.controller.accounts.UserAccountsOrdering; +import edu.cornell.mannlib.vitro.webapp.controller.accounts.UserAccountsOrdering.Direction; +import edu.cornell.mannlib.vitro.webapp.controller.accounts.UserAccountsOrdering.Field; import edu.cornell.mannlib.vitro.webapp.controller.accounts.UserAccountsPage; import edu.cornell.mannlib.vitro.webapp.controller.accounts.UserAccountsSelection; import edu.cornell.mannlib.vitro.webapp.controller.accounts.UserAccountsSelectionCriteria; import edu.cornell.mannlib.vitro.webapp.controller.accounts.UserAccountsSelector; -import edu.cornell.mannlib.vitro.webapp.controller.accounts.UserAccountsOrdering.Direction; -import edu.cornell.mannlib.vitro.webapp.controller.accounts.UserAccountsOrdering.Field; import edu.cornell.mannlib.vitro.webapp.controller.freemarker.responsevalues.ResponseValues; import edu.cornell.mannlib.vitro.webapp.controller.freemarker.responsevalues.TemplateResponseValues; @@ -91,55 +94,7 @@ public class UserAccountsListPage extends UserAccountsPage { UserAccountsSelection selection = UserAccountsSelector.select( userAccountsModel, criteria); Map body = buildTemplateBodyMap(selection); - return new TemplateResponseValues(TEMPLATE_NAME, body); - } - - /** - * We just came from adding a new account. Show the list with a message. - */ - public ResponseValues showPageWithNewAccount(UserAccount userAccount, - boolean emailWasSent) { - UserAccountsSelection selection = UserAccountsSelector.select( - userAccountsModel, criteria); - Map body = buildTemplateBodyMap(selection); - - body.put("newUserAccount", new UserAccountWrapper(userAccount, - Collections. emptyList())); - if (emailWasSent) { - body.put("emailWasSent", Boolean.TRUE); - } - - return new TemplateResponseValues(TEMPLATE_NAME, body); - } - - /** - * We just came from editing an account. Show the list with a message. - */ - public ResponseValues showPageWithUpdatedAccount(UserAccount userAccount, - boolean emailWasSent) { - UserAccountsSelection selection = UserAccountsSelector.select( - userAccountsModel, criteria); - Map body = buildTemplateBodyMap(selection); - - body.put("updatedUserAccount", new UserAccountWrapper(userAccount, - Collections. emptyList())); - if (emailWasSent) { - body.put("emailWasSent", Boolean.TRUE); - } - - return new TemplateResponseValues(TEMPLATE_NAME, body); - } - - /** - * We just came from deleting accounts. Show the list with a message. - */ - public ResponseValues showPageWithDeletions(Collection deletedUris) { - UserAccountsSelection selection = UserAccountsSelector.select( - userAccountsModel, criteria); - Map body = buildTemplateBodyMap(selection); - - body.put("deletedAccountCount", deletedUris.size()); - + Message.applyToBodyMap(vreq, body); return new TemplateResponseValues(TEMPLATE_NAME, body); } @@ -238,10 +193,15 @@ public class UserAccountsListPage extends UserAccountsPage { private List findPermissionSetLabels(UserAccount account) { List labels = new ArrayList(); - for (String uri : account.getPermissionSetUris()) { - PermissionSet pSet = userAccountsDao.getPermissionSetByUri(uri); - if (pSet != null) { - labels.add(pSet.getLabel()); + + if (account.isRootUser()) { + labels.add("ROOT"); + } else { + for (String uri : account.getPermissionSetUris()) { + PermissionSet pSet = userAccountsDao.getPermissionSetByUri(uri); + if (pSet != null) { + labels.add(pSet.getLabel()); + } } } return labels; @@ -301,4 +261,84 @@ public class UserAccountsListPage extends UserAccountsPage { } + /** + * Message info that lives in the session. Another request can store this, + * and it will be displayed (once) by the list page. + */ + public static class Message { + private static final String ATTRIBUTE = Message.class.getName(); + private static final Collection EMPTY = Collections.emptySet(); + + public static void showNewAccount(HttpServletRequest req, + UserAccount userAccount, boolean emailWasSent) { + Message message = new Message(Type.NEW_ACCOUNT, userAccount, + emailWasSent, EMPTY); + setMessage(req, message); + } + + public static void showUpdatedAccount(HttpServletRequest req, + UserAccount userAccount, boolean emailWasSent) { + Message message = new Message(Type.UPDATED_ACCOUNT, userAccount, + emailWasSent, EMPTY); + setMessage(req, message); + } + + public static void showDeletions(HttpServletRequest req, + Collection deletedUris) { + Message message = new Message(Type.DELETIONS, null, false, + deletedUris); + setMessage(req, message); + } + + private static void setMessage(HttpServletRequest req, Message message) { + req.getSession().setAttribute(ATTRIBUTE, message); + } + + public static void applyToBodyMap(HttpServletRequest req, + Map body) { + HttpSession session = req.getSession(); + Object o = session.getAttribute(ATTRIBUTE); + session.removeAttribute(ATTRIBUTE); + + if (o instanceof Message) { + ((Message) o).applyToBodyMap(body); + } + } + + enum Type { + NEW_ACCOUNT, UPDATED_ACCOUNT, DELETIONS + } + + private final Type type; + private final UserAccount userAccount; + private final boolean emailWasSent; + private final Collection deletedUris; + + public Message(Type type, UserAccount userAccount, + boolean emailWasSent, Collection deletedUris) { + this.type = type; + this.userAccount = userAccount; + this.emailWasSent = emailWasSent; + this.deletedUris = deletedUris; + } + + private void applyToBodyMap(Map body) { + if (type == Type.NEW_ACCOUNT) { + body.put("newUserAccount", new UserAccountWrapper(userAccount, + Collections. emptyList())); + if (emailWasSent) { + body.put("emailWasSent", Boolean.TRUE); + } + } else if (type == Type.UPDATED_ACCOUNT) { + body.put("updatedUserAccount", new UserAccountWrapper( + userAccount, Collections. emptyList())); + if (emailWasSent) { + body.put("emailWasSent", Boolean.TRUE); + } + } else { + body.put("deletedAccountCount", deletedUris.size()); + } + } + } + } diff --git a/webapp/src/edu/cornell/mannlib/vitro/webapp/controller/accounts/user/UserAccountsCreatePasswordPage.java b/webapp/src/edu/cornell/mannlib/vitro/webapp/controller/accounts/user/UserAccountsCreatePasswordPage.java index d134fd737..40ae4d6d0 100644 --- a/webapp/src/edu/cornell/mannlib/vitro/webapp/controller/accounts/user/UserAccountsCreatePasswordPage.java +++ b/webapp/src/edu/cornell/mannlib/vitro/webapp/controller/accounts/user/UserAccountsCreatePasswordPage.java @@ -26,6 +26,7 @@ public class UserAccountsCreatePasswordPage extends .getLog(UserAccountsCreatePasswordPage.class); private static final String TEMPLATE_NAME = "userAccounts-createPassword.ftl"; + private static final String EMAIL_TEMPLATE = "userAccounts-passwordCreatedEmail.ftl"; public UserAccountsCreatePasswordPage(VitroRequest vreq) { super(vreq); @@ -39,7 +40,7 @@ public class UserAccountsCreatePasswordPage extends userAccountsDao.updateUserAccount(userAccount); log.debug("Set password on '" + userAccount.getEmailAddress() + "' to '" + newPassword + "'"); - + notifyUser(); } @@ -56,15 +57,14 @@ public class UserAccountsCreatePasswordPage extends private void notifyUser() { Map body = new HashMap(); body.put("userAccount", userAccount); - body.put("subjectLine", "Password successfully created."); FreemarkerEmailMessage email = FreemarkerEmailFactory .createNewMessage(vreq); email.addRecipient(TO, userAccount.getEmailAddress()); email.setSubject("Password successfully created."); - email.setHtmlTemplate("userAccounts-passwordCreatedEmail-html.ftl"); - email.setTextTemplate("userAccounts-passwordCreatedEmail-text.ftl"); + email.setTemplate(EMAIL_TEMPLATE); email.setBodyMap(body); + email.processTemplate(); email.send(); } } diff --git a/webapp/src/edu/cornell/mannlib/vitro/webapp/controller/accounts/user/UserAccountsFirstTimeExternalPage.java b/webapp/src/edu/cornell/mannlib/vitro/webapp/controller/accounts/user/UserAccountsFirstTimeExternalPage.java index f180d23c9..7b1d00572 100644 --- a/webapp/src/edu/cornell/mannlib/vitro/webapp/controller/accounts/user/UserAccountsFirstTimeExternalPage.java +++ b/webapp/src/edu/cornell/mannlib/vitro/webapp/controller/accounts/user/UserAccountsFirstTimeExternalPage.java @@ -9,6 +9,8 @@ import java.util.Map; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpSession; +import org.apache.commons.lang.StringUtils; + import edu.cornell.mannlib.vitro.webapp.auth.permissions.PermissionSetsLoader; import edu.cornell.mannlib.vitro.webapp.beans.UserAccount; import edu.cornell.mannlib.vitro.webapp.beans.UserAccount.Status; @@ -22,13 +24,14 @@ import edu.cornell.mannlib.vitro.webapp.controller.freemarker.responsevalues.Tem * Handle the first-time login of an Externally Authenticated user who has no * UserAccount - let's create one! * - * If they get here from the login, there should an externalAuthId waiting in - * the session. Otherwise, they should get here by submitting the form, which - * will have the externalAuthId as a hidden field. + * If they get here from the login, there should be an ExternalLoginInfo waiting + * in the session. Otherwise, they should get here by submitting the form, which + * will have the info in hidden fields. */ public class UserAccountsFirstTimeExternalPage extends UserAccountsPage { private static final String PARAMETER_SUBMIT = "submit"; private static final String PARAMETER_EXTERNAL_AUTH_ID = "externalAuthId"; + private static final String PARAMETER_AFTER_LOGIN_URL = "afterLoginUrl"; private static final String PARAMETER_EMAIL_ADDRESS = "emailAddress"; private static final String PARAMETER_FIRST_NAME = "firstName"; private static final String PARAMETER_LAST_NAME = "lastName"; @@ -41,23 +44,25 @@ public class UserAccountsFirstTimeExternalPage extends UserAccountsPage { private static final String TEMPLATE_NAME = "userAccounts-firstTimeExternal.ftl"; - private static final String ATTRIBUTE_EXTERNAL_AUTH_ID = UserAccountsFirstTimeExternalPage.class + private static final String ATTRIBUTE_EXTERNAL_LOGIN_INFO = UserAccountsFirstTimeExternalPage.class .getName(); /** - * Let some other request set the External Auth ID before redirecting to - * here. + * Let some other request set the External Auth ID and the afterLogin URL + * before redirecting to here. */ - public static void setExternalAuthId(HttpServletRequest req, - String externalAuthId) { - req.getSession().setAttribute(ATTRIBUTE_EXTERNAL_AUTH_ID, - externalAuthId); + public static void setExternalLoginInfo(HttpServletRequest req, + String externalAuthId, String afterLoginUrl) { + req.getSession().setAttribute(ATTRIBUTE_EXTERNAL_LOGIN_INFO, + new ExternalLoginInfo(externalAuthId, afterLoginUrl)); } private final UserAccountsFirstTimeExternalPageStrategy strategy; - private boolean submit = false; private String externalAuthId = ""; + private String afterLoginUrl = ""; + + private boolean submit = false; private String emailAddress = ""; private String firstName = ""; private String lastName = ""; @@ -71,7 +76,7 @@ public class UserAccountsFirstTimeExternalPage extends UserAccountsPage { this.strategy = UserAccountsFirstTimeExternalPageStrategy.getInstance( vreq, this, isEmailEnabled()); - checkSessionForExternalAuthId(); + checkSessionForExternalLoginInfo(); if (externalAuthId.isEmpty()) { parseRequestParameters(); } @@ -83,20 +88,26 @@ public class UserAccountsFirstTimeExternalPage extends UserAccountsPage { } } - private void checkSessionForExternalAuthId() { + private void checkSessionForExternalLoginInfo() { HttpSession session = vreq.getSession(); - Object o = session.getAttribute(ATTRIBUTE_EXTERNAL_AUTH_ID); - session.removeAttribute(ATTRIBUTE_EXTERNAL_AUTH_ID); + Object o = session.getAttribute(ATTRIBUTE_EXTERNAL_LOGIN_INFO); + session.removeAttribute(ATTRIBUTE_EXTERNAL_LOGIN_INFO); - if (o instanceof String) { - externalAuthId = (String) o; + if (o instanceof ExternalLoginInfo) { + externalAuthId = ((ExternalLoginInfo) o).externalAuthId; + afterLoginUrl = ((ExternalLoginInfo) o).afterLoginUrl; + if (afterLoginUrl == null) { + afterLoginUrl = ""; + } } } private void parseRequestParameters() { - submit = isFlagOnRequest(PARAMETER_SUBMIT); externalAuthId = getStringParameter(PARAMETER_EXTERNAL_AUTH_ID, ""); + afterLoginUrl = getStringParameter(PARAMETER_AFTER_LOGIN_URL, ""); + + submit = isFlagOnRequest(PARAMETER_SUBMIT); emailAddress = getStringParameter(PARAMETER_EMAIL_ADDRESS, ""); firstName = getStringParameter(PARAMETER_FIRST_NAME, ""); lastName = getStringParameter(PARAMETER_LAST_NAME, ""); @@ -156,10 +167,12 @@ public class UserAccountsFirstTimeExternalPage extends UserAccountsPage { public final ResponseValues showPage() { Map body = new HashMap(); + body.put("externalAuthId", externalAuthId); + body.put("afterLoginUrl", afterLoginUrl); + body.put("emailAddress", emailAddress); body.put("firstName", firstName); body.put("lastName", lastName); - body.put("externalAuthId", externalAuthId); body.put("formUrls", buildUrlsMap()); if (!errorCode.isEmpty()) { @@ -191,4 +204,25 @@ public class UserAccountsFirstTimeExternalPage extends UserAccountsPage { return u; } + /** + * If the afterLoginUrl is missing, go to the home page. If it is relative, + * make sure it doesn't start with the cotext path. + */ + public String getAfterLoginUrl() { + if (StringUtils.isEmpty(afterLoginUrl)) { + return null; + } + return afterLoginUrl; + } + + private static class ExternalLoginInfo { + final String externalAuthId; + final String afterLoginUrl; + + public ExternalLoginInfo(String externalAuthId, String afterLoginUrl) { + this.externalAuthId = externalAuthId; + this.afterLoginUrl = afterLoginUrl; + } + } + } diff --git a/webapp/src/edu/cornell/mannlib/vitro/webapp/controller/accounts/user/UserAccountsFirstTimeExternalPageStrategy.java b/webapp/src/edu/cornell/mannlib/vitro/webapp/controller/accounts/user/UserAccountsFirstTimeExternalPageStrategy.java index 71ce98161..975551fdc 100644 --- a/webapp/src/edu/cornell/mannlib/vitro/webapp/controller/accounts/user/UserAccountsFirstTimeExternalPageStrategy.java +++ b/webapp/src/edu/cornell/mannlib/vitro/webapp/controller/accounts/user/UserAccountsFirstTimeExternalPageStrategy.java @@ -52,6 +52,8 @@ public abstract class UserAccountsFirstTimeExternalPageStrategy extends public static class EmailStrategy extends UserAccountsFirstTimeExternalPageStrategy { + private static final String EMAIL_TEMPLATE = "userAccounts-firstTimeExternalEmail.ftl"; + public EmailStrategy(VitroRequest vreq, UserAccountsFirstTimeExternalPage page) { super(vreq, page); @@ -66,15 +68,14 @@ public abstract class UserAccountsFirstTimeExternalPageStrategy extends public void notifyUser(UserAccount ua) { Map body = new HashMap(); body.put("userAccount", ua); - body.put("subjectLine", "Your VIVO account has been created."); FreemarkerEmailMessage email = FreemarkerEmailFactory .createNewMessage(vreq); email.addRecipient(TO, ua.getEmailAddress()); email.setSubject("Your VIVO account has been created."); - email.setHtmlTemplate("userAccounts-firstTimeExternalEmail-html.ftl"); - email.setTextTemplate("userAccounts-firstTimeExternalEmail-text.ftl"); + email.setTemplate(EMAIL_TEMPLATE); email.setBodyMap(body); + email.processTemplate(); email.send(); } diff --git a/webapp/src/edu/cornell/mannlib/vitro/webapp/controller/accounts/user/UserAccountsMyAccountPageStrategy.java b/webapp/src/edu/cornell/mannlib/vitro/webapp/controller/accounts/user/UserAccountsMyAccountPageStrategy.java index f220d3236..d962165cd 100644 --- a/webapp/src/edu/cornell/mannlib/vitro/webapp/controller/accounts/user/UserAccountsMyAccountPageStrategy.java +++ b/webapp/src/edu/cornell/mannlib/vitro/webapp/controller/accounts/user/UserAccountsMyAccountPageStrategy.java @@ -107,6 +107,8 @@ public abstract class UserAccountsMyAccountPageStrategy extends private static final String ERROR_WRONG_PASSWORD_LENGTH = "errorPasswordIsWrongLength"; private static final String ERROR_PASSWORDS_DONT_MATCH = "errorPasswordsDontMatch"; + + private static final String EMAIL_TEMPLATE = "userAccounts-confirmEmailChangedEmail.ftl"; private final String originalEmail; @@ -167,15 +169,14 @@ public abstract class UserAccountsMyAccountPageStrategy extends Map body = new HashMap(); body.put("userAccount", page.getUserAccount()); - body.put("subjectLine", "Your VIVO email account has been changed."); FreemarkerEmailMessage email = FreemarkerEmailFactory .createNewMessage(vreq); email.addRecipient(TO, page.getUserAccount().getEmailAddress()); email.setSubject("Your VIVO email account has been changed."); - email.setHtmlTemplate("userAccounts-confirmEmailChangedEmail-html.ftl"); - email.setTextTemplate("userAccounts-confirmEmailChangedEmail-text.ftl"); + email.setTemplate(EMAIL_TEMPLATE); email.setBodyMap(body); + email.processTemplate(); email.send(); emailSent = true; diff --git a/webapp/src/edu/cornell/mannlib/vitro/webapp/controller/accounts/user/UserAccountsResetPasswordPage.java b/webapp/src/edu/cornell/mannlib/vitro/webapp/controller/accounts/user/UserAccountsResetPasswordPage.java index 7902cbe5b..3bd511b83 100644 --- a/webapp/src/edu/cornell/mannlib/vitro/webapp/controller/accounts/user/UserAccountsResetPasswordPage.java +++ b/webapp/src/edu/cornell/mannlib/vitro/webapp/controller/accounts/user/UserAccountsResetPasswordPage.java @@ -26,6 +26,8 @@ public class UserAccountsResetPasswordPage extends UserAccountsPasswordBasePage private static final String TEMPLATE_NAME = "userAccounts-resetPassword.ftl"; + private static final String EMAIL_TEMPLATE = "userAccounts-passwordResetEmail.ftl"; + protected UserAccountsResetPasswordPage(VitroRequest vreq) { super(vreq); } @@ -38,7 +40,7 @@ public class UserAccountsResetPasswordPage extends UserAccountsPasswordBasePage userAccountsDao.updateUserAccount(userAccount); log.debug("Set password on '" + userAccount.getEmailAddress() + "' to '" + newPassword + "'"); - + notifyUser(); } @@ -55,15 +57,14 @@ public class UserAccountsResetPasswordPage extends UserAccountsPasswordBasePage private void notifyUser() { Map body = new HashMap(); body.put("userAccount", userAccount); - body.put("subjectLine", "Password changed."); FreemarkerEmailMessage email = FreemarkerEmailFactory .createNewMessage(vreq); email.addRecipient(TO, userAccount.getEmailAddress()); email.setSubject("Password changed."); - email.setHtmlTemplate("userAccounts-passwordResetEmail-html.ftl"); - email.setTextTemplate("userAccounts-passwordResetEmail-text.ftl"); + email.setTemplate(EMAIL_TEMPLATE); email.setBodyMap(body); + email.processTemplate(); email.send(); } diff --git a/webapp/src/edu/cornell/mannlib/vitro/webapp/controller/accounts/user/UserAccountsUserController.java b/webapp/src/edu/cornell/mannlib/vitro/webapp/controller/accounts/user/UserAccountsUserController.java index 7628362ac..3dafd87f9 100644 --- a/webapp/src/edu/cornell/mannlib/vitro/webapp/controller/accounts/user/UserAccountsUserController.java +++ b/webapp/src/edu/cornell/mannlib/vitro/webapp/controller/accounts/user/UserAccountsUserController.java @@ -4,10 +4,10 @@ package edu.cornell.mannlib.vitro.webapp.controller.accounts.user; import static edu.cornell.mannlib.vedit.beans.LoginStatusBean.AuthenticationSource.EXTERNAL; +import org.apache.commons.lang.StringUtils; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; -import edu.cornell.mannlib.vedit.beans.LoginStatusBean.AuthenticationSource; import edu.cornell.mannlib.vitro.webapp.auth.requestedAction.Actions; import edu.cornell.mannlib.vitro.webapp.auth.requestedAction.usepages.EditOwnAccount; import edu.cornell.mannlib.vitro.webapp.beans.DisplayMessage; @@ -115,9 +115,7 @@ public class UserAccountsUserController extends FreemarkerHttpServlet { UserAccount userAccount = page.createAccount(); Authenticator auth = Authenticator.getInstance(vreq); auth.recordLoginAgainstUserAccount(userAccount, EXTERNAL); - LoginProcessBean.removeBean(vreq); - - return showLoginRedirection(vreq); + return showLoginRedirection(vreq, page.getAfterLoginUrl()); } else { return page.showPage(); } @@ -132,10 +130,31 @@ public class UserAccountsUserController extends FreemarkerHttpServlet { return new RedirectResponseValues("/"); } - private ResponseValues showLoginRedirection(VitroRequest vreq) { - LoginRedirector lr = new LoginRedirector(vreq); + private ResponseValues showLoginRedirection(VitroRequest vreq, + String afterLoginUrl) { + LoginRedirector lr = new LoginRedirector(vreq, afterLoginUrl); DisplayMessage.setMessage(vreq, lr.assembleWelcomeMessage()); String uri = lr.getRedirectionUriForLoggedInUser(); - return new RedirectResponseValues(uri); + return new RedirectResponseValues(stripContextPath(vreq, uri)); + } + + /** + * TODO The LoginRedirector gives a URI that includes the context path. But + * the RedirectResponseValues wants a URI that does not include the context + * path. + * + * Bridge the gap. + */ + private String stripContextPath(VitroRequest vreq, String uri) { + if ((uri == null) || uri.isEmpty() || uri.equals(vreq.getContextPath())) { + return "/"; + } + if (uri.contains("://")) { + return uri; + } + if (uri.startsWith(vreq.getContextPath() + '/')) { + return uri.substring(vreq.getContextPath().length()); + } + return uri; } } diff --git a/webapp/src/edu/cornell/mannlib/vitro/webapp/controller/authenticate/Authenticator.java b/webapp/src/edu/cornell/mannlib/vitro/webapp/controller/authenticate/Authenticator.java index cddad2bdc..dce2bf46a 100644 --- a/webapp/src/edu/cornell/mannlib/vitro/webapp/controller/authenticate/Authenticator.java +++ b/webapp/src/edu/cornell/mannlib/vitro/webapp/controller/authenticate/Authenticator.java @@ -51,10 +51,10 @@ public abstract class Authenticator { // ---------------------------------------------------------------------- /** Maximum inactive interval for a ordinary logged-in session, in seconds. */ - public static final int LOGGED_IN_TIMEOUT_INTERVAL = 300; + public static final int LOGGED_IN_TIMEOUT_INTERVAL = 60 * 60; /** Maximum inactive interval for a editor (or better) session, in seconds. */ - public static final int PRIVILEGED_TIMEOUT_INTERVAL = 32000; + public static final int PRIVILEGED_TIMEOUT_INTERVAL = 60 * 60 * 8; /** * Get the UserAccount for this external ID, or null if there is none. @@ -96,8 +96,9 @@ public abstract class Authenticator { *
 	 * Record that the user has logged in, with all of the housekeeping that 
 	 * goes with it:
-	 * - updating the user record
-	 * - setting login status and timeout limit in the session
+	 * - update the user record
+	 * - set login status and timeout limit in the session
+	 * - refresh the Identifiers on the request
 	 * - record the user in the session map
 	 * - notify other users of the model
 	 * 
@@ -105,17 +106,6 @@ public abstract class Authenticator { public abstract void recordLoginAgainstUserAccount(UserAccount userAccount, AuthenticationSource authSource); - /** - *
-	 * Record that the user has logged in but with only external authentication 
-	 * info, so no internal user account.
-	 * - this involves everything except updating the user record.
-	 * 
- * - * TODO JB This goes away. - */ - public abstract void recordLoginWithoutUserAccount(String individualUri); - /** *
 	 * Record that the current user has logged out: - notify other users of the
diff --git a/webapp/src/edu/cornell/mannlib/vitro/webapp/controller/authenticate/BasicAuthenticator.java b/webapp/src/edu/cornell/mannlib/vitro/webapp/controller/authenticate/BasicAuthenticator.java
index 2135f007f..6ebb7a4fd 100644
--- a/webapp/src/edu/cornell/mannlib/vitro/webapp/controller/authenticate/BasicAuthenticator.java
+++ b/webapp/src/edu/cornell/mannlib/vitro/webapp/controller/authenticate/BasicAuthenticator.java
@@ -3,7 +3,6 @@
 package edu.cornell.mannlib.vitro.webapp.controller.authenticate;
 
 import java.util.ArrayList;
-import java.util.Collections;
 import java.util.List;
 import java.util.Map;
 
@@ -16,6 +15,7 @@ import org.apache.commons.logging.LogFactory;
 
 import edu.cornell.mannlib.vedit.beans.LoginStatusBean;
 import edu.cornell.mannlib.vedit.beans.LoginStatusBean.AuthenticationSource;
+import edu.cornell.mannlib.vitro.webapp.auth.identifier.RequestIdentifiers;
 import edu.cornell.mannlib.vitro.webapp.beans.BaseResourceBean.RoleLevel;
 import edu.cornell.mannlib.vitro.webapp.beans.Individual;
 import edu.cornell.mannlib.vitro.webapp.beans.SelfEditingConfiguration;
@@ -123,24 +123,13 @@ public class BasicAuthenticator extends Authenticator {
 		}
 
 		recordLoginOnUserRecord(userAccount);
-		recordLoginWithOrWithoutUserAccount(userAccount.getUri(), authSource);
-	}
 
-	// TODO JB This goes away.
-	@Override
-	public void recordLoginWithoutUserAccount(String individualUri) {
-		recordLoginWithOrWithoutUserAccount(individualUri,
-				AuthenticationSource.EXTERNAL);
-	}
-
-	/** This much is in common on login, whether or not you have a user account. */
-	private void recordLoginWithOrWithoutUserAccount(String userUri,
-			AuthenticationSource authSource) {
 		HttpSession session = request.getSession();
-		createLoginStatusBean(userUri, authSource, session);
-		setSessionTimeoutLimit(session);
-		recordInUserSessionMap(userUri, session);
-		notifyOtherUsers(userUri, session);
+		createLoginStatusBean(userAccount.getUri(), authSource, session);
+		RequestIdentifiers.resetIdentifiers(request);
+		setSessionTimeoutLimit(userAccount, session);
+		recordInUserSessionMap(userAccount.getUri(), session);
+		notifyOtherUsers(userAccount.getUri(), session);
 	}
 
 	/**
@@ -164,11 +153,14 @@ public class BasicAuthenticator extends Authenticator {
 	/**
 	 * Editors and other privileged users get a longer timeout interval.
 	 */
-	private void setSessionTimeoutLimit(HttpSession session) {
+	private void setSessionTimeoutLimit(UserAccount userAccount,
+			HttpSession session) {
 		RoleLevel role = RoleLevel.getRoleFromLoginStatus(request);
 		if (role == RoleLevel.EDITOR || role == RoleLevel.CURATOR
 				|| role == RoleLevel.DB_ADMIN) {
 			session.setMaxInactiveInterval(PRIVILEGED_TIMEOUT_INTERVAL);
+		} else if (userAccount.isRootUser()) {
+			session.setMaxInactiveInterval(PRIVILEGED_TIMEOUT_INTERVAL);
 		} else {
 			session.setMaxInactiveInterval(LOGGED_IN_TIMEOUT_INTERVAL);
 		}
diff --git a/webapp/src/edu/cornell/mannlib/vitro/webapp/controller/authenticate/LoginExternalAuthReturn.java b/webapp/src/edu/cornell/mannlib/vitro/webapp/controller/authenticate/LoginExternalAuthReturn.java
index 8b525f6fe..ce008c64e 100644
--- a/webapp/src/edu/cornell/mannlib/vitro/webapp/controller/authenticate/LoginExternalAuthReturn.java
+++ b/webapp/src/edu/cornell/mannlib/vitro/webapp/controller/authenticate/LoginExternalAuthReturn.java
@@ -5,7 +5,6 @@ package edu.cornell.mannlib.vitro.webapp.controller.authenticate;
 import static edu.cornell.mannlib.vitro.webapp.controller.authenticate.LoginExternalAuthSetup.ATTRIBUTE_REFERRER;
 
 import java.io.IOException;
-import java.util.List;
 
 import javax.servlet.ServletException;
 import javax.servlet.http.HttpServletRequest;
@@ -16,6 +15,9 @@ import org.apache.commons.logging.LogFactory;
 
 import edu.cornell.mannlib.vedit.beans.LoginStatusBean.AuthenticationSource;
 import edu.cornell.mannlib.vitro.webapp.beans.UserAccount;
+import edu.cornell.mannlib.vitro.webapp.controller.accounts.user.UserAccountsFirstTimeExternalPage;
+import edu.cornell.mannlib.vitro.webapp.controller.freemarker.UrlBuilder;
+import edu.cornell.mannlib.vitro.webapp.controller.login.LoginProcessBean;
 
 /**
  * Handle the return from the external authorization login server. If we are
@@ -36,6 +38,13 @@ public class LoginExternalAuthReturn extends BaseLoginServlet {
 	 * - User corresponds to a User acocunt. Record the login. 
 	 * - User corresponds to an Individual (self-editor). 
 	 * - User is not recognized.
+	 * 
+	 * On entry, we expect to find:
+	 * - A LoginProcessBean, which will give us the afterLoginUrl if the login
+	 *      succeeds.
+	 * - A referrer URL, to which we will redirect if the login fails.
+	 *      TODO: is this equal to LoginProcessBean.getLoginPageUrl()?
+	 * These are removed on exit.
 	 * 
*/ @Override @@ -50,39 +59,30 @@ public class LoginExternalAuthReturn extends BaseLoginServlet { return; } + String afterLoginUrl = LoginProcessBean.getBean(req).getAfterLoginUrl(); + removeLoginProcessArtifacts(req); + UserAccount userAccount = getAuthenticator(req) .getAccountForExternalAuth(externalAuthId); - if (userAccount != null) { + if (userAccount == null) { + log.debug("Creating new account for " + externalAuthId + + ", return to '" + afterLoginUrl + "'"); + UserAccountsFirstTimeExternalPage.setExternalLoginInfo(req, + externalAuthId, afterLoginUrl); + resp.sendRedirect(UrlBuilder.getUrl("/accounts/firstTimeExternal")); + return; + } else { log.debug("Logging in as " + userAccount.getUri()); getAuthenticator(req).recordLoginAgainstUserAccount(userAccount, AuthenticationSource.EXTERNAL); - removeLoginProcessArtifacts(req); - new LoginRedirector(req).redirectLoggedInUser(resp); + new LoginRedirector(req, afterLoginUrl).redirectLoggedInUser(resp); return; } - - List associatedUris = getAuthenticator(req) - .getAssociatedIndividualUris(userAccount); - // TODO JB - this case should lead to creating a new account. - if (!associatedUris.isEmpty()) { - log.debug("Recognize '" + externalAuthId + "' as self-editor for " - + associatedUris); - String uri = associatedUris.get(0); - - getAuthenticator(req).recordLoginWithoutUserAccount(uri); - removeLoginProcessArtifacts(req); - new LoginRedirector(req).redirectLoggedInUser(resp); - return; - } - - log.debug("User is not recognized: " + externalAuthId); - removeLoginProcessArtifacts(req); - new LoginRedirector(req).redirectUnrecognizedExternalUser(resp, - externalAuthId); } private void removeLoginProcessArtifacts(HttpServletRequest req) { req.getSession().removeAttribute(ATTRIBUTE_REFERRER); + LoginProcessBean.removeBean(req); } @Override diff --git a/webapp/src/edu/cornell/mannlib/vitro/webapp/controller/authenticate/LoginRedirector.java b/webapp/src/edu/cornell/mannlib/vitro/webapp/controller/authenticate/LoginRedirector.java index 57c9375f1..ede8e4101 100644 --- a/webapp/src/edu/cornell/mannlib/vitro/webapp/controller/authenticate/LoginRedirector.java +++ b/webapp/src/edu/cornell/mannlib/vitro/webapp/controller/authenticate/LoginRedirector.java @@ -16,11 +16,14 @@ import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import edu.cornell.mannlib.vedit.beans.LoginStatusBean; +import edu.cornell.mannlib.vitro.webapp.auth.identifier.IdentifierBundle; +import edu.cornell.mannlib.vitro.webapp.auth.identifier.RequestIdentifiers; +import edu.cornell.mannlib.vitro.webapp.auth.identifier.common.HasRoleLevel; +import edu.cornell.mannlib.vitro.webapp.auth.identifier.common.IsRootUser; import edu.cornell.mannlib.vitro.webapp.beans.BaseResourceBean.RoleLevel; import edu.cornell.mannlib.vitro.webapp.beans.DisplayMessage; import edu.cornell.mannlib.vitro.webapp.beans.UserAccount; import edu.cornell.mannlib.vitro.webapp.controller.Controllers; -import edu.cornell.mannlib.vitro.webapp.controller.login.LoginProcessBean; /** * A user has just completed the login process. What page do we direct them to? @@ -34,15 +37,12 @@ public class LoginRedirector { private final String uriOfAssociatedIndividual; private final String afterLoginPage; - public LoginRedirector(HttpServletRequest request) { + public LoginRedirector(HttpServletRequest request, String afterLoginPage) { this.request = request; this.session = request.getSession(); + this.afterLoginPage = afterLoginPage; uriOfAssociatedIndividual = getAssociatedIndividualUri(); - - LoginProcessBean processBean = LoginProcessBean.getBean(request); - log.debug("process bean is: " + processBean); - afterLoginPage = processBean.getAfterLoginUrl(); } /** Is there an Individual associated with this user? */ @@ -106,7 +106,6 @@ public class LoginRedirector { try { DisplayMessage.setMessage(request, assembleWelcomeMessage()); response.sendRedirect(getRedirectionUriForLoggedInUser()); - LoginProcessBean.removeBean(request); } catch (IOException e) { log.debug("Problem with re-direction", e); response.sendRedirect(getApplicationHomePageUrl()); @@ -142,7 +141,6 @@ public class LoginRedirector { throws IOException { try { response.sendRedirect(getRedirectionUriForCancellingUser()); - LoginProcessBean.removeBean(request); } catch (IOException e) { log.debug("Problem with re-direction", e); response.sendRedirect(getApplicationHomePageUrl()); @@ -158,7 +156,12 @@ public class LoginRedirector { } private boolean isMerelySelfEditor() { - RoleLevel role = RoleLevel.getRoleFromLoginStatus(request); + IdentifierBundle ids = RequestIdentifiers.getIdBundleForRequest(request); + if (IsRootUser.isRootUser(ids)) { + return false; + } + + RoleLevel role = HasRoleLevel.getUsersRoleLevel(ids); return role == RoleLevel.PUBLIC || role == RoleLevel.SELF; } diff --git a/webapp/src/edu/cornell/mannlib/vitro/webapp/controller/edit/Authenticate.java b/webapp/src/edu/cornell/mannlib/vitro/webapp/controller/edit/Authenticate.java index 9bf9847f6..1949009e6 100644 --- a/webapp/src/edu/cornell/mannlib/vitro/webapp/controller/edit/Authenticate.java +++ b/webapp/src/edu/cornell/mannlib/vitro/webapp/controller/edit/Authenticate.java @@ -124,7 +124,7 @@ public class Authenticate extends VitroHttpServlet { // Send them on their way. switch (exitState) { case NOWHERE: - new LoginRedirector(vreq).redirectCancellingUser(response); + showLoginCanceled(response, vreq); break; case LOGGING_IN: showLoginScreen(vreq, response); @@ -133,7 +133,7 @@ public class Authenticate extends VitroHttpServlet { showLoginScreen(vreq, response); break; default: // LOGGED_IN: - new LoginRedirector(vreq).redirectLoggedInUser(response); + showLoginComplete(response, vreq); break; } } catch (Exception e) { @@ -477,6 +477,31 @@ public class Authenticate extends VitroHttpServlet { response.sendRedirect(loginProcessPage); return; } + + /** + * Exit: user has completed the login. Redirect appropriately and clear the bean. + */ + private void showLoginComplete(HttpServletResponse response, + VitroRequest vreq) throws IOException { + getLoginRedirector(vreq).redirectLoggedInUser(response); + LoginProcessBean.removeBean(vreq); + } + + /** + * Exit: user has canceled. Redirect and clear the bean. + */ + private void showLoginCanceled(HttpServletResponse response, + VitroRequest vreq) throws IOException { + getLoginRedirector(vreq).redirectCancellingUser(response); + LoginProcessBean.removeBean(vreq); + } + + private LoginRedirector getLoginRedirector(VitroRequest vreq) { + String afterLoginUrl = LoginProcessBean.getBean(vreq).getAfterLoginUrl(); + return new LoginRedirector(vreq, afterLoginUrl); + } + + /** Get a reference to the Authenticator. */ private Authenticator getAuthenticator(HttpServletRequest request) { diff --git a/webapp/src/edu/cornell/mannlib/vitro/webapp/controller/freemarker/FreemarkerHttpServlet.java b/webapp/src/edu/cornell/mannlib/vitro/webapp/controller/freemarker/FreemarkerHttpServlet.java index fed74cbe0..8792a0225 100644 --- a/webapp/src/edu/cornell/mannlib/vitro/webapp/controller/freemarker/FreemarkerHttpServlet.java +++ b/webapp/src/edu/cornell/mannlib/vitro/webapp/controller/freemarker/FreemarkerHttpServlet.java @@ -184,16 +184,24 @@ public class FreemarkerHttpServlet extends VitroHttpServlet { templateDataModel.put("body", bodyString); // Tell the template and any directives it uses that we're processing a page template. - templateDataModel.put("templateType", PAGE_TEMPLATE_TYPE); - writePage(templateDataModel, config, vreq, response); + templateDataModel.put("templateType", PAGE_TEMPLATE_TYPE); + + writePage(templateDataModel, config, vreq, response, values.getStatusCode()); } protected void doRedirect(HttpServletRequest request, HttpServletResponse response, ResponseValues values) throws ServletException, IOException { String redirectUrl = values.getRedirectUrl(); + setResponseStatus(response, values.getStatusCode()); response.sendRedirect(redirectUrl); } + private void setResponseStatus(HttpServletResponse response, int statusCode) { + if (statusCode > 0) { + response.setStatus(statusCode); + } + } + protected void doForward(HttpServletRequest request, HttpServletResponse response, ResponseValues values) throws ServletException, IOException { String forwardUrl = values.getForwardUrl(); @@ -306,11 +314,18 @@ public class FreemarkerHttpServlet extends VitroHttpServlet { */ public static Map getDirectives() { Map map = new HashMap(); - map.put("dump", new freemarker.ext.dump.DumpDirective()); - map.put("dumpAll", new freemarker.ext.dump.DumpAllDirective()); - map.put("help", new freemarker.ext.dump.HelpDirective()); + map.putAll(getDirectivesForAllEnvironments()); map.put("url", new edu.cornell.mannlib.vitro.webapp.web.directives.UrlDirective()); map.put("widget", new edu.cornell.mannlib.vitro.webapp.web.directives.WidgetDirective()); + + return map; + } + + public static Map getDirectivesForAllEnvironments() { + Map map = new HashMap(); + map.put("dump", new freemarker.ext.dump.DumpDirective()); + map.put("dumpAll", new freemarker.ext.dump.DumpAllDirective()); + map.put("help", new freemarker.ext.dump.HelpDirective()); return map; } @@ -330,9 +345,6 @@ public class FreemarkerHttpServlet extends VitroHttpServlet { Map map = new HashMap(); ApplicationBean appBean = vreq.getAppBean(); - // Ideally, templates wouldn't need portal id. Currently used as a hidden input value - // in the site search box, so needed for now. - String siteName = appBean.getApplicationName(); map.put("siteName", siteName); @@ -365,11 +377,6 @@ public class FreemarkerHttpServlet extends VitroHttpServlet { // This value is used only in stylesheets.ftl and already contains the context path. map.put("stylesheetPath", UrlBuilder.getUrl(themeDir + "/css")); -// String bannerImage = portal.getBannerImage(); -// if ( ! StringUtils.isEmpty(bannerImage)) { -// map.put("bannerImage", UrlBuilder.getUrl(themeDir + "site_icons/" + bannerImage)); -// } - String flashMessage = DisplayMessage.getMessageAndClear(vreq); if (! flashMessage.isEmpty()) { map.put("flash", flashMessage); @@ -448,18 +455,24 @@ public class FreemarkerHttpServlet extends VitroHttpServlet { } protected void writePage(Map root, Configuration config, - HttpServletRequest request, HttpServletResponse response) throws TemplateProcessingException { - writeTemplate(getPageTemplateName(), root, config, request, response); + HttpServletRequest request, HttpServletResponse response, int statusCode) throws TemplateProcessingException { + writeTemplate(getPageTemplateName(), root, config, request, response, statusCode); } protected void writeTemplate(String templateName, Map map, Configuration config, - HttpServletRequest request, HttpServletResponse response) throws TemplateProcessingException { - StringWriter sw = processTemplate(templateName, map, config, request); - write(sw, response); + HttpServletRequest request, HttpServletResponse response) throws TemplateProcessingException { + writeTemplate(templateName, map, config, request, response, 0); + } + + protected void writeTemplate(String templateName, Map map, Configuration config, + HttpServletRequest request, HttpServletResponse response, int statusCode) throws TemplateProcessingException { + StringWriter sw = processTemplate(templateName, map, config, request); + write(sw, response, statusCode); } - protected void write(StringWriter sw, HttpServletResponse response) { + protected void write(StringWriter sw, HttpServletResponse response, int statusCode) { try { + setResponseStatus(response, statusCode); PrintWriter out = response.getWriter(); out.print(sw); } catch (IOException e) { diff --git a/webapp/src/edu/cornell/mannlib/vitro/webapp/controller/freemarker/IndividualController.java b/webapp/src/edu/cornell/mannlib/vitro/webapp/controller/freemarker/IndividualController.java index 303ca289e..23d9082fe 100644 --- a/webapp/src/edu/cornell/mannlib/vitro/webapp/controller/freemarker/IndividualController.java +++ b/webapp/src/edu/cornell/mannlib/vitro/webapp/controller/freemarker/IndividualController.java @@ -100,7 +100,7 @@ public class IndividualController extends FreemarkerHttpServlet { // Check to see if the request is for a non-information resource, redirect if it is. String redirectURL = checkForRedirect ( url, vreq ); if( redirectURL != null ){ - return new RedirectResponseValues(redirectURL); + return new RedirectResponseValues(redirectURL, HttpServletResponse.SC_SEE_OTHER); } Individual individual = null; @@ -463,12 +463,19 @@ public class IndividualController extends FreemarkerHttpServlet { return null; } - - private static Pattern URI_PATTERN = Pattern.compile("^/individual/([^/]*)$"); - //Redirect if the request is for http://hostname/individual/localname - // if accept is nothing or text/html redirect to ??? - // if accept is some RDF thing redirect to the URL for RDF + /* + * Following recipe 3 from "Best Practice Recipes for Publishing RDF Vocabularies." + * See http://www.w3.org/TR/swbp-vocab-pub/#recipe3. + * The basic idea is that a URI like http://vivo.cornell.edu/individual/n1234 + * identifies a real world individual. HTTP cannot send that as the response + * to a GET request because it can only send bytes and not things. The server + * sends a 303, to mean "you asked for something I cannot send you, but I can + * send you this other stream of bytes about that thing." + * In the case of a request like http://vivo.cornell.edu/individual/n1234/n1234.rdf, + * the request is for a set of bytes rather than an individual, so no 303 is needed. + */ + private static Pattern URI_PATTERN = Pattern.compile("^/individual/([^/]*)$"); private String checkForRedirect(String url, VitroRequest vreq) { Matcher m = URI_PATTERN.matcher(url); if( m.matches() && m.groupCount() == 1 ){ @@ -506,7 +513,11 @@ public class IndividualController extends FreemarkerHttpServlet { protected ContentType checkForLinkedDataRequest(String url, VitroRequest vreq ) { try { Matcher m; - // Check for url param specifying format + /* + * Check for url param specifying format. + * Example: http://vivo.cornell.edu/individual/n23?format=rdfxml + * This request will trigger a redirect with a 303. + */ String formatParam = (String) vreq.getParameter("format"); if (formatParam != null) { m = RDFXML_FORMAT.matcher(formatParam); @@ -523,7 +534,11 @@ public class IndividualController extends FreemarkerHttpServlet { } } - //check the accept header + /* + * Check the accept header. This request will trigger a + * redirect with a 303, because the request is for an individual + * but the server can only provide a set of bytes. + */ String acceptHeader = vreq.getHeader("accept"); if (acceptHeader != null) { String ctStr = ContentType.getBestContentType( @@ -538,10 +553,12 @@ public class IndividualController extends FreemarkerHttpServlet { } /* - * check for parts of URL that indicate request for RDF - http://vivo.cornell.edu/individual/n23/n23.rdf - http://vivo.cornell.edu/individual/n23/n23.n3 - http://vivo.cornell.edu/individual/n23/n23.ttl + * Check for parts of URL that indicate request for RDF + * http://vivo.cornell.edu/individual/n23/n23.rdf + * http://vivo.cornell.edu/individual/n23/n23.n3 + * http://vivo.cornell.edu/individual/n23/n23.ttl + * This request will not trigger a redirect and 303, because + * the request is for a set of bytes rather than an individual. */ m = RDF_REQUEST.matcher(url); if( m.matches() ) { @@ -555,8 +572,7 @@ public class IndividualController extends FreemarkerHttpServlet { if( m.matches() ) { return ContentType.TURTLE; } - - + } catch (Throwable th) { log.error("problem while checking accept header " , th); } diff --git a/webapp/src/edu/cornell/mannlib/vitro/webapp/controller/freemarker/responsevalues/BaseResponseValues.java b/webapp/src/edu/cornell/mannlib/vitro/webapp/controller/freemarker/responsevalues/BaseResponseValues.java index 229a0c5a8..c4c6b5f4b 100644 --- a/webapp/src/edu/cornell/mannlib/vitro/webapp/controller/freemarker/responsevalues/BaseResponseValues.java +++ b/webapp/src/edu/cornell/mannlib/vitro/webapp/controller/freemarker/responsevalues/BaseResponseValues.java @@ -22,6 +22,11 @@ public abstract class BaseResponseValues implements ResponseValues { BaseResponseValues(ContentType contentType) { this.contentType = contentType; } + + BaseResponseValues(ContentType contentType, int statusCode) { + this.contentType = contentType; + this.statusCode = statusCode; + } @Override public int getStatusCode() { @@ -38,6 +43,7 @@ public abstract class BaseResponseValues implements ResponseValues { return contentType; } + @Override public void setContentType(ContentType contentType) { this.contentType = contentType; } diff --git a/webapp/src/edu/cornell/mannlib/vitro/webapp/controller/freemarker/responsevalues/RdfResponseValues.java b/webapp/src/edu/cornell/mannlib/vitro/webapp/controller/freemarker/responsevalues/RdfResponseValues.java index 470139e0b..4a3107857 100644 --- a/webapp/src/edu/cornell/mannlib/vitro/webapp/controller/freemarker/responsevalues/RdfResponseValues.java +++ b/webapp/src/edu/cornell/mannlib/vitro/webapp/controller/freemarker/responsevalues/RdfResponseValues.java @@ -14,6 +14,11 @@ public class RdfResponseValues extends BaseResponseValues { this.model = model; } + public RdfResponseValues(ContentType contentType, Model model, int statusCode) { + super(contentType, statusCode); + this.model = model; + } + @Override public Model getModel() { return model; diff --git a/webapp/src/edu/cornell/mannlib/vitro/webapp/controller/freemarker/responsevalues/ResponseValues.java b/webapp/src/edu/cornell/mannlib/vitro/webapp/controller/freemarker/responsevalues/ResponseValues.java index d72d6e22d..6ebdef627 100644 --- a/webapp/src/edu/cornell/mannlib/vitro/webapp/controller/freemarker/responsevalues/ResponseValues.java +++ b/webapp/src/edu/cornell/mannlib/vitro/webapp/controller/freemarker/responsevalues/ResponseValues.java @@ -28,6 +28,8 @@ public interface ResponseValues { public ContentType getContentType(); + public void setContentType(ContentType contentType); + public Model getModel(); } diff --git a/webapp/src/edu/cornell/mannlib/vitro/webapp/controller/freemarker/responsevalues/TemplateResponseValues.java b/webapp/src/edu/cornell/mannlib/vitro/webapp/controller/freemarker/responsevalues/TemplateResponseValues.java index 7e35ed737..b02ce7b0c 100644 --- a/webapp/src/edu/cornell/mannlib/vitro/webapp/controller/freemarker/responsevalues/TemplateResponseValues.java +++ b/webapp/src/edu/cornell/mannlib/vitro/webapp/controller/freemarker/responsevalues/TemplateResponseValues.java @@ -49,7 +49,9 @@ public class TemplateResponseValues extends BaseResponseValues { } public static TemplateResponseValues getTemplateResponseValuesFromException(ExceptionResponseValues responseValues) { - return new TemplateResponseValues(responseValues.getTemplateName(), responseValues.getMap()); + return new TemplateResponseValues(responseValues.getTemplateName(), + responseValues.getMap(), + responseValues.getStatusCode()); } } diff --git a/webapp/src/edu/cornell/mannlib/vitro/webapp/controller/login/LoginProcessBean.java b/webapp/src/edu/cornell/mannlib/vitro/webapp/controller/login/LoginProcessBean.java index 28a30115e..80b28c2d8 100644 --- a/webapp/src/edu/cornell/mannlib/vitro/webapp/controller/login/LoginProcessBean.java +++ b/webapp/src/edu/cornell/mannlib/vitro/webapp/controller/login/LoginProcessBean.java @@ -14,6 +14,14 @@ import org.apache.commons.logging.LogFactory; /** * Where are we in the process of logging on? What message should we show to the * user? + * + * TODO: the contents loginPageUrl is not explicitly defined, but it is assumed + * to be either null, absolute, or relative to the host. It would be better if + * it were explicitly defined, and either null, absolute or relative to the + * context path. Then, the context path could be applied when the URL is used. + * Later for that. + * + * TODO: similar for afterLoginUrl, I presume. */ public class LoginProcessBean { private static final Log log = LogFactory.getLog(LoginProcessBean.class); diff --git a/webapp/src/edu/cornell/mannlib/vitro/webapp/dao/UserAccountsDao.java b/webapp/src/edu/cornell/mannlib/vitro/webapp/dao/UserAccountsDao.java index 46115df77..927538a31 100644 --- a/webapp/src/edu/cornell/mannlib/vitro/webapp/dao/UserAccountsDao.java +++ b/webapp/src/edu/cornell/mannlib/vitro/webapp/dao/UserAccountsDao.java @@ -40,11 +40,6 @@ public interface UserAccountsDao { */ UserAccount getUserAccountByExternalAuthId(String externalAuthId); - /** - * Is this UserAccount a root user? - */ - boolean isRootUser(UserAccount userAccount); - /** * Create a new UserAccount in the model. * diff --git a/webapp/src/edu/cornell/mannlib/vitro/webapp/dao/filtering/UserAccountsDaoFiltering.java b/webapp/src/edu/cornell/mannlib/vitro/webapp/dao/filtering/UserAccountsDaoFiltering.java index 12b725c5b..2a08eb9f8 100644 --- a/webapp/src/edu/cornell/mannlib/vitro/webapp/dao/filtering/UserAccountsDaoFiltering.java +++ b/webapp/src/edu/cornell/mannlib/vitro/webapp/dao/filtering/UserAccountsDaoFiltering.java @@ -47,11 +47,6 @@ public class UserAccountsDaoFiltering extends BaseFiltering implements return innerDao.getUserAccountByExternalAuthId(externalAuthId); } - @Override - public boolean isRootUser(UserAccount userAccount) { - return innerDao.isRootUser(userAccount); - } - @Override public String insertUserAccount(UserAccount userAccount) { return innerDao.insertUserAccount(userAccount); diff --git a/webapp/src/edu/cornell/mannlib/vitro/webapp/dao/jena/DataPropertyStatementDaoSDB.java b/webapp/src/edu/cornell/mannlib/vitro/webapp/dao/jena/DataPropertyStatementDaoSDB.java index a93ac88ae..dac869b54 100644 --- a/webapp/src/edu/cornell/mannlib/vitro/webapp/dao/jena/DataPropertyStatementDaoSDB.java +++ b/webapp/src/edu/cornell/mannlib/vitro/webapp/dao/jena/DataPropertyStatementDaoSDB.java @@ -57,15 +57,13 @@ public class DataPropertyStatementDaoSDB extends DataPropertyStatementDaoJena } else { - String[] graphVars = { "?g" }; String query = "CONSTRUCT { \n" + " <" + entity.getURI() + "> ?p ?o . \n" + - "} WHERE { GRAPH ?g { \n" + + "} WHERE { \n" + " <" + entity.getURI() + "> ?p ?o . \n" + " FILTER(isLiteral(?o)) \n" + - WebappDaoFactorySDB.getFilterBlock(graphVars, datasetMode) + - "} }" ; + "}" ; Model results = null; DatasetWrapper w = dwf.getDatasetWrapper(); Dataset dataset = w.getDataset(); diff --git a/webapp/src/edu/cornell/mannlib/vitro/webapp/dao/jena/IndividualJena.java b/webapp/src/edu/cornell/mannlib/vitro/webapp/dao/jena/IndividualJena.java index 26adf499a..1a282b6ef 100644 --- a/webapp/src/edu/cornell/mannlib/vitro/webapp/dao/jena/IndividualJena.java +++ b/webapp/src/edu/cornell/mannlib/vitro/webapp/dao/jena/IndividualJena.java @@ -781,7 +781,7 @@ public class IndividualJena extends IndividualImpl implements Individual { if (stmt.getObject().isURIResource()) { String typeURI = ((Resource)stmt.getObject()).getURI(); if (pfs.isClassProhibitedFromSearch(typeURI)) { - return false; + return true; } } } diff --git a/webapp/src/edu/cornell/mannlib/vitro/webapp/dao/jena/IndividualSDB.java b/webapp/src/edu/cornell/mannlib/vitro/webapp/dao/jena/IndividualSDB.java index d16fdbbad..a91740814 100644 --- a/webapp/src/edu/cornell/mannlib/vitro/webapp/dao/jena/IndividualSDB.java +++ b/webapp/src/edu/cornell/mannlib/vitro/webapp/dao/jena/IndividualSDB.java @@ -395,8 +395,8 @@ public class IndividualSDB extends IndividualImpl implements Individual { String[] graphVars = { "?g" }; String queryStr = "CONSTRUCT { <"+ind.getURI()+"> <" + propertyURI + "> ?value } \n" + - "WHERE { GRAPH ?g { \n" + - "<" + ind.getURI() +"> <" + propertyURI + "> ?value } \n" + + "WHERE { \n" + + "<" + ind.getURI() +"> <" + propertyURI + "> ?value \n" + WebappDaoFactorySDB.getFilterBlock(graphVars, datasetMode) + "\n} "; Query query = QueryFactory.create(queryStr); @@ -456,13 +456,13 @@ public class IndividualSDB extends IndividualImpl implements Individual { if (this.mainImageUri != NOT_INITIALIZED) { return mainImageUri; } else { - for (ObjectPropertyStatement stmt : getObjectPropertyStatements()) { - if (stmt.getPropertyURI() - .equals(VitroVocabulary.IND_MAIN_IMAGE)) { - mainImageUri = stmt.getObjectURI(); - return mainImageUri; - } - } + List mainImgStmts = + getObjectPropertyStatements(VitroVocabulary.IND_MAIN_IMAGE); + if (mainImgStmts != null && mainImgStmts.size() > 0) { + // arbitrarily return the first value in the list + mainImageUri = mainImgStmts.get(0).getObjectURI(); + return mainImageUri; + } return null; } } @@ -502,14 +502,11 @@ public class IndividualSDB extends IndividualImpl implements Individual { if( _hasThumb != null ){ return _hasThumb; }else{ - String[] graphVars = { "?g" }; String ask = - "ASK { GRAPH ?g " + - " { <" + individualURI + "> ?mainImage . \n" + - " ?mainImage ?thumbImage . }\n" + - WebappDaoFactorySDB.getFilterBlock(graphVars, datasetMode) + - "}"; - DatasetWrapper w = getDatasetWrapper(); + "ASK { " + + " <" + individualURI + "> ?mainImage . \n" + + " ?mainImage ?thumbImage . }\n" ; + DatasetWrapper w = getDatasetWrapper(); Dataset dataset = w.getDataset(); dataset.getLock().enterCriticalSection(Lock.READ); try{ @@ -552,10 +549,8 @@ public class IndividualSDB extends IndividualImpl implements Individual { Dataset dataset = w.getDataset(); dataset.getLock().enterCriticalSection(Lock.READ); try { - String graphVars[] = { "?g" }; StringBuffer selectPrimaryLinkQueryBuff = new StringBuffer().append( - "SELECT ?url ?anchor \n" ).append( - "WHERE{ GRAPH ?g { \n " ).append( + "SELECT ?url ?anchor \n" ).append(" WHERE { \n").append( " <" + this.individualURI + "> ").append( "<" + VitroVocabulary.PRIMARY_LINK + "> " ).append( "?link . \n").append( @@ -563,9 +558,7 @@ public class IndividualSDB extends IndividualImpl implements Individual { ).append( " ?link <" + VitroVocabulary.LINK_ANCHOR + "> ?anchor . \n" ).append( - "} \n") - .append(WebappDaoFactorySDB.getFilterBlock(graphVars, datasetMode)) - .append("}"); + "} \n"); QueryExecution qexec = QueryExecutionFactory.create( QueryFactory.create(selectPrimaryLinkQueryBuff.toString()) , dataset); @@ -678,12 +671,9 @@ public class IndividualSDB extends IndividualImpl implements Individual { Dataset dataset = w.getDataset(); dataset.getLock().enterCriticalSection(Lock.READ); try { - String[] graphVars = { "?g" }; String valuesOfProperty = - "CONSTRUCT{<" + this.individualURI + "> <" + propertyURI + "> ?object}" + - "WHERE{ GRAPH ?g { <" + this.individualURI + "> <" + propertyURI + "> ?object} \n" + - WebappDaoFactorySDB.getFilterBlock(graphVars, datasetMode) + - "}"; + "CONSTRUCT{ <" + this.individualURI + "> <" + propertyURI + "> ?object }" + + "WHERE{ <" + this.individualURI + "> <" + propertyURI + "> ?object } \n"; tempModel = QueryExecutionFactory.create(QueryFactory.create(valuesOfProperty), dataset).execConstruct(); ontModel.add(tempModel.listStatements()); Resource ontRes = ontModel.getResource(this.individualURI); @@ -727,13 +717,10 @@ public class IndividualSDB extends IndividualImpl implements Individual { Dataset dataset = w.getDataset(); dataset.getLock().enterCriticalSection(Lock.READ); try { - String[] graphVars = { "?g" }; String valuesOfProperty = "SELECT ?object" + - "WHERE{ GRAPH ?g { <" + this.individualURI + "> <" + - propertyURI + "> ?object} \n" + - WebappDaoFactorySDB.getFilterBlock(graphVars, datasetMode) + - "}"; + "WHERE{ <" + this.individualURI + "> <" + + propertyURI + "> ?object } \n"; ResultSet values = QueryExecutionFactory.create( QueryFactory.create(valuesOfProperty), dataset) .execSelect(); @@ -767,13 +754,10 @@ public class IndividualSDB extends IndividualImpl implements Individual { Dataset dataset = w.getDataset(); dataset.getLock().enterCriticalSection(Lock.READ); try { - String[] graphVars = { "?g" }; String valueOfProperty = "SELECT ?object " + - "WHERE{ GRAPH ?g { <" + this.individualURI + "> <" + - propertyURI + "> ?object} \n" + - WebappDaoFactorySDB.getFilterBlock(graphVars, datasetMode) + - "}"; + "WHERE{ <" + this.individualURI + "> <" + + propertyURI + "> ?object } \n"; QueryExecution qe = QueryExecutionFactory.create( QueryFactory.create(valueOfProperty), dataset); try { diff --git a/webapp/src/edu/cornell/mannlib/vitro/webapp/dao/jena/UserAccountsDaoJena.java b/webapp/src/edu/cornell/mannlib/vitro/webapp/dao/jena/UserAccountsDaoJena.java index 53db0d845..3621cbf34 100644 --- a/webapp/src/edu/cornell/mannlib/vitro/webapp/dao/jena/UserAccountsDaoJena.java +++ b/webapp/src/edu/cornell/mannlib/vitro/webapp/dao/jena/UserAccountsDaoJena.java @@ -103,6 +103,7 @@ public class UserAccountsDaoJena extends JenaBaseDao implements UserAccountsDao USERACCOUNT_EXTERNAL_AUTH_ID)); u.setPermissionSetUris(getPropertyResourceURIValues(r, USERACCOUNT_HAS_PERMISSION_SET)); + u.setRootUser(isResourceOfType(r, USERACCOUNT_ROOT_USER)); return u; } finally { getOntModel().leaveCriticalSection(); @@ -157,21 +158,6 @@ public class UserAccountsDaoJena extends JenaBaseDao implements UserAccountsDao return getUserAccountByUri(userUri); } - @Override - public boolean isRootUser(UserAccount userAccount) { - if (userAccount == null) { - return false; - } - - getOntModel().enterCriticalSection(Lock.READ); - try { - OntResource r = getOntModel().getOntResource(userAccount.getUri()); - return isResourceOfType(r, USERACCOUNT_ROOT_USER); - } finally { - getOntModel().leaveCriticalSection(); - } - } - @Override public String insertUserAccount(UserAccount userAccount) { if (userAccount == null) { @@ -214,6 +200,10 @@ public class UserAccountsDaoJena extends JenaBaseDao implements UserAccountsDao USERACCOUNT_HAS_PERMISSION_SET, userAccount.getPermissionSetUris(), model); + if (userAccount.isRootUser()) { + model.add(res, RDF.type, USERACCOUNT_ROOT_USER); + } + userAccount.setUri(userUri); return userUri; } catch (InsertException e) { @@ -268,6 +258,13 @@ public class UserAccountsDaoJena extends JenaBaseDao implements UserAccountsDao updatePropertyResourceURIValues(res, USERACCOUNT_HAS_PERMISSION_SET, userAccount.getPermissionSetUris(), model); + + if (userAccount.isRootUser()) { + model.add(res, RDF.type, USERACCOUNT_ROOT_USER); + } else { + model.remove(res, RDF.type, USERACCOUNT_ROOT_USER); + } + } finally { model.leaveCriticalSection(); } @@ -367,7 +364,7 @@ public class UserAccountsDaoJena extends JenaBaseDao implements UserAccountsDao throw new InsertException("Could not create URI for individual: " + errMsg); } - + private boolean isUriUsed(String uri) { return (getOntModel().getOntResource(uri) != null); } @@ -385,7 +382,7 @@ public class UserAccountsDaoJena extends JenaBaseDao implements UserAccountsDao if (type == null) { return false; } - + StmtIterator stmts = getOntModel().listStatements(r, RDF.type, type); if (stmts.hasNext()) { stmts.close(); diff --git a/webapp/src/edu/cornell/mannlib/vitro/webapp/email/FreemarkerEmailMessage.java b/webapp/src/edu/cornell/mannlib/vitro/webapp/email/FreemarkerEmailMessage.java index 3bc9115cf..d980dd8d7 100644 --- a/webapp/src/edu/cornell/mannlib/vitro/webapp/email/FreemarkerEmailMessage.java +++ b/webapp/src/edu/cornell/mannlib/vitro/webapp/email/FreemarkerEmailMessage.java @@ -2,6 +2,8 @@ package edu.cornell.mannlib.vitro.webapp.email; +import java.io.IOException; +import java.io.StringWriter; import java.io.UnsupportedEncodingException; import java.util.ArrayList; import java.util.Collections; @@ -21,22 +23,30 @@ import javax.mail.internet.InternetAddress; import javax.mail.internet.MimeBodyPart; import javax.mail.internet.MimeMessage; import javax.mail.internet.MimeMultipart; -import javax.servlet.ServletContext; import javax.servlet.http.HttpServletRequest; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; -import edu.cornell.mannlib.vitro.webapp.controller.freemarker.TemplateProcessingHelper; -import edu.cornell.mannlib.vitro.webapp.controller.freemarker.TemplateProcessingHelper.TemplateProcessingException; +import edu.cornell.mannlib.vitro.webapp.controller.freemarker.FreemarkerHttpServlet; +import edu.cornell.mannlib.vitro.webapp.web.directives.EmailDirective; +import freemarker.core.Environment; import freemarker.template.Configuration; +import freemarker.template.Template; +import freemarker.template.TemplateException; /** * A framework that makes it simpler to send email messages with a body built * from a Freemarker template. * - * In fact, the body can be plain text from a template, HTML from a template, or - * both. + * The template must contain the @email directive, which may provide the subject + * line, the HTML content, and the plain text content. If these values are not + * provided by the directive, they default to empty strings, or to values that + * were set by the controller. + * + * The directive also calls the send() method here. + * + * @see EmailDirective */ public class FreemarkerEmailMessage { private static final Log log = LogFactory @@ -47,15 +57,15 @@ public class FreemarkerEmailMessage { private final HttpServletRequest req; private final Session session; private final Configuration config; - private final ServletContext ctx; private final List recipients = new ArrayList(); private final InternetAddress replyToAddress; private InternetAddress fromAddress = null; private String subject = ""; - private String htmlTemplateName; - private String textTemplateName; + private String templateName = ""; + private String htmlContent = ""; + private String textContent = ""; private Map bodyMap = Collections.emptyMap(); /** @@ -67,8 +77,6 @@ public class FreemarkerEmailMessage { this.session = session; this.replyToAddress = replyToAddress; - this.ctx = req.getSession().getServletContext(); - Object o = req.getAttribute(ATTRIBUTE_NAME); if (!(o instanceof Configuration)) { String oClass = (o == null) ? "null" : o.getClass().getName(); @@ -115,10 +123,6 @@ public class FreemarkerEmailMessage { try { recipients.add(new Recipient(type, emailAddress, personalName)); - } catch (AddressException e) { - log.warn("invalid recipient address: " + type + ", '" - + emailAddress + "', personal name '" + personalName + "'"); - return; } catch (UnsupportedEncodingException e) { log.warn("invalid recipient address: " + type + ", '" + emailAddress + "', personal name '" + personalName + "'"); @@ -130,27 +134,41 @@ public class FreemarkerEmailMessage { this.subject = nonNull(subject, ""); } - public void setHtmlTemplate(String templateName) { - this.htmlTemplateName = nonNull(templateName, ""); + public void setHtmlContent(String htmlContent) { + this.htmlContent = nonNull(htmlContent, ""); } - public void setTextTemplate(String templateName) { - this.textTemplateName = nonNull(templateName, ""); + public void setTextContent(String textContent) { + this.textContent = nonNull(textContent, ""); + } + + public void setTemplate(String templateName) { + this.templateName = nonNull(templateName, ""); } public void setBodyMap(Map body) { if (body == null) { this.bodyMap = Collections.emptyMap(); } else { - this.bodyMap = Collections - .unmodifiableMap(new HashMap(body)); + this.bodyMap = new HashMap(body); + } + } + + public void processTemplate() { + bodyMap.putAll(FreemarkerHttpServlet.getDirectivesForAllEnvironments()); + bodyMap.put("email", new EmailDirective(this)); + + try { + Template template = config.getTemplate(templateName); + template.process(bodyMap, new StringWriter()); + } catch (TemplateException e) { + log.error(e, e); + } catch (IOException e) { + log.error(e, e); } } public void send() { - String textBody = figureMessageBody(textTemplateName); - String htmlBody = figureMessageBody(htmlTemplateName); - try { MimeMessage msg = new MimeMessage(session); msg.setReplyTo(new Address[] { replyToAddress }); @@ -167,19 +185,19 @@ public class FreemarkerEmailMessage { msg.setSubject(subject); - if (textBody.isEmpty()) { - if (htmlBody.isEmpty()) { + if (textContent.isEmpty()) { + if (htmlContent.isEmpty()) { log.error("Message has neither text body nor HTML body"); } else { - msg.setContent(htmlBody, "text/html"); + msg.setContent(htmlContent, "text/html"); } } else { - if (htmlBody.isEmpty()) { - msg.setContent(textBody, "text/plain"); + if (htmlContent.isEmpty()) { + msg.setContent(textContent, "text/plain"); } else { MimeMultipart content = new MimeMultipart("alternative"); - addBodyPart(content, textBody, "text/plain"); - addBodyPart(content, htmlBody, "text/html"); + addBodyPart(content, textContent, "text/plain"); + addBodyPart(content, htmlContent, "text/html"); msg.setContent(content); } } @@ -193,26 +211,6 @@ public class FreemarkerEmailMessage { } } - /** - * Process the template. If there is no template name or if there is a - * problem with the process, return an empty string. - */ - private String figureMessageBody(String templateName) { - if (templateName.isEmpty()) { - return ""; - } - - try { - TemplateProcessingHelper helper = new TemplateProcessingHelper( - config, req, ctx); - return helper.processTemplate(templateName, bodyMap).toString(); - } catch (TemplateProcessingException e) { - log.warn("Exception while processing email template '" - + templateName + "'", e); - return ""; - } - } - private void addBodyPart(MimeMultipart content, String textBody, String type) throws MessagingException { MimeBodyPart bodyPart = new MimeBodyPart(); @@ -235,7 +233,7 @@ public class FreemarkerEmailMessage { } public Recipient(RecipientType type, String address, String personalName) - throws AddressException, UnsupportedEncodingException { + throws UnsupportedEncodingException { this.type = type; this.address = new InternetAddress(address, personalName); } diff --git a/webapp/src/edu/cornell/mannlib/vitro/webapp/web/directives/EmailDirective.java b/webapp/src/edu/cornell/mannlib/vitro/webapp/web/directives/EmailDirective.java new file mode 100644 index 000000000..58ca99ada --- /dev/null +++ b/webapp/src/edu/cornell/mannlib/vitro/webapp/web/directives/EmailDirective.java @@ -0,0 +1,98 @@ +/* $This file is distributed under the terms of the license in /doc/license.txt$ */ + +package edu.cornell.mannlib.vitro.webapp.web.directives; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + +import edu.cornell.mannlib.vitro.webapp.email.FreemarkerEmailMessage; +import freemarker.core.Environment; +import freemarker.template.SimpleScalar; +import freemarker.template.TemplateDirectiveBody; +import freemarker.template.TemplateException; +import freemarker.template.TemplateModel; +import freemarker.template.TemplateModelException; + +/** + * Process the inputs for a FreemarkerEmailMessage. + * + * @see FreemarkerEmailMessage + */ +public class EmailDirective extends BaseTemplateDirectiveModel { + + private static final Log log = LogFactory.getLog(EmailDirective.class); + + private final FreemarkerEmailMessage message; + + public EmailDirective(FreemarkerEmailMessage message) { + this.message = message; + } + + @Override + public void execute(Environment env, Map params, TemplateModel[] loopVars, + TemplateDirectiveBody body) throws TemplateException, IOException { + + String subject = getOptionalSimpleScalarParameter(params, "subject"); + if (subject != null) { + message.setSubject(subject); + } + + String htmlContent = getOptionalSimpleScalarParameter(params, "html"); + if (htmlContent != null) { + message.setHtmlContent(htmlContent); + } + + String textContent = getOptionalSimpleScalarParameter(params, "text"); + if (textContent != null) { + message.setTextContent(textContent); + } + + if ((htmlContent == null) && (textContent == null)) { + throw new TemplateModelException("The email directive must have " + + "either a 'html' parameter or a 'text' parameter."); + } + } + + private String getOptionalSimpleScalarParameter(Map params, + String name) throws TemplateModelException { + Object o = params.get(name); + if (o == null) { + return null; + } + + if (!(o instanceof SimpleScalar)) { + throw new TemplateModelException("The '" + name + "' parameter " + + "for the email directive must be a string value."); + } + + return o.toString(); + } + + @Override + public Map help(String name) { + Map map = new LinkedHashMap(); + + map.put("effect", + "Create an email message from the parameters set in the invoking template."); + + Map params = new HashMap(); + params.put("subject", "email subject (optional)"); + params.put("html", "HTML version of email message (optional)"); + params.put("text", "Plain text version of email message (optional)"); + map.put("parameters", params); + + List examples = new ArrayList(); + examples.add("<email subject=\"Password reset confirmation\" html=html text=text>"); + examples.add("<email html=html text=text>"); + map.put("examples", examples); + + return map; + } +} diff --git a/webapp/src/edu/cornell/mannlib/vitro/webapp/web/directives/UrlDirective.java b/webapp/src/edu/cornell/mannlib/vitro/webapp/web/directives/UrlDirective.java index 85a59a7cd..9edf3ff85 100644 --- a/webapp/src/edu/cornell/mannlib/vitro/webapp/web/directives/UrlDirective.java +++ b/webapp/src/edu/cornell/mannlib/vitro/webapp/web/directives/UrlDirective.java @@ -15,6 +15,7 @@ import org.apache.commons.logging.LogFactory; import edu.cornell.mannlib.vitro.webapp.controller.freemarker.UrlBuilder; import freemarker.core.Environment; +import freemarker.template.SimpleScalar; import freemarker.template.TemplateDirectiveBody; import freemarker.template.TemplateException; import freemarker.template.TemplateModel; @@ -44,12 +45,19 @@ public class UrlDirective extends BaseTemplateDirectiveModel { "The url directive doesn't allow nested content."); } - String path = params.get("path").toString(); - if (path == null) { + Object o = params.get("path"); + if (o == null) { throw new TemplateModelException( "The url directive requires a value for parameter 'path'."); } + if (! ( o instanceof SimpleScalar)) { + throw new TemplateModelException( + "The url directive requires a string value for parameter 'path'."); + } + + String path = o.toString(); + if (!path.startsWith("/")) { throw new TemplateModelException( "The url directive requires that the value of parameter 'path' is an absolute path starting with '/'."); @@ -60,6 +68,7 @@ public class UrlDirective extends BaseTemplateDirectiveModel { out.write(url); } + @Override public Map help(String name) { Map map = new LinkedHashMap(); @@ -77,6 +86,5 @@ public class UrlDirective extends BaseTemplateDirectiveModel { return map; } - } diff --git a/webapp/src/edu/cornell/mannlib/vitro/webapp/web/directives/WidgetDirective.java b/webapp/src/edu/cornell/mannlib/vitro/webapp/web/directives/WidgetDirective.java index 654f5cecc..dff8eae6c 100644 --- a/webapp/src/edu/cornell/mannlib/vitro/webapp/web/directives/WidgetDirective.java +++ b/webapp/src/edu/cornell/mannlib/vitro/webapp/web/directives/WidgetDirective.java @@ -94,6 +94,7 @@ public class WidgetDirective extends BaseTemplateDirectiveModel { } + @Override public Map help(String name) { Map map = new LinkedHashMap(); diff --git a/webapp/src/edu/cornell/mannlib/vitro/webapp/web/templatemodels/User.java b/webapp/src/edu/cornell/mannlib/vitro/webapp/web/templatemodels/User.java index e533847e5..5b9a217d7 100644 --- a/webapp/src/edu/cornell/mannlib/vitro/webapp/web/templatemodels/User.java +++ b/webapp/src/edu/cornell/mannlib/vitro/webapp/web/templatemodels/User.java @@ -32,13 +32,27 @@ public class User extends BaseTemplateModel { return ""; } - String firstName = currentUser.getFirstName(); - String lastName = currentUser.getLastName(); - if (firstName.isEmpty() && lastName.isEmpty()) { - return currentUser.getEmailAddress(); - } - - return firstName + " " + lastName; + if (currentUser.getFirstName().isEmpty()) { + return currentUser.getEmailAddress(); + } + + return currentUser.getFirstName(); + } + + public String getFirstName() { + if (currentUser == null) { + return ""; + } else { + return currentUser.getFirstName(); + } + } + + public String getLastName() { + if (currentUser == null) { + return ""; + } else { + return currentUser.getLastName(); + } } public boolean getHasSiteAdminAccess() { diff --git a/webapp/src/edu/cornell/mannlib/vitro/webapp/web/templatemodels/VClassTemplateModel.java b/webapp/src/edu/cornell/mannlib/vitro/webapp/web/templatemodels/VClassTemplateModel.java index 059d70d6a..676dbfe02 100644 --- a/webapp/src/edu/cornell/mannlib/vitro/webapp/web/templatemodels/VClassTemplateModel.java +++ b/webapp/src/edu/cornell/mannlib/vitro/webapp/web/templatemodels/VClassTemplateModel.java @@ -6,6 +6,7 @@ import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import edu.cornell.mannlib.vitro.webapp.beans.VClass; +import edu.cornell.mannlib.vitro.webapp.beans.VClassGroup; import edu.cornell.mannlib.vitro.webapp.controller.freemarker.UrlBuilder.ParamMap; import edu.cornell.mannlib.vitro.webapp.controller.freemarker.UrlBuilder.Route; @@ -36,7 +37,8 @@ public class VClassTemplateModel extends BaseTemplateModel { return vclass.getEntityCount(); } - public VClassGroupTemplateModel getGroup(){ - return new VClassGroupTemplateModel(vclass.getGroup()); + public VClassGroupTemplateModel getGroup() { + VClassGroup group = vclass.getGroup(); + return (group == null) ? null : new VClassGroupTemplateModel(vclass.getGroup()); } } diff --git a/webapp/src/edu/cornell/mannlib/vitro/webapp/web/templatemodels/individual/NameStatementTemplateModel.java b/webapp/src/edu/cornell/mannlib/vitro/webapp/web/templatemodels/individual/NameStatementTemplateModel.java index d00e5041f..f9d9480eb 100644 --- a/webapp/src/edu/cornell/mannlib/vitro/webapp/web/templatemodels/individual/NameStatementTemplateModel.java +++ b/webapp/src/edu/cornell/mannlib/vitro/webapp/web/templatemodels/individual/NameStatementTemplateModel.java @@ -2,26 +2,19 @@ package edu.cornell.mannlib.vitro.webapp.web.templatemodels.individual; -import java.util.List; - import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.openrdf.model.URI; import org.openrdf.model.impl.URIImpl; -import com.hp.hpl.jena.rdf.model.Literal; - import edu.cornell.mannlib.vitro.webapp.auth.requestedAction.ifaces.RequestedAction; -import edu.cornell.mannlib.vitro.webapp.auth.requestedAction.propstmt.DropDataPropStmt; import edu.cornell.mannlib.vitro.webapp.auth.requestedAction.propstmt.EditDataPropStmt; import edu.cornell.mannlib.vitro.webapp.beans.DataPropertyStatement; import edu.cornell.mannlib.vitro.webapp.beans.DataPropertyStatementImpl; import edu.cornell.mannlib.vitro.webapp.controller.VitroRequest; -import edu.cornell.mannlib.vitro.webapp.dao.DataPropertyStatementDao; import edu.cornell.mannlib.vitro.webapp.dao.IndividualDao; import edu.cornell.mannlib.vitro.webapp.dao.VitroVocabulary; import edu.cornell.mannlib.vitro.webapp.dao.WebappDaoFactory; -import edu.cornell.mannlib.vitro.webapp.dao.jena.WebappDaoFactoryJena; import edu.cornell.mannlib.vitro.webapp.edit.EditLiteral; import edu.cornell.mannlib.vitro.webapp.edit.n3editing.processEdit.RdfLiteralHash; diff --git a/webapp/test/edu/cornell/mannlib/vitro/webapp/controller/accounts/UserAccountsSelectorTest.java b/webapp/test/edu/cornell/mannlib/vitro/webapp/controller/accounts/UserAccountsSelectorTest.java index bba2ad4ba..4a08a9766 100644 --- a/webapp/test/edu/cornell/mannlib/vitro/webapp/controller/accounts/UserAccountsSelectorTest.java +++ b/webapp/test/edu/cornell/mannlib/vitro/webapp/controller/accounts/UserAccountsSelectorTest.java @@ -12,7 +12,6 @@ import java.util.Arrays; import java.util.Collections; import java.util.List; -import org.junit.Before; import org.junit.BeforeClass; import org.junit.Test; @@ -91,6 +90,27 @@ public class UserAccountsSelectorTest extends AbstractTestClass { Collections .singleton("http://vivo.mydomain.edu/individual/role2"), acct.getPermissionSetUris()); + assertEquals("rootUser", false, acct.isRootUser()); + } + + @Test + public void checkFieldsForRootUser() { + selectOnCriteria(1, 8, DEFAULT_ORDERING, "", ""); + assertSelectedUris(10, "user08"); + + UserAccount acct = selection.getUserAccounts().get(0); + assertEquals("uri", "http://vivo.mydomain.edu/individual/user08", + acct.getUri()); + assertEquals("email", "email@henry.edu", acct.getEmailAddress()); + assertEquals("firstName", "Mary", acct.getFirstName()); + assertEquals("lastName", "McInerney", acct.getLastName()); + assertEquals("password", "garbage", acct.getMd5Password()); + assertEquals("expires", 0L, acct.getPasswordLinkExpires()); + assertEquals("loginCount", 7, acct.getLoginCount()); + assertEquals("status", UserAccount.Status.ACTIVE, acct.getStatus()); + assertEqualSets("permissions", Collections. emptySet(), + acct.getPermissionSetUris()); + assertEquals("rootUser", true, acct.isRootUser()); } // ---------------------------------------------------------------------- diff --git a/webapp/test/edu/cornell/mannlib/vitro/webapp/controller/accounts/UserAccountsSelectorTest.n3 b/webapp/test/edu/cornell/mannlib/vitro/webapp/controller/accounts/UserAccountsSelectorTest.n3 index 85d5895c1..7e39d30fb 100644 --- a/webapp/test/edu/cornell/mannlib/vitro/webapp/controller/accounts/UserAccountsSelectorTest.n3 +++ b/webapp/test/edu/cornell/mannlib/vitro/webapp/controller/accounts/UserAccountsSelectorTest.n3 @@ -101,6 +101,7 @@ mydomain:user07 mydomain:user08 a auth:UserAccount ; + a auth:RootUserAccount ; auth:emailAddress "email@henry.edu" ; auth:firstName "Mary" ; auth:lastName "McInerney" ; diff --git a/webapp/test/edu/cornell/mannlib/vitro/webapp/controller/authenticate/AuthenticatorStub.java b/webapp/test/edu/cornell/mannlib/vitro/webapp/controller/authenticate/AuthenticatorStub.java index 6171387f9..3dd4fabd0 100644 --- a/webapp/test/edu/cornell/mannlib/vitro/webapp/controller/authenticate/AuthenticatorStub.java +++ b/webapp/test/edu/cornell/mannlib/vitro/webapp/controller/authenticate/AuthenticatorStub.java @@ -182,10 +182,4 @@ public class AuthenticatorStub extends Authenticator { "AuthenticatorStub.accountRequiresEditing() not implemented."); } - @Override - public void recordLoginWithoutUserAccount(String individualUri) { - throw new RuntimeException( - "AuthenticatorStub.recordLoginWithoutUserAccount() not implemented."); - } - } diff --git a/webapp/test/edu/cornell/mannlib/vitro/webapp/controller/edit/AuthenticateTest.java b/webapp/test/edu/cornell/mannlib/vitro/webapp/controller/edit/AuthenticateTest.java index 80042b241..7f64b7b14 100644 --- a/webapp/test/edu/cornell/mannlib/vitro/webapp/controller/edit/AuthenticateTest.java +++ b/webapp/test/edu/cornell/mannlib/vitro/webapp/controller/edit/AuthenticateTest.java @@ -18,10 +18,13 @@ import java.util.HashSet; import java.util.Map; import java.util.Set; +import org.apache.log4j.Level; import org.junit.Before; import org.junit.Ignore; import org.junit.Test; +import stubs.edu.cornell.mannlib.vitro.webapp.config.ConfigurationPropertiesStub; +import stubs.edu.cornell.mannlib.vitro.webapp.dao.IndividualDaoStub; import stubs.edu.cornell.mannlib.vitro.webapp.dao.UserAccountsDaoStub; import stubs.edu.cornell.mannlib.vitro.webapp.dao.WebappDaoFactoryStub; import stubs.javax.servlet.ServletConfigStub; @@ -32,7 +35,10 @@ import stubs.javax.servlet.http.HttpSessionStub; import edu.cornell.mannlib.vedit.beans.LoginStatusBean; import edu.cornell.mannlib.vedit.beans.LoginStatusBean.AuthenticationSource; import edu.cornell.mannlib.vitro.testing.AbstractTestClass; +import edu.cornell.mannlib.vitro.webapp.auth.identifier.ActiveIdentifierBundleFactories; +import edu.cornell.mannlib.vitro.webapp.auth.identifier.common.CommonIdentifierBundleFactory; import edu.cornell.mannlib.vitro.webapp.beans.UserAccount; +import edu.cornell.mannlib.vitro.webapp.config.ConfigurationProperties; import edu.cornell.mannlib.vitro.webapp.controller.authenticate.Authenticator; import edu.cornell.mannlib.vitro.webapp.controller.authenticate.AuthenticatorStub; import edu.cornell.mannlib.vitro.webapp.controller.login.LoginProcessBean; @@ -46,6 +52,7 @@ public class AuthenticateTest extends AbstractTestClass { private ServletContextStub servletContext; private WebappDaoFactoryStub webappDaoFactory; private UserAccountsDaoStub userAccountsDao; + private IndividualDaoStub individualDao; private ServletConfigStub servletConfig; private HttpSessionStub session; private HttpServletRequestStub request; @@ -122,8 +129,11 @@ public class AuthenticateTest extends AbstractTestClass { userAccountsDao.addUser(createUserFromUserInfo(OLD_SELF)); userAccountsDao.addUser(createUserFromUserInfo(OLD_STRANGER)); + individualDao = new IndividualDaoStub(); + webappDaoFactory = new WebappDaoFactoryStub(); webappDaoFactory.setUserAccountsDao(userAccountsDao); + webappDaoFactory.setIndividualDao(individualDao); servletContext = new ServletContextStub(); servletContext.setAttribute("webappDaoFactory", webappDaoFactory); @@ -143,6 +153,12 @@ public class AuthenticateTest extends AbstractTestClass { auth = new Authenticate(); auth.init(servletConfig); + + setLoggerLevel(ConfigurationProperties.class, Level.WARN); + new ConfigurationPropertiesStub().setBean(servletContext); + + ActiveIdentifierBundleFactories.addFactory(servletContext, + new CommonIdentifierBundleFactory(servletContext)); } private UserAccount createUserFromUserInfo(UserInfo userInfo) { diff --git a/webapp/test/stubs/edu/cornell/mannlib/vitro/webapp/dao/UserAccountsDaoStub.java b/webapp/test/stubs/edu/cornell/mannlib/vitro/webapp/dao/UserAccountsDaoStub.java index c0a96befe..42aa6a104 100644 --- a/webapp/test/stubs/edu/cornell/mannlib/vitro/webapp/dao/UserAccountsDaoStub.java +++ b/webapp/test/stubs/edu/cornell/mannlib/vitro/webapp/dao/UserAccountsDaoStub.java @@ -48,11 +48,6 @@ public class UserAccountsDaoStub implements UserAccountsDao { "UserAccountsDaoStub.getUserAccountByEmail() not implemented."); } - @Override - public boolean isRootUser(UserAccount userAccount) { - throw new RuntimeException("UserAccountsDao.isRootUser() not implemented."); - } - @Override public String insertUserAccount(UserAccount userAccount) { throw new RuntimeException( diff --git a/webapp/web/css/edit.css b/webapp/web/css/edit.css index ad2099f6f..7ae7dd1d4 100644 --- a/webapp/web/css/edit.css +++ b/webapp/web/css/edit.css @@ -359,4 +359,5 @@ div.sparqlform .parenthetical { .note { font-size: .8em; line-height: 1.3em; + color: #7f8993; } \ No newline at end of file diff --git a/webapp/web/css/menupage/menupage.css b/webapp/web/css/menupage/menupage.css index 38dd1fb4b..cf61e2bab 100644 --- a/webapp/web/css/menupage/menupage.css +++ b/webapp/web/css/menupage/menupage.css @@ -193,14 +193,14 @@ ul#browse-classes a { ul#alpha-browse-individuals { float: left; width: 619px; - padding-left: 10px; - list-style: none; - margin-left: 1px; + /* padding-left: 10px; */ + list-style: none; + /* margin-left: 1px; */ } ul#alpha-browse-individuals li { float: left; margin-right: 4px; - padding-top: 8px; + padding-top: 0px; } ul#alpha-browse-individuals li:last-child { border-bottom: 0; @@ -212,6 +212,11 @@ ul#alpha-browse-individuals a { padding-left: 8px; width: 10px; } +h4#selectedClassHeading { + margin-left:4px; + margin-top:-5px; + margin-bottom:-10px; +} /* BROWSE INDIVIDUALS------> */ #individuals-in-class { float: right; diff --git a/webapp/web/css/vitro.css b/webapp/web/css/vitro.css index 184c7f688..7ec39d5dd 100644 --- a/webapp/web/css/vitro.css +++ b/webapp/web/css/vitro.css @@ -74,3 +74,42 @@ height:620px; overflow:visible; } +/* <------ SEARCH RESULTS PAGE*/ +.searchTOC { + margin-bottom: 1.5em; + float:right; + margin-right:35px; + margin-left:45px; + width:182px; + text-align:center; +/* border: 1px solid #dde4e3;*/ + padding-top:4px; + color: #fff; + background: #5e6363; +} +.searchTOC h4 { + color: #fff; + padding: 0 0 0 0; +} +.searchTOC ul { + width: 160px; + border: 1px solid #dde4e3; + background: #f1f2ee; + padding: 0 10px 0px 10px; + margin-top: 4px; + text-align: left; +} +.searchTOC ul li { + display: block; + border-bottom: 1px solid #dde4e3; + font-size: 14px; + height: 35px; + line-height: 35px; +} +.searchTOC ul li:last-child { + border-bottom: none +} +.searchTOC ul a { + display: block; + padding-left: 15px; +} \ No newline at end of file diff --git a/webapp/web/edit/forms/autoCompleteDatapropForm.jsp b/webapp/web/edit/forms/autoCompleteDatapropForm.jsp index cc8d81917..cb1785fcc 100644 --- a/webapp/web/edit/forms/autoCompleteDatapropForm.jsp +++ b/webapp/web/edit/forms/autoCompleteDatapropForm.jsp @@ -119,10 +119,10 @@ Model model = (Model)application.getAttribute("jenaOntModel"); editConfig.prepareForDataPropUpdate(model,dps); formTitle = "Change text for: "+prop.getPublicName()+""; - submitLabel = "save change"; + submitLabel = "Save change"; } else { formTitle = "Add new entry for: "+prop.getPublicName()+""; - submitLabel = "save entry"; + submitLabel = "Save entry"; } %> diff --git a/webapp/web/edit/forms/autoCompleteObjPropForm.jsp b/webapp/web/edit/forms/autoCompleteObjPropForm.jsp index 80480c14f..772391ff2 100644 --- a/webapp/web/edit/forms/autoCompleteObjPropForm.jsp +++ b/webapp/web/edit/forms/autoCompleteObjPropForm.jsp @@ -111,10 +111,10 @@ if ( prop.getOfferCreateNewOption() ) { log.debug("property set to offer \"create new\" option; custom form: ["+prop.getCustomEntryForm()+"]"); formTitle = "Select an existing "+rangeClass.getName()+" for "+subject.getName(); - submitLabel = "select existing"; + submitLabel = "Select existing"; } else { formTitle = "Add an entry to: "+prop.getDomainPublic()+""; - submitLabel = "save entry"; + submitLabel = "Save entry"; } } %> @@ -174,7 +174,7 @@ $(document).ready(function() {

If you don't find the appropriate entry on the selection list, - +

diff --git a/webapp/web/edit/forms/defaultDatapropForm.jsp b/webapp/web/edit/forms/defaultDatapropForm.jsp index 127cc8aa9..27118bfdb 100644 --- a/webapp/web/edit/forms/defaultDatapropForm.jsp +++ b/webapp/web/edit/forms/defaultDatapropForm.jsp @@ -137,10 +137,10 @@ Model model = (Model)application.getAttribute("jenaOntModel"); editConfig.prepareForDataPropUpdate(model,dps); formTitle = "Change text for: "+prop.getPublicName()+""; - submitLabel = "save change"; + submitLabel = "Save change"; } else { formTitle ="Add new entry for: "+prop.getPublicName()+""; - submitLabel ="save entry"; + submitLabel ="Save entry"; } %> diff --git a/webapp/web/edit/forms/defaultObjPropForm.jsp b/webapp/web/edit/forms/defaultObjPropForm.jsp index 0307a3a39..956f402c6 100644 --- a/webapp/web/edit/forms/defaultObjPropForm.jsp +++ b/webapp/web/edit/forms/defaultObjPropForm.jsp @@ -128,10 +128,10 @@ log.debug("property set to offer \"create new\" option; custom form: ["+prop.getCustomEntryForm()+"]"); formTitle = "Select an existing "+classOfObjectFillers.getName()+" for "+subject.getName(); - submitLabel = "select existing"; + submitLabel = "Select existing"; } else { formTitle = "Add an entry to: "+prop.getDomainPublic()+""; - submitLabel = "save entry"; + submitLabel = "Save entry"; } } @@ -193,7 +193,7 @@ - + diff --git a/webapp/web/js/account/accountUtils.js b/webapp/web/js/account/accountUtils.js index 6f1aa0d69..786ea38c9 100644 --- a/webapp/web/js/account/accountUtils.js +++ b/webapp/web/js/account/accountUtils.js @@ -6,7 +6,6 @@ function changeAction(form, url) { return true; } - $(document).ready(function(){ //Accounts per page diff --git a/webapp/web/js/menupage/browseByVClass.js b/webapp/web/js/menupage/browseByVClass.js index 712c46e22..ef28d5f74 100644 --- a/webapp/web/js/menupage/browseByVClass.js +++ b/webapp/web/js/menupage/browseByVClass.js @@ -140,8 +140,9 @@ var browseByVClass = { browseByVClass.pagination(pages, page); } - selectedClassHeading = '

'+ results.vclass.name +'

'; - browseByVClass.individualsContainer.prepend(selectedClassHeading); + // selectedClassHeading = '

'+ results.vclass.name +'

'; NIHVIVO-2483 tlw72 + // browseByVClass.individualsContainer.prepend(selectedClassHeading); + $('h4#selectedClassHeading').text(results.vclass.name); // NIHVIVO-2483 tlw72 // Set selected class, alpha and page browseByVClass.selectedVClass(results.vclass.URI); diff --git a/webapp/web/templates/edit/specific/ents_edit.jsp b/webapp/web/templates/edit/specific/ents_edit.jsp index 32b09295d..1a9ff77c6 100644 --- a/webapp/web/templates/edit/specific/ents_edit.jsp +++ b/webapp/web/templates/edit/specific/ents_edit.jsp @@ -34,13 +34,13 @@
@@ -51,13 +51,13 @@ diff --git a/webapp/web/templates/freemarker/body/accounts/userAccounts-acctCreatedEmail-text.ftl b/webapp/web/templates/freemarker/body/accounts/userAccounts-acctCreatedEmail-text.ftl deleted file mode 100644 index 69b8d5f30..000000000 --- a/webapp/web/templates/freemarker/body/accounts/userAccounts-acctCreatedEmail-text.ftl +++ /dev/null @@ -1,20 +0,0 @@ -<#-- $This file is distributed under the terms of the license in /doc/license.txt$ --> - -<#-- Confirmation that an account has been created. --> - -${userAccount.firstName} ${userAccount.lastName} - -Congratulations! - -We have created your new VIVO account associated with -${userAccount.emailAddress}. - -If you did not request this new account you can safely ignore this email. -This request will expire if not acted upon for 30 days. - -Paste the link below into your browser's address bar to create your password -for your new account using our secure server. - -${passwordLink} - -Thanks! diff --git a/webapp/web/templates/freemarker/body/accounts/userAccounts-acctCreatedEmail-html.ftl b/webapp/web/templates/freemarker/body/accounts/userAccounts-acctCreatedEmail.ftl similarity index 60% rename from webapp/web/templates/freemarker/body/accounts/userAccounts-acctCreatedEmail-html.ftl rename to webapp/web/templates/freemarker/body/accounts/userAccounts-acctCreatedEmail.ftl index 5fa2c5f44..4aa355468 100644 --- a/webapp/web/templates/freemarker/body/accounts/userAccounts-acctCreatedEmail-html.ftl +++ b/webapp/web/templates/freemarker/body/accounts/userAccounts-acctCreatedEmail.ftl @@ -2,9 +2,12 @@ <#-- Confirmation that an account has been created. --> +<#assign subject = "Your VIVO account has been created." /> + +<#assign html> - ${subjectLine} + ${subject}

@@ -40,4 +43,26 @@ Thanks!

- \ No newline at end of file + + + +<#assign text> +${userAccount.firstName} ${userAccount.lastName} + +Congratulations! + +We have created your new VIVO account associated with +${userAccount.emailAddress}. + +If you did not request this new account you can safely ignore this email. +This request will expire if not acted upon for 30 days. + +Paste the link below into your browser's address bar to create your password +for your new account using our secure server. + +${passwordLink} + +Thanks! + + +<@email subject=subject html=html text=text /> \ No newline at end of file diff --git a/webapp/web/templates/freemarker/body/accounts/userAccounts-add.ftl b/webapp/web/templates/freemarker/body/accounts/userAccounts-add.ftl index 9ec0d64a5..5013f2bfa 100644 --- a/webapp/web/templates/freemarker/body/accounts/userAccounts-add.ftl +++ b/webapp/web/templates/freemarker/body/accounts/userAccounts-add.ftl @@ -2,7 +2,7 @@ <#-- Template for adding a user account --> -

Add new account

+

> Add new account

<#if errorEmailIsEmpty??> <#assign errorMessage = "You must supply an email address." /> @@ -16,6 +16,10 @@ <#assign errorMessage = "'${emailAddress}' is not a valid email address." /> + <#if errorExternalAuthIdInUse??> + <#assign errorMessage = "An account with that external authorization ID already exists." /> + + <#if errorFirstNameIsEmpty??> <#assign errorMessage = "You must supply a first name." /> @@ -61,9 +65,12 @@ + + +

Roles *

<#list roles as role> - selected /> + checked />
@@ -93,7 +100,7 @@

- or Cancel + or Cancel

* required fields

diff --git a/webapp/web/templates/freemarker/body/accounts/userAccounts-confirmEmailChangedEmail-text.ftl b/webapp/web/templates/freemarker/body/accounts/userAccounts-confirmEmailChangedEmail-text.ftl deleted file mode 100644 index 78f1457bf..000000000 --- a/webapp/web/templates/freemarker/body/accounts/userAccounts-confirmEmailChangedEmail-text.ftl +++ /dev/null @@ -1,10 +0,0 @@ -<#-- $This file is distributed under the terms of the license in /doc/license.txt$ --> - -<#-- Confirmation that the user has changed his email account. --> - -Hi, ${userAccount.firstName} ${userAccount.lastName} - -You recently changed the email address associated with -${userAccount.firstName} ${userAccount.lastName} - -Thank you. diff --git a/webapp/web/templates/freemarker/body/accounts/userAccounts-confirmEmailChangedEmail-html.ftl b/webapp/web/templates/freemarker/body/accounts/userAccounts-confirmEmailChangedEmail.ftl similarity index 55% rename from webapp/web/templates/freemarker/body/accounts/userAccounts-confirmEmailChangedEmail-html.ftl rename to webapp/web/templates/freemarker/body/accounts/userAccounts-confirmEmailChangedEmail.ftl index e06f7e1ae..9acec6fcf 100644 --- a/webapp/web/templates/freemarker/body/accounts/userAccounts-confirmEmailChangedEmail-html.ftl +++ b/webapp/web/templates/freemarker/body/accounts/userAccounts-confirmEmailChangedEmail.ftl @@ -2,9 +2,12 @@ <#-- Confirmation that the user has changed his email account. --> +<#assign subject = "Your VIVO email account has been changed." /> + +<#assign html> - ${subjectLine} + ${subject}

@@ -20,4 +23,16 @@ Thank you.

- \ No newline at end of file + + + +<#assign text> +Hi, ${userAccount.firstName} ${userAccount.lastName} + +You recently changed the email address associated with +${userAccount.firstName} ${userAccount.lastName} + +Thank you. + + +<@email subject=subject html=html text=text /> \ No newline at end of file diff --git a/webapp/web/templates/freemarker/body/accounts/userAccounts-createPassword.ftl b/webapp/web/templates/freemarker/body/accounts/userAccounts-createPassword.ftl index 2b75cff0c..9c1f93f5c 100644 --- a/webapp/web/templates/freemarker/body/accounts/userAccounts-createPassword.ftl +++ b/webapp/web/templates/freemarker/body/accounts/userAccounts-createPassword.ftl @@ -3,7 +3,6 @@ <#-- Template for adding a user account -->

Create your Password

- <#if errorPasswordIsEmpty??> <#assign errorMessage = "No password supplied." /> @@ -24,26 +23,24 @@
-
- Please enter your new password for ${userAccount.emailAddress} +

Please enter your new password for ${userAccount.emailAddress}

-
- - + + + + + + - - - -

Minimum of ${minimumLength} characters in length.

+

Minimum of ${minimumLength} characters in length.

- - + + - +

-

* required fields

-
-
+

* required fields

+
${stylesheets.add('')} \ No newline at end of file diff --git a/webapp/web/templates/freemarker/body/accounts/userAccounts-edit.ftl b/webapp/web/templates/freemarker/body/accounts/userAccounts-edit.ftl index b6d818f65..e6e2e0374 100644 --- a/webapp/web/templates/freemarker/body/accounts/userAccounts-edit.ftl +++ b/webapp/web/templates/freemarker/body/accounts/userAccounts-edit.ftl @@ -2,7 +2,7 @@ <#-- Template for editing a user account --> -

Edit account

+

> Edit account

<#if errorEmailIsEmpty??> <#assign errorMessage = "You must supply an email address." /> @@ -16,6 +16,10 @@ <#assign errorMessage = "'${emailAddress}' is not a valid email address." /> + <#if errorExternalAuthIdInUse??> + <#assign errorMessage = "An account with that external authorization ID already exists." /> + + <#if errorFirstNameIsEmpty??> <#assign errorMessage = "You must supply a first name." /> @@ -53,30 +57,35 @@
- + - + - + -

Roles *

- <#list roles as role> - selected /> - -
- + + + + <#if roles?has_content> +

Roles *

+ <#list roles as role> + checked /> + +
+ + <#if !emailIsEnabled??> - +

Minimum of ${minimumLength} characters in length.

Leaving this blank means that the password will not be changed.

- +

Associate a profile with this account

@@ -87,18 +96,18 @@
- + checked /> <#if emailIsEnabled??>

- Note: A confirmation email with instructions for resetting a password - will be sent to the address entered above. - The password will not be reset until the user follows the link provided in this email. + Note: Instructions for resetting the password will + be emailed to the address entered above. The password will not + be reset until the user follows the link provided in this email.

- or Cancel + or Cancel

* required fields

diff --git a/webapp/web/templates/freemarker/body/accounts/userAccounts-firstTimeExternal.ftl b/webapp/web/templates/freemarker/body/accounts/userAccounts-firstTimeExternal.ftl index 1b6b78791..fed4e4486 100644 --- a/webapp/web/templates/freemarker/body/accounts/userAccounts-firstTimeExternal.ftl +++ b/webapp/web/templates/freemarker/body/accounts/userAccounts-firstTimeExternal.ftl @@ -38,16 +38,17 @@

- + + - + - + - + <#if emailIsEnabled??>

@@ -56,7 +57,7 @@

- or Cancel + or Cancel
diff --git a/webapp/web/templates/freemarker/body/accounts/userAccounts-firstTimeExternalEmail-text.ftl b/webapp/web/templates/freemarker/body/accounts/userAccounts-firstTimeExternalEmail-text.ftl deleted file mode 100644 index 8b788d56d..000000000 --- a/webapp/web/templates/freemarker/body/accounts/userAccounts-firstTimeExternalEmail-text.ftl +++ /dev/null @@ -1,12 +0,0 @@ -<#-- $This file is distributed under the terms of the license in /doc/license.txt$ --> - -<#-- Confirmation that an account has been created for an externally-authenticated user. --> - -${userAccount.firstName} ${userAccount.lastName} - -Congratulations! - -We have created your new VIVO account associated with -${userAccount.emailAddress}. - -Thanks! diff --git a/webapp/web/templates/freemarker/body/accounts/userAccounts-firstTimeExternalEmail-html.ftl b/webapp/web/templates/freemarker/body/accounts/userAccounts-firstTimeExternalEmail.ftl similarity index 58% rename from webapp/web/templates/freemarker/body/accounts/userAccounts-firstTimeExternalEmail-html.ftl rename to webapp/web/templates/freemarker/body/accounts/userAccounts-firstTimeExternalEmail.ftl index 5faa53dff..434c65851 100644 --- a/webapp/web/templates/freemarker/body/accounts/userAccounts-firstTimeExternalEmail-html.ftl +++ b/webapp/web/templates/freemarker/body/accounts/userAccounts-firstTimeExternalEmail.ftl @@ -2,9 +2,12 @@ <#-- Confirmation that an account has been created for an externally-authenticated user. --> +<#assign subject = "Your VIVO account has been created." /> + +<#assign html> - ${subjectLine} + ${subject}

@@ -23,4 +26,18 @@ Thanks!

- \ No newline at end of file + + + +<#assign text> +${userAccount.firstName} ${userAccount.lastName} + +Congratulations! + +We have created your new VIVO account associated with +${userAccount.emailAddress}. + +Thanks! + + +<@email subject=subject html=html text=text /> \ No newline at end of file diff --git a/webapp/web/templates/freemarker/body/accounts/userAccounts-myAccount.ftl b/webapp/web/templates/freemarker/body/accounts/userAccounts-myAccount.ftl index e00f13c41..3067e43f6 100644 --- a/webapp/web/templates/freemarker/body/accounts/userAccounts-myAccount.ftl +++ b/webapp/web/templates/freemarker/body/accounts/userAccounts-myAccount.ftl @@ -2,7 +2,7 @@ <#-- Template for editing a user account --> -

Edit account

+

My account

<#if errorEmailIsEmpty??> <#assign errorMessage = "You must supply an email address." /> @@ -43,7 +43,6 @@ - <#if confirmChange??> <#assign confirmMessage = "Your changes have been saved." /> @@ -65,28 +64,27 @@
- + -

Note: if email changes, a confirmation email will be sent to the new email address entered above.

- +

Note: if email changes, a confirmation email will be sent to the new email address entered above.

+ - + - + <#if !externalAuth??> - - + + -

Minimum of ${minimumLength} characters in length.

-

Leaving this blank means that the password will not be changed.

+

Minimum of ${minimumLength} characters in length. Leaving this blank means that the password will not be changed.

- - + + - +

* required fields

diff --git a/webapp/web/templates/freemarker/body/accounts/userAccounts-passwordCreatedEmail-text.ftl b/webapp/web/templates/freemarker/body/accounts/userAccounts-passwordCreatedEmail-text.ftl deleted file mode 100644 index 2a339d4a9..000000000 --- a/webapp/web/templates/freemarker/body/accounts/userAccounts-passwordCreatedEmail-text.ftl +++ /dev/null @@ -1,12 +0,0 @@ -<#-- $This file is distributed under the terms of the license in /doc/license.txt$ --> - -<#-- Confirmation that a password has been created. --> - -${userAccount.firstName} ${userAccount.lastName} - -Password successfully created. - -Your new password associated with ${userAccount.emailAddress} -has been created. - -Thank you. diff --git a/webapp/web/templates/freemarker/body/accounts/userAccounts-passwordCreatedEmail-html.ftl b/webapp/web/templates/freemarker/body/accounts/userAccounts-passwordCreatedEmail.ftl similarity index 57% rename from webapp/web/templates/freemarker/body/accounts/userAccounts-passwordCreatedEmail-html.ftl rename to webapp/web/templates/freemarker/body/accounts/userAccounts-passwordCreatedEmail.ftl index 1773766e9..a99c51e08 100644 --- a/webapp/web/templates/freemarker/body/accounts/userAccounts-passwordCreatedEmail-html.ftl +++ b/webapp/web/templates/freemarker/body/accounts/userAccounts-passwordCreatedEmail.ftl @@ -2,9 +2,12 @@ <#-- Confirmation that an password has been created. --> +<#assign subject = "Password successfully created." /> + +<#assign html> - ${subjectLine} + ${subject}

@@ -23,4 +26,18 @@ Thank you.

- \ No newline at end of file + + + +<#assign text> +${userAccount.firstName} ${userAccount.lastName} + +Password successfully created. + +Your new password associated with ${userAccount.emailAddress} +has been created. + +Thank you. + + +<@email subject=subject html=html text=text /> \ No newline at end of file diff --git a/webapp/web/templates/freemarker/body/accounts/userAccounts-passwordResetEmail-text.ftl b/webapp/web/templates/freemarker/body/accounts/userAccounts-passwordResetEmail-text.ftl deleted file mode 100644 index cfe0cd00f..000000000 --- a/webapp/web/templates/freemarker/body/accounts/userAccounts-passwordResetEmail-text.ftl +++ /dev/null @@ -1,12 +0,0 @@ -<#-- $This file is distributed under the terms of the license in /doc/license.txt$ --> - -<#-- Confirmation that a password has been reset. --> - -${userAccount.firstName} ${userAccount.lastName} - -Password successfully changed. - -Your new password associated with ${userAccount.emailAddress} -has been changed. - -Thank you. diff --git a/webapp/web/templates/freemarker/body/accounts/userAccounts-passwordResetEmail-html.ftl b/webapp/web/templates/freemarker/body/accounts/userAccounts-passwordResetEmail.ftl similarity index 52% rename from webapp/web/templates/freemarker/body/accounts/userAccounts-passwordResetEmail-html.ftl rename to webapp/web/templates/freemarker/body/accounts/userAccounts-passwordResetEmail.ftl index 5212fbebb..6650ccd8e 100644 --- a/webapp/web/templates/freemarker/body/accounts/userAccounts-passwordResetEmail-html.ftl +++ b/webapp/web/templates/freemarker/body/accounts/userAccounts-passwordResetEmail.ftl @@ -1,10 +1,13 @@ <#-- $This file is distributed under the terms of the license in /doc/license.txt$ --> -<#-- Confirmation that an password has been reset. --> +<#-- Confirmation that a password has been reset. --> +<#assign subject = "Password changed." /> + +<#assign html> - ${subjectLine} + ${subject}

@@ -23,4 +26,18 @@ Thank you.

- \ No newline at end of file + + + +<#assign text> +${userAccount.firstName} ${userAccount.lastName} + +Password successfully changed. + +Your new password associated with ${userAccount.emailAddress} +has been changed. + +Thank you. + + +<@email subject=subject html=html text=text /> \ No newline at end of file diff --git a/webapp/web/templates/freemarker/body/accounts/userAccounts-resetPassword.ftl b/webapp/web/templates/freemarker/body/accounts/userAccounts-resetPassword.ftl index d0f8e75a5..7208e325b 100644 --- a/webapp/web/templates/freemarker/body/accounts/userAccounts-resetPassword.ftl +++ b/webapp/web/templates/freemarker/body/accounts/userAccounts-resetPassword.ftl @@ -4,6 +4,8 @@

Reset your Password

+

Please enter your new password for ${userAccount.emailAddress}

+ <#if errorPasswordIsEmpty??> <#assign errorMessage = "No password supplied." /> @@ -24,26 +26,22 @@
-
- Please enter your new password for ${userAccount.emailAddress} - -
- - + + + - - + + -

Minimum of ${minimumLength} characters in length.

+

Minimum of ${minimumLength} characters in length.

- - + + - +

-

* required fields

-
-
+

* required fields

+
${stylesheets.add('')} \ No newline at end of file diff --git a/webapp/web/templates/freemarker/body/accounts/userAccounts-resetPasswordEmail-html.ftl b/webapp/web/templates/freemarker/body/accounts/userAccounts-resetPasswordEmail-html.ftl deleted file mode 100644 index 0fa37fba3..000000000 --- a/webapp/web/templates/freemarker/body/accounts/userAccounts-resetPasswordEmail-html.ftl +++ /dev/null @@ -1,40 +0,0 @@ -<#-- $This file is distributed under the terms of the license in /doc/license.txt$ --> - -<#-- Notification that your password has been reset. --> - - - - ${subjectLine} - - -

- ${userAccount.firstName} ${userAccount.lastName} -

- -

- We received a request to reset the password for your account (${userAccount.emailAddress}). - Please follow the instructions below to proceed with your password reset. -

- -

- If you did not request this new account you can safely ignore this email. - This request will expire if not acted upon for 30 days. -

- -

- Click the link below to reset your password using our secure server. -

- -

- ${passwordLink} -

- -

- If the link above doesn't work, you can copy and paste the link directly into your browser's address bar. -

- -

- Thank you! -

- - \ No newline at end of file diff --git a/webapp/web/templates/freemarker/body/accounts/userAccounts-resetPasswordEmail-text.ftl b/webapp/web/templates/freemarker/body/accounts/userAccounts-resetPasswordEmail-text.ftl deleted file mode 100644 index 82624c327..000000000 --- a/webapp/web/templates/freemarker/body/accounts/userAccounts-resetPasswordEmail-text.ftl +++ /dev/null @@ -1,19 +0,0 @@ -<#-- $This file is distributed under the terms of the license in /doc/license.txt$ --> - -<#-- Notification that your password has been reset. --> - -${userAccount.firstName} ${userAccount.lastName} - -We received a request to reset the password for your account -(${userAccount.emailAddress}). -Please follow the instructions below to proceed with your password reset. - -If you did not request this new account you can safely ignore this email. -This request will expire if not acted upon for 30 days. - -Paste the link below into your browser's address bar to reset your password -using our secure server. - -${passwordLink} - -Thank you! diff --git a/webapp/web/templates/freemarker/body/accounts/userAccounts-resetPasswordEmail.ftl b/webapp/web/templates/freemarker/body/accounts/userAccounts-resetPasswordEmail.ftl new file mode 100644 index 000000000..d6fcc59a2 --- /dev/null +++ b/webapp/web/templates/freemarker/body/accounts/userAccounts-resetPasswordEmail.ftl @@ -0,0 +1,61 @@ +<#-- $This file is distributed under the terms of the license in /doc/license.txt$ --> + +<#-- Confirmation email for user account password reset --> + +<#assign subject = "${siteName} reset password request" /> + +<#assign html> + + + ${subject} + + +

+ Dear ${userAccount.firstName} ${userAccount.lastName}: +

+ +

+ We have received a request to reset the password for your ${siteName} account (${userAccount.emailAddress}). +

+ +

+ Please follow the instructions below to proceed with your password reset. +

+ +

+ If you did not request this new account you can safely ignore this email. + This request will expire if not acted upon within 30 days. +

+ +

+ Click on the link below or paste it into your browser's address bar to reset your password + using our secure server. +

+ +

${passwordLink}

+ +

Thank you!

+ + + + +<#assign text> +Dear ${userAccount.firstName} ${userAccount.lastName}: + +We have received a request to reset the password for your ${siteName} account +(${userAccount.emailAddress}). + +Please follow the instructions below to proceed with your password reset. + +If you did not request this new account you can safely ignore this email. +This request will expire if not acted upon within 30 days. + +Paste the link below into your browser's address bar to reset your password +using our secure server. + +${passwordLink} + +Thank you! + + +<@email subject=subject html=html text=text /> \ No newline at end of file diff --git a/webapp/web/templates/freemarker/body/partials/menupage/menupage-browse.ftl b/webapp/web/templates/freemarker/body/partials/menupage/menupage-browse.ftl index 212361a99..afaf93e09 100644 --- a/webapp/web/templates/freemarker/body/partials/menupage/menupage-browse.ftl +++ b/webapp/web/templates/freemarker/body/partials/menupage/menupage-browse.ftl @@ -20,7 +20,8 @@ -