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.jsp;
029
030import org.opencms.file.CmsObject;
031import org.opencms.file.CmsResource;
032import org.opencms.file.CmsResourceFilter;
033import org.opencms.file.CmsVfsResourceNotFoundException;
034import org.opencms.flex.CmsFlexController;
035import org.opencms.i18n.CmsLocaleManager;
036import org.opencms.jsp.util.CmsJspLinkWrapper;
037import org.opencms.main.CmsException;
038import org.opencms.main.CmsLog;
039import org.opencms.main.OpenCms;
040import org.opencms.staticexport.CmsLinkManager;
041import org.opencms.util.CmsStringUtil;
042
043import java.util.Locale;
044
045import javax.servlet.ServletRequest;
046import javax.servlet.jsp.JspException;
047import javax.servlet.jsp.PageContext;
048import javax.servlet.jsp.tagext.BodyTagSupport;
049
050import org.apache.commons.logging.Log;
051
052/**
053 * Implements the <code>&lt;cms:link&gt;[filename]&lt;/cms:link&gt;</code>
054 * tag to add OpenCms managed links to a JSP page, required for link
055 * management and the static
056 * export to work properly.<p>
057 *
058 * @since 6.0.0
059 */
060public class CmsJspTagLink extends BodyTagSupport {
061
062    /**
063     * Parameters for the link tag.
064     */
065    public static class Parameters {
066
067        /** The target. */
068        public String m_target;
069
070        /** The base uri. */
071        public String m_baseUri;
072
073        /** The detail page. */
074        public String m_detailPage;
075
076        /** The locale. */
077        public Locale m_locale;
078
079        /** The type. */
080        public Type m_type;
081
082        /**
083         * Default constructor.
084         */
085        public Parameters() {}
086
087        /**
088         * Instantiates a new link tag params.
089         *
090         * @param target the target
091         * @param baseUri the base uri
092         * @param detailPage the detail page
093         * @param locale the locale
094         * @param type the type
095         */
096        public Parameters(String target, String baseUri, String detailPage, Locale locale, Type type) {
097
098            m_target = target;
099            m_baseUri = baseUri;
100            m_detailPage = detailPage;
101            m_locale = locale;
102            m_type = type != null ? type : Type.DEFAULT;
103        }
104
105        /**
106         * Gets the base uri.
107         *
108         * @return the base uri
109         */
110        public String getBaseUri() {
111
112            return m_baseUri;
113        }
114
115        /**
116         * Gets the detail page.
117         *
118         * @return the detail page
119         */
120        public String getDetailPage() {
121
122            return m_detailPage;
123        }
124
125        /**
126         * Gets the locale.
127         *
128         * @return the locale
129         */
130        public Locale getLocale() {
131
132            return m_locale;
133        }
134
135        /**
136         * Gets the target.
137         *
138         * @return the target
139         */
140        public String getTarget() {
141
142            return m_target;
143        }
144
145        /**
146         * Gets the type.
147         *
148         * @return the type
149         */
150        public Type getType() {
151
152            return m_type;
153        }
154
155        /**
156         * Sets the base uri.
157         *
158         * @param baseUri the new base uri
159         */
160        public void setBaseUri(String baseUri) {
161
162            m_baseUri = baseUri;
163        }
164
165        /**
166         * Sets the detail page.
167         *
168         * @param detailPage the new detail page
169         */
170        public void setDetailPage(String detailPage) {
171
172            m_detailPage = detailPage;
173        }
174
175        /**
176         * Sets the locale.
177         *
178         * @param locale the new locale
179         */
180        public void setLocale(Locale locale) {
181
182            m_locale = locale;
183        }
184
185        /**
186         * Sets the target.
187         *
188         * @param target the new target
189         */
190        public void setTarget(String target) {
191
192            m_target = target;
193        }
194
195        /**
196         * Sets the type.
197         *
198         * @param type the new type
199         */
200        public void setType(Type type) {
201
202            m_type = type;
203        }
204    }
205
206    /** Link type. */
207    public enum Type {
208        /** Default mode. */
209        DEFAULT,
210        /** Online link mode. */
211        ONLINE,
212
213        /** Server link mode. */
214        SERVER,
215
216        /** Permalink mode. */
217        PERMA;
218    }
219
220    /** String describing request scope. */
221    private static final String SCOPE_REQUEST = "request";
222
223    /** String describing session scope. */
224    private static final String SCOPE_SESSION = "session";
225
226    /** String describing application scope. */
227    private static final String SCOPE_APPLICATION = "application";
228
229    /** The log object for this class. */
230    private static final Log LOG = CmsLog.getLog(CmsJspTagLink.class);
231
232    /** Serial version UID required for safe serialization. */
233    private static final long serialVersionUID = -2361021288258405388L;
234
235    /** The variable to store the result. */
236    private String m_var;
237
238    /** The scope used for the var parameter. */
239    private String m_scope;
240
241    /** The optional base URI to create the link from. */
242    private String m_baseUri;
243
244    /** The target detail page path. */
245    private String m_detailPage;
246
247    /** The optional locale attribute. */
248    private Locale m_locale;
249
250    /** The link type. */
251    private Type m_type;
252
253    /**
254     * Tries to read the active locale for the given (site) path, or for its parent path if the path can't be read.
255     * <p>
256     * If this fails, null is returned.
257     *
258     * @param cms the CMS context
259     * @param baseUri the base URI for which to read the locale
260     * @return the locale
261     */
262    public static Locale getBaseUriLocale(CmsObject cms, String baseUri) {
263
264        try {
265            try {
266                return OpenCms.getLocaleManager().getDefaultLocale(
267                    cms,
268                    cms.readResource(baseUri, CmsResourceFilter.IGNORE_EXPIRATION));
269            } catch (CmsVfsResourceNotFoundException e) {
270                String parent = CmsResource.getParentFolder(baseUri);
271                if (parent != null) {
272                    return OpenCms.getLocaleManager().getDefaultLocale(
273                        cms,
274                        cms.readResource(parent, CmsResourceFilter.IGNORE_EXPIRATION));
275                }
276            }
277        } catch (CmsException e) {
278            LOG.info(e.getLocalizedMessage(), e);
279        }
280        return null;
281
282    }
283
284    /**
285     * Returns a link to a file in the OpenCms VFS
286     * that has been adjusted according to the web application path and the
287     * OpenCms static export rules.<p>
288     *
289     * <p>If the <code>baseUri</code> parameter is provided, this will be treated as the source of the link,
290     * if this is <code>null</code> then the current OpenCms user context URI will be used as source.</p>
291     *
292     * <p>If the <code>locale</code> parameter is provided, the locale in the request context will be switched
293     * to the provided locale. This influences only the behavior of the
294     * {@link org.opencms.staticexport.CmsLocalePrefixLinkSubstitutionHandler}.</p>
295     *
296     *
297     * Relative links are converted to absolute links, using the current element URI as base.<p>
298     * @param params TODO
299     * @param req the current request
300     *
301     * @return the target link adjusted according to the web application path and the OpenCms static export rules
302     *
303     * @see #linkTagAction(String, ServletRequest)
304     *
305     * @since 8.0.3
306     */
307    public static String linkTagAction(Parameters params, ServletRequest req) {
308
309        CmsFlexController controller = CmsFlexController.getController(req);
310        // be sure the link is absolute
311        String uri = CmsLinkManager.getAbsoluteUri(params.getTarget(), controller.getCurrentRequest().getElementUri());
312        CmsObject cms = prepareCmsObject(controller.getCmsObject(), params);
313        CmsLinkManager linkManager = OpenCms.getLinkManager();
314        // generate the link
315        switch (params.getType()) {
316            case ONLINE:
317                return linkManager.getOnlineLink(cms, uri, params.getDetailPage(), false);
318            case PERMA:
319                return linkManager.getPermalink(cms, uri);
320            case SERVER:
321                return linkManager.getServerLink(cms, uri);
322            case DEFAULT:
323            default:
324                return linkManager.substituteLinkForUnknownTarget(cms, uri, params.getDetailPage(), false);
325
326        }
327    }
328
329    /**
330     * Returns a link to a file in the OpenCms VFS
331     * that has been adjusted according to the web application path and the
332     * OpenCms static export rules.<p>
333     *
334     * The current OpenCms user context URI will be used as source of the link.</p>
335     *
336     * Since OpenCms version 7.0.2, you can also use this method in case you are not sure
337     * if the link is internal or external, as
338     * {@link CmsLinkManager#substituteLinkForUnknownTarget(org.opencms.file.CmsObject, String)}
339     * is used to calculate the link target.<p>
340     *
341     * Relative links are converted to absolute links, using the current element URI as base.<p>
342     *
343     * @param target the link that should be calculated, can be relative or absolute
344     * @param req the current request
345     *
346     * @return the target link adjusted according to the web application path and the OpenCms static export rules
347     *
348     * @see org.opencms.staticexport.CmsLinkManager#substituteLinkForUnknownTarget(org.opencms.file.CmsObject, String)
349     */
350    public static String linkTagAction(String target, ServletRequest req) {
351
352        return linkTagAction(target, req, null);
353    }
354
355    /**
356     * Returns a link to a file in the OpenCms VFS
357     * that has been adjusted according to the web application path and the
358     * OpenCms static export rules.<p>
359     *
360     * If the <code>baseUri</code> parameter is provided, this will be treated as the source of the link,
361     * if this is <code>null</code> then the current OpenCms user context URI will be used as source.</p>
362     *
363     * Relative links are converted to absolute links, using the current element URI as base.<p>
364     *
365     * @param target the link that should be calculated, can be relative or absolute
366     * @param req the current request
367     * @param baseUri the base URI for the link source
368     *
369     * @return the target link adjusted according to the web application path and the OpenCms static export rules
370     *
371     * @see #linkTagAction(String, ServletRequest)
372     *
373     * @since 8.0.3
374     */
375    public static String linkTagAction(String target, ServletRequest req, String baseUri) {
376
377        return linkTagAction(target, req, baseUri, null);
378    }
379
380    /**
381     * Returns a link to a file in the OpenCms VFS
382     * that has been adjusted according to the web application path and the
383     * OpenCms static export rules.<p>
384     *
385     * <p>If the <code>baseUri</code> parameter is provided, this will be treated as the source of the link,
386     * if this is <code>null</code> then the current OpenCms user context URI will be used as source.</p>
387     *
388     * <p>If the <code>locale</code> parameter is provided, the locale in the request context will be switched
389     * to the provided locale. This influences only the behavior of the
390     * {@link org.opencms.staticexport.CmsLocalePrefixLinkSubstitutionHandler}.</p>
391     *
392     *
393     * Relative links are converted to absolute links, using the current element URI as base.<p>
394     *
395     * @param target the link that should be calculated, can be relative or absolute
396     * @param req the current request
397     * @param baseUri the base URI for the link source
398     * @param locale the locale for which the link should be created (see {@link org.opencms.staticexport.CmsLocalePrefixLinkSubstitutionHandler}
399     *
400     * @return the target link adjusted according to the web application path and the OpenCms static export rules
401     *
402     * @see #linkTagAction(String, ServletRequest)
403     *
404     * @since 8.0.3
405     */
406    public static String linkTagAction(String target, ServletRequest req, String baseUri, Locale locale) {
407
408        return linkTagAction(target, req, baseUri, null, locale);
409    }
410
411    /**
412     * Returns a link to a file in the OpenCms VFS
413     * that has been adjusted according to the web application path and the
414     * OpenCms static export rules.<p>
415     *
416     * <p>If the <code>baseUri</code> parameter is provided, this will be treated as the source of the link,
417     * if this is <code>null</code> then the current OpenCms user context URI will be used as source.</p>
418     *
419     * <p>If the <code>locale</code> parameter is provided, the locale in the request context will be switched
420     * to the provided locale. This influences only the behavior of the
421     * {@link org.opencms.staticexport.CmsLocalePrefixLinkSubstitutionHandler}.</p>
422     *
423     *
424     * Relative links are converted to absolute links, using the current element URI as base.<p>
425     *
426     * @param target the link that should be calculated, can be relative or absolute
427     * @param req the current request
428     * @param baseUri the base URI for the link source
429     * @param detailPage the target detail page, in case of linking to a specific detail page
430     * @param locale the locale for which the link should be created (see {@link org.opencms.staticexport.CmsLocalePrefixLinkSubstitutionHandler}
431     *
432     * @return the target link adjusted according to the web application path and the OpenCms static export rules
433     *
434     * @see #linkTagAction(String, ServletRequest)
435     *
436     * @since 8.0.3
437     */
438    public static String linkTagAction(
439        String target,
440        ServletRequest req,
441        String baseUri,
442        String detailPage,
443        Locale locale) {
444
445        return linkTagAction(new Parameters(target, baseUri, detailPage, locale, Type.DEFAULT), req);
446    }
447
448    /**
449     * Initializes CmsObject with data from the tag parameters.
450     *
451     * @param cms1 the CmsObject to use as a base
452     * @param params the parameters to use for the initialization
453     * @return the initialized CmsObject
454     */
455    private static CmsObject prepareCmsObject(CmsObject cms1, Parameters params) {
456
457        CmsObject cms = cms1;
458        if (CmsStringUtil.isNotEmptyOrWhitespaceOnly(params.getBaseUri()) || (null != params.getLocale())) {
459            try {
460                cms = OpenCms.initCmsObject(cms);
461                if (CmsStringUtil.isNotEmptyOrWhitespaceOnly(params.getBaseUri())) {
462                    cms.getRequestContext().setUri(params.getBaseUri());
463                    if (params.getLocale() == null) {
464                        Locale baseUriLocale = getBaseUriLocale(cms, params.getBaseUri());
465                        if (baseUriLocale != null) {
466                            cms.getRequestContext().setLocale(baseUriLocale);
467                        }
468                    }
469                }
470                if (null != params.getLocale()) {
471                    cms.getRequestContext().setLocale(params.getLocale());
472                }
473            } catch (CmsException e) {
474                // should not happen, if it does we can't do anything useful and will just keep the original object
475                LOG.debug(e.getLocalizedMessage(), e);
476            }
477        }
478        return cms;
479    }
480
481    /**
482     * @see javax.servlet.jsp.tagext.Tag#doEndTag()
483     *
484     * @return EVAL_PAGE
485     *
486     * @throws JspException in case something goes wrong
487     */
488    @Override
489    public int doEndTag() throws JspException {
490
491        ServletRequest req = pageContext.getRequest();
492
493        // This will always be true if the page is called through OpenCms
494        if (CmsFlexController.isCmsRequest(req)) {
495            try {
496
497                // Get link-string from the body and reset body
498                String link = getBodyContent().getString();
499                getBodyContent().clear();
500                Parameters params = new Parameters(link, getBaseUri(), getDetailPage(), m_locale, m_type);
501                if (m_var != null) {
502                    int scope = PageContext.PAGE_SCOPE; // default
503                    if (SCOPE_REQUEST.equalsIgnoreCase(m_scope)) {
504                        scope = PageContext.REQUEST_SCOPE;
505                    } else if (SCOPE_SESSION.equalsIgnoreCase(m_scope)) {
506                        scope = PageContext.SESSION_SCOPE;
507                    } else if (SCOPE_APPLICATION.equalsIgnoreCase(m_scope)) {
508                        scope = PageContext.APPLICATION_SCOPE;
509                    }
510                    CmsFlexController controller = CmsFlexController.getController(req);
511                    CmsObject cms = prepareCmsObject(controller.getCmsObject(), params);
512                    pageContext.setAttribute(m_var, new CmsJspLinkWrapper(cms, link, true), scope);
513                } else {
514                    // Calculate the link substitution
515                    String newlink = linkTagAction(params, req);
516                    // Write the result back to the page
517                    getBodyContent().print(newlink);
518                    getBodyContent().writeOut(pageContext.getOut());
519                }
520            } catch (Exception ex) {
521                if (LOG.isErrorEnabled()) {
522                    LOG.error(Messages.get().getBundle().key(Messages.ERR_PROCESS_TAG_1, "link"), ex);
523                }
524                throw new JspException(ex);
525            }
526        }
527        return EVAL_PAGE;
528    }
529
530    /**
531     * Returns the base URI used to create the link target.<p>
532     *
533     * @return  the base URI used to create the link target
534     */
535    public String getBaseUri() {
536
537        return m_baseUri;
538    }
539
540    /**
541     * Returns the target detail page path.<p>
542     *
543     * @return the target detail page path
544     */
545    public String getDetailPage() {
546
547        return m_detailPage;
548    }
549
550    /**
551     * @see javax.servlet.jsp.tagext.Tag#release()
552     */
553    @Override
554    public void release() {
555
556        super.release();
557    }
558
559    /**
560     * Sets the base URI used to create the link target.<p>
561     *
562     * @param baseUri the base URI used to create the link target
563     */
564    public void setBaseUri(String baseUri) {
565
566        m_baseUri = baseUri;
567    }
568
569    /**
570     * Sets the target detail page path.<p>
571     *
572     * @param detailPage the target detail page path
573     */
574    public void setDetailPage(String detailPage) {
575        if ("".equals(detailPage)) {
576            detailPage = null;
577        }
578
579        m_detailPage = detailPage;
580    }
581
582    /**
583     * Sets the locale to use for the link.
584     *
585     * @param locale the locale to use for the link
586     */
587    public void setLocale(Locale locale) {
588
589        m_locale = locale;
590    }
591
592    /**
593     * Sets the locale for the link to create.
594     *
595     * @param localeName name of the locale, e.g. "en", "en_US", ...
596     */
597    public void setLocale(String localeName) {
598
599        m_locale = CmsLocaleManager.getLocale(localeName);
600    }
601
602    /**
603     * Sets the scope (only used in combination with the var parameter).
604     * @param scope the scope for the variable
605     */
606    public void setScope(String scope) {
607
608        m_scope = scope;
609    }
610
611    /**
612     * Sets the type.
613     *
614     * @param type the link type
615     */
616    public void setType(String type) {
617
618        if (type == null) {
619            m_type = null;
620        } else {
621            m_type = Type.valueOf(type.toUpperCase());
622        }
623
624    }
625
626    /**
627     * Sets the type.
628     *
629     * @param type the type
630     */
631    public void setType(Type type) {
632
633        m_type = type;
634    }
635
636    /**
637     * Sets the variable name to store the result in.
638     * @param var the variable name to store the result in
639     */
640    public void setVar(String var) {
641
642        m_var = var;
643    }
644
645}