This commit is contained in:
anupsawant 2011-06-17 16:30:47 +00:00
commit e5f73e181c
82 changed files with 1842 additions and 1072 deletions

View file

@ -1020,18 +1020,6 @@
<url-pattern>/dwr/*</url-pattern> <url-pattern>/dwr/*</url-pattern>
</servlet-mapping> </servlet-mapping>
<!-- Deprecated servlet (still used in Datastar) - retain temporarily for testing and comparison. -->
<servlet>
<servlet-name>sendmail</servlet-name>
<servlet-class>edu.cornell.mannlib.vitro.webapp.controller.ContactMailServlet</servlet-class>
<load-on-startup>5</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>sendmail</servlet-name>
<url-pattern>/sendmail</url-pattern>
</servlet-mapping>
<!-- end deprecated servlet -->
<servlet> <servlet>
<servlet-name>sendMail</servlet-name> <servlet-name>sendMail</servlet-name>
<servlet-class>edu.cornell.mannlib.vitro.webapp.controller.freemarker.ContactMailController</servlet-class> <servlet-class>edu.cornell.mannlib.vitro.webapp.controller.freemarker.ContactMailController</servlet-class>

View file

@ -46,4 +46,11 @@ public class RequestIdentifiers {
return (IdentifierBundle) obj; return (IdentifierBundle) obj;
} }
/**
* The login status has changed, so discard the cached Identifiers.
*/
public static void resetIdentifiers(ServletRequest request) {
request.removeAttribute(ATTRIBUTE_ID_BUNDLE);
}
} }

View file

@ -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.SelfEditingConfiguration;
import edu.cornell.mannlib.vitro.webapp.beans.UserAccount; import edu.cornell.mannlib.vitro.webapp.beans.UserAccount;
import edu.cornell.mannlib.vitro.webapp.dao.IndividualDao; import edu.cornell.mannlib.vitro.webapp.dao.IndividualDao;
import edu.cornell.mannlib.vitro.webapp.dao.UserAccountsDao;
import edu.cornell.mannlib.vitro.webapp.dao.WebappDaoFactory; import edu.cornell.mannlib.vitro.webapp.dao.WebappDaoFactory;
/** /**
@ -73,7 +72,7 @@ public class CommonIdentifierBundleFactory implements IdentifierBundleFactory {
private Collection<? extends Identifier> createRootUserIdentifiers( private Collection<? extends Identifier> createRootUserIdentifiers(
HttpServletRequest req) { HttpServletRequest req) {
UserAccount user = LoginStatusBean.getCurrentUser(req); UserAccount user = LoginStatusBean.getCurrentUser(req);
if (isRootUser(user)) { if ((user != null) && user.isRootUser()) {
return Collections.singleton(new IsRootUser()); return Collections.singleton(new IsRootUser());
} else { } else {
return Collections.emptySet(); return Collections.emptySet();
@ -143,25 +142,6 @@ public class CommonIdentifierBundleFactory implements IdentifierBundleFactory {
return individuals; 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 @Override
public String toString() { public String toString() {
return this.getClass().getSimpleName() + " - " + hashCode(); return this.getClass().getSimpleName() + " - " + hashCode();

View file

@ -9,11 +9,6 @@ import javax.servlet.ServletContextListener;
import org.apache.commons.logging.Log; import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory; 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.IdentifierBundle;
import edu.cornell.mannlib.vitro.webapp.auth.identifier.common.IsRootUser; import edu.cornell.mannlib.vitro.webapp.auth.identifier.common.IsRootUser;
import edu.cornell.mannlib.vitro.webapp.auth.policy.ifaces.Authorization; 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.config.ConfigurationProperties;
import edu.cornell.mannlib.vitro.webapp.controller.authenticate.Authenticator; import edu.cornell.mannlib.vitro.webapp.controller.authenticate.Authenticator;
import edu.cornell.mannlib.vitro.webapp.dao.UserAccountsDao; 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.WebappDaoFactory;
import edu.cornell.mannlib.vitro.webapp.dao.jena.ModelContext;
import edu.cornell.mannlib.vitro.webapp.servlet.setup.AbortStartup; import edu.cornell.mannlib.vitro.webapp.servlet.setup.AbortStartup;
/** /**
@ -73,10 +66,11 @@ public class RootUserPolicy implements PolicyIface {
try { try {
UserAccountsDao uaDao = getUserAccountsDao(ctx); UserAccountsDao uaDao = getUserAccountsDao(ctx);
OntModel userAccountsModel = getUserAccountsModel(ctx);
checkForWrongRootUser(ctx, uaDao);
if (!rootUserExists(uaDao)) { if (!rootUserExists(uaDao)) {
createRootUser(ctx, uaDao, userAccountsModel); createRootUser(ctx, uaDao);
} }
ServletPolicyList.addPolicy(ctx, new RootUserPolicy()); ServletPolicyList.addPolicy(ctx, new RootUserPolicy());
@ -98,26 +92,41 @@ public class RootUserPolicy implements PolicyIface {
return wadf.getUserAccountsDao(); return wadf.getUserAccountsDao();
} }
private OntModel getUserAccountsModel(ServletContext ctx) { private void checkForWrongRootUser(ServletContext ctx,
return ModelContext.getBaseOntModelSelector(ctx) UserAccountsDao uaDao) {
.getUserAccountsModel(); 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) { private boolean rootUserExists(UserAccountsDao uaDao) {
return (getRootUser(uaDao) != null);
}
private UserAccount getRootUser(UserAccountsDao uaDao) {
for (UserAccount ua : uaDao.getAllUserAccounts()) { for (UserAccount ua : uaDao.getAllUserAccounts()) {
if (uaDao.isRootUser(ua)) { if (ua.isRootUser()) {
return true; return ua;
} }
} }
return false; return null;
} }
/** /**
* TODO The first and last name should be left blank, so the user will * 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. * be forced to edit them. However, that's not in place yet.
*/ */
private void createRootUser(ServletContext ctx, UserAccountsDao uaDao, private void createRootUser(ServletContext ctx, UserAccountsDao uaDao) {
OntModel userAccountsModel) {
String emailAddress = ConfigurationProperties.getBean(ctx) String emailAddress = ConfigurationProperties.getBean(ctx)
.getProperty(PROPERTY_ROOT_USER_EMAIL); .getProperty(PROPERTY_ROOT_USER_EMAIL);
if (emailAddress == null) { if (emailAddress == null) {
@ -147,19 +156,10 @@ public class RootUserPolicy implements PolicyIface {
.applyMd5Encoding(ROOT_USER_INITIAL_PASSWORD)); .applyMd5Encoding(ROOT_USER_INITIAL_PASSWORD));
ua.setPasswordChangeRequired(true); ua.setPasswordChangeRequired(true);
ua.setStatus(Status.ACTIVE); ua.setStatus(Status.ACTIVE);
ua.setRootUser(true);
uaDao.insertUserAccount(ua); 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 + "'"); log.info("Created root user as '" + emailAddress + "'");
} }

View file

@ -60,6 +60,8 @@ public class UserAccount {
/** This may be empty, but should never be null. */ /** This may be empty, but should never be null. */
private Set<String> permissionSetUris = Collections.emptySet(); private Set<String> permissionSetUris = Collections.emptySet();
private boolean rootUser = false;
public String getUri() { public String getUri() {
return uri; return uri;
} }
@ -172,6 +174,14 @@ public class UserAccount {
this.permissionSetUris = new HashSet<String>(permissionSetUris); this.permissionSetUris = new HashSet<String>(permissionSetUris);
} }
public boolean isRootUser() {
return rootUser;
}
public void setRootUser(boolean rootUser) {
this.rootUser = rootUser;
}
private <T> T nonNull(T value, T defaultValue) { private <T> T nonNull(T value, T defaultValue) {
return (value == null) ? defaultValue : value; return (value == null) ? defaultValue : value;
} }
@ -196,6 +206,7 @@ public class UserAccount {
+ (", passwordChangeRequired=" + passwordChangeRequired) + (", passwordChangeRequired=" + passwordChangeRequired)
+ (", loginCount=" + loginCount) + (", status=" + status) + (", loginCount=" + loginCount) + (", status=" + status)
+ (", externalAuthId=" + externalAuthId) + (", externalAuthId=" + externalAuthId)
+ (", rootUser=" + rootUser)
+ (", permissionSetUris=" + permissionSetUris) + "]"; + (", permissionSetUris=" + permissionSetUris) + "]";
} }
} }

View file

@ -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("<html>" + lineSeparator );
msgBuf.append("<head>" + lineSeparator );
msgBuf.append("<style>a {text-decoration: none}</style>" + lineSeparator );
msgBuf.append("<title>" + deliveryfrom + "</title>" + lineSeparator );
msgBuf.append("</head>" + lineSeparator );
msgBuf.append("<body>" + lineSeparator );
msgBuf.append("<h4>" + deliveryfrom + "</h4>" + lineSeparator );
msgBuf.append("<h4>From: "+webusername +" (" + webuseremail + ")" +
" at IP address " + ipAddr + "</h4>"+lineSeparator);
if (!(originalReferer == null || originalReferer.equals("none"))){
//The spam filter that is being used by the listsrv is rejecting <a href="...
//so try with out the markup, if that sill doesn't work,
//uncomment the following line to strip the http://
//msgBuf.append("<p><i>likely viewing page " + stripProtocol(originalReferer) );
msgBuf.append("<p><i>likely viewing page " + originalReferer );
}
msgBuf.append(lineSeparator + "</i></p><h3>Comments:</h3>" + lineSeparator );
if (comments==null || comments.equals("")) {
msgBuf.append("<p>BLANK MESSAGE</p>");
} else {
msgBuf.append("<p>"+comments+"</p>");
}
msgBuf.append("</body>" + lineSeparator );
msgBuf.append("</html>" + lineSeparator );
return msgBuf.toString();
}
private void writeBackupCopy(PrintWriter outFile, String msgText,
String spamReason) {
Calendar cal = Calendar.getInstance();
outFile.println("<hr/>");
outFile.println();
outFile.println("<p>"+cal.getTime()+"</p>");
outFile.println();
if (spamReason != null) {
outFile.println("<p>REJECTED - SPAM</p>");
outFile.println("<p>"+spamReason+"</p>");
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<recipientCount; i++){
address[i] = new InternetAddress(deliverToArray[i]);
}
msg.setRecipients( Message.RecipientType.TO, address );
}
// Set the subject and text
msg.setSubject( deliveryfrom );
// add the multipart to the message
msg.setContent(msgText,"text/html");
// set the Date: header
msg.setSentDate( new Date() );
Transport.send( msg ); // try to send the message via smtp - catch error exceptions
}
private String validateInput(String webusername, String webuseremail,
String comments) {
if( webusername == null || "".equals(webusername.trim()) ){
return "A proper webusername field was not found in the form submitted.";
}
if( webuseremail == null || "".equals(webuseremail.trim()) ){
return "A proper webuser email field was not found in the form submitted.";
}
if (comments==null || "".equals(comments.trim())) {
return "The proper comments field was not found in the form submitted.";
}
return null;
}
/**
* @param request
* @return null if message not judged to be spam, otherwise a String
* containing the reason the message was flagged as spam.
*/
private String checkForSpam(String comments) {
/* if this blog markup is found, treat comment as blog spam */
if (
(comments.indexOf("[/url]") > -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;
}
}

View file

@ -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<String> propURIs = new HashSet<String>();
HashMap<VClass, List<DataProperty>> classPropertiesMap =
populateClassPropertiesMap(vcDao, dao, topUri, propURIs);
// Construct json String
JSONObject completeJson = new JSONObject();
JSONArray propertiesJsonArr = new JSONArray();
if (classPropertiesMap.size() > 0) {
for (Iterator<VClass> 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<DataProperty> vcProps = (ArrayList<DataProperty>)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<VClass> lvl2Classes = new ArrayList<VClass>();
List roots = null;
String requestType = vreq.getParameter("type");
if (requestType != null) {
roots = new LinkedList<VClass>();
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<VClass> lvl2ChildClasses = new ArrayList<VClass>();
addChildren(vcDao, vreq.getFullWebappDaoFactory(), root, lvl2ChildClasses, 0, ontologyUri);
lvl2Classes.addAll(lvl2ChildClasses);
}
}
}
}
for (VClass lvl2Class: lvl2Classes) {
HashMap<VClass, List<DataProperty>> lvl2ClassPropertiesMap =
populateClassPropertiesMap(vcDao, dao, lvl2Class.getURI(), propURIs);
if (lvl2ClassPropertiesMap.size() > 0) {
for (Iterator<VClass> iter = lvl2ClassPropertiesMap.keySet().iterator(); iter.hasNext();) { // add results to schema
VClass vc = (VClass) iter.next();
ArrayList<DataProperty> vcProps = (ArrayList<DataProperty>)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<VClass, List<DataProperty>> populateClassPropertiesMap (
VClassDao vcDao,
DataPropertyDao dao,
String uri,
HashSet<String> propURIs) {
HashMap<VClass, List<DataProperty>> classPropertiesMap = new HashMap<VClass, List<DataProperty>>();
List<DataProperty> props = new ArrayList<DataProperty>();
VClass topVc = vcDao.getVClassByURI(uri);
Collection <DataProperty> dataProps = dao.getDataPropertiesForVClass(uri);
Iterator<DataProperty> 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<DataProperty> existingList = (ArrayList<DataProperty>)classPropertiesMap.get(vc);
existingList.add(prop);
} else {
ArrayList<DataProperty> newList = new ArrayList<DataProperty>();
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<DataProperty> existingList = (ArrayList<DataProperty>)classPropertiesMap.get(topVc);
existingList.add(prop);
} else {
ArrayList<DataProperty> newList = new ArrayList<DataProperty>();
newList.add(prop);
classPropertiesMap.put(topVc, newList);
}
}
}
}
}
return classPropertiesMap;
}
private void addChildren(VClassDao vcDao, WebappDaoFactory wadf, VClass parent, List<VClass> list, int position, String ontologyUri) {
List<VClass> 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<MAXDEPTH) {
List childClasses = new ArrayList();
Iterator childURIstrIt = childURIstrs.iterator();
while (childURIstrIt.hasNext()) {
String URIstr = (String) childURIstrIt.next();
try {
VClass child = (VClass) vcDao.getVClassByURI(URIstr);
if (!child.getURI().equals(OWL.Nothing.getURI())) {
childClasses.add(child);
}
} catch (Exception e) {}
}
Collections.sort(childClasses);
Iterator childClassIt = childClasses.iterator();
while (childClassIt.hasNext()) {
VClass child = (VClass) childClassIt.next();
addChildren(vcDao, wadf, child, list, position + childShift, ontologyUri);
}
}
}
private List<VClass> addVClassDataToResultsList(WebappDaoFactory wadf, VClass vcw, int position, String ontologyUri) {
List<VClass> results = new ArrayList<VClass>();
if (ontologyUri == null || ( (vcw.getNamespace()!=null) && (vcw.getNamespace().equals(ontologyUri)) ) ) {
results.add(vcw);
/*
for (int i=0; i<position; i++) {
results.add("@@entities");
}
if (position==0)
results.add("XX"); // column 1
Integer numCols = (NUM_COLS-1)-position;
try {
numCols = addColToResults(((vcw.getLocalNameWithPrefix() == null) ? "" : "<a href=\"vclassEdit?uri="+URLEncoder.encode(vcw.getURI(),"UTF-8")+"\">"+vcw.getLocalNameWithPrefix()+"</a>"), 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<String, JSONObject> searchWithTypeMap = new HashMap<String, JSONObject>();
HashMap<String, JSONObject> searchNoTypeMap = new HashMap<String, JSONObject>();
ArrayList<String> queries = new ArrayList<String>();
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<String> iter = json.keys(); iter.hasNext();) {
ArrayList<JSONObject> jsonList = new ArrayList<JSONObject>();
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<splitList.length; i++) {
idNameArray[i] = splitList[i].split(",");
}
// process and add to json defaultTypes
JSONArray defaultTypesJsonArr = new JSONArray();
for (int i = 0; i<idNameArray.length; i++) {
JSONObject defaultTypesJson = new JSONObject();
defaultTypesJson.put("id", idNameArray[i][0].trim());
defaultTypesJson.put("name", idNameArray[i][1].trim());
defaultTypesJsonArr.put(defaultTypesJson);
}
json.put("defaultTypes", defaultTypesJsonArr);
}
} catch (JSONException ex) {
throw new ServletException(
"JSONReconcileServlet: Could not create metadata: " + ex);
}
return json;
}
private JSONObject runSearch(HashMap<String, JSONObject> currMap,
VitroRequest vreq) throws ServletException {
JSONObject qJson = new JSONObject();
try {
Analyzer analyzer = getAnalyzer(getServletContext());
IndexSearcher searcherForRequest = LuceneIndexFactory
.getIndexSearcher(getServletContext());
for (Map.Entry<String, JSONObject> 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<String[]> propertiesList = new ArrayList<String[]>();
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<String[]> 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<String[]> 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;
}
}

View file

@ -15,6 +15,7 @@ import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse; import javax.servlet.http.HttpServletResponse;
import org.apache.commons.lang.StringUtils;
import org.apache.commons.logging.Log; import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory; import org.apache.commons.logging.LogFactory;
import org.apache.lucene.analysis.Analyzer; import org.apache.lucene.analysis.Analyzer;
@ -53,6 +54,7 @@ import edu.cornell.mannlib.vitro.webapp.search.lucene.LuceneSetup;
*/ */
public class JSONReconcileServlet extends VitroHttpServlet { public class JSONReconcileServlet extends VitroHttpServlet {
private static final long serialVersionUID = 1L;
private static String QUERY_PARAMETER_NAME = "term"; private static String QUERY_PARAMETER_NAME = "term";
public static final int MAX_QUERY_LENGTH = 500; public static final int MAX_QUERY_LENGTH = 500;
private static final Log log = LogFactory.getLog(JSONReconcileServlet.class.getName()); private static final Log log = LogFactory.getLog(JSONReconcileServlet.class.getName());
@ -70,14 +72,14 @@ public class JSONReconcileServlet extends VitroHttpServlet {
super.doGet(req, resp); super.doGet(req, resp);
resp.setContentType("application/json"); resp.setContentType("application/json");
VitroRequest vreq = new VitroRequest(req); VitroRequest vreq = new VitroRequest(req);
System.out.println("vreq"); //log.debug("vreq");
System.out.println(vreq.getWebappDaoFactory()); //log.debug(vreq.getWebappDaoFactory());
try { try {
if (vreq.getParameter("query") != null if (vreq.getParameter("query") != null
|| vreq.getParameter("queries") != null) { || vreq.getParameter("queries") != null) {
JSONObject qJson = getResult(vreq, req, resp); JSONObject qJson = getResult(vreq, req, resp);
System.out.println("result: " + qJson.toString()); log.debug("result: " + qJson.toString());
String responseStr = (vreq.getParameter("callback") == null) ? qJson String responseStr = (vreq.getParameter("callback") == null) ? qJson
.toString() : vreq.getParameter("callback") + "(" .toString() : vreq.getParameter("callback") + "("
+ qJson.toString() + ")"; + 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"}} // "q2":{"query":"Dina","type":"http://xmlns.com/foaf/0.1/Person","type_strict":"should"}}
String qStr = (String) qObj; String qStr = (String) qObj;
queries.add(qStr); queries.add(qStr);
System.out.println(); log.debug("\nquery: " + qStr + "\n");
System.out.println("query: " + qStr + "\n");
} }
try { try {
@ -158,7 +159,7 @@ public class JSONReconcileServlet extends VitroHttpServlet {
} }
} }
} catch (JSONException ex) { } catch (JSONException ex) {
System.err.println("JSONReconcileServlet JSONException: " + ex); log.error("JSONException: " + ex);
throw new ServletException("JSONReconcileServlet JSONException: " throw new ServletException("JSONReconcileServlet JSONException: "
+ ex); + ex);
} }
@ -327,15 +328,15 @@ public class JSONReconcileServlet extends VitroHttpServlet {
} }
} catch (JSONException ex) { } catch (JSONException ex) {
System.err.println("JSONReconcileServlet JSONException: " + ex); log.error("JSONException: " + ex);
throw new ServletException("JSONReconcileServlet JSONException: " throw new ServletException("JSONReconcileServlet JSONException: "
+ ex); + ex);
} catch (SearchException ex) { } catch (SearchException ex) {
System.err.println("JSONReconcileServlet SearchException: " + ex); log.error("SearchException: " + ex);
throw new ServletException("JSONReconcileServlet SearchException: " throw new ServletException("JSONReconcileServlet SearchException: "
+ ex); + ex);
} catch (IOException ex) { } catch (IOException ex) {
System.err.println("JSONReconcileServlet IOException: " + ex); log.error("IOException: " + ex);
throw new ServletException("JSONReconcileServlet IOException: " throw new ServletException("JSONReconcileServlet IOException: "
+ ex); + ex);
} }
@ -354,30 +355,12 @@ public class JSONReconcileServlet extends VitroHttpServlet {
private Query makeReconcileNameQuery(String querystr, Analyzer analyzer, HttpServletRequest request) { 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); return makeTokenizedNameQuery(querystr, analyzer, request);
} }
private Query makeTokenizedNameQuery(String querystr, Analyzer analyzer, HttpServletRequest request) { private Query makeTokenizedNameQuery(String querystr, Analyzer analyzer, HttpServletRequest request) {
String stemParam = (String) request.getParameter("stem"); String termName = VitroLuceneTermNames.NAME_STEMMED;
boolean stem = "true".equals(stemParam);
String termName = stem ? VitroLuceneTermNames.NAME_STEMMED : VitroLuceneTermNames.NAME_UNSTEMMED;
BooleanQuery boolQuery = new BooleanQuery(); BooleanQuery boolQuery = new BooleanQuery();
@ -408,23 +391,9 @@ public class JSONReconcileServlet extends VitroHttpServlet {
log.warn(e, e); log.warn(e, e);
} }
return boolQuery; 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){ private QueryParser getQueryParser(String searchField, Analyzer analyzer){
// searchField indicates which field to search against when there is no term // searchField indicates which field to search against when there is no term
// indicated in the query string. // indicated in the query string.
@ -451,10 +420,8 @@ public class JSONReconcileServlet extends VitroHttpServlet {
return null; return null;
} }
query = makeReconcileNameQuery(querystr, analyzer, request); query = makeReconcileNameQuery(querystr, analyzer, request);
// filter by type // filter by type
if (typeParam != null) { if (typeParam != null) {
BooleanQuery boolQuery = new BooleanQuery(); BooleanQuery boolQuery = new BooleanQuery();
@ -471,7 +438,7 @@ public class JSONReconcileServlet extends VitroHttpServlet {
while (it.hasNext()) { while (it.hasNext()) {
String[] pvPair = it.next(); String[] pvPair = it.next();
Query extraQuery = makeReconcileNameQuery(pvPair[1], analyzer, request); Query extraQuery = makeReconcileNameQuery(pvPair[1], analyzer, request);
if (!"".equals(pvPair[0]) && pvPair[0] != null) { if ( ! StringUtils.isEmpty(pvPair[0]) ) {
BooleanQuery boolQuery = new BooleanQuery(); BooleanQuery boolQuery = new BooleanQuery();
boolQuery.add(new TermQuery(new Term( boolQuery.add(new TermQuery(new Term(
VitroLuceneTermNames.RDFTYPE, pvPair[0])), VitroLuceneTermNames.RDFTYPE, pvPair[0])),
@ -489,3 +456,4 @@ public class JSONReconcileServlet extends VitroHttpServlet {
} }
} }

View file

@ -39,7 +39,7 @@ public class UserAccountsSelector {
+ "PREFIX fn: <http://www.w3.org/2005/xpath-functions#> \n" + "PREFIX fn: <http://www.w3.org/2005/xpath-functions#> \n"
+ "PREFIX auth: <http://vitro.mannlib.cornell.edu/ns/vitro/authorization#> \n"; + "PREFIX auth: <http://vitro.mannlib.cornell.edu/ns/vitro/authorization#> \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"; private static final String COUNT_VARIABLE = "?uri";
@ -169,7 +169,8 @@ public class UserAccountsSelector {
+ " OPTIONAL { ?uri auth:md5password ?pwd } \n" + " OPTIONAL { ?uri auth:md5password ?pwd } \n"
+ " OPTIONAL { ?uri auth:passwordChangeExpires ?expire } \n" + " OPTIONAL { ?uri auth:passwordChangeExpires ?expire } \n"
+ " OPTIONAL { ?uri auth:loginCount ?count } \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() { private String filterClauses() {
@ -203,9 +204,8 @@ public class UserAccountsSelector {
/** /**
* Escape any regex special characters in the string. * Escape any regex special characters in the string.
* *
* Note that the SPARQL * Note that the SPARQL parser requires two backslashes, in order to pass a
* parser requires two backslashes, in order to pass a single backslash to * single backslash to the REGEX function.
* the REGEX function.
*/ */
private String escapeForRegex(String raw) { private String escapeForRegex(String raw) {
StringBuilder clean = new StringBuilder(); StringBuilder clean = new StringBuilder();
@ -327,6 +327,7 @@ public class UserAccountsSelector {
user.setPasswordLinkExpires(ifLongPresent(solution, "expire", 0L)); user.setPasswordLinkExpires(ifLongPresent(solution, "expire", 0L));
user.setLoginCount(ifIntPresent(solution, "count", 0)); user.setLoginCount(ifIntPresent(solution, "count", 0));
user.setStatus(parseStatus(solution, "status", null)); user.setStatus(parseStatus(solution, "status", null));
user.setRootUser(solution.contains("isRoot"));
return user; return user;
} }

View file

@ -22,6 +22,7 @@ import edu.cornell.mannlib.vitro.webapp.controller.freemarker.responsevalues.Tem
public class UserAccountsAddPage extends UserAccountsPage { public class UserAccountsAddPage extends UserAccountsPage {
private static final String PARAMETER_SUBMIT = "submitAdd"; private static final String PARAMETER_SUBMIT = "submitAdd";
private static final String PARAMETER_EMAIL_ADDRESS = "emailAddress"; 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_FIRST_NAME = "firstName";
private static final String PARAMETER_LAST_NAME = "lastName"; private static final String PARAMETER_LAST_NAME = "lastName";
private static final String PARAMETER_ROLE = "role"; 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_NO_EMAIL = "errorEmailIsEmpty";
private static final String ERROR_EMAIL_IN_USE = "errorEmailInUse"; private static final String ERROR_EMAIL_IN_USE = "errorEmailInUse";
private static final String ERROR_EMAIL_INVALID_FORMAT = "errorEmailInvalidFormat"; 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_FIRST_NAME = "errorFirstNameIsEmpty";
private static final String ERROR_NO_LAST_NAME = "errorLastNameIsEmpty"; private static final String ERROR_NO_LAST_NAME = "errorLastNameIsEmpty";
private static final String ERROR_NO_ROLE = "errorNoRoleSelected"; private static final String ERROR_NO_ROLE = "errorNoRoleSelected";
@ -41,6 +43,7 @@ public class UserAccountsAddPage extends UserAccountsPage {
/* The request parameters */ /* The request parameters */
private boolean submit; private boolean submit;
private String emailAddress = ""; private String emailAddress = "";
private String externalAuthId = "";
private String firstName = ""; private String firstName = "";
private String lastName = ""; private String lastName = "";
private String selectedRoleUri = ""; private String selectedRoleUri = "";
@ -68,6 +71,7 @@ public class UserAccountsAddPage extends UserAccountsPage {
private void parseRequestParameters() { private void parseRequestParameters() {
submit = isFlagOnRequest(PARAMETER_SUBMIT); submit = isFlagOnRequest(PARAMETER_SUBMIT);
emailAddress = getStringParameter(PARAMETER_EMAIL_ADDRESS, ""); emailAddress = getStringParameter(PARAMETER_EMAIL_ADDRESS, "");
externalAuthId = getStringParameter(PARAMETER_EXTERNAL_AUTH_ID, "");
firstName = getStringParameter(PARAMETER_FIRST_NAME, ""); firstName = getStringParameter(PARAMETER_FIRST_NAME, "");
lastName = getStringParameter(PARAMETER_LAST_NAME, ""); lastName = getStringParameter(PARAMETER_LAST_NAME, "");
selectedRoleUri = getStringParameter(PARAMETER_ROLE, ""); selectedRoleUri = getStringParameter(PARAMETER_ROLE, "");
@ -88,6 +92,8 @@ public class UserAccountsAddPage extends UserAccountsPage {
errorCode = ERROR_EMAIL_IN_USE; errorCode = ERROR_EMAIL_IN_USE;
} else if (!isEmailValidFormat()) { } else if (!isEmailValidFormat()) {
errorCode = ERROR_EMAIL_INVALID_FORMAT; errorCode = ERROR_EMAIL_INVALID_FORMAT;
} else if (isExternalAuthIdInUse()) {
errorCode = ERROR_EXTERNAL_AUTH_ID_IN_USE;
} else if (firstName.isEmpty()) { } else if (firstName.isEmpty()) {
errorCode = ERROR_NO_FIRST_NAME; errorCode = ERROR_NO_FIRST_NAME;
} else if (lastName.isEmpty()) { } else if (lastName.isEmpty()) {
@ -103,6 +109,13 @@ public class UserAccountsAddPage extends UserAccountsPage {
return userAccountsDao.getUserAccountByEmail(emailAddress) != null; return userAccountsDao.getUserAccountByEmail(emailAddress) != null;
} }
private boolean isExternalAuthIdInUse() {
if (externalAuthId.isEmpty()) {
return false;
}
return userAccountsDao.getUserAccountByExternalAuthId(externalAuthId) != null;
}
private boolean isEmailValidFormat() { private boolean isEmailValidFormat() {
return Authenticator.isValidEmailAddress(emailAddress); return Authenticator.isValidEmailAddress(emailAddress);
} }
@ -116,7 +129,7 @@ public class UserAccountsAddPage extends UserAccountsPage {
u.setEmailAddress(emailAddress); u.setEmailAddress(emailAddress);
u.setFirstName(firstName); u.setFirstName(firstName);
u.setLastName(lastName); u.setLastName(lastName);
u.setExternalAuthId(""); u.setExternalAuthId(externalAuthId);
u.setMd5Password(""); u.setMd5Password("");
u.setOldPassword(""); u.setOldPassword("");
@ -139,6 +152,7 @@ public class UserAccountsAddPage extends UserAccountsPage {
Map<String, Object> body = new HashMap<String, Object>(); Map<String, Object> body = new HashMap<String, Object>();
body.put("emailAddress", emailAddress); body.put("emailAddress", emailAddress);
body.put("externalAuthId", externalAuthId);
body.put("firstName", firstName); body.put("firstName", firstName);
body.put("lastName", lastName); body.put("lastName", lastName);
body.put("selectedRole", selectedRoleUri); body.put("selectedRole", selectedRoleUri);

View file

@ -57,6 +57,7 @@ public abstract class UserAccountsAddPageStrategy extends UserAccountsPage {
private static class EmailStrategy extends UserAccountsAddPageStrategy { private static class EmailStrategy extends UserAccountsAddPageStrategy {
public static final String CREATE_PASSWORD_URL = "/accounts/createPassword"; public static final String CREATE_PASSWORD_URL = "/accounts/createPassword";
private static final String EMAIL_TEMPLATE = "userAccounts-acctCreatedEmail.ftl";
private boolean sentEmail; private boolean sentEmail;
@ -91,15 +92,14 @@ public abstract class UserAccountsAddPageStrategy extends UserAccountsPage {
Map<String, Object> body = new HashMap<String, Object>(); Map<String, Object> body = new HashMap<String, Object>();
body.put("userAccount", page.getAddedAccount()); body.put("userAccount", page.getAddedAccount());
body.put("passwordLink", buildCreatePasswordLink()); body.put("passwordLink", buildCreatePasswordLink());
body.put("subjectLine", "Your VIVO account has been created.");
FreemarkerEmailMessage email = FreemarkerEmailFactory FreemarkerEmailMessage email = FreemarkerEmailFactory
.createNewMessage(vreq); .createNewMessage(vreq);
email.addRecipient(TO, page.getAddedAccount().getEmailAddress()); email.addRecipient(TO, page.getAddedAccount().getEmailAddress());
email.setSubject("Your VIVO account has been created."); email.setSubject("Your VIVO account has been created.");
email.setHtmlTemplate("userAccounts-acctCreatedEmail-html.ftl"); email.setTemplate(EMAIL_TEMPLATE);
email.setTextTemplate("userAccounts-acctCreatedEmail-text.ftl");
email.setBodyMap(body); email.setBodyMap(body);
email.processTemplate();
email.send(); email.send();
sentEmail = true; sentEmail = true;

View file

@ -57,9 +57,9 @@ public class UserAccountsAdminController extends FreemarkerHttpServlet {
if (page.isSubmit() && page.isValid()) { if (page.isSubmit() && page.isValid()) {
page.createNewAccount(); page.createNewAccount();
UserAccountsListPage listPage = new UserAccountsListPage(vreq); UserAccountsListPage.Message.showNewAccount(vreq,
return listPage.showPageWithNewAccount(page.getAddedAccount(), page.getAddedAccount(), page.wasPasswordEmailSent());
page.wasPasswordEmailSent()); return redirectToList();
} else { } else {
return page.showPage(); return page.showPage();
} }
@ -71,9 +71,10 @@ public class UserAccountsAdminController extends FreemarkerHttpServlet {
return showHomePage(vreq, page.getBogusMessage()); return showHomePage(vreq, page.getBogusMessage());
} else if (page.isSubmit() && page.isValid()) { } else if (page.isSubmit() && page.isValid()) {
page.updateAccount(); page.updateAccount();
UserAccountsListPage listPage = new UserAccountsListPage(vreq);
return listPage.showPageWithUpdatedAccount( UserAccountsListPage.Message.showUpdatedAccount(vreq,
page.getUpdatedAccount(), page.wasPasswordEmailSent()); page.getUpdatedAccount(), page.wasPasswordEmailSent());
return redirectToList();
} else { } else {
return page.showPage(); return page.showPage();
} }
@ -83,8 +84,8 @@ public class UserAccountsAdminController extends FreemarkerHttpServlet {
UserAccountsDeleter deleter = new UserAccountsDeleter(vreq); UserAccountsDeleter deleter = new UserAccountsDeleter(vreq);
Collection<String> deletedUris = deleter.delete(); Collection<String> deletedUris = deleter.delete();
return new UserAccountsListPage(vreq) UserAccountsListPage.Message.showDeletions(vreq, deletedUris);
.showPageWithDeletions(deletedUris); return redirectToList();
} }
private ResponseValues handleListRequest(VitroRequest vreq) { private ResponseValues handleListRequest(VitroRequest vreq) {
@ -92,6 +93,14 @@ public class UserAccountsAdminController extends FreemarkerHttpServlet {
return page.showPage(); 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) { private ResponseValues showHomePage(VitroRequest vreq, String message) {
DisplayMessage.setMessage(vreq, message); DisplayMessage.setMessage(vreq, message);
return new RedirectResponseValues("/"); return new RedirectResponseValues("/");

View file

@ -28,6 +28,7 @@ public class UserAccountsEditPage extends UserAccountsPage {
private static final String PARAMETER_SUBMIT = "submitEdit"; private static final String PARAMETER_SUBMIT = "submitEdit";
private static final String PARAMETER_USER_URI = "editAccount"; private static final String PARAMETER_USER_URI = "editAccount";
private static final String PARAMETER_EMAIL_ADDRESS = "emailAddress"; 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_FIRST_NAME = "firstName";
private static final String PARAMETER_LAST_NAME = "lastName"; private static final String PARAMETER_LAST_NAME = "lastName";
private static final String PARAMETER_ROLE = "role"; 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_NO_EMAIL = "errorEmailIsEmpty";
private static final String ERROR_EMAIL_IN_USE = "errorEmailInUse"; private static final String ERROR_EMAIL_IN_USE = "errorEmailInUse";
private static final String ERROR_EMAIL_INVALID_FORMAT = "errorEmailInvalidFormat"; 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_FIRST_NAME = "errorFirstNameIsEmpty";
private static final String ERROR_NO_LAST_NAME = "errorLastNameIsEmpty"; private static final String ERROR_NO_LAST_NAME = "errorLastNameIsEmpty";
private static final String ERROR_NO_ROLE = "errorNoRoleSelected"; private static final String ERROR_NO_ROLE = "errorNoRoleSelected";
@ -48,6 +50,7 @@ public class UserAccountsEditPage extends UserAccountsPage {
private boolean submit; private boolean submit;
private String userUri = ""; private String userUri = "";
private String emailAddress = ""; private String emailAddress = "";
private String externalAuthId = "";
private String firstName = ""; private String firstName = "";
private String lastName = ""; private String lastName = "";
private String selectedRoleUri = ""; private String selectedRoleUri = "";
@ -79,6 +82,7 @@ public class UserAccountsEditPage extends UserAccountsPage {
submit = isFlagOnRequest(PARAMETER_SUBMIT); submit = isFlagOnRequest(PARAMETER_SUBMIT);
userUri = getStringParameter(PARAMETER_USER_URI, ""); userUri = getStringParameter(PARAMETER_USER_URI, "");
emailAddress = getStringParameter(PARAMETER_EMAIL_ADDRESS, ""); emailAddress = getStringParameter(PARAMETER_EMAIL_ADDRESS, "");
externalAuthId = getStringParameter(PARAMETER_EXTERNAL_AUTH_ID, "");
firstName = getStringParameter(PARAMETER_FIRST_NAME, ""); firstName = getStringParameter(PARAMETER_FIRST_NAME, "");
lastName = getStringParameter(PARAMETER_LAST_NAME, ""); lastName = getStringParameter(PARAMETER_LAST_NAME, "");
selectedRoleUri = getStringParameter(PARAMETER_ROLE, ""); selectedRoleUri = getStringParameter(PARAMETER_ROLE, "");
@ -117,11 +121,13 @@ public class UserAccountsEditPage extends UserAccountsPage {
errorCode = ERROR_EMAIL_IN_USE; errorCode = ERROR_EMAIL_IN_USE;
} else if (!isEmailValidFormat()) { } else if (!isEmailValidFormat()) {
errorCode = ERROR_EMAIL_INVALID_FORMAT; errorCode = ERROR_EMAIL_INVALID_FORMAT;
} else if (externalAuthIdIsChanged() && isExternalAuthIdInUse()) {
errorCode = ERROR_EXTERNAL_AUTH_ID_IN_USE;
} else if (firstName.isEmpty()) { } else if (firstName.isEmpty()) {
errorCode = ERROR_NO_FIRST_NAME; errorCode = ERROR_NO_FIRST_NAME;
} else if (lastName.isEmpty()) { } else if (lastName.isEmpty()) {
errorCode = ERROR_NO_LAST_NAME; errorCode = ERROR_NO_LAST_NAME;
} else if (selectedRoleUri.isEmpty()) { } else if (!isRootUser() && selectedRoleUri.isEmpty()) {
errorCode = ERROR_NO_ROLE; errorCode = ERROR_NO_ROLE;
} else { } else {
errorCode = strategy.additionalValidations(); errorCode = strategy.additionalValidations();
@ -140,6 +146,21 @@ public class UserAccountsEditPage extends UserAccountsPage {
return Authenticator.isValidEmailAddress(emailAddress); 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() { public boolean isValid() {
return errorCode.isEmpty(); return errorCode.isEmpty();
} }
@ -149,16 +170,22 @@ public class UserAccountsEditPage extends UserAccountsPage {
if (isSubmit()) { if (isSubmit()) {
body.put("emailAddress", emailAddress); body.put("emailAddress", emailAddress);
body.put("externalAuthId", externalAuthId);
body.put("firstName", firstName); body.put("firstName", firstName);
body.put("lastName", lastName); body.put("lastName", lastName);
body.put("selectedRole", selectedRoleUri); body.put("selectedRole", selectedRoleUri);
} else { } else {
body.put("emailAddress", userAccount.getEmailAddress()); body.put("emailAddress", userAccount.getEmailAddress());
body.put("externalAuthId", userAccount.getExternalAuthId());
body.put("firstName", userAccount.getFirstName()); body.put("firstName", userAccount.getFirstName());
body.put("lastName", userAccount.getLastName()); body.put("lastName", userAccount.getLastName());
body.put("selectedRole", getExistingRoleUri()); body.put("selectedRole", getExistingRoleUri());
} }
if (!isRootUser()) {
body.put("roles", buildRolesList()); body.put("roles", buildRolesList());
}
if (associateWithProfile) { if (associateWithProfile) {
body.put("associate", Boolean.TRUE); body.put("associate", Boolean.TRUE);
} }
@ -192,9 +219,14 @@ public class UserAccountsEditPage extends UserAccountsPage {
userAccount.setEmailAddress(emailAddress); userAccount.setEmailAddress(emailAddress);
userAccount.setFirstName(firstName); userAccount.setFirstName(firstName);
userAccount.setLastName(lastName); userAccount.setLastName(lastName);
userAccount.setExternalAuthId(externalAuthId);
userAccount if (isRootUser()) {
.setPermissionSetUris(Collections.singleton(selectedRoleUri)); userAccount.setPermissionSetUris(Collections.<String> emptySet());
} else {
userAccount.setPermissionSetUris(Collections
.singleton(selectedRoleUri));
}
strategy.setAdditionalProperties(userAccount); strategy.setAdditionalProperties(userAccount);

View file

@ -9,8 +9,8 @@ import java.net.URL;
import java.util.HashMap; import java.util.HashMap;
import java.util.Map; 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;
import edu.cornell.mannlib.vitro.webapp.beans.UserAccount.Status;
import edu.cornell.mannlib.vitro.webapp.controller.VitroRequest; import edu.cornell.mannlib.vitro.webapp.controller.VitroRequest;
import edu.cornell.mannlib.vitro.webapp.controller.accounts.UserAccountsPage; import edu.cornell.mannlib.vitro.webapp.controller.accounts.UserAccountsPage;
import edu.cornell.mannlib.vitro.webapp.controller.authenticate.Authenticator; import edu.cornell.mannlib.vitro.webapp.controller.authenticate.Authenticator;
@ -57,6 +57,7 @@ public abstract class UserAccountsEditPageStrategy extends UserAccountsPage {
private static class EmailStrategy extends UserAccountsEditPageStrategy { private static class EmailStrategy extends UserAccountsEditPageStrategy {
private static final String PARAMETER_RESET_PASSWORD = "resetPassword"; 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"; public static final String RESET_PASSWORD_URL = "/accounts/resetPassword";
@ -102,20 +103,24 @@ public abstract class UserAccountsEditPageStrategy extends UserAccountsPage {
Map<String, Object> body = new HashMap<String, Object>(); Map<String, Object> body = new HashMap<String, Object>();
body.put("userAccount", page.getUpdatedAccount()); body.put("userAccount", page.getUpdatedAccount());
body.put("passwordLink", buildResetPasswordLink()); body.put("passwordLink", buildResetPasswordLink());
body.put("subjectLine", "Reset password request"); body.put("siteName", getSiteName());
FreemarkerEmailMessage email = FreemarkerEmailFactory FreemarkerEmailMessage email = FreemarkerEmailFactory
.createNewMessage(vreq); .createNewMessage(vreq);
email.addRecipient(TO, page.getUpdatedAccount().getEmailAddress()); email.addRecipient(TO, page.getUpdatedAccount().getEmailAddress());
email.setSubject("Reset password request"); email.setTemplate(EMAIL_TEMPLATE);
email.setHtmlTemplate("userAccounts-resetPasswordEmail-html.ftl");
email.setTextTemplate("userAccounts-resetPasswordEmail-text.ftl");
email.setBodyMap(body); email.setBodyMap(body);
email.processTemplate();
email.send(); email.send();
sentEmail = true; sentEmail = true;
} }
private String getSiteName() {
ApplicationBean appBean = vreq.getAppBean();
return appBean.getApplicationName();
}
private String buildResetPasswordLink() { private String buildResetPasswordLink() {
try { try {
String email = page.getUpdatedAccount().getEmailAddress(); String email = page.getUpdatedAccount().getEmailAddress();

View file

@ -11,6 +11,9 @@ import java.util.HashMap;
import java.util.List; import java.util.List;
import java.util.Map; 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.Log;
import org.apache.commons.logging.LogFactory; 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.beans.UserAccount.Status;
import edu.cornell.mannlib.vitro.webapp.controller.VitroRequest; 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;
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.UserAccountsPage;
import edu.cornell.mannlib.vitro.webapp.controller.accounts.UserAccountsSelection; 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.UserAccountsSelectionCriteria;
import edu.cornell.mannlib.vitro.webapp.controller.accounts.UserAccountsSelector; 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.ResponseValues;
import edu.cornell.mannlib.vitro.webapp.controller.freemarker.responsevalues.TemplateResponseValues; import edu.cornell.mannlib.vitro.webapp.controller.freemarker.responsevalues.TemplateResponseValues;
@ -91,55 +94,7 @@ public class UserAccountsListPage extends UserAccountsPage {
UserAccountsSelection selection = UserAccountsSelector.select( UserAccountsSelection selection = UserAccountsSelector.select(
userAccountsModel, criteria); userAccountsModel, criteria);
Map<String, Object> body = buildTemplateBodyMap(selection); Map<String, Object> body = buildTemplateBodyMap(selection);
return new TemplateResponseValues(TEMPLATE_NAME, body); Message.applyToBodyMap(vreq, 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<String, Object> body = buildTemplateBodyMap(selection);
body.put("newUserAccount", new UserAccountWrapper(userAccount,
Collections.<String> 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<String, Object> body = buildTemplateBodyMap(selection);
body.put("updatedUserAccount", new UserAccountWrapper(userAccount,
Collections.<String> 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<String> deletedUris) {
UserAccountsSelection selection = UserAccountsSelector.select(
userAccountsModel, criteria);
Map<String, Object> body = buildTemplateBodyMap(selection);
body.put("deletedAccountCount", deletedUris.size());
return new TemplateResponseValues(TEMPLATE_NAME, body); return new TemplateResponseValues(TEMPLATE_NAME, body);
} }
@ -238,12 +193,17 @@ public class UserAccountsListPage extends UserAccountsPage {
private List<String> findPermissionSetLabels(UserAccount account) { private List<String> findPermissionSetLabels(UserAccount account) {
List<String> labels = new ArrayList<String>(); List<String> labels = new ArrayList<String>();
if (account.isRootUser()) {
labels.add("ROOT");
} else {
for (String uri : account.getPermissionSetUris()) { for (String uri : account.getPermissionSetUris()) {
PermissionSet pSet = userAccountsDao.getPermissionSetByUri(uri); PermissionSet pSet = userAccountsDao.getPermissionSetByUri(uri);
if (pSet != null) { if (pSet != null) {
labels.add(pSet.getLabel()); labels.add(pSet.getLabel());
} }
} }
}
return labels; 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<String> 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<String> 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<String, Object> 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<String> deletedUris;
public Message(Type type, UserAccount userAccount,
boolean emailWasSent, Collection<String> deletedUris) {
this.type = type;
this.userAccount = userAccount;
this.emailWasSent = emailWasSent;
this.deletedUris = deletedUris;
}
private void applyToBodyMap(Map<String, Object> body) {
if (type == Type.NEW_ACCOUNT) {
body.put("newUserAccount", new UserAccountWrapper(userAccount,
Collections.<String> emptyList()));
if (emailWasSent) {
body.put("emailWasSent", Boolean.TRUE);
}
} else if (type == Type.UPDATED_ACCOUNT) {
body.put("updatedUserAccount", new UserAccountWrapper(
userAccount, Collections.<String> emptyList()));
if (emailWasSent) {
body.put("emailWasSent", Boolean.TRUE);
}
} else {
body.put("deletedAccountCount", deletedUris.size());
}
}
}
} }

View file

@ -26,6 +26,7 @@ public class UserAccountsCreatePasswordPage extends
.getLog(UserAccountsCreatePasswordPage.class); .getLog(UserAccountsCreatePasswordPage.class);
private static final String TEMPLATE_NAME = "userAccounts-createPassword.ftl"; private static final String TEMPLATE_NAME = "userAccounts-createPassword.ftl";
private static final String EMAIL_TEMPLATE = "userAccounts-passwordCreatedEmail.ftl";
public UserAccountsCreatePasswordPage(VitroRequest vreq) { public UserAccountsCreatePasswordPage(VitroRequest vreq) {
super(vreq); super(vreq);
@ -56,15 +57,14 @@ public class UserAccountsCreatePasswordPage extends
private void notifyUser() { private void notifyUser() {
Map<String, Object> body = new HashMap<String, Object>(); Map<String, Object> body = new HashMap<String, Object>();
body.put("userAccount", userAccount); body.put("userAccount", userAccount);
body.put("subjectLine", "Password successfully created.");
FreemarkerEmailMessage email = FreemarkerEmailFactory FreemarkerEmailMessage email = FreemarkerEmailFactory
.createNewMessage(vreq); .createNewMessage(vreq);
email.addRecipient(TO, userAccount.getEmailAddress()); email.addRecipient(TO, userAccount.getEmailAddress());
email.setSubject("Password successfully created."); email.setSubject("Password successfully created.");
email.setHtmlTemplate("userAccounts-passwordCreatedEmail-html.ftl"); email.setTemplate(EMAIL_TEMPLATE);
email.setTextTemplate("userAccounts-passwordCreatedEmail-text.ftl");
email.setBodyMap(body); email.setBodyMap(body);
email.processTemplate();
email.send(); email.send();
} }
} }

View file

@ -9,6 +9,8 @@ import java.util.Map;
import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpSession; 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.auth.permissions.PermissionSetsLoader;
import edu.cornell.mannlib.vitro.webapp.beans.UserAccount; import edu.cornell.mannlib.vitro.webapp.beans.UserAccount;
import edu.cornell.mannlib.vitro.webapp.beans.UserAccount.Status; 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 * Handle the first-time login of an Externally Authenticated user who has no
* UserAccount - let's create one! * UserAccount - let's create one!
* *
* If they get here from the login, there should an externalAuthId waiting in * If they get here from the login, there should be an ExternalLoginInfo waiting
* the session. Otherwise, they should get here by submitting the form, which * in the session. Otherwise, they should get here by submitting the form, which
* will have the externalAuthId as a hidden field. * will have the info in hidden fields.
*/ */
public class UserAccountsFirstTimeExternalPage extends UserAccountsPage { public class UserAccountsFirstTimeExternalPage extends UserAccountsPage {
private static final String PARAMETER_SUBMIT = "submit"; private static final String PARAMETER_SUBMIT = "submit";
private static final String PARAMETER_EXTERNAL_AUTH_ID = "externalAuthId"; 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_EMAIL_ADDRESS = "emailAddress";
private static final String PARAMETER_FIRST_NAME = "firstName"; private static final String PARAMETER_FIRST_NAME = "firstName";
private static final String PARAMETER_LAST_NAME = "lastName"; 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 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(); .getName();
/** /**
* Let some other request set the External Auth ID before redirecting to * Let some other request set the External Auth ID and the afterLogin URL
* here. * before redirecting to here.
*/ */
public static void setExternalAuthId(HttpServletRequest req, public static void setExternalLoginInfo(HttpServletRequest req,
String externalAuthId) { String externalAuthId, String afterLoginUrl) {
req.getSession().setAttribute(ATTRIBUTE_EXTERNAL_AUTH_ID, req.getSession().setAttribute(ATTRIBUTE_EXTERNAL_LOGIN_INFO,
externalAuthId); new ExternalLoginInfo(externalAuthId, afterLoginUrl));
} }
private final UserAccountsFirstTimeExternalPageStrategy strategy; private final UserAccountsFirstTimeExternalPageStrategy strategy;
private boolean submit = false;
private String externalAuthId = ""; private String externalAuthId = "";
private String afterLoginUrl = "";
private boolean submit = false;
private String emailAddress = ""; private String emailAddress = "";
private String firstName = ""; private String firstName = "";
private String lastName = ""; private String lastName = "";
@ -71,7 +76,7 @@ public class UserAccountsFirstTimeExternalPage extends UserAccountsPage {
this.strategy = UserAccountsFirstTimeExternalPageStrategy.getInstance( this.strategy = UserAccountsFirstTimeExternalPageStrategy.getInstance(
vreq, this, isEmailEnabled()); vreq, this, isEmailEnabled());
checkSessionForExternalAuthId(); checkSessionForExternalLoginInfo();
if (externalAuthId.isEmpty()) { if (externalAuthId.isEmpty()) {
parseRequestParameters(); parseRequestParameters();
} }
@ -83,20 +88,26 @@ public class UserAccountsFirstTimeExternalPage extends UserAccountsPage {
} }
} }
private void checkSessionForExternalAuthId() { private void checkSessionForExternalLoginInfo() {
HttpSession session = vreq.getSession(); HttpSession session = vreq.getSession();
Object o = session.getAttribute(ATTRIBUTE_EXTERNAL_AUTH_ID); Object o = session.getAttribute(ATTRIBUTE_EXTERNAL_LOGIN_INFO);
session.removeAttribute(ATTRIBUTE_EXTERNAL_AUTH_ID); session.removeAttribute(ATTRIBUTE_EXTERNAL_LOGIN_INFO);
if (o instanceof String) { if (o instanceof ExternalLoginInfo) {
externalAuthId = (String) o; externalAuthId = ((ExternalLoginInfo) o).externalAuthId;
afterLoginUrl = ((ExternalLoginInfo) o).afterLoginUrl;
if (afterLoginUrl == null) {
afterLoginUrl = "";
}
} }
} }
private void parseRequestParameters() { private void parseRequestParameters() {
submit = isFlagOnRequest(PARAMETER_SUBMIT);
externalAuthId = getStringParameter(PARAMETER_EXTERNAL_AUTH_ID, ""); externalAuthId = getStringParameter(PARAMETER_EXTERNAL_AUTH_ID, "");
afterLoginUrl = getStringParameter(PARAMETER_AFTER_LOGIN_URL, "");
submit = isFlagOnRequest(PARAMETER_SUBMIT);
emailAddress = getStringParameter(PARAMETER_EMAIL_ADDRESS, ""); emailAddress = getStringParameter(PARAMETER_EMAIL_ADDRESS, "");
firstName = getStringParameter(PARAMETER_FIRST_NAME, ""); firstName = getStringParameter(PARAMETER_FIRST_NAME, "");
lastName = getStringParameter(PARAMETER_LAST_NAME, ""); lastName = getStringParameter(PARAMETER_LAST_NAME, "");
@ -156,10 +167,12 @@ public class UserAccountsFirstTimeExternalPage extends UserAccountsPage {
public final ResponseValues showPage() { public final ResponseValues showPage() {
Map<String, Object> body = new HashMap<String, Object>(); Map<String, Object> body = new HashMap<String, Object>();
body.put("externalAuthId", externalAuthId);
body.put("afterLoginUrl", afterLoginUrl);
body.put("emailAddress", emailAddress); body.put("emailAddress", emailAddress);
body.put("firstName", firstName); body.put("firstName", firstName);
body.put("lastName", lastName); body.put("lastName", lastName);
body.put("externalAuthId", externalAuthId);
body.put("formUrls", buildUrlsMap()); body.put("formUrls", buildUrlsMap());
if (!errorCode.isEmpty()) { if (!errorCode.isEmpty()) {
@ -191,4 +204,25 @@ public class UserAccountsFirstTimeExternalPage extends UserAccountsPage {
return u; 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;
}
}
} }

View file

@ -52,6 +52,8 @@ public abstract class UserAccountsFirstTimeExternalPageStrategy extends
public static class EmailStrategy extends public static class EmailStrategy extends
UserAccountsFirstTimeExternalPageStrategy { UserAccountsFirstTimeExternalPageStrategy {
private static final String EMAIL_TEMPLATE = "userAccounts-firstTimeExternalEmail.ftl";
public EmailStrategy(VitroRequest vreq, public EmailStrategy(VitroRequest vreq,
UserAccountsFirstTimeExternalPage page) { UserAccountsFirstTimeExternalPage page) {
super(vreq, page); super(vreq, page);
@ -66,15 +68,14 @@ public abstract class UserAccountsFirstTimeExternalPageStrategy extends
public void notifyUser(UserAccount ua) { public void notifyUser(UserAccount ua) {
Map<String, Object> body = new HashMap<String, Object>(); Map<String, Object> body = new HashMap<String, Object>();
body.put("userAccount", ua); body.put("userAccount", ua);
body.put("subjectLine", "Your VIVO account has been created.");
FreemarkerEmailMessage email = FreemarkerEmailFactory FreemarkerEmailMessage email = FreemarkerEmailFactory
.createNewMessage(vreq); .createNewMessage(vreq);
email.addRecipient(TO, ua.getEmailAddress()); email.addRecipient(TO, ua.getEmailAddress());
email.setSubject("Your VIVO account has been created."); email.setSubject("Your VIVO account has been created.");
email.setHtmlTemplate("userAccounts-firstTimeExternalEmail-html.ftl"); email.setTemplate(EMAIL_TEMPLATE);
email.setTextTemplate("userAccounts-firstTimeExternalEmail-text.ftl");
email.setBodyMap(body); email.setBodyMap(body);
email.processTemplate();
email.send(); email.send();
} }

View file

@ -108,6 +108,8 @@ public abstract class UserAccountsMyAccountPageStrategy extends
private static final String ERROR_WRONG_PASSWORD_LENGTH = "errorPasswordIsWrongLength"; private static final String ERROR_WRONG_PASSWORD_LENGTH = "errorPasswordIsWrongLength";
private static final String ERROR_PASSWORDS_DONT_MATCH = "errorPasswordsDontMatch"; private static final String ERROR_PASSWORDS_DONT_MATCH = "errorPasswordsDontMatch";
private static final String EMAIL_TEMPLATE = "userAccounts-confirmEmailChangedEmail.ftl";
private final String originalEmail; private final String originalEmail;
private String newPassword; private String newPassword;
@ -167,15 +169,14 @@ public abstract class UserAccountsMyAccountPageStrategy extends
Map<String, Object> body = new HashMap<String, Object>(); Map<String, Object> body = new HashMap<String, Object>();
body.put("userAccount", page.getUserAccount()); body.put("userAccount", page.getUserAccount());
body.put("subjectLine", "Your VIVO email account has been changed.");
FreemarkerEmailMessage email = FreemarkerEmailFactory FreemarkerEmailMessage email = FreemarkerEmailFactory
.createNewMessage(vreq); .createNewMessage(vreq);
email.addRecipient(TO, page.getUserAccount().getEmailAddress()); email.addRecipient(TO, page.getUserAccount().getEmailAddress());
email.setSubject("Your VIVO email account has been changed."); email.setSubject("Your VIVO email account has been changed.");
email.setHtmlTemplate("userAccounts-confirmEmailChangedEmail-html.ftl"); email.setTemplate(EMAIL_TEMPLATE);
email.setTextTemplate("userAccounts-confirmEmailChangedEmail-text.ftl");
email.setBodyMap(body); email.setBodyMap(body);
email.processTemplate();
email.send(); email.send();
emailSent = true; emailSent = true;

View file

@ -26,6 +26,8 @@ public class UserAccountsResetPasswordPage extends UserAccountsPasswordBasePage
private static final String TEMPLATE_NAME = "userAccounts-resetPassword.ftl"; private static final String TEMPLATE_NAME = "userAccounts-resetPassword.ftl";
private static final String EMAIL_TEMPLATE = "userAccounts-passwordResetEmail.ftl";
protected UserAccountsResetPasswordPage(VitroRequest vreq) { protected UserAccountsResetPasswordPage(VitroRequest vreq) {
super(vreq); super(vreq);
} }
@ -55,15 +57,14 @@ public class UserAccountsResetPasswordPage extends UserAccountsPasswordBasePage
private void notifyUser() { private void notifyUser() {
Map<String, Object> body = new HashMap<String, Object>(); Map<String, Object> body = new HashMap<String, Object>();
body.put("userAccount", userAccount); body.put("userAccount", userAccount);
body.put("subjectLine", "Password changed.");
FreemarkerEmailMessage email = FreemarkerEmailFactory FreemarkerEmailMessage email = FreemarkerEmailFactory
.createNewMessage(vreq); .createNewMessage(vreq);
email.addRecipient(TO, userAccount.getEmailAddress()); email.addRecipient(TO, userAccount.getEmailAddress());
email.setSubject("Password changed."); email.setSubject("Password changed.");
email.setHtmlTemplate("userAccounts-passwordResetEmail-html.ftl"); email.setTemplate(EMAIL_TEMPLATE);
email.setTextTemplate("userAccounts-passwordResetEmail-text.ftl");
email.setBodyMap(body); email.setBodyMap(body);
email.processTemplate();
email.send(); email.send();
} }

View file

@ -4,10 +4,10 @@ package edu.cornell.mannlib.vitro.webapp.controller.accounts.user;
import static edu.cornell.mannlib.vedit.beans.LoginStatusBean.AuthenticationSource.EXTERNAL; 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.Log;
import org.apache.commons.logging.LogFactory; 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.Actions;
import edu.cornell.mannlib.vitro.webapp.auth.requestedAction.usepages.EditOwnAccount; import edu.cornell.mannlib.vitro.webapp.auth.requestedAction.usepages.EditOwnAccount;
import edu.cornell.mannlib.vitro.webapp.beans.DisplayMessage; import edu.cornell.mannlib.vitro.webapp.beans.DisplayMessage;
@ -115,9 +115,7 @@ public class UserAccountsUserController extends FreemarkerHttpServlet {
UserAccount userAccount = page.createAccount(); UserAccount userAccount = page.createAccount();
Authenticator auth = Authenticator.getInstance(vreq); Authenticator auth = Authenticator.getInstance(vreq);
auth.recordLoginAgainstUserAccount(userAccount, EXTERNAL); auth.recordLoginAgainstUserAccount(userAccount, EXTERNAL);
LoginProcessBean.removeBean(vreq); return showLoginRedirection(vreq, page.getAfterLoginUrl());
return showLoginRedirection(vreq);
} else { } else {
return page.showPage(); return page.showPage();
} }
@ -132,10 +130,31 @@ public class UserAccountsUserController extends FreemarkerHttpServlet {
return new RedirectResponseValues("/"); return new RedirectResponseValues("/");
} }
private ResponseValues showLoginRedirection(VitroRequest vreq) { private ResponseValues showLoginRedirection(VitroRequest vreq,
LoginRedirector lr = new LoginRedirector(vreq); String afterLoginUrl) {
LoginRedirector lr = new LoginRedirector(vreq, afterLoginUrl);
DisplayMessage.setMessage(vreq, lr.assembleWelcomeMessage()); DisplayMessage.setMessage(vreq, lr.assembleWelcomeMessage());
String uri = lr.getRedirectionUriForLoggedInUser(); 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;
} }
} }

View file

@ -51,10 +51,10 @@ public abstract class Authenticator {
// ---------------------------------------------------------------------- // ----------------------------------------------------------------------
/** Maximum inactive interval for a ordinary logged-in session, in seconds. */ /** 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. */ /** 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. * Get the UserAccount for this external ID, or null if there is none.
@ -96,8 +96,9 @@ public abstract class Authenticator {
* <pre> * <pre>
* Record that the user has logged in, with all of the housekeeping that * Record that the user has logged in, with all of the housekeeping that
* goes with it: * goes with it:
* - updating the user record * - update the user record
* - setting login status and timeout limit in the session * - set login status and timeout limit in the session
* - refresh the Identifiers on the request
* - record the user in the session map * - record the user in the session map
* - notify other users of the model * - notify other users of the model
* </pre> * </pre>
@ -105,17 +106,6 @@ public abstract class Authenticator {
public abstract void recordLoginAgainstUserAccount(UserAccount userAccount, public abstract void recordLoginAgainstUserAccount(UserAccount userAccount,
AuthenticationSource authSource); AuthenticationSource authSource);
/**
* <pre>
* 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.
* </pre>
*
* TODO JB This goes away.
*/
public abstract void recordLoginWithoutUserAccount(String individualUri);
/** /**
* <pre> * <pre>
* Record that the current user has logged out: - notify other users of the * Record that the current user has logged out: - notify other users of the

View file

@ -3,7 +3,6 @@
package edu.cornell.mannlib.vitro.webapp.controller.authenticate; package edu.cornell.mannlib.vitro.webapp.controller.authenticate;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collections;
import java.util.List; import java.util.List;
import java.util.Map; 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;
import edu.cornell.mannlib.vedit.beans.LoginStatusBean.AuthenticationSource; 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.BaseResourceBean.RoleLevel;
import edu.cornell.mannlib.vitro.webapp.beans.Individual; import edu.cornell.mannlib.vitro.webapp.beans.Individual;
import edu.cornell.mannlib.vitro.webapp.beans.SelfEditingConfiguration; import edu.cornell.mannlib.vitro.webapp.beans.SelfEditingConfiguration;
@ -123,24 +123,13 @@ public class BasicAuthenticator extends Authenticator {
} }
recordLoginOnUserRecord(userAccount); 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(); HttpSession session = request.getSession();
createLoginStatusBean(userUri, authSource, session); createLoginStatusBean(userAccount.getUri(), authSource, session);
setSessionTimeoutLimit(session); RequestIdentifiers.resetIdentifiers(request);
recordInUserSessionMap(userUri, session); setSessionTimeoutLimit(userAccount, session);
notifyOtherUsers(userUri, 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. * 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); RoleLevel role = RoleLevel.getRoleFromLoginStatus(request);
if (role == RoleLevel.EDITOR || role == RoleLevel.CURATOR if (role == RoleLevel.EDITOR || role == RoleLevel.CURATOR
|| role == RoleLevel.DB_ADMIN) { || role == RoleLevel.DB_ADMIN) {
session.setMaxInactiveInterval(PRIVILEGED_TIMEOUT_INTERVAL); session.setMaxInactiveInterval(PRIVILEGED_TIMEOUT_INTERVAL);
} else if (userAccount.isRootUser()) {
session.setMaxInactiveInterval(PRIVILEGED_TIMEOUT_INTERVAL);
} else { } else {
session.setMaxInactiveInterval(LOGGED_IN_TIMEOUT_INTERVAL); session.setMaxInactiveInterval(LOGGED_IN_TIMEOUT_INTERVAL);
} }

View file

@ -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 static edu.cornell.mannlib.vitro.webapp.controller.authenticate.LoginExternalAuthSetup.ATTRIBUTE_REFERRER;
import java.io.IOException; import java.io.IOException;
import java.util.List;
import javax.servlet.ServletException; import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest; 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.vedit.beans.LoginStatusBean.AuthenticationSource;
import edu.cornell.mannlib.vitro.webapp.beans.UserAccount; 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 * 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 a User acocunt. Record the login.
* - User corresponds to an Individual (self-editor). * - User corresponds to an Individual (self-editor).
* - User is not recognized. * - 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.
* </pre> * </pre>
*/ */
@Override @Override
@ -50,39 +59,30 @@ public class LoginExternalAuthReturn extends BaseLoginServlet {
return; return;
} }
String afterLoginUrl = LoginProcessBean.getBean(req).getAfterLoginUrl();
removeLoginProcessArtifacts(req);
UserAccount userAccount = getAuthenticator(req) UserAccount userAccount = getAuthenticator(req)
.getAccountForExternalAuth(externalAuthId); .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()); log.debug("Logging in as " + userAccount.getUri());
getAuthenticator(req).recordLoginAgainstUserAccount(userAccount, getAuthenticator(req).recordLoginAgainstUserAccount(userAccount,
AuthenticationSource.EXTERNAL); AuthenticationSource.EXTERNAL);
removeLoginProcessArtifacts(req); new LoginRedirector(req, afterLoginUrl).redirectLoggedInUser(resp);
new LoginRedirector(req).redirectLoggedInUser(resp);
return; return;
} }
List<String> 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) { private void removeLoginProcessArtifacts(HttpServletRequest req) {
req.getSession().removeAttribute(ATTRIBUTE_REFERRER); req.getSession().removeAttribute(ATTRIBUTE_REFERRER);
LoginProcessBean.removeBean(req);
} }
@Override @Override

View file

@ -16,11 +16,14 @@ import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory; import org.apache.commons.logging.LogFactory;
import edu.cornell.mannlib.vedit.beans.LoginStatusBean; 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.BaseResourceBean.RoleLevel;
import edu.cornell.mannlib.vitro.webapp.beans.DisplayMessage; import edu.cornell.mannlib.vitro.webapp.beans.DisplayMessage;
import edu.cornell.mannlib.vitro.webapp.beans.UserAccount; import edu.cornell.mannlib.vitro.webapp.beans.UserAccount;
import edu.cornell.mannlib.vitro.webapp.controller.Controllers; 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? * 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 uriOfAssociatedIndividual;
private final String afterLoginPage; private final String afterLoginPage;
public LoginRedirector(HttpServletRequest request) { public LoginRedirector(HttpServletRequest request, String afterLoginPage) {
this.request = request; this.request = request;
this.session = request.getSession(); this.session = request.getSession();
this.afterLoginPage = afterLoginPage;
uriOfAssociatedIndividual = getAssociatedIndividualUri(); uriOfAssociatedIndividual = getAssociatedIndividualUri();
LoginProcessBean processBean = LoginProcessBean.getBean(request);
log.debug("process bean is: " + processBean);
afterLoginPage = processBean.getAfterLoginUrl();
} }
/** Is there an Individual associated with this user? */ /** Is there an Individual associated with this user? */
@ -106,7 +106,6 @@ public class LoginRedirector {
try { try {
DisplayMessage.setMessage(request, assembleWelcomeMessage()); DisplayMessage.setMessage(request, assembleWelcomeMessage());
response.sendRedirect(getRedirectionUriForLoggedInUser()); response.sendRedirect(getRedirectionUriForLoggedInUser());
LoginProcessBean.removeBean(request);
} catch (IOException e) { } catch (IOException e) {
log.debug("Problem with re-direction", e); log.debug("Problem with re-direction", e);
response.sendRedirect(getApplicationHomePageUrl()); response.sendRedirect(getApplicationHomePageUrl());
@ -142,7 +141,6 @@ public class LoginRedirector {
throws IOException { throws IOException {
try { try {
response.sendRedirect(getRedirectionUriForCancellingUser()); response.sendRedirect(getRedirectionUriForCancellingUser());
LoginProcessBean.removeBean(request);
} catch (IOException e) { } catch (IOException e) {
log.debug("Problem with re-direction", e); log.debug("Problem with re-direction", e);
response.sendRedirect(getApplicationHomePageUrl()); response.sendRedirect(getApplicationHomePageUrl());
@ -158,7 +156,12 @@ public class LoginRedirector {
} }
private boolean isMerelySelfEditor() { 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; return role == RoleLevel.PUBLIC || role == RoleLevel.SELF;
} }

View file

@ -124,7 +124,7 @@ public class Authenticate extends VitroHttpServlet {
// Send them on their way. // Send them on their way.
switch (exitState) { switch (exitState) {
case NOWHERE: case NOWHERE:
new LoginRedirector(vreq).redirectCancellingUser(response); showLoginCanceled(response, vreq);
break; break;
case LOGGING_IN: case LOGGING_IN:
showLoginScreen(vreq, response); showLoginScreen(vreq, response);
@ -133,7 +133,7 @@ public class Authenticate extends VitroHttpServlet {
showLoginScreen(vreq, response); showLoginScreen(vreq, response);
break; break;
default: // LOGGED_IN: default: // LOGGED_IN:
new LoginRedirector(vreq).redirectLoggedInUser(response); showLoginComplete(response, vreq);
break; break;
} }
} catch (Exception e) { } catch (Exception e) {
@ -478,6 +478,31 @@ public class Authenticate extends VitroHttpServlet {
return; 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. */ /** Get a reference to the Authenticator. */
private Authenticator getAuthenticator(HttpServletRequest request) { private Authenticator getAuthenticator(HttpServletRequest request) {
return Authenticator.getInstance(request); return Authenticator.getInstance(request);

View file

@ -185,15 +185,23 @@ public class FreemarkerHttpServlet extends VitroHttpServlet {
// Tell the template and any directives it uses that we're processing a page template. // Tell the template and any directives it uses that we're processing a page template.
templateDataModel.put("templateType", PAGE_TEMPLATE_TYPE); templateDataModel.put("templateType", PAGE_TEMPLATE_TYPE);
writePage(templateDataModel, config, vreq, response);
writePage(templateDataModel, config, vreq, response, values.getStatusCode());
} }
protected void doRedirect(HttpServletRequest request, HttpServletResponse response, ResponseValues values) protected void doRedirect(HttpServletRequest request, HttpServletResponse response, ResponseValues values)
throws ServletException, IOException { throws ServletException, IOException {
String redirectUrl = values.getRedirectUrl(); String redirectUrl = values.getRedirectUrl();
setResponseStatus(response, values.getStatusCode());
response.sendRedirect(redirectUrl); 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) protected void doForward(HttpServletRequest request, HttpServletResponse response, ResponseValues values)
throws ServletException, IOException { throws ServletException, IOException {
String forwardUrl = values.getForwardUrl(); String forwardUrl = values.getForwardUrl();
@ -305,12 +313,19 @@ public class FreemarkerHttpServlet extends VitroHttpServlet {
* the transition from JSP to Freemarker. * the transition from JSP to Freemarker.
*/ */
public static Map<String, Object> getDirectives() { public static Map<String, Object> getDirectives() {
Map<String, Object> map = new HashMap<String, Object>();
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<String, Object> getDirectivesForAllEnvironments() {
Map<String, Object> map = new HashMap<String, Object>(); Map<String, Object> map = new HashMap<String, Object>();
map.put("dump", new freemarker.ext.dump.DumpDirective()); map.put("dump", new freemarker.ext.dump.DumpDirective());
map.put("dumpAll", new freemarker.ext.dump.DumpAllDirective()); map.put("dumpAll", new freemarker.ext.dump.DumpAllDirective());
map.put("help", new freemarker.ext.dump.HelpDirective()); map.put("help", new freemarker.ext.dump.HelpDirective());
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; return map;
} }
@ -330,9 +345,6 @@ public class FreemarkerHttpServlet extends VitroHttpServlet {
Map<String, Object> map = new HashMap<String, Object>(); Map<String, Object> map = new HashMap<String, Object>();
ApplicationBean appBean = vreq.getAppBean(); 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(); String siteName = appBean.getApplicationName();
map.put("siteName", siteName); 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. // This value is used only in stylesheets.ftl and already contains the context path.
map.put("stylesheetPath", UrlBuilder.getUrl(themeDir + "/css")); 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); String flashMessage = DisplayMessage.getMessageAndClear(vreq);
if (! flashMessage.isEmpty()) { if (! flashMessage.isEmpty()) {
map.put("flash", flashMessage); map.put("flash", flashMessage);
@ -448,18 +455,24 @@ public class FreemarkerHttpServlet extends VitroHttpServlet {
} }
protected void writePage(Map<String, Object> root, Configuration config, protected void writePage(Map<String, Object> root, Configuration config,
HttpServletRequest request, HttpServletResponse response) throws TemplateProcessingException { HttpServletRequest request, HttpServletResponse response, int statusCode) throws TemplateProcessingException {
writeTemplate(getPageTemplateName(), root, config, request, response); writeTemplate(getPageTemplateName(), root, config, request, response, statusCode);
} }
protected void writeTemplate(String templateName, Map<String, Object> map, Configuration config, protected void writeTemplate(String templateName, Map<String, Object> map, Configuration config,
HttpServletRequest request, HttpServletResponse response) throws TemplateProcessingException { HttpServletRequest request, HttpServletResponse response) throws TemplateProcessingException {
StringWriter sw = processTemplate(templateName, map, config, request); writeTemplate(templateName, map, config, request, response, 0);
write(sw, response);
} }
protected void write(StringWriter sw, HttpServletResponse response) { protected void writeTemplate(String templateName, Map<String, Object> 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, int statusCode) {
try { try {
setResponseStatus(response, statusCode);
PrintWriter out = response.getWriter(); PrintWriter out = response.getWriter();
out.print(sw); out.print(sw);
} catch (IOException e) { } catch (IOException e) {

View file

@ -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. // Check to see if the request is for a non-information resource, redirect if it is.
String redirectURL = checkForRedirect ( url, vreq ); String redirectURL = checkForRedirect ( url, vreq );
if( redirectURL != null ){ if( redirectURL != null ){
return new RedirectResponseValues(redirectURL); return new RedirectResponseValues(redirectURL, HttpServletResponse.SC_SEE_OTHER);
} }
Individual individual = null; Individual individual = null;
@ -464,11 +464,18 @@ public class IndividualController extends FreemarkerHttpServlet {
return null; return null;
} }
/*
* 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 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
private String checkForRedirect(String url, VitroRequest vreq) { private String checkForRedirect(String url, VitroRequest vreq) {
Matcher m = URI_PATTERN.matcher(url); Matcher m = URI_PATTERN.matcher(url);
if( m.matches() && m.groupCount() == 1 ){ if( m.matches() && m.groupCount() == 1 ){
@ -506,7 +513,11 @@ public class IndividualController extends FreemarkerHttpServlet {
protected ContentType checkForLinkedDataRequest(String url, VitroRequest vreq ) { protected ContentType checkForLinkedDataRequest(String url, VitroRequest vreq ) {
try { try {
Matcher m; 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"); String formatParam = (String) vreq.getParameter("format");
if (formatParam != null) { if (formatParam != null) {
m = RDFXML_FORMAT.matcher(formatParam); 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"); String acceptHeader = vreq.getHeader("accept");
if (acceptHeader != null) { if (acceptHeader != null) {
String ctStr = ContentType.getBestContentType( String ctStr = ContentType.getBestContentType(
@ -538,10 +553,12 @@ public class IndividualController extends FreemarkerHttpServlet {
} }
/* /*
* check for parts of URL that indicate request for RDF * 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.rdf
http://vivo.cornell.edu/individual/n23/n23.n3 * http://vivo.cornell.edu/individual/n23/n23.n3
http://vivo.cornell.edu/individual/n23/n23.ttl * 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); m = RDF_REQUEST.matcher(url);
if( m.matches() ) { if( m.matches() ) {
@ -556,7 +573,6 @@ public class IndividualController extends FreemarkerHttpServlet {
return ContentType.TURTLE; return ContentType.TURTLE;
} }
} catch (Throwable th) { } catch (Throwable th) {
log.error("problem while checking accept header " , th); log.error("problem while checking accept header " , th);
} }

View file

@ -23,6 +23,11 @@ public abstract class BaseResponseValues implements ResponseValues {
this.contentType = contentType; this.contentType = contentType;
} }
BaseResponseValues(ContentType contentType, int statusCode) {
this.contentType = contentType;
this.statusCode = statusCode;
}
@Override @Override
public int getStatusCode() { public int getStatusCode() {
return statusCode; return statusCode;
@ -38,6 +43,7 @@ public abstract class BaseResponseValues implements ResponseValues {
return contentType; return contentType;
} }
@Override
public void setContentType(ContentType contentType) { public void setContentType(ContentType contentType) {
this.contentType = contentType; this.contentType = contentType;
} }

View file

@ -14,6 +14,11 @@ public class RdfResponseValues extends BaseResponseValues {
this.model = model; this.model = model;
} }
public RdfResponseValues(ContentType contentType, Model model, int statusCode) {
super(contentType, statusCode);
this.model = model;
}
@Override @Override
public Model getModel() { public Model getModel() {
return model; return model;

View file

@ -28,6 +28,8 @@ public interface ResponseValues {
public ContentType getContentType(); public ContentType getContentType();
public void setContentType(ContentType contentType);
public Model getModel(); public Model getModel();
} }

View file

@ -49,7 +49,9 @@ public class TemplateResponseValues extends BaseResponseValues {
} }
public static TemplateResponseValues getTemplateResponseValuesFromException(ExceptionResponseValues responseValues) { public static TemplateResponseValues getTemplateResponseValuesFromException(ExceptionResponseValues responseValues) {
return new TemplateResponseValues(responseValues.getTemplateName(), responseValues.getMap()); return new TemplateResponseValues(responseValues.getTemplateName(),
responseValues.getMap(),
responseValues.getStatusCode());
} }
} }

View file

@ -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 * Where are we in the process of logging on? What message should we show to the
* user? * 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 { public class LoginProcessBean {
private static final Log log = LogFactory.getLog(LoginProcessBean.class); private static final Log log = LogFactory.getLog(LoginProcessBean.class);

View file

@ -40,11 +40,6 @@ public interface UserAccountsDao {
*/ */
UserAccount getUserAccountByExternalAuthId(String externalAuthId); UserAccount getUserAccountByExternalAuthId(String externalAuthId);
/**
* Is this UserAccount a root user?
*/
boolean isRootUser(UserAccount userAccount);
/** /**
* Create a new UserAccount in the model. * Create a new UserAccount in the model.
* *

View file

@ -47,11 +47,6 @@ public class UserAccountsDaoFiltering extends BaseFiltering implements
return innerDao.getUserAccountByExternalAuthId(externalAuthId); return innerDao.getUserAccountByExternalAuthId(externalAuthId);
} }
@Override
public boolean isRootUser(UserAccount userAccount) {
return innerDao.isRootUser(userAccount);
}
@Override @Override
public String insertUserAccount(UserAccount userAccount) { public String insertUserAccount(UserAccount userAccount) {
return innerDao.insertUserAccount(userAccount); return innerDao.insertUserAccount(userAccount);

View file

@ -57,15 +57,13 @@ public class DataPropertyStatementDaoSDB extends DataPropertyStatementDaoJena
} }
else else
{ {
String[] graphVars = { "?g" };
String query = String query =
"CONSTRUCT { \n" + "CONSTRUCT { \n" +
" <" + entity.getURI() + "> ?p ?o . \n" + " <" + entity.getURI() + "> ?p ?o . \n" +
"} WHERE { GRAPH ?g { \n" + "} WHERE { \n" +
" <" + entity.getURI() + "> ?p ?o . \n" + " <" + entity.getURI() + "> ?p ?o . \n" +
" FILTER(isLiteral(?o)) \n" + " FILTER(isLiteral(?o)) \n" +
WebappDaoFactorySDB.getFilterBlock(graphVars, datasetMode) + "}" ;
"} }" ;
Model results = null; Model results = null;
DatasetWrapper w = dwf.getDatasetWrapper(); DatasetWrapper w = dwf.getDatasetWrapper();
Dataset dataset = w.getDataset(); Dataset dataset = w.getDataset();

View file

@ -781,7 +781,7 @@ public class IndividualJena extends IndividualImpl implements Individual {
if (stmt.getObject().isURIResource()) { if (stmt.getObject().isURIResource()) {
String typeURI = ((Resource)stmt.getObject()).getURI(); String typeURI = ((Resource)stmt.getObject()).getURI();
if (pfs.isClassProhibitedFromSearch(typeURI)) { if (pfs.isClassProhibitedFromSearch(typeURI)) {
return false; return true;
} }
} }
} }

View file

@ -395,8 +395,8 @@ public class IndividualSDB extends IndividualImpl implements Individual {
String[] graphVars = { "?g" }; String[] graphVars = { "?g" };
String queryStr = String queryStr =
"CONSTRUCT { <"+ind.getURI()+"> <" + propertyURI + "> ?value } \n" + "CONSTRUCT { <"+ind.getURI()+"> <" + propertyURI + "> ?value } \n" +
"WHERE { GRAPH ?g { \n" + "WHERE { \n" +
"<" + ind.getURI() +"> <" + propertyURI + "> ?value } \n" + "<" + ind.getURI() +"> <" + propertyURI + "> ?value \n" +
WebappDaoFactorySDB.getFilterBlock(graphVars, datasetMode) + WebappDaoFactorySDB.getFilterBlock(graphVars, datasetMode) +
"\n} "; "\n} ";
Query query = QueryFactory.create(queryStr); Query query = QueryFactory.create(queryStr);
@ -456,13 +456,13 @@ public class IndividualSDB extends IndividualImpl implements Individual {
if (this.mainImageUri != NOT_INITIALIZED) { if (this.mainImageUri != NOT_INITIALIZED) {
return mainImageUri; return mainImageUri;
} else { } else {
for (ObjectPropertyStatement stmt : getObjectPropertyStatements()) { List<ObjectPropertyStatement> mainImgStmts =
if (stmt.getPropertyURI() getObjectPropertyStatements(VitroVocabulary.IND_MAIN_IMAGE);
.equals(VitroVocabulary.IND_MAIN_IMAGE)) { if (mainImgStmts != null && mainImgStmts.size() > 0) {
mainImageUri = stmt.getObjectURI(); // arbitrarily return the first value in the list
mainImageUri = mainImgStmts.get(0).getObjectURI();
return mainImageUri; return mainImageUri;
} }
}
return null; return null;
} }
} }
@ -502,13 +502,10 @@ public class IndividualSDB extends IndividualImpl implements Individual {
if( _hasThumb != null ){ if( _hasThumb != null ){
return _hasThumb; return _hasThumb;
}else{ }else{
String[] graphVars = { "?g" };
String ask = String ask =
"ASK { GRAPH ?g " + "ASK { " +
" { <" + individualURI + "> <http://vitro.mannlib.cornell.edu/ns/vitro/public#mainImage> ?mainImage . \n" + " <" + individualURI + "> <http://vitro.mannlib.cornell.edu/ns/vitro/public#mainImage> ?mainImage . \n" +
" ?mainImage <http://vitro.mannlib.cornell.edu/ns/vitro/public#thumbnailImage> ?thumbImage . }\n" + " ?mainImage <http://vitro.mannlib.cornell.edu/ns/vitro/public#thumbnailImage> ?thumbImage . }\n" ;
WebappDaoFactorySDB.getFilterBlock(graphVars, datasetMode) +
"}";
DatasetWrapper w = getDatasetWrapper(); DatasetWrapper w = getDatasetWrapper();
Dataset dataset = w.getDataset(); Dataset dataset = w.getDataset();
dataset.getLock().enterCriticalSection(Lock.READ); dataset.getLock().enterCriticalSection(Lock.READ);
@ -552,10 +549,8 @@ public class IndividualSDB extends IndividualImpl implements Individual {
Dataset dataset = w.getDataset(); Dataset dataset = w.getDataset();
dataset.getLock().enterCriticalSection(Lock.READ); dataset.getLock().enterCriticalSection(Lock.READ);
try { try {
String graphVars[] = { "?g" };
StringBuffer selectPrimaryLinkQueryBuff = new StringBuffer().append( StringBuffer selectPrimaryLinkQueryBuff = new StringBuffer().append(
"SELECT ?url ?anchor \n" ).append( "SELECT ?url ?anchor \n" ).append(" WHERE { \n").append(
"WHERE{ GRAPH ?g { \n " ).append(
" <" + this.individualURI + "> ").append( " <" + this.individualURI + "> ").append(
"<" + VitroVocabulary.PRIMARY_LINK + "> " ).append( "<" + VitroVocabulary.PRIMARY_LINK + "> " ).append(
"?link . \n").append( "?link . \n").append(
@ -563,9 +558,7 @@ public class IndividualSDB extends IndividualImpl implements Individual {
).append( ).append(
" ?link <" + VitroVocabulary.LINK_ANCHOR + "> ?anchor . \n" " ?link <" + VitroVocabulary.LINK_ANCHOR + "> ?anchor . \n"
).append( ).append(
"} \n") "} \n");
.append(WebappDaoFactorySDB.getFilterBlock(graphVars, datasetMode))
.append("}");
QueryExecution qexec = QueryExecutionFactory.create( QueryExecution qexec = QueryExecutionFactory.create(
QueryFactory.create(selectPrimaryLinkQueryBuff.toString()) QueryFactory.create(selectPrimaryLinkQueryBuff.toString())
, dataset); , dataset);
@ -678,12 +671,9 @@ public class IndividualSDB extends IndividualImpl implements Individual {
Dataset dataset = w.getDataset(); Dataset dataset = w.getDataset();
dataset.getLock().enterCriticalSection(Lock.READ); dataset.getLock().enterCriticalSection(Lock.READ);
try { try {
String[] graphVars = { "?g" };
String valuesOfProperty = String valuesOfProperty =
"CONSTRUCT{ <" + this.individualURI + "> <" + propertyURI + "> ?object }" + "CONSTRUCT{ <" + this.individualURI + "> <" + propertyURI + "> ?object }" +
"WHERE{ GRAPH ?g { <" + this.individualURI + "> <" + propertyURI + "> ?object} \n" + "WHERE{ <" + this.individualURI + "> <" + propertyURI + "> ?object } \n";
WebappDaoFactorySDB.getFilterBlock(graphVars, datasetMode) +
"}";
tempModel = QueryExecutionFactory.create(QueryFactory.create(valuesOfProperty), dataset).execConstruct(); tempModel = QueryExecutionFactory.create(QueryFactory.create(valuesOfProperty), dataset).execConstruct();
ontModel.add(tempModel.listStatements()); ontModel.add(tempModel.listStatements());
Resource ontRes = ontModel.getResource(this.individualURI); Resource ontRes = ontModel.getResource(this.individualURI);
@ -727,13 +717,10 @@ public class IndividualSDB extends IndividualImpl implements Individual {
Dataset dataset = w.getDataset(); Dataset dataset = w.getDataset();
dataset.getLock().enterCriticalSection(Lock.READ); dataset.getLock().enterCriticalSection(Lock.READ);
try { try {
String[] graphVars = { "?g" };
String valuesOfProperty = String valuesOfProperty =
"SELECT ?object" + "SELECT ?object" +
"WHERE{ GRAPH ?g { <" + this.individualURI + "> <" + "WHERE{ <" + this.individualURI + "> <" +
propertyURI + "> ?object} \n" + propertyURI + "> ?object } \n";
WebappDaoFactorySDB.getFilterBlock(graphVars, datasetMode) +
"}";
ResultSet values = QueryExecutionFactory.create( ResultSet values = QueryExecutionFactory.create(
QueryFactory.create(valuesOfProperty), dataset) QueryFactory.create(valuesOfProperty), dataset)
.execSelect(); .execSelect();
@ -767,13 +754,10 @@ public class IndividualSDB extends IndividualImpl implements Individual {
Dataset dataset = w.getDataset(); Dataset dataset = w.getDataset();
dataset.getLock().enterCriticalSection(Lock.READ); dataset.getLock().enterCriticalSection(Lock.READ);
try { try {
String[] graphVars = { "?g" };
String valueOfProperty = String valueOfProperty =
"SELECT ?object " + "SELECT ?object " +
"WHERE{ GRAPH ?g { <" + this.individualURI + "> <" + "WHERE{ <" + this.individualURI + "> <" +
propertyURI + "> ?object} \n" + propertyURI + "> ?object } \n";
WebappDaoFactorySDB.getFilterBlock(graphVars, datasetMode) +
"}";
QueryExecution qe = QueryExecutionFactory.create( QueryExecution qe = QueryExecutionFactory.create(
QueryFactory.create(valueOfProperty), dataset); QueryFactory.create(valueOfProperty), dataset);
try { try {

View file

@ -103,6 +103,7 @@ public class UserAccountsDaoJena extends JenaBaseDao implements UserAccountsDao
USERACCOUNT_EXTERNAL_AUTH_ID)); USERACCOUNT_EXTERNAL_AUTH_ID));
u.setPermissionSetUris(getPropertyResourceURIValues(r, u.setPermissionSetUris(getPropertyResourceURIValues(r,
USERACCOUNT_HAS_PERMISSION_SET)); USERACCOUNT_HAS_PERMISSION_SET));
u.setRootUser(isResourceOfType(r, USERACCOUNT_ROOT_USER));
return u; return u;
} finally { } finally {
getOntModel().leaveCriticalSection(); getOntModel().leaveCriticalSection();
@ -157,21 +158,6 @@ public class UserAccountsDaoJena extends JenaBaseDao implements UserAccountsDao
return getUserAccountByUri(userUri); 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 @Override
public String insertUserAccount(UserAccount userAccount) { public String insertUserAccount(UserAccount userAccount) {
if (userAccount == null) { if (userAccount == null) {
@ -214,6 +200,10 @@ public class UserAccountsDaoJena extends JenaBaseDao implements UserAccountsDao
USERACCOUNT_HAS_PERMISSION_SET, USERACCOUNT_HAS_PERMISSION_SET,
userAccount.getPermissionSetUris(), model); userAccount.getPermissionSetUris(), model);
if (userAccount.isRootUser()) {
model.add(res, RDF.type, USERACCOUNT_ROOT_USER);
}
userAccount.setUri(userUri); userAccount.setUri(userUri);
return userUri; return userUri;
} catch (InsertException e) { } catch (InsertException e) {
@ -268,6 +258,13 @@ public class UserAccountsDaoJena extends JenaBaseDao implements UserAccountsDao
updatePropertyResourceURIValues(res, updatePropertyResourceURIValues(res,
USERACCOUNT_HAS_PERMISSION_SET, USERACCOUNT_HAS_PERMISSION_SET,
userAccount.getPermissionSetUris(), model); userAccount.getPermissionSetUris(), model);
if (userAccount.isRootUser()) {
model.add(res, RDF.type, USERACCOUNT_ROOT_USER);
} else {
model.remove(res, RDF.type, USERACCOUNT_ROOT_USER);
}
} finally { } finally {
model.leaveCriticalSection(); model.leaveCriticalSection();
} }

View file

@ -2,6 +2,8 @@
package edu.cornell.mannlib.vitro.webapp.email; package edu.cornell.mannlib.vitro.webapp.email;
import java.io.IOException;
import java.io.StringWriter;
import java.io.UnsupportedEncodingException; import java.io.UnsupportedEncodingException;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collections; import java.util.Collections;
@ -21,22 +23,30 @@ import javax.mail.internet.InternetAddress;
import javax.mail.internet.MimeBodyPart; import javax.mail.internet.MimeBodyPart;
import javax.mail.internet.MimeMessage; import javax.mail.internet.MimeMessage;
import javax.mail.internet.MimeMultipart; import javax.mail.internet.MimeMultipart;
import javax.servlet.ServletContext;
import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletRequest;
import org.apache.commons.logging.Log; import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory; import org.apache.commons.logging.LogFactory;
import edu.cornell.mannlib.vitro.webapp.controller.freemarker.TemplateProcessingHelper; import edu.cornell.mannlib.vitro.webapp.controller.freemarker.FreemarkerHttpServlet;
import edu.cornell.mannlib.vitro.webapp.controller.freemarker.TemplateProcessingHelper.TemplateProcessingException; import edu.cornell.mannlib.vitro.webapp.web.directives.EmailDirective;
import freemarker.core.Environment;
import freemarker.template.Configuration; 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 * A framework that makes it simpler to send email messages with a body built
* from a Freemarker template. * from a Freemarker template.
* *
* In fact, the body can be plain text from a template, HTML from a template, or * The template must contain the @email directive, which may provide the subject
* both. * 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 { public class FreemarkerEmailMessage {
private static final Log log = LogFactory private static final Log log = LogFactory
@ -47,15 +57,15 @@ public class FreemarkerEmailMessage {
private final HttpServletRequest req; private final HttpServletRequest req;
private final Session session; private final Session session;
private final Configuration config; private final Configuration config;
private final ServletContext ctx;
private final List<Recipient> recipients = new ArrayList<Recipient>(); private final List<Recipient> recipients = new ArrayList<Recipient>();
private final InternetAddress replyToAddress; private final InternetAddress replyToAddress;
private InternetAddress fromAddress = null; private InternetAddress fromAddress = null;
private String subject = ""; private String subject = "";
private String htmlTemplateName; private String templateName = "";
private String textTemplateName; private String htmlContent = "";
private String textContent = "";
private Map<String, Object> bodyMap = Collections.emptyMap(); private Map<String, Object> bodyMap = Collections.emptyMap();
/** /**
@ -67,8 +77,6 @@ public class FreemarkerEmailMessage {
this.session = session; this.session = session;
this.replyToAddress = replyToAddress; this.replyToAddress = replyToAddress;
this.ctx = req.getSession().getServletContext();
Object o = req.getAttribute(ATTRIBUTE_NAME); Object o = req.getAttribute(ATTRIBUTE_NAME);
if (!(o instanceof Configuration)) { if (!(o instanceof Configuration)) {
String oClass = (o == null) ? "null" : o.getClass().getName(); String oClass = (o == null) ? "null" : o.getClass().getName();
@ -115,10 +123,6 @@ public class FreemarkerEmailMessage {
try { try {
recipients.add(new Recipient(type, emailAddress, personalName)); recipients.add(new Recipient(type, emailAddress, personalName));
} catch (AddressException e) {
log.warn("invalid recipient address: " + type + ", '"
+ emailAddress + "', personal name '" + personalName + "'");
return;
} catch (UnsupportedEncodingException e) { } catch (UnsupportedEncodingException e) {
log.warn("invalid recipient address: " + type + ", '" log.warn("invalid recipient address: " + type + ", '"
+ emailAddress + "', personal name '" + personalName + "'"); + emailAddress + "', personal name '" + personalName + "'");
@ -130,27 +134,41 @@ public class FreemarkerEmailMessage {
this.subject = nonNull(subject, ""); this.subject = nonNull(subject, "");
} }
public void setHtmlTemplate(String templateName) { public void setHtmlContent(String htmlContent) {
this.htmlTemplateName = nonNull(templateName, ""); this.htmlContent = nonNull(htmlContent, "");
} }
public void setTextTemplate(String templateName) { public void setTextContent(String textContent) {
this.textTemplateName = nonNull(templateName, ""); this.textContent = nonNull(textContent, "");
}
public void setTemplate(String templateName) {
this.templateName = nonNull(templateName, "");
} }
public void setBodyMap(Map<String, Object> body) { public void setBodyMap(Map<String, Object> body) {
if (body == null) { if (body == null) {
this.bodyMap = Collections.emptyMap(); this.bodyMap = Collections.emptyMap();
} else { } else {
this.bodyMap = Collections this.bodyMap = new HashMap<String, Object>(body);
.unmodifiableMap(new HashMap<String, Object>(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() { public void send() {
String textBody = figureMessageBody(textTemplateName);
String htmlBody = figureMessageBody(htmlTemplateName);
try { try {
MimeMessage msg = new MimeMessage(session); MimeMessage msg = new MimeMessage(session);
msg.setReplyTo(new Address[] { replyToAddress }); msg.setReplyTo(new Address[] { replyToAddress });
@ -167,19 +185,19 @@ public class FreemarkerEmailMessage {
msg.setSubject(subject); msg.setSubject(subject);
if (textBody.isEmpty()) { if (textContent.isEmpty()) {
if (htmlBody.isEmpty()) { if (htmlContent.isEmpty()) {
log.error("Message has neither text body nor HTML body"); log.error("Message has neither text body nor HTML body");
} else { } else {
msg.setContent(htmlBody, "text/html"); msg.setContent(htmlContent, "text/html");
} }
} else { } else {
if (htmlBody.isEmpty()) { if (htmlContent.isEmpty()) {
msg.setContent(textBody, "text/plain"); msg.setContent(textContent, "text/plain");
} else { } else {
MimeMultipart content = new MimeMultipart("alternative"); MimeMultipart content = new MimeMultipart("alternative");
addBodyPart(content, textBody, "text/plain"); addBodyPart(content, textContent, "text/plain");
addBodyPart(content, htmlBody, "text/html"); addBodyPart(content, htmlContent, "text/html");
msg.setContent(content); 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) private void addBodyPart(MimeMultipart content, String textBody, String type)
throws MessagingException { throws MessagingException {
MimeBodyPart bodyPart = new MimeBodyPart(); MimeBodyPart bodyPart = new MimeBodyPart();
@ -235,7 +233,7 @@ public class FreemarkerEmailMessage {
} }
public Recipient(RecipientType type, String address, String personalName) public Recipient(RecipientType type, String address, String personalName)
throws AddressException, UnsupportedEncodingException { throws UnsupportedEncodingException {
this.type = type; this.type = type;
this.address = new InternetAddress(address, personalName); this.address = new InternetAddress(address, personalName);
} }

View file

@ -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<String, Object> help(String name) {
Map<String, Object> map = new LinkedHashMap<String, Object>();
map.put("effect",
"Create an email message from the parameters set in the invoking template.");
Map<String, String> params = new HashMap<String, String>();
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<String> examples = new ArrayList<String>();
examples.add("&lt;email subject=\"Password reset confirmation\" html=html text=text&gt;");
examples.add("&lt;email html=html text=text&gt;");
map.put("examples", examples);
return map;
}
}

View file

@ -15,6 +15,7 @@ import org.apache.commons.logging.LogFactory;
import edu.cornell.mannlib.vitro.webapp.controller.freemarker.UrlBuilder; import edu.cornell.mannlib.vitro.webapp.controller.freemarker.UrlBuilder;
import freemarker.core.Environment; import freemarker.core.Environment;
import freemarker.template.SimpleScalar;
import freemarker.template.TemplateDirectiveBody; import freemarker.template.TemplateDirectiveBody;
import freemarker.template.TemplateException; import freemarker.template.TemplateException;
import freemarker.template.TemplateModel; import freemarker.template.TemplateModel;
@ -44,12 +45,19 @@ public class UrlDirective extends BaseTemplateDirectiveModel {
"The url directive doesn't allow nested content."); "The url directive doesn't allow nested content.");
} }
String path = params.get("path").toString(); Object o = params.get("path");
if (path == null) { if (o == null) {
throw new TemplateModelException( throw new TemplateModelException(
"The url directive requires a value for parameter 'path'."); "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("/")) { if (!path.startsWith("/")) {
throw new TemplateModelException( throw new TemplateModelException(
"The url directive requires that the value of parameter 'path' is an absolute path starting with '/'."); "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); out.write(url);
} }
@Override
public Map<String, Object> help(String name) { public Map<String, Object> help(String name) {
Map<String, Object> map = new LinkedHashMap<String, Object>(); Map<String, Object> map = new LinkedHashMap<String, Object>();
@ -78,5 +87,4 @@ public class UrlDirective extends BaseTemplateDirectiveModel {
return map; return map;
} }
} }

View file

@ -94,6 +94,7 @@ public class WidgetDirective extends BaseTemplateDirectiveModel {
} }
@Override
public Map<String, Object> help(String name) { public Map<String, Object> help(String name) {
Map<String, Object> map = new LinkedHashMap<String, Object>(); Map<String, Object> map = new LinkedHashMap<String, Object>();

View file

@ -32,13 +32,27 @@ public class User extends BaseTemplateModel {
return ""; return "";
} }
String firstName = currentUser.getFirstName(); if (currentUser.getFirstName().isEmpty()) {
String lastName = currentUser.getLastName();
if (firstName.isEmpty() && lastName.isEmpty()) {
return currentUser.getEmailAddress(); return currentUser.getEmailAddress();
} }
return firstName + " " + lastName; 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() { public boolean getHasSiteAdminAccess() {

View file

@ -6,6 +6,7 @@ import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory; import org.apache.commons.logging.LogFactory;
import edu.cornell.mannlib.vitro.webapp.beans.VClass; 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.ParamMap;
import edu.cornell.mannlib.vitro.webapp.controller.freemarker.UrlBuilder.Route; import edu.cornell.mannlib.vitro.webapp.controller.freemarker.UrlBuilder.Route;
@ -37,6 +38,7 @@ public class VClassTemplateModel extends BaseTemplateModel {
} }
public VClassGroupTemplateModel getGroup() { public VClassGroupTemplateModel getGroup() {
return new VClassGroupTemplateModel(vclass.getGroup()); VClassGroup group = vclass.getGroup();
return (group == null) ? null : new VClassGroupTemplateModel(vclass.getGroup());
} }
} }

View file

@ -2,26 +2,19 @@
package edu.cornell.mannlib.vitro.webapp.web.templatemodels.individual; package edu.cornell.mannlib.vitro.webapp.web.templatemodels.individual;
import java.util.List;
import org.apache.commons.logging.Log; import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory; import org.apache.commons.logging.LogFactory;
import org.openrdf.model.URI; import org.openrdf.model.URI;
import org.openrdf.model.impl.URIImpl; 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.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.auth.requestedAction.propstmt.EditDataPropStmt;
import edu.cornell.mannlib.vitro.webapp.beans.DataPropertyStatement; import edu.cornell.mannlib.vitro.webapp.beans.DataPropertyStatement;
import edu.cornell.mannlib.vitro.webapp.beans.DataPropertyStatementImpl; import edu.cornell.mannlib.vitro.webapp.beans.DataPropertyStatementImpl;
import edu.cornell.mannlib.vitro.webapp.controller.VitroRequest; 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.IndividualDao;
import edu.cornell.mannlib.vitro.webapp.dao.VitroVocabulary; import edu.cornell.mannlib.vitro.webapp.dao.VitroVocabulary;
import edu.cornell.mannlib.vitro.webapp.dao.WebappDaoFactory; 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.EditLiteral;
import edu.cornell.mannlib.vitro.webapp.edit.n3editing.processEdit.RdfLiteralHash; import edu.cornell.mannlib.vitro.webapp.edit.n3editing.processEdit.RdfLiteralHash;

View file

@ -12,7 +12,6 @@ import java.util.Arrays;
import java.util.Collections; import java.util.Collections;
import java.util.List; import java.util.List;
import org.junit.Before;
import org.junit.BeforeClass; import org.junit.BeforeClass;
import org.junit.Test; import org.junit.Test;
@ -91,6 +90,27 @@ public class UserAccountsSelectorTest extends AbstractTestClass {
Collections Collections
.singleton("http://vivo.mydomain.edu/individual/role2"), .singleton("http://vivo.mydomain.edu/individual/role2"),
acct.getPermissionSetUris()); 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.<String> emptySet(),
acct.getPermissionSetUris());
assertEquals("rootUser", true, acct.isRootUser());
} }
// ---------------------------------------------------------------------- // ----------------------------------------------------------------------

View file

@ -101,6 +101,7 @@ mydomain:user07
mydomain:user08 mydomain:user08
a auth:UserAccount ; a auth:UserAccount ;
a auth:RootUserAccount ;
auth:emailAddress "email@henry.edu" ; auth:emailAddress "email@henry.edu" ;
auth:firstName "Mary" ; auth:firstName "Mary" ;
auth:lastName "McInerney" ; auth:lastName "McInerney" ;

View file

@ -182,10 +182,4 @@ public class AuthenticatorStub extends Authenticator {
"AuthenticatorStub.accountRequiresEditing() not implemented."); "AuthenticatorStub.accountRequiresEditing() not implemented.");
} }
@Override
public void recordLoginWithoutUserAccount(String individualUri) {
throw new RuntimeException(
"AuthenticatorStub.recordLoginWithoutUserAccount() not implemented.");
}
} }

View file

@ -18,10 +18,13 @@ import java.util.HashSet;
import java.util.Map; import java.util.Map;
import java.util.Set; import java.util.Set;
import org.apache.log4j.Level;
import org.junit.Before; import org.junit.Before;
import org.junit.Ignore; import org.junit.Ignore;
import org.junit.Test; 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.UserAccountsDaoStub;
import stubs.edu.cornell.mannlib.vitro.webapp.dao.WebappDaoFactoryStub; import stubs.edu.cornell.mannlib.vitro.webapp.dao.WebappDaoFactoryStub;
import stubs.javax.servlet.ServletConfigStub; 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;
import edu.cornell.mannlib.vedit.beans.LoginStatusBean.AuthenticationSource; import edu.cornell.mannlib.vedit.beans.LoginStatusBean.AuthenticationSource;
import edu.cornell.mannlib.vitro.testing.AbstractTestClass; 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.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.Authenticator;
import edu.cornell.mannlib.vitro.webapp.controller.authenticate.AuthenticatorStub; import edu.cornell.mannlib.vitro.webapp.controller.authenticate.AuthenticatorStub;
import edu.cornell.mannlib.vitro.webapp.controller.login.LoginProcessBean; import edu.cornell.mannlib.vitro.webapp.controller.login.LoginProcessBean;
@ -46,6 +52,7 @@ public class AuthenticateTest extends AbstractTestClass {
private ServletContextStub servletContext; private ServletContextStub servletContext;
private WebappDaoFactoryStub webappDaoFactory; private WebappDaoFactoryStub webappDaoFactory;
private UserAccountsDaoStub userAccountsDao; private UserAccountsDaoStub userAccountsDao;
private IndividualDaoStub individualDao;
private ServletConfigStub servletConfig; private ServletConfigStub servletConfig;
private HttpSessionStub session; private HttpSessionStub session;
private HttpServletRequestStub request; private HttpServletRequestStub request;
@ -122,8 +129,11 @@ public class AuthenticateTest extends AbstractTestClass {
userAccountsDao.addUser(createUserFromUserInfo(OLD_SELF)); userAccountsDao.addUser(createUserFromUserInfo(OLD_SELF));
userAccountsDao.addUser(createUserFromUserInfo(OLD_STRANGER)); userAccountsDao.addUser(createUserFromUserInfo(OLD_STRANGER));
individualDao = new IndividualDaoStub();
webappDaoFactory = new WebappDaoFactoryStub(); webappDaoFactory = new WebappDaoFactoryStub();
webappDaoFactory.setUserAccountsDao(userAccountsDao); webappDaoFactory.setUserAccountsDao(userAccountsDao);
webappDaoFactory.setIndividualDao(individualDao);
servletContext = new ServletContextStub(); servletContext = new ServletContextStub();
servletContext.setAttribute("webappDaoFactory", webappDaoFactory); servletContext.setAttribute("webappDaoFactory", webappDaoFactory);
@ -143,6 +153,12 @@ public class AuthenticateTest extends AbstractTestClass {
auth = new Authenticate(); auth = new Authenticate();
auth.init(servletConfig); auth.init(servletConfig);
setLoggerLevel(ConfigurationProperties.class, Level.WARN);
new ConfigurationPropertiesStub().setBean(servletContext);
ActiveIdentifierBundleFactories.addFactory(servletContext,
new CommonIdentifierBundleFactory(servletContext));
} }
private UserAccount createUserFromUserInfo(UserInfo userInfo) { private UserAccount createUserFromUserInfo(UserInfo userInfo) {

View file

@ -48,11 +48,6 @@ public class UserAccountsDaoStub implements UserAccountsDao {
"UserAccountsDaoStub.getUserAccountByEmail() not implemented."); "UserAccountsDaoStub.getUserAccountByEmail() not implemented.");
} }
@Override
public boolean isRootUser(UserAccount userAccount) {
throw new RuntimeException("UserAccountsDao.isRootUser() not implemented.");
}
@Override @Override
public String insertUserAccount(UserAccount userAccount) { public String insertUserAccount(UserAccount userAccount) {
throw new RuntimeException( throw new RuntimeException(

View file

@ -359,4 +359,5 @@ div.sparqlform .parenthetical {
.note { .note {
font-size: .8em; font-size: .8em;
line-height: 1.3em; line-height: 1.3em;
color: #7f8993;
} }

View file

@ -193,14 +193,14 @@ ul#browse-classes a {
ul#alpha-browse-individuals { ul#alpha-browse-individuals {
float: left; float: left;
width: 619px; width: 619px;
padding-left: 10px; /* padding-left: 10px; */
list-style: none; list-style: none;
margin-left: 1px; /* margin-left: 1px; */
} }
ul#alpha-browse-individuals li { ul#alpha-browse-individuals li {
float: left; float: left;
margin-right: 4px; margin-right: 4px;
padding-top: 8px; padding-top: 0px;
} }
ul#alpha-browse-individuals li:last-child { ul#alpha-browse-individuals li:last-child {
border-bottom: 0; border-bottom: 0;
@ -212,6 +212,11 @@ ul#alpha-browse-individuals a {
padding-left: 8px; padding-left: 8px;
width: 10px; width: 10px;
} }
h4#selectedClassHeading {
margin-left:4px;
margin-top:-5px;
margin-bottom:-10px;
}
/* BROWSE INDIVIDUALS------> */ /* BROWSE INDIVIDUALS------> */
#individuals-in-class { #individuals-in-class {
float: right; float: right;

View file

@ -74,3 +74,42 @@
height:620px; height:620px;
overflow:visible; 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;
}

View file

@ -119,10 +119,10 @@
Model model = (Model)application.getAttribute("jenaOntModel"); Model model = (Model)application.getAttribute("jenaOntModel");
editConfig.prepareForDataPropUpdate(model,dps); editConfig.prepareForDataPropUpdate(model,dps);
formTitle = "Change text for: <em>"+prop.getPublicName()+"</em>"; formTitle = "Change text for: <em>"+prop.getPublicName()+"</em>";
submitLabel = "save change"; submitLabel = "Save change";
} else { } else {
formTitle = "Add new entry for: <em>"+prop.getPublicName()+"</em>"; formTitle = "Add new entry for: <em>"+prop.getPublicName()+"</em>";
submitLabel = "save entry"; submitLabel = "Save entry";
} }
%> %>

View file

@ -111,10 +111,10 @@
if ( prop.getOfferCreateNewOption() ) { if ( prop.getOfferCreateNewOption() ) {
log.debug("property set to offer \"create new\" option; custom form: ["+prop.getCustomEntryForm()+"]"); log.debug("property set to offer \"create new\" option; custom form: ["+prop.getCustomEntryForm()+"]");
formTitle = "Select an existing "+rangeClass.getName()+" for "+subject.getName(); formTitle = "Select an existing "+rangeClass.getName()+" for "+subject.getName();
submitLabel = "select existing"; submitLabel = "Select existing";
} else { } else {
formTitle = "Add an entry to: <em>"+prop.getDomainPublic()+"</em>"; formTitle = "Add an entry to: <em>"+prop.getDomainPublic()+"</em>";
submitLabel = "save entry"; submitLabel = "Save entry";
} }
} }
%> %>
@ -174,7 +174,7 @@ $(document).ready(function() {
<v:input type="submit" id="submit" value="<%=submitLabel%>" cancel="true"/> <v:input type="submit" id="submit" value="<%=submitLabel%>" cancel="true"/>
<c:if test="${predicate.offerCreateNewOption == true}"> <c:if test="${predicate.offerCreateNewOption == true}">
<p>If you don't find the appropriate entry on the selection list, <p>If you don't find the appropriate entry on the selection list,
<button type="button" onclick="javascript:document.location.href='${createNewUrl}'">add a new item to this list</button> <button type="button" onclick="javascript:document.location.href='${createNewUrl}'">Add a new item to this list</button>
</p> </p>
</c:if> </c:if>
</form> </form>

View file

@ -137,10 +137,10 @@
Model model = (Model)application.getAttribute("jenaOntModel"); Model model = (Model)application.getAttribute("jenaOntModel");
editConfig.prepareForDataPropUpdate(model,dps); editConfig.prepareForDataPropUpdate(model,dps);
formTitle = "Change text for: <em>"+prop.getPublicName()+"</em>"; formTitle = "Change text for: <em>"+prop.getPublicName()+"</em>";
submitLabel = "save change"; submitLabel = "Save change";
} else { } else {
formTitle ="Add new entry for: <em>"+prop.getPublicName()+"</em>"; formTitle ="Add new entry for: <em>"+prop.getPublicName()+"</em>";
submitLabel ="save entry"; submitLabel ="Save entry";
} }
%> %>

View file

@ -128,10 +128,10 @@
log.debug("property set to offer \"create new\" option; custom form: ["+prop.getCustomEntryForm()+"]"); log.debug("property set to offer \"create new\" option; custom form: ["+prop.getCustomEntryForm()+"]");
formTitle = "Select an existing "+classOfObjectFillers.getName()+" for "+subject.getName(); formTitle = "Select an existing "+classOfObjectFillers.getName()+" for "+subject.getName();
submitLabel = "select existing"; submitLabel = "Select existing";
} else { } else {
formTitle = "Add an entry to: <em>"+prop.getDomainPublic()+"</em>"; formTitle = "Add an entry to: <em>"+prop.getDomainPublic()+"</em>";
submitLabel = "save entry"; submitLabel = "Save entry";
} }
} }
@ -193,7 +193,7 @@
<input type="hidden" value="${param.objectUri}" name="objectUri"/> <input type="hidden" value="${param.objectUri}" name="objectUri"/>
<input type="hidden" value="create" name="cmd"/> <input type="hidden" value="create" name="cmd"/>
<v:input type="typesForCreateNew" id="typeOfNew" /> <v:input type="typesForCreateNew" id="typeOfNew" />
<v:input type="submit" id="submit" value="add a new item of this type" cancel="${offerCancel}"/> <v:input type="submit" id="submit" value="Add a new item of this type" cancel="${offerCancel}"/>
</form> </form>
</c:if> </c:if>

View file

@ -6,7 +6,6 @@ function changeAction(form, url) {
return true; return true;
} }
$(document).ready(function(){ $(document).ready(function(){
//Accounts per page //Accounts per page

View file

@ -140,8 +140,9 @@ var browseByVClass = {
browseByVClass.pagination(pages, page); browseByVClass.pagination(pages, page);
} }
selectedClassHeading = '<h3 class="selected-class">'+ results.vclass.name +'</h3>'; // selectedClassHeading = '<h3 class="selected-class">'+ results.vclass.name +'</h3>'; NIHVIVO-2483 tlw72
browseByVClass.individualsContainer.prepend(selectedClassHeading); // browseByVClass.individualsContainer.prepend(selectedClassHeading);
$('h4#selectedClassHeading').text(results.vclass.name); // NIHVIVO-2483 tlw72
// Set selected class, alpha and page // Set selected class, alpha and page
browseByVClass.selectedVClass(results.vclass.URI); browseByVClass.selectedVClass(results.vclass.URI);

View file

@ -34,13 +34,13 @@
<c:set var="query" <c:set var="query"
value="PREFIX rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#> value="PREFIX rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#>
PREFIX rdfs: <http://www.w3.org/2000/01/rdf-schema#> PREFIX rdfs: <http://www.w3.org/2000/01/rdf-schema#>
SELECT ?pred ?predLabel ?obj ?objLabel SELECT ?pred ?predLabel ?obj ?objLabel ?graph
WHERE WHERE
{ {
{ GRAPH ?g { <${entity.URI}> ?pred ?obj} } { GRAPH ?graph { <${entity.URI}> ?pred ?obj} }
OPTIONAL { GRAPH ?h { ?obj rdfs:label ?objLabel } } OPTIONAL { GRAPH ?h { ?obj rdfs:label ?objLabel } }
OPTIONAL { GRAPH ?i { ?pred rdfs:label ?predLabel } } OPTIONAL { GRAPH ?i { ?pred rdfs:label ?predLabel } }
} } ORDER BY ?graph ?pred
limit 10000"/> limit 10000"/>
<form action="admin/sparqlquery" method="get"> <form action="admin/sparqlquery" method="get">
<input type="hidden" name="query" value="${query}"/> <input type="hidden" name="query" value="${query}"/>
@ -51,13 +51,13 @@
<c:set var="query" <c:set var="query"
value="PREFIX rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#> value="PREFIX rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#>
PREFIX rdfs: <http://www.w3.org/2000/01/rdf-schema#> PREFIX rdfs: <http://www.w3.org/2000/01/rdf-schema#>
SELECT ?sub ?subL ?pred ?predLabel SELECT ?sub ?subL ?pred ?predLabel ?graph
WHERE WHERE
{ {
{ GRAPH ?g { ?sub ?pred <${entity.URI}> } } { GRAPH ?graph { ?sub ?pred <${entity.URI}> } }
OPTIONAL { GRAPH ?h { ?sub rdfs:label ?subL } } OPTIONAL { GRAPH ?h { ?sub rdfs:label ?subL } }
OPTIONAL { GRAPH ?i { ?pred rdfs:label ?predLabel } } OPTIONAL { GRAPH ?i { ?pred rdfs:label ?predLabel } }
} } ORDER BY ?graph ?pred
limit 10000"/> limit 10000"/>
<form action="admin/sparqlquery" method="get"> <form action="admin/sparqlquery" method="get">
<input type="hidden" name="query" value="${query}"/> <input type="hidden" name="query" value="${query}"/>

View file

@ -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!

View file

@ -2,9 +2,12 @@
<#-- Confirmation that an account has been created. --> <#-- Confirmation that an account has been created. -->
<#assign subject = "Your VIVO account has been created." />
<#assign html>
<html> <html>
<head> <head>
<title>${subjectLine}</title> <title>${subject}</title>
</head> </head>
<body> <body>
<p> <p>
@ -41,3 +44,25 @@
</p> </p>
</body> </body>
</html> </html>
</#assign>
<#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!
</#assign>
<@email subject=subject html=html text=text />

View file

@ -2,7 +2,7 @@
<#-- Template for adding a user account --> <#-- Template for adding a user account -->
<h3>Add new account</h3> <h3><a class="account-menu" href="accountsAdmin">User accounts</a> > Add new account</h3>
<#if errorEmailIsEmpty??> <#if errorEmailIsEmpty??>
<#assign errorMessage = "You must supply an email address." /> <#assign errorMessage = "You must supply an email address." />
@ -16,6 +16,10 @@
<#assign errorMessage = "'${emailAddress}' is not a valid email address." /> <#assign errorMessage = "'${emailAddress}' is not a valid email address." />
</#if> </#if>
<#if errorExternalAuthIdInUse??>
<#assign errorMessage = "An account with that external authorization ID already exists." />
</#if>
<#if errorFirstNameIsEmpty??> <#if errorFirstNameIsEmpty??>
<#assign errorMessage = "You must supply a first name." /> <#assign errorMessage = "You must supply a first name." />
</#if> </#if>
@ -61,9 +65,12 @@
<label for="last-name">Last name<span class="requiredHint"> *</span></label> <label for="last-name">Last name<span class="requiredHint"> *</span></label>
<input type="text" name="lastName" value="${lastName}" id="last-name" role="input "/> <input type="text" name="lastName" value="${lastName}" id="last-name" role="input "/>
<label for="external-auth-id">External authorization ID (optional)</label>
<input type="text" name="externalAuthId" value="${externalAuthId}" id="external-auth-id" role="input "/>
<p>Roles<span class="requiredHint"> *</span> </p> <p>Roles<span class="requiredHint"> *</span> </p>
<#list roles as role> <#list roles as role>
<input type="radio" name="role" value="${role.uri}" role="radio" <#if selectedRole = role.uri>selected</#if> /> <input type="radio" name="role" value="${role.uri}" role="radio" <#if selectedRole = role.uri>checked</#if> />
<label class="inline" for="${role.label}"> ${role.label}</label> <label class="inline" for="${role.label}"> ${role.label}</label>
<br /> <br />
</#list> </#list>
@ -93,7 +100,7 @@
</p> </p>
</#if> </#if>
<input type="submit" name="submitAdd" value="Add new account" class="submit"/> or <a href="${formUrls.list}">Cancel</a> <input type="submit" name="submitAdd" value="Add new account" class="submit"/> or <a class="cancel" href="${formUrls.list}">Cancel</a>
<p class="requiredHint">* required fields</p> <p class="requiredHint">* required fields</p>
</form> </form>

View file

@ -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.

View file

@ -2,9 +2,12 @@
<#-- Confirmation that the user has changed his email account. --> <#-- Confirmation that the user has changed his email account. -->
<#assign subject = "Your VIVO email account has been changed." />
<#assign html>
<html> <html>
<head> <head>
<title>${subjectLine}</title> <title>${subject}</title>
</head> </head>
<body> <body>
<p> <p>
@ -21,3 +24,15 @@
</p> </p>
</body> </body>
</html> </html>
</#assign>
<#assign text>
Hi, ${userAccount.firstName} ${userAccount.lastName}
You recently changed the email address associated with
${userAccount.firstName} ${userAccount.lastName}
Thank you.
</#assign>
<@email subject=subject html=html text=text />

View file

@ -3,7 +3,6 @@
<#-- Template for adding a user account --> <#-- Template for adding a user account -->
<h3>Create your Password</h3> <h3>Create your Password</h3>
<#if errorPasswordIsEmpty??> <#if errorPasswordIsEmpty??>
<#assign errorMessage = "No password supplied." /> <#assign errorMessage = "No password supplied." />
</#if> </#if>
@ -24,26 +23,24 @@
</#if> </#if>
<section id="create-password" role="region"> <section id="create-password" role="region">
<fieldset> <p>Please enter your new password for ${userAccount.emailAddress}</p>
<legend>Please enter your new password for ${userAccount.emailAddress}</legend>
<form method="POST" action="${formUrls.createPassword}" class="customForm" role="create password"> <form method="POST" action="${formUrls.createPassword}" class="customForm" role="create password">
<input type="hidden" name="user" value="${userAccount.emailAddress}" /> <input type="hidden" name="user" value="${userAccount.emailAddress}" role="input" />
<input type="hidden" name="key" value="${userAccount.passwordLinkExpiresHash}" /> <input type="hidden" name="key" value="${userAccount.passwordLinkExpiresHash}" role="input" />
<label for="new-password">Password<span class="requiredHint"> *</span></label> <label for="new-password">Password<span class="requiredHint"> *</span></label>
<input type="password" name="newPassword" value="${newPassword}" id="new-password" role="input" /> <input type="password" name="newPassword" value="${newPassword}" id="new-password" role="input" />
<p>Minimum of ${minimumLength} characters in length.</p> <p class="note">Minimum of ${minimumLength} characters in length.</p>
<label for="confirm-password">Confirm Password<span class="requiredHint"> *</span></label> <label for="confirm-password">Confirm Password<span class="requiredHint"> *</span></label>
<input type="password" name="confirmPassword" value="${confirmPassword}" id="confirm-password" role="input" /> <input type="password" name="confirmPassword" value="${confirmPassword}" id="confirm-password" role="input" />
<input type="submit" name="submit" value="Save changes" class="submit"/> <p><input type="submit" name="submit" value="Save changes" class="submit"/></p>
<p class="requiredHint">* required fields</p> <p class="requiredHint">* required fields</p>
</form> </form>
</fieldset>
</section> </section>
${stylesheets.add('<link rel="stylesheet" href="${urls.base}/edit/forms/css/customForm.css" />')} ${stylesheets.add('<link rel="stylesheet" href="${urls.base}/edit/forms/css/customForm.css" />')}

View file

@ -2,7 +2,7 @@
<#-- Template for editing a user account --> <#-- Template for editing a user account -->
<h3>Edit account</h3> <h3><a class="account-menu" href="accountsAdmin">User accounts</a> > Edit account</h3>
<#if errorEmailIsEmpty??> <#if errorEmailIsEmpty??>
<#assign errorMessage = "You must supply an email address." /> <#assign errorMessage = "You must supply an email address." />
@ -16,6 +16,10 @@
<#assign errorMessage = "'${emailAddress}' is not a valid email address." /> <#assign errorMessage = "'${emailAddress}' is not a valid email address." />
</#if> </#if>
<#if errorExternalAuthIdInUse??>
<#assign errorMessage = "An account with that external authorization ID already exists." />
</#if>
<#if errorFirstNameIsEmpty??> <#if errorFirstNameIsEmpty??>
<#assign errorMessage = "You must supply a first name." /> <#assign errorMessage = "You must supply a first name." />
</#if> </#if>
@ -61,12 +65,17 @@
<label for="last-name">Last name<span class="requiredHint"> *</span></label> <label for="last-name">Last name<span class="requiredHint"> *</span></label>
<input type="text" name="lastName" value="${lastName}" id="last-name" role="input" /> <input type="text" name="lastName" value="${lastName}" id="last-name" role="input" />
<label for="external-auth-id">External authorization ID (optional)</label>
<input type="text" name="externalAuthId" value="${externalAuthId}" id="external-auth-id" role="input "/>
<#if roles?has_content>
<p>Roles<span class="requiredHint"> *</span> </p> <p>Roles<span class="requiredHint"> *</span> </p>
<#list roles as role> <#list roles as role>
<input type="radio" name="role" value="${role.uri}" role="radio" <#if selectedRole = role.uri>selected</#if> /> <input type="radio" name="role" value="${role.uri}" role="radio" <#if selectedRole = role.uri>checked</#if> />
<label class="inline" for="${role.label}"> ${role.label}</label> <label class="inline" for="${role.label}"> ${role.label}</label>
<br /> <br />
</#list> </#list>
</#if>
<#if !emailIsEnabled??> <#if !emailIsEnabled??>
<label for="new-password">New password<span class="requiredHint"> *</span></label> <label for="new-password">New password<span class="requiredHint"> *</span></label>
@ -87,18 +96,18 @@
<label class="inline" for="no-associate"> No</label> <label class="inline" for="no-associate"> No</label>
<br /> <br />
<input type="checkbox" name="resetPassword" value="" id="reset-password" role="checkbox" /> <input type="checkbox" name="resetPassword" value="" id="reset-password" role="checkbox" <#if resetPassword??>checked</#if> />
<label class="inline" for="reset-password"> Reset password</label> <label class="inline" for="reset-password"> Reset password</label>
<#if emailIsEnabled??> <#if emailIsEnabled??>
<p class="note"> <p class="note">
Note: A confirmation email with instructions for resetting a password Note: Instructions for resetting the password will
will be sent to the address entered above. be emailed to the address entered above. The password will not
The password will not be reset until the user follows the link provided in this email. be reset until the user follows the link provided in this email.
</p> </p>
</#if> </#if>
<input type="submit" name="submitEdit" value="Save changes" class="submit"/> or <a href="${formUrls.list}">Cancel</a> <input type="submit" name="submitEdit" value="Save changes" class="submit" /> or <a class="cancel" href="${formUrls.list}">Cancel</a>
<p class="requiredHint">* required fields</p> <p class="requiredHint">* required fields</p>
</form> </form>

View file

@ -38,7 +38,8 @@
</p> </p>
<form method="POST" action="${formUrls.firstTimeExternal}" class="customForm" role="my account"> <form method="POST" action="${formUrls.firstTimeExternal}" class="customForm" role="my account">
<input type="hidden" name="externalAuthId" value="${externalAuthId}" /> <input type="hidden" name="externalAuthId" value="${externalAuthId}" role="input" />
<input type="hidden" name="afterLoginUrl" value="${afterLoginUrl}" role="input" />
<label for="first-name">First name<span class="requiredHint"> *</span></label> <label for="first-name">First name<span class="requiredHint"> *</span></label>
<input type="text" name="firstName" value="${firstName}" id="first-name" role="input" /> <input type="text" name="firstName" value="${firstName}" id="first-name" role="input" />
@ -56,7 +57,7 @@
</p> </p>
</#if> </#if>
<input type="submit" name="submit" value="Create account" class="submit"/> or <a href="${urls.home}">Cancel</a> <input type="submit" name="submit" value="Create account" class="submit"/> or <a class="cancel" href="${urls.home}">Cancel</a>
</form> </form>
</fieldset> </fieldset>
</section> </section>

View file

@ -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!

View file

@ -2,9 +2,12 @@
<#-- Confirmation that an account has been created for an externally-authenticated user. --> <#-- Confirmation that an account has been created for an externally-authenticated user. -->
<#assign subject = "Your VIVO account has been created." />
<#assign html>
<html> <html>
<head> <head>
<title>${subjectLine}</title> <title>${subject}</title>
</head> </head>
<body> <body>
<p> <p>
@ -24,3 +27,17 @@
</p> </p>
</body> </body>
</html> </html>
</#assign>
<#assign text>
${userAccount.firstName} ${userAccount.lastName}
Congratulations!
We have created your new VIVO account associated with
${userAccount.emailAddress}.
Thanks!
</#assign>
<@email subject=subject html=html text=text />

View file

@ -2,7 +2,7 @@
<#-- Template for editing a user account --> <#-- Template for editing a user account -->
<h3>Edit account</h3> <h3>My account</h3>
<#if errorEmailIsEmpty??> <#if errorEmailIsEmpty??>
<#assign errorMessage = "You must supply an email address." /> <#assign errorMessage = "You must supply an email address." />
@ -43,7 +43,6 @@
</section> </section>
</#if> </#if>
<#if confirmChange??> <#if confirmChange??>
<#assign confirmMessage = "Your changes have been saved." /> <#assign confirmMessage = "Your changes have been saved." />
</#if> </#if>
@ -67,7 +66,7 @@
<label for="email-address">Email address<span class="requiredHint"> *</span></label> <label for="email-address">Email address<span class="requiredHint"> *</span></label>
<input type="text" name="emailAddress" value="${emailAddress}" id="email-address" role="input" /> <input type="text" name="emailAddress" value="${emailAddress}" id="email-address" role="input" />
<p>Note: if email changes, a confirmation email will be sent to the new email address entered above.</p> <p class="note">Note: if email changes, a confirmation email will be sent to the new email address entered above.</p>
<label for="first-name">First name<span class="requiredHint"> *</span></label> <label for="first-name">First name<span class="requiredHint"> *</span></label>
<input type="text" name="firstName" value="${firstName}" id="first-name" role="input" /> <input type="text" name="firstName" value="${firstName}" id="first-name" role="input" />
@ -76,17 +75,16 @@
<input type="text" name="lastName" value="${lastName}" id="last-name" role="input" /> <input type="text" name="lastName" value="${lastName}" id="last-name" role="input" />
<#if !externalAuth??> <#if !externalAuth??>
<label for="new-password">New password<span class="requiredHint"> *</span></label> <label for="new-password">New password</label>
<input type="password" name="newPassword" value="${newPassword}" id="new-password" role="input" /> <input type="password" name="newPassword" value="${newPassword}" id="new-password" role="input" />
<p>Minimum of ${minimumLength} characters in length.</p> <p class="note">Minimum of ${minimumLength} characters in length. Leaving this blank means that the password will not be changed.</p>
<p>Leaving this blank means that the password will not be changed.</p>
<label for="confirm-password">Confirm initial password<span class="requiredHint"> *</span></label> <label for="confirm-password">Confirm new password</label>
<input type="text" name="confirmPassword" value="${confirmPassword}" id="confirm-password" role="input "/> <input type="password" name="confirmPassword" value="${confirmPassword}" id="confirm-password" role="input" />
</#if> </#if>
<input type="submit" name="submitMyAccount" value="Save changes" class="submit"/> <p><input type="submit" name="submitMyAccount" value="Save changes" class="submit" /></p>
<p class="requiredHint">* required fields</p> <p class="requiredHint">* required fields</p>
</form> </form>

View file

@ -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.

View file

@ -2,9 +2,12 @@
<#-- Confirmation that an password has been created. --> <#-- Confirmation that an password has been created. -->
<#assign subject = "Password successfully created." />
<#assign html>
<html> <html>
<head> <head>
<title>${subjectLine}</title> <title>${subject}</title>
</head> </head>
<body> <body>
<p> <p>
@ -24,3 +27,17 @@
</p> </p>
</body> </body>
</html> </html>
</#assign>
<#assign text>
${userAccount.firstName} ${userAccount.lastName}
Password successfully created.
Your new password associated with ${userAccount.emailAddress}
has been created.
Thank you.
</#assign>
<@email subject=subject html=html text=text />

View file

@ -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.

View file

@ -1,10 +1,13 @@
<#-- $This file is distributed under the terms of the license in /doc/license.txt$ --> <#-- $This file is distributed under the terms of the license in /doc/license.txt$ -->
<#-- Confirmation that an password has been reset. --> <#-- Confirmation that a password has been reset. -->
<#assign subject = "Password changed." />
<#assign html>
<html> <html>
<head> <head>
<title>${subjectLine}</title> <title>${subject}</title>
</head> </head>
<body> <body>
<p> <p>
@ -24,3 +27,17 @@
</p> </p>
</body> </body>
</html> </html>
</#assign>
<#assign text>
${userAccount.firstName} ${userAccount.lastName}
Password successfully changed.
Your new password associated with ${userAccount.emailAddress}
has been changed.
Thank you.
</#assign>
<@email subject=subject html=html text=text />

View file

@ -4,6 +4,8 @@
<h3>Reset your Password</h3> <h3>Reset your Password</h3>
<p>Please enter your new password for ${userAccount.emailAddress}</p>
<#if errorPasswordIsEmpty??> <#if errorPasswordIsEmpty??>
<#assign errorMessage = "No password supplied." /> <#assign errorMessage = "No password supplied." />
</#if> </#if>
@ -24,9 +26,6 @@
</#if> </#if>
<section id="reset-password" role="region"> <section id="reset-password" role="region">
<fieldset>
<legend>Please enter your new password for ${userAccount.emailAddress}</legend>
<form method="POST" action="${formUrls.resetPassword}" class="customForm" role="create password"> <form method="POST" action="${formUrls.resetPassword}" class="customForm" role="create password">
<input type="hidden" name="user" value="${userAccount.emailAddress}" /> <input type="hidden" name="user" value="${userAccount.emailAddress}" />
<input type="hidden" name="key" value="${userAccount.passwordLinkExpiresHash}" /> <input type="hidden" name="key" value="${userAccount.passwordLinkExpiresHash}" />
@ -34,16 +33,15 @@
<label for="new-password">Password<span class="requiredHint"> *</span></label> <label for="new-password">Password<span class="requiredHint"> *</span></label>
<input type="password" name="newPassword" value="${newPassword}" id="new-password" role="input" /> <input type="password" name="newPassword" value="${newPassword}" id="new-password" role="input" />
<p>Minimum of ${minimumLength} characters in length.</p> <p class="note">Minimum of ${minimumLength} characters in length.</p>
<label for="confirm-password">Confirm Password<span class="requiredHint"> *</span></label> <label for="confirm-password">Confirm Password<span class="requiredHint"> *</span></label>
<input type="password" name="confirmPassword" value="${confirmPassword}" id="confirm-password" role="input" /> <input type="password" name="confirmPassword" value="${confirmPassword}" id="confirm-password" role="input" />
<input type="submit" name="submit" value="Save changes" class="submit"/> <p><input type="submit" name="submit" value="Save changes" class="submit" /></p>
<p class="requiredHint">* required fields</p> <p class="requiredHint">* required fields</p>
</form> </form>
</fieldset>
</section> </section>
${stylesheets.add('<link rel="stylesheet" href="${urls.base}/edit/forms/css/customForm.css" />')} ${stylesheets.add('<link rel="stylesheet" href="${urls.base}/edit/forms/css/customForm.css" />')}

View file

@ -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. -->
<html>
<head>
<title>${subjectLine}</title>
</head>
<body>
<p>
${userAccount.firstName} ${userAccount.lastName}
</p>
<p>
We received a request to reset the password for your account (${userAccount.emailAddress}).
Please follow the instructions below to proceed with your password reset.
</p>
<p>
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.
</p>
<p>
Click the link below to reset your password using our secure server.
</p>
<p>
<a href="${passwordLink}">${passwordLink}</a>
</p>
<p>
If the link above doesn't work, you can copy and paste the link directly into your browser's address bar.
</p>
<p>
Thank you!
</p>
</body>
</html>

View file

@ -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!

View file

@ -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>
<html>
<head>
<title>${subject}</title>
</head>
<body>
<p>
Dear ${userAccount.firstName} ${userAccount.lastName}:
</p>
<p>
We have received a request to reset the password for your ${siteName} account (${userAccount.emailAddress}).
</p>
<p>
Please follow the instructions below to proceed with your password reset.
</p>
<p>
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.
</p>
<p>
Click on the link below or paste it into your browser's address bar to reset your password
using our secure server.
</p>
<p>${passwordLink}</p>
<p>Thank you!</p>
</body>
</html>
</#assign>
<#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!
</#assign>
<@email subject=subject html=html text=text />

View file

@ -20,7 +20,8 @@
</#if> </#if>
</#list> </#list>
</ul> </ul>
<nav role="navigation"> <nav id="alpha-browse-container" role="navigation">
<h4 id="selectedClassHeading"></h4>
<#assign alphabet = ["A", "B", "C", "D", "E", "F", "G" "H", "I", "J", "K", "L", "M", "N", "O", "P", "Q", "R", "S", "T", "U", "V", "W", "X", "Y", "Z"] /> <#assign alphabet = ["A", "B", "C", "D", "E", "F", "G" "H", "I", "J", "K", "L", "M", "N", "O", "P", "Q", "R", "S", "T", "U", "V", "W", "X", "Y", "Z"] />
<ul id="alpha-browse-individuals"> <ul id="alpha-browse-individuals">
<li><a href="#" class="selected" data-alpha="all">All</a></li> <li><a href="#" class="selected" data-alpha="all">All</a></li>

View file

@ -13,19 +13,23 @@
<#-- Refinement links --> <#-- Refinement links -->
<#if classGroupLinks?has_content> <#if classGroupLinks?has_content>
<div class="searchTOC"> <div class="searchTOC">
<span class="jumpText">Show only results of this <b>type</b>:</span> <h4>Display only</h4>
<ul>
<#list classGroupLinks as link> <#list classGroupLinks as link>
<a href="${link.url}">${link.text}</a> <li><a href="${link.url}">${link.text}</a></li>
</#list> </#list>
</ul>
</div> </div>
</#if> </#if>
<#if classLinks?has_content> <#if classLinks?has_content>
<div class="searchTOC"> <div class="searchTOC">
<span class="jumpText">Show only results of this <b>subtype</b>:</span> <h4>Limit ${classGroupName} to</h4>
<ul>
<#list classLinks as link> <#list classLinks as link>
<a href="${link.url}">${link.text}</a> <li><a href="${link.url}">${link.text}</a></li>
</#list> </#list>
</ul>
</div> </div>
</#if> </#if>