001/* 002 * This library is part of OpenCms - 003 * the Open Source Content Management System 004 * 005 * Copyright (c) Alkacon Software GmbH & Co. KG (http://www.alkacon.com) 006 * 007 * This library is free software; you can redistribute it and/or 008 * modify it under the terms of the GNU Lesser General Public 009 * License as published by the Free Software Foundation; either 010 * version 2.1 of the License, or (at your option) any later version. 011 * 012 * This library is distributed in the hope that it will be useful, 013 * but WITHOUT ANY WARRANTY; without even the implied warranty of 014 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 015 * Lesser General Public License for more details. 016 * 017 * For further information about Alkacon Software GmbH & Co. KG, please see the 018 * company website: http://www.alkacon.com 019 * 020 * For further information about OpenCms, please see the 021 * project website: http://www.opencms.org 022 * 023 * You should have received a copy of the GNU Lesser General Public 024 * License along with this library; if not, write to the Free Software 025 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 026 */ 027 028package org.opencms.file.history; 029 030import org.opencms.file.CmsObject; 031import org.opencms.file.CmsResource; 032import org.opencms.file.CmsResourceFilter; 033import org.opencms.file.Messages; 034import org.opencms.main.CmsException; 035import org.opencms.main.CmsLog; 036import org.opencms.main.CmsResourceInitException; 037import org.opencms.main.I_CmsResourceInit; 038import org.opencms.util.CmsRequestUtil; 039 040import java.util.Map; 041 042import javax.servlet.ServletRequest; 043import javax.servlet.http.HttpServletRequest; 044import javax.servlet.http.HttpServletResponse; 045 046import org.apache.commons.logging.Log; 047 048/** 049 * Resource init handler that loads historical versions of resources.<p> 050 * 051 * @since 6.9.1 052 */ 053public class CmsHistoryResourceHandler implements I_CmsResourceInit { 054 055 /** Constant for the historical version request attribute name. */ 056 public static final String ATTRIBUTE_NAME = "org.opencms.file.history.CmsHistoryResourceHandler"; 057 058 /** The historical version handler path. */ 059 public static final String HISTORY_HANDLER = "/system/config/showversion"; 060 061 /** Request parameter name for the version number. */ 062 public static final String PARAM_VERSION = "version"; 063 064 /** Constant for the offline project version. */ 065 public static final int PROJECT_OFFLINE_VERSION = Integer.MAX_VALUE; 066 067 /** The static log object for this class. */ 068 private static final Log LOG = CmsLog.getLog(CmsHistoryResourceHandler.class); 069 070 /** 071 * Returns the historical version of a resource, 072 * if the given request is displaying a history version.<p> 073 * 074 * @param req the request to check 075 * 076 * @return the historical resource if the given request is displaying an historical version 077 */ 078 public static I_CmsHistoryResource getHistoryResource(ServletRequest req) { 079 080 return (I_CmsHistoryResource)req.getAttribute(ATTRIBUTE_NAME); 081 } 082 083 /** 084 * Appends the <code>version</code> parameter to the URI if needed.<p> 085 * 086 * @param uri the resource URI 087 * @param req the current request 088 * 089 * @return the same URI, with additional parameters in case of a historical request 090 */ 091 public static String getHistoryResourceURI(String uri, ServletRequest req) { 092 093 String histUri = uri; 094 if (CmsHistoryResourceHandler.isHistoryRequest(req)) { 095 String version = req.getParameter(CmsHistoryResourceHandler.PARAM_VERSION); 096 histUri = CmsRequestUtil.appendParameter(uri, CmsHistoryResourceHandler.PARAM_VERSION, version); 097 } 098 return histUri; 099 } 100 101 /** 102 * Returns the correct resource for the given URI, taken into account historical versions 103 * marked by the <code>version</code> parameter.<p> 104 * 105 * @param cms the current CMS context 106 * @param resourceUri the resource URI 107 * 108 * @return the resource, which can be an instance of {@link org.opencms.file.history.I_CmsHistoryResource} 109 * 110 * @throws CmsException if something goes wrong 111 */ 112 public static CmsResource getResourceWithHistory(CmsObject cms, String resourceUri) throws CmsException { 113 114 CmsResource resource = null; 115 116 if (resourceUri.contains(CmsRequestUtil.URL_DELIMITER)) { 117 int pos = resourceUri.indexOf(CmsRequestUtil.URL_DELIMITER); 118 Map<String, String[]> params = CmsRequestUtil.createParameterMap(resourceUri.substring(pos)); 119 if (params.containsKey(CmsHistoryResourceHandler.PARAM_VERSION)) { 120 int version = Integer.parseInt(params.get(CmsHistoryResourceHandler.PARAM_VERSION)[0]); 121 String sitemapPath = resourceUri.substring(0, pos); 122 resource = cms.readResource(sitemapPath); 123 resource = (CmsResource)cms.readResource(resource.getStructureId(), version); 124 } 125 } 126 if (resource == null) { 127 resource = cms.readResource(resourceUri); 128 } 129 return resource; 130 } 131 132 /** 133 * Returns <code>true</code> if the given request is displaying an historical version.<p> 134 * 135 * @param req the request to check 136 * 137 * @return <code>true</code> if the given request is displaying a historical version 138 */ 139 public static boolean isHistoryRequest(ServletRequest req) { 140 141 return (null != req.getAttribute(ATTRIBUTE_NAME)); 142 } 143 144 /** 145 * @see org.opencms.main.I_CmsResourceInit#initResource(org.opencms.file.CmsResource, org.opencms.file.CmsObject, javax.servlet.http.HttpServletRequest, javax.servlet.http.HttpServletResponse) 146 */ 147 public CmsResource initResource( 148 CmsResource resource, 149 CmsObject cms, 150 HttpServletRequest req, 151 HttpServletResponse res) 152 throws CmsResourceInitException { 153 154 // we only have to check for history resources if the handler was called 155 // during a real request and NOT during a dummy-request while doing 156 // a static export 157 if (req != null) { 158 String uri = cms.getRequestContext().getUri(); 159 // check if the resource starts with the HISTORY_HANDLER 160 if (uri.startsWith(HISTORY_HANDLER)) { 161 String version = req.getParameter(PARAM_VERSION); 162 163 // only do something if the resource was not found and there was a "versionid" parameter included 164 if ((resource == null) && (version != null)) { 165 166 // test if the current user is allowed to read historical versions of resources 167 // this can be done by trying to read the history handler resource 168 if (cms.existsResource(HISTORY_HANDLER)) { 169 String storedSiteRoot = cms.getRequestContext().getSiteRoot(); 170 try { 171 // we now must switch to the root site to read the history resource 172 cms.getRequestContext().setSiteRoot("/"); 173 174 // extract the "real" resourcename 175 uri = uri.substring(HISTORY_HANDLER.length(), uri.length()); 176 int id = Integer.valueOf(version).intValue(); 177 if (id == CmsHistoryResourceHandler.PROJECT_OFFLINE_VERSION) { 178 resource = new CmsHistoryFile(cms.readFile(uri, CmsResourceFilter.IGNORE_EXPIRATION)); 179 } else { 180 // get the current resource 181 CmsResource currRes = cms.readResource(uri, CmsResourceFilter.IGNORE_EXPIRATION); 182 // get the historical version of the resource 183 CmsHistoryFile hisRes = (CmsHistoryFile)cms.readResource( 184 cms.readResource(uri, CmsResourceFilter.IGNORE_EXPIRATION).getStructureId(), 185 id); 186 187 // the resource root path is not changed after the resource is moved or renamed 188 // change the resource root path to current root path, so e.g. properties can be read if necessary 189 if (!currRes.getRootPath().equals(hisRes.getRootPath())) { 190 191 resource = new CmsHistoryFile( 192 hisRes.getPublishTag(), 193 hisRes.getStructureId(), 194 hisRes.getResourceId(), 195 currRes.getRootPath(), 196 hisRes.getTypeId(), 197 hisRes.getFlags(), 198 hisRes.getProjectLastModified(), 199 hisRes.getState(), 200 hisRes.getDateCreated(), 201 hisRes.getUserCreated(), 202 hisRes.getDateLastModified(), 203 hisRes.getUserLastModified(), 204 hisRes.getDateReleased(), 205 hisRes.getDateExpired(), 206 hisRes.getLength(), 207 hisRes.getDateContent(), 208 hisRes.getVersion(), 209 hisRes.getParentId(), 210 null, 211 hisRes.getResourceVersion(), 212 hisRes.getStructureVersion()); 213 } else { 214 resource = hisRes; 215 } 216 if (resource instanceof CmsHistoryFile) { 217 try { 218 resource = cms.readFile(resource); 219 } catch (Exception e) { 220 LOG.info(e.getLocalizedMessage(), e); 221 } 222 } 223 } 224 if (res != null) { 225 // store a request attribute to indicate that this is in fact a historical version 226 req.setAttribute(ATTRIBUTE_NAME, resource); 227 } 228 } catch (CmsException e) { 229 if (LOG.isErrorEnabled()) { 230 LOG.error(Messages.get().getBundle().key(Messages.ERR_HISTORYRESOURCE_2, uri, version)); 231 } 232 throw new CmsResourceInitException( 233 Messages.get().container(Messages.ERR_SHOWVERSION_2, uri, version), 234 e); 235 } finally { 236 // restore the siteroot and modify the uri to the one of the correct resource 237 cms.getRequestContext().setSiteRoot(storedSiteRoot); 238 if (resource != null) { 239 // resource may be null in case of an error 240 cms.getRequestContext().setUri(cms.getSitePath(resource)); 241 } 242 } 243 } 244 } 245 } 246 } 247 return resource; 248 } 249}