/********************************************************************************** * $URL: https://source.sakaiproject.org/svn/web/trunk/web-tool/tool/src/java/org/sakaiproject/web/tool/IFrameAction.java $ * $Id: IFrameAction.java 105080 2012-02-24 23:10:31Z ottenhoff@longsight.com $ *********************************************************************************** * * 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.web.tool; import java.io.File; import java.net.URLEncoder; import java.util.ArrayList; import java.util.Iterator; import java.util.Locale; import java.util.Properties; import org.apache.commons.lang.StringUtils; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.apache.commons.validator.UrlValidator; import org.sakaiproject.authz.api.AuthzGroup; import org.sakaiproject.authz.api.GroupNotDefinedException; import org.sakaiproject.authz.api.Role; import org.sakaiproject.authz.cover.AuthzGroupService; import org.sakaiproject.cheftool.Context; import org.sakaiproject.cheftool.JetspeedRunData; import org.sakaiproject.cheftool.RunData; import org.sakaiproject.cheftool.VelocityPortlet; import org.sakaiproject.cheftool.VelocityPortletPaneledAction; import org.sakaiproject.component.cover.ComponentManager; import org.sakaiproject.component.cover.ServerConfigurationService; import org.sakaiproject.entity.api.Reference; import org.sakaiproject.entity.cover.EntityManager; import org.sakaiproject.event.api.EventTrackingService; import org.sakaiproject.event.api.NotificationService; import org.sakaiproject.event.api.SessionState; import org.sakaiproject.exception.IdUnusedException; import org.sakaiproject.site.api.Site; import org.sakaiproject.site.api.SitePage; import org.sakaiproject.site.api.ToolConfiguration; import org.sakaiproject.site.cover.SiteService; import org.sakaiproject.tool.api.Placement; import org.sakaiproject.tool.api.Session; import org.sakaiproject.tool.api.ToolSession; import org.sakaiproject.tool.cover.SessionManager; import org.sakaiproject.tool.cover.ToolManager; import org.sakaiproject.user.api.User; import org.sakaiproject.user.api.UserNotDefinedException; import org.sakaiproject.user.cover.UserDirectoryService; import org.sakaiproject.util.FormattedText; import org.sakaiproject.util.ResourceLoader; /** * <p> * IFrameAction is the Sakai tool to place any web content in an IFrame on the page. * </p> * <p> * Four special modes are supported - these pick the URL content from special places: * </p> * <ul> * <li>"site" - to show the services "server.info.url" configuration URL setting</li> * <li>"workspace" - to show the configured "myworkspace.info.url" URL, introducing a my workspace to users</li> * <li>"worksite" - to show the current site's "getInfoUrlFull()" setting</li> * <li>"annotatedurl" - to show a link to a configured target url, with a description following the link. Aid in redirection.</li> * </ul> */ public class IFrameAction extends VelocityPortletPaneledAction { private static Log M_log = LogFactory.getLog(IFrameAction.class); /** Event for accessing the web-content tool */ protected final static String EVENT_ACCESS_WEB_CONTENT = "webcontent.read"; /** Event for modifying the web-content tool configuration */ protected final static String EVENT_REVISE_WEB_CONTENT = "webcontent.revise"; /** Resource bundle using current language locale */ protected static ResourceLoader rb = new ResourceLoader("iframe"); /** The source URL, in state, config and context. */ protected final static String SOURCE = "source"; /** The value in state and context for the source URL to actually used, as computed from special and URL. */ protected final static String URL = "url"; /** The height, in state, config and context. */ protected final static String HEIGHT = "height"; /** The custom height from user input * */ protected final static String CUSTOM_HEIGHT = "customNumberField"; /** The special attribute, in state, config and context. */ protected final static String SPECIAL = "special"; /** The Annotated URL Tool's url attribute, in state, config and context. */ protected final static String TARGETPAGE_URL = "TargetPageUrl"; /** The Annotated URL Tool's name attribute, in state, config and context. */ protected final static String TARGETPAGE_NAME = "TargetPageName"; /** The Annotated URL Tool's text attribute, in state, config and context. */ protected final static String ANNOTATED_TEXT = "desp"; /** Support an external url defined in sakai.properties, in state, config and context. */ protected final static String SAKAI_PROPERTIES_URL_KEY = "sakai.properties.url.key"; /** If set, always hide the OPTIONS button */ protected final static String HIDE_OPTIONS = "hide.options"; /** Special value for site. */ protected final static String SPECIAL_SITE = "site"; /** Special value for Annotated URL Tool. */ protected final static String SPECIAL_ANNOTATEDURL = "annotatedurl"; /** Special value for myworkspace. */ protected final static String SPECIAL_WORKSPACE = "workspace"; /** Special value for worksite. */ protected final static String SPECIAL_WORKSITE = "worksite"; /** The title, in state and context. */ protected final static String TITLE = "title"; /** * Whether to pass through the PID to the URL displayed in the IFRAME. This enables integration in that the application in the IFRAME will know what site and tool it is part of. */ private final static String PASS_PID = "passthroughPID"; /** Valid digits for custom height from user input **/ protected static final String VALID_DIGITS = "0123456789"; /** Choices of pixels displayed in the customization page */ public String[] ourPixels = { "300px", "450px", "600px", "750px", "900px", "1200px", "1800px", "2400px" }; /** Attributes for web content tool page title **/ private static final String STATE_PAGE_TITLE = "pageTitle"; private static final String FORM_PAGE_TITLE = "title-of-page"; private static final String FORM_TOOL_TITLE = "title-of-tool"; private static final int MAX_TITLE_LENGTH = 99; private static final int MAX_SITE_INFO_URL_LENGTH = 255; /** * Expand macros to insert session information into the URL? */ private final static String MACRO_EXPANSION = "expandMacros"; /** Macro name: Site id (GUID) */ protected static final String MACRO_SITE_ID = "${SITE_ID}"; /** Macro name: User id */ protected static final String MACRO_USER_ID = "${USER_ID}"; /** Macro name: User enterprise id */ protected static final String MACRO_USER_EID = "${USER_EID}"; /** Macro name: First name */ protected static final String MACRO_USER_FIRST_NAME = "${USER_FIRST_NAME}"; /** Macro name: Last name */ protected static final String MACRO_USER_LAST_NAME = "${USER_LAST_NAME}"; /** Macro name: Role */ protected static final String MACRO_USER_ROLE = "${USER_ROLE}"; /** Macro name: Session */ protected static final String MACRO_SESSION_ID = "${SESSION_ID}"; private static final String MACRO_CLASS_SITE_PROP = "SITE_PROP:"; private static final String IFRAME_ALLOWED_MACROS_PROPERTY = "iframe.allowed.macros"; private static final String MACRO_DEFAULT_ALLOWED = "${USER_ID},${USER_EID},${USER_FIRST_NAME},${USER_LAST_NAME},${SITE_ID},${USER_ROLE}"; private static ArrayList allowedMacrosList; // initialize list of approved macros for replacement within URL static { allowedMacrosList = new ArrayList(); final String allowedMacros = ServerConfigurationService.getString(IFRAME_ALLOWED_MACROS_PROPERTY, MACRO_DEFAULT_ALLOWED); String parts[] = allowedMacros.split(","); if(parts != null) { for(int i = 0; i < parts.length; i++) { allowedMacrosList.add(parts[i]); } } } /** For tracking event */ private static EventTrackingService m_eventTrackingService = null; /** * Populate the state with configuration settings */ protected void initState(SessionState state, VelocityPortlet portlet, JetspeedRunData rundata) { // TODO: we might want to keep this from running for each request - but by letting it we get fresh info each time... -ggolden super.initState(state, portlet, rundata); Placement placement = ToolManager.getCurrentPlacement(); Properties config = placement.getConfig(); // set the pass_pid parameter boolean passPid = false; String passPidStr = config.getProperty(PASS_PID, "false"); state.removeAttribute(PASS_PID); if ("true".equalsIgnoreCase(passPidStr)) { state.setAttribute(PASS_PID, Boolean.TRUE); passPid = true; } // Assume macro expansion (disable on request) boolean macroExpansion = true; String macroExpansionStr = config.getProperty(MACRO_EXPANSION, "true"); state.removeAttribute(MACRO_EXPANSION); if ("false".equalsIgnoreCase(macroExpansionStr)) { state.setAttribute(MACRO_EXPANSION, Boolean.FALSE); macroExpansion = false; } // set the special setting String special = config.getProperty(SPECIAL); final String sakaiPropertiesUrlKey = config.getProperty(SAKAI_PROPERTIES_URL_KEY); final String hideOptions = config.getProperty(HIDE_OPTIONS); // check for an older way the ChefWebPagePortlet took parameters, converting to our "special" values if (special == null) { if ("true".equals(config.getProperty("site"))) { special = SPECIAL_SITE; } else if ("true".equals(config.getProperty("workspace"))) { special = SPECIAL_WORKSPACE; } else if ("true".equals(config.getProperty("worksite"))) { special = SPECIAL_WORKSITE; } else if ("true".equals(config.getProperty("annotatedurl"))) { special = SPECIAL_ANNOTATEDURL; } } state.removeAttribute(SPECIAL); if ((special != null) && (special.trim().length() > 0)) { state.setAttribute(SPECIAL, special); } state.removeAttribute(HIDE_OPTIONS); if ((hideOptions != null) && (hideOptions.trim().length() > 0)) { state.setAttribute(HIDE_OPTIONS, hideOptions); } // set the source url setting String source = StringUtils.trimToNull(config.getProperty(SOURCE)); // check for an older way the ChefWebPagePortlet took parameters, converting to our "source" value if (source == null) { source = StringUtils.trimToNull(config.getProperty("url")); } // store the raw as-configured source url state.removeAttribute(SOURCE); if (source != null) { state.setAttribute(SOURCE, source); } // compute working URL, modified from the configuration URL if special String url = sourceUrl(special, source, placement.getContext(), macroExpansion, passPid, placement.getId(), sakaiPropertiesUrlKey); state.setAttribute(URL, url); // set the height state.setAttribute(HEIGHT, config.getProperty(HEIGHT, "600px")); state.setAttribute(ANNOTATED_TEXT, config.getProperty(ANNOTATED_TEXT, "")); if(config.getProperty(TARGETPAGE_URL)!=null) { // set Target page url for Annotated URL Tool state.setAttribute(TARGETPAGE_URL,config.getProperty(TARGETPAGE_URL)); // set Target page name for Annotated URL Tool state.setAttribute(TARGETPAGE_NAME,config.getProperty(TARGETPAGE_NAME)); } // set the title state.setAttribute(TITLE, placement.getTitle()); if (state.getAttribute(STATE_PAGE_TITLE) == null) { SitePage p = SiteService.findPage(getCurrentSitePageId()); state.setAttribute(STATE_PAGE_TITLE, p.getTitle()); } // if events found in tool registration file put them in state if((StringUtils.trimToNull(config.getProperty(EVENT_ACCESS_WEB_CONTENT)) != null)) { state.setAttribute(EVENT_ACCESS_WEB_CONTENT, config.getProperty(EVENT_ACCESS_WEB_CONTENT)); } if((StringUtils.trimToNull(config.getProperty(EVENT_REVISE_WEB_CONTENT)) != null)) { state.setAttribute(EVENT_REVISE_WEB_CONTENT, config.getProperty(EVENT_REVISE_WEB_CONTENT)); } if (m_eventTrackingService == null) { m_eventTrackingService = (EventTrackingService) ComponentManager.get("org.sakaiproject.event.api.EventTrackingService"); } } /** * Get the current site page our current tool is placed on. * * @return The site page id on which our tool is placed. */ protected String getCurrentSitePageId() { ToolSession ts = SessionManager.getCurrentToolSession(); if (ts != null) { ToolConfiguration tool = SiteService.findTool(ts.getPlacementId()); if (tool != null) { return tool.getPageId(); } } return null; } /** Construct and return localized filepath, if it exists **/ private String getLocalizedURL(String property) { String filename = ServerConfigurationService.getString(property); if ( filename == null || filename.trim().length()==0 ) return filename; else filename = filename.trim(); int extIndex = filename.lastIndexOf(".") >= 0 ? filename.lastIndexOf(".") : filename.length()-1; String ext = filename.substring(extIndex); String doc = filename.substring(0,extIndex); Locale locale = new ResourceLoader().getLocale(); if (locale != null){ // check if localized file exists for current language/locale/variant String localizedFile = doc + "_" + locale.toString() + ext; String filePath = getServletConfig().getServletContext().getRealPath( ".."+localizedFile ); if ( (new File(filePath)).exists() ) return localizedFile; // otherwise, check if localized file exists for current language localizedFile = doc + "_" + locale.getLanguage() + ext; filePath = getServletConfig().getServletContext().getRealPath( ".."+localizedFile ); if ( (new File(filePath)).exists() ) return localizedFile; } return filename; } /** * Compute the actual URL we will used, based on the configuration special and source URLs */ protected String sourceUrl(String special, String source, String context, boolean macroExpansion, boolean passPid, String pid, String sakaiPropertiesUrlKey) { String rv = StringUtils.trimToNull(source); // if marked for "site", use the site intro from the properties if (SPECIAL_SITE.equals(special)) { rv = StringUtils.trimToNull(getLocalizedURL("server.info.url")); } // if marked for "workspace", use the "user" site info from the properties else if (SPECIAL_WORKSPACE.equals(special)) { rv = StringUtils.trimToNull(getLocalizedURL("myworkspace.info.url")); } // if marked for "worksite", use the setting from the site's definition else if (SPECIAL_WORKSITE.equals(special)) { // set the url to the site of this request's config'ed url try { // get the site's info URL, if defined Site s = SiteService.getSite(context); rv = StringUtils.trimToNull(s.getInfoUrlFull()); // compute the info url for the site if it has no specific InfoUrl if (rv == null) { // access will show the site description or title... rv = ServerConfigurationService.getAccessUrl() + s.getReference(); } } catch (Exception any) { } } else if (sakaiPropertiesUrlKey != null && sakaiPropertiesUrlKey.length() > 1) { // set the url to a string defined in sakai.properties rv = StringUtils.trimToNull(ServerConfigurationService.getString(sakaiPropertiesUrlKey)); } // if it's not special, and we have no value yet, set it to the webcontent instruction page, as configured if (rv == null || rv.equals("http://") || rv.equals("https://")) { rv = StringUtils.trimToNull(getLocalizedURL("webcontent.instructions.url")); } if (rv != null) { // accept a partial reference url (i.e. "/content/group/sakai/test.gif"), convert to full url rv = convertReferenceUrl(rv); // pass the PID through on the URL, IF configured to do so if (passPid) { if (rv.indexOf("?") < 0) { rv = rv + "?"; } else { rv = rv + "&"; } rv = rv + "pid=" + pid; } if (macroExpansion) { rv = doMacroExpansion(rv); } } return rv; } /** * If the url is a valid reference, convert it to a URL, else return it unchanged. */ protected String convertReferenceUrl(String url) { // make a reference Reference ref = EntityManager.newReference(url); // if it didn't recognize this, return it unchanged if (ref.isKnownType()) { // return the reference's url String refUrl = ref.getUrl(); if (refUrl != null) { return refUrl; } } return url; } /** * Get the current user id * @throws SessionDataException * @return User id */ private String getUserId() throws SessionDataException { Session session = SessionManager.getCurrentSession(); if (session == null) { throw new SessionDataException("No current user session"); } return session.getUserId(); } /** * Get the current session id * @throws SessionDataException * @return Session id */ private String getSessionId() throws SessionDataException { Session session = SessionManager.getCurrentSession(); if (session == null) { throw new SessionDataException("No current user session"); } return session.getId(); } /** * Get the current user eid * @throws SessionDataException * @return User eid */ private String getUserEid() throws SessionDataException { Session session = SessionManager.getCurrentSession(); if (session == null) { throw new SessionDataException("No current user session"); } return session.getUserEid(); } /** * Get current User information * @throws IdUnusedException, SessionDataException * @return {@link User} data * @throws UserNotDefinedException */ private User getUser() throws IdUnusedException, SessionDataException, UserNotDefinedException { return UserDirectoryService.getUser(this.getUserId()); } /** * Get the current site id * @throws SessionDataException * @return Site id (GUID) */ private String getSiteId() throws SessionDataException { Placement placement = ToolManager.getCurrentPlacement(); if (placement == null) { throw new SessionDataException("No current tool placement"); } return placement.getContext(); } /** * Fetch the user role in the current site * @throws IdUnusedException, SessionDataException * @return Role * @throws GroupNotDefinedException */ private String getUserRole() throws IdUnusedException, SessionDataException, GroupNotDefinedException { AuthzGroup group; Role role; group = AuthzGroupService.getAuthzGroup("/site/" + getSiteId()); if (group == null) { throw new SessionDataException("No current group"); } role = group.getUserRole(this.getUserId()); if (role == null) { throw new SessionDataException("No current role"); } return role.getId(); } /** * Get a site property by name * * @param name Property name * @throws IdUnusedException, SessionDataException * @return The property value (null if none) */ private String getSiteProperty(String name) throws IdUnusedException, SessionDataException { Site site; site = SiteService.getSite(getSiteId()); return site.getProperties().getProperty(name); } /** * Lookup value for requested macro name */ private String getMacroValue(String macroName) { try { if (macroName.equals(MACRO_USER_ID)) { return this.getUserId(); } if (macroName.equals(MACRO_USER_EID)) { return this.getUserEid(); } if (macroName.equals(MACRO_USER_FIRST_NAME)) { return this.getUser().getFirstName(); } if (macroName.equals(MACRO_USER_LAST_NAME)) { return this.getUser().getLastName(); } if (macroName.equals(MACRO_SITE_ID)) { return getSiteId(); } if (macroName.equals(MACRO_USER_ROLE)) { return this.getUserRole(); } if (macroName.equals(MACRO_SESSION_ID)) { return this.getSessionId(); } if (macroName.startsWith("${"+MACRO_CLASS_SITE_PROP)) { macroName = macroName.substring(2); // Remove leading "${" macroName = macroName.substring(0, macroName.length()-1); // Remove trailing "}" // at this point we have "SITE_PROP:some-property-name" // separate the property name from the prefix then return the property value String[] sitePropertyKey = macroName.split(":"); if (sitePropertyKey != null && sitePropertyKey.length > 1) { String sitePropertyValue = getSiteProperty(sitePropertyKey[1]); return (sitePropertyValue == null) ? "" : sitePropertyValue; } } } catch (Throwable throwable) { return ""; } /* * An unsupported macro: use the original text "as is" */ return macroName; } /** * Expand one macro reference * @param text Expand macros found in this text * @param macroName Macro name */ private void expand(StringBuilder sb, String macroName) { int index; /* * Replace every occurance of the macro in the parameter list */ index = sb.indexOf(macroName); while (index != -1) { String macroValue = URLEncoder.encode(getMacroValue(macroName)); sb.replace(index, (index + macroName.length()), macroValue); index = sb.indexOf(macroName, (index + macroValue.length())); } } /** * Expand macros, inserting session and site information * @param originalText Expand macros found in this text * @return [possibly] Updated text */ private String doMacroExpansion(String originalText) { StringBuilder sb; /* * Quit now if no macros are embedded in the text */ if (originalText.indexOf("${") == -1) { return originalText; } /* * Expand each macro */ sb = new StringBuilder(originalText); Iterator i = allowedMacrosList.iterator(); while(i.hasNext()) { String macro = (String) i.next(); expand(sb, macro); } return sb.toString(); } /** * Setup the velocity context and choose the template for the response. */ public String buildMainPanelContext(VelocityPortlet portlet, Context context, RunData rundata, SessionState state) { // do options if we are in options mode if (MODE_OPTIONS.equals(state.getAttribute(STATE_MODE))) { return buildOptionsPanelContext(portlet, context, rundata, state); } // if we rely on state (like all the other tools), we won't pick up any changes others make to the configuration till we are refreshed... -ggolden // set our configuration into the context for the vm String url = (String) state.getAttribute(URL); String special = (String) state.getAttribute(SPECIAL); context.put(URL, url); context.put(HEIGHT, state.getAttribute(HEIGHT)); //for annotatedurl context.put(TARGETPAGE_URL, state.getAttribute(TARGETPAGE_URL)); context.put(TARGETPAGE_NAME, state.getAttribute(TARGETPAGE_NAME)); context.put(ANNOTATED_TEXT, state.getAttribute(ANNOTATED_TEXT)); // set the resource bundle with our strings context.put("tlang", rb); // setup for the options menu if needed String hideOptions = (String) state.getAttribute(HIDE_OPTIONS); if (hideOptions != null && "true".equalsIgnoreCase(hideOptions)) { // always hide Options menu if hide.options is specified } else if (SiteService.allowUpdateSite(ToolManager.getCurrentPlacement().getContext())) { context.put("options_title", ToolManager.getCurrentPlacement().getTitle() + " " + rb.getString("gen.options")); } // tracking event String siteId = ""; try { Site s = SiteService.getSite(ToolManager.getCurrentPlacement().getContext()); siteId = s.getId(); } catch (Throwable e) { } if (special == null) { if(state.getAttribute(EVENT_ACCESS_WEB_CONTENT) == null) { // this is a Web Content tool m_eventTrackingService.post(m_eventTrackingService.newEvent(EVENT_ACCESS_WEB_CONTENT, url, siteId, false, NotificationService.NOTI_NONE)); } else { // event in tool registration file will be used m_eventTrackingService.post(m_eventTrackingService.newEvent((String)state.getAttribute(EVENT_ACCESS_WEB_CONTENT), url, siteId, false, NotificationService.NOTI_NONE)); } } else { if(state.getAttribute(EVENT_ACCESS_WEB_CONTENT) != null) { // special and event in tool registration file m_eventTrackingService.post(m_eventTrackingService.newEvent((String)state.getAttribute(EVENT_ACCESS_WEB_CONTENT), url, siteId, false, NotificationService.NOTI_NONE)); } } return (String) getContext(rundata).get("template"); } /** * Setup the velocity context and choose the template for options. */ public String buildOptionsPanelContext(VelocityPortlet portlet, Context context, RunData data, SessionState state) { // provide the source, and let the user edit, if not special String special = (String) state.getAttribute(SPECIAL); String source = ""; String siteId = ""; if (special == null) { source = (String) state.getAttribute(SOURCE); if (source == null) source = ""; context.put(SOURCE, source); context.put("heading", rb.getString("gen.custom")); } // set the heading based on special else { if (SPECIAL_SITE.equals(special)) { context.put("heading", rb.getString("gen.custom.site")); } else if (SPECIAL_WORKSPACE.equals(special)) { context.put("heading", rb.getString("gen.custom.workspace")); } else if (SPECIAL_WORKSITE.equals(special)) { context.put("heading", rb.getString("gen.custom.worksite")); // for worksite, also include the Site's infourl and description try { Site s = SiteService.getSite(ToolManager.getCurrentPlacement().getContext()); siteId = s.getId(); String infoUrl = StringUtils.trimToNull(s.getInfoUrl()); if (infoUrl != null) { context.put("info_url", infoUrl); } String description = StringUtils.trimToNull(s.getDescription()); if (description != null) { description = FormattedText.escapeHtmlFormattedTextarea(description); context.put("description", description); } } catch (Throwable e) { } } else if (SPECIAL_ANNOTATEDURL.equals(special)) { context.put("heading", rb.getString("gen.custom.annotatedurl")); // for Annotated URL Tool page, also include the description try { String desp = state.getAttribute(ANNOTATED_TEXT).toString(); context.put("description", desp); } catch (Throwable e) { } } else { context.put("heading", rb.getString("gen.custom")); } } boolean selected = false; String height = state.getAttribute(HEIGHT).toString(); for (int i = 0; i < ourPixels.length; i++) { if (height.equals(ourPixels[i])) { selected = true; continue; } } if (!selected) { String[] strings = height.trim().split("px"); context.put("custom_height", strings[0]); height = rb.getString("gen.heisomelse"); } context.put(HEIGHT, height); context.put(TITLE, state.getAttribute(TITLE)); context.put("tlang", rb); context.put("doUpdate", BUTTON + "doConfigure_update"); context.put("doCancel", BUTTON + "doCancel"); context.put("form_tool_title", FORM_TOOL_TITLE); context.put("form_page_title", FORM_PAGE_TITLE); // if we are part of a site, and the only tool on the page, offer the popup to edit Placement placement = ToolManager.getCurrentPlacement(); ToolConfiguration toolConfig = SiteService.findTool(placement.getId()); if ((state.getAttribute(SPECIAL) == null) && (toolConfig != null)) { try { Site site = SiteService.getSite(toolConfig.getSiteId()); siteId = site.getId(); SitePage page = site.getPage(toolConfig.getPageId()); // if this is the only tool on that page, update the page's title also if ((page.getTools() != null) && (page.getTools().size() == 1)) { context.put("showPopup", Boolean.TRUE); context.put("popup", Boolean.valueOf(page.isPopUp())); context.put("pageTitleEditable", Boolean.TRUE); context.put("page_title", (String) state.getAttribute(STATE_PAGE_TITLE)); } } catch (Throwable e) { } } // pick the "-customize" template based on the standard template name String template = (String) getContext(data).get("template"); // pick the site customize template if we are in that mode if (SPECIAL_WORKSITE.equals(special)) { template = template + "-site-customize"; } else if (SPECIAL_WORKSPACE.equals(special)) { template = template + "-customize"; } else if (SPECIAL_ANNOTATEDURL.equals(special)) { template = template + "-annotatedurl-customize"; } else { template = template + "-customize"; } // tracking event if(siteId.length() == 0) { try { Site s = SiteService.getSite(ToolManager.getCurrentPlacement().getContext()); siteId = s.getId(); } catch (Throwable e) { } } if (special == null) { if(state.getAttribute(EVENT_REVISE_WEB_CONTENT) == null) { // this is a Web Content tool m_eventTrackingService.post(m_eventTrackingService.newEvent(EVENT_REVISE_WEB_CONTENT, source, siteId, true, NotificationService.NOTI_NONE)); } else { // event in tool registration file will be used m_eventTrackingService.post(m_eventTrackingService.newEvent((String)state.getAttribute(EVENT_REVISE_WEB_CONTENT), source, siteId, true, NotificationService.NOTI_NONE)); } } else { if(state.getAttribute(EVENT_REVISE_WEB_CONTENT) != null) { // special and event in tool registration file m_eventTrackingService.post(m_eventTrackingService.newEvent((String)state.getAttribute(EVENT_REVISE_WEB_CONTENT), source, siteId, true, NotificationService.NOTI_NONE)); } } // output the max limit context.put("max_length_title", MAX_TITLE_LENGTH); context.put("max_length_info_url", MAX_SITE_INFO_URL_LENGTH); return template; } /** * Handle the configure context's update button */ public void doConfigure_update(RunData data, Context context) { // TODO: if we do limit the initState() calls, we need to make sure we get a new one after this call -ggolden String peid = ((JetspeedRunData) data).getJs_peid(); SessionState state = ((JetspeedRunData) data).getPortletSessionState(peid); Placement placement = ToolManager.getCurrentPlacement(); // get the site toolConfiguration, if this is part of a site. ToolConfiguration toolConfig = SiteService.findTool(placement.getId()); // height String height = data.getParameters().getString(HEIGHT); if (height.equals(rb.getString("gen.heisomelse"))) { String customHeight = data.getParameters().getString(CUSTOM_HEIGHT); if ((customHeight != null) && (!customHeight.equals(""))) { if (!checkDigits(customHeight)) { addAlert(state, rb.getString("java.alert.pleentval")); return; } state.setAttribute(HEIGHT, customHeight); height = customHeight + "px"; state.setAttribute(HEIGHT, height); placement.getPlacementConfig().setProperty(HEIGHT, height); } else { addAlert(state, rb.getString("java.alert.pleentval")); return; } } else if (SPECIAL_ANNOTATEDURL.equals(state.getAttribute(SPECIAL))) { // update the site info try { String desp = data.getParameters().getString("description"); state.setAttribute(ANNOTATED_TEXT, desp); placement.getPlacementConfig().setProperty(ANNOTATED_TEXT, desp); } catch (Throwable e) { } } else { state.setAttribute(HEIGHT, height); placement.getPlacementConfig().setProperty(HEIGHT, height); } // title String title = data.getParameters().getString(TITLE); if (StringUtils.isBlank(title)) { addAlert(state, rb.getString("gen.tootit.empty")); return; // SAK-19515 check for LENGTH of tool title } else if (title.length() > MAX_TITLE_LENGTH) { addAlert(state, rb.getString("gen.tootit.toolong")); return; } placement.setTitle(title); // site info url String infoUrl = StringUtils.trimToNull(data.getParameters().getString("infourl")); if (infoUrl != null && infoUrl.length() > MAX_SITE_INFO_URL_LENGTH) { addAlert(state, rb.getString("gen.info.url.toolong")); return; } try { Site site = SiteService.getSite(toolConfig.getSiteId()); SitePage page = site.getPage(toolConfig.getPageId()); page.setTitleCustom(true); // for web content tool, if it is a site page tool, and the only tool on the page, update the page title / popup. if ((state.getAttribute(SPECIAL) == null) && (toolConfig != null)) { // if this is the only tool on that page, update the page's title also if ((page.getTools() != null) && (page.getTools().size() == 1)) { String newPageTitle = data.getParameters().getString(FORM_PAGE_TITLE); if (StringUtils.isBlank(newPageTitle)) { addAlert(state, rb.getString("gen.pagtit.empty")); return; } else if (newPageTitle.length() > MAX_TITLE_LENGTH) { addAlert(state, rb.getString("gen.pagtit.toolong")); return; } page.setTitle(newPageTitle); state.setAttribute(STATE_PAGE_TITLE, newPageTitle); // popup boolean popup = data.getParameters().getBoolean("popup"); page.setPopup(popup); } } SiteService.save(site); } catch (Exception ignore) { M_log.warn("doConfigure_update: " + ignore); } // read source if we are not special if (state.getAttribute(SPECIAL) == null) { String source = StringUtils.trimToEmpty(data.getParameters().getString(SOURCE)); // User entered nothing in the source box; give the user an alert if (StringUtils.isBlank(source)) { addAlert(state, rb.getString("gen.url.empty")); return; } if ((!source.startsWith("/")) && (source.indexOf("://") == -1)) { source = "http://" + source; } // Validate the url UrlValidator urlValidator = new UrlValidator(); if (!urlValidator.isValid(source)) { addAlert(state, rb.getString("gen.url.invalid")); return; } // update state placement.getPlacementConfig().setProperty(SOURCE, source); } else if (SPECIAL_WORKSITE.equals(state.getAttribute(SPECIAL))) { if ((infoUrl != null) && (infoUrl.length() > 0) && (!infoUrl.startsWith("/")) && (infoUrl.indexOf("://") == -1)) { infoUrl = "http://" + infoUrl; } String description = StringUtils.trimToNull(data.getParameters().getString("description")); description = FormattedText.processEscapedHtml(description); // update the site info try { SiteService.saveSiteInfo(ToolManager.getCurrentPlacement().getContext(), description, infoUrl); } catch (Throwable e) { M_log.warn("doConfigure_update: " + e); } } // save // TODO: we might have just saved the entire site, so this would not be needed -ggolden placement.save(); // we are done with customization... back to the main mode state.removeAttribute(STATE_MODE); // refresh the whole page, since popup and title may have changed scheduleTopRefresh(); } /** * doCancel called for form input tags type="submit" named="eventSubmit_doCancel" cancel the options process */ public void doCancel(RunData data, Context context) { // access the portlet element id to find our state String peid = ((JetspeedRunData) data).getJs_peid(); SessionState state = ((JetspeedRunData) data).getPortletSessionState(peid); // we are done with customization... back to the main mode state.removeAttribute(STATE_MODE); state.removeAttribute(STATE_MESSAGE); } /** * Check if the string from user input contains any characters other than digits * * @param height * String from user input * @return True if all are digits. Or False if any is not digit. */ private boolean checkDigits(String height) { for (int i = 0; i < height.length(); i++) { if (VALID_DIGITS.indexOf(height.charAt(i)) == -1) return false; } return true; } /** * Note a "local" problem (we failed to get session or site data) */ private static class SessionDataException extends Exception { public SessionDataException(String text) { super(text); } } }