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.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 javax.servlet.http.HttpServletRequest;
051import javax.servlet.http.HttpServletResponse;
052import javax.servlet.jsp.PageContext;
053
054import org.apache.commons.logging.Log;
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(Messages.get().container(
169                    Messages.ERR_RESOURCE_DELETED_2,
170                    resourceStr,
171                    getCms().getRequestContext().getCurrentProject().getName()));
172            }
173
174            // check for release / expiration time window
175            autoTimeWarp(resource);
176
177            // code for display resource after all tests for displayability (exists, not deleted)
178            //            if (CmsResourceTypeXmlContainerPage.isContainerPage(resource)) {
179            //                // if we have a container page look for the first sitemap entry
180            //                // and use that to display the container page
181            //                List<CmsInternalSitemapEntry> entries = OpenCms.getSitemapManager().getEntriesForStructureId(
182            //                    getJsp().getCmsObject(),
183            //                    resource.getStructureId());
184            //                if (!entries.isEmpty()) {
185            //                    CmsInternalSitemapEntry entry = entries.get(0);
186            //                    resourceStr = entry.getRootPath();
187            //                }
188            //            }
189            if (OpenCms.getSiteManager().isSharedFolder(getCms().getRequestContext().getSiteRoot())) {
190                if (!OpenCms.getSiteManager().startsWithShared(resourceStr)) {
191                    resourceStr = CmsStringUtil.joinPaths(OpenCms.getSiteManager().getSharedFolder(), resourceStr);
192                }
193            }
194            String url = getJsp().link(resourceStr);
195            // if in online project
196            if ((url.indexOf("://") < 0) && getCms().getRequestContext().getCurrentProject().isOnlineProject()) {
197                url = prependSiteRoot(url);
198            }
199            getJsp().getResponse().sendRedirect(url);
200        }
201    }
202
203    /**
204     * Returns the version number parameter value.<p>
205     *
206     * @return the version number parameter value
207     */
208    public String getParamVersion() {
209
210        return m_paramVersion;
211    }
212
213    /**
214     * Sets the version number parameter value.<p>
215     *
216     * @param paramVersion the version number parameter value to set
217     */
218    public void setParamVersionid(String paramVersion) {
219
220        m_paramVersion = paramVersion;
221    }
222
223    /**
224     * Performs a timewarp for resources that are expired or not released yet to always allow a
225     * preview of a page out of the workplace.<p>
226     *
227     * If the user has a configured timewarp (preferences dialog) a mandatory timewarp will lead to
228     * an exception. One cannot auto timewarp with configured timewarp time.<p>
229     *
230     * @param resource the resource to show
231     *
232     * @throws CmsVfsResourceNotFoundException if a warp would be needed to show the resource but the user has a configured
233     *      timewarp which disallows auto warping
234     */
235    protected void autoTimeWarp(CmsResource resource) throws CmsVfsResourceNotFoundException {
236
237        long surfTime = getCms().getRequestContext().getRequestTime();
238        if (resource.isReleasedAndNotExpired(surfTime)) {
239            // resource is valid, no modification of time required
240            return;
241        }
242
243        if (getSettings().getUserSettings().getTimeWarp() == CmsContextInfo.CURRENT_TIME) {
244            // no time warp has been set, enable auto time warp
245            long timeWarp;
246            // will also work if ATTRIBUTE_REQUEST_TIME was CmsResource.DATE_RELEASED_EXPIRED_IGNORE
247            if (resource.isExpired(surfTime)) {
248                // do a time warp into the past
249                timeWarp = resource.getDateExpired() - 1;
250            } else if (!resource.isReleased(surfTime)) {
251                // do a time warp into the future
252                timeWarp = resource.getDateReleased() + 1;
253            } else {
254                // do no time warp
255                timeWarp = CmsContextInfo.CURRENT_TIME;
256            }
257            if (timeWarp != CmsContextInfo.CURRENT_TIME) {
258                // let's do the time warp again...
259                getSession().setAttribute(CmsContextInfo.ATTRIBUTE_REQUEST_TIME, Long.valueOf(timeWarp));
260            }
261        } else {
262            // resource is not vaild in the time window set by the user,
263            // report an error message
264            throw new CmsVfsResourceNotFoundException(
265                Messages.get().container(Messages.ERR_RESOURCE_OUTSIDE_TIMEWINDOW_1, getParamResource()));
266        }
267    }
268
269    /**
270     * @see org.opencms.workplace.CmsDialog#initWorkplaceRequestValues(org.opencms.workplace.CmsWorkplaceSettings, javax.servlet.http.HttpServletRequest)
271     */
272    @Override
273    protected void initWorkplaceRequestValues(CmsWorkplaceSettings settings, HttpServletRequest request) {
274
275        fillParamValues(settings, request);
276    }
277
278    /**
279     * Returns if the given resource has the state "deleted".<p>
280     *
281     * @param resource the resource
282     *
283     * @return <code>true</code> if the resource is of state "deleted"
284     */
285    private boolean isDeleted(CmsResource resource) {
286
287        return resource.getState().isDeleted();
288    }
289
290    /**
291     * Prepends the site-root to the given URL.<p>
292     *
293     * @param url the URL
294     *
295     * @return the absolute URL
296     */
297    private String prependSiteRoot(String url) {
298
299        String site = getCms().getRequestContext().getSiteRoot();
300        if (CmsStringUtil.isEmptyOrWhitespaceOnly(site) || OpenCms.getSiteManager().isSharedFolder(site)) {
301            site = OpenCms.getSiteManager().getDefaultUri();
302            if (CmsStringUtil.isEmptyOrWhitespaceOnly(site)
303                || (OpenCms.getSiteManager().getSiteForSiteRoot(site) == null)) {
304                return OpenCms.getSiteManager().getWorkplaceServer() + url;
305            } else {
306                return OpenCms.getSiteManager().getSiteForSiteRoot(site).getUrl() + url;
307            }
308        }
309        return OpenCms.getSiteManager().getSiteForSiteRoot(site).getUrl() + url;
310    }
311
312    /**
313     * Reads the resource from the DB.<p>
314     *
315     * @param resourceName the resource name
316     *
317     * @return the resource
318     *
319     * @throws CmsException if the resource can not be read
320     */
321    private CmsResource readResource(String resourceName) throws CmsException {
322
323        CmsResource resource = null;
324        try {
325            resource = getCms().readResource(resourceName, CmsResourceFilter.ALL);
326        } catch (CmsVfsResourceNotFoundException e) {
327            throw new CmsVfsResourceNotFoundException(
328                Messages.get().container(
329                    Messages.ERR_RESOURCE_DOES_NOT_EXIST_3,
330                    resourceName,
331                    getCms().getRequestContext().getCurrentProject().getName(),
332                    getCms().getRequestContext().getSiteRoot()),
333                e);
334        }
335        return resource;
336    }
337
338    /**
339     * Displays the requested historic version of the resource.<p>
340     */
341    private void showHistoricVersion() {
342
343        String resourceStr = getParamResource();
344        byte[] result = getHistoricalResourceContent(getCms(), resourceStr, getParamVersion());
345        if (result != null) {
346            // get the top level response to change the content type
347            String contentType = OpenCms.getResourceManager().getMimeType(
348                resourceStr,
349                getCms().getRequestContext().getEncoding());
350
351            HttpServletResponse res = getJsp().getResponse();
352            HttpServletRequest req = getJsp().getRequest();
353
354            res.setHeader(
355                CmsRequestUtil.HEADER_CONTENT_DISPOSITION,
356                new StringBuffer("attachment; filename=\"").append(resourceStr).append("\"").toString());
357            res.setContentLength(result.length);
358
359            CmsFlexController controller = CmsFlexController.getController(req);
360            res = controller.getTopResponse();
361            res.setContentType(contentType);
362
363            try {
364                res.getOutputStream().write(result);
365                res.getOutputStream().flush();
366            } catch (IOException e) {
367                // can usually be ignored
368                if (LOG.isInfoEnabled()) {
369                    LOG.info(e.getLocalizedMessage());
370                }
371            }
372        }
373    }
374}