/**********************************************************************************
* $URL: https://source.sakaiproject.org/svn/sam/trunk/samigo-app/src/java/org/sakaiproject/jsf/util/SamigoJsfTool.java $
* $Id: SamigoJsfTool.java 106463 2012-04-02 12:20:09Z david.horwitz@uct.ac.za $
**********************************************************************************
*
* Copyright (c) 2005, 2006, 2007, 2008 The Sakai Foundation
*
* Licensed under the Educational Community License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.opensource.org/licenses/ECL-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
**********************************************************************************/
package org.sakaiproject.jsf.util;
import java.io.IOException;
import java.util.Enumeration;
import javax.servlet.RequestDispatcher;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.sakaiproject.tool.api.ActiveTool;
import org.sakaiproject.tool.api.Tool;
import org.sakaiproject.tool.api.ToolException;
import org.sakaiproject.tool.api.ToolSession;
import org.sakaiproject.tool.cover.ActiveToolManager;
import org.sakaiproject.tool.cover.SessionManager;
import org.sakaiproject.tool.cover.ToolManager;
import org.sakaiproject.util.Web;
import org.sakaiproject.tool.assessment.data.ifc.assessment.AssessmentIfc;
import org.sakaiproject.tool.assessment.facade.AssessmentFacade;
import org.sakaiproject.tool.assessment.services.assessment.AssessmentService;
import org.sakaiproject.tool.assessment.services.assessment.PublishedAssessmentService;
import org.sakaiproject.tool.assessment.ui.listener.util.ContextUtil;
import org.sakaiproject.tool.assessment.ui.bean.author.AssessmentSettingsBean;
import org.sakaiproject.tool.assessment.ui.bean.author.AssessmentBean;
import org.sakaiproject.tool.assessment.ui.bean.author.AuthorBean;
import org.sakaiproject.tool.assessment.ui.bean.author.ItemAuthorBean;
import org.sakaiproject.tool.assessment.ui.bean.author.PublishedAssessmentSettingsBean;
import org.sakaiproject.tool.assessment.ui.bean.author.SectionBean;
import org.sakaiproject.tool.assessment.ui.bean.delivery.ItemContentsBean;
import org.sakaiproject.tool.assessment.ui.bean.evaluation.QuestionScoresBean;
import org.sakaiproject.tool.assessment.ui.bean.util.EmailBean;
/**
* <p>
* Customized JsfTool for Samigo - just to workaround the fact that Samigo
* has the JSF URL mapping "*.faces" hard-coded in several places. If
* all instances of "*.faces" were changed to "*.jsf", this class could be removed.
* </p>
*
*/
public class SamigoJsfTool extends JsfTool {
private static final String HELPER_EXT = ".helper";
private static final String HELPER_SESSION_PREFIX = "session.";
private static final String HELPER_RETURN_NOTIFICATION = "/returnToCaller";
private static final String RESET_ASSESSMENT_BEAN = "/resetAssessmentBean";
private static Log log = LogFactory.getLog(SamigoJsfTool.class);
/**
* Recognize a path that is a resource request. It must have an "extension", i.e. a dot followed by characters that do not include a slash.
*
* @param path
* The path to check
* @return true if the path is a resource request, false if not.
*/
protected boolean isResourceRequest(String path)
{
log.debug("****0. inside isResourceRequest, path="+path);
// we need some path
if ((path == null) || (path.length() == 0)) return false;
// we need a last dot
int pos = path.lastIndexOf(".");
if (pos == -1) return false;
// we need that last dot to be the end of the path, not burried in the path somewhere (i.e. no more slashes after the last dot)
String ext = path.substring(pos);
log.debug("****1. inside isResourceRequest, ext="+ext);
if (ext.indexOf("/") != -1) return false;
// these are JSF pages, not resources
// THESE LINES OF CODE IS THE ONLY REASON THIS CLASS EXISTS!
if (ext.equals(".jsf")) return false;
if (ext.equals(".faces")) return false;
if (path.startsWith("/faces/")) return false;
if (path.indexOf(".helper") > -1) return false;
// ok, it's a resource request
return true;
}
protected void dispatch(HttpServletRequest req, HttpServletResponse res) throws ServletException, IOException {
// NOTE: this is a simple path dispatching, taking the path as the view id = jsp file name for the view,
// with default used if no path and a path prefix as configured.
// build up the target that will be dispatched to
String target = req.getPathInfo();
log.debug("***0. dispatch, target ="+target);
// see if we need to reset the assessmentBean, such as when returning
// from a helper
// TODO: there MUST be a cleaner way to do this!! These dependencies
// shouldn't be here!!
if (target != null && target.startsWith(RESET_ASSESSMENT_BEAN)) {
AssessmentBean assessmentBean = (AssessmentBean) ContextUtil
.lookupBeanFromExternalServlet("assessmentBean", req, res);
if (assessmentBean != null && assessmentBean.getAssessmentId() != null) {
AssessmentIfc assessment;
AuthorBean author = (AuthorBean) ContextUtil.lookupBean("author");
AssessmentService assessmentService;
if (author.getIsEditPendingAssessmentFlow()) {
assessmentService = new AssessmentService();
}
else {
assessmentService = new PublishedAssessmentService();
}
assessment = assessmentService.getAssessment(Long.valueOf(assessmentBean.getAssessmentId()));
assessmentBean.setAssessment(assessment);
}
target = target.replaceFirst(RESET_ASSESSMENT_BEAN, "");
}
// see if this is a helper trying to return to caller
if (HELPER_RETURN_NOTIFICATION.equals(target)) {
ToolSession session = SessionManager.getCurrentToolSession();
target = (String) session.getAttribute(ToolManager.getCurrentTool()
.getId()
+ Tool.HELPER_DONE_URL);
if (target != null) {
session.removeAttribute(ToolManager.getCurrentTool().getId()
+ Tool.HELPER_DONE_URL);
res.sendRedirect(target);
return;
}
}
boolean sendToHelper = sendToHelper(req, res);
boolean isResourceRequest = isResourceRequest(target);
log.debug("***1. dispatch, send to helper ="+sendToHelper);
log.debug("***2. dispatch, isResourceRequest ="+ isResourceRequest);
// see if we have a helper request
if (sendToHelper) {
return;
}
if (isResourceRequest) {
// get a dispatcher to the path
RequestDispatcher resourceDispatcher = getServletContext().getRequestDispatcher(target);
if (resourceDispatcher != null) {
resourceDispatcher.forward(req, res);
return;
}
}
if (target == null || "/".equals(target)) {
target = computeDefaultTarget();
// make sure it's a valid path
if (!target.startsWith("/")){
target = "/" + target;
}
// now that we've messed with the URL, send a redirect to make it official
res.sendRedirect(Web.returnUrl(req, target));
return;
}
// see if we want to change the specifically requested view
String newTarget = redirectRequestedTarget(target);
// make sure it's a valid path
if (!newTarget.startsWith("/")){
newTarget = "/" + newTarget;
}
if (!newTarget.equals(target)){
// now that we've messed with the URL, send a redirect to make it official
res.sendRedirect(Web.returnUrl(req, newTarget));
return;
}
target = newTarget;
// store this
ToolSession toolSession = SessionManager.getCurrentToolSession();
if (toolSession!=null){
toolSession.setAttribute(LAST_VIEW_VISITED, target);
}
log.debug("3a. dispatch: toolSession="+toolSession);
log.debug("3b. dispatch: target="+target);
log.debug("3c. dispatch: lastview?"+m_defaultToLastView);
// add the configured folder root and extension (if missing)
target = m_path + target;
// add the default JSF extension (if we have no extension)
int lastSlash = target.lastIndexOf("/");
int lastDot = target.lastIndexOf(".");
if (lastDot < 0 || lastDot < lastSlash){
target += JSF_EXT;
}
// set the information that can be removed from return URLs
req.setAttribute(URL_PATH, m_path);
req.setAttribute(URL_EXT, ".jsp");
// set the sakai request object wrappers to provide the native, not Sakai set up, URL information
// - this assures that the FacesServlet can dispatch to the proper view based on the path info
req.setAttribute(Tool.NATIVE_URL, Tool.NATIVE_URL);
// TODO: Should setting the HTTP headers be moved up to the portal level as well?
res.setContentType("text/html; charset=UTF-8");
res.addDateHeader("Expires", System.currentTimeMillis() - (1000L * 60L * 60L * 24L * 365L));
res.addDateHeader("Last-Modified", System.currentTimeMillis());
res.addHeader("Cache-Control", "no-store, no-cache, must-revalidate, max-age=0, post-check=0, pre-check=0");
res.addHeader("Pragma", "no-cache");
// dispatch to the target
log.debug("***4. dispatch, dispatching path: " + req.getPathInfo() + " to: " + target + " context: "
+ getServletContext().getServletContextName());
// if this is a return from the file picker and going back to
// case 1: item mofification, then set
// itemAuthorbean.attachmentlist = filepicker list
if (target.indexOf("/jsf/author/item/") > -1
&& ("true").equals(toolSession.getAttribute("SENT_TO_FILEPICKER_HELPER"))){
ItemAuthorBean bean = (ItemAuthorBean) ContextUtil.lookupBeanFromExternalServlet(
"itemauthor", req, res);
bean.setItemAttachment();
toolSession.removeAttribute("SENT_TO_FILEPICKER_HELPER");
}
// case 2: part mofification, then set
// sectionBean.attachmentList = filepicker list
else if (target.indexOf("/jsf/author/editPart") > -1
&& ("true").equals(toolSession.getAttribute("SENT_TO_FILEPICKER_HELPER"))){
SectionBean bean = (SectionBean) ContextUtil.lookupBeanFromExternalServlet(
"sectionBean", req, res);
bean.setPartAttachment();
toolSession.removeAttribute("SENT_TO_FILEPICKER_HELPER");
}
// case 3.1: assessment settings mofification, then set assessmentSettingsBean.attachmentList = filepicker list
else if (target.indexOf("/jsf/author/authorSettings") > -1
&& ("true").equals(toolSession.getAttribute("SENT_TO_FILEPICKER_HELPER"))){
AssessmentSettingsBean bean = (AssessmentSettingsBean) ContextUtil.lookupBeanFromExternalServlet(
"assessmentSettings", req, res);
bean.setAssessmentAttachment();
toolSession.removeAttribute("SENT_TO_FILEPICKER_HELPER");
}
// case 3.2: published assessment settings mofification, then set assessmentSettingsBean.attachmentList = filepicker list
else if (target.indexOf("/jsf/author/publishedSettings") > -1
&& ("true").equals(toolSession.getAttribute("SENT_TO_FILEPICKER_HELPER"))){
PublishedAssessmentSettingsBean bean = (PublishedAssessmentSettingsBean) ContextUtil.lookupBeanFromExternalServlet(
"publishedSettings", req, res);
bean.setAssessmentAttachment();
toolSession.removeAttribute("SENT_TO_FILEPICKER_HELPER");
}
// case 4: create new mail, then set
// emailBean.attachmentList = filepicker list
else if (target.indexOf("/jsf/evaluation/createNewEmail") > -1
&& ("true").equals(toolSession.getAttribute("SENT_TO_FILEPICKER_HELPER"))) {
EmailBean bean = (EmailBean) ContextUtil.lookupBeanFromExternalServlet("email", req, res);
bean.prepareAttachment();
toolSession.removeAttribute("SENT_TO_FILEPICKER_HELPER");
}
else if (target.indexOf("/jsf/evaluation/questionScore") > -1
&& ("true").equals(toolSession.getAttribute("SENT_TO_FILEPICKER_HELPER"))) {
QuestionScoresBean bean = (QuestionScoresBean) ContextUtil.lookupBeanFromExternalServlet("questionScores", req, res);
bean.setAttachment((Long) toolSession.getAttribute("itemGradingId"));
toolSession.removeAttribute("SENT_TO_FILEPICKER_HELPER");
}
else if (target.indexOf("/jsf/evaluation/gradeStudentResult") > -1
&& ("true").equals(toolSession.getAttribute("SENT_TO_FILEPICKER_HELPER"))) {
ItemContentsBean bean = (ItemContentsBean) ContextUtil.lookupBeanFromExternalServlet("itemContents", req, res);
bean.setAttachment((Long) toolSession.getAttribute("itemGradingId"));
toolSession.removeAttribute("SENT_TO_FILEPICKER_HELPER");
}
RequestDispatcher dispatcher = getServletContext().getRequestDispatcher(target);
dispatcher.forward(req, res);
// restore the request object
req.removeAttribute(Tool.NATIVE_URL);
req.removeAttribute(URL_PATH);
req.removeAttribute(URL_EXT);
}
protected boolean sendToHelper(HttpServletRequest req, HttpServletResponse res)
throws ToolException {
String path = req.getPathInfo();
if (path == null) path = "/";
// 0 parts means the path was just "/", otherwise parts[0] = "", parts[1] = item id, parts[2]
// if present is "edit"...
String[] parts = path.split("/");
log.debug("***a. sendToHelper.partLength="+parts.length);
String helperPath =null;
String toolPath=null;
// e.g. helper url in Samigo can be /jsf/author/item/sakai.filepicker.helper/tool
// or /sakai.filepicker.helper
if (parts.length > 2){
log.debug("***b. sendToHelper.partLength="+parts.length);
helperPath = parts[parts.length - 2];
toolPath = parts[parts.length - 1];
}
else if (parts.length == 2){
log.debug("***c. sendToHelper.partLength="+parts.length);
helperPath = parts[1];
}
else return false;
if (!helperPath.endsWith(HELPER_EXT)) return false;
log.debug("****d. sendToHelper, part #1="+helperPath);
log.debug("****e. sendToHelper, part #2="+toolPath);
ToolSession toolSession = SessionManager.getCurrentToolSession();
toolSession.setAttribute("SENT_TO_FILEPICKER_HELPER", "true");
Enumeration params = req.getParameterNames();
while (params.hasMoreElements()) {
String paramName = (String)params.nextElement();
if (paramName.startsWith(HELPER_SESSION_PREFIX)) {
String attributeName = paramName.substring(HELPER_SESSION_PREFIX.length());
toolSession.setAttribute(attributeName, req.getParameter(paramName));
}
}
// calc helper id
int posEnd = helperPath.lastIndexOf(".");
String helperId = helperPath.substring(0, posEnd);
log.debug("****f. sendToHelper, helperId="+helperId);
ActiveTool helperTool = ActiveToolManager.getActiveTool(helperId);
String url = req.getContextPath() + req.getServletPath();
if (toolSession.getAttribute(helperTool.getId() + Tool.HELPER_DONE_URL) == null) {
toolSession.setAttribute(helperTool.getId() + Tool.HELPER_DONE_URL,
url + RESET_ASSESSMENT_BEAN + computeDefaultTarget(true));
}
log.debug("****g. sendToHelper, url="+url);
String context = url + "/"+ helperPath;
log.debug("****h. sendToHelper, context="+context);
if (toolPath != null)
helperTool.help(req, res, context, "/"+toolPath);
else
helperTool.help(req, res, context, "");
return true; // was handled as helper call
}
protected String computeDefaultTarget(boolean lastVisited){
// setup for the default view as configured
ToolSession session = SessionManager.getCurrentToolSession();
String target = "/" + m_default;
// if we are doing lastVisit and there's a last-visited view, for this tool placement / user, use that
if (lastVisited) {
String last = (String) session.getAttribute(LAST_VIEW_VISITED);
if (last != null) {
target = last;
}
}
session.removeAttribute(LAST_VIEW_VISITED);
log.debug("***3. computeDefaultTarget()="+target);
return target;
}
}