Fixed problem with copying default theme to new theme's directory. Added Label as Widget attribute (but not evaluated by Mandalay theme engine). Still a NPE if theme evaluation detects an error, e.g. duplicate theme url.
git-svn-id: https://svn.libreccm.org/ccm/trunk@2681 8810af33-2d31-482b-a856-94f89814c4dfmaster
parent
e4823ae780
commit
6c272debdd
|
|
@ -65,7 +65,10 @@ import org.apache.log4j.Logger;
|
||||||
*/
|
*/
|
||||||
public abstract class BaseApplicationServlet extends BaseServlet {
|
public abstract class BaseApplicationServlet extends BaseServlet {
|
||||||
|
|
||||||
/** Logger instance for debugging purpose. */
|
/** Internal logger instance to faciliate debugging. Enable logging output
|
||||||
|
* by editing /WEB-INF/conf/log4j.properties int hte runtime environment
|
||||||
|
* and set com.arsdigita.web.BaseApplicationServlet=DEBUG by uncommenting
|
||||||
|
* or adding the line. */
|
||||||
private static final Logger s_log = Logger.getLogger(BaseApplicationServlet.class);
|
private static final Logger s_log = Logger.getLogger(BaseApplicationServlet.class);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
||||||
|
|
@ -34,13 +34,14 @@ import org.apache.log4j.LogManager;
|
||||||
import org.apache.log4j.PropertyConfigurator;
|
import org.apache.log4j.PropertyConfigurator;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Web application lifecycle listener, used to perform central initialisation tasks at CCM startup
|
* Web application lifecycle listener, used to perform central initialisation
|
||||||
* in a Servlet container / web application server, expecially setting the runtime context (file
|
* tasks at CCM startup in a Servlet container / web application server,
|
||||||
* locations) and (in the future) the database connection.
|
* expecially setting the runtime context (file locations) and (in the future)
|
||||||
|
* the database connection.
|
||||||
*
|
*
|
||||||
* The methods of this classes are by definition only invoked by the Servlet container / web
|
* The methods of this classes are by definition only invoked by the Servlet
|
||||||
* application server, not by any Servlet or java class of the application itself! Invocation is
|
* container / web application server, not by any Servlet or java class of the
|
||||||
* managed by the deployment descriptor.
|
* application itself! Invocation is managed by the deployment descriptor.
|
||||||
*
|
*
|
||||||
* Note! Don't forget to configure it in web.xml deployment descriptor!
|
* Note! Don't forget to configure it in web.xml deployment descriptor!
|
||||||
* <listener>
|
* <listener>
|
||||||
|
|
@ -48,25 +49,31 @@ import org.apache.log4j.PropertyConfigurator;
|
||||||
* com.arsdigita.runtime.CCMApplicationContextListener
|
* com.arsdigita.runtime.CCMApplicationContextListener
|
||||||
* </listener-class>
|
* </listener-class>
|
||||||
* </listener>
|
* </listener>
|
||||||
* According to the 2.3 specification these tags must be placed after the filter tags and before the
|
* According to the 2.3 specification these tags must be placed after the
|
||||||
* Servlet tags!
|
* filter tags and before the Servlet tags!
|
||||||
*
|
*
|
||||||
* @author pboy
|
* @author pboy
|
||||||
* @version $Id: $
|
* @version $Id: $
|
||||||
*/
|
*/
|
||||||
public class CCMApplicationContextListener implements ServletContextListener {
|
public class CCMApplicationContextListener implements ServletContextListener {
|
||||||
|
|
||||||
private static Logger s_log = Logger.getLogger(CCMApplicationContextListener.class);
|
/** Internal logger instance to faciliate debugging. Enable logging output
|
||||||
|
* by editing /WEB-INF/conf/log4j.properties int hte runtime environment
|
||||||
|
* and set com.arsdigita.web.CCMApplicationContextListener=DEBUG by
|
||||||
|
* uncommenting or adding the line. */
|
||||||
|
private static final Logger s_log = Logger.getLogger(
|
||||||
|
CCMApplicationContextListener.class);
|
||||||
|
|
||||||
private static Runtime runtime;
|
private static Runtime runtime;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Used to initialise classes at startup of the application, most of which needs to be plain
|
* Used to initialise classes at startup of the application, most of which
|
||||||
* java objects (because they are also used by command line interface - installation,
|
* needs to be plain java objects (because they are also used by command
|
||||||
* configuration, maintenance).
|
* line interface - installation, configuration, maintenance).
|
||||||
*
|
*
|
||||||
* Here we provide one of the two supported ways to bring up the CCM application. This handles
|
* Here we provide one of the two supported ways to bring up the CCM
|
||||||
* the startup inside a Servlet container. The command line utilities handle the startup there.
|
* application. This handles the startup inside a Servlet container.
|
||||||
|
* The command line utilities handle the startup there.
|
||||||
* Both initialise the same set of classes needed for CCM operations
|
* Both initialise the same set of classes needed for CCM operations
|
||||||
*
|
*
|
||||||
* @param applicationStartEvent
|
* @param applicationStartEvent
|
||||||
|
|
@ -124,6 +131,7 @@ public class CCMApplicationContextListener implements ServletContextListener {
|
||||||
*
|
*
|
||||||
* @param applicationEndEvent
|
* @param applicationEndEvent
|
||||||
*/
|
*/
|
||||||
|
@Override
|
||||||
public void contextDestroyed(ServletContextEvent applicationEndEvent) {
|
public void contextDestroyed(ServletContextEvent applicationEndEvent) {
|
||||||
s_log.info("Shutdown procedure started.");
|
s_log.info("Shutdown procedure started.");
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -115,8 +115,7 @@ public class Web {
|
||||||
/**
|
/**
|
||||||
* Gets the servlet request object of the current thread.
|
* Gets the servlet request object of the current thread.
|
||||||
*
|
*
|
||||||
* @return The current <code>HttpServletRequest</code>; it can be
|
* @return The current <code>HttpServletRequest</code>; it can be null
|
||||||
* null
|
|
||||||
*/
|
*/
|
||||||
public static HttpServletRequest getRequest() {
|
public static HttpServletRequest getRequest() {
|
||||||
return (HttpServletRequest) s_request.get();
|
return (HttpServletRequest) s_request.get();
|
||||||
|
|
@ -126,7 +125,6 @@ public class Web {
|
||||||
* Gets the servlet context of the current thread.
|
* Gets the servlet context of the current thread.
|
||||||
*
|
*
|
||||||
* @return The current <code>ServletContext</code>; it can be null
|
* @return The current <code>ServletContext</code>; it can be null
|
||||||
*
|
|
||||||
*/
|
*/
|
||||||
public static ServletContext getServletContext() {
|
public static ServletContext getServletContext() {
|
||||||
return (ServletContext) s_servletContext.get();
|
return (ServletContext) s_servletContext.get();
|
||||||
|
|
|
||||||
|
|
@ -65,7 +65,8 @@ public class ThemeDirectorConfig extends AbstractConfig {
|
||||||
return s_conf;
|
return s_conf;
|
||||||
}
|
}
|
||||||
|
|
||||||
// set of configuration parameters
|
// /////////////////////////////////////////////////////////////////
|
||||||
|
// Set of Configuration Parameters
|
||||||
// /////////////////////////////////////////////////////////////////
|
// /////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
/** Directory that all of the default themes are copied from. */
|
/** Directory that all of the default themes are copied from. */
|
||||||
|
|
@ -74,19 +75,19 @@ public class ThemeDirectorConfig extends AbstractConfig {
|
||||||
("themedirector.default_theme_path",
|
("themedirector.default_theme_path",
|
||||||
Parameter.OPTIONAL, "/themes/master/");
|
Parameter.OPTIONAL, "/themes/master/");
|
||||||
|
|
||||||
/** Servlet context path containing the default theme.
|
// /** Servlet context path containing the default theme.
|
||||||
* Previously ccm-themedirector used to be installed in its own
|
// * Previously ccm-themedirector used to be installed in its own
|
||||||
* web context. In this case the appropriate web context should
|
// * web context. In this case the appropriate web context should
|
||||||
* be specified.
|
// * be specified.
|
||||||
* Currently, it is installed as part of the main application,
|
// * Currently, it is installed as part of the main application,
|
||||||
* therefore it is empty by default.
|
// * therefore it is empty by default.
|
||||||
* @deprecated without direct replacement. Themedirector's Webapp context
|
// * @deprecated without direct replacement. Themedirector's Webapp context
|
||||||
* has to be determined at runtime.
|
// * has to be determined at runtime.
|
||||||
*/
|
// */
|
||||||
private final Parameter m_defaultThemeContext =
|
// private final Parameter m_defaultThemeContext =
|
||||||
new StringParameter
|
// new StringParameter
|
||||||
("themedirector.default_theme_context",
|
// ("themedirector.default_theme_context",
|
||||||
Parameter.OPTIONAL, "");
|
// Parameter.OPTIONAL, "");
|
||||||
// Parameter.OPTIONAL, "/ccm-themedirector/");
|
// Parameter.OPTIONAL, "/ccm-themedirector/");
|
||||||
|
|
||||||
/** File containing the default themes directory. Used in conjuntion with
|
/** File containing the default themes directory. Used in conjuntion with
|
||||||
|
|
@ -143,10 +144,11 @@ public class ThemeDirectorConfig extends AbstractConfig {
|
||||||
*/
|
*/
|
||||||
public ThemeDirectorConfig() {
|
public ThemeDirectorConfig() {
|
||||||
|
|
||||||
register(m_fileExtParam);
|
|
||||||
register(m_defaultThemeContext);
|
|
||||||
register(m_defaultThemeManifest);
|
|
||||||
register(m_defaultThemePath);
|
register(m_defaultThemePath);
|
||||||
|
// register(m_defaultThemeContext);
|
||||||
|
register(m_defaultThemeManifest);
|
||||||
|
register(m_fileExtParam);
|
||||||
|
|
||||||
register(m_themeDevFileWatchStartupDelay);
|
register(m_themeDevFileWatchStartupDelay);
|
||||||
register(m_themeDevFileWatchPollDelay);
|
register(m_themeDevFileWatchPollDelay);
|
||||||
register(m_themePubFileWatchStartupDelay);
|
register(m_themePubFileWatchStartupDelay);
|
||||||
|
|
@ -155,6 +157,88 @@ public class ThemeDirectorConfig extends AbstractConfig {
|
||||||
loadInfo();
|
loadInfo();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// /////////////////////////////////////////////////////////////////
|
||||||
|
// Set of Configuration Parameters
|
||||||
|
// /////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieves the path to a directory containing a complete set of theme
|
||||||
|
* files to copy into a new theme as a default implementation.
|
||||||
|
* By default it is set to a master directory containing the distribution's
|
||||||
|
* default theme. When creating a new theme it is copied over to provide
|
||||||
|
* a default for the new theme.
|
||||||
|
*
|
||||||
|
* Developer's note:
|
||||||
|
* Previously it was used as a string to filter a files directory stored in
|
||||||
|
* the Manifest file. Matching files were copied to the new theme's directory.
|
||||||
|
* The reason to use this approach is not documented. As a guess: it enables
|
||||||
|
* to copy from more than one directory, if those directories share a common
|
||||||
|
* name part which is specified here. But because all files must be present
|
||||||
|
* at deploy time to be included into the Manifest file it makes no sense.
|
||||||
|
* From original comment: "Specifically, if this is not null
|
||||||
|
* (or the empty string) than any file that is used as part of
|
||||||
|
* the default directory must start with this string."
|
||||||
|
*
|
||||||
|
* @return name of a directory containing a default theme implementation
|
||||||
|
*/
|
||||||
|
public String getDefaultThemePath() {
|
||||||
|
String defaultThemePath = (String)get(m_defaultThemePath);
|
||||||
|
if (defaultThemePath == null || defaultThemePath.trim().length() == 0) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
// remove leading slashwhich is already included in themedirector's
|
||||||
|
// constants of the directory layout.
|
||||||
|
if (defaultThemePath.startsWith("/")) {
|
||||||
|
defaultThemePath = defaultThemePath.substring(1);
|
||||||
|
}
|
||||||
|
return defaultThemePath;
|
||||||
|
}
|
||||||
|
|
||||||
|
// /**
|
||||||
|
// * This returns the name of the servlet context containing
|
||||||
|
// * the default theme.
|
||||||
|
// *
|
||||||
|
// * @return
|
||||||
|
// * @deprecated without direct replacement, See note above
|
||||||
|
// */
|
||||||
|
// public String getDefaultThemeContext() {
|
||||||
|
// String ctx = (String)get(m_defaultThemeContext);
|
||||||
|
// if (ctx == null) {
|
||||||
|
// ctx = "/";
|
||||||
|
// }
|
||||||
|
// if (!ctx.endsWith("/")) {
|
||||||
|
// ctx = ctx + "/";
|
||||||
|
// }
|
||||||
|
// if (!ctx.startsWith("/")) {
|
||||||
|
// ctx = "/" + ctx;
|
||||||
|
// }
|
||||||
|
// return ctx;
|
||||||
|
// }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This returns the name of the manifest file containing a list of default
|
||||||
|
* theme.
|
||||||
|
*
|
||||||
|
* @return
|
||||||
|
* @deprecated replaced by a direct copy from the default directory
|
||||||
|
*/
|
||||||
|
public String getDefaultThemeManifest() {
|
||||||
|
return (String)get(m_defaultThemeManifest);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private static final String DEFAULT_THEME_URL =
|
||||||
|
ThemeDirector.DEFAULT_THEME + "." + Theme.URL;
|
||||||
|
private static final String DEFAULT_THEME_URL_ATTRIBUTE =
|
||||||
|
"defaultThemeURLAttribute";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Purpose uncodumented.
|
||||||
|
*
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
public Collection getDownloadFileExtensions() {
|
public Collection getDownloadFileExtensions() {
|
||||||
if (m_downloadFileExtensions == null) {
|
if (m_downloadFileExtensions == null) {
|
||||||
String extensions = (String)get(m_fileExtParam);
|
String extensions = (String)get(m_fileExtParam);
|
||||||
|
|
@ -170,90 +254,8 @@ public class ThemeDirectorConfig extends AbstractConfig {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The number of seconds to wait before checking the database
|
|
||||||
* for the first time. A value of 0 means that the thread
|
|
||||||
* should not be started. This checks for published files.
|
|
||||||
*/
|
|
||||||
public Integer getThemePubFileWatchStartupDelay() {
|
|
||||||
return (Integer)get(m_themePubFileWatchStartupDelay);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns the number of seconds between checking for updated
|
|
||||||
* files in the file system. This checks for published files.
|
|
||||||
*/
|
|
||||||
public Integer getThemePubFileWatchPollDelay() {
|
|
||||||
return (Integer)get(m_themePubFileWatchPollDelay);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The number of seconds to wait before checking the database
|
|
||||||
* for the first time. A value of 0 means that the thread
|
|
||||||
* should not be started. This checks for development files.
|
|
||||||
*/
|
|
||||||
public Integer getThemeDevFileWatchStartupDelay() {
|
|
||||||
return (Integer)get(m_themeDevFileWatchStartupDelay);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns the number of seconds between checking for updated
|
|
||||||
* files in the file system. This checks for development files.
|
|
||||||
*/
|
|
||||||
public Integer getThemeDevFileWatchPollDelay() {
|
|
||||||
return (Integer)get(m_themeDevFileWatchPollDelay);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* This returns the name of the servlet context containing
|
|
||||||
* the default theme
|
|
||||||
*/
|
|
||||||
public String getDefaultThemeContext() {
|
|
||||||
String ctx = (String)get(m_defaultThemeContext);
|
|
||||||
if (ctx == null) {
|
|
||||||
ctx = "/";
|
|
||||||
}
|
|
||||||
if (!ctx.endsWith("/")) {
|
|
||||||
ctx = ctx + "/";
|
|
||||||
}
|
|
||||||
if (!ctx.startsWith("/")) {
|
|
||||||
ctx = "/" + ctx;
|
|
||||||
}
|
|
||||||
return ctx;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* This returns the name of the manifest file containing
|
|
||||||
* the default theme.
|
|
||||||
*/
|
|
||||||
public String getDefaultThemeManifest() {
|
|
||||||
return (String)get(m_defaultThemeManifest);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* This returns a string that can be used as a file filter for
|
|
||||||
* the default directory. Specifically, if this is not null
|
|
||||||
* (or the empty string) than any file that is used as part of
|
|
||||||
* the default directory must start with this string
|
|
||||||
*/
|
|
||||||
public String getDefaultThemePath() {
|
|
||||||
String defaultThemePath = (String)get(m_defaultThemePath);
|
|
||||||
if (defaultThemePath == null || defaultThemePath.trim().length() == 0) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
if (defaultThemePath.startsWith("/")) {
|
|
||||||
defaultThemePath = defaultThemePath.substring(1);
|
|
||||||
}
|
|
||||||
return defaultThemePath;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
private static final String DEFAULT_THEME_URL =
|
|
||||||
ThemeDirector.DEFAULT_THEME + "." + Theme.URL;
|
|
||||||
private static final String DEFAULT_THEME_URL_ATTRIBUTE =
|
|
||||||
"defaultThemeURLAttribute";
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
* Purpose undocumented.
|
||||||
*
|
*
|
||||||
* @param req
|
* @param req
|
||||||
* @return
|
* @return
|
||||||
|
|
@ -285,4 +287,45 @@ public class ThemeDirectorConfig extends AbstractConfig {
|
||||||
req.setAttribute( DEFAULT_THEME_URL_ATTRIBUTE, themeURL );
|
req.setAttribute( DEFAULT_THEME_URL_ATTRIBUTE, themeURL );
|
||||||
return themeURL;
|
return themeURL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The number of seconds to wait before checking the database
|
||||||
|
* for the first time. A value of 0 means that the thread
|
||||||
|
* should not be started. This checks for published files.
|
||||||
|
*
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
public Integer getThemePubFileWatchStartupDelay() {
|
||||||
|
return (Integer)get(m_themePubFileWatchStartupDelay);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the number of seconds between checking for updated
|
||||||
|
* files in the file system. This checks for published files.
|
||||||
|
*
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
public Integer getThemePubFileWatchPollDelay() {
|
||||||
|
return (Integer)get(m_themePubFileWatchPollDelay);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The number of seconds to wait before checking the database
|
||||||
|
* for the first time. A value of 0 means that the thread
|
||||||
|
* should not be started. This checks for development files.
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
public Integer getThemeDevFileWatchStartupDelay() {
|
||||||
|
return (Integer)get(m_themeDevFileWatchStartupDelay);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the number of seconds between checking for updated
|
||||||
|
* files in the file system. This checks for development files.
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
public Integer getThemeDevFileWatchPollDelay() {
|
||||||
|
return (Integer)get(m_themeDevFileWatchPollDelay);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -35,27 +35,29 @@ public interface ThemeDirectorConstants {
|
||||||
public final static String DEV_DIR_STUB = "/devel-themedir";
|
public final static String DEV_DIR_STUB = "/devel-themedir";
|
||||||
|
|
||||||
/** Path stub into directory for production themes (sub-dir of THEMES_DIR).
|
/** Path stub into directory for production themes (sub-dir of THEMES_DIR).
|
||||||
* According to JavaEE spec with leading but without trailing "/"! */
|
* According to JavaEE spec with leading "/", but deviating from the
|
||||||
|
* JavaEE spec we add a trailing "/" for backwards compatibility to
|
||||||
|
* versions of CCM! */
|
||||||
public final static String
|
public final static String
|
||||||
PROD_THEMES_BASE_DIR = THEMES_DIR + PROD_DIR_STUB;
|
PROD_THEMES_BASE_DIR = THEMES_DIR + PROD_DIR_STUB + "/";
|
||||||
/** Path stub into directory for production themes (sub-dir of THEMES_DIR).
|
/** Path stub into directory for production themes (sub-dir of THEMES_DIR).
|
||||||
* According to JavaEE spec with leading but without trailing "/"! */
|
* According to JavaEE spec with leading "/", but deviating from the
|
||||||
|
* JavaEE spec we add a trailing "/" for backwards compatibility to
|
||||||
|
* versions of CCM! */
|
||||||
public final static String
|
public final static String
|
||||||
DEV_THEMES_BASE_DIR = THEMES_DIR + DEV_DIR_STUB ;
|
DEV_THEMES_BASE_DIR = THEMES_DIR + DEV_DIR_STUB + "/";
|
||||||
|
|
||||||
// ccm-themedirector (formerly ccm-ldn-theme) is no longer installed in its
|
// Developers NOTE:
|
||||||
// own web context (ROOT or ccm-ldn-theme/ccm-themedirector) so it is not
|
// ================
|
||||||
// needed anymore. We we want to install it in its own context again, we
|
// We should consider to use the theme's url as entered by the user with
|
||||||
// should find a way to determin the context from a central configuration.
|
// a leading slash according to the specification and for sake of
|
||||||
// public final static String WEB_APP_NAME = "ROOT";
|
// consistency within CCM.
|
||||||
|
// We would have to adjust the validation listener and the process listener
|
||||||
|
// in class ui/ThemeForm and to update the existing themes in the database.
|
||||||
|
|
||||||
/** The location of the sync jsp used to sync up the multiple servers. */
|
/** The location of the sync jsp used to sync up the multiple servers. */
|
||||||
public final static String SYNC_JSP = "sync-theme.jsp";
|
public final static String SYNC_JSP = "sync-theme.jsp";
|
||||||
|
|
||||||
/** This can be used to find the root webapp directory that is used
|
|
||||||
by default for most of the applications in CCM */
|
|
||||||
public final static String ROOT_WEBAPP_PATH = "/ROOT";
|
|
||||||
|
|
||||||
public static final String THEME_XML_PREFIX = "theme:";
|
public static final String THEME_XML_PREFIX = "theme:";
|
||||||
public final static String XML_NS =
|
public final static String XML_NS =
|
||||||
"http://ccm.redhat.com/themedirector/1.0";
|
"http://ccm.redhat.com/themedirector/1.0";
|
||||||
|
|
|
||||||
|
|
@ -32,3 +32,9 @@ theme.undo.default_style=Default Style
|
||||||
theme.none=None
|
theme.none=None
|
||||||
theme.save=Save
|
theme.save=Save
|
||||||
theme.set_default_theme=Default Theme
|
theme.set_default_theme=Default Theme
|
||||||
|
theme.title_hint=Enter the title of the theme, up to 80 characters.
|
||||||
|
theme.description_hint=Enter a short description for the theme, up to 4000 characters.
|
||||||
|
theme.url_hint=Enter the LAST part of the url for the theme, eg 'holiday'. Should NOT include a leading nor a trailing slash!
|
||||||
|
theme.save_button_hint=Save the details in the form
|
||||||
|
theme.cancel_button_hint=Abort changes & reset the form.
|
||||||
|
theme.cancel_button_pressed_msg=cancel pressed
|
||||||
|
|
|
||||||
|
|
@ -32,3 +32,9 @@ theme.undo.default_style=Standard-Theme
|
||||||
theme.none=Keines
|
theme.none=Keines
|
||||||
theme.save=Speichern
|
theme.save=Speichern
|
||||||
theme.set_default_theme=Standard-Theme
|
theme.set_default_theme=Standard-Theme
|
||||||
|
theme.title_hint=Der Titel des Themes, kann bis zu 80 Zeichen lang sein.
|
||||||
|
theme.description_hint=Geben Sie eine kurze Beschreibung ein, bis zu 4000 Zeichen lang.
|
||||||
|
theme.url_hint=Geben Sie den LETZTEN Teil der URL ein, z.B. 'ferien'. Der Eintrag darf weder ein f\u00fchrendes noch ein abschlie\u00dfendes '/' enthalten!
|
||||||
|
theme.save_button_hint=Speichern der Angaben in dem Formular.
|
||||||
|
theme.cancel_button_hint=Verwerfen der Eintragungen und R\u00fccksetzen des Formulars.
|
||||||
|
theme.cancel_button_pressed_msg=Vorgang abgebrochen
|
||||||
|
|
|
||||||
|
|
@ -32,3 +32,9 @@ theme.undo.default_style=Default Style
|
||||||
theme.none=None
|
theme.none=None
|
||||||
theme.save=Save
|
theme.save=Save
|
||||||
theme.set_default_theme=Default Theme
|
theme.set_default_theme=Default Theme
|
||||||
|
theme.title_hint=Enter the title of the theme, up to 80 characters.
|
||||||
|
theme.description_hint=Enter a short description for the theme, up to 4000 characters.
|
||||||
|
theme.url_hint=Enter the LAST part of tht url for the theme, eg 'holiday'. Should NOT include a leading nor a trailing slash!
|
||||||
|
theme.save_button_hint=Save the details in the form
|
||||||
|
theme.cancel_button_hint=Abort changes & reset the form.
|
||||||
|
theme.cancel_button_pressed_msg=cancel pressed
|
||||||
|
|
|
||||||
|
|
@ -32,3 +32,9 @@ theme.undo.default_style=Default Style
|
||||||
theme.none=None
|
theme.none=None
|
||||||
theme.save=Save
|
theme.save=Save
|
||||||
theme.set_default_theme=Default Theme
|
theme.set_default_theme=Default Theme
|
||||||
|
theme.title_hint=Enter the title of the theme, up to 80 characters.
|
||||||
|
theme.description_hint=Enter a short description for the theme, up to 4000 characters.
|
||||||
|
theme.url_hint=Enter the LAST part of tht url for the theme, eg 'holiday'. Should NOT include a leading nor a trailing slash!
|
||||||
|
theme.save_button_hint=Save the details in the form
|
||||||
|
theme.cancel_button_hint=Abort changes & reset the form.
|
||||||
|
theme.cancel_button_pressed_msg=cancel pressed
|
||||||
|
|
|
||||||
|
|
@ -52,6 +52,8 @@ import java.io.File;
|
||||||
import java.io.FileOutputStream;
|
import java.io.FileOutputStream;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
|
import org.apache.commons.io.FileUtils;
|
||||||
|
import org.apache.commons.io.filefilter.DirectoryFileFilter;
|
||||||
|
|
||||||
import org.apache.log4j.Logger;
|
import org.apache.log4j.Logger;
|
||||||
|
|
||||||
|
|
@ -64,50 +66,68 @@ import org.apache.log4j.Logger;
|
||||||
*/
|
*/
|
||||||
public class ThemeForm extends Form implements Cancellable, ThemeDirectorConstants {
|
public class ThemeForm extends Form implements Cancellable, ThemeDirectorConstants {
|
||||||
|
|
||||||
|
/** Internal logger instance to faciliate debugging. Enable logging output
|
||||||
|
* by editing /WEB-INF/conf/log4j.properties int hte runtime environment
|
||||||
|
* and set com.arsdigita.themedirector.ui.ThemeForm=DEBUG by uncommenting
|
||||||
|
* or adding the line. */
|
||||||
private static final Logger s_log = Logger.getLogger(ThemeForm.class);
|
private static final Logger s_log = Logger.getLogger(ThemeForm.class);
|
||||||
|
|
||||||
private ThemeSelectionModel m_theme;
|
private final ThemeSelectionModel m_theme;
|
||||||
private TextField m_title;
|
private final TextField m_title;
|
||||||
private TextArea m_description;
|
private final TextArea m_description;
|
||||||
private TextField m_url;
|
private final TextField m_url;
|
||||||
private SaveCancelSection m_buttons;
|
private final SaveCancelSection m_buttons;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructor creats the input form to create a new theme or edit an
|
||||||
|
* existing one.
|
||||||
|
*
|
||||||
|
* @param name
|
||||||
|
* @param theme
|
||||||
|
*/
|
||||||
public ThemeForm(String name,
|
public ThemeForm(String name,
|
||||||
ThemeSelectionModel theme) {
|
ThemeSelectionModel theme) {
|
||||||
super(name, new GridPanel(2));
|
super(name, new GridPanel(2));
|
||||||
setClassAttr("simpleThemeForm");
|
setClassAttr("simpleThemeForm");
|
||||||
setRedirecting(true);
|
setRedirecting(true);
|
||||||
|
|
||||||
m_theme = theme;
|
m_theme = theme; // Initialize ThemeSelectionModel
|
||||||
|
|
||||||
|
// Add the Title input field
|
||||||
add(new Label(GlobalizationUtil.globalize("theme.title")));
|
add(new Label(GlobalizationUtil.globalize("theme.title")));
|
||||||
m_title = new TextField(new StringParameter("title"));
|
m_title = new TextField(new StringParameter("title"));
|
||||||
|
// Experimental. We are migrating the Label if a widget as part of the
|
||||||
|
// widgets's xml properties.
|
||||||
|
m_title.setLabel(GlobalizationUtil.globalize("theme.title"));
|
||||||
m_title.addValidationListener(new NotEmptyValidationListener());
|
m_title.addValidationListener(new NotEmptyValidationListener());
|
||||||
m_title.setHint("Enter the title of the theme, up to 80 characters");
|
m_title.setHint(GlobalizationUtil.globalize("theme.title_hint"));
|
||||||
m_title.setSize(40);
|
m_title.setSize(40);
|
||||||
add(m_title);
|
add(m_title);
|
||||||
|
|
||||||
add(new Label(GlobalizationUtil.globalize("theme.description")));
|
add(new Label(GlobalizationUtil.globalize("theme.description")));
|
||||||
m_description = new TextArea(new StringParameter("description"));
|
m_description = new TextArea(new StringParameter("description"));
|
||||||
|
// Experimental, see above
|
||||||
|
m_description.setLabel(GlobalizationUtil.globalize("theme.description"));
|
||||||
m_description.setCols(40);
|
m_description.setCols(40);
|
||||||
m_description.setRows(4);
|
m_description.setRows(4);
|
||||||
m_description.setHint(
|
m_description.setHint(GlobalizationUtil
|
||||||
"Enter a short description for the theme, up to 4000 characters"
|
.globalize("theme.description_hint"));
|
||||||
);
|
|
||||||
add(m_description);
|
add(m_description);
|
||||||
|
|
||||||
add(new Label(GlobalizationUtil.globalize("theme.url")));
|
add(new Label(GlobalizationUtil.globalize("theme.url")));
|
||||||
m_url = new TextField(new StringParameter("url"));
|
m_url = new TextField(new StringParameter("url"));
|
||||||
|
// Experimental, see above
|
||||||
|
m_url.setLabel(GlobalizationUtil.globalize("theme.url"));
|
||||||
m_url.addValidationListener(new NotEmptyValidationListener());
|
m_url.addValidationListener(new NotEmptyValidationListener());
|
||||||
m_title.setSize(40);
|
m_title.setSize(40);
|
||||||
m_url.setHint(
|
m_url.setHint(GlobalizationUtil.globalize("theme.url_hint"));
|
||||||
"Enter the url for the theme, eg 'holiday'"
|
|
||||||
);
|
|
||||||
add(m_url);
|
add(m_url);
|
||||||
|
|
||||||
m_buttons = new SaveCancelSection();
|
m_buttons = new SaveCancelSection();
|
||||||
m_buttons.getSaveButton().setHint("Save the details in the form");
|
m_buttons.getSaveButton().setHint(GlobalizationUtil
|
||||||
m_buttons.getCancelButton().setHint("Abort changes & reset the form");
|
.globalize("theme.save_button_hint"));
|
||||||
|
m_buttons.getCancelButton().setHint(GlobalizationUtil
|
||||||
|
.globalize("theme.cancel_button_hint"));
|
||||||
add(m_buttons);
|
add(m_buttons);
|
||||||
|
|
||||||
addSubmissionListener(new ThemeSubmissionListener());
|
addSubmissionListener(new ThemeSubmissionListener());
|
||||||
|
|
@ -116,23 +136,47 @@ public class ThemeForm extends Form implements Cancellable, ThemeDirectorConstan
|
||||||
addValidationListener(new ThemeValidationListener());
|
addValidationListener(new ThemeValidationListener());
|
||||||
}
|
}
|
||||||
|
|
||||||
// if this form is cancelled
|
/**
|
||||||
|
* Processed if this form is cancelled.
|
||||||
|
*
|
||||||
|
* @param s
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
public boolean isCancelled(PageState s) {
|
public boolean isCancelled(PageState s) {
|
||||||
return m_buttons.getCancelButton().isSelected(s);
|
return m_buttons.getCancelButton().isSelected(s);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
*/
|
||||||
private class ThemeSubmissionListener implements FormSubmissionListener {
|
private class ThemeSubmissionListener implements FormSubmissionListener {
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @param e
|
||||||
|
* @throws FormProcessException
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
public void submitted(FormSectionEvent e)
|
public void submitted(FormSectionEvent e)
|
||||||
throws FormProcessException {
|
throws FormProcessException {
|
||||||
PageState state = e.getPageState();
|
PageState state = e.getPageState();
|
||||||
|
|
||||||
if (m_buttons.getCancelButton().isSelected(state)) {
|
if (m_buttons.getCancelButton().isSelected(state)) {
|
||||||
throw new FormProcessException("cancel pressed");
|
throw new FormProcessException(
|
||||||
|
"cancel pressed",
|
||||||
|
GlobalizationUtil.globalize("theme.cancel_button_hint")
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Initializes the theme form with appropriate values if theme already
|
||||||
|
* exists.
|
||||||
|
*/
|
||||||
private class ThemeInitListener implements FormInitListener {
|
private class ThemeInitListener implements FormInitListener {
|
||||||
|
@Override
|
||||||
public void init(FormSectionEvent e)
|
public void init(FormSectionEvent e)
|
||||||
throws FormProcessException {
|
throws FormProcessException {
|
||||||
PageState state = e.getPageState();
|
PageState state = e.getPageState();
|
||||||
|
|
@ -150,15 +194,34 @@ public class ThemeForm extends Form implements Cancellable, ThemeDirectorConstan
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ProcessListener class to act upon the themedirector form input (after
|
||||||
|
* successful input validation if any). It's process method is the entry
|
||||||
|
* point.
|
||||||
|
*/
|
||||||
private class ThemeProcessListener implements FormProcessListener {
|
private class ThemeProcessListener implements FormProcessListener {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Process the form input data. The data are first stored into the
|
||||||
|
* database and than the file system is synced if required. In case of
|
||||||
|
* a new theme the default theme files (if existent) are copied. If for
|
||||||
|
* an existing theme the name (url) has changed, the filesystem
|
||||||
|
* directories are modified accordingly.
|
||||||
|
*
|
||||||
|
* @param e
|
||||||
|
* @throws FormProcessException
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
public void process(FormSectionEvent e)
|
public void process(FormSectionEvent e)
|
||||||
throws FormProcessException {
|
throws FormProcessException {
|
||||||
|
|
||||||
PageState state = e.getPageState();
|
PageState state = e.getPageState();
|
||||||
|
|
||||||
Theme theme = m_theme.getSelectedTheme(state);
|
Theme theme = m_theme.getSelectedTheme(state);
|
||||||
String oldURL = null;
|
String oldURL = null;
|
||||||
String newURL = null;
|
String newURL = null;
|
||||||
if (theme == null) {
|
if (theme == null) {
|
||||||
|
/* We handle a new (created) theme. No previous values exist.*/
|
||||||
newURL = (String)m_url.getValue(state);
|
newURL = (String)m_url.getValue(state);
|
||||||
theme = new Theme((String)m_title.getValue(state),
|
theme = new Theme((String)m_title.getValue(state),
|
||||||
(String)m_description.getValue(state),
|
(String)m_description.getValue(state),
|
||||||
|
|
@ -171,7 +234,7 @@ public class ThemeForm extends Form implements Cancellable, ThemeDirectorConstan
|
||||||
theme.setURL(newURL);
|
theme.setURL(newURL);
|
||||||
}
|
}
|
||||||
|
|
||||||
// only add the theme if it is published
|
// only add to the theme if it is published
|
||||||
if (theme.getLastPublishedUser() != null) {
|
if (theme.getLastPublishedUser() != null) {
|
||||||
Subsite.getConfig().addTheme(theme.getURL(), theme.getTitle());
|
Subsite.getConfig().addTheme(theme.getURL(), theme.getTitle());
|
||||||
}
|
}
|
||||||
|
|
@ -180,24 +243,23 @@ public class ThemeForm extends Form implements Cancellable, ThemeDirectorConstan
|
||||||
Subsite.getConfig().removeTheme(oldURL);
|
Subsite.getConfig().removeTheme(oldURL);
|
||||||
}
|
}
|
||||||
|
|
||||||
theme.save();
|
theme.save(); // save theme to database
|
||||||
m_theme.setSelectedObject(state, theme);
|
m_theme.setSelectedObject(state, theme);
|
||||||
|
|
||||||
// now that the db part is done, we do the file IO
|
// now that the db part is done, we do the file IO
|
||||||
File newDirectory = null;
|
File newDirectory = null;
|
||||||
File oldDirectory = null;
|
File oldDirectory = null;
|
||||||
try {
|
try {
|
||||||
// The WebAppRoot should be something like this:
|
// Determine the WebAppRoot should be something like this:
|
||||||
// /var/ccm-devel/web/<username>/<projectname>/webapps/ccm-ldn-theme;
|
// /var/ccm-devel/web/<username>/<projectname>/webapps/libreccm;
|
||||||
File currentRoot = new File(Web.getServletContext().getRealPath("/"));
|
File currentRoot = new File(Web.getServletContext().getRealPath("/"));
|
||||||
|
|
||||||
newDirectory = new File(currentRoot, DEV_THEMES_BASE_DIR +
|
newDirectory = new File(currentRoot, DEV_THEMES_BASE_DIR +
|
||||||
newURL);
|
newURL);
|
||||||
if (newDirectory.exists() && !newURL.equals(oldURL)) {
|
if (newDirectory.exists() && !newURL.equals(oldURL)) {
|
||||||
// this means there is a file in the file system
|
// this means there is a file in the file system but not in
|
||||||
// but not in the database
|
// the database this should never happen because "validate"
|
||||||
// this should never happen because "validate" should
|
// should catch it.
|
||||||
// catch it.
|
|
||||||
throw new UncheckedWrapperException
|
throw new UncheckedWrapperException
|
||||||
("The file " + newDirectory.getName() + " already " +
|
("The file " + newDirectory.getName() + " already " +
|
||||||
"exists in the file system but not in the " +
|
"exists in the file system but not in the " +
|
||||||
|
|
@ -210,16 +272,17 @@ public class ThemeForm extends Form implements Cancellable, ThemeDirectorConstan
|
||||||
oldURL);
|
oldURL);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (oldURL == null || !oldDirectory.exists()) {
|
if ( oldURL == null || !oldDirectory.exists()) {
|
||||||
// we make sure that the base directory exists and
|
// we make sure that the base directory exists and then we
|
||||||
// then we copy the files over.
|
// copy the files over.
|
||||||
File baseDirectory = new File(currentRoot,
|
File baseDirectory = new File(currentRoot,
|
||||||
DEV_THEMES_BASE_DIR);
|
DEV_THEMES_BASE_DIR);
|
||||||
if (!baseDirectory.exists()) {
|
if (!baseDirectory.exists()) {
|
||||||
baseDirectory.mkdirs();
|
baseDirectory.mkdirs();
|
||||||
}
|
}
|
||||||
|
|
||||||
copyDefaultFiles(newDirectory);
|
copyDefaultTheme(newDirectory,null);
|
||||||
|
// copyDefaultFiles(newDirectory);
|
||||||
|
|
||||||
if (oldDirectory != null && !oldDirectory.exists()) {
|
if (oldDirectory != null && !oldDirectory.exists()) {
|
||||||
s_log.warn("We were asked to move files from " +
|
s_log.warn("We were asked to move files from " +
|
||||||
|
|
@ -251,8 +314,9 @@ public class ThemeForm extends Form implements Cancellable, ThemeDirectorConstan
|
||||||
// the old directory...we need them to point to the
|
// the old directory...we need them to point to the
|
||||||
// new directory
|
// new directory
|
||||||
DataCollection collection =
|
DataCollection collection =
|
||||||
SessionManager.getSession().retrieve
|
SessionManager
|
||||||
(Site.BASE_DATA_OBJECT_TYPE);
|
.getSession()
|
||||||
|
.retrieve(Site.BASE_DATA_OBJECT_TYPE);
|
||||||
collection.addEqualsFilter(Site.STYLE_DIRECTORY,
|
collection.addEqualsFilter(Site.STYLE_DIRECTORY,
|
||||||
oldURL);
|
oldURL);
|
||||||
while (collection.next()) {
|
while (collection.next()) {
|
||||||
|
|
@ -270,10 +334,23 @@ public class ThemeForm extends Form implements Cancellable, ThemeDirectorConstan
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ValöidatgionListener class to check the themedirector form input data.
|
||||||
|
* It's validate method is the entry point and executed when submitting
|
||||||
|
* the form.
|
||||||
|
*/
|
||||||
private class ThemeValidationListener implements FormValidationListener {
|
private class ThemeValidationListener implements FormValidationListener {
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @param e
|
||||||
|
* @throws FormProcessException
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
public void validate(FormSectionEvent e)
|
public void validate(FormSectionEvent e)
|
||||||
throws FormProcessException {
|
throws FormProcessException {
|
||||||
PageState state = e.getPageState();
|
PageState state = e.getPageState();
|
||||||
|
|
||||||
String url = (String)m_url.getValue(state);
|
String url = (String)m_url.getValue(state);
|
||||||
validateURLForm(state, url);
|
validateURLForm(state, url);
|
||||||
validateURLUniqueness(state, url);
|
validateURLUniqueness(state, url);
|
||||||
|
|
@ -298,6 +375,9 @@ public class ThemeForm extends Form implements Cancellable, ThemeDirectorConstan
|
||||||
/**
|
/**
|
||||||
* This checks the form of the url...specifically, we are only allowing
|
* This checks the form of the url...specifically, we are only allowing
|
||||||
* [A-Z,a-z,0-9,_,-].
|
* [A-Z,a-z,0-9,_,-].
|
||||||
|
* @param state
|
||||||
|
* @param url
|
||||||
|
* @throws com.arsdigita.bebop.FormProcessException
|
||||||
*/
|
*/
|
||||||
public void validateURLForm(PageState state, String url)
|
public void validateURLForm(PageState state, String url)
|
||||||
throws FormProcessException {
|
throws FormProcessException {
|
||||||
|
|
@ -316,13 +396,14 @@ public class ThemeForm extends Form implements Cancellable, ThemeDirectorConstan
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This makes sure no other theme has the same URL
|
* This makes sure no other theme has the same URL
|
||||||
|
* @param state
|
||||||
|
* @param url
|
||||||
|
* @throws com.arsdigita.bebop.FormProcessException
|
||||||
*/
|
*/
|
||||||
public void validateURLUniqueness(PageState state, String url)
|
public void validateURLUniqueness(PageState state, String url)
|
||||||
throws FormProcessException {
|
throws FormProcessException {
|
||||||
|
|
||||||
if ( url != null ) {
|
if ( url != null ) {
|
||||||
DataCollection collection = SessionManager.getSession()
|
DataCollection collection = SessionManager.getSession()
|
||||||
|
|
@ -341,10 +422,39 @@ public class ThemeForm extends Form implements Cancellable, ThemeDirectorConstan
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Copies a complete directory containing the default theme to a newly
|
||||||
|
* created theme's directory without any filtering or other processing.
|
||||||
|
* It assumes, that the source directory contains a complete and working
|
||||||
|
* set of theme files.
|
||||||
|
*
|
||||||
|
* @param newThemeDirectory specifies the target directory. Must not
|
||||||
|
* be null.
|
||||||
|
* @param defaultThemeDirectory Directory containing a complete set of
|
||||||
|
* theme files intended as default theme.
|
||||||
|
* If null the default theme directory is
|
||||||
|
* retrieved from ThemeDirector config
|
||||||
|
*
|
||||||
|
* @throws IOException
|
||||||
|
*/
|
||||||
|
private void copyDefaultTheme(File newThemeDirectory,
|
||||||
|
File defaultThemeDirectory) throws IOException {
|
||||||
|
if (defaultThemeDirectory == null) {
|
||||||
|
defaultThemeDirectory = new File(
|
||||||
|
Web.getServletContext().getRealPath("/")
|
||||||
|
+ ThemeDirector.getConfig().getDefaultThemePath());
|
||||||
|
}
|
||||||
|
|
||||||
|
FileUtils.copyDirectory(defaultThemeDirectory,
|
||||||
|
newThemeDirectory);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This copies the default theme files to the new directory that
|
* Copies the default theme files to the new directory using a
|
||||||
* is specified by the pass in File
|
* Manifest file to determine the files to copy.
|
||||||
|
*
|
||||||
|
* @param newDirectory specifies the target directory
|
||||||
|
* @throws IOException
|
||||||
*/
|
*/
|
||||||
private void copyDefaultFiles(File newDirectory) throws IOException {
|
private void copyDefaultFiles(File newDirectory) throws IOException {
|
||||||
ClassLoader loader = Thread.currentThread().getContextClassLoader();
|
ClassLoader loader = Thread.currentThread().getContextClassLoader();
|
||||||
|
|
@ -358,7 +468,8 @@ public class ThemeForm extends Form implements Cancellable, ThemeDirectorConstan
|
||||||
}
|
}
|
||||||
|
|
||||||
if ( !newDirectory.mkdirs() ) {
|
if ( !newDirectory.mkdirs() ) {
|
||||||
throw new UncheckedWrapperException("Cannot create theme directory "+newDirectory.getAbsolutePath());
|
throw new UncheckedWrapperException("Cannot create theme directory "
|
||||||
|
+newDirectory.getAbsolutePath());
|
||||||
}
|
}
|
||||||
|
|
||||||
ManifestReader reader =
|
ManifestReader reader =
|
||||||
|
|
@ -367,10 +478,19 @@ public class ThemeForm extends Form implements Cancellable, ThemeDirectorConstan
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
*/
|
||||||
private class FileWriterManifestReader extends ManifestReader {
|
private class FileWriterManifestReader extends ManifestReader {
|
||||||
private File m_newDirectory;
|
private final File m_newDirectory;
|
||||||
private String m_directoryFilter;
|
private final String m_directoryFilter;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructor.
|
||||||
|
*
|
||||||
|
* @param stream
|
||||||
|
* @param newDirectory
|
||||||
|
*/
|
||||||
FileWriterManifestReader(InputStream stream, File newDirectory) {
|
FileWriterManifestReader(InputStream stream, File newDirectory) {
|
||||||
super(stream);
|
super(stream);
|
||||||
m_newDirectory = newDirectory;
|
m_newDirectory = newDirectory;
|
||||||
|
|
@ -378,6 +498,13 @@ public class ThemeForm extends Form implements Cancellable, ThemeDirectorConstan
|
||||||
m_directoryFilter = ThemeDirector.getConfig().getDefaultThemePath();
|
m_directoryFilter = ThemeDirector.getConfig().getDefaultThemePath();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @param is
|
||||||
|
* @param filePath
|
||||||
|
* @param isStyleFile
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
public void processManifestFileLine(InputStream is,
|
public void processManifestFileLine(InputStream is,
|
||||||
String filePath,
|
String filePath,
|
||||||
boolean isStyleFile) {
|
boolean isStyleFile) {
|
||||||
|
|
|
||||||
|
|
@ -42,6 +42,7 @@ import java.util.Collection;
|
||||||
import java.util.Iterator;
|
import java.util.Iterator;
|
||||||
import com.arsdigita.templating.XSLTemplate;
|
import com.arsdigita.templating.XSLTemplate;
|
||||||
import com.arsdigita.templating.WrappedTransformerException;
|
import com.arsdigita.templating.WrappedTransformerException;
|
||||||
|
import java.net.MalformedURLException;
|
||||||
import javax.xml.transform.ErrorListener;
|
import javax.xml.transform.ErrorListener;
|
||||||
|
|
||||||
import javax.xml.transform.TransformerException;
|
import javax.xml.transform.TransformerException;
|
||||||
|
|
@ -49,14 +50,19 @@ import javax.xml.transform.TransformerException;
|
||||||
import org.apache.log4j.Logger;
|
import org.apache.log4j.Logger;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This displays information about the results of running a validation
|
* This displays information about the results of running a validation test
|
||||||
* test on all of the stylesheets for a given theme. It also includes
|
* on all of the stylesheets for a given theme. It also includes
|
||||||
* links to "revalidate" and to return the viewing the theme.
|
* links to "revalidate" and to return the viewing the theme.
|
||||||
*
|
*
|
||||||
* @author Randy Graebner <randyg@redhat.com>
|
* @author Randy Graebner <randyg@redhat.com>
|
||||||
*/
|
*/
|
||||||
class ThemeValidationPanel extends GridPanel implements ThemeDirectorConstants {
|
class ThemeValidationPanel extends GridPanel implements ThemeDirectorConstants {
|
||||||
|
|
||||||
|
/** Internal logger instance to faciliate debugging. Enable logging output
|
||||||
|
* by editing /WEB-INF/conf/log4j.properties int the runtime environment
|
||||||
|
* and set
|
||||||
|
* com.arsdigita.themedirector.ui.ThemeValidationPanel=DEBUG
|
||||||
|
* by uncommenting or adding the line. */
|
||||||
private static final Logger s_log =
|
private static final Logger s_log =
|
||||||
Logger.getLogger(ThemeValidationPanel.class);
|
Logger.getLogger(ThemeValidationPanel.class);
|
||||||
|
|
||||||
|
|
@ -65,14 +71,16 @@ class ThemeValidationPanel extends GridPanel implements ThemeDirectorConstants {
|
||||||
RequestLocal m_listener;
|
RequestLocal m_listener;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This creates a new validation panel
|
* This creates a new validation panel.
|
||||||
|
*
|
||||||
* @param model This is the selection model so that the panel knows
|
* @param model This is the selection model so that the panel knows
|
||||||
* which theme to operation on
|
* which theme to operation on
|
||||||
* @param container This is the parent container that holds this theme.
|
* @param container This is the parent container that holds this theme.
|
||||||
* When the user wants to quit validation and return to where they were,
|
* When the user wants to quit validation and return
|
||||||
* the visibility of the passed in container inverted (if it is visible
|
* to where they were, the visibility of the passed in
|
||||||
* then it becomes invisible, if it is invisible, it becomes visible) and
|
* container inverted (if it is visible then it becomes
|
||||||
* this item becomes invisible.
|
* invisible, if it is invisible, it becomes visible) and
|
||||||
|
* this item becomes invisible.
|
||||||
*/
|
*/
|
||||||
// TODO: passing in the container is a hackish way to do the visibility...
|
// TODO: passing in the container is a hackish way to do the visibility...
|
||||||
// is there a better way to do this?
|
// is there a better way to do this?
|
||||||
|
|
@ -80,8 +88,8 @@ class ThemeValidationPanel extends GridPanel implements ThemeDirectorConstants {
|
||||||
SimpleComponent parentContainer) {
|
SimpleComponent parentContainer) {
|
||||||
super(1);
|
super(1);
|
||||||
m_model = model;
|
m_model = model;
|
||||||
Label results =
|
Label results = new Label(GlobalizationUtil
|
||||||
new Label(GlobalizationUtil.globalize("theme.validation_results"));
|
.globalize("theme.validation_results"));
|
||||||
results.setFontWeight(Label.BOLD);
|
results.setFontWeight(Label.BOLD);
|
||||||
add(results);
|
add(results);
|
||||||
m_listener = new RequestLocal();
|
m_listener = new RequestLocal();
|
||||||
|
|
@ -92,14 +100,16 @@ class ThemeValidationPanel extends GridPanel implements ThemeDirectorConstants {
|
||||||
new ActionLink(new Label(GlobalizationUtil.globalize
|
new ActionLink(new Label(GlobalizationUtil.globalize
|
||||||
("theme.revalidate_theme")));
|
("theme.revalidate_theme")));
|
||||||
revalidateLink.addActionListener(new ActionListener() {
|
revalidateLink.addActionListener(new ActionListener() {
|
||||||
|
@Override
|
||||||
public void actionPerformed(ActionEvent e) {
|
public void actionPerformed(ActionEvent e) {
|
||||||
validateStylesheets(e.getPageState());
|
validateStylesheets(e.getPageState());
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
add(revalidateLink);
|
add(revalidateLink);
|
||||||
|
|
||||||
ToggleActionLink returnLink = new ToggleActionLink
|
ToggleActionLink returnLink = new ToggleActionLink(new Label(
|
||||||
(new Label(GlobalizationUtil.globalize("theme.return_to_previous")));
|
GlobalizationUtil
|
||||||
|
.globalize("theme.return_to_previous")));
|
||||||
returnLink.addToggleComponent(this);
|
returnLink.addToggleComponent(this);
|
||||||
returnLink.addToggleComponent(parentContainer);
|
returnLink.addToggleComponent(parentContainer);
|
||||||
add(returnLink);
|
add(returnLink);
|
||||||
|
|
@ -112,7 +122,7 @@ class ThemeValidationPanel extends GridPanel implements ThemeDirectorConstants {
|
||||||
* class but this is more clear as to what is being done.
|
* class but this is more clear as to what is being done.
|
||||||
*/
|
*/
|
||||||
private class ToggleActionLink extends ActionLink implements ActionListener {
|
private class ToggleActionLink extends ActionLink implements ActionListener {
|
||||||
private ArrayList m_components;
|
private final ArrayList m_components;
|
||||||
|
|
||||||
ToggleActionLink(Label name) {
|
ToggleActionLink(Label name) {
|
||||||
super(name);
|
super(name);
|
||||||
|
|
@ -120,6 +130,7 @@ class ThemeValidationPanel extends GridPanel implements ThemeDirectorConstants {
|
||||||
addActionListener(this);
|
addActionListener(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public void actionPerformed(ActionEvent e) {
|
public void actionPerformed(ActionEvent e) {
|
||||||
Iterator iter = m_components.iterator();
|
Iterator iter = m_components.iterator();
|
||||||
PageState state = e.getPageState();
|
PageState state = e.getPageState();
|
||||||
|
|
@ -149,35 +160,45 @@ class ThemeValidationPanel extends GridPanel implements ThemeDirectorConstants {
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This method sets up the validation by finding the correct base
|
* This method sets up the validation by finding the correct base
|
||||||
* directory and setting up other necessary initialization variables
|
* directory and setting up other necessary initialization variables.
|
||||||
|
*
|
||||||
|
* @param state
|
||||||
|
* @param listener
|
||||||
*/
|
*/
|
||||||
private boolean validateStylesheets(PageState state,
|
private boolean validateStylesheets(PageState state,
|
||||||
LoggingErrorListener listener) {
|
LoggingErrorListener listener) {
|
||||||
// this should verify the stylesheets and then present
|
|
||||||
// any error messages that are found
|
/* Determine the theme to check */
|
||||||
Theme theme = m_model.getSelectedTheme(state);
|
Theme theme = m_model.getSelectedTheme(state);
|
||||||
|
|
||||||
// The call to resolve returns a url similar to this:
|
/* Determine the location in the servers file system */
|
||||||
// http://localhost:9008/resource/ccm-ldn-theme,ROOT/themes/heirloom/apps/theme/xsl/index.xs
|
|
||||||
//
|
|
||||||
// TODO: This is VERY UGLY! Bad style to code a path into source code!
|
|
||||||
// String base = "http://" + Web.getConfig().getHost().toString() +
|
|
||||||
// "/resource/ccm-ldn-theme/";
|
|
||||||
String base = "http://" + Web.getConfig().getHost().toString() +
|
|
||||||
"/resource/ROOT/";
|
|
||||||
File currentRoot = new File(Web.getServletContext().getRealPath("/"));
|
File currentRoot = new File(Web.getServletContext().getRealPath("/"));
|
||||||
File devDir = new File(currentRoot, DEV_THEMES_BASE_DIR +
|
File devDir = new File(currentRoot,
|
||||||
theme.getURL());
|
DEV_THEMES_BASE_DIR + theme.getURL() );
|
||||||
|
|
||||||
// TODO: There has to be a better way to do this
|
/* Determine the URL to the stylesheets. Usually the URL is determined
|
||||||
String stylesheetPath = base +
|
by the templating system, based on a stylesheetPath.txt file
|
||||||
DEV_THEMES_BASE_DIR + theme.getURL();
|
containing patterns to search for.
|
||||||
|
|
||||||
|
Developer's Note:
|
||||||
|
We used to use a 'resource' tag involing a resource servlet to
|
||||||
|
deliver the correct file either from database or filesystem. Ir would
|
||||||
|
require an URL similar to
|
||||||
|
http://localhost:9008/libreccm/resource/themes/heirloom/apps/theme/xsl/index.xs
|
||||||
|
where librecms is the context ccm happpens to be installed in.
|
||||||
|
Currently we bypass the resource servlet and access the filesystem
|
||||||
|
directly. Must be modified as soon as we deliver the theme from db. */
|
||||||
|
String stylesheetPath = "http://" + Web.getConfig().getHost().toString()
|
||||||
|
+ Web.getWebappContextPath()
|
||||||
|
+ DEV_THEMES_BASE_DIR + theme.getURL() ;
|
||||||
|
|
||||||
if (s_log.isDebugEnabled()) {
|
if (s_log.isDebugEnabled()) {
|
||||||
s_log.debug("Path is " + stylesheetPath);
|
s_log.debug("Path is " + stylesheetPath);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// this should verify the stylesheets and then present
|
||||||
|
// any error messages that are found
|
||||||
checkFiles(devDir, stylesheetPath, listener);
|
checkFiles(devDir, stylesheetPath, listener);
|
||||||
|
|
||||||
return !listener.hasErrors();
|
return !listener.hasErrors();
|
||||||
|
|
@ -189,7 +210,8 @@ class ThemeValidationPanel extends GridPanel implements ThemeDirectorConstants {
|
||||||
* the entire folder subtree, loading every single available xsl
|
* the entire folder subtree, loading every single available xsl
|
||||||
* file.
|
* file.
|
||||||
*/
|
*/
|
||||||
private void checkFiles(File baseDirectory, String basePath,
|
private void checkFiles(File baseDirectory,
|
||||||
|
String basePath,
|
||||||
ErrorListener listener) {
|
ErrorListener listener) {
|
||||||
File[] list = baseDirectory.listFiles(new XSLFileFilter());
|
File[] list = baseDirectory.listFiles(new XSLFileFilter());
|
||||||
if (list == null) {
|
if (list == null) {
|
||||||
|
|
@ -203,43 +225,39 @@ class ThemeValidationPanel extends GridPanel implements ThemeDirectorConstants {
|
||||||
// future caught errors. This is sort of a hack but I am not
|
// future caught errors. This is sort of a hack but I am not
|
||||||
// sure of a better way around it
|
// sure of a better way around it
|
||||||
boolean transformUsesListener = true;
|
boolean transformUsesListener = true;
|
||||||
|
for (File list1 : list) {
|
||||||
for (int i = 0; i < list.length; i ++) {
|
if (list1.isDirectory()) {
|
||||||
if (list[i].isDirectory()) {
|
|
||||||
// Only check top level XSL - the rest are xsl:import'd
|
|
||||||
//checkFiles(list[i], basePath + "/" + list[i].getName(),
|
|
||||||
// listener);
|
|
||||||
} else {
|
} else {
|
||||||
String filePath = basePath + "/" + list[i].getName();
|
String filePath = basePath + "/" + list1.getName();
|
||||||
try {
|
try {
|
||||||
URL stylesheetURL = new URL(filePath);
|
URL stylesheetURL = new URL(filePath);
|
||||||
if (s_log.isDebugEnabled()) {
|
if (s_log.isDebugEnabled()) {
|
||||||
s_log.debug("Validating " + stylesheetURL);
|
s_log.debug("Validating " + stylesheetURL);
|
||||||
}
|
}
|
||||||
XSLTemplate template = new XSLTemplate(stylesheetURL,
|
XSLTemplate template = new XSLTemplate(stylesheetURL,
|
||||||
listener);
|
listener);
|
||||||
} catch (WrappedTransformerException we) {
|
} catch (WrappedTransformerException we) {
|
||||||
if (transformUsesListener &&
|
if (transformUsesListener &&
|
||||||
listener instanceof LoggingErrorListener &&
|
listener instanceof LoggingErrorListener &&
|
||||||
!((LoggingErrorListener)listener).hasErrors()) {
|
!((LoggingErrorListener)listener).hasErrors()) {
|
||||||
transformUsesListener = false;
|
transformUsesListener = false;
|
||||||
}
|
}
|
||||||
if (!transformUsesListener) {
|
if (!transformUsesListener) {
|
||||||
TransformerException transEx =
|
TransformerException transEx =
|
||||||
(TransformerException)we.getRootCause();
|
(TransformerException)we.getRootCause();
|
||||||
try {
|
try {
|
||||||
listener.error(transEx);
|
listener.error(transEx);
|
||||||
} catch (TransformerException transformerEx) {
|
} catch (TransformerException transformerEx) {
|
||||||
s_log.error("Error logging the exception " +
|
s_log.error("Error logging the exception " +
|
||||||
transEx.getMessage(),
|
transEx.getMessage(),
|
||||||
transformerEx);
|
transformerEx);
|
||||||
transEx.printStackTrace();
|
transEx.printStackTrace();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
s_log.debug("Wrapper excpetion thrown");
|
s_log.debug("Wrapper excpetion thrown");
|
||||||
} catch (Exception exp) {
|
} catch (MalformedURLException exp) {
|
||||||
s_log.warn("Error creating template that was not a " +
|
s_log.warn("Error creating template that was not a "
|
||||||
" standard wrapper transformer exception.",
|
+ "standard wrapper transformer exception.",
|
||||||
exp);
|
exp);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -249,9 +267,10 @@ class ThemeValidationPanel extends GridPanel implements ThemeDirectorConstants {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This is simply used so that only directories and xsl files are
|
* This is simply used so that only directories and xsl files are
|
||||||
* examined
|
* examined.
|
||||||
*/
|
*/
|
||||||
private static class XSLFileFilter implements FileFilter {
|
private static class XSLFileFilter implements FileFilter {
|
||||||
|
@Override
|
||||||
public boolean accept(File pathname) {
|
public boolean accept(File pathname) {
|
||||||
return pathname.isDirectory() ||
|
return pathname.isDirectory() ||
|
||||||
pathname.getName().endsWith(".xsl");
|
pathname.getName().endsWith(".xsl");
|
||||||
|
|
@ -260,8 +279,8 @@ class ThemeValidationPanel extends GridPanel implements ThemeDirectorConstants {
|
||||||
|
|
||||||
|
|
||||||
private class ValidationResults extends SimpleContainer {
|
private class ValidationResults extends SimpleContainer {
|
||||||
private Label m_noErrorsLabel;
|
private final Label m_noErrorsLabel;
|
||||||
private Label m_errorsLabel;
|
private final Label m_errorsLabel;
|
||||||
|
|
||||||
ValidationResults() {
|
ValidationResults() {
|
||||||
super();
|
super();
|
||||||
|
|
@ -299,8 +318,8 @@ class ThemeValidationPanel extends GridPanel implements ThemeDirectorConstants {
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* if the collection size > 0 then it prints out the xml
|
* If the collection size > 0 then it prints out the xml
|
||||||
* to display the messages
|
* to display the messages.
|
||||||
*/
|
*/
|
||||||
private void printMessages(String name, Element parent,
|
private void printMessages(String name, Element parent,
|
||||||
Collection messages) {
|
Collection messages) {
|
||||||
|
|
|
||||||
|
|
@ -44,23 +44,40 @@ import java.util.TreeSet;
|
||||||
import org.apache.log4j.Logger;
|
import org.apache.log4j.Logger;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This approves the theme and pushes it to the production file location
|
* This approves the theme and pushes it to the production file location
|
||||||
* This action means that the user wants to approve the themes and
|
* This action means that the user wants to approve the themes and
|
||||||
* push them live. This is done by copying the files from
|
* push them live. This is done by copying the files from the devel directory
|
||||||
|
* into the published directory.
|
||||||
*
|
*
|
||||||
* @author Randy Graebner <randyg@redhat.com>
|
* @author Randy Graebner <randyg@redhat.com>
|
||||||
*/
|
*/
|
||||||
public class ApproveThemeActionListener implements ThemeDirectorConstants, ActionListener {
|
public class ApproveThemeActionListener implements ThemeDirectorConstants,
|
||||||
|
ActionListener {
|
||||||
|
|
||||||
private static final Logger s_log =
|
/** Internal logger instance to faciliate debugging. Enable logging output
|
||||||
Logger.getLogger(ApproveThemeActionListener.class);
|
* by editing /WEB-INF/conf/log4j.properties int the runtime environment
|
||||||
|
* and set
|
||||||
|
* com.arsdigita.themedirector.ui.listeners.ApproveThemeActionListener=DEBUG
|
||||||
|
* by uncommenting or adding the line. */
|
||||||
|
private static final Logger s_log = Logger.getLogger(
|
||||||
|
ApproveThemeActionListener.class);
|
||||||
|
|
||||||
private ThemeSelectionModel m_model;
|
private final ThemeSelectionModel m_model;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructor, just stores the ThemeSelectionModel.
|
||||||
|
*
|
||||||
|
* @param model the ThemeSelectionModel
|
||||||
|
*/
|
||||||
public ApproveThemeActionListener(ThemeSelectionModel model) {
|
public ApproveThemeActionListener(ThemeSelectionModel model) {
|
||||||
m_model = model;
|
m_model = model;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @param e
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
public void actionPerformed(ActionEvent e) {
|
public void actionPerformed(ActionEvent e) {
|
||||||
// First, we rename the current production directory
|
// First, we rename the current production directory
|
||||||
// so that if there is an exception, we can try to move it
|
// so that if there is an exception, we can try to move it
|
||||||
|
|
|
||||||
|
|
@ -19,32 +19,37 @@
|
||||||
package com.arsdigita.themedirector.util;
|
package com.arsdigita.themedirector.util;
|
||||||
|
|
||||||
|
|
||||||
|
import com.arsdigita.themedirector.ThemeDirector;
|
||||||
|
import com.arsdigita.themedirector.ThemeDirectorConstants;
|
||||||
|
import com.arsdigita.util.UncheckedWrapperException;
|
||||||
|
import com.arsdigita.web.Web;
|
||||||
|
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
import java.io.InputStreamReader;
|
import java.io.InputStreamReader;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.LineNumberReader;
|
import java.io.LineNumberReader;
|
||||||
import java.util.HashMap;
|
|
||||||
import com.arsdigita.web.Web;
|
|
||||||
import com.arsdigita.util.UncheckedWrapperException;
|
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
import com.arsdigita.themedirector.ThemeDirector;
|
import java.util.HashMap;
|
||||||
import com.arsdigita.themedirector.ThemeDirectorConstants;
|
|
||||||
import javax.servlet.ServletContext;
|
import javax.servlet.ServletContext;
|
||||||
|
|
||||||
import org.apache.log4j.Logger;
|
import org.apache.log4j.Logger;
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This is a utility class that will take in a manifest file and
|
* This is a utility class that will take in a manifest file, read it, and
|
||||||
* read it and then make calls to methods for each file that is found.
|
* then make calls to methods for each file that is found.
|
||||||
* In a typical usage, code will subclass this so that certain methods
|
* In a typical usage, code will subclass this so that certain methods will
|
||||||
* will write to different places. For instance, some code may
|
* write to different places. For instance, some code may override the
|
||||||
* override the "processManifestFileLine" to write the contents to
|
* "processManifestFileLine" to write the contents to the file system
|
||||||
* the file system while another may write it to a zip file.
|
* while another may write it to a zip file.
|
||||||
*/
|
*/
|
||||||
public abstract class ManifestReader implements ThemeDirectorConstants {
|
public abstract class ManifestReader implements ThemeDirectorConstants {
|
||||||
|
|
||||||
private static final Logger s_log =
|
/** Internal logger instance to faciliate debugging. Enable logging output
|
||||||
Logger.getLogger(ManifestReader.class);
|
* by editing /WEB-INF/conf/log4j.properties int hte runtime environment
|
||||||
|
* and set com.arsdigita.themedirector.util.ManifestReader=DEBUG by
|
||||||
|
* uncommenting or adding the line. */
|
||||||
|
private static final Logger s_log = Logger.getLogger(ManifestReader.class);
|
||||||
|
|
||||||
private InputStream m_stream;
|
private InputStream m_stream;
|
||||||
private String m_fileName;
|
private String m_fileName;
|
||||||
|
|
@ -52,8 +57,10 @@ public abstract class ManifestReader implements ThemeDirectorConstants {
|
||||||
private HashMap m_actualContextList;
|
private HashMap m_actualContextList;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This takes in the actual input stream that is the Manifest File
|
* This takes in the actual input stream that is the Manifest File
|
||||||
* so that the input stream can be correctly read
|
* so that the input stream can be correctly read.
|
||||||
|
*
|
||||||
|
* @param stream the input stream to read
|
||||||
*/
|
*/
|
||||||
public ManifestReader(InputStream stream) {
|
public ManifestReader(InputStream stream) {
|
||||||
this(stream, null);
|
this(stream, null);
|
||||||
|
|
@ -64,6 +71,8 @@ public abstract class ManifestReader implements ThemeDirectorConstants {
|
||||||
* so that the input stream can be correctly read. It also
|
* so that the input stream can be correctly read. It also
|
||||||
* takes in the fileName so that it can be used in error messages
|
* takes in the fileName so that it can be used in error messages
|
||||||
* if there is an error.
|
* if there is an error.
|
||||||
|
* @param stream the input stream to read
|
||||||
|
* @param fileName
|
||||||
*/
|
*/
|
||||||
public ManifestReader(InputStream stream, String fileName) {
|
public ManifestReader(InputStream stream, String fileName) {
|
||||||
this(stream, fileName, null);
|
this(stream, fileName, null);
|
||||||
|
|
@ -77,11 +86,11 @@ public abstract class ManifestReader implements ThemeDirectorConstants {
|
||||||
* @param stream The input stream to read
|
* @param stream The input stream to read
|
||||||
* @param fileName The name of the file we are reading that will
|
* @param fileName The name of the file we are reading that will
|
||||||
* be displayed in the case of an error.
|
* be displayed in the case of an error.
|
||||||
* @param possibleServletContext The servlet context to try to use
|
* @param possibleServletContext The servlet context to try to use when
|
||||||
* when looking for files listed in the Manifest. This should be
|
* looking for files listed in the Manifest. This should be set
|
||||||
* set when it is know that the manifest file specifies files
|
* when it is known that the manifest file specifies files that
|
||||||
* that are located under a different webapps. If the
|
* are located under a different webapps. If the file ist not
|
||||||
* file is not found under this context or this context is null
|
* found under this context or this context is null
|
||||||
* then the file tries to use the default ServletContext
|
* then the file tries to use the default ServletContext
|
||||||
*/
|
*/
|
||||||
public ManifestReader(InputStream stream, String fileName,
|
public ManifestReader(InputStream stream, String fileName,
|
||||||
|
|
@ -94,13 +103,20 @@ public abstract class ManifestReader implements ThemeDirectorConstants {
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* this is the name of the file that is being parsed. This will
|
* Retrieves the name of the file that is being parsed. Will return null
|
||||||
* return null if the name has not been set
|
* if the name has not been set.
|
||||||
|
*
|
||||||
|
* @return file name if set, otherwise null
|
||||||
*/
|
*/
|
||||||
public String getFileName() {
|
public String getFileName() {
|
||||||
return m_fileName;
|
return m_fileName;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the name of the file that is being parsed.
|
||||||
|
*
|
||||||
|
* @param fileName
|
||||||
|
*/
|
||||||
public void setFileName(String fileName) {
|
public void setFileName(String fileName) {
|
||||||
m_fileName = fileName;
|
m_fileName = fileName;
|
||||||
}
|
}
|
||||||
|
|
@ -112,17 +128,18 @@ public abstract class ManifestReader implements ThemeDirectorConstants {
|
||||||
* this method will only really do anything once.
|
* this method will only really do anything once.
|
||||||
*/
|
*/
|
||||||
public void processFile() {
|
public void processFile() {
|
||||||
LineNumberReader lines =
|
|
||||||
new LineNumberReader(new InputStreamReader(m_stream));
|
|
||||||
Collection extensions = ThemeDirector.getConfig()
|
|
||||||
.getDownloadFileExtensions();
|
|
||||||
|
|
||||||
try {
|
LineNumberReader lines = new LineNumberReader(
|
||||||
String line = lines.readLine();
|
new InputStreamReader(m_stream));
|
||||||
while (line != null) {
|
Collection extensions = ThemeDirector.getConfig()
|
||||||
line = line.trim();
|
.getDownloadFileExtensions();
|
||||||
|
|
||||||
int fileExtensionIndex = line.lastIndexOf(".");
|
try {
|
||||||
|
String line = lines.readLine();
|
||||||
|
while (line != null) {
|
||||||
|
line = line.trim();
|
||||||
|
|
||||||
|
int fileExtensionIndex = line.lastIndexOf(".");
|
||||||
|
|
||||||
// We check the following things to set the boolean
|
// We check the following things to set the boolean
|
||||||
// indicating if it is a file that should
|
// indicating if it is a file that should
|
||||||
|
|
@ -134,70 +151,77 @@ public abstract class ManifestReader implements ThemeDirectorConstants {
|
||||||
// extensions as specified in the config file
|
// extensions as specified in the config file
|
||||||
// 4. the file starts with the default directory and is
|
// 4. the file starts with the default directory and is
|
||||||
// not just the directory itself
|
// not just the directory itself
|
||||||
boolean fileForDownload = fileExtensionIndex > -1 &&
|
boolean fileForDownload = fileExtensionIndex > -1
|
||||||
line.length() > (fileExtensionIndex+1) &&
|
&& line.length() > (fileExtensionIndex+1)
|
||||||
extensions.contains(line.substring(fileExtensionIndex+1)
|
&& extensions.contains(line.substring(fileExtensionIndex+1)
|
||||||
.toLowerCase());
|
.toLowerCase());
|
||||||
|
|
||||||
// get the stream from the WAR or file system
|
// get the stream from the WAR or file system
|
||||||
InputStream stream =
|
InputStream stream = getResourceAsStream(line,
|
||||||
getResourceAsStream(line, m_possibleServletContext);
|
m_possibleServletContext);
|
||||||
|
|
||||||
if (stream == null) {
|
if (stream == null) {
|
||||||
s_log.debug
|
s_log.debug(m_fileName + ": " + lines.getLineNumber()
|
||||||
(m_fileName + ": " +
|
+ ": no such resource '" + line + "'");
|
||||||
lines.getLineNumber() +
|
} else {
|
||||||
": no such resource '" + line + "'");
|
processManifestFileLine(stream, line, fileForDownload);
|
||||||
} else {
|
stream.close();
|
||||||
processManifestFileLine(stream, line, fileForDownload);
|
|
||||||
|
|
||||||
stream.close();
|
|
||||||
}
|
|
||||||
line = lines.readLine();
|
|
||||||
}
|
|
||||||
} catch (IOException e) {
|
|
||||||
throw new UncheckedWrapperException
|
|
||||||
("Error with " + m_fileName + ": " +
|
|
||||||
lines.getLineNumber(), e);
|
|
||||||
} finally {
|
|
||||||
try {
|
|
||||||
m_stream.close();
|
|
||||||
}
|
|
||||||
catch (IOException e) {
|
|
||||||
throw new UncheckedWrapperException(e);
|
|
||||||
}
|
}
|
||||||
|
line = lines.readLine();
|
||||||
}
|
}
|
||||||
|
} catch (IOException e) {
|
||||||
|
throw new UncheckedWrapperException("Error with " + m_fileName
|
||||||
|
+ ": "
|
||||||
|
+ lines.getLineNumber(), e);
|
||||||
|
} finally {
|
||||||
|
try {
|
||||||
|
m_stream.close();
|
||||||
|
}
|
||||||
|
catch (IOException e) {
|
||||||
|
throw new UncheckedWrapperException(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This provides a way for child classes to look for the resource
|
* This provides a way for child classes to look for the resource in
|
||||||
* in multiple places. By default, it only looks in the ServletContext
|
* multiple places. By default, it only looks in the ServletContext.
|
||||||
|
*
|
||||||
|
* @param line
|
||||||
|
* @param possibleServletContext
|
||||||
|
* @return stream, may be null
|
||||||
*/
|
*/
|
||||||
protected InputStream getResourceAsStream(String line,
|
protected InputStream getResourceAsStream(String line,
|
||||||
String possibleServletContext) {
|
String possibleServletContext) {
|
||||||
InputStream stream = null;
|
InputStream stream = null;
|
||||||
|
|
||||||
if (possibleServletContext != null) {
|
if (possibleServletContext != null) {
|
||||||
stream = Web.getServletContext().getContext(possibleServletContext)
|
stream = Web.getServletContext() // gets the servlet context of
|
||||||
.getResourceAsStream(line);
|
// the current thread
|
||||||
|
.getContext(possibleServletContext)
|
||||||
|
.getResourceAsStream(line);
|
||||||
}
|
}
|
||||||
if (stream != null) {
|
if (stream != null) {
|
||||||
setActualContext
|
setActualContext(line,
|
||||||
(line,
|
Web.getServletContext()
|
||||||
Web.getServletContext().getContext(possibleServletContext));
|
.getContext(possibleServletContext));
|
||||||
} else {
|
} else {
|
||||||
stream = Web.getServletContext().getResourceAsStream(line);
|
stream = Web.getServletContext() // servlet ctx of actual thread
|
||||||
|
.getResourceAsStream(line);
|
||||||
|
|
||||||
if (stream == null) {
|
if (stream == null) {
|
||||||
// this means that the file is not under the passed in
|
// this means that the file is not under the passed in
|
||||||
// context or the default context so let's check the "ROOT"
|
// context nor the default context so let's check the "ROOT"
|
||||||
// context
|
// context
|
||||||
stream = Web.getServletContext().getContext(ROOT_WEBAPP_PATH)
|
// DEPRECATED. CCM may be installed at any context, in many
|
||||||
.getResourceAsStream(line);
|
// cases dedicatedly no longer in ROOT
|
||||||
if (stream != null) {
|
// stream = Web.getServletContext().getContext(ROOT_WEBAPP_PATH)
|
||||||
setActualContext(line, Web.getServletContext()
|
// .getResourceAsStream(line);
|
||||||
.getContext(ROOT_WEBAPP_PATH));
|
// if (stream != null) {
|
||||||
}
|
// setActualContext(line, Web.getServletContext()
|
||||||
|
// .getContext(ROOT_WEBAPP_PATH));
|
||||||
|
// }
|
||||||
} else {
|
} else {
|
||||||
setActualContext(line, Web.getServletContext());
|
setActualContext(line, Web.getServletContext());
|
||||||
}
|
}
|
||||||
|
|
@ -207,14 +231,22 @@ public abstract class ManifestReader implements ThemeDirectorConstants {
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This provides subclasses with access to the actual ServletContext
|
* This provides subclasses with access to the actual ServletContext
|
||||||
* where the line is found. The info for the line should be available
|
* where the line is found. The info for the line should be available
|
||||||
* when processManifestFileLine is called for a given line
|
* when processManifestFileLine is called for a given line.
|
||||||
|
*
|
||||||
|
* @param line
|
||||||
|
* @return
|
||||||
*/
|
*/
|
||||||
protected ServletContext getActualContext(String line) {
|
protected ServletContext getActualContext(String line) {
|
||||||
return (ServletContext)m_actualContextList.get(line);
|
return (ServletContext)m_actualContextList.get(line);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @param line
|
||||||
|
* @param context
|
||||||
|
*/
|
||||||
protected void setActualContext(String line, ServletContext context) {
|
protected void setActualContext(String line, ServletContext context) {
|
||||||
m_actualContextList.put(line, context);
|
m_actualContextList.put(line, context);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -40,8 +40,12 @@ import org.apache.log4j.Logger;
|
||||||
*/
|
*/
|
||||||
public class ThemeDevelopmentFileManager extends ThemeFileManager {
|
public class ThemeDevelopmentFileManager extends ThemeFileManager {
|
||||||
|
|
||||||
private static Logger s_log =
|
/** Internal logger instance to faciliate debugging. Enable logging output
|
||||||
Logger.getLogger(ThemeDevelopmentFileManager.class);
|
* by editing /WEB-INF/conf/log4j.properties int hte runtime environment
|
||||||
|
* and set com.arsdigita.themedirector.util.ThemeDevelopmentFileManager=DEBUG
|
||||||
|
* by uncommenting or adding the line. */
|
||||||
|
private static Logger s_log = Logger
|
||||||
|
.getLogger(ThemeDevelopmentFileManager.class);
|
||||||
|
|
||||||
// The code in this class borrows heavily from
|
// The code in this class borrows heavily from
|
||||||
// com.arsdigita.cms.publishToFile.FileManager
|
// com.arsdigita.cms.publishToFile.FileManager
|
||||||
|
|
@ -51,13 +55,16 @@ public class ThemeDevelopmentFileManager extends ThemeFileManager {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Constructor just delegates to super class.
|
* Constructor just delegates to super class.
|
||||||
|
*
|
||||||
* @param startupDelay
|
* @param startupDelay
|
||||||
* @param pollDelay
|
* @param pollDelay
|
||||||
* @param baseDirectory
|
* @param baseDirectory
|
||||||
*/
|
*/
|
||||||
protected ThemeDevelopmentFileManager(int startupDelay, int pollDelay,
|
protected ThemeDevelopmentFileManager(int startupDelay, int pollDelay,
|
||||||
String baseDirectory) {
|
String baseDirectory) {
|
||||||
super(s_log, startupDelay, pollDelay, baseDirectory);
|
|
||||||
|
super(s_log, // Injects it's own logger
|
||||||
|
startupDelay, pollDelay, baseDirectory); // to the parent class methods!
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -16,16 +16,12 @@
|
||||||
package com.arsdigita.themedirector.util;
|
package com.arsdigita.themedirector.util;
|
||||||
|
|
||||||
import com.arsdigita.themedirector.Theme;
|
import com.arsdigita.themedirector.Theme;
|
||||||
import com.arsdigita.themedirector.ThemeDirector;
|
|
||||||
import com.arsdigita.themedirector.ThemeCollection;
|
import com.arsdigita.themedirector.ThemeCollection;
|
||||||
import com.arsdigita.themedirector.ThemeDirectorConstants;
|
import com.arsdigita.themedirector.ThemeDirectorConstants;
|
||||||
import com.arsdigita.themedirector.ThemeFileCollection;
|
import com.arsdigita.themedirector.ThemeFileCollection;
|
||||||
import com.arsdigita.persistence.SessionManager;
|
import com.arsdigita.persistence.SessionManager;
|
||||||
import com.arsdigita.persistence.TransactionContext;
|
import com.arsdigita.persistence.TransactionContext;
|
||||||
import com.arsdigita.themedirector.dispatcher.InternalThemePrefixerServlet;
|
import com.arsdigita.themedirector.dispatcher.InternalThemePrefixerServlet;
|
||||||
import com.arsdigita.web.Application;
|
|
||||||
import com.arsdigita.web.ApplicationCollection;
|
|
||||||
import com.arsdigita.web.Web;
|
|
||||||
|
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.io.FileOutputStream;
|
import java.io.FileOutputStream;
|
||||||
|
|
@ -38,8 +34,9 @@ import org.apache.log4j.Logger;
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Class for polling the database to look for new/updated files in
|
* Class providing client classes (as FileManager for development and published
|
||||||
* the ThemeFile table.
|
* themes) with base methods for polling the database to look for new/updated
|
||||||
|
* files in the ThemeFile table.
|
||||||
*
|
*
|
||||||
* For "published" files, It goes through each Theme and looks at the
|
* For "published" files, It goes through each Theme and looks at the
|
||||||
* last time it was published. If the last time published > last
|
* last time it was published. If the last time published > last
|
||||||
|
|
@ -52,15 +49,14 @@ import org.apache.log4j.Logger;
|
||||||
* then it writes out the new file. If the timestamp on the file system
|
* then it writes out the new file. If the timestamp on the file system
|
||||||
* is newer, it ignores the file.
|
* is newer, it ignores the file.
|
||||||
*
|
*
|
||||||
*
|
|
||||||
* @author <a href="mailto:randyg@redhat.com">Randy Graebner</a>
|
* @author <a href="mailto:randyg@redhat.com">Randy Graebner</a>
|
||||||
*
|
|
||||||
* @version $Revision: #2 $ $DateTime: 2004/01/30 17:24:49 $
|
* @version $Revision: #2 $ $DateTime: 2004/01/30 17:24:49 $
|
||||||
*/
|
*/
|
||||||
public abstract class ThemeFileManager extends Thread
|
public abstract class ThemeFileManager extends Thread
|
||||||
implements ThemeDirectorConstants {
|
implements ThemeDirectorConstants {
|
||||||
|
|
||||||
/** Internal logger instance to faciliate debugging */
|
/** Internal logger instance to faciliate debugging. Carries over the
|
||||||
|
* logger instance from the client child which actually does the work. */
|
||||||
private final Logger m_log;
|
private final Logger m_log;
|
||||||
|
|
||||||
// The code in this class borrows heavily from
|
// The code in this class borrows heavily from
|
||||||
|
|
@ -89,8 +85,10 @@ public abstract class ThemeFileManager extends Thread
|
||||||
* @param pollDelay
|
* @param pollDelay
|
||||||
* @param baseDirectory
|
* @param baseDirectory
|
||||||
*/
|
*/
|
||||||
protected ThemeFileManager(Logger log, int startupDelay, int pollDelay,
|
protected ThemeFileManager(Logger log,
|
||||||
String baseDirectory) {
|
int startupDelay,
|
||||||
|
int pollDelay,
|
||||||
|
String baseDirectory) {
|
||||||
m_log = log;
|
m_log = log;
|
||||||
m_startupDelay = startupDelay;
|
m_startupDelay = startupDelay;
|
||||||
m_pollDelay = pollDelay;
|
m_pollDelay = pollDelay;
|
||||||
|
|
@ -122,6 +120,7 @@ public abstract class ThemeFileManager extends Thread
|
||||||
* Watch file for entries to process. The main routine that starts
|
* Watch file for entries to process. The main routine that starts
|
||||||
* file processing.
|
* file processing.
|
||||||
*/
|
*/
|
||||||
|
@Override
|
||||||
public void run() {
|
public void run() {
|
||||||
m_log.info("Start polling file in " + m_startupDelay + "s.");
|
m_log.info("Start polling file in " + m_startupDelay + "s.");
|
||||||
if (m_lastRunDate == null) {
|
if (m_lastRunDate == null) {
|
||||||
|
|
@ -129,7 +128,7 @@ public abstract class ThemeFileManager extends Thread
|
||||||
sleepSeconds(m_startupDelay);
|
sleepSeconds(m_startupDelay);
|
||||||
}
|
}
|
||||||
m_log.info("Polling file every " + m_pollDelay + "s.");
|
m_log.info("Polling file every " + m_pollDelay + "s.");
|
||||||
while ( (sleepSeconds(m_pollDelay) || m_ignoreInterrupt)
|
while ( (sleepSeconds(m_pollDelay) || m_ignoreInterrupt)
|
||||||
&& m_keepWatchingFiles ) {
|
&& m_keepWatchingFiles ) {
|
||||||
// Get the last run date before we do anything,
|
// Get the last run date before we do anything,
|
||||||
// so we can be sure that we do not miss any themes
|
// so we can be sure that we do not miss any themes
|
||||||
|
|
@ -186,6 +185,8 @@ public abstract class ThemeFileManager extends Thread
|
||||||
* This allows an outside piece of code to force an automatic update
|
* This allows an outside piece of code to force an automatic update
|
||||||
* on a single theme instead of making it wait for the thread to wake
|
* on a single theme instead of making it wait for the thread to wake
|
||||||
* up.
|
* up.
|
||||||
|
*
|
||||||
|
* @param theme
|
||||||
*/
|
*/
|
||||||
public void updateThemeNow(Theme theme) {
|
public void updateThemeNow(Theme theme) {
|
||||||
updateTheme(theme);
|
updateTheme(theme);
|
||||||
|
|
@ -194,13 +195,15 @@ public abstract class ThemeFileManager extends Thread
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This returns the base directory to use when writing out files
|
* This returns the base directory to use when writing out files.
|
||||||
* THIS IS A HACK BECAUSE IT REQUIRES A SERVER TO BE RUNNING
|
* THIS IS A HACK BECAUSE IT REQUIRES A SERVER TO BE RUNNING.
|
||||||
|
*
|
||||||
|
* @return
|
||||||
*/
|
*/
|
||||||
protected String getBaseDirectory() {
|
protected String getBaseDirectory() {
|
||||||
|
|
||||||
if (m_baseDirectory == null) {
|
if (m_baseDirectory == null) {
|
||||||
// Because the constructor sets the base deirectory this should
|
// Because the constructor sets the base directory this should
|
||||||
// never happen, but just in case ....
|
// never happen, but just in case ....
|
||||||
// ThemeDirector may execute in a different web application context
|
// ThemeDirector may execute in a different web application context
|
||||||
// as core oder CMS. To determine the actual context we may ask
|
// as core oder CMS. To determine the actual context we may ask
|
||||||
|
|
@ -231,13 +234,18 @@ public abstract class ThemeFileManager extends Thread
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* this typically returns something like "getBaseDirectory() + PUB_DIR"
|
* This typically returns something like "getBaseDirectory() + PUB_DIR".
|
||||||
|
*
|
||||||
|
* @return
|
||||||
*/
|
*/
|
||||||
protected abstract String getManagerSpecificDirectory();
|
protected abstract String getManagerSpecificDirectory();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This allows subclasses to filter the collection as appropriate
|
* This allows subclasses to filter the collection as appropriate.
|
||||||
* (e.g. only return "live" files or only "draft" files).
|
* (e.g. only return "live" files or only "draft" files).
|
||||||
|
*
|
||||||
|
* @param theme
|
||||||
|
* @return
|
||||||
*/
|
*/
|
||||||
protected abstract ThemeFileCollection getThemeFilesCollection(Theme theme);
|
protected abstract ThemeFileCollection getThemeFilesCollection(Theme theme);
|
||||||
|
|
||||||
|
|
@ -268,9 +276,11 @@ public abstract class ThemeFileManager extends Thread
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This looks at all of the files in the db for the passed in theme
|
* This looks at all of the files in the db for the passed in theme
|
||||||
* and makes sure that this servers file system has all of the
|
* and makes sure that this servers file system has all of the
|
||||||
* updated files.
|
* updated files.
|
||||||
|
*
|
||||||
|
* @param theme
|
||||||
*/
|
*/
|
||||||
protected void updateTheme(Theme theme) {
|
protected void updateTheme(Theme theme) {
|
||||||
String stub = getManagerSpecificDirectory();
|
String stub = getManagerSpecificDirectory();
|
||||||
|
|
@ -329,7 +339,10 @@ public abstract class ThemeFileManager extends Thread
|
||||||
|
|
||||||
|
|
||||||
/***
|
/***
|
||||||
* Sleep for n seconds
|
* Sleep for n seconds.
|
||||||
|
*
|
||||||
|
* @param n
|
||||||
|
* @return
|
||||||
***/
|
***/
|
||||||
protected boolean sleepSeconds(long n) {
|
protected boolean sleepSeconds(long n) {
|
||||||
try {
|
try {
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue