Merge branch 'maint-rel-1.6' into develop

This commit is contained in:
j2blake 2013-12-05 16:37:18 -05:00
commit c95c812c1d
14 changed files with 685 additions and 624 deletions

View file

@ -28,7 +28,6 @@ import edu.cornell.mannlib.vitro.webapp.auth.requestedAction.propstmt.DropDataPr
import edu.cornell.mannlib.vitro.webapp.auth.requestedAction.propstmt.DropObjectPropertyStatement; import edu.cornell.mannlib.vitro.webapp.auth.requestedAction.propstmt.DropObjectPropertyStatement;
import edu.cornell.mannlib.vitro.webapp.beans.UserAccount; import edu.cornell.mannlib.vitro.webapp.beans.UserAccount;
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.BasicAuthenticator;
/** /**
* A collection of static methods to help determine whether requested actions * A collection of static methods to help determine whether requested actions
@ -66,45 +65,51 @@ public class PolicyHelper {
} }
/** /**
* Is the email/password authorized for these actions? * Is the email/password authorized for these actions? This should be used
* This should be used when a controller or something needs allow * when a controller or something needs allow actions if the user passes in
* actions if the user passes in their email and password. * their email and password.
* *
* It may be better to check this as part of a servlet Filter and * It may be better to check this as part of a servlet Filter and add an
* add an identifier bundle. * identifier bundle.
*/ */
public static boolean isAuthorizedForActions( HttpServletRequest req, public static boolean isAuthorizedForActions(HttpServletRequest req,
String email, String password, String email, String password, Actions actions) {
Actions actions){
if( password == null || email == null || if (password == null || email == null || password.isEmpty()
password.isEmpty() || email.isEmpty()){ || email.isEmpty()) {
return false; return false;
} }
try{ try {
Authenticator auth = Authenticator.getInstance(req); Authenticator auth = Authenticator.getInstance(req);
UserAccount user = auth.getAccountForInternalAuth( email ); UserAccount user = auth.getAccountForInternalAuth(email);
log.debug("userAccount is " + user==null?"null":user.getUri() ); if (user == null) {
log.debug("No account for '" + email + "'");
return false;
}
if( ! auth.isCurrentPassword( user, password ) ){ String uri = user.getUri();
log.debug(String.format("UNAUTHORIZED, password not accepted for %s, account URI: %s", log.debug("userAccount is '" + uri + "'");
user.getEmailAddress(), user.getUri()));
return false;
}else{
log.debug(String.format("password accepted for %s, account URI: %s",
user.getEmailAddress(), user.getUri() ));
// figure out if that account can do the actions
IdentifierBundle ids =
ActiveIdentifierBundleFactories.getUserIdentifierBundle(req,user);
PolicyIface policy = ServletPolicyList.getPolicies(req);
return PolicyHelper.isAuthorizedForActions( ids, policy, actions );
}
}catch(Exception ex){ if (!auth.isCurrentPassword(user, password)) {
log.error("Error while attempting to authorize actions " + actions.toString(), ex); log.debug(String.format("UNAUTHORIZED, password not accepted "
return false; + "for %s, account URI: %s", email, uri));
} return false;
}
log.debug(String.format("password accepted for %s, "
+ "account URI: %s", email, uri));
// figure out if that account can do the actions
IdentifierBundle ids = ActiveIdentifierBundleFactories
.getUserIdentifierBundle(req, user);
PolicyIface policy = ServletPolicyList.getPolicies(req);
return PolicyHelper.isAuthorizedForActions(ids, policy, actions);
} catch (Exception ex) {
log.error(
"Error while attempting to authorize actions "
+ actions.toString(), ex);
return false;
}
} }
/** /**
@ -120,8 +125,8 @@ public class PolicyHelper {
} }
Resource subject = stmt.getSubject(); Resource subject = stmt.getSubject();
edu.cornell.mannlib.vitro.webapp.beans.Property predicate = edu.cornell.mannlib.vitro.webapp.beans.Property predicate = new edu.cornell.mannlib.vitro.webapp.beans.Property(
new edu.cornell.mannlib.vitro.webapp.beans.Property(stmt.getPredicate().getURI()); stmt.getPredicate().getURI());
RDFNode objectNode = stmt.getObject(); RDFNode objectNode = stmt.getObject();
if ((subject == null) || (predicate == null) || (objectNode == null)) { if ((subject == null) || (predicate == null) || (objectNode == null)) {
return false; return false;
@ -130,8 +135,8 @@ public class PolicyHelper {
RequestedAction action; RequestedAction action;
if (objectNode.isResource()) { if (objectNode.isResource()) {
action = new AddObjectPropertyStatement(modelToBeModified, action = new AddObjectPropertyStatement(modelToBeModified,
subject.getURI(), predicate, objectNode subject.getURI(), predicate, objectNode.asResource()
.asResource().getURI()); .getURI());
} else { } else {
action = new AddDataPropertyStatement(modelToBeModified, action = new AddDataPropertyStatement(modelToBeModified,
subject.getURI(), predicate.getURI(), objectNode subject.getURI(), predicate.getURI(), objectNode
@ -153,8 +158,7 @@ public class PolicyHelper {
} }
Resource subject = stmt.getSubject(); Resource subject = stmt.getSubject();
edu.cornell.mannlib.vitro.webapp.beans.Property predicate = edu.cornell.mannlib.vitro.webapp.beans.Property predicate = new edu.cornell.mannlib.vitro.webapp.beans.Property();
new edu.cornell.mannlib.vitro.webapp.beans.Property();
predicate.setURI(stmt.getPredicate().getURI()); predicate.setURI(stmt.getPredicate().getURI());
RDFNode objectNode = stmt.getObject(); RDFNode objectNode = stmt.getObject();
if ((subject == null) || (predicate == null) || (objectNode == null)) { if ((subject == null) || (predicate == null) || (objectNode == null)) {
@ -164,8 +168,8 @@ public class PolicyHelper {
RequestedAction action; RequestedAction action;
if (objectNode.isResource()) { if (objectNode.isResource()) {
action = new DropObjectPropertyStatement(modelToBeModified, action = new DropObjectPropertyStatement(modelToBeModified,
subject.getURI(), predicate, objectNode subject.getURI(), predicate, objectNode.asResource()
.asResource().getURI()); .getURI());
} else { } else {
action = new DropDataPropertyStatement(modelToBeModified, action = new DropDataPropertyStatement(modelToBeModified,
subject.getURI(), predicate.getURI(), objectNode subject.getURI(), predicate.getURI(), objectNode
@ -310,7 +314,6 @@ public class PolicyHelper {
+ stmt.getObject() + ">"; + stmt.getObject() + ">";
} }
/** /**
* No need to instantiate this helper class - all methods are static. * No need to instantiate this helper class - all methods are static.
*/ */

View file

@ -0,0 +1,22 @@
/* $This file is distributed under the terms of the license in /doc/license.txt$ */
package edu.cornell.mannlib.vitro.webapp.controller.api;
/**
* When you try to use an API that you aren't authorized for, we don't redirect
* you to the login page. We complain.
*/
public class NotAuthorizedToUseApiException extends Exception {
public NotAuthorizedToUseApiException(String message) {
super(message);
}
public NotAuthorizedToUseApiException(Throwable cause) {
super(cause);
}
public NotAuthorizedToUseApiException(String message, Throwable cause) {
super(message, cause);
}
}

View file

@ -3,15 +3,8 @@
package edu.cornell.mannlib.vitro.webapp.filestorage.uploadrequest; package edu.cornell.mannlib.vitro.webapp.filestorage.uploadrequest;
import java.io.IOException; import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.net.URLDecoder;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Map.Entry;
import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletRequestWrapper; import javax.servlet.http.HttpServletRequestWrapper;
@ -19,8 +12,6 @@ import javax.servlet.http.HttpServletRequestWrapper;
import org.apache.commons.fileupload.FileItem; import org.apache.commons.fileupload.FileItem;
import org.apache.commons.fileupload.FileUploadException; import org.apache.commons.fileupload.FileUploadException;
import org.apache.commons.fileupload.servlet.ServletFileUpload; import org.apache.commons.fileupload.servlet.ServletFileUpload;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
/** /**
* <p> * <p>
@ -51,41 +42,22 @@ import org.apache.commons.logging.LogFactory;
* implemented in the sub-classes. * implemented in the sub-classes.
* </p> * </p>
*/ */
@SuppressWarnings("deprecation")
public abstract class FileUploadServletRequest extends HttpServletRequestWrapper { public abstract class FileUploadServletRequest extends HttpServletRequestWrapper {
private static final Log log = LogFactory
.getLog(FileUploadServletRequest.class);
public static final String FILE_ITEM_MAP = "file_item_map"; public static final String FILE_ITEM_MAP = "file_item_map";
public static final String FILE_UPLOAD_EXCEPTION = "file_upload_exception"; public static final String FILE_UPLOAD_EXCEPTION = "file_upload_exception";
private Map<String, List<String>> parameters;
private Map<String, List<FileItem>> files;
private FileUploadException fileUploadException;
private static final String[] EMPTY_ARRAY = new String[0];
// ---------------------------------------------------------------------- // ----------------------------------------------------------------------
// The factory method // The factory method
// ---------------------------------------------------------------------- // ----------------------------------------------------------------------
/** /**
* Wrap this {@link HttpServletRequest} in an appropriate wrapper class. * Wrap this {@link HttpServletRequest} in an appropriate wrapper class.
* set maxTempFileSize to 0 or -1 if streaming is desired. Set it to > 0 if
* you want any parts uploaded to a temporary directory
*/ */
public static FileUploadServletRequest parseRequest( public static FileUploadServletRequest parseRequest(
HttpServletRequest request, int maxTempFileSize) throws IOException { HttpServletRequest request, int maxFileSize) throws IOException {
boolean isMultipart = ServletFileUpload.isMultipartContent(request); boolean isMultipart = ServletFileUpload.isMultipartContent(request);
if (isMultipart) { if (isMultipart) {
if( maxTempFileSize <= 0 ){ return new MultipartHttpServletRequest(request, maxFileSize);
return new StreamingMultipartHttpServletRequest(request);
}else{
return new MultipartHttpServletRequest(request, maxTempFileSize);
}
} else { } else {
return new SimpleHttpServletRequestWrapper(request); return new SimpleHttpServletRequestWrapper(request);
} }
@ -113,183 +85,28 @@ public abstract class FileUploadServletRequest extends HttpServletRequestWrapper
/** Was this a multipart HTTP request? */ /** Was this a multipart HTTP request? */
public abstract boolean isMultipart(); public abstract boolean isMultipart();
protected void stashParametersInRequest(HttpServletRequest request, ServletFileUpload upload){ /**
Map<String, List<String>> parameters = new HashMap<String, List<String>>(); * Get the map of file items, by name.
Map<String, List<FileItem>> files = new HashMap<String, List<FileItem>>(); */
public abstract Map<String, List<FileItem>> getFiles();
parseQueryString(request.getQueryString(), parameters);
try {
List<FileItem> items = upload.parseRequest( request );
for (FileItem item : items) {
// Process a regular form field
if (item.isFormField()) {
addToParameters(parameters, item.getFieldName(), item
.getString("UTF-8"));
log.debug("Form field (parameter) " + item.getFieldName()
+ "=" + item.getString());
} else {
addToFileItems(files, item);
log.debug("File " + item.getFieldName() + ": "
+ item.getName());
}
}
} catch (FileUploadException e) {
fileUploadException = e;
request.setAttribute(
FileUploadServletRequest.FILE_UPLOAD_EXCEPTION, e);
} catch (UnsupportedEncodingException e) {
log.error("could not convert to UTF-8",e);
}
this.parameters = Collections.unmodifiableMap(parameters);
log.debug("Parameters are: " + this.parameters);
this.files = Collections.unmodifiableMap(files);
log.debug("Files are: " + this.files);
request.setAttribute(FILE_ITEM_MAP, this.files);
}
/** /**
* Pull any parameters out of the URL. * Find a non-empty file item with this name.
*
* @return the first such file item, or <code>null</code> if no matching,
* non-empty items were found.
*/ */
private void parseQueryString(String queryString, public abstract FileItem getFileItem(String string);
Map<String, List<String>> parameters) {
log.debug("Query string is : '" + queryString + "'");
if (queryString != null) {
String[] pieces = queryString.split("&");
for (String piece : pieces) {
int equalsHere = piece.indexOf('=');
if (piece.trim().isEmpty()) {
// Ignore an empty piece.
} else if (equalsHere <= 0) {
// A parameter without a value.
addToParameters(parameters, decode(piece), "");
} else {
// A parameter with a value.
String key = piece.substring(0, equalsHere);
String value = piece.substring(equalsHere + 1);
addToParameters(parameters, decode(key), decode(value));
}
}
}
log.debug("Parameters from query string are: " + parameters);
}
/** /**
* Remove any special URL-style encoding. * Was there an exception when uploading the file items?
*/ */
private String decode(String encoded) { public abstract boolean hasFileUploadException();
try {
return URLDecoder.decode(encoded, "UTF-8");
} catch (UnsupportedEncodingException e) {
log.error(e, e);
return encoded;
}
}
/**
/** Either create a new List for the value, or add to an existing List. */ * Get the exception that occurred when uploading the file items. If no such
private void addToParameters(Map<String, List<String>> map, String name, * exception, return <code>null</code>.
String value) { */
if (!map.containsKey(name)) { public abstract FileUploadException getFileUploadException();
map.put(name, new ArrayList<String>());
}
map.get(name).add(value);
}
/** Either create a new List for the file, or add to an existing List. */
private void addToFileItems(Map<String, List<FileItem>> map, FileItem file) {
String name = file.getFieldName();
if (!map.containsKey(name)) {
map.put(name, new ArrayList<FileItem>());
}
map.get(name).add(file);
}
public FileUploadException getFileUploadException() {
return fileUploadException;
}
public boolean hasFileUploadException() {
return fileUploadException != null;
}
// ----------------------------------------------------------------------
// Parameter-related methods won't find anything on the delegate request,
// since parsing consumed the parameters. So we need to look to the parsed
// info for the answers.
// ----------------------------------------------------------------------
@Override
public String getParameter(String name) {
if (parameters.containsKey(name)) {
return parameters.get(name).get(0);
} else {
return null;
}
}
@Override
public Enumeration<?> getParameterNames() {
return Collections.enumeration(parameters.keySet());
}
@Override
public String[] getParameterValues(String name) {
if (parameters.containsKey(name)) {
return parameters.get(name).toArray(EMPTY_ARRAY);
} else {
return null;
}
}
@Override
public Map<String, String[]> getParameterMap() {
Map<String, String[]> result = new HashMap<String, String[]>();
for (Entry<String, List<String>> entry : parameters.entrySet()) {
result.put(entry.getKey(), entry.getValue().toArray(EMPTY_ARRAY));
}
log.debug("resulting parameter map: " + result);
return result;
}
/**
* {@inheritDoc}
* <p>
* There may be more than one file item with the given name. If the first
* one is empty (size is zero), keep looking for a non-empty one.
* </p>
*/
public FileItem getFileItem(String name) {
List<FileItem> items = files.get(name);
if (items == null) {
return null;
}
for (FileItem item : items) {
if (item.getSize() > 0L) {
return item;
}
}
return null;
}
/**
* Gets a map of parameter names to files.
* This will should return null.
*/
public Map<String, List<FileItem>> getFiles() {
if( files == null )
return Collections.emptyMap();
else
return files;
}
} }

View file

@ -29,32 +29,99 @@ import org.apache.commons.logging.LogFactory;
* any parameter-related requests. File-related information will also be held * any parameter-related requests. File-related information will also be held
* here, to answer file-related requests. * here, to answer file-related requests.
*/ */
public class MultipartHttpServletRequest extends FileUploadServletRequest { class MultipartHttpServletRequest extends FileUploadServletRequest {
private static final Log log = LogFactory private static final Log log = LogFactory
.getLog(MultipartHttpServletRequest.class); .getLog(MultipartHttpServletRequest.class);
private int maxFileSize = 0; private static final String[] EMPTY_ARRAY = new String[0];
private File tempDir = null;
private final Map<String, List<String>> parameters;
private final Map<String, List<FileItem>> files;
private FileUploadException fileUploadException;
/** /**
* Parse the multipart request. Store the info about the request parameters * Parse the multipart request. Store the info about the request parameters
* and the uploaded files to a temporary directory. * and the uploaded files.
*
* This offers a simple way to deal with uploaded files by having a size
* limit. This limit may be rather large if desired. Many megabytes for example.
*/ */
public MultipartHttpServletRequest(HttpServletRequest request, public MultipartHttpServletRequest(HttpServletRequest request,
int maxFileSize) throws IOException { int maxFileSize) throws IOException {
super(request); super(request);
this.maxFileSize = maxFileSize; Map<String, List<String>> parameters = new HashMap<String, List<String>>();
this.tempDir = figureTemporaryDirectory(request); Map<String, List<FileItem>> files = new HashMap<String, List<FileItem>>();
//use an upload handler that will stash the file items in a temporary directory. File tempDir = figureTemporaryDirectory(request);
ServletFileUpload upload = createUploadHandler( this.maxFileSize, this.tempDir ); ServletFileUpload upload = createUploadHandler(maxFileSize, tempDir);
stashParametersInRequest( request , upload );
parseQueryString(request.getQueryString(), parameters);
try {
List<FileItem> items = parseRequestIntoFileItems(request, upload);
for (FileItem item : items) {
// Process a regular form field
if (item.isFormField()) {
addToParameters(parameters, item.getFieldName(), item
.getString("UTF-8"));
log.debug("Form field (parameter) " + item.getFieldName()
+ "=" + item.getString());
} else {
addToFileItems(files, item);
log.debug("File " + item.getFieldName() + ": "
+ item.getName());
}
}
} catch (FileUploadException e) {
fileUploadException = e;
request.setAttribute(
FileUploadServletRequest.FILE_UPLOAD_EXCEPTION, e);
}
this.parameters = Collections.unmodifiableMap(parameters);
log.debug("Parameters are: " + this.parameters);
this.files = Collections.unmodifiableMap(files);
log.debug("Files are: " + this.files);
request.setAttribute(FILE_ITEM_MAP, this.files);
} }
/**
* Pull any parameters out of the URL.
*/
private void parseQueryString(String queryString,
Map<String, List<String>> parameters) {
log.debug("Query string is : '" + queryString + "'");
if (queryString != null) {
String[] pieces = queryString.split("&");
for (String piece : pieces) {
int equalsHere = piece.indexOf('=');
if (piece.trim().isEmpty()) {
// Ignore an empty piece.
} else if (equalsHere <= 0) {
// A parameter without a value.
addToParameters(parameters, decode(piece), "");
} else {
// A parameter with a value.
String key = piece.substring(0, equalsHere);
String value = piece.substring(equalsHere + 1);
addToParameters(parameters, decode(key), decode(value));
}
}
}
log.debug("Parameters from query string are: " + parameters);
}
/**
* Remove any special URL-style encoding.
*/
private String decode(String encoded) {
try {
return URLDecoder.decode(encoded, "UTF-8");
} catch (UnsupportedEncodingException e) {
log.error(e, e);
return encoded;
}
}
/** /**
* Find the temporary storage directory for this webapp. * Find the temporary storage directory for this webapp.
@ -79,10 +146,116 @@ public class MultipartHttpServletRequest extends FileUploadServletRequest {
return upload; return upload;
} }
@Override /** Either create a new List for the value, or add to an existing List. */
public boolean isMultipart() { private void addToParameters(Map<String, List<String>> map, String name,
return true; String value) {
} if (!map.containsKey(name)) {
map.put(name, new ArrayList<String>());
}
map.get(name).add(value);
}
/** Either create a new List for the file, or add to an existing List. */
private void addToFileItems(Map<String, List<FileItem>> map, FileItem file) {
String name = file.getFieldName();
if (!map.containsKey(name)) {
map.put(name, new ArrayList<FileItem>());
}
map.get(name).add(file);
}
/** Minimize the code that uses the unchecked cast. */
@SuppressWarnings("unchecked")
private List<FileItem> parseRequestIntoFileItems(HttpServletRequest req,
ServletFileUpload upload) throws FileUploadException {
return upload.parseRequest(req);
}
// ----------------------------------------------------------------------
// This is a multipart request, so make the file info available. If there
// was an exception during parsing, make that available too.
// ----------------------------------------------------------------------
@Override
public boolean isMultipart() {
return true;
}
@Override
public Map<String, List<FileItem>> getFiles() {
return files;
}
/**
* {@inheritDoc}
* <p>
* There may be more than one file item with the given name. If the first
* one is empty (size is zero), keep looking for a non-empty one.
* </p>
*/
@Override
public FileItem getFileItem(String name) {
List<FileItem> items = files.get(name);
if (items == null) {
return null;
}
for (FileItem item : items) {
if (item.getSize() > 0L) {
return item;
}
}
return null;
}
@Override
public FileUploadException getFileUploadException() {
return fileUploadException;
}
@Override
public boolean hasFileUploadException() {
return fileUploadException != null;
}
// ----------------------------------------------------------------------
// Parameter-related methods won't find anything on the delegate request,
// since parsing consumed the parameters. So we need to look to the parsed
// info for the answers.
// ----------------------------------------------------------------------
@Override
public String getParameter(String name) {
if (parameters.containsKey(name)) {
return parameters.get(name).get(0);
} else {
return null;
}
}
@Override
public Enumeration<?> getParameterNames() {
return Collections.enumeration(parameters.keySet());
}
@Override
public String[] getParameterValues(String name) {
if (parameters.containsKey(name)) {
return parameters.get(name).toArray(EMPTY_ARRAY);
} else {
return null;
}
}
@Override
public Map<String, String[]> getParameterMap() {
Map<String, String[]> result = new HashMap<String, String[]>();
for (Entry<String, List<String>> entry : parameters.entrySet()) {
result.put(entry.getKey(), entry.getValue().toArray(EMPTY_ARRAY));
}
log.debug("resulting parameter map: " + result);
return result;
}
} }

View file

@ -33,6 +33,25 @@ class SimpleHttpServletRequestWrapper extends FileUploadServletRequest {
return false; return false;
} }
@Override
public Map<String, List<FileItem>> getFiles() {
return Collections.emptyMap();
}
@Override
public FileItem getFileItem(String string) {
return null;
}
@Override
public FileUploadException getFileUploadException() {
return null;
}
@Override
public boolean hasFileUploadException() {
return false;
}
// ---------------------------------------------------------------------- // ----------------------------------------------------------------------
// Since this is not a multipart request, the parameter methods can be // Since this is not a multipart request, the parameter methods can be
@ -45,7 +64,7 @@ class SimpleHttpServletRequestWrapper extends FileUploadServletRequest {
} }
@Override @Override
public Map<String, String[]> getParameterMap() { public Map<?, ?> getParameterMap() {
return getDelegate().getParameterMap(); return getDelegate().getParameterMap();
} }

View file

@ -1,38 +0,0 @@
/* $This file is distributed under the terms of the license in /doc/license.txt$ */
package edu.cornell.mannlib.vitro.webapp.filestorage.uploadrequest;
import javax.servlet.http.HttpServletRequest;
import org.apache.commons.fileupload.servlet.ServletFileUpload;
/**
* Wrapping ServletRequest that does multipart. In order to allow
* streaming, this class does NOT save the parts to a temporary directory.
*
*
*/
public class StreamingMultipartHttpServletRequest extends
FileUploadServletRequest {
/**
* Parse the multipart request. Store the info about the request parameters.
* Don't store the uploaded files to a temporary directory to allow streaming.
*
* Only use this if you plan to consume the FileItems using streaming
* to deal with inputs of very large sizes.
*
*/
public StreamingMultipartHttpServletRequest(HttpServletRequest request) {
super(request);
//use a file uploader that does not save the files to a temporary directory.
stashParametersInRequest( request , new ServletFileUpload());
}
@Override
public boolean isMultipart() {
return true;
}
}

View file

@ -2,7 +2,12 @@
package edu.cornell.mannlib.vitro.webapp.search.controller; package edu.cornell.mannlib.vitro.webapp.search.controller;
import static javax.servlet.http.HttpServletResponse.SC_FORBIDDEN;
import static javax.servlet.http.HttpServletResponse.SC_INTERNAL_SERVER_ERROR;
import java.io.IOException; import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
import javax.servlet.ServletException; import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletRequest;
@ -10,23 +15,15 @@ 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.auth.identifier.ActiveIdentifierBundleFactories;
import edu.cornell.mannlib.vitro.webapp.auth.identifier.IdentifierBundle;
import edu.cornell.mannlib.vitro.webapp.auth.permissions.SimplePermission; import edu.cornell.mannlib.vitro.webapp.auth.permissions.SimplePermission;
import edu.cornell.mannlib.vitro.webapp.auth.policy.PolicyHelper; import edu.cornell.mannlib.vitro.webapp.auth.policy.PolicyHelper;
import edu.cornell.mannlib.vitro.webapp.auth.policy.ServletPolicyList;
import edu.cornell.mannlib.vitro.webapp.auth.policy.ifaces.PolicyIface;
import edu.cornell.mannlib.vitro.webapp.auth.requestedAction.Actions;
import edu.cornell.mannlib.vitro.webapp.beans.UserAccount;
import edu.cornell.mannlib.vitro.webapp.controller.VitroRequest; import edu.cornell.mannlib.vitro.webapp.controller.VitroRequest;
import edu.cornell.mannlib.vitro.webapp.controller.authenticate.Authenticator; import edu.cornell.mannlib.vitro.webapp.controller.api.NotAuthorizedToUseApiException;
import edu.cornell.mannlib.vitro.webapp.controller.authenticate.BasicAuthenticator;
import edu.cornell.mannlib.vitro.webapp.controller.freemarker.FreemarkerHttpServlet; import edu.cornell.mannlib.vitro.webapp.controller.freemarker.FreemarkerHttpServlet;
import edu.cornell.mannlib.vitro.webapp.controller.freemarker.responsevalues.ExceptionResponseValues; import edu.cornell.mannlib.vitro.webapp.controller.freemarker.responsevalues.ExceptionResponseValues;
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;
import edu.cornell.mannlib.vitro.webapp.filestorage.uploadrequest.FileUploadServletRequest; import edu.cornell.mannlib.vitro.webapp.filestorage.uploadrequest.FileUploadServletRequest;
import edu.cornell.mannlib.vitro.webapp.filestorage.uploadrequest.MultipartHttpServletRequest;
import edu.cornell.mannlib.vitro.webapp.search.indexing.IndexBuilder; import edu.cornell.mannlib.vitro.webapp.search.indexing.IndexBuilder;
/** /**
@ -34,44 +31,11 @@ import edu.cornell.mannlib.vitro.webapp.search.indexing.IndexBuilder;
*/ */
@SuppressWarnings("serial") @SuppressWarnings("serial")
public class SearchServiceController extends FreemarkerHttpServlet { public class SearchServiceController extends FreemarkerHttpServlet {
private static final Log log = LogFactory.getLog(SearchServiceController.class); private static final Log log = LogFactory
.getLog(SearchServiceController.class);
/**
* Attempt to check if there is a email and password in the request parameters.
* If there is not, fall back on the normal usage pattern of requestedAction().
* If there is, then try to authenticate and authorize the
* userAccount associated with the email.
*/
@Override
public Actions requiredActions(VitroRequest vreq) {
try{
// Works by side effect: parse the multi-part request and stash FileItems in request
FileUploadServletRequest.parseRequest(vreq, 0);
//first figure out if the password and email login to an account with a password
String pw = vreq.getParameter("password");
String email = vreq.getParameter("email");
if( pw == null || email == null || pw.isEmpty() || email.isEmpty()){
return SimplePermission.MANAGE_SEARCH_INDEX.ACTIONS;
}
if( PolicyHelper.isAuthorizedForActions(vreq, email, pw,
SimplePermission.MANAGE_SEARCH_INDEX.ACTIONS ) ){
return Actions.AUTHORIZED;
}else{
log.debug(email + " is unauthorized to manage the search index. " +
"client IP "+vreq.getClientAddr());
return Actions.UNAUTHORIZED;
}
}catch(Exception ex){
log.error("Error while client IP "+ vreq.getClientAddr() + " attempting to log in " +
"to SearchServiceController: " + ex.getMessage());
return Actions.UNAUTHORIZED;
}
}
/** Limit file size to 1 Gigabyte. */
public static final int MAXIMUM_FILE_SIZE = 1024 * 1024 * 1024;
/** /**
* Handle the different actions. If not specified, the default action is to * Handle the different actions. If not specified, the default action is to
@ -80,54 +44,101 @@ public class SearchServiceController extends FreemarkerHttpServlet {
@Override @Override
protected ResponseValues processRequest(VitroRequest req) { protected ResponseValues processRequest(VitroRequest req) {
try { try {
req = new VitroRequest(FileUploadServletRequest.parseRequest(req,
MAXIMUM_FILE_SIZE));
//figure out what action to perform // Check the authorization here, because we don't want to redirect
String pathInfo = req.getPathInfo(); // to the login page if they are not authorized. (The file upload
// would be lost.
confirmAuthorization(req);
if( pathInfo == null || pathInfo.trim().isEmpty() || "/".equals(pathInfo.trim()) ){ switch (figureActionVerb(req)) {
return doHelpForm(req); case UPDATE_URIS_IN_SEARCH:
}
pathInfo = pathInfo.substring(1); //get rid of leading slash
if (VERBS.UPDATE_URIS_IN_SEARCH.verb.equals( pathInfo )) {
return doUpdateUrisInSearch(req); return doUpdateUrisInSearch(req);
} else { default:
return doHelpForm(req); return doHelpForm();
} }
} catch (NotAuthorizedToUseApiException e) {
Map<String, Object> map = new HashMap<>();
map.put("errorMessage", e.getMessage());
return new TemplateResponseValues("error-message.ftl", map,
SC_FORBIDDEN);
} catch (Exception e) { } catch (Exception e) {
return new ExceptionResponseValues(e); return new ExceptionResponseValues(e, SC_INTERNAL_SERVER_ERROR);
} }
} }
/**
* Process requests to the web service to update a list of URIs in the
* search index.
*/
private ResponseValues doUpdateUrisInSearch(HttpServletRequest req)
throws IOException, ServletException {
IndexBuilder builder = IndexBuilder.getBuilder(getServletContext());
int uriCount = new UpdateUrisInIndex().doUpdateUris(req, builder);
/** Map<String, Object> body = new HashMap<>();
* Process requests to the web service to update a list of URIs in the search index. */ body.put("msg", "Received " + uriCount + " URIs.");
public ResponseValues doUpdateUrisInSearch(HttpServletRequest req )
throws IOException, ServletException {
IndexBuilder builder = IndexBuilder.getBuilder(getServletContext()); return new TemplateResponseValues("searchService-help.ftl", body);
if( builder == null ) }
throw new ServletException( "Could not get search index builder from context. Check smoke test");
new UpdateUrisInIndex().doUpdateUris( req, builder); private ResponseValues doHelpForm() {
return new TemplateResponseValues("searchService-help.ftl");
}
TemplateResponseValues trv = new TemplateResponseValues( "" ); private void confirmAuthorization(VitroRequest vreq)
return trv; throws NotAuthorizedToUseApiException {
} Verb verb = figureActionVerb(vreq);
String pw = vreq.getParameter("password");
String email = vreq.getParameter("email");
// If you just want the help screen, no problem.
if (verb == Verb.VIEW_HELP_FORM) {
return;
}
// For other functions, your credentials must have moxie.
if (PolicyHelper.isAuthorizedForActions(vreq, email, pw,
SimplePermission.MANAGE_SEARCH_INDEX.ACTIONS)) {
return;
}
// Otherwise, you can't do this.
throw new NotAuthorizedToUseApiException(email
+ " is not authorized to manage the search index.");
}
public ResponseValues doHelpForm(HttpServletRequest req){ private Verb figureActionVerb(VitroRequest vreq) {
return new TemplateResponseValues( "searchService-help.ftl"); String pathInfo = vreq.getPathInfo();
} if (pathInfo == null) {
pathInfo = "";
}
if (pathInfo.startsWith("/")) {
pathInfo = pathInfo.substring(1);
}
return Verb.fromString(pathInfo);
}
public enum VERBS{ // ----------------------------------------------------------------------
UPDATE_URIS_IN_SEARCH("updateUrisInSearch"); // Helper class
// ----------------------------------------------------------------------
public final String verb; public enum Verb {
VERBS(String verb){ VIEW_HELP_FORM("viewHelpForm"), UPDATE_URIS_IN_SEARCH(
this.verb = verb; "updateUrisInSearch");
}
}
public final String verb;
Verb(String verb) {
this.verb = verb;
}
static Verb fromString(String s) {
for (Verb v : values()) {
if (v.verb.equals(s)) {
return v;
}
}
return VIEW_HELP_FORM;
}
}
} }

View file

@ -2,191 +2,101 @@
package edu.cornell.mannlib.vitro.webapp.search.controller; package edu.cornell.mannlib.vitro.webapp.search.controller;
import java.io.BufferedReader; import static edu.cornell.mannlib.vitro.webapp.filestorage.uploadrequest.FileUploadServletRequest.FILE_ITEM_MAP;
import java.io.IOException; import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader; import java.io.InputStreamReader;
import java.io.Reader; import java.io.Reader;
import java.util.ArrayList; import java.nio.charset.Charset;
import java.util.Arrays;
import java.util.Iterator;
import java.util.List; import java.util.List;
import java.util.Map;
import java.util.Scanner;
import java.util.regex.Pattern; import java.util.regex.Pattern;
import javax.servlet.ServletException; import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.apache.commons.fileupload.FileItemIterator; import org.apache.commons.fileupload.FileItem;
import org.apache.commons.fileupload.FileItemStream; import org.apache.commons.lang.StringUtils;
import org.apache.commons.fileupload.FileUploadException;
import org.apache.commons.fileupload.servlet.ServletFileUpload;
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.search.indexing.IndexBuilder; import edu.cornell.mannlib.vitro.webapp.search.indexing.IndexBuilder;
/** /**
* Class that performs the update of the uris in the search index * Class that performs the update of the uris in the search index for the
* for the SearchService. * SearchService.
*/ */
public class UpdateUrisInIndex { public class UpdateUrisInIndex {
private static final Log log = LogFactory.getLog(UpdateUrisInIndex.class); private static final Log log = LogFactory.getLog(UpdateUrisInIndex.class);
/** /** Pattern to split URIs on whitespace and commas. */
* Web service for update in search index of a list of URIs. public static final Pattern DELIMITER = Pattern.compile("[,\\s]+");
* @throws IOException
*/
protected void doUpdateUris(HttpServletRequest req, IndexBuilder builder)
throws ServletException, IOException{
if( ! ServletFileUpload.isMultipartContent(req) ) /**
throw new ServletException("Expected Multipart Content"); * Web service for update in search index of a list of URIs.
*
* @throws IOException
*/
protected int doUpdateUris(HttpServletRequest req, IndexBuilder builder)
throws ServletException, IOException {
@SuppressWarnings("unchecked")
Map<String, List<FileItem>> map = (Map<String, List<FileItem>>) req
.getAttribute(FILE_ITEM_MAP);
if (map == null) {
throw new ServletException("Expected Multipart Content");
}
String enc = getEncoding(req); Charset enc = getEncoding(req);
try{
//loop over the fileds and add any URIs to the IndexBuilder queue
ServletFileUpload upload = new ServletFileUpload();
FileItemIterator iter = upload.getItemIterator(req);
while( iter.hasNext()){
FileItemStream item = iter.next();
String name = item.getFieldName();
if( "email".equals(name) || "password".equals(name) )
continue; //skip the password and email fields
InputStream stream = item.openStream(); int uriCount = 0;
try{ for (String name : map.keySet()) {
addToSearchQueue(builder, stream, enc); for (FileItem item : map.get(name)) {
}finally{ log.debug("Found " + item.getSize() + " byte file for '" + name + "'");
stream.close(); uriCount += processFileItem(builder, name, item, enc);
} }
} }
}catch (FileUploadException fex){ return uriCount;
throw new ServletException("Could not upload file to SearchServiceController", fex); }
}finally{
builder.doUpdateIndex();
}
}
/** private int processFileItem(IndexBuilder builder, String name,
* Get the encoding of the request, default to UTF-8 FileItem item, Charset enc) throws IOException {
* since that is in the vitro install instructions int count = 0;
* to put on the connector. Reader reader = new InputStreamReader(item.getInputStream(), enc.name());
*/ try (Scanner scanner = createScanner(reader)) {
private String getEncoding(HttpServletRequest req){ while (scanner.hasNext()) {
String enc = req.getCharacterEncoding(); String uri = scanner.next();
if( enc == null || enc.isEmpty() ){ log.debug("Request to index uri '" + uri + "'");
log.debug("No encoding on POST request, That is acceptable."); builder.addToChanged(uri);
enc = "UTF-8"; count++;
}else if( enc.length() > 30){ }
log.debug("Ignoring odd encoding of '" + enc + "'"); }
enc = "UTF-8"; return count;
}else{ }
log.debug("Encoding set on POST request: " + enc);
}
log.debug("Reading POSTed URIs with encoding " + enc);
return enc;
}
/** @SuppressWarnings("resource")
* Adds URIs from Reader to search queue. protected Scanner createScanner(Reader in) {
*/ return new Scanner(in).useDelimiter(DELIMITER);
private void addToSearchQueue( IndexBuilder builder, InputStream stream , String charEncoding ) }
throws IOException{
Iterator<String> uris = /**
new UrisFromInputIterator( new InputStreamReader(stream, charEncoding) ); * Get the encoding of the request, default to UTF-8 since that is in the
* vitro install instructions to put on the connector.
while(uris.hasNext()){ */
String uri = uris.next(); private Charset getEncoding(HttpServletRequest req) {
log.debug("Request to index uri '" + uri + "'"); String enc = req.getCharacterEncoding();
builder.addToChanged( uri ); if (StringUtils.isBlank(enc)) {
} log.debug("No encoding on POST request, That is acceptable.");
} enc = "UTF-8";
} else if (enc.length() > 30) {
log.debug("Ignoring odd encoding of '" + enc + "'");
/** enc = "UTF-8";
* Iterator for URIs in a reader to make top level methods simpler. } else {
*/ log.debug("Encoding set on POST request: " + enc);
public static class UrisFromInputIterator implements Iterator<String> { }
BufferedReader reader; log.debug("Reading POSTed URIs with encoding " + enc);
Iterator<String> uris; return Charset.forName(enc);
}
public UrisFromInputIterator(Reader in ){
this.reader = new BufferedReader(in);
}
public void remove(){ throw new UnsupportedOperationException() ; }
public boolean hasNext(){
if( uris != null && uris.hasNext() ){
return true;
}else{
try {
return getFromBuffer();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
return false;
}
}
}
public String next(){
return uris.next();
}
/** Returns true if there are uris to get.
* @throws IOException */
private boolean getFromBuffer() throws IOException{
uris = null;
while( uris == null || !uris.hasNext() ){
String chunk = reader.readLine();
if( chunk == null ){ //at end of input
break;
} else if( chunk.trim().isEmpty() ){
continue;
}else{
uris = lineToUris(chunk).iterator();
if( uris.hasNext() ){
return true;
}
}
}
return false;
}
}
/**
* Removes null and empty elements from in.
* Returned list will not be null.
*/
private static List<String> removeNullAndEmpty(List<String> in ){
ArrayList<String> out = new ArrayList<String>();
if( in == null )
return out;
for( String s : in ){
if( s != null && !s.trim().isEmpty() ){
out.add(s);
}
}
return out;
}
/**
* Parses a line to a list of URIs.
* Retruned list will not be null.
* No elements in returned list will be empty or null.
*/
protected static List<String> lineToUris(String line){
List<String> parts = removeNullAndEmpty( Arrays.asList(commaAndWhitespace.split( line ) ));
return parts;
}
/** Pattern to split URIs on whitespace and commas. */
private static final Pattern commaAndWhitespace = Pattern.compile("[,\\s]");
} }

View file

@ -2,9 +2,8 @@
package edu.cornell.mannlib.vitro.webapp.search.controller; package edu.cornell.mannlib.vitro.webapp.search.controller;
import java.io.Reader;
import java.io.StringReader; import java.io.StringReader;
import java.util.Arrays;
import java.util.Collections;
import java.util.Iterator; import java.util.Iterator;
import org.junit.Assert; import org.junit.Assert;
@ -15,52 +14,76 @@ import org.junit.Test;
*/ */
public class UpdateUrisInIndexTest { public class UpdateUrisInIndexTest {
@Test @Test(expected = NullPointerException.class)
public void lineToUrisTest(){ public void nullString() {
Assert.assertEquals(Arrays.asList("uri1"), UpdateUrisInIndex.lineToUris( "uri1")); scan(null, 0);
Assert.assertEquals(Arrays.asList("uri1", "uri2"), UpdateUrisInIndex.lineToUris( "uri1,uri2")); }
Assert.assertEquals(Arrays.asList("uri1"), UpdateUrisInIndex.lineToUris( "uri1\n")); @Test
Assert.assertEquals(Arrays.asList("uri1","uri2"), UpdateUrisInIndex.lineToUris( "uri1\nuri2")); public void emptyString() {
scan("", 0);
}
Assert.assertEquals(Collections.EMPTY_LIST, UpdateUrisInIndex.lineToUris( "" )); @Test
Assert.assertEquals(Collections.EMPTY_LIST, UpdateUrisInIndex.lineToUris( "," )); public void nothingButDelimiters() {
Assert.assertEquals(Collections.EMPTY_LIST, UpdateUrisInIndex.lineToUris( " , " )); scan(" , ", 0);
} scan("\n", 0);
scan("\n\n\n", 0);
scan("\n, \t\r ,\n\n", 0);
}
@Test
public void oneTokenNoDelimiters() {
scan("http://bogus.com/n234", 1);
}
@Test @Test
public void UrisFromInputIteratorTest(){ public void oneTokenAssortedDelimiters() {
doUrisFromInputIterator("",0); scan("http://bogus.com/n234\n", 1);
doUrisFromInputIterator(" ",0); scan("\nhttp://bogus.com/n234", 1);
doUrisFromInputIterator(" , ",0); scan("\nhttp://bogus.com/n234\n", 1);
doUrisFromInputIterator("\n",0); }
doUrisFromInputIterator("\n\n\n",0);
doUrisFromInputIterator("http://bogus.com/n234",1);
doUrisFromInputIterator("http://bogus.com/n234\nhttp://bogus.com/n442",2);
doUrisFromInputIterator("http://bogus.com/n234, http://bogus.com/n442",2);
doUrisFromInputIterator("http://bogus.com/n234,\nhttp://bogus.com/n442\n",2);
doUrisFromInputIterator("http://bogus.com/n234\n",1); @Test
doUrisFromInputIterator("\nhttp://bogus.com/n234",1); public void twoTokensAssortedDelimiters() {
doUrisFromInputIterator("\nhttp://bogus.com/n234\n",1); scan("http://bogus.com/n234\nhttp://bogus.com/n442", 2);
scan("http://bogus.com/n234, http://bogus.com/n442", 2);
scan("http://bogus.com/n234,\nhttp://bogus.com/n442\n", 2);
}
} @Test
public void nonBreakingSpace() {
scan("non\u00A0breaking\u00A0space", 1);
}
public void doUrisFromInputIterator(String input, int expectedUris){ @Test
Iterator<String> it = new UpdateUrisInIndex.UrisFromInputIterator( new StringReader(input) ); public void omnibus() {
int count = 0; scan(" a , b,c d\t,\re", 5);
while( it.hasNext()){ }
String uri = it.next();
if( uri == null) // ----------------------------------------------------------------------
Assert.fail("UrisFromInputIterator should not return null strings \n " + // Helper methods
"Null string for uri #" + count + " for input '" + input + "'"); // ----------------------------------------------------------------------
if( uri.isEmpty())
Assert.fail("UrisFromInputIterator should not return empty strings \n " + public void scan(String input, int expectedUris) {
"Empty string for uri #" + count + " for input '" + input + "'"); Reader reader = (input == null) ? null : new StringReader(input);
count++; Iterator<String> it = new UpdateUrisInIndex().createScanner(reader);
} int count = 0;
Assert.assertEquals("Incorrect number of URIs from input '" + input + "'", expectedUris, count); while (it.hasNext()) {
} String uri = it.next();
if (uri == null) {
Assert.fail("Scanner should not return null strings \n "
+ "Null string for uri #" + count + " for input '"
+ input + "'");
} else if (uri.isEmpty()) {
Assert.fail("Scanner should not return empty strings \n "
+ "Empty string for uri #" + count + " for input '"
+ input + "'");
}
count++;
}
Assert.assertEquals("Incorrect number of URIs from input '" + input
+ "'", expectedUris, count);
}
} }

View file

@ -697,6 +697,7 @@ select_existing_collaborator = Select an existing Collaborator for {0}
selected = Selected selected = Selected
change_selection = change selection change_selection = change selection
there_are_no_entries_for_selection = There are no entries in the system from which to select. there_are_no_entries_for_selection = There are no entries in the system from which to select.
the_range_class_does_not_exist= The range class for this property does not exist in the system.
editing_prohibited = This property is currently configured to prohibit editing. editing_prohibited = This property is currently configured to prohibit editing.
confirm_entry_deletion_from = Are you sure you want to delete the following entry from confirm_entry_deletion_from = Are you sure you want to delete the following entry from
@ -752,6 +753,7 @@ custom_template_containing_content = Custom template containing all content
a_menu_page = This is a menu page a_menu_page = This is a menu page
menu_item_name = Menu Item Name menu_item_name = Menu Item Name
if_blank_page_title_used = If left blank, the page title will be used. if_blank_page_title_used = If left blank, the page title will be used.
multiple_content_default_template_error = With multiple content types, you must specify a custom template.
label = label label = label
no_classes_to_select = There are no Classes in the system from which to select. no_classes_to_select = There are no Classes in the system from which to select.

View file

@ -205,13 +205,15 @@ var pageManagementUtils = {
//Also clear custom template value so as not to submit it //Also clear custom template value so as not to submit it
pageManagementUtils.clearInputs(pageManagementUtils.customTemplate); pageManagementUtils.clearInputs(pageManagementUtils.customTemplate);
pageManagementUtils.rightSideDiv.show(); pageManagementUtils.rightSideDiv.show();
pageManagementUtils.disablePageSave(); //Check to see if there is already content on page, in which case save should be enabled
var pageContentSections = $("section[class='pageContent']");
if(pageContentSections.length == 0) {
pageManagementUtils.disablePageSave();
}
}); });
this.customTemplateRadio.click( function() { this.customTemplateRadio.click( function() {
pageManagementUtils.customTemplate.removeClass('hidden'); pageManagementUtils.handleSelectCustomTemplate();
pageManagementUtils.rightSideDiv.show();
pageManagementUtils.disablePageSave();
}); });
this.selfContainedTemplateRadio.click( function() { this.selfContainedTemplateRadio.click( function() {
@ -265,6 +267,16 @@ var pageManagementUtils = {
}); });
}, },
handleSelectCustomTemplate: function() {
pageManagementUtils.customTemplate.removeClass('hidden');
pageManagementUtils.rightSideDiv.show();
//Check to see if there is already content on page, in which case save should be enabled
var pageContentSections = $("section[class='pageContent']");
if(pageContentSections.length == 0) {
pageManagementUtils.disablePageSave();
}
},
handleClickDone:function() { handleClickDone:function() {
var selectedType = pageManagementUtils.contentTypeSelect.val(); var selectedType = pageManagementUtils.contentTypeSelect.val();
var selectedTypeText = $("#typeSelect option:selected").text(); var selectedTypeText = $("#typeSelect option:selected").text();
@ -392,6 +404,9 @@ var pageManagementUtils = {
pageManagementUtils.adjustSaveButtonHeight(); pageManagementUtils.adjustSaveButtonHeight();
//Disable save button until the user has clicked done or cancel from the addition //Disable save button until the user has clicked done or cancel from the addition
pageManagementUtils.disablePageSave(); pageManagementUtils.disablePageSave();
//If the default template is selected, there is already content on the page, and the user is selecting new content
//display alert message that they must select a custom template and select
pageManagementUtils.checkTemplateForMultipleContent(_this.contentTypeSelect.val());
}, },
disablePageSave:function() { disablePageSave:function() {
pageManagementUtils.pageSaveButton.attr("disabled", "disabled"); pageManagementUtils.pageSaveButton.attr("disabled", "disabled");
@ -430,6 +445,21 @@ var pageManagementUtils = {
$el.find("select option:eq(0)").attr("selected", "selected"); $el.find("select option:eq(0)").attr("selected", "selected");
}, },
checkTemplateForMultipleContent:function(contentTypeSelected) {
if(contentTypeSelected != "") {
var pageContentSections = $("section[class='pageContent']");
var selectedTemplateValue = $('input:radio[name=selectedTemplate]:checked').val();
//A new section hasn't been added yet so check to see if there is at least one content type already on page
if(selectedTemplateValue == "default" && pageContentSections.length >= 1) {
//alert the user that they should be picking custom template instead
alert(pageManagementUtils.multipleContentWithDefaultTemplateError);
//pick custom template
$('input:radio[name=selectedTemplate][value="custom"]').attr("checked", true);
pageManagementUtils.handleSelectCustomTemplate();
}
}
},
//Clone content area //Clone content area
//When adding a new content type, this function will copy the values from the new content form and generate //When adding a new content type, this function will copy the values from the new content form and generate
//the content for the new section containing the content //the content for the new section containing the content
@ -874,7 +904,12 @@ var pageManagementUtils = {
if(pageContentSections.length == 0) { if(pageContentSections.length == 0) {
validationErrorMsg = pageManagementUtils.selectContentType + " <br /> "; validationErrorMsg = pageManagementUtils.selectContentType + " <br /> ";
} else { } else {
//For each, based on type, validate if a validation function exists //If there are multiple content types, and the default template option is selected, then display error message
var selectedTemplateValue = $('input:radio[name=selectedTemplate]:checked').val();
if(selectedTemplateValue == "default") {
validationErrorMsg += pageManagementUtils.multipleContentWithDefaultTemplateError;
}
//For each, based on type, validate if a validation function exists
$.each(pageContentSections, function(i) { $.each(pageContentSections, function(i) {
if(pageManagementUtils.processDataGetterUtils != null) { if(pageManagementUtils.processDataGetterUtils != null) {
var dataGetterType = pageManagementUtils.processDataGetterUtils.selectDataGetterType($(this)); var dataGetterType = pageManagementUtils.processDataGetterUtils.selectDataGetterType($(this));

View file

@ -0,0 +1,84 @@
<#-- $This file is distributed under the terms of the license in /doc/license.txt$ -->
<#--This is an example of including multiple content types in the same template, this combines the default templates for Fixed HTML, Class groups and Solr Individuals in one template-->
<#include "menupage-checkForData.ftl">
<#--Fixed HTML portion-->
<#--Note that variableName is employed by both the fixed html and sparql query templates, this is used to store the
actual name of the variable that is used to store either the fixed html or sparql query results. If combining fixed html
and sparql query results in a custom template, the template can utilize the actual variable name e.g. "query results" instead of how
variableName is used below.-->
<#assign htmlExists = false/>
<#if variableName?has_content>
<#assign htmlExists = true />
</#if>
<#if htmlExists>
${.globals[variableName]}
<#else>
${i18n().no_html_specified}
</#if>
<#--Class grou section-->
<#if !noData>
<section id="menupage-intro" role="region">
<h2>${page.title}</h2>
</section>
<#include "menupage-browse.ftl">
${stylesheets.add('<link rel="stylesheet" href="${urls.base}/css/menupage/menupage.css" />')}
<#include "menupage-scripts.ftl">
<#else>
${noDataNotification}
</#if>
<#--Solr Individuals section-->
<#import "lib-list.ftl" as l>
<#include "individualList-checkForData.ftl">
${stylesheets.add('<link rel="stylesheet" href="${urls.base}/css/browseIndex.css" />')}
<section class="individualList">
<h2>${title}
</h2>
<#if subtitle?has_content>
<h4>${subtitle}</h4>
</#if>
<#if (!noData)>
<#if errorMessage?has_content>
<p>${errorMessage}</p>
<#else>
<#assign pagination>
<#if (pages?has_content && pages?size > 1)>
${i18n().pages}:
<ul class="pagination">
<#list pages as page>
<#if page.selected>
<li class="selectedNavPage">${page.text}</li>
<#else>
<#-- RY Ideally the urls would be generated by the controller; see search-pagedResults.ftl -->
<li><a href="${urls.base}/individuallist?${page.param}&vclassId=${vclassId?url}" title="${i18n().page_text}">${page.text}</a></li>
</#if>
</#list>
</ul>
</#if>
</#assign>
${pagination}
<ul>
<#list individuals as individual>
<li>
<@shortView uri=individual.uri viewContext="index" />
</li>
</#list>
</ul>
${pagination}
</#if>
<#else>
${noDataNotification}
</#if>
</section> <!-- .individualList -->

View file

@ -12,9 +12,8 @@
<p>This service will update the search index for the list of URIs it <p>This service will update the search index for the list of URIs it
receives. It expectes a POST with an encoding of receives. It expectes a POST with an encoding of
multpart/form-data. The service inspect all parts of this POST for multpart/form-data. The service inspect all parts of this POST for
lists of URIs to reindex. The URIs should be comma or space lists of URIs to reindex. The URIs should be separated by commas and/or white space.
seperated. If no information can be found for a URI it will be If no information can be found for a URI it will be ignored.</p>
ignored.</p>
<p>The request parameters email and password allow the form to be submitted <p>The request parameters email and password allow the form to be submitted
for a user account. Only internal accounts are supported, external authentication for a user account. Only internal accounts are supported, external authentication
@ -33,8 +32,8 @@ is not supported.</p>
<label for="password">Account password</label> <label for="password">Account password</label>
<input type="text" name="password" id="password"/> <input type="text" name="password" id="password"/>
<label for="urisToUpdate">List of URIs to update in the search index</label> <label for="urisToUpdate">File of URIs to update in the search index</label>
<textarea name="urisToUpdate" id="urisToUpdate" rows="4" cols="50" ></textarea> <input type="file" name="datafile" size="30" />
<button type="submit">submit</button> <button type="submit">submit</button>
</form> </form>

View file

@ -189,7 +189,8 @@
supplyPrettyUrl: '${i18n().supply_url}', supplyPrettyUrl: '${i18n().supply_url}',
startUrlWithSlash: '${i18n().start_url_with_slash}', startUrlWithSlash: '${i18n().start_url_with_slash}',
supplyTemplate: '${i18n().supply_template}', supplyTemplate: '${i18n().supply_template}',
selectContentType: '${i18n().select_content_type}' selectContentType: '${i18n().select_content_type}',
multipleContentWithDefaultTemplateError: '${i18n().multiple_content_default_template_error}'
}; };
</script> </script>