org.sakaiproject.cheftool.VelocityPortletPaneledAction Maven / Gradle / Ivy
/**********************************************************************************
* $URL$
* $Id$
***********************************************************************************
*
* Copyright (c) 2003, 2004, 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.cheftool;
import java.io.IOException;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedHashSet;
import java.util.Locale;
import java.util.Map;
import java.util.Set;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.apache.commons.lang3.StringUtils;
import org.sakaiproject.authz.cover.SecurityService;
import org.sakaiproject.cheftool.api.Alert;
import org.sakaiproject.cheftool.api.Menu;
import org.sakaiproject.cheftool.menu.MenuEntry;
import org.sakaiproject.component.cover.ComponentManager;
import org.sakaiproject.component.cover.ServerConfigurationService;
import org.sakaiproject.content.api.ContentHostingService;
import org.sakaiproject.event.api.SessionState;
import org.sakaiproject.event.api.UsageSession;
import org.sakaiproject.event.cover.UsageSessionService;
import org.sakaiproject.portal.util.PortalUtils;
import org.sakaiproject.tool.api.Placement;
import org.sakaiproject.tool.api.Session;
import org.sakaiproject.tool.api.Tool;
import org.sakaiproject.tool.api.ToolException;
import org.sakaiproject.tool.api.ToolSession;
import org.sakaiproject.user.api.Preferences;
import org.sakaiproject.user.api.PreferencesService;
import org.sakaiproject.tool.cover.SessionManager;
import org.sakaiproject.tool.cover.ToolManager;
import org.sakaiproject.util.EditorConfiguration;
import org.sakaiproject.util.ParameterParser;
import org.sakaiproject.util.RequestFilter;
import org.sakaiproject.util.ResourceLoader;
import org.sakaiproject.util.Web;
import org.sakaiproject.util.api.FormattedText;
import org.sakaiproject.vm.ActionURL;
import lombok.extern.slf4j.Slf4j;
/**
*
* VelocityPortletPaneledAction ...
*
*/
@SuppressWarnings("deprecation")
@Slf4j
public abstract class VelocityPortletPaneledAction extends ToolServlet
{
private static final long serialVersionUID = 1L;
/** message bundle */
private static ResourceLoader rb = new ResourceLoader("velocity-tool");
protected static final String BUTTON = "eventSubmit_";
/** The currently active helper mode static class. */
public static final String STATE_HELPER = "vppa.helper";
protected static final String STATE_MODE = "mode";
protected static final String STATE_OBSERVER = "obsever";
protected static final String STATE_ACTION = "action";
protected static final String STATE_NEW_PANEL = "state:new_panel";
/** The name of the context variable containing the identifier for the site's root content collection */
protected static final String CONTEXT_SITE_COLLECTION_ID = "vppa_site_collection_id";
/** The name of the context variable containing the access URL for the site's root content collection */
protected static final String CONTEXT_SITE_COLLECTION_URL = "vppa_site_collection_url";
/** The panel name of the main panel - append the tool's id. */
protected static final String LAYOUT_MAIN = "Main";
/** The name of the param used for CSRF protection */
protected static final String SAKAI_CSRF_TOKEN = "sakai_csrf_token";
/** Constants to handle helper situations */
protected static final String HELPER_LINK_MODE = "link_mode";
protected static final String HELPER_MODE_DONE = "helper.done";
private ContentHostingService contentHostingService;
private FormattedText formattedText;
public VelocityPortletPaneledAction() {
contentHostingService = (ContentHostingService) ComponentManager.get(ContentHostingService.class.getName());
formattedText = ComponentManager.get(FormattedText.class);
}
protected void initState(SessionState state, VelocityPortlet portlet, JetspeedRunData rundata)
{
HttpServletRequest req = rundata.getRequest();
Session session = SessionManager.getCurrentSession();
if (getVmReference("is_wireless_device", req) == null)
{
Object c = session.getAttribute("is_wireless_device");
setVmReference("is_wireless_device", c, req);
}
// Set a CSRF token for velocity-based forms
if (getVmReference(SAKAI_CSRF_TOKEN, req) == null)
{
Object csrfToken = session.getAttribute(UsageSessionService.SAKAI_CSRF_SESSION_ATTRIBUTE);
setVmReference(SAKAI_CSRF_TOKEN, csrfToken, req);
}
}
/**
* Compute the courier update html element id for the main panel - add "." and other names for inner panels.
*
* @param toolId
* The tool (portlet) id.
* @return The courier update html element id for the main panel.
*/
public static String mainPanelUpdateId(String toolId)
{
// TODO: who should be responsible for "Main" here? It's a Portal thing... -ggolden
return ComponentManager.get(FormattedText.class).escapeJavascript("Main" + toolId);
} // mainPanelUpdateId
/**
* Compute the courier update html element id for the title panel.
*
* @param toolId
* The tool (portlet) id.
* @return The courier update html element id for the title panel.
*/
public static String titlePanelUpdateId(String toolId)
{
// TODO: who should be responsible for "Title" here? It's a Portal thing... -ggolden
return ComponentManager.get(FormattedText.class).escapeJavascript("Title" + toolId);
} // titlePanelUpdateId
/**
* Add another string to the alert message.
* Defaults to removing duplicates from the alert message
*
* @param state
* The session state.
* @param message
* The string to add.
*/
public static void addAlert(SessionState state, String message) {
addAlert(state, message, true);
}
/**
* Add another string to the alert message.
*
* @param state
* The session state.
* @param message
* The string to add.
* @param removeDuplicates
* Remove duplicates from the alert
*/
public static void addAlert(SessionState state, String message, boolean removeDuplicates)
{
String soFar = (String) state.getAttribute(STATE_MESSAGE);
if (soFar == null)
{
soFar = message;
}
else if (!removeDuplicates || !soFar.contains(message))
{
soFar += "
" + message;
}
state.setAttribute(STATE_MESSAGE, soFar);
} // addAlert
/**
* Add another string to the flash notification message.
*
* @param state
* The session state.
* @param message
* The string to add.
*/
public static void addFlashNotif(SessionState state, String message)
{
String soFar = (String) state.getAttribute(STATE_NOTIF);
if (soFar != null)
{
soFar = soFar + "\n\n" + message;
}
else
{
soFar = message;
}
state.setAttribute(STATE_NOTIF, soFar);
} // addAlert
/**
* Switch to a new panel
*
* @param state
* The session state.
* @param newPanel
* The new panel name
*/
public static void switchPanel(SessionState state, String newPanel)
{
state.setAttribute(STATE_NEW_PANEL, newPanel);
} // addAlert
/**
* Initialize for the first time the session state for this session. If overridden in a sub-class, make sure to call super.
*
* @param state
* The session state.
* @param req
* The current portlet request.
* @param res
* The current portlet response.
*/
protected void initState(SessionState state, HttpServletRequest req, HttpServletResponse res)
{
super.initState(state, req, res);
// call the old initState:
VelocityPortlet portlet = (VelocityPortlet) req.getAttribute(ATTR_PORTLET);
JetspeedRunData rundata = (JetspeedRunData) req.getAttribute(ATTR_RUNDATA);
initState(state, portlet, rundata);
} // initState
/**
* Update for this request processing the session state. If overridden in a sub-class, make sure to call super.
*
* @param state
* The session state.
* @param req
* The current portlet request.
* @param res
* The current portlet response.
*/
protected void updateState(SessionState state, HttpServletRequest req, HttpServletResponse res)
{
super.updateState(state, req, res);
// the old way has just initState, so...
VelocityPortlet portlet = (VelocityPortlet) req.getAttribute(ATTR_PORTLET);
JetspeedRunData rundata = (JetspeedRunData) req.getAttribute(ATTR_RUNDATA);
initState(state, portlet, rundata);
} // updateState
/**
* Dispatch to a "do" method based on reflection. Override ToolServlet to support the old "build" ways.
*
* @param methodBase
* The base name of the method to call.
* @param methodExt
* The end name of the method to call.
* @param req
* The HttpServletRequest.
* @param res
* The HttpServletResponse
*/
protected void toolModeDispatch(String methodBase, String methodExt, HttpServletRequest req, HttpServletResponse res)
throws ToolException
{
// the context wraps our real vm attribute set
Context context = (Context) req.getAttribute(ATTR_CONTEXT);
// other wrappers
VelocityPortlet portlet = (VelocityPortlet) req.getAttribute(ATTR_PORTLET);
JetspeedRunData rundata = (JetspeedRunData) req.getAttribute(ATTR_RUNDATA);
// "panel" is used to identify the specific panel in the URL
context.put("param_panel", ActionURL.PARAM_PANEL);
// set the "action"
context.put("action", getState(req).getAttribute(STATE_ACTION));
// set the "pid"
context.put("param_pid", ActionURL.PARAM_PID);
context.put("pid", getPid(req));
String collectionId = contentHostingService.getSiteCollection(ToolManager.getCurrentPlacement().getContext());
context.put(CONTEXT_SITE_COLLECTION_ID, collectionId);
// indicate which WYSIWYG editor to use in legacy tools
String editor = EditorConfiguration.getWysiwigEditor();
context.put("sakai_editor", editor);
context.put("editorConfig", new EditorConfiguration());
UsageSession session = UsageSessionService.getSession();
if (session != null)
{
// SAK-23047 Set the proper country code in the chef_start generated markup
String userId = session.getUserId();
ResourceLoader rl = new ResourceLoader(userId);
Locale locale = rl.getLocale();
String languageCode = locale.getLanguage();
String countryCode = locale.getCountry();
if(countryCode != null && countryCode.length() > 0) {
languageCode += "-" + countryCode;
}
context.put("language",languageCode);
context.put("dir", rl.getOrientation(locale));
String userTheme = "sakaiUserTheme-notSet";
boolean sakaiThemesEnabled = ServerConfigurationService.getBoolean("portal.themes", true);
if ( sakaiThemesEnabled ) {
String thisUser = SessionManager.getCurrentSessionUserId();
PreferencesService preferencesService = ComponentManager.get(PreferencesService.class);
Preferences prefs = preferencesService.getPreferences(thisUser);
if ( prefs != null ) {
userTheme = StringUtils.defaultIfEmpty(prefs.getProperties(PreferencesService.USER_SELECTED_UI_THEME_PREFS).getProperty("theme"), "sakaiUserTheme-notSet");
}
}
context.put("userTheme", userTheme);
String browserId = session.getBrowserId();
if (UsageSession.WIN_IE.equals(browserId) || UsageSession.WIN_MZ.equals(browserId)
|| UsageSession.WIN_NN.equals(browserId) || UsageSession.MAC_MZ.equals(browserId)
|| UsageSession.MAC_NN.equals(browserId))
{
context.put("wysiwyg", "true");
}
}
try
{
// dispatch panels (Note: panel must be in the URL, not the body - this is not from the parsed params)
String panel = ((ParameterParser) req.getAttribute(ATTR_PARAMS)).getString(ActionURL.PARAM_PANEL);
/*
* TODO: float support from before... // special case for floating and the Main panel: if (LAYOUT_MAIN.equals(panel)) { if (handleFloat(portlet, context, rundata, state)) return; }
*/
if (panel == null || "".equals(panel) || "null".equals(panel))
{
// default to main panel
panel = LAYOUT_MAIN;
} else {
// sanitize value
panel = panel.replaceAll("[\r\n]","");
}
context.put("panel", panel);
// form a method name "build" + panel name (first letter caps) + "PanelContext"
// buildPanelContext( VelocityPortlet, Context, ControllerState, RunData )
Class[] types = new Class[4];
types[0] = VelocityPortlet.class;
types[1] = Context.class;
types[2] = RunData.class;
types[3] = SessionState.class;
// let our extension classes override the pannel name for the method
String methodName = panelMethodName(panel);
Method method = getClass().getMethod(methodName, types);
Object[] args = new Object[4];
args[0] = portlet;
args[1] = context;
args[2] = rundata;
args[3] = getState(req);
String template = (String) method.invoke(this, args);
// if the method did something like a redirect, we don't want to try to put out any more
if (!res.isCommitted())
{
if (template == null)
{
// pick the template for the panel - the base + "-" + panel
template = (String) getContext(rundata).get("template") + "-" + panel;
}
// the vm file needs a path and an extension
if(!template.equals(MODE_PERMISSIONS)) {
template = "/vm/" + template;
}
template += ".vm";
// setup for old style alert
StringBuilder buf = new StringBuilder();
String msg = (String) getState(req).getAttribute(STATE_MESSAGE);
if (msg != null)
{
buf.append(msg);
getState(req).removeAttribute(STATE_MESSAGE);
}
Alert alert = getAlert(req);
if (!alert.isEmpty())
{
buf.append(alert.peekAlert());
setVmReference(ALERT_ATTR, alert, req);
}
if (buf.length() > 0)
{
setVmReference("alertMessage", buf.toString(), req);
}
//set up for duplicate site alert
StringBuilder sbNotif = new StringBuilder();
String sNotif = (String) getState(req).getAttribute(STATE_NOTIF);
if (sNotif != null)
{
sbNotif.append(sNotif);
getState(req).removeAttribute(STATE_NOTIF);
}
if (sbNotif.length() > 0)
{
setVmReference("flashNotif", sbNotif.toString(), req);
setVmReference("flashNotifCloseTitle",rb.getString("flashNotifCloseTitle"),req);
}
// setup for old style validator
setVmReference("validator", m_validator, req);
setVmReference("formattedText", formattedText, req);
// set standard no-cache headers
setNoCacheHeaders(res);
// add a standard header
includeVm("chef_header.vm", req, res);
includeVm(template, req, res);
// add a standard footer
includeVm("chef_footer.vm", req, res);
}
}
catch (NoSuchMethodException e)
{
try {
res.sendError(HttpServletResponse.SC_BAD_REQUEST, "NoSuchMethodException for panel name");
} catch (IOException e1) {
// ignore
}
}
catch (IllegalAccessException e)
{
throw new ToolException(e);
}
catch (InvocationTargetException e)
{
throw new ToolException(e);
}
catch (ServletException e)
{
throw new ToolException(e);
}
} // toolModeDispatch
/**
* Allow extension classes to control which build method gets called for this pannel
* @param panel
* @return
*/
protected String panelMethodName(String panel)
{
return "build" + panel + "PanelContext";
}
/**
* Process a Portlet action.
*/
public void processAction(HttpServletRequest req, HttpServletResponse res)
{
// lets use the parsed params
JetspeedRunData rundata = (JetspeedRunData) req.getAttribute(ATTR_RUNDATA);
ParameterParser params = rundata.getParameters();
// see if there's an action parameter, whose value has the action to use
String action = params.get(PARAM_ACTION);
// if that's not present, see if there's a combination name with the action encoded in the name
if (action == null)
{
Iterator names = params.getNames();
while (names.hasNext())
{
String name = names.next();
if (name.startsWith(BUTTON))
{
action = name.substring(BUTTON.length());
break;
}
}
}
else
{
// SAK-18148 look for first non empty action
if ("".equals(action))
{
String[] actions = params.getStrings(PARAM_ACTION);
if (actions != null)
{
for (int i = 0; i < actions.length; i++)
{
if (!"".equals(actions[i]))
{
action = actions[i];
break;
}
}
}
}
}
// process the action if present
if (action != null)
{
if (!checkCSRFToken(req, rundata, action)) return;
// if we have an active helper, send the action there
String helperClass = (String) getState(req).getAttribute(STATE_HELPER);
if (helperClass != null)
{
helperActionDispatch("", action, req, res, helperClass);
}
else
{
actionDispatch("", action, req, res);
}
// Handle shortcut return from a tool helper between its post and redirect
// Helper does this in an Action method:
// SessionManager.getCurrentToolSession().setAttribute(HELPER_LINK_MODE, HELPER_MODE_DONE);
// and then returns
ToolSession toolSession = SessionManager.getCurrentToolSession();
if (HELPER_MODE_DONE.equals(toolSession.getAttribute(HELPER_LINK_MODE)))
{
Tool tool = ToolManager.getCurrentTool();
String url = (String) toolSession.getAttribute(tool.getId() + Tool.HELPER_DONE_URL);
toolSession.removeAttribute(tool.getId() + Tool.HELPER_DONE_URL);
toolSession.removeAttribute(HELPER_LINK_MODE);
if ( url != null )
{
try
{
res.sendRedirect(url);
return;
}
catch (IOException e)
{
log.warn("IOException: ", e);
}
}
}
// Continue non-shortcut processing
// redirect to the tool's registration's url, with the tool id (pid) and panel
// Tool tool = (Tool) req.getAttribute(ATTR_TOOL);
// TODO: redirect url? pannel? placement id?
String url = Web.returnUrl(req, null);
String panel = ((ParameterParser) req.getAttribute(ATTR_PARAMS)).getString(ActionURL.PARAM_PANEL);
String newPanel = (String) getState(req).getAttribute(STATE_NEW_PANEL);
getState(req).removeAttribute(STATE_NEW_PANEL);
if ( newPanel != null ) panel = newPanel;
if (panel == null || panel.equals("") || panel.equals("null")) {
panel = MAIN_PANEL;
} else {
// sanitize value
panel = panel.replaceAll("[\r\n]","");
}
String redirect = url + "?" + ActionURL.PARAM_PANEL + "=" + panel;
try
{
//to prevent the 'response already committed' error
if(!(res.isCommitted())) {
res.sendRedirect(redirect);
}
}
catch (IOException e)
{
}
}
else
{
log.debug("processAction: no action");
}
} // processAction
public boolean checkCSRFToken(HttpServletRequest request, RunData rundata, String action)
{
ParameterParser params = rundata.getParameters();
// if user if manipulating data via POST, check for presence of CSRF token
if ("POST".equals(rundata.getRequest().getMethod()))
{
// check if tool id is in list of tools to skip the CSRF check
Placement placement = ToolManager.getCurrentPlacement();
String toolId = null;
if (placement != null)
{
toolId = placement.getToolId();
}
boolean skipCSRFCheck = false;
String[] insecureTools = ServerConfigurationService.getStrings("velocity.csrf.insecure.tools");
if (toolId != null && insecureTools != null)
{
for (int i = 0; i < insecureTools.length; i++)
{
if (StringUtils.equalsIgnoreCase(toolId, insecureTools[i]))
{
if (log.isDebugEnabled())
{
log.debug("Will skip all CSRF checks on toolId=" + toolId);
}
skipCSRFCheck = true;
break;
}
}
}
// if the user is not logged in, then do not worry about csrf
Session session = SessionManager.getCurrentSession();
boolean loggedIn = session.getUserId() != null;
if (loggedIn && !skipCSRFCheck)
{
// If the attribute is missing, it is likely an internal error,
// not an error in the tool
Object sessionAttr = SessionManager.getCurrentSession().getAttribute(UsageSessionService.SAKAI_CSRF_SESSION_ATTRIBUTE);
if ( sessionAttr == null )
{
log.warn("Missing CSRF Token session attribute: " + action + "; toolId=" + toolId);
return false;
}
String csrfToken = params.getString(SAKAI_CSRF_TOKEN);
String sessionToken = sessionAttr.toString();
if (csrfToken == null || sessionToken == null || !StringUtils.equals(csrfToken, sessionToken))
{
log.warn("CSRF Token mismatched or missing on velocity action: " + action + "; toolId=" + toolId);
return false;
}
if (log.isDebugEnabled())
{
log.debug("CSRF token (" + csrfToken + ") matches on action: " + action + "; toolId=" + toolId);
}
}
}
return true;
}
/**
* Dispatch to a "processAction" method based on reflection.
*
* @param methodBase
* The base name of the method to call.
* @param methodExt
* The end name of the method to call.
* @param req
* The HttpServletRequest.
* @param res
* The HttpServletResponse
* @throws PortletExcption,
* IOException, just like the "do" methods.
*/
protected void actionDispatch(String methodBase, String methodExt, HttpServletRequest req, HttpServletResponse res)
{
String methodName = null;
try
{
// the method signature
Class[] signature = new Class[2];
signature[0] = RunData.class;
signature[1] = Context.class;
// the method name
methodName = methodBase + methodExt;
// find a method of this class with this name and signature
Method method = getClass().getMethod(methodName, signature);
// parameters - the context for a "do" should not be used, so we will send in null
Object[] args = new Object[2];
args[0] = (JetspeedRunData) req.getAttribute(ATTR_RUNDATA);
args[1] = null;
// make the call
method.invoke(this, args);
}
catch (NoSuchMethodException e)
{
// try for a single parameter call
try
{
// the method signature
Class[] signature = new Class[1];
signature[0] = RunData.class;
// the method name
methodName = methodBase + methodExt;
// find a method of this class with this name and signature
Method method = getClass().getMethod(methodName, signature);
// parameters - the context for a "do" should not be used, so we will send in null
Object[] args = new Object[1];
args[0] = (JetspeedRunData) req.getAttribute(ATTR_RUNDATA);
// make the call
method.invoke(this, args);
}
catch (NoSuchMethodException e2)
{
log.warn("Exception calling method " + methodName + " " + e2);
}
catch (IllegalAccessException e2)
{
log.warn("Exception calling method " + methodName + " " + e2);
}
catch (InvocationTargetException e2)
{
String xtra = "";
if (e2.getCause() != null) xtra = " (Caused by " + e2.getCause() + ")";
log.warn("Exception calling method " + methodName + " " + e2 + xtra, e2);
}
}
catch (IllegalAccessException e)
{
log.warn("Exception calling method " + methodName + " " + e);
}
catch (InvocationTargetException e)
{
String xtra = "";
if (e.getCause() != null) xtra = " (Caused by " + e.getCause() + ")";
log.warn("Exception calling method " + methodName + " " + e + xtra, e);
}
} // actionDispatch
/**
* Dispatch to a "processAction" method based on reflection in a helper class.
*
* @param methodBase
* The base name of the method to call.
* @param methodExt
* The end name of the method to call.
* @param req
* The HttpServletRequest.
* @param res
* The HttpServletResponse
* @throws PortletExcption,
* IOException, just like the "do" methods.
*/
protected void helperActionDispatch(String methodBase, String methodExt, HttpServletRequest req, HttpServletResponse res,
String className)
{
String methodName = null;
try
{
// the method signature
Class[] signature = new Class[1];
signature[0] = RunData.class;
// the method name
methodName = methodBase + methodExt;
Class cls = Class.forName(className);
// find a method of this class with this name and signature
Method method = cls.getMethod(methodName, signature);
// parameters - the context for a "do" should not be used, so we will send in null
Object[] args = new Object[1];
args[0] = (JetspeedRunData) req.getAttribute(ATTR_RUNDATA);
// make the call
method.invoke(this, args);
}
catch (ClassNotFoundException e)
{
log.warn("Exception helper class not found " + e);
}
catch (NoSuchMethodException e)
{
log.warn("Exception calling method " + methodName + " " + e);
}
catch (IllegalAccessException e)
{
log.warn("Exception calling method " + methodName + " " + e);
}
catch (InvocationTargetException e)
{
String xtra = "";
if (e.getCause() != null) xtra = " (Caused by " + e.getCause() + ")";
log.warn("Exception calling method " + methodName + " " + e + xtra);
}
} // helperActionDispatch
/**
* This is used to get "template" from the map, the default template registered for the tool in chef_tools.xreg.
*/
protected Map getContext(RunData data)
{
// get template from the servlet config
String template = getServletConfig().getInitParameter("template");
Map rv = new HashMap();
rv.put("template", template);
return rv;
}
// OPTIONS SUPPORT
public static final String STATE_OBSERVER2 = "obsever2";
public static final String STATE_PRESENCE_OBSERVER = "presence_observer";
public static final String STATE_FLOAT = "float";
public static final String STATE_TOOL = "tool";
public static final String STATE_TOOL_KEY = "tool_key";
public static final String STATE_BUNDLE_KEY = "bundle_key";
public static final String STATE_MESSAGE = "message";
public static final String STATE_NOTIF = "notification";
/** Standard modes. */
public static final String MODE_OPTIONS = "options";
public static final String MODE_PERMISSIONS = "permissions";
/**
* Handle a request to set options.
*/
public void doOptions(RunData runData, Context context)
{
// ignore if not allowed
if (!allowedToOptions())
{
return;
//msg = "you do not have permission to set options for this Worksite.";
}
Placement placement = ToolManager.getCurrentPlacement();
String pid = null;
if (placement != null) pid = placement.getId();
SessionState state = ((JetspeedRunData) runData).getPortletSessionState(pid);
// go into options mode
state.setAttribute(STATE_MODE, MODE_OPTIONS);
// if we're not in the main panel for this tool, schedule an update of the main panel
String currentPanelId = runData.getParameters().getString(ActionURL.PARAM_PANEL);
if (!LAYOUT_MAIN.equals(currentPanelId))
{
String mainPanelId = mainPanelUpdateId(pid);
schedulePeerFrameRefresh(mainPanelId);
}
} // doOptions
protected String build_permissions_context(VelocityPortlet portlet, Context context, RunData data, SessionState state) {
String toolKey = (String) state.getAttribute(STATE_TOOL_KEY);
context.put("toolKey", toolKey);
String bundleKey = (String) state.getAttribute(STATE_BUNDLE_KEY);
if(StringUtils.isNotBlank(bundleKey)){
context.put("bundleKey", bundleKey);
}
context.put("permissions", rb.getString("permissions"));
return MODE_PERMISSIONS;
}
/**
* Complete the options process with a save.
*/
protected void saveOptions()
{
// ask the current placement to save
Placement placement = ToolManager.getCurrentPlacement();
if (placement != null)
{
placement.save();
}
} // saveOptions
/**
* Cancel the options process.
*/
protected void cancelOptions()
{
// TODO: how to indicate that we need to get clean placement options into the current tool?
} // cancelOptions
/**
* Add the options to the menu bar, if allowed.
*
* @param bar
* The menu bar to add to,
* @param ref
* The resource reference to base the security decision upon.
*/
protected void addOptionsMenu(Menu bar, JetspeedRunData data) // %%% don't need data -ggolden
{
if (allowedToOptions())
{
bar.add(new MenuEntry(rb.getString("options"), "doOptions"));
}
} // addOptionsMenu
/**
* Check if the current user is allowed to do options for the current context (site based)
* @return true if the user is allowed to modify the current context's options, false if not.
*/
protected boolean allowedToOptions()
{
Placement placement = ToolManager.getCurrentPlacement();
String context = null;
if (placement != null) context = placement.getContext();
// TODO: stolen from site -ggolden
if (SecurityService.unlock("site.upd", "/site/" + context))
{
return true;
}
return false;
}
/**
* Handle the "reset tool" option from the Title bar.
*/
public void doReset(RunData runData, Context context)
{
// access the portlet element id to find "our" state (i.e. the state for this portlet)
String peid = ((JetspeedRunData) runData).getJs_peid();
SessionState state = ((JetspeedRunData) runData).getPortletSessionState(peid);
// clear this state
resetTool(state);
// // make sure the Main panel is updated
String main = VelocityPortletPaneledAction.mainPanelUpdateId(peid);
schedulePeerFrameRefresh(main);
} // doReset
/**
* Reset the tool (state) to "home" conditions. Default here is to clear everything from state.
*
* @param state
* The tool's session state.
*/
protected void resetTool(SessionState state)
{
state.clear();
} // resetTool
/**
* Add some standard references to the vm context.
*
* @param request
* The render request.
* @param response
* The render response.
*/
@SuppressWarnings("unchecked")
protected void setVmStdRef(HttpServletRequest request, HttpServletResponse response)
{
// pick up all the super ones
super.setVmStdRef(request, response);
// add a "$config" which accesses the config service
setVmReference("config", ServerConfigurationService.getInstance(), request);
// add the pid
setVmReference("pid", getPid(request), request);
// add the css version
final String query = PortalUtils.getCDNQuery();
setVmReference("portalCdnQuery", query, request);
// check for a scheduled peer frame or focus refresh
ToolSession session = SessionManager.getCurrentToolSession();
if (session != null)
{
if (session.getAttribute(ATTR_TOP_REFRESH) != null)
{
setVmReference("topRefresh", Boolean.TRUE, request);
session.removeAttribute(ATTR_TOP_REFRESH);
}
Set ids = (Set) session.getAttribute(ATTR_FRAME_REFRESH);
if (ids != null)
{
setVmReference("frameRefresh", ids, request);
session.removeAttribute(ATTR_FRAME_REFRESH);
}
String focusPath = (String) session.getAttribute(ATTR_FRAME_FOCUS);
if (focusPath != null)
{
setVmReference("focusChange", focusPath, request);
session.removeAttribute(ATTR_FRAME_FOCUS);
}
}
Tool tool = ToolManager.getCurrentTool();
if (tool != null)
{
setVmReference("toolTitle", tool.getTitle(), request);
}
}
/** A Context bound into the request attributes. */
protected final static String ATTR_CONTEXT = "sakai.wrapper.context";
/** A PortletConfig bound into the request attributes. */
protected final static String ATTR_CONFIG = "sakai.wrapper.config";
/** A VelocityPortlet bound into the request attributes. */
protected final static String ATTR_PORTLET = "sakai.wrapper.portlet";
/** A JetspeedRunData bound into the request attributes. */
protected final static String ATTR_RUNDATA = "sakai.wrapper.rundata";
/**
* Respond to a request by dispatching to a portlet like "do" method based on the portlet mode and tool mode
*/
protected void doGet(HttpServletRequest req, HttpServletResponse res) throws ServletException
{
// set in VmServlet
ParameterParser params = (ParameterParser) req.getAttribute(ATTR_PARAMS);
// we will need some covers... Note: parameters are parsed (i.e. files are read) here
Context context = new Context(this, req);
Placement placement = ToolManager.getCurrentPlacement();
PortletConfig config = new PortletConfig(getServletConfig(), placement.getPlacementConfig(), placement.getTool()
.getRegisteredConfig(), placement);
VelocityPortlet portlet = new VelocityPortlet(getPid(req), config);
JetspeedRunData rundata = new JetspeedRunData(req, getState(req), getPid(req), params);
req.setAttribute(ATTR_CONTEXT, context);
req.setAttribute(ATTR_CONFIG, config);
req.setAttribute(ATTR_PORTLET, portlet);
req.setAttribute(ATTR_RUNDATA, rundata);
super.doGet(req, res);
}
// Set up RunData if it's not already set up
protected void checkRunData(HttpServletRequest req)
{
if (req.getAttribute(ATTR_RUNDATA) != null)
return;
// set in VmServlet
ParameterParser params = (ParameterParser) req.getAttribute(ATTR_PARAMS);
JetspeedRunData rundata = new JetspeedRunData(req, getState(req), getPid(req), params);
req.setAttribute(ATTR_RUNDATA, rundata);
}
/** Tool session attribute name used to schedule a peer frame refresh. */
public static final String ATTR_FRAME_REFRESH = "sakai.vppa.frame.refresh";
/** Tool session attribute name used to schedule a whole page refresh. */
public static final String ATTR_TOP_REFRESH = "sakai.vppa.top.refresh";
/** Tool session attribute name used to schedule a focus change. */
public static final String ATTR_FRAME_FOCUS = "sakai.vppa.frame.focus";
/**
* Schedule a refresh for whole page
*/
protected void scheduleTopRefresh()
{
ToolSession session = SessionManager.getCurrentToolSession();
// add to (or create) our set of ids to refresh
if (session.getAttribute(ATTR_TOP_REFRESH) == null)
{
session.setAttribute(ATTR_TOP_REFRESH, Boolean.TRUE);
}
}
/**
* Schedule a refresh for a peer frame.
*
* @param id
* The peer frame's id.
*/
@SuppressWarnings("unchecked")
protected void schedulePeerFrameRefresh(String id)
{
ToolSession session = SessionManager.getCurrentToolSession();
// add to (or create) our set of ids to refresh
Set soFar = (Set) session.getAttribute(ATTR_FRAME_REFRESH);
if (soFar == null)
{
soFar = new HashSet();
session.setAttribute(ATTR_FRAME_REFRESH, soFar);
}
soFar.add(id);
}
/**
* Schedule a focus change.
*
* @param path
* The desired focus path elements
*/
protected void scheduleFocusRefresh(String[] path)
{
ToolSession session = SessionManager.getCurrentToolSession();
// make the js string from the elements
String jsArray = "[";
for (int i = 0; i < path.length; i++)
{
if (i > 0)
{
jsArray = jsArray + ",";
}
jsArray = jsArray + " \"" + path[i] + "\"";
}
jsArray = jsArray + " ]";
// save it for the next display
session.setAttribute(ATTR_FRAME_FOCUS, jsArray);
}
/**
** Return a String array containing the "m", "d", "y" characters (corresponding to month, day, year)
** in the locale specific order
**/
private static String[] DEFAULT_FORMAT_ARRAY = new String[] {"m","d","y"};
public String[] getDateFormatString()
{
SimpleDateFormat sdf = (SimpleDateFormat)DateFormat.getDateInstance(DateFormat.SHORT, rb.getLocale());
String[] formatArray = sdf.toPattern().split("[/\\-\\.]");
for (int i=0; i formatSet = new LinkedHashSet();
char curChar;
char lastChar = 0;
for (int i = 0; i < format.length(); i++)
{
curChar = format.charAt(i);
if ((curChar == 'h' || curChar == 'm' || curChar == 'a' || curChar == 'H') && curChar != lastChar)
{
formatSet.add(String.valueOf(curChar));
lastChar = curChar;
}
}
String[] formatArray = formatSet.toArray(new String[formatSet.size()]);
if (formatArray.length != DEFAULT_TIME_FORMAT_ARRAY.length
&& formatArray.length != DEFAULT_TIME_FORMAT_ARRAY.length - 1)
{
log.warn("Unknown time format string (using default): " + format);
return DEFAULT_TIME_FORMAT_ARRAY.clone();
}
else
{
return formatArray;
}
}
} // class VelocityPortletPaneledAction
© 2015 - 2024 Weber Informatics LLC | Privacy Policy