001/* 002 * This library is part of OpenCms - 003 * the Open Source Content Management System 004 * 005 * Copyright (c) Alkacon Software GmbH & Co. KG (https://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: https://www.alkacon.com 019 * 020 * For further information about OpenCms, please see the 021 * project website: https://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.workplace.commons; 029 030import org.opencms.file.CmsFile; 031import org.opencms.file.CmsObject; 032import org.opencms.file.CmsResource; 033import org.opencms.file.CmsResourceFilter; 034import org.opencms.file.CmsVfsResourceNotFoundException; 035import org.opencms.file.history.I_CmsHistoryResource; 036import org.opencms.flex.CmsFlexController; 037import org.opencms.i18n.CmsEncoder; 038import org.opencms.jsp.CmsJspActionElement; 039import org.opencms.main.CmsContextInfo; 040import org.opencms.main.CmsException; 041import org.opencms.main.CmsLog; 042import org.opencms.main.OpenCms; 043import org.opencms.util.CmsRequestUtil; 044import org.opencms.util.CmsStringUtil; 045import org.opencms.workplace.CmsDialog; 046import org.opencms.workplace.CmsWorkplaceSettings; 047 048import java.io.IOException; 049 050import org.apache.commons.logging.Log; 051 052import jakarta.servlet.http.HttpServletRequest; 053import jakarta.servlet.http.HttpServletResponse; 054import jakarta.servlet.jsp.PageContext; 055 056/** 057 * Shows a preview of the selected resource in the Explorer view.<p> 058 * 059 * This is required to get correct previews of statically exported pages 060 * in the Online project.<p> 061 * 062 * The following file uses this class: 063 * <ul> 064 * <li>/commons/displayresource.jsp 065 * </ul> 066 * <p> 067 * 068 * @since 6.0.0 069 */ 070public class CmsDisplayResource extends CmsDialog { 071 072 /** Request parameter name for versionid. */ 073 public static final String PARAM_VERSION = "version"; 074 075 /** The log object for this class. */ 076 private static final Log LOG = CmsLog.getLog(CmsDisplayResource.class); 077 078 /** The version number parameter. */ 079 private String m_paramVersion; 080 081 /** 082 * Public constructor with JSP action element.<p> 083 * 084 * @param jsp an initialized JSP action element 085 */ 086 public CmsDisplayResource(CmsJspActionElement jsp) { 087 088 super(jsp); 089 } 090 091 /** 092 * Public constructor with JSP variables.<p> 093 * 094 * @param context the JSP page context 095 * @param req the JSP request 096 * @param res the JSP response 097 */ 098 public CmsDisplayResource(PageContext context, HttpServletRequest req, HttpServletResponse res) { 099 100 this(new CmsJspActionElement(context, req, res)); 101 } 102 103 /** 104 * Returns the content of an historical resource.<p> 105 * 106 * @param cms a CmsObject 107 * @param resource the name of the historical resource 108 * @param version the version number of the historical resource 109 * 110 * @return the content of an historical resource 111 */ 112 protected static byte[] getHistoricalResourceContent(CmsObject cms, String resource, String version) { 113 114 if (CmsStringUtil.isNotEmpty(resource) && CmsStringUtil.isNotEmpty(version)) { 115 // try to load the historical resource 116 I_CmsHistoryResource res = null; 117 String storedSiteRoot = cms.getRequestContext().getSiteRoot(); 118 try { 119 cms.getRequestContext().setSiteRoot("/"); 120 res = cms.readResource( 121 cms.readResource(resource, CmsResourceFilter.ALL).getStructureId(), 122 Integer.parseInt(version)); 123 } catch (CmsException e) { 124 // can usually be ignored 125 if (LOG.isInfoEnabled()) { 126 LOG.info(e.getLocalizedMessage()); 127 } 128 return "".getBytes(); 129 } finally { 130 cms.getRequestContext().setSiteRoot(storedSiteRoot); 131 } 132 if (res.isFile()) { 133 byte[] historyResourceContent = ((CmsFile)res).getContents(); 134 if ((historyResourceContent == null) || (historyResourceContent.length == 0)) { 135 try { 136 CmsFile file = cms.readFile((CmsResource)res); 137 historyResourceContent = file.getContents(); 138 } catch (CmsException e) { 139 // ignore 140 } 141 } 142 historyResourceContent = CmsEncoder.changeEncoding( 143 historyResourceContent, 144 OpenCms.getSystemInfo().getDefaultEncoding(), 145 cms.getRequestContext().getEncoding()); 146 return historyResourceContent; 147 } 148 } 149 return "".getBytes(); 150 } 151 152 /** 153 * Redirects to the specified file or shows an historical resource.<p> 154 * 155 * @throws Exception if redirection fails 156 */ 157 public void actionShow() throws Exception { 158 159 // try to load the historical resource 160 if (CmsStringUtil.isNotEmpty(getParamVersion())) { 161 showHistoricVersion(); 162 } else { 163 String resourceStr = getParamResource(); 164 // trying to read the resource 165 CmsResource resource = readResource(resourceStr); 166 if (isDeleted(resource)) { 167 // resource has been deleted in offline project 168 throw new CmsVfsResourceNotFoundException( 169 Messages.get().container( 170 Messages.ERR_RESOURCE_DELETED_2, 171 resourceStr, 172 getCms().getRequestContext().getCurrentProject().getName())); 173 } 174 175 // check for release / expiration time window 176 autoTimeWarp(resource); 177 178 // code for display resource after all tests for displayability (exists, not deleted) 179 // if (CmsResourceTypeXmlContainerPage.isContainerPage(resource)) { 180 // // if we have a container page look for the first sitemap entry 181 // // and use that to display the container page 182 // List<CmsInternalSitemapEntry> entries = OpenCms.getSitemapManager().getEntriesForStructureId( 183 // getJsp().getCmsObject(), 184 // resource.getStructureId()); 185 // if (!entries.isEmpty()) { 186 // CmsInternalSitemapEntry entry = entries.get(0); 187 // resourceStr = entry.getRootPath(); 188 // } 189 // } 190 if (OpenCms.getSiteManager().isSharedFolder(getCms().getRequestContext().getSiteRoot())) { 191 if (!OpenCms.getSiteManager().startsWithShared(resourceStr)) { 192 resourceStr = CmsStringUtil.joinPaths(OpenCms.getSiteManager().getSharedFolder(), resourceStr); 193 } 194 } 195 String url = getJsp().link(resourceStr); 196 // if in online project 197 if ((url.indexOf("://") < 0) && getCms().getRequestContext().getCurrentProject().isOnlineProject()) { 198 url = prependSiteRoot(url); 199 } 200 getJsp().getResponse().sendRedirect(url); 201 } 202 } 203 204 /** 205 * Returns the version number parameter value.<p> 206 * 207 * @return the version number parameter value 208 */ 209 public String getParamVersion() { 210 211 return m_paramVersion; 212 } 213 214 /** 215 * Sets the version number parameter value.<p> 216 * 217 * @param paramVersion the version number parameter value to set 218 */ 219 public void setParamVersionid(String paramVersion) { 220 221 m_paramVersion = paramVersion; 222 } 223 224 /** 225 * Performs a timewarp for resources that are expired or not released yet to always allow a 226 * preview of a page out of the workplace.<p> 227 * 228 * If the user has a configured timewarp (preferences dialog) a mandatory timewarp will lead to 229 * an exception. One cannot auto timewarp with configured timewarp time.<p> 230 * 231 * @param resource the resource to show 232 * 233 * @throws CmsVfsResourceNotFoundException if a warp would be needed to show the resource but the user has a configured 234 * timewarp which disallows auto warping 235 */ 236 protected void autoTimeWarp(CmsResource resource) throws CmsVfsResourceNotFoundException { 237 238 long surfTime = getCms().getRequestContext().getRequestTime(); 239 if (resource.isReleasedAndNotExpired(surfTime)) { 240 // resource is valid, no modification of time required 241 return; 242 } 243 244 if (getSettings().getUserSettings().getTimeWarp() == CmsContextInfo.CURRENT_TIME) { 245 // no time warp has been set, enable auto time warp 246 long timeWarp; 247 // will also work if ATTRIBUTE_REQUEST_TIME was CmsResource.DATE_RELEASED_EXPIRED_IGNORE 248 if (resource.isExpired(surfTime)) { 249 // do a time warp into the past 250 timeWarp = resource.getDateExpired() - 1; 251 } else if (!resource.isReleased(surfTime)) { 252 // do a time warp into the future 253 timeWarp = resource.getDateReleased() + 1; 254 } else { 255 // do no time warp 256 timeWarp = CmsContextInfo.CURRENT_TIME; 257 } 258 if (timeWarp != CmsContextInfo.CURRENT_TIME) { 259 // let's do the time warp again... 260 getSession().setAttribute(CmsContextInfo.ATTRIBUTE_REQUEST_TIME, Long.valueOf(timeWarp)); 261 } 262 } else { 263 // resource is not vaild in the time window set by the user, 264 // report an error message 265 throw new CmsVfsResourceNotFoundException( 266 Messages.get().container(Messages.ERR_RESOURCE_OUTSIDE_TIMEWINDOW_1, getParamResource())); 267 } 268 } 269 270 /** 271 * @see org.opencms.workplace.CmsDialog#initWorkplaceRequestValues(org.opencms.workplace.CmsWorkplaceSettings, jakarta.servlet.http.HttpServletRequest) 272 */ 273 @Override 274 protected void initWorkplaceRequestValues(CmsWorkplaceSettings settings, HttpServletRequest request) { 275 276 fillParamValues(settings, request); 277 } 278 279 /** 280 * Returns if the given resource has the state "deleted".<p> 281 * 282 * @param resource the resource 283 * 284 * @return <code>true</code> if the resource is of state "deleted" 285 */ 286 private boolean isDeleted(CmsResource resource) { 287 288 return resource.getState().isDeleted(); 289 } 290 291 /** 292 * Prepends the site-root to the given URL.<p> 293 * 294 * @param url the URL 295 * 296 * @return the absolute URL 297 */ 298 private String prependSiteRoot(String url) { 299 300 String site = getCms().getRequestContext().getSiteRoot(); 301 if (CmsStringUtil.isEmptyOrWhitespaceOnly(site) || OpenCms.getSiteManager().isSharedFolder(site)) { 302 site = OpenCms.getSiteManager().getDefaultUri(); 303 if (CmsStringUtil.isEmptyOrWhitespaceOnly(site) 304 || (OpenCms.getSiteManager().getSiteForSiteRoot(site) == null)) { 305 return OpenCms.getSiteManager().getWorkplaceServer() + url; 306 } else { 307 return OpenCms.getSiteManager().getSiteForSiteRoot(site).getUrl() + url; 308 } 309 } 310 return OpenCms.getSiteManager().getSiteForSiteRoot(site).getUrl() + url; 311 } 312 313 /** 314 * Reads the resource from the DB.<p> 315 * 316 * @param resourceName the resource name 317 * 318 * @return the resource 319 * 320 * @throws CmsException if the resource can not be read 321 */ 322 private CmsResource readResource(String resourceName) throws CmsException { 323 324 CmsResource resource = null; 325 try { 326 resource = getCms().readResource(resourceName, CmsResourceFilter.ALL); 327 } catch (CmsVfsResourceNotFoundException e) { 328 throw new CmsVfsResourceNotFoundException( 329 Messages.get().container( 330 Messages.ERR_RESOURCE_DOES_NOT_EXIST_3, 331 resourceName, 332 getCms().getRequestContext().getCurrentProject().getName(), 333 getCms().getRequestContext().getSiteRoot()), 334 e); 335 } 336 return resource; 337 } 338 339 /** 340 * Displays the requested historic version of the resource.<p> 341 */ 342 private void showHistoricVersion() { 343 344 String resourceStr = getParamResource(); 345 byte[] result = getHistoricalResourceContent(getCms(), resourceStr, getParamVersion()); 346 if (result != null) { 347 // get the top level response to change the content type 348 String contentType = OpenCms.getResourceManager().getMimeType( 349 resourceStr, 350 getCms().getRequestContext().getEncoding()); 351 352 HttpServletResponse res = getJsp().getResponse(); 353 HttpServletRequest req = getJsp().getRequest(); 354 355 res.setHeader( 356 CmsRequestUtil.HEADER_CONTENT_DISPOSITION, 357 new StringBuffer("attachment; filename=\"").append(resourceStr).append("\"").toString()); 358 res.setContentLength(result.length); 359 360 CmsFlexController controller = CmsFlexController.getController(req); 361 res = controller.getTopResponse(); 362 res.setContentType(contentType); 363 364 try { 365 res.getOutputStream().write(result); 366 res.getOutputStream().flush(); 367 } catch (IOException e) { 368 // can usually be ignored 369 if (LOG.isInfoEnabled()) { 370 LOG.info(e.getLocalizedMessage()); 371 } 372 } 373 } 374 } 375}