/********************************************************************************** * $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; } }