/*==========================================================================*\ | $Id: WCBasePage.java,v 1.8 2012/02/13 02:52:32 stedwar2 Exp $ |*-------------------------------------------------------------------------*| | Copyright (C) 2006-2012 Virginia Tech | | This file is part of Web-CAT. | | Web-CAT is free software; you can redistribute it and/or modify | it under the terms of the GNU Affero General Public License as published | by the Free Software Foundation; either version 3 of the License, or | (at your option) any later version. | | Web-CAT is distributed in the hope that it will be useful, | but WITHOUT ANY WARRANTY; without even the implied warranty of | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | GNU General Public License for more details. | | You should have received a copy of the GNU Affero General Public License | along with Web-CAT; if not, see <http://www.gnu.org/licenses/>. \*==========================================================================*/ package org.webcat.ui; import com.webobjects.appserver.WOApplication; import com.webobjects.appserver.WOComponent; import com.webobjects.appserver.WOContext; import com.webobjects.appserver.WOResourceManager; import com.webobjects.appserver.WOResponse; import com.webobjects.foundation.NSArray; import com.webobjects.foundation.NSBundle; import com.webobjects.foundation.NSDictionary; import com.webobjects.foundation.NSKeyValueCodingAdditions; import com.webobjects.foundation.NSMutableArray; import com.webobjects.foundation.NSMutableDictionary; import java.io.File; import java.io.IOException; import java.net.URL; import java.util.regex.Matcher; import java.util.regex.Pattern; import org.apache.log4j.Logger; import org.webcat.core.Application; import org.webcat.core.MutableArray; import org.webcat.core.MutableDictionary; import org.webcat.core.Session; import org.webcat.core.Theme; import org.webcat.woextensions.WCResourceManager; // ------------------------------------------------------------------------ /** * <p> * The base class for any page that uses the Dojo toolkit. This component * defines the base HTML content and also manages the stylesheets and scripts * used by Dojo. * </p><p> * WCBasePage also provides the ability to automatically import CSS and * JavaScript resources for the particular component page that contains this * WCBasePage component. That is, if you have a component named FooPage defined * as follows: * <pre> * <wo:WCBasePage> * your content... * </wo:WCBasePage> * </pre> * then this component will automatically search in the WebServerResources * directory of the framework that contains FooPage to find * "stylesheets/FooPage.css" and "javascript/FooPage.js". If they are found, * they will be imported. * </p><p> * The logic described above only applies to the page component itself. If * nested components need to import their own scripts, they should make use of * {@link WCScriptFragment}. * </p> * * <h2>Bindings</h2> * <table> * <tr> * <td>{@code extraRequires}</td> * <td>A semicolon-separated list of additional module names that should be * <tt>dojo.require</tt>d in the page header.</td> * </tr> * </table> * * @author Tony Allevato * @author Last changed by $Author: stedwar2 $ * @version $Revision: 1.8 $, $Date: 2012/02/13 02:52:32 $ */ public class WCBasePage extends WOComponent { //~ Constructor ........................................................... // ---------------------------------------------------------- /** * Creates a new Dojo page. * * @param context the context */ public WCBasePage(WOContext context) { super(context); } //~ KVC attributes (must be public) ....................................... public String title; public String extraBodyCssClass; public String extraRequires; public String extraCssFiles; public String pageScriptName; public String dojoScriptName; public String inlineHeaderContents; public boolean includePageWrapping = true; /** Used to refer to a single item in a repetition on the page. */ public String oneExtraRequire; //~ Methods ............................................................... // ---------------------------------------------------------- public void appendToResponse(WOResponse response, WOContext context) { if (Application.isDevelopmentModeSafe()) { pageScriptName = DEVELOPMENT_PAGE_SCRIPT_NAME; dojoScriptName = DEVELOPMENT_DOJO_SCRIPT_NAME; } else { pageScriptName = DEPLOYMENT_PAGE_SCRIPT_NAME; dojoScriptName = DEPLOYMENT_DOJO_SCRIPT_NAME; } String nowrap = context.request().stringFormValueForKey("nowrap"); if (log.isDebugEnabled()) { log.debug("nowrap = " + nowrap); } includePageWrapping = (nowrap == null); response.appendHeader("no-cache", "pragma"); response.appendHeader("no-cache", "cache-control"); super.appendToResponse(response, context); } // ---------------------------------------------------------- /** * Returns the HTML page's title string. This is the title * string that will show as the "page title" in the browser. * This generic implementation returns "Web-CAT", which is the * title that will be used for pages that do not provide one. * Ideally, subsystems will override this default. * * @return The page title */ public String pageTitle() { return (title == null) ? "Web-CAT" : ("Web-CAT: " + title); } // ---------------------------------------------------------- public NSArray<String> extraRequiresArray() { if (extraRequires == null) { return null; } NSMutableArray<String> array = new NSMutableArray<String>(); String[] requires = extraRequires.split("[,;]\\s*"); for (String require : requires) { if (require != null && require.length() > 0) { array.addObject(require); } } return array; } // ---------------------------------------------------------- /** * Returns true if a page-specific stylesheet exists for the component * containing this WCBasePage instance. * * @return true if a page-specific stylesheet exists for the component, * otherwise false */ public boolean doesPageSpecificStylesheetExist() { return doesPageSpecificResourceExist( STYLESHEETS_RESOURCE_DIR, STYLESHEETS_RESOURCE_EXT); } // ---------------------------------------------------------- /** * Returns true if a page-specific JavaScript file exists for the component * containing this WCBasePage instance. * * @return true if a page-specific JavaScript file exists for the component, * otherwise false */ public boolean doesPageSpecificJavascriptExist() { return doesPageSpecificResourceExist( JAVASCRIPT_RESOURCE_DIR, JAVASCRIPT_RESOURCE_EXT); } // ---------------------------------------------------------- /** * Returns true if a page-specific resource for the component containing * this WCBasePage instance. * * @param directory the WebServerResources subdirectory containing the * resource to look for * @param extension the extension of the resource to look for * * @return true if the page-specific resource exists for the component, * otherwise false */ protected boolean doesPageSpecificResourceExist( String directory, String extension) { String resName = pageSpecificResourcePath(directory, extension); WOResourceManager manager = WOApplication.application().resourceManager(); URL url = manager.pathURLForResourceNamed( resName, pageFramework(), context()._languages()); return (url != null); } // ---------------------------------------------------------- /** * Gets the name of the framework that contains the component containing * this WCBasePage instance. * * @return the name of the framework that contains the component */ public String pageFramework() { WOComponent root = this; while (root.parent() != null) { root = root.parent(); } return NSBundle.bundleForClass(root.getClass()).name(); } // ---------------------------------------------------------- /** * Returns the path (relative to WebServerResources) of the page-specific * stylesheet for the component containing this WCBasePage instance. * * @return the WebServerResources-relative path of the page-specific * stylesheet */ public String pageSpecificStylesheetPath() { return pageSpecificResourcePath( STYLESHEETS_RESOURCE_DIR, STYLESHEETS_RESOURCE_EXT); } // ---------------------------------------------------------- /** * Returns the path (relative to WebServerResources) of the page-specific * Javascript file for the component containing this WCBasePage instance. * * @return the WebServerResources-relative path of the page-specific * JavaScript file */ public String pageSpecificJavascriptPath() { return pageSpecificResourcePath( JAVASCRIPT_RESOURCE_DIR, JAVASCRIPT_RESOURCE_EXT); } // ---------------------------------------------------------- public String bodyCssClass() { String result = null; Object dojoTheme = theme().valueForKeyPath("inherit.properties.dojoTheme"); if (dojoTheme != null) { result = dojoTheme.toString(); } if (extraBodyCssClass != null) { if (result == null) { result = ""; } else { result += " "; } result += extraBodyCssClass; } return result; } // ---------------------------------------------------------- public String extraCssLinkTags() { String result = null; if (extraCssFiles != null) { String[] links = extraCssFiles.split("[,;]\\s*"); StringBuffer buf = new StringBuffer(80 * links.length); for (String link : links) { if (link != null && link.length() > 0) { String file = link; String extra = ""; Matcher m = CSS_LINK_PATTERN.matcher(link); if (m.matches()) { file = m.group(1); extra = m.group(2); } buf.append("<link rel=\"stylesheet\" type=\"text/css\"" + "href=\""); buf.append(WCResourceManager.resourceURLFor(file, null)); buf.append('\"'); if (extra != null) { buf.append(' '); buf.append(extra); } buf.append("/>"); } } result = buf.toString(); } return result; } // ---------------------------------------------------------- public boolean hasCustomExternalCssLinks() { try { return hasSession() && ((Session)session()).user().preferences() .valueForKey("customCss") != null; } catch (Exception e) { return false; } } // ---------------------------------------------------------- public String customExternalCssLinks() { String result = null; String customCss = (String)((Session)session()).user().preferences() .valueForKey("customCss"); if (customCss != null) { String[] links = customCss.split("[,;]\\s*"); StringBuffer buf = new StringBuffer(80 * links.length); for (String link : links) { if (link != null && link.length() > 0) { String file = link; String extra = ""; Matcher m = CSS_LINK_PATTERN.matcher(link); if (m.matches()) { file = m.group(1); extra = m.group(2); } buf.append("<link rel=\"stylesheet\" type=\"text/css\"" + "href=\""); buf.append(file); buf.append('\"'); if (extra != null) { buf.append(' '); buf.append(extra); } buf.append("/>"); } } result = buf.toString(); } return result; } // ---------------------------------------------------------- public String customBackground() { if (hasSession()) { Session session = (Session)session(); if (session.user() != null) { org.webcat.core.User user = session.user(); if (user != null) { Object result = user.preferences() .valueForKey("customBackgroundUrl"); if (result != null) { return result.toString(); } } } } return null; } // ---------------------------------------------------------- public NSKeyValueCodingAdditions theme() { if (hasSession()) { return ((Session)session()).theme(); } else { NSKeyValueCodingAdditions lastUsedTheme = null; if (Application.wcApplication().needsInstallation()) { lastUsedTheme = installTheme(); } else { lastUsedTheme = Theme.lastUsedThemeInContext(context()); } return (lastUsedTheme != null) ? lastUsedTheme : Theme.defaultTheme(); } } // ---------------------------------------------------------- public String dojoBaseUrl() { // The resource manager doesn't let us get the path to a directory, so // we search for a known file (in this case, dojo/dojo.js inside // Core.framework), and then remove the dojo/dojo.js part from the end // of the path. String basePath = "Core.framework/WebServerResources/"; String subPath = "dojo/dojo.js"; String url = WCResourceManager.resourceURLFor(basePath + subPath, context().request()); url = url.replaceFirst(subPath + "(\\?.*)?", ""); return url; } // ---------------------------------------------------------- public String resourceUrl(String partialUrl) { return WCResourceManager.resourceURLFor( partialUrl, context().request()); } // ---------------------------------------------------------- /** * Returns true if a page-specific resource for the component containing * this WCBasePage instance. * * @param directory the WebServerResources subdirectory containing the * resource to look for * @param extension the extension of the resource to look for * * @return the WebServerResources-relative path of the page-specific * resource */ protected String pageSpecificResourcePath(String directory, String extension) { WOComponent root = this; while (root.parent() != null) root = root.parent(); return directory + "/" + root.getClass().getSimpleName() + "." + extension; } // ---------------------------------------------------------- private static NSKeyValueCodingAdditions installTheme() { if (installTheme == null) { // Can't use the subsystem manager, since it isn't initialized NSBundle core = NSBundle.bundleForName("Core"); if (core != null) { @SuppressWarnings("deprecation") String themeDir = core.bundlePath() + "/WebServerResources/theme/"; try { @SuppressWarnings("unchecked") NSMutableDictionary<String, Object> properties = MutableDictionary.fromPropertyList(new File( themeDir, "dream-way/theme.plist")); Object parentName = properties.get("extends"); properties.remove("extends"); MutableArray cssOrder = (MutableArray)properties.get("cssOrder"); while (parentName != null) { MutableDictionary parentProps = MutableDictionary .fromPropertyList(new File( themeDir, parentName + "/theme.plist")); @SuppressWarnings("unchecked") NSArray<NSDictionary<String, String>> cssFiles = (NSArray<NSDictionary<String, String>>)parentProps .get("cssOrder"); for (NSDictionary<String, String> css : cssFiles) { css.put("file", "../" + parentName + "/" + css.get("file")); cssOrder.add(css); } if (parentProps.containsKey("dojoTheme")) { properties.put( "dojoTheme", parentProps.get("dojoTheme")); } parentName = parentProps.get("extends"); } installTheme = properties; installTheme.takeValueForKey("Dream Way", "name"); installTheme.takeValueForKey("dream-way", "dirName"); installTheme.takeValueForKey(installTheme, "inherit"); } catch (IOException e) { log.error("error creating temporary theme", e); } } } return installTheme; } //~ Static/instance variables ............................................. private static final long serialVersionUID = 1L; private static final String JAVASCRIPT_RESOURCE_DIR = "javascript"; private static final String JAVASCRIPT_RESOURCE_EXT = "js"; private static final String STYLESHEETS_RESOURCE_DIR = "stylesheets"; private static final String STYLESHEETS_RESOURCE_EXT = "css"; private static final String DEPLOYMENT_PAGE_SCRIPT_NAME = "BasePage.js"; private static final String DEVELOPMENT_PAGE_SCRIPT_NAME = "BasePage.js.uncompressed.js"; private static final String DEPLOYMENT_DOJO_SCRIPT_NAME = "dojo.xd.js"; private static final String DEVELOPMENT_DOJO_SCRIPT_NAME = "dojo.xd.js.uncompressed.js"; private static final Pattern CSS_LINK_PATTERN = Pattern.compile("^(.*)\\[([^\\]]*)\\]$"); private static NSDictionary<String, Object> installTheme; static Logger log = Logger.getLogger(WCBasePage.class); }