Java tutorial
/* * Copyright 2004-2009 Luciano Vernaschi * * This file is part of MeshCMS. * * MeshCMS is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * MeshCMS 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 General Public License * along with MeshCMS. If not, see <http://www.gnu.org/licenses/>. */ package org.meshcms.core; import com.thoughtworks.xstream.XStream; import com.thoughtworks.xstream.io.StreamException; import com.thoughtworks.xstream.io.xml.DomDriver; import java.io.BufferedInputStream; import java.io.BufferedOutputStream; import java.io.BufferedWriter; import java.io.File; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.io.OutputStream; import java.io.OutputStreamWriter; import java.io.Writer; import java.util.Date; import javax.servlet.ServletContext; import javax.servlet.ServletRequest; import javax.servlet.http.HttpServletRequest; import org.apache.commons.fileupload.FileItem; import org.meshcms.util.DirectoryRemover; import org.meshcms.util.Path; import org.meshcms.util.Utils; import org.meshcms.util.XStreamPathConverter; import org.meshcms.util.ZipArchiver; public class WebSite { public static final String APP_NAME = "MeshCMS"; public static final String VERSION_ID = "3.6"; /** * A prefix to be used for every backup file. */ public static final String BACKUP_PREFIX = "_bak_"; /** * A prefix to be used for every backup dir. */ public static final String BACKUP_DIR_PREFIX = "_dirbak_"; /** * A prefix to be used for every temporary file created in the repository. */ public static final String TEMP_PREFIX = "_tmp_"; /** * Name of the default admin theme folder. */ public static final String ADMIN_THEME = "theme"; public static final String CMS_ID_FILE = "meshcms_id"; public static final String ADMIN_ID_FILE = "meshcms_admin_id"; protected ServletContext sc; protected String[] welcomeFiles; protected File rootFile; protected long lastAdminThemeBlock; protected long statsZero; protected int statsLength; protected Configuration configuration; protected SiteInfo siteInfo; protected SiteMap siteMap; protected Path rootPath; protected Path cmsPath; protected Path adminPath; protected Path adminThemePath; protected Path adminModulesPath; protected Path adminThemesPath; protected Path adminScriptsPath; protected Path privatePath; protected Path usersPath; protected Path virtualSitesPath; protected Path repositoryPath; protected Path customModulesPath; protected Path generatedFilesPath; protected Path moduleDataPath; protected Path customThemesPath; protected Path configFilePath; protected Path propertiesFilePath; protected Path sitesFilePath; protected WebSite() { } protected static WebSite create(ServletContext sc, String[] welcomeFiles, File rootFile, Path rootPath, Path cmsPath) { WebSite webSite = new WebSite(); webSite.init(sc, welcomeFiles, rootFile, rootPath, cmsPath); return webSite; } protected void init(ServletContext sc, String[] welcomeFiles, File rootFile, Path rootPath, Path cmsPath) { this.sc = sc; this.welcomeFiles = welcomeFiles; this.rootFile = rootFile; this.rootPath = rootPath; this.cmsPath = cmsPath; // PLEASE NOTE: the initialization order is important. if (cmsPath != null) { adminPath = cmsPath.add("admin"); adminThemePath = adminPath.add(ADMIN_THEME); adminModulesPath = adminPath.add("modules"); adminThemesPath = adminPath.add("themes"); adminScriptsPath = adminPath.add("scripts"); privatePath = cmsPath.add("private"); createDir(usersPath = privatePath.add("users")); virtualSitesPath = cmsPath.add("sites"); createDir(repositoryPath = privatePath.add("repository")); createDir(customModulesPath = cmsPath.add("modules")); createDir(generatedFilesPath = cmsPath.add("generated")); createDir(moduleDataPath = cmsPath.add("moduledata")); createDir(customThemesPath = cmsPath.add("themes")); configFilePath = privatePath.add("configuration.xml"); propertiesFilePath = privatePath.add("siteinfo.xml"); sitesFilePath = privatePath.add("sites.xml"); readConfig(); statsLength = configuration.getStatsLength(); updateSiteMap(true); } } public void readConfig() { configuration = Configuration.load(this); siteInfo = SiteInfo.load(this); } protected ServletContext getServletContext() { return sc; } /** * Creates another instance of <code>SiteMap</code>. If <code>force</code> * is true, a new site map is always created and the method * returns after the new site map is completed. If it is false, a new site map * is created only if the current one is too old. In this case, the site map * is created asynchronously and the method returns immediately. The * repository will be cleaned too. * * @param force it to force the SiteMap creation. */ public void updateSiteMap(boolean force) { if (force) { new SiteMap(this).process(); } else if (System.currentTimeMillis() - siteMap.getLastModified() > configuration .getUpdateIntervalMillis()) { new SiteMap(this).start(); new DirectoryCleaner(getFile(repositoryPath), configuration.getBackupLifeMillis()).start(); new DirectoryCleaner(getFile(generatedFilesPath), configuration.getBackupLifeMillis()).start(); new DirectoryCleaner(getFile(moduleDataPath)).start(); } } void setSiteMap(SiteMap siteMap) { this.siteMap = siteMap; } /** * Returns the instance of the <code>SiteMap</code> that is currently manage * the site map. Since this object can be replaced with a new one at any * moment, a class that wants to use it should store it in a local variable * and use it for all the operation/method. * * @return the current instance of SiteMap */ public SiteMap getSiteMap() { return siteMap; } /** * Returns the current configuration of the web application. */ public Configuration getConfiguration() { return configuration; } /** * Sets the <code>SiteInfo</code> object related to this website. * * @param siteInfo the site info for this website */ void setSiteInfo(SiteInfo siteInfo) { this.siteInfo = siteInfo; } /** * Returns the instance of the <code>SiteInfo</code> class that is managing * the site information. * * @see SiteInfo */ public SiteInfo getSiteInfo() { return siteInfo; } /** * Returns the index of the current day in the array of stats included in any * PageInfo instance. */ public int getStatsIndex() { long now = System.currentTimeMillis(); long days = (now - statsZero) / Configuration.LENGTH_OF_DAY; if (days >= statsLength) { statsZero = now; return 0; } else { return (int) days; } } /** * Returns the length of stats (hit counts) measured in days. */ public int getStatsLength() { return statsLength; } /** * Creates a new directory. * * @param user the user that requests the creation of the directory * @param dirPath the path of the new directory * * @return true if the directory has been created or already * existed, false otherwise */ public boolean createDirectory(UserInfo user, Path dirPath) { File newDir = getFile(dirPath); if (newDir.isDirectory()) { return true; } return user != null && user.canWrite(this, dirPath) && newDir.mkdir(); } /** * Creates a new file. If the extension of the file denotes a web page, the * basic template is copied into the file, otherwise an empty file is created. * * @param user the user that requests the file creation * @param filePath the path of the new file * * @return true if the new file has been created or already existed, * false otherwise */ public boolean createFile(UserInfo user, Path filePath) { if (user == null || !user.canWrite(this, filePath)) { return false; } File newFile = getFile(filePath); if (newFile.exists()) { return !newFile.isDirectory(); } try { if (FileTypes.isPage(filePath.getLastElement())) { if (newFile.exists()) { return false; } return saveToFile(user, getHTMLTemplate(null), filePath); } else { return newFile.createNewFile(); } } catch (IOException ex) { sc.log("Can't create file " + newFile, ex); } return false; } /** * Copies a file to another file in the same directory. An existing file won't * be overwritten. * * @param user the user that requests the operation * @param filePath the path of the old file * @param newName the name of the new file * * @return true if the new file has been copied, false otherwise */ public boolean copyFile(UserInfo user, Path filePath, String newName) { return copyFile(user, filePath, filePath.getParent().add(newName)); } /** * Copies a file (or directory) to another file (or directory). * Existing files won't be overwritten. * * @param user the user that requests the operation * @param oldPath the location of the existing file * @param newPath the location of the new copy of the file * * @return true if the new file has been copied, false otherwise */ public boolean copyFile(UserInfo user, Path oldPath, Path newPath) { File oldFile = getFile(oldPath); if (!oldFile.exists()) { return false; } File newFile = getFile(newPath); if (user == null || !user.canWrite(this, newPath)) { return false; } if (oldFile.isDirectory()) { return Utils.copyDirectory(oldFile, newFile, false, false, false); } else { try { return Utils.copyFile(oldFile, newFile, false, false); } catch (IOException ex) { sc.log("Can't copy file " + oldFile + " to file " + newFile, ex); } } return false; } /** * Renames a file. * * @param user the user that requests the operation * @param filePath the path of the file * @param newName the name of the new file * * @return true if the new file has been renamed, false otherwise */ public boolean rename(UserInfo user, Path filePath, String newName) { return move(user, filePath, filePath.getParent().add(newName)); } /** * Moves (or renames) a file. * * @param user the user that requests the operation * @param oldPath the current location of the file * @param newPath the new location of the file * * @return true if the new file has been moved, false otherwise */ public boolean move(UserInfo user, Path oldPath, Path newPath) { File oldFile = getFile(oldPath); if (!oldFile.exists()) { return false; } if (newPath.isContainedIn(oldPath)) { return false; } if (user == null || !(user.canWrite(this, oldPath) && user.canWrite(this, newPath))) { return false; } File newFile = getFile(newPath); if (Utils.forceRenameTo(oldFile, newFile, false)) { return true; } else { sc.log("Can't move file " + oldFile + " to file " + newFile); } return false; } /** * Deletes a file or directory. * * @param user the user that requests the operation * @param filePath the path of the file * @param deleteNonEmptyDirectories if true, non-empty directories will be * deleted too * * @return true if the file has been deleted, false otherwise */ public boolean delete(UserInfo user, Path filePath, boolean deleteNonEmptyDirectories) { if (user == null || !user.canWrite(this, filePath)) { return false; } // avoid deletion of mandatory CMS items if (cmsPath.isContainedIn(filePath) || filePath.isChildOf(cmsPath) || filePath.isContainedIn(adminPath) || filePath.isChildOf(privatePath)) { return false; } File file = getFile(filePath); if (!file.exists()) { return false; } if (file.isDirectory()) { /* First try to delete a directory as if it were empty. If not, and if deletion of non-empty directories is allowed, backup and delete with backupDir */ return file.delete() || (deleteNonEmptyDirectories && backupDir(user, filePath)); } else { return backupFile(user, filePath); } } /** * Sets the last modified date of the file to the current time. * * @param user the user that requests the operation * @param filePath the path of the file * * @return true if the date has been changed, false otherwise */ public boolean touch(UserInfo user, Path filePath) { return setFileTime(user, filePath, System.currentTimeMillis()); } public boolean setFileTime(UserInfo user, Path filePath, long time) { if (user == null || !user.canWrite(this, filePath)) { return false; } File file = getFile(filePath); if (!file.exists()) { return false; } file.setLastModified(time); return true; } /** * Stores an object into a file. Supported objects are: * * <ul> * <li>byte arrays</li> * <li>input streams</li> * <li><code>org.apache.commons.fileupload.FileItem</code> * (uploaded files)</li> * <li>generic objects. The <code>toString()</code> method is used in this * cases. This is compatible with many kinds of objects: strings, * string buffers and so on.</li> * </ul> * * @param user the user that requests the operation * @param saveThis the object to be stored in the file * @param filePath the path of the file to be written. If the file exists, it * will be backed up and overwritten * * @return true if the operation has been completed successfully, * false otherwise */ public boolean saveToFile(UserInfo user, Object saveThis, Path filePath) { if (user == null || !user.canWrite(this, filePath)) { return false; } File file = getFile(filePath); File dir = file.getParentFile(); dir.mkdirs(); File tempFile = null; String fileName = file.getName(); int dot = fileName.lastIndexOf('.'); String fileExt = (dot == -1) ? ".bak" : fileName.substring(dot); if (file.exists()) { tempFile = getRepositoryFile(filePath, TEMP_PREFIX + System.currentTimeMillis() + fileExt); } File writeTo = tempFile == null ? file : tempFile; if (saveThis instanceof byte[]) { byte[] b = (byte[]) saveThis; FileOutputStream fos = null; try { fos = new FileOutputStream(writeTo); fos.write(b); } catch (IOException ex) { sc.log("Can't write byte array to file " + writeTo, ex); return false; } finally { if (fos != null) { try { fos.close(); } catch (IOException ex) { sc.log("Can't close file " + writeTo, ex); } } } } else if (saveThis instanceof InputStream) { try { InputStream is = (InputStream) saveThis; Utils.copyStream(is, new FileOutputStream(writeTo), true); is.close(); } catch (Exception ex) { sc.log("Can't write stream to file " + writeTo, ex); return false; } } else if (saveThis instanceof FileItem) { try { ((FileItem) saveThis).write(writeTo); } catch (Exception ex) { sc.log("Can't write uploaded file to file " + writeTo, ex); return false; } } else { Writer writer = null; try { writer = new BufferedWriter( new OutputStreamWriter(new FileOutputStream(writeTo), Utils.SYSTEM_CHARSET)); writer.write(saveThis.toString()); } catch (IOException ex) { sc.log("Can't write generic object to file " + writeTo, ex); return false; } finally { if (writer != null) { try { writer.close(); } catch (IOException ex) { sc.log("Can't close file " + writeTo, ex); } } } } if (tempFile != null) { if (!backupFile(user, filePath)) { return false; } if (!Utils.forceRenameTo(tempFile, file, true)) { sc.log("Can't rename temporary file " + tempFile + " to file " + file); return false; } } return true; } /** * Returns the correct file in the repository. For example, if you need to * create a temporary copy of /somedir/index.html, you could use * <code>filePath = /somedir/index.html</code> and * <code>fileName = tmp.html</code>. * * @param filePath the path where to search * @param fileName the file name * * @return the searched file */ public File getRepositoryFile(Path filePath, String fileName) { File repoDir = getFile(repositoryPath.add(filePath)); repoDir.mkdirs(); return new File(repoDir, fileName); } private boolean backupFile(UserInfo user, Path filePath) { /* permissions already checked in methods that call this one if (user == null || !user.canWrite(this, filePath)) { return false; } */ File file = getFile(filePath); String fileName = file.getName(); int dot = fileName.lastIndexOf('.'); String fileExt = (dot == -1) ? ".bak" : fileName.substring(dot); File bakFile = getRepositoryFile(filePath, BACKUP_PREFIX + user.getUsername() + "_" + WebUtils.numericDateFormatter.format(new Date()) + fileExt); if (Utils.forceRenameTo(file, bakFile, true)) { return true; } sc.log("Can't backup path " + filePath); return false; } private boolean backupDir(UserInfo user, Path dirPath) { /* permissions to be checked by the caller */ File dir = getFile(dirPath); File bakFile = getRepositoryFile(dirPath, BACKUP_DIR_PREFIX + user.getUsername() + "_" + WebUtils.numericDateFormatter.format(new Date()) + ".zip"); OutputStream os; try { os = new BufferedOutputStream(new FileOutputStream(bakFile)); } catch (FileNotFoundException ex) { sc.log("can't create stream on " + bakFile, ex); return false; } new ZipArchiver(dir, os).process(); try { os.close(); } catch (IOException ex) { sc.log("Can't close file " + bakFile, ex); return false; } DirectoryRemover dr = new DirectoryRemover(dir); dr.process(); return dr.getResult(); } public File createDir(Path path) { File dir = getFile(path); dir.mkdirs(); return dir.isDirectory() ? dir : null; } /** * Returns the file object for a given path in the web application. The file * is not checked for existance. * * @param path the path representation of the file * * @return the file object for this path, or null if it's not found */ public File getFile(Path path) { return (path == null || path.isRelative()) ? null : new File(rootFile, path.toString()); } public File getRootFile() { return rootFile; } /** * Returns the site root path. */ public Path getRootPath() { return rootPath; } /** * Returns the <code>Path</code> of a file in the context. * * @see #getFile */ public Path getPath(File file) { return new Path(Utils.getRelativePath(rootFile, file, "/")); } public Path getRequestedPath(HttpServletRequest request) { return new Path(request.getServletPath()); } public Path getServedPath(HttpServletRequest request) { return new Path(request.getServletPath()); } public Path getServedPath(Path requestedPath) { return requestedPath; } /** * Checks if the given path is a directory in the file system. * * @param path the Path to check * * @return true if the path is a directory. */ public boolean isDirectory(Path path) { return getFile(path).isDirectory(); } /** * Returns the directory that contains the given path. This is different from * {@link org.meshcms.util.Path#getParent}, since if the path is known to * be a directory in the web application, the path itself is returned. * * @param path the Path to check * * @return a directory(that contains the given path) as a Path object. */ public Path getDirectory(Path path) { // PageInfo pageInfo = getPageInfo(path); // return pageInfo.isDirectory() ? path : pageInfo.getPath().getParent(); if (path == null) { return null; } if (getFile(path).isDirectory()) { return path; } path = path.getParent(); if (!path.isRelative() && getFile(path).isDirectory()) { return path; } return null; } public String[] getLinkList(PageInfo[] pages, Path pagePath, String target, String style) { if (pages == null) { return null; } String[] links = new String[pages.length]; target = Utils.isNullOrEmpty(target) ? "" : " target=\"" + target + "\""; style = Utils.isNullOrEmpty(style) ? "" : " class=\"" + style + "\""; for (int i = 0; i < pages.length; i++) { if (pages[i] == null) { links[i] = "..."; } else { links[i] = "<a href=\"" + getLink(pages[i], pagePath) + "\"" + target + style + ">" + siteInfo.getPageTitle(pages[i]) + "</a>"; } } return links; } public String getAbsoluteLink(Path path) { return getFile(path).isDirectory() ? path.getAsLink() + '/' : path.getAsLink(); } public String getAbsoluteLink(PageInfo pageInfo) { return getAbsoluteLink(pageInfo.getPath()); } public Path getLink(Path path, Path pagePath) { Path link = path.getRelativeTo(getDirectory(pagePath)); if (link.getElementCount() == 0 && isDirectory(path)) { Path welcome = getSiteMap().getCurrentWelcome(path); if (welcome != null) { link = welcome.getRelativeTo(getDirectory(pagePath)); } } return link; } public Path getLink(PageInfo pageInfo, Path pagePath) { return getLink(pageInfo.getPath(), pagePath); } /** * Returns an array of menu titles for the given pages. * {@link SiteInfo} is used to get the titles. * * @param pages an array of pages to be processed * * @return the array of titles for the given pages. */ public String[] getTitles(PageInfo[] pages) { if (pages == null) { return null; } String[] titles = new String[pages.length]; for (int i = 0; i < pages.length; i++) { titles[i] = siteInfo.getPageTitle(pages[i]); } return titles; } /** * Determines if the given path is a system directory, or is contained in a * system directory. System directories are: * * <ul> * <li>the WEB-INF directory (/WEB-INF)</li> * <li>the META-INF directory (/META-INF)</li> * <li>the standard CGI-BIN directory (/cgi-bin)</li> * <li>the MeshCMS admin directory (if <code>checkAdmin</code> is true</li> * </ul> * * @param path the given path(directory) to be checked * * @return true if it is a system directory, false otherwise */ public boolean isSystem(Path path) { if (path == null || path.isRoot()) { return false; } if (path.isRelative()) { return true; } if (adminPath != null && (path.isContainedIn(adminPath) || path.isContainedIn(privatePath) || path.equals(cmsPath.add(CMS_ID_FILE)))) { return true; } String level1 = path.getElementAt(0).toLowerCase(); return level1.equals("web-inf") || level1.equals("meta-inf") || level1.equals("cgi-bin"); } /** * Returns true if the extension of the path is known to denote a type of * file that can be edited using the wysiwyg editor. */ public boolean isVisuallyEditable(Path path) { return Utils.searchString(configuration.getVisualExtensions(), Utils.getExtension(path, false), true) != -1; } /** * Returns the path of the module file with the given name. */ public Path getModulePath(String moduleName) { if (moduleName.endsWith(".jsp")) { // old module names were in the form module_name.jsp moduleName = moduleName.substring(0, moduleName.length() - 4); } return (Path) siteMap.getModulesMap().get(moduleName); } /** * Returns the path of the admin directory. */ public Path getAdminPath() { return adminPath; } public Path getCMSPath() { return cmsPath; } public boolean isVirtual() { return false; } /** * Returns the complete tag used by pages in the admin folder. This way those * pages can set to be themed according to the site preferences (i.e. using * a custom theme or the default admin theme). */ public String getAdminMetaThemeTag() { Path themePath = getThemePath(adminPath); return "<meta name=\"decorator\" content=\"/" + getServedPath(themePath) + "/" + SiteMap.THEME_DECORATOR + "\" />"; } public String getDummyMetaThemeTag() { return "<meta name=\"decorator\" content=\"/" + adminPath + "/theme/dummy.jsp\" />"; } /** * Returns a string containing a basic HTML page. * * @param pageTitle the content of the <title> tag (if null, the title * will be "New Page") * * @return a basic "empty" HTML page */ public String getHTMLTemplate(String pageTitle) throws IOException { String text = Utils.readFully(getFile(getAdminPath().add("template.html"))); if (pageTitle != null) { int idx = text.indexOf("New Page"); if (idx >= 0) { text = text.substring(0, idx) + pageTitle + text.substring(idx + 8); } } return text; } public boolean isInsideModules(Path path) { return path.isContainedIn(customModulesPath) || path.isContainedIn(adminModulesPath); } public boolean isInsideThemes(Path path) { return path.isContainedIn(customThemesPath) || path.isContainedIn(adminThemesPath); } /** * Logs a string by calling <code>ServletContext.log(s)</code> */ protected void log(String s) { sc.log(s); } /** * Logs an exception by calling * <code>ServletContext.log(message, throwable)</code> */ public void log(String message, Throwable throwable) { sc.log(message, throwable); } /** * Checks if the file name is one of the welcome files. * * @param fileName the file name to check * @return true if the given file name is known to be a welcome file name. */ public boolean isWelcomeFileName(String fileName) { return Utils.searchString(welcomeFiles, fileName, false) != -1; } /** * Returns the array of welcome file names. * * @return an array of welcome file names for the current web application. * Values are fetched from the web.xml file. */ public String[] getWelcomeFileNames() { return welcomeFiles; } /** * Returns the current welcome file path for the given folder. If there is no * welcome file in that folder, this method returns null. * * @param dirPath the folder where to search the welcome file * * @return the welcome file as a Path object of null if not found. */ public Path findCurrentWelcome(Path dirPath) { File dirFile = getFile(dirPath); if (dirFile.isDirectory()) { for (int i = 0; i < welcomeFiles.length; i++) { Path wPath = dirPath.add(welcomeFiles[i]); if (getFile(wPath).exists()) { return wPath; } } } return null; } public WebSite getWebSite(ServletRequest request) { return this; } public HttpServletRequest wrapRequest(ServletRequest request) { return (HttpServletRequest) request; } public String getTypeDescription() { return "single web site"; } public String toString() { return "Type: " + getTypeDescription() + "; path: /" + rootPath + "; CMS: " + (cmsPath == null ? "disabled" : "enabled on path /" + cmsPath); } public Object loadFromXML(Path path) { File file = getFile(path); if (file.exists()) { InputStream is = null; try { is = new BufferedInputStream(new FileInputStream(file)); XStream xStream = new XStream(new DomDriver()); XStreamPathConverter pConv = new XStreamPathConverter(); pConv.setPrependSlash(true); xStream.registerConverter(pConv); try { return xStream.fromXML(new InputStreamReader(is, "UTF-8")); } catch (StreamException ex) { return xStream.fromXML(is); } } catch (IOException ex) { ex.printStackTrace(); } finally { if (is != null) { try { is.close(); } catch (IOException ex) { sc.log("Can't close file " + file, ex); } } } } return null; } public boolean storeToXML(Object o, Path path) { File file = getFile(path); OutputStreamWriter osw = null; try { osw = new OutputStreamWriter(new BufferedOutputStream(new FileOutputStream(file)), "UTF-8"); XStream xStream = new XStream(new DomDriver()); XStreamPathConverter pConv = new XStreamPathConverter(); pConv.setPrependSlash(true); xStream.registerConverter(pConv); xStream.toXML(o, osw); return true; } catch (IOException ex) { ex.printStackTrace(); } finally { if (osw != null) { try { osw.close(); } catch (IOException ex) { sc.log("Can't close file " + file, ex); } } } return false; } /** * Returns the path of the theme to be applied to the given path. This depends * on the stored values and on the option to use the default theme for the * admin pages. This method returns null if no theme is found. * * @param pagePath the path of the page who's theme is searched. * * @return the theme of this page as a Path object */ public Path getThemePath(Path pagePath) { Path themePath = siteInfo.getThemePath(pagePath); if (pagePath.isContainedIn(adminPath)) { if (configuration.isUseAdminTheme() || themePath == null || System.currentTimeMillis() - lastAdminThemeBlock < 300000L || // 5 minutes !getFile(themePath.add(SiteMap.THEME_DECORATOR)).exists()) { themePath = adminThemePath; } } return themePath; } public Path getAdminThemePath() { return adminThemePath; } public Path getAdminModulesPath() { return adminModulesPath; } public Path getModuleDataPath() { return moduleDataPath; } public Path getAdminThemesPath() { return adminThemesPath; } public Path getAdminScriptsPath() { return adminScriptsPath; } public Path getPrivatePath() { return privatePath; } public Path getUsersPath() { return usersPath; } public Path getVirtualSitesPath() { return virtualSitesPath; } public Path getRepositoryPath() { return repositoryPath; } public Path getCustomModulesPath() { return customModulesPath; } public Path getGeneratedFilesPath() { return generatedFilesPath; } public Path getCustomThemesPath() { return customThemesPath; } public Path getConfigFilePath() { return configFilePath; } public Path getPropertiesFilePath() { return propertiesFilePath; } public Path getSitesFilePath() { return sitesFilePath; } public long getLastAdminThemeBlock() { return lastAdminThemeBlock; } public void setLastAdminThemeBlock(long lastAdminThemeBlock) { this.lastAdminThemeBlock = lastAdminThemeBlock; } }