From ac97b5792ad32cbf939e709ccd1d5525e759ec2c Mon Sep 17 00:00:00 2001 From: j2blake Date: Fri, 10 Jun 2011 18:53:35 +0000 Subject: [PATCH 1/7] NIHVIVO-2696 Refactor LoginRedirector to make it more flexible - usable from Freemarker controllers. --- .../authenticate/LoginExternalAuthReturn.java | 8 +- .../authenticate/LoginRedirector.java | 80 ++++++++++--------- .../webapp/controller/edit/Authenticate.java | 4 +- 3 files changed, 49 insertions(+), 43 deletions(-) diff --git a/webapp/src/edu/cornell/mannlib/vitro/webapp/controller/authenticate/LoginExternalAuthReturn.java b/webapp/src/edu/cornell/mannlib/vitro/webapp/controller/authenticate/LoginExternalAuthReturn.java index b4eca1079..8b525f6fe 100644 --- a/webapp/src/edu/cornell/mannlib/vitro/webapp/controller/authenticate/LoginExternalAuthReturn.java +++ b/webapp/src/edu/cornell/mannlib/vitro/webapp/controller/authenticate/LoginExternalAuthReturn.java @@ -57,7 +57,7 @@ public class LoginExternalAuthReturn extends BaseLoginServlet { getAuthenticator(req).recordLoginAgainstUserAccount(userAccount, AuthenticationSource.EXTERNAL); removeLoginProcessArtifacts(req); - new LoginRedirector(req, resp).redirectLoggedInUser(); + new LoginRedirector(req).redirectLoggedInUser(resp); return; } @@ -71,14 +71,14 @@ public class LoginExternalAuthReturn extends BaseLoginServlet { getAuthenticator(req).recordLoginWithoutUserAccount(uri); removeLoginProcessArtifacts(req); - new LoginRedirector(req, resp).redirectLoggedInUser(); + new LoginRedirector(req).redirectLoggedInUser(resp); return; } log.debug("User is not recognized: " + externalAuthId); removeLoginProcessArtifacts(req); - new LoginRedirector(req, resp) - .redirectUnrecognizedExternalUser(externalAuthId); + new LoginRedirector(req).redirectUnrecognizedExternalUser(resp, + externalAuthId); } private void removeLoginProcessArtifacts(HttpServletRequest req) { diff --git a/webapp/src/edu/cornell/mannlib/vitro/webapp/controller/authenticate/LoginRedirector.java b/webapp/src/edu/cornell/mannlib/vitro/webapp/controller/authenticate/LoginRedirector.java index d0746ad92..1853c04f0 100644 --- a/webapp/src/edu/cornell/mannlib/vitro/webapp/controller/authenticate/LoginRedirector.java +++ b/webapp/src/edu/cornell/mannlib/vitro/webapp/controller/authenticate/LoginRedirector.java @@ -29,17 +29,14 @@ public class LoginRedirector { private static final Log log = LogFactory.getLog(LoginRedirector.class); private final HttpServletRequest request; - private final HttpServletResponse response; private final HttpSession session; private final String uriOfAssociatedIndividual; private final String afterLoginPage; - public LoginRedirector(HttpServletRequest request, - HttpServletResponse response) { + public LoginRedirector(HttpServletRequest request) { this.request = request; this.session = request.getSession(); - this.response = response; uriOfAssociatedIndividual = getAssociatedIndividualUri(); @@ -70,28 +67,45 @@ public class LoginRedirector { } } - public void redirectLoggedInUser() throws IOException { - DisplayMessage.setMessage(request, assembleWelcomeMessage()); - - try { - if (isSelfEditorWithIndividual()) { - log.debug("Going to Individual home page."); - response.sendRedirect(getAssociatedIndividualHomePage()); - } else if (isMerelySelfEditor()) { - log.debug("User not recognized. Going to application home."); - response.sendRedirect(getApplicationHomePageUrl()); + public String getRedirectionUriForLoggedInUser() { + if (isSelfEditorWithIndividual()) { + log.debug("Going to Individual home page."); + return getAssociatedIndividualHomePage(); + } else if (isMerelySelfEditor()) { + log.debug("User not recognized. Going to application home."); + return getApplicationHomePageUrl(); + } else { + if (isLoginPage(afterLoginPage)) { + log.debug("Coming from /login. Going to site admin page."); + return getSiteAdminPageUrl(); + } else if (null != afterLoginPage) { + log.debug("Returning to requested page: " + afterLoginPage); + return afterLoginPage; } else { - if (isLoginPage(afterLoginPage)) { - log.debug("Coming from /login. Going to site admin page."); - response.sendRedirect(getSiteAdminPageUrl()); - } else if (null != afterLoginPage) { - log.debug("Returning to requested page: " + afterLoginPage); - response.sendRedirect(afterLoginPage); - } else { - log.debug("Don't know what to do. Go home."); - response.sendRedirect(getApplicationHomePageUrl()); - } + log.debug("Don't know what to do. Go home."); + return getApplicationHomePageUrl(); } + } + } + + public String getRedirectionUriForCancellingUser() { + if (isLoginPage(afterLoginPage)) { + log.debug("Coming from /login. Going to home."); + return getApplicationHomePageUrl(); + } else if (null != afterLoginPage) { + log.debug("Returning to requested page: " + afterLoginPage); + return afterLoginPage; + } else { + log.debug("Don't know what to do. Go home."); + return getApplicationHomePageUrl(); + } + } + + public void redirectLoggedInUser(HttpServletResponse response) + throws IOException { + try { + DisplayMessage.setMessage(request, assembleWelcomeMessage()); + response.sendRedirect(getRedirectionUriForLoggedInUser()); LoginProcessBean.removeBean(request); } catch (IOException e) { log.debug("Problem with re-direction", e); @@ -124,18 +138,10 @@ public class LoginRedirector { return "Welcome" + backString + ", " + greeting; } - public void redirectCancellingUser() throws IOException { + public void redirectCancellingUser(HttpServletResponse response) + throws IOException { try { - if (isLoginPage(afterLoginPage)) { - log.debug("Coming from /login. Going to home."); - response.sendRedirect(getApplicationHomePageUrl()); - } else if (null != afterLoginPage) { - log.debug("Returning to requested page: " + afterLoginPage); - response.sendRedirect(afterLoginPage); - } else { - log.debug("Don't know what to do. Go home."); - response.sendRedirect(getApplicationHomePageUrl()); - } + response.sendRedirect(getRedirectionUriForCancellingUser()); LoginProcessBean.removeBean(request); } catch (IOException e) { log.debug("Problem with re-direction", e); @@ -143,8 +149,8 @@ public class LoginRedirector { } } - public void redirectUnrecognizedExternalUser(String username) - throws IOException { + public void redirectUnrecognizedExternalUser(HttpServletResponse response, + String username) throws IOException { log.debug("Redirecting unrecognized external user: " + username); DisplayMessage.setMessage(request, "VIVO cannot find a profile for your account."); diff --git a/webapp/src/edu/cornell/mannlib/vitro/webapp/controller/edit/Authenticate.java b/webapp/src/edu/cornell/mannlib/vitro/webapp/controller/edit/Authenticate.java index 232e08323..9bf9847f6 100644 --- a/webapp/src/edu/cornell/mannlib/vitro/webapp/controller/edit/Authenticate.java +++ b/webapp/src/edu/cornell/mannlib/vitro/webapp/controller/edit/Authenticate.java @@ -124,7 +124,7 @@ public class Authenticate extends VitroHttpServlet { // Send them on their way. switch (exitState) { case NOWHERE: - new LoginRedirector(vreq, response).redirectCancellingUser(); + new LoginRedirector(vreq).redirectCancellingUser(response); break; case LOGGING_IN: showLoginScreen(vreq, response); @@ -133,7 +133,7 @@ public class Authenticate extends VitroHttpServlet { showLoginScreen(vreq, response); break; default: // LOGGED_IN: - new LoginRedirector(vreq, response).redirectLoggedInUser(); + new LoginRedirector(vreq).redirectLoggedInUser(response); break; } } catch (Exception e) { From 1077f283d671771e3a2e8c0359b2f1eabbf57ce9 Mon Sep 17 00:00:00 2001 From: ryounes Date: Fri, 10 Jun 2011 21:37:50 +0000 Subject: [PATCH 2/7] NIHVIVO-2466 For individuals with multiple labels, ensure the same label is displayed on the profile page as elsewhere in the application (individuallist pages, menu pages, back end) by using the same method to get the label. --- .../vitro/webapp/dao/IndividualDao.java | 3 + .../dao/filtering/IndividualDaoFiltering.java | 7 +++ .../webapp/dao/jena/IndividualDaoJena.java | 16 +++++ .../vitro/webapp/dao/jena/JenaBaseDao.java | 7 ++- .../DataPropertyStatementTemplateModel.java | 4 +- .../NameStatementTemplateModel.java | 61 +++++++++++-------- .../vitro/webapp/dao/IndividualDaoStub.java | 7 +++ 7 files changed, 77 insertions(+), 28 deletions(-) diff --git a/webapp/src/edu/cornell/mannlib/vitro/webapp/dao/IndividualDao.java b/webapp/src/edu/cornell/mannlib/vitro/webapp/dao/IndividualDao.java index 87d94c262..d32fa0407 100644 --- a/webapp/src/edu/cornell/mannlib/vitro/webapp/dao/IndividualDao.java +++ b/webapp/src/edu/cornell/mannlib/vitro/webapp/dao/IndividualDao.java @@ -10,6 +10,7 @@ import edu.cornell.mannlib.vitro.webapp.beans.DataPropertyStatement; import edu.cornell.mannlib.vitro.webapp.beans.Individual; import edu.cornell.mannlib.vitro.webapp.beans.Keyword; import edu.cornell.mannlib.vitro.webapp.beans.VClass; +import edu.cornell.mannlib.vitro.webapp.edit.EditLiteral; import edu.cornell.mannlib.vitro.webapp.search.beans.ObjectSourceIface; public interface IndividualDao extends ObjectSourceIface { @@ -135,6 +136,8 @@ public interface IndividualDao extends ObjectSourceIface { * @throws InsertException Could not create a URI */ String getUnusedURI(Individual individual) throws InsertException; + + EditLiteral getLabelEditLiteral(String individualUri); @Deprecated public abstract Individual getIndividualByExternalId(int externalIdType, diff --git a/webapp/src/edu/cornell/mannlib/vitro/webapp/dao/filtering/IndividualDaoFiltering.java b/webapp/src/edu/cornell/mannlib/vitro/webapp/dao/filtering/IndividualDaoFiltering.java index ad813013d..7f530c04e 100644 --- a/webapp/src/edu/cornell/mannlib/vitro/webapp/dao/filtering/IndividualDaoFiltering.java +++ b/webapp/src/edu/cornell/mannlib/vitro/webapp/dao/filtering/IndividualDaoFiltering.java @@ -19,6 +19,7 @@ import edu.cornell.mannlib.vitro.webapp.beans.VClass; import edu.cornell.mannlib.vitro.webapp.dao.IndividualDao; import edu.cornell.mannlib.vitro.webapp.dao.InsertException; import edu.cornell.mannlib.vitro.webapp.dao.filtering.filters.VitroFilters; +import edu.cornell.mannlib.vitro.webapp.edit.EditLiteral; class IndividualDaoFiltering extends BaseFiltering implements IndividualDao{ @@ -242,4 +243,10 @@ class IndividualDaoFiltering extends BaseFiltering implements IndividualDao{ return innerIndividualDao.getUnusedURI(individual); } + + @Override + public EditLiteral getLabelEditLiteral(String individualUri) { + return innerIndividualDao.getLabelEditLiteral(individualUri); + } + } \ No newline at end of file diff --git a/webapp/src/edu/cornell/mannlib/vitro/webapp/dao/jena/IndividualDaoJena.java b/webapp/src/edu/cornell/mannlib/vitro/webapp/dao/jena/IndividualDaoJena.java index 29b3b8397..50dd064f9 100644 --- a/webapp/src/edu/cornell/mannlib/vitro/webapp/dao/jena/IndividualDaoJena.java +++ b/webapp/src/edu/cornell/mannlib/vitro/webapp/dao/jena/IndividualDaoJena.java @@ -58,6 +58,7 @@ import edu.cornell.mannlib.vitro.webapp.dao.VitroVocabulary; import edu.cornell.mannlib.vitro.webapp.dao.jena.event.IndividualCreationEvent; import edu.cornell.mannlib.vitro.webapp.dao.jena.event.IndividualDeletionEvent; import edu.cornell.mannlib.vitro.webapp.dao.jena.event.IndividualUpdateEvent; +import edu.cornell.mannlib.vitro.webapp.edit.EditLiteral; public class IndividualDaoJena extends JenaBaseDao implements IndividualDao { @@ -1047,5 +1048,20 @@ public class IndividualDaoJena extends JenaBaseDao implements IndividualDao { return uri; } + + @Override + // This method returns an EditLiteral rather than a Jena Literal, since IndividualDao + // should not reference Jena objects. (However, the problem isn't really solved + // because EditLiteral currently references the Jena API.) + public EditLiteral getLabelEditLiteral(String individualUri) { + Literal literal = getLabelLiteral(individualUri); + if (literal == null) { + return null; + } + String value = literal.getLexicalForm(); + String datatype = literal.getDatatypeURI(); + String lang = literal.getLanguage(); + return new EditLiteral(value, datatype, lang); + } } diff --git a/webapp/src/edu/cornell/mannlib/vitro/webapp/dao/jena/JenaBaseDao.java b/webapp/src/edu/cornell/mannlib/vitro/webapp/dao/jena/JenaBaseDao.java index 4b7d3c7d2..6b9a40079 100644 --- a/webapp/src/edu/cornell/mannlib/vitro/webapp/dao/jena/JenaBaseDao.java +++ b/webapp/src/edu/cornell/mannlib/vitro/webapp/dao/jena/JenaBaseDao.java @@ -776,12 +776,17 @@ public class JenaBaseDao extends JenaBaseDaoCon { } return label; } + + protected Literal getLabelLiteral(String individualUri) { + OntResource resource = webappDaoFactory.getOntModel().createOntResource(individualUri); + return getLabelLiteral(resource); + } /** * works through list of PREFERRED_LANGUAGES to find an appropriate * label, or NULL if not found. */ - public Literal getLabelLiteral(OntResource r) { + protected Literal getLabelLiteral(OntResource r) { Literal labelLiteral = null; r.getOntModel().enterCriticalSection(Lock.READ); try { diff --git a/webapp/src/edu/cornell/mannlib/vitro/webapp/web/templatemodels/individual/DataPropertyStatementTemplateModel.java b/webapp/src/edu/cornell/mannlib/vitro/webapp/web/templatemodels/individual/DataPropertyStatementTemplateModel.java index 98316e222..da631e0bc 100644 --- a/webapp/src/edu/cornell/mannlib/vitro/webapp/web/templatemodels/individual/DataPropertyStatementTemplateModel.java +++ b/webapp/src/edu/cornell/mannlib/vitro/webapp/web/templatemodels/individual/DataPropertyStatementTemplateModel.java @@ -29,7 +29,7 @@ public class DataPropertyStatementTemplateModel extends PropertyStatementTemplat protected String value = null; // Used for editing - private String dataPropHash = null; + protected String dataPropHash = null; //Useful in case additional params to be retrieved for URL private VitroRequest vitroRequest= null; @@ -84,7 +84,7 @@ public class DataPropertyStatementTemplateModel extends PropertyStatementTemplat } // Determine whether the statement can be deleted - // Hack for rdfs:label - the policy doesn't prevent deletion + // Hack for rdfs:label - the policy doesn't prevent deletion. if ( ! propertyUri.equals(VitroVocabulary.LABEL) ) { action = new DropDataPropStmt(dps); if (policyHelper.isAuthorizedAction(action)) { diff --git a/webapp/src/edu/cornell/mannlib/vitro/webapp/web/templatemodels/individual/NameStatementTemplateModel.java b/webapp/src/edu/cornell/mannlib/vitro/webapp/web/templatemodels/individual/NameStatementTemplateModel.java index b5e6c16e2..d00e5041f 100644 --- a/webapp/src/edu/cornell/mannlib/vitro/webapp/web/templatemodels/individual/NameStatementTemplateModel.java +++ b/webapp/src/edu/cornell/mannlib/vitro/webapp/web/templatemodels/individual/NameStatementTemplateModel.java @@ -9,16 +9,21 @@ import org.apache.commons.logging.LogFactory; import org.openrdf.model.URI; import org.openrdf.model.impl.URIImpl; -import com.hp.hpl.jena.ontology.OntModel; -import com.hp.hpl.jena.ontology.OntResource; import com.hp.hpl.jena.rdf.model.Literal; +import edu.cornell.mannlib.vitro.webapp.auth.requestedAction.ifaces.RequestedAction; +import edu.cornell.mannlib.vitro.webapp.auth.requestedAction.propstmt.DropDataPropStmt; +import edu.cornell.mannlib.vitro.webapp.auth.requestedAction.propstmt.EditDataPropStmt; +import edu.cornell.mannlib.vitro.webapp.beans.DataPropertyStatement; +import edu.cornell.mannlib.vitro.webapp.beans.DataPropertyStatementImpl; import edu.cornell.mannlib.vitro.webapp.controller.VitroRequest; import edu.cornell.mannlib.vitro.webapp.dao.DataPropertyStatementDao; +import edu.cornell.mannlib.vitro.webapp.dao.IndividualDao; import edu.cornell.mannlib.vitro.webapp.dao.VitroVocabulary; import edu.cornell.mannlib.vitro.webapp.dao.WebappDaoFactory; -import edu.cornell.mannlib.vitro.webapp.dao.jena.JenaBaseDao; import edu.cornell.mannlib.vitro.webapp.dao.jena.WebappDaoFactoryJena; +import edu.cornell.mannlib.vitro.webapp.edit.EditLiteral; +import edu.cornell.mannlib.vitro.webapp.edit.n3editing.processEdit.RdfLiteralHash; public class NameStatementTemplateModel extends DataPropertyStatementTemplateModel { @@ -27,43 +32,49 @@ public class NameStatementTemplateModel extends /* * This method handles the special case where we are creating a DataPropertyStatementTemplateModel outside the GroupedPropertyList. - * Specifically, it allows rdfs:label to be treated like a data property statement and thus have editing links. It is not possible - * to handle rdfs:label like vitro links and vitroPublic image, because it is not possible to construct a DataProperty from - * rdfs:label. + * Specifically, it allows rdfs:label to be treated like a data property statement and thus have editing links. */ NameStatementTemplateModel(String subjectUri, VitroRequest vreq, EditingPolicyHelper policyHelper) { super(subjectUri, VitroVocabulary.LABEL, vreq, policyHelper); - Literal literal = null; WebappDaoFactory wdf = vreq.getWebappDaoFactory(); - // Use the same methods to get the label that are used elsewhere in the application, to - // guarantee consistent results for individuals with multiple labels. - // RY The problem here is we have a WebappDaoFactoryFiltering instead of WebappDaoFactoryJena. - if (wdf instanceof WebappDaoFactoryJena) { - WebappDaoFactoryJena wdfj = (WebappDaoFactoryJena) wdf; - OntResource resource = wdfj.getOntModel().createOntResource(subjectUri); - JenaBaseDao baseDao = wdfj.getJenaBaseDao(); - literal = baseDao.getLabelLiteral(resource); - } else { - DataPropertyStatementDao dpsDao = vreq.getWebappDaoFactory().getDataPropertyStatementDao(); - List literals = dpsDao.getDataPropertyValuesForIndividualByProperty(subjectUri, VitroVocabulary.LABEL); - // Make sure the subject has a value for this property - if (literals.size() > 0) { - literal = literals.get(0); - } - } + // NIHVIVO-2466 Use the same methods to get the label that are used elsewhere in the + // application, to guarantee consistent results for individuals with multiple labels + // across the application. + IndividualDao iDao = wdf.getIndividualDao(); + EditLiteral literal = iDao.getLabelEditLiteral(subjectUri); if (literal != null) { value = literal.getLexicalForm(); setEditAccess(literal, policyHelper); } else { - // If the individual has no rdfs:label, use the local name. It will not be editable (this replicates previous behavior; + // If the individual has no rdfs:label, use the local name. It will not be editable. (This replicates previous behavior; // perhaps we would want to allow a label to be added. But such individuals do not usually have their profiles viewed or - // edited directly. + // edited directly.) URI uri = new URIImpl(subjectUri); value = uri.getLocalName(); } } + + protected void setEditAccess(EditLiteral value, EditingPolicyHelper policyHelper) { + + if (policyHelper != null) { // we're editing + DataPropertyStatement dps = new DataPropertyStatementImpl(subjectUri, propertyUri, value.getLexicalForm()); + // Language and datatype are needed to get the correct hash value + dps.setLanguage(value.getLanguage()); + dps.setDatatypeURI(value.getDatatypeURI()); + this.dataPropHash = String.valueOf(RdfLiteralHash.makeRdfLiteralHash(dps)); + + // Determine whether the statement can be edited + RequestedAction action = new EditDataPropStmt(dps); + if (policyHelper.isAuthorizedAction(action)) { + markEditable(); + } + + // The label cannot be deleted, so we don't need to check + // the policy for the delete action. + } + } } diff --git a/webapp/test/stubs/edu/cornell/mannlib/vitro/webapp/dao/IndividualDaoStub.java b/webapp/test/stubs/edu/cornell/mannlib/vitro/webapp/dao/IndividualDaoStub.java index 4caf12cbe..cdbb4d31b 100644 --- a/webapp/test/stubs/edu/cornell/mannlib/vitro/webapp/dao/IndividualDaoStub.java +++ b/webapp/test/stubs/edu/cornell/mannlib/vitro/webapp/dao/IndividualDaoStub.java @@ -14,6 +14,7 @@ import edu.cornell.mannlib.vitro.webapp.beans.Keyword; import edu.cornell.mannlib.vitro.webapp.beans.VClass; import edu.cornell.mannlib.vitro.webapp.dao.IndividualDao; import edu.cornell.mannlib.vitro.webapp.dao.InsertException; +import edu.cornell.mannlib.vitro.webapp.edit.EditLiteral; /** * A minimal implementation of the IndividualDao. @@ -237,4 +238,10 @@ public class IndividualDaoStub implements IndividualDao { "IndividualDaoStub.getIndividualByExternalId() not implemented."); } + @Override + public EditLiteral getLabelEditLiteral(String individualUri) { + throw new RuntimeException( + "IndividualDaoStub.getLabelLiteral() not implemented."); + } + } From 2118a510d7c01937150f9a9391680cf38793deec Mon Sep 17 00:00:00 2001 From: manolobevia Date: Mon, 13 Jun 2011 13:26:27 +0000 Subject: [PATCH 3/7] NIHVIVO-2280: Removed javascript code for userAccounts-list.ftl to accountUtils.js --- webapp/web/js/account/accountUtils.js | 10 +++- .../body/accounts/userAccounts-list.ftl | 46 +++++++++++++++---- 2 files changed, 44 insertions(+), 12 deletions(-) diff --git a/webapp/web/js/account/accountUtils.js b/webapp/web/js/account/accountUtils.js index c95585c2c..b797cd6f4 100644 --- a/webapp/web/js/account/accountUtils.js +++ b/webapp/web/js/account/accountUtils.js @@ -2,9 +2,15 @@ $(document).ready(function(){ + // Change form actions in account main page + function changeAction(form, url) { + form.action = url; + return true; + } + //Accounts per page - //Hide is javascrip is disable - $('.accounts-per-page-form input[type="submit"]').hide(); + //Hide is javascrip is enable + $('input[name="accounts-per-page"]').addClass('hide'); $('.accounts-per-page').change(function() { $('#account-display').submit(); diff --git a/webapp/web/templates/freemarker/body/accounts/userAccounts-list.ftl b/webapp/web/templates/freemarker/body/accounts/userAccounts-list.ftl index a526db236..eaa862d0b 100644 --- a/webapp/web/templates/freemarker/body/accounts/userAccounts-list.ftl +++ b/webapp/web/templates/freemarker/body/accounts/userAccounts-list.ftl @@ -107,13 +107,6 @@ - -
@@ -126,6 +119,7 @@ | n accounts | <#assign counts = [25, 50, 100]> + - - - accounts per page | + accounts per page | <#if page.previous?has_content> Previous @@ -192,6 +184,40 @@ + +
+ + + + +
${scripts.add('')} \ No newline at end of file From e8ee90e9ba76018966fd739e4bf1b53d3b20a76c Mon Sep 17 00:00:00 2001 From: manolobevia Date: Mon, 13 Jun 2011 13:30:09 +0000 Subject: [PATCH 4/7] Added some extra space between the "Browse all" link and the first graph bar in "Browse By" section in homepage. --- webapp/web/css/browseClassGroups.css | 2 ++ 1 file changed, 2 insertions(+) diff --git a/webapp/web/css/browseClassGroups.css b/webapp/web/css/browseClassGroups.css index 43d59b4ee..c2c491b04 100644 --- a/webapp/web/css/browseClassGroups.css +++ b/webapp/web/css/browseClassGroups.css @@ -64,11 +64,13 @@ ul#browse-classgroups .count-classes { border: 1px solid #dde6e5; background: #fff; min-height: 230px; + padding-top: 10px; } a.browse-superclass { position: absolute; right: 0.5em; font-size: 0.9em; + top: 3px; } ul#classes-in-classgroup { float: left; From 11a048605bc0788cbcba6f5db2d2b811fef06615 Mon Sep 17 00:00:00 2001 From: j2blake Date: Mon, 13 Jun 2011 14:17:59 +0000 Subject: [PATCH 5/7] NIHVIVO-2696 Create the Create Account page for externally authenticated users who are logging in for the first time. --- .../controller/accounts/UserAccountsPage.java | 1 + .../UserAccountsFirstTimeExternalPage.java | 194 ++++++++++++++++++ ...AccountsFirstTimeExternalPageStrategy.java | 107 ++++++++++ .../user/UserAccountsUserController.java | 34 +++ .../userAccounts-firstTimeExternal.ftl | 64 ++++++ ...erAccounts-firstTimeExternalEmail-html.ftl | 26 +++ ...erAccounts-firstTimeExternalEmail-text.ftl | 12 ++ 7 files changed, 438 insertions(+) create mode 100644 webapp/src/edu/cornell/mannlib/vitro/webapp/controller/accounts/user/UserAccountsFirstTimeExternalPage.java create mode 100644 webapp/src/edu/cornell/mannlib/vitro/webapp/controller/accounts/user/UserAccountsFirstTimeExternalPageStrategy.java create mode 100644 webapp/web/templates/freemarker/body/accounts/userAccounts-firstTimeExternal.ftl create mode 100644 webapp/web/templates/freemarker/body/accounts/userAccounts-firstTimeExternalEmail-html.ftl create mode 100644 webapp/web/templates/freemarker/body/accounts/userAccounts-firstTimeExternalEmail-text.ftl diff --git a/webapp/src/edu/cornell/mannlib/vitro/webapp/controller/accounts/UserAccountsPage.java b/webapp/src/edu/cornell/mannlib/vitro/webapp/controller/accounts/UserAccountsPage.java index 9327f9078..de197e5da 100644 --- a/webapp/src/edu/cornell/mannlib/vitro/webapp/controller/accounts/UserAccountsPage.java +++ b/webapp/src/edu/cornell/mannlib/vitro/webapp/controller/accounts/UserAccountsPage.java @@ -129,6 +129,7 @@ public abstract class UserAccountsPage { map.put("myAccount", UrlBuilder.getUrl("/accounts/myAccount")); map.put("createPassword", UrlBuilder.getUrl("/accounts/createPassword")); map.put("resetPassword", UrlBuilder.getUrl("/accounts/resetPassword")); + map.put("firstTimeExternal", UrlBuilder.getUrl("/accounts/firstTimeExternal")); return map; } diff --git a/webapp/src/edu/cornell/mannlib/vitro/webapp/controller/accounts/user/UserAccountsFirstTimeExternalPage.java b/webapp/src/edu/cornell/mannlib/vitro/webapp/controller/accounts/user/UserAccountsFirstTimeExternalPage.java new file mode 100644 index 000000000..f180d23c9 --- /dev/null +++ b/webapp/src/edu/cornell/mannlib/vitro/webapp/controller/accounts/user/UserAccountsFirstTimeExternalPage.java @@ -0,0 +1,194 @@ +/* $This file is distributed under the terms of the license in /doc/license.txt$ */ + +package edu.cornell.mannlib.vitro.webapp.controller.accounts.user; + +import java.util.Collections; +import java.util.HashMap; +import java.util.Map; + +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpSession; + +import edu.cornell.mannlib.vitro.webapp.auth.permissions.PermissionSetsLoader; +import edu.cornell.mannlib.vitro.webapp.beans.UserAccount; +import edu.cornell.mannlib.vitro.webapp.beans.UserAccount.Status; +import edu.cornell.mannlib.vitro.webapp.controller.VitroRequest; +import edu.cornell.mannlib.vitro.webapp.controller.accounts.UserAccountsPage; +import edu.cornell.mannlib.vitro.webapp.controller.authenticate.Authenticator; +import edu.cornell.mannlib.vitro.webapp.controller.freemarker.responsevalues.ResponseValues; +import edu.cornell.mannlib.vitro.webapp.controller.freemarker.responsevalues.TemplateResponseValues; + +/** + * Handle the first-time login of an Externally Authenticated user who has no + * UserAccount - let's create one! + * + * If they get here from the login, there should an externalAuthId waiting in + * the session. Otherwise, they should get here by submitting the form, which + * will have the externalAuthId as a hidden field. + */ +public class UserAccountsFirstTimeExternalPage extends UserAccountsPage { + private static final String PARAMETER_SUBMIT = "submit"; + private static final String PARAMETER_EXTERNAL_AUTH_ID = "externalAuthId"; + private static final String PARAMETER_EMAIL_ADDRESS = "emailAddress"; + private static final String PARAMETER_FIRST_NAME = "firstName"; + private static final String PARAMETER_LAST_NAME = "lastName"; + + private static final String ERROR_NO_EMAIL = "errorEmailIsEmpty"; + private static final String ERROR_EMAIL_IN_USE = "errorEmailInUse"; + private static final String ERROR_EMAIL_INVALID_FORMAT = "errorEmailInvalidFormat"; + private static final String ERROR_NO_FIRST_NAME = "errorFirstNameIsEmpty"; + private static final String ERROR_NO_LAST_NAME = "errorLastNameIsEmpty"; + + private static final String TEMPLATE_NAME = "userAccounts-firstTimeExternal.ftl"; + + private static final String ATTRIBUTE_EXTERNAL_AUTH_ID = UserAccountsFirstTimeExternalPage.class + .getName(); + + /** + * Let some other request set the External Auth ID before redirecting to + * here. + */ + public static void setExternalAuthId(HttpServletRequest req, + String externalAuthId) { + req.getSession().setAttribute(ATTRIBUTE_EXTERNAL_AUTH_ID, + externalAuthId); + } + + private final UserAccountsFirstTimeExternalPageStrategy strategy; + + private boolean submit = false; + private String externalAuthId = ""; + private String emailAddress = ""; + private String firstName = ""; + private String lastName = ""; + + private String errorCode = ""; + private String bogusMessage = ""; + + protected UserAccountsFirstTimeExternalPage(VitroRequest vreq) { + super(vreq); + + this.strategy = UserAccountsFirstTimeExternalPageStrategy.getInstance( + vreq, this, isEmailEnabled()); + + checkSessionForExternalAuthId(); + if (externalAuthId.isEmpty()) { + parseRequestParameters(); + } + + validateExternalAuthId(); + + if (isSubmit() && !isBogus()) { + validateParameters(); + } + } + + private void checkSessionForExternalAuthId() { + HttpSession session = vreq.getSession(); + + Object o = session.getAttribute(ATTRIBUTE_EXTERNAL_AUTH_ID); + session.removeAttribute(ATTRIBUTE_EXTERNAL_AUTH_ID); + + if (o instanceof String) { + externalAuthId = (String) o; + } + } + + private void parseRequestParameters() { + submit = isFlagOnRequest(PARAMETER_SUBMIT); + externalAuthId = getStringParameter(PARAMETER_EXTERNAL_AUTH_ID, ""); + emailAddress = getStringParameter(PARAMETER_EMAIL_ADDRESS, ""); + firstName = getStringParameter(PARAMETER_FIRST_NAME, ""); + lastName = getStringParameter(PARAMETER_LAST_NAME, ""); + } + + private void validateExternalAuthId() { + if (externalAuthId.isEmpty()) { + bogusMessage = "Login failed - External ID is not found."; + return; + } + if (null != userAccountsDao + .getUserAccountByExternalAuthId(externalAuthId)) { + bogusMessage = "User account already exists for '" + externalAuthId + + "'"; + return; + } + } + + public boolean isBogus() { + return !bogusMessage.isEmpty(); + } + + public String getBogusMessage() { + return bogusMessage; + } + + public boolean isSubmit() { + return submit; + } + + private void validateParameters() { + if (firstName.isEmpty()) { + errorCode = ERROR_NO_FIRST_NAME; + } else if (lastName.isEmpty()) { + errorCode = ERROR_NO_LAST_NAME; + } else if (emailAddress.isEmpty()) { + errorCode = ERROR_NO_EMAIL; + } else if (isEmailInUse()) { + errorCode = ERROR_EMAIL_IN_USE; + } else if (!isEmailValidFormat()) { + errorCode = ERROR_EMAIL_INVALID_FORMAT; + } + } + + private boolean isEmailInUse() { + return userAccountsDao.getUserAccountByEmail(emailAddress) != null; + } + + private boolean isEmailValidFormat() { + return Authenticator.isValidEmailAddress(emailAddress); + } + + public boolean isValid() { + return errorCode.isEmpty(); + } + + public final ResponseValues showPage() { + Map body = new HashMap(); + + body.put("emailAddress", emailAddress); + body.put("firstName", firstName); + body.put("lastName", lastName); + body.put("externalAuthId", externalAuthId); + body.put("formUrls", buildUrlsMap()); + + if (!errorCode.isEmpty()) { + body.put(errorCode, Boolean.TRUE); + } + + strategy.addMoreBodyValues(body); + + return new TemplateResponseValues(TEMPLATE_NAME, body); + } + + public UserAccount createAccount() { + UserAccount u = new UserAccount(); + u.setEmailAddress(emailAddress); + u.setFirstName(firstName); + u.setLastName(lastName); + u.setExternalAuthId(externalAuthId); + u.setPasswordChangeRequired(false); + u.setPasswordLinkExpires(0); + u.setLoginCount(0); + u.setStatus(Status.ACTIVE); + u.setPermissionSetUris(Collections + .singleton(PermissionSetsLoader.URI_SELF_EDITOR)); + + userAccountsDao.insertUserAccount(u); + + strategy.notifyUser(u); + + return u; + } + +} diff --git a/webapp/src/edu/cornell/mannlib/vitro/webapp/controller/accounts/user/UserAccountsFirstTimeExternalPageStrategy.java b/webapp/src/edu/cornell/mannlib/vitro/webapp/controller/accounts/user/UserAccountsFirstTimeExternalPageStrategy.java new file mode 100644 index 000000000..71ce98161 --- /dev/null +++ b/webapp/src/edu/cornell/mannlib/vitro/webapp/controller/accounts/user/UserAccountsFirstTimeExternalPageStrategy.java @@ -0,0 +1,107 @@ +/* $This file is distributed under the terms of the license in /doc/license.txt$ */ + +package edu.cornell.mannlib.vitro.webapp.controller.accounts.user; + +import static javax.mail.Message.RecipientType.TO; + +import java.util.HashMap; +import java.util.Map; + +import edu.cornell.mannlib.vitro.webapp.beans.UserAccount; +import edu.cornell.mannlib.vitro.webapp.controller.VitroRequest; +import edu.cornell.mannlib.vitro.webapp.controller.accounts.UserAccountsPage; +import edu.cornell.mannlib.vitro.webapp.email.FreemarkerEmailFactory; +import edu.cornell.mannlib.vitro.webapp.email.FreemarkerEmailMessage; + +/** + * Handle the variations in the UserAccountsFirstTimeExternal page. If email is + * available, inform the template, and send a notification to the user. + * + * If not, then don't. + */ +public abstract class UserAccountsFirstTimeExternalPageStrategy extends + UserAccountsPage { + + public static UserAccountsFirstTimeExternalPageStrategy getInstance( + VitroRequest vreq, UserAccountsFirstTimeExternalPage page, + boolean emailEnabled) { + if (emailEnabled) { + return new EmailStrategy(vreq, page); + } else { + return new NoEmailStrategy(vreq, page); + } + } + + @SuppressWarnings("unused") + private UserAccountsFirstTimeExternalPage page; + + public UserAccountsFirstTimeExternalPageStrategy(VitroRequest vreq, + UserAccountsFirstTimeExternalPage page) { + super(vreq); + this.page = page; + } + + public abstract void addMoreBodyValues(Map body); + + public abstract void notifyUser(UserAccount ua); + + // ---------------------------------------------------------------------- + // Strategy to use if email is enabled. + // ---------------------------------------------------------------------- + + public static class EmailStrategy extends + UserAccountsFirstTimeExternalPageStrategy { + + public EmailStrategy(VitroRequest vreq, + UserAccountsFirstTimeExternalPage page) { + super(vreq, page); + } + + @Override + public void addMoreBodyValues(Map body) { + body.put("emailIsEnabled", Boolean.TRUE); + } + + @Override + public void notifyUser(UserAccount ua) { + Map body = new HashMap(); + body.put("userAccount", ua); + body.put("subjectLine", "Your VIVO account has been created."); + + FreemarkerEmailMessage email = FreemarkerEmailFactory + .createNewMessage(vreq); + email.addRecipient(TO, ua.getEmailAddress()); + email.setSubject("Your VIVO account has been created."); + email.setHtmlTemplate("userAccounts-firstTimeExternalEmail-html.ftl"); + email.setTextTemplate("userAccounts-firstTimeExternalEmail-text.ftl"); + email.setBodyMap(body); + email.send(); + } + + } + + // ---------------------------------------------------------------------- + // Strategy to use if email is disabled. + // ---------------------------------------------------------------------- + + public static class NoEmailStrategy extends + UserAccountsFirstTimeExternalPageStrategy { + + public NoEmailStrategy(VitroRequest vreq, + UserAccountsFirstTimeExternalPage page) { + super(vreq, page); + } + + @Override + public void addMoreBodyValues(Map body) { + // Nothing to add. + } + + @Override + public void notifyUser(UserAccount ua) { + // No way to notify. + } + + } + +} diff --git a/webapp/src/edu/cornell/mannlib/vitro/webapp/controller/accounts/user/UserAccountsUserController.java b/webapp/src/edu/cornell/mannlib/vitro/webapp/controller/accounts/user/UserAccountsUserController.java index 75ba9ec72..7628362ac 100644 --- a/webapp/src/edu/cornell/mannlib/vitro/webapp/controller/accounts/user/UserAccountsUserController.java +++ b/webapp/src/edu/cornell/mannlib/vitro/webapp/controller/accounts/user/UserAccountsUserController.java @@ -2,16 +2,23 @@ package edu.cornell.mannlib.vitro.webapp.controller.accounts.user; +import static edu.cornell.mannlib.vedit.beans.LoginStatusBean.AuthenticationSource.EXTERNAL; + import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; +import edu.cornell.mannlib.vedit.beans.LoginStatusBean.AuthenticationSource; import edu.cornell.mannlib.vitro.webapp.auth.requestedAction.Actions; import edu.cornell.mannlib.vitro.webapp.auth.requestedAction.usepages.EditOwnAccount; import edu.cornell.mannlib.vitro.webapp.beans.DisplayMessage; +import edu.cornell.mannlib.vitro.webapp.beans.UserAccount; import edu.cornell.mannlib.vitro.webapp.controller.VitroRequest; +import edu.cornell.mannlib.vitro.webapp.controller.authenticate.Authenticator; +import edu.cornell.mannlib.vitro.webapp.controller.authenticate.LoginRedirector; import edu.cornell.mannlib.vitro.webapp.controller.freemarker.FreemarkerHttpServlet; import edu.cornell.mannlib.vitro.webapp.controller.freemarker.responsevalues.RedirectResponseValues; import edu.cornell.mannlib.vitro.webapp.controller.freemarker.responsevalues.ResponseValues; +import edu.cornell.mannlib.vitro.webapp.controller.login.LoginProcessBean; /** * Parcel out the different actions required of the UserAccounts GUI. @@ -25,6 +32,7 @@ public class UserAccountsUserController extends FreemarkerHttpServlet { private static final String ACTION_CREATE_PASSWORD = "/createPassword"; private static final String ACTION_RESET_PASSWORD = "/resetPassword"; private static final String ACTION_MY_ACCOUNT = "/myAccount"; + private static final String ACTION_FIRST_TIME_EXTERNAL = "/firstTimeExternal"; @Override protected Actions requiredActions(VitroRequest vreq) { @@ -52,6 +60,8 @@ public class UserAccountsUserController extends FreemarkerHttpServlet { return handleCreatePasswordRequest(vreq); } else if (ACTION_RESET_PASSWORD.equals(action)) { return handleResetPasswordRequest(vreq); + } else if (ACTION_FIRST_TIME_EXTERNAL.equals(action)) { + return handleFirstTimeLoginFromExternalAccount(vreq); } else { return handleInvalidRequest(vreq); } @@ -95,6 +105,24 @@ public class UserAccountsUserController extends FreemarkerHttpServlet { } + private ResponseValues handleFirstTimeLoginFromExternalAccount( + VitroRequest vreq) { + UserAccountsFirstTimeExternalPage page = new UserAccountsFirstTimeExternalPage( + vreq); + if (page.isBogus()) { + return showHomePage(vreq, page.getBogusMessage()); + } else if (page.isSubmit() && page.isValid()) { + UserAccount userAccount = page.createAccount(); + Authenticator auth = Authenticator.getInstance(vreq); + auth.recordLoginAgainstUserAccount(userAccount, EXTERNAL); + LoginProcessBean.removeBean(vreq); + + return showLoginRedirection(vreq); + } else { + return page.showPage(); + } + } + private ResponseValues handleInvalidRequest(VitroRequest vreq) { return showHomePage(vreq, BOGUS_STANDARD_MESSAGE); } @@ -104,4 +132,10 @@ public class UserAccountsUserController extends FreemarkerHttpServlet { return new RedirectResponseValues("/"); } + private ResponseValues showLoginRedirection(VitroRequest vreq) { + LoginRedirector lr = new LoginRedirector(vreq); + DisplayMessage.setMessage(vreq, lr.assembleWelcomeMessage()); + String uri = lr.getRedirectionUriForLoggedInUser(); + return new RedirectResponseValues(uri); + } } diff --git a/webapp/web/templates/freemarker/body/accounts/userAccounts-firstTimeExternal.ftl b/webapp/web/templates/freemarker/body/accounts/userAccounts-firstTimeExternal.ftl new file mode 100644 index 000000000..1b6b78791 --- /dev/null +++ b/webapp/web/templates/freemarker/body/accounts/userAccounts-firstTimeExternal.ftl @@ -0,0 +1,64 @@ +<#-- $This file is distributed under the terms of the license in /doc/license.txt$ --> + +<#-- Template for creating an account for the first time an external user logs in. --> + +

First time log in

+ + <#if errorEmailIsEmpty??> + <#assign errorMessage = "You must supply an email address." /> + + + <#if errorEmailInUse??> + <#assign errorMessage = "An account with that email address already exists." /> + + + <#if errorEmailInvalidFormat??> + <#assign errorMessage = "'${emailAddress}' is not a valid email address." /> + + + <#if errorFirstNameIsEmpty??> + <#assign errorMessage = "You must supply a first name." /> + + + <#if errorLastNameIsEmpty??> + <#assign errorMessage = "You must supply a last name." /> + + + <#if errorMessage?has_content> + + + +
+
+

+ Please provide your contact information to finish creating your account. +

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

+ Note: An email will be sent to the address entered above notifying + that an account has been created. +

+ + + or Cancel +
+
+
+ +${stylesheets.add('')} \ No newline at end of file diff --git a/webapp/web/templates/freemarker/body/accounts/userAccounts-firstTimeExternalEmail-html.ftl b/webapp/web/templates/freemarker/body/accounts/userAccounts-firstTimeExternalEmail-html.ftl new file mode 100644 index 000000000..5faa53dff --- /dev/null +++ b/webapp/web/templates/freemarker/body/accounts/userAccounts-firstTimeExternalEmail-html.ftl @@ -0,0 +1,26 @@ +<#-- $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. --> + + + + ${subjectLine} + + +

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

+ +

+ Congratulations! +

+ +

+ We have created your new VIVO account associated with ${userAccount.emailAddress}. +

+ +

+ Thanks! +

+ + \ No newline at end of file diff --git a/webapp/web/templates/freemarker/body/accounts/userAccounts-firstTimeExternalEmail-text.ftl b/webapp/web/templates/freemarker/body/accounts/userAccounts-firstTimeExternalEmail-text.ftl new file mode 100644 index 000000000..8b788d56d --- /dev/null +++ b/webapp/web/templates/freemarker/body/accounts/userAccounts-firstTimeExternalEmail-text.ftl @@ -0,0 +1,12 @@ +<#-- $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! From d5f425b7a6c251ee3a67a9100f6cc83290935c18 Mon Sep 17 00:00:00 2001 From: j2blake Date: Mon, 13 Jun 2011 14:47:46 +0000 Subject: [PATCH 6/7] NIHVIVO-2696 assembleWelcomeMessage must be public in order for UserAccountsUserController to compile. --- .../vitro/webapp/controller/authenticate/LoginRedirector.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/webapp/src/edu/cornell/mannlib/vitro/webapp/controller/authenticate/LoginRedirector.java b/webapp/src/edu/cornell/mannlib/vitro/webapp/controller/authenticate/LoginRedirector.java index 1853c04f0..57c9375f1 100644 --- a/webapp/src/edu/cornell/mannlib/vitro/webapp/controller/authenticate/LoginRedirector.java +++ b/webapp/src/edu/cornell/mannlib/vitro/webapp/controller/authenticate/LoginRedirector.java @@ -113,7 +113,7 @@ public class LoginRedirector { } } - private String assembleWelcomeMessage() { + public String assembleWelcomeMessage() { if (isMerelySelfEditor() && !isSelfEditorWithIndividual()) { // A special message for unrecognized self-editors: return "You have logged in, " From d73c30673a2241177dbe4e4f3b7e16cfcd6b4a64 Mon Sep 17 00:00:00 2001 From: manolobevia Date: Mon, 13 Jun 2011 15:06:40 +0000 Subject: [PATCH 7/7] NIHVIVO-2280: Delete function in main account page wasn't working. It has been fixed. --- webapp/web/js/account/accountUtils.js | 15 ++++++++------- .../body/accounts/userAccounts-list.ftl | 3 ++- 2 files changed, 10 insertions(+), 8 deletions(-) diff --git a/webapp/web/js/account/accountUtils.js b/webapp/web/js/account/accountUtils.js index b797cd6f4..6f1aa0d69 100644 --- a/webapp/web/js/account/accountUtils.js +++ b/webapp/web/js/account/accountUtils.js @@ -1,13 +1,14 @@ /* $This file is distributed under the terms of the license in /doc/license.txt$ */ + +// Change form actions in account main page +function changeAction(form, url) { + form.action = url; + return true; +} + $(document).ready(function(){ - - // Change form actions in account main page - function changeAction(form, url) { - form.action = url; - return true; - } - + //Accounts per page //Hide is javascrip is enable $('input[name="accounts-per-page"]').addClass('hide'); diff --git a/webapp/web/templates/freemarker/body/accounts/userAccounts-list.ftl b/webapp/web/templates/freemarker/body/accounts/userAccounts-list.ftl index eaa862d0b..0cfe7c7f8 100644 --- a/webapp/web/templates/freemarker/body/accounts/userAccounts-list.ftl +++ b/webapp/web/templates/freemarker/body/accounts/userAccounts-list.ftl @@ -196,7 +196,8 @@ | n accounts | <#assign counts = [25, 50, 100]> - <#list counts as count>