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.collectors.I_CmsResourceCollector;
033import org.opencms.flex.CmsFlexController;
034import org.opencms.jsp.util.CmsJspResourceLoadBean;
035import org.opencms.main.CmsException;
036import org.opencms.main.CmsIllegalArgumentException;
037import org.opencms.main.OpenCms;
038import org.opencms.util.CmsMacroResolver;
039import org.opencms.util.CmsStringUtil;
040
041import java.util.List;
042
043import javax.servlet.jsp.JspException;
044import javax.servlet.jsp.PageContext;
045import javax.servlet.jsp.tagext.Tag;
046
047/**
048 * Implementation of the <code>&lt;cms:resourceload/&gt;</code> tag,
049 * used to access and display resource information from the VFS.<p>
050 *
051 * @since 8.0
052 */
053public class CmsJspTagResourceLoad extends CmsJspScopedVarBodyTagSuport implements I_CmsResourceContainer {
054
055    /** Serial version UID required for safe serialization. */
056    private static final long serialVersionUID = -3753361821868919139L;
057
058    /** The CmsObject for the current user. */
059    protected transient CmsObject m_cms;
060
061    /** The name of the collector to use for list building. */
062    protected String m_collector;
063
064    /** The name of the resource collector used. */
065    protected String m_collectorName;
066
067    /** The parameters of the resource collector uses. */
068    protected String m_collectorParam;
069
070    /** The list of collected resource items. */
071    protected List<CmsResource> m_collectorResult;
072
073    /** The bean to store information required to make the result list browsable. */
074    protected CmsContentInfoBean m_contentInfoBean;
075
076    /** The FlexController for the current request. */
077    protected CmsFlexController m_controller;
078
079    /** The index of the current page that gets displayed. */
080    protected String m_pageIndex;
081
082    /** The number of page links in the Google-like page navigation. */
083    protected String m_pageNavLength;
084
085    /** The size of a page to be displayed. */
086    protected String m_pageSize;
087
088    /** Parameter used for the collector. */
089    protected String m_param;
090
091    /** Indicates if the collector results should be preloaded. */
092    protected boolean m_preload;
093
094    /** The (optional) property to extend the parameter with. */
095    protected String m_property;
096
097    /** Reference to the last loaded resource element. */
098    protected transient CmsResource m_resource;
099
100    /** The file name to load the current content value from. */
101    protected String m_resourceName;
102
103    /**
104     * Empty constructor, required for JSP tags.<p>
105     */
106    public CmsJspTagResourceLoad() {
107
108        super();
109    }
110
111    /**
112     * Constructor used when using <code>resourceload</code> from scriptlet code.<p>
113     *
114     * @param container the parent resource container (could be a preloader)
115     * @param context the JSP page context
116     * @param collectorName the collector name to use
117     * @param collectorParam the collector param to use
118     *
119     * @throws JspException in case something goes wrong
120     */
121    public CmsJspTagResourceLoad(
122        I_CmsResourceContainer container,
123        PageContext context,
124        String collectorName,
125        String collectorParam)
126        throws JspException {
127
128        this(container, context, collectorName, collectorParam, null, null);
129    }
130
131    /**
132     * Constructor used when using <code>resourceload</code> from scriptlet code.<p>
133     *
134     * @param container the parent resource container (could be a preloader)
135     * @param context the JSP page context
136     * @param collectorName the collector name to use
137     * @param collectorParam the collector param to use
138     * @param pageIndex the display page index (may contain macros)
139     * @param pageSize the display page size (may contain macros)
140     *
141     * @throws JspException in case something goes wrong
142     */
143    public CmsJspTagResourceLoad(
144        I_CmsResourceContainer container,
145        PageContext context,
146        String collectorName,
147        String collectorParam,
148        String pageIndex,
149        String pageSize)
150        throws JspException {
151
152        setCollector(collectorName);
153        setParam(collectorParam);
154        setPageIndex(pageIndex);
155        setPageSize(pageSize);
156        m_preload = false;
157
158        setPageContext(context);
159        init(container);
160    }
161
162    /**
163     * Returns the resource name currently processed.<p>
164     *
165     * @param cms the current OpenCms user context
166     * @param contentContainer the current resource container
167     *
168     * @return the resource name currently processed
169     */
170    protected static String getResourceName(CmsObject cms, I_CmsResourceContainer contentContainer) {
171
172        if ((contentContainer != null) && (contentContainer.getResourceName() != null)) {
173            return contentContainer.getResourceName();
174        } else if (cms != null) {
175            return cms.getRequestContext().getUri();
176        } else {
177            return null;
178        }
179    }
180
181    /**
182     * Limits the collector's result list to the size of a page to be displayed in a JSP.<p>
183     *
184     * @param contentInfoBean the info bean of the collector
185     * @param collectorResult the result list of the collector
186     *
187     * @return a limited collector's result list
188     */
189    protected static List<CmsResource> limitCollectorResult(
190        CmsContentInfoBean contentInfoBean,
191        List<CmsResource> collectorResult) {
192
193        List<CmsResource> result = null;
194        int pageCount = -1;
195
196        if (contentInfoBean.getPageSize() > 0) {
197
198            pageCount = collectorResult.size() / contentInfoBean.getPageSize();
199            if ((collectorResult.size() % contentInfoBean.getPageSize()) != 0) {
200                pageCount++;
201            }
202
203            contentInfoBean.setPageCount(pageCount);
204
205            int startIndex = (contentInfoBean.getPageIndex() - 1) * contentInfoBean.getPageSize();
206            int endIndex = contentInfoBean.getPageIndex() * contentInfoBean.getPageSize();
207            if (endIndex > collectorResult.size()) {
208                endIndex = collectorResult.size();
209            }
210
211            result = collectorResult.subList(startIndex, endIndex);
212        } else {
213
214            result = collectorResult;
215            if (collectorResult.size() > 0) {
216                contentInfoBean.setPageCount(1);
217            }
218        }
219
220        return result;
221    }
222
223    /**
224     * @see javax.servlet.jsp.tagext.BodyTagSupport#doAfterBody()
225     */
226    @Override
227    public int doAfterBody() throws JspException {
228
229        // close open direct edit first
230        if (hasMoreResources()) {
231            // another loop is required
232            return EVAL_BODY_AGAIN;
233        }
234        if (OpenCms.getSystemInfo().getServletContainerSettings().isReleaseTagsAfterEnd()) {
235            // need to release manually, JSP container may not call release as required (happens with Tomcat)
236            release();
237        }
238        // no more files are available, so skip the body and finish the loop
239        return SKIP_BODY;
240    }
241
242    /**
243     * @see javax.servlet.jsp.tagext.Tag#doEndTag()
244     */
245    @Override
246    public int doEndTag() {
247
248        release();
249        return EVAL_PAGE;
250    }
251
252    /**
253     * @see javax.servlet.jsp.tagext.Tag#doStartTag()
254     */
255    @Override
256    public int doStartTag() throws JspException, CmsIllegalArgumentException {
257
258        // get a reference to the parent "content container" class (if available)
259        Tag ancestor = findAncestorWithClass(this, I_CmsResourceContainer.class);
260        I_CmsResourceContainer container = null;
261        if (ancestor != null) {
262            // parent content container available, use preloaded values from this container
263            container = (I_CmsResourceContainer)ancestor;
264            // check if container really is a preloader
265            if (!container.isPreloader()) {
266                // don't use ancestor if not a preloader
267                container = null;
268            }
269        }
270
271        // initialize the content load tag
272        init(container);
273
274        hasMoreResources();
275        return isScopeVarSet() ? SKIP_BODY : EVAL_BODY_INCLUDE;
276    }
277
278    /**
279     * Returns the collector.<p>
280     *
281     * @return the collector
282     */
283    public String getCollector() {
284
285        return m_collector;
286    }
287
288    /**
289     * @see org.opencms.jsp.I_CmsResourceContainer#getCollectorName()
290     */
291    public String getCollectorName() {
292
293        return m_collectorName;
294    }
295
296    /**
297     * @see org.opencms.jsp.I_CmsResourceContainer#getCollectorParam()
298     */
299    public String getCollectorParam() {
300
301        return m_collectorParam;
302    }
303
304    /**
305     * @see org.opencms.jsp.I_CmsResourceContainer#getCollectorResult()
306     */
307    public List<CmsResource> getCollectorResult() {
308
309        return m_collectorResult;
310    }
311
312    /**
313     * Returns the index of the page to be displayed.<p>
314     *
315     * @return the index of the page to be displayed
316     */
317    public String getPageIndex() {
318
319        return m_pageIndex;
320    }
321
322    /**
323     * Returns the number of page links in the Google-like page navigation.<p>
324     *
325     * @return the number of page links in the Google-like page navigation
326     */
327    public String getPageNavLength() {
328
329        return m_pageNavLength;
330    }
331
332    /**
333     * Returns the size of a single page to be displayed.<p>
334     *
335     * @return the size of a single page to be displayed
336     */
337    public String getPageSize() {
338
339        return m_pageSize;
340    }
341
342    /**
343     * Returns the collector parameter.<p>
344     *
345     * @return the collector parameter
346     */
347    public String getParam() {
348
349        return m_param;
350    }
351
352    /**
353     * Returns <code>"true"</code> if this content load tag should only preload the values from the collector.<p>
354     *
355     * @return <code>"true"</code> if this content load tag should only preload the values from the collector
356     */
357    public String getPreload() {
358
359        return String.valueOf(isPreloader());
360    }
361
362    /**
363     * Returns the property.<p>
364     *
365     * @return the property
366     */
367    public String getProperty() {
368
369        return m_property;
370    }
371
372    /**
373     * @see org.opencms.jsp.I_CmsResourceContainer#getResource()
374     */
375    public CmsResource getResource() {
376
377        return m_resource;
378    }
379
380    /**
381     * @see org.opencms.jsp.I_CmsResourceContainer#getResourceName()
382     */
383    public String getResourceName() {
384
385        return m_resourceName;
386    }
387
388    /**
389     * @see org.opencms.jsp.I_CmsResourceContainer#hasMoreContent()
390     */
391    @Deprecated
392    public boolean hasMoreContent() throws JspException {
393
394        return hasMoreResources();
395    }
396
397    /**
398     * @see org.opencms.jsp.I_CmsResourceContainer#hasMoreResources()
399     */
400    @SuppressWarnings("unused")
401    public boolean hasMoreResources() throws JspException {
402
403        if (isPreloader()) {
404            // if in preload mode, no result is required
405            return false;
406        }
407
408        // check if there are more files to iterate
409        boolean hasMoreResources = m_collectorResult.size() > 0;
410        if (hasMoreResources) {
411            // there are more results available...
412            doLoadNextResource();
413        }
414
415        return hasMoreResources;
416    }
417
418    /**
419     * @see org.opencms.jsp.I_CmsResourceContainer#isPreloader()
420     */
421    public boolean isPreloader() {
422
423        return isScopeVarSet() ? true : m_preload;
424    }
425
426    /**
427     * @see javax.servlet.jsp.tagext.Tag#release()
428     */
429    @Override
430    public void release() {
431
432        m_cms = null;
433        m_collector = null;
434        m_collectorName = null;
435        m_collectorParam = null;
436        m_collectorResult = null;
437        m_resource = null;
438        m_contentInfoBean = null;
439        m_controller = null;
440        m_pageIndex = null;
441        m_pageNavLength = null;
442        m_pageSize = null;
443        m_param = null;
444        m_preload = false;
445        m_property = null;
446        m_resourceName = null;
447        super.release();
448    }
449
450    /**
451     * Sets the collector name.<p>
452     *
453     * @param collector the collector name to set
454     */
455    public void setCollector(String collector) {
456
457        m_collector = collector;
458    }
459
460    /**
461     * Sets the index of the page to be displayed.<p>
462     *
463     * @param pageIndex the index of the page to be displayed
464     */
465    public void setPageIndex(String pageIndex) {
466
467        m_pageIndex = pageIndex;
468    }
469
470    /**
471     * Sets the number of page links in the Google-like page navigation.<p>
472     *
473     * @param pageNavLength the number of page links in the Google-like page navigation
474     */
475    public void setPageNavLength(String pageNavLength) {
476
477        m_pageNavLength = pageNavLength;
478    }
479
480    /**
481     * Sets the size of a single page to be displayed.<p>
482     *
483     * @param pageSize the size of a single page to be displayed
484     */
485    public void setPageSize(String pageSize) {
486
487        m_pageSize = pageSize;
488    }
489
490    /**
491     * Sets the collector parameter.<p>
492     *
493     * @param param the collector parameter to set
494     */
495    public void setParam(String param) {
496
497        m_param = param;
498    }
499
500    /**
501     * Sets the preload flag for this resource load tag.<p>
502     *
503     * If this is set to <code>true</code>, then the collector result will only
504     * be preloaded, but not iterated.<p>
505     *
506     * @param preload the preload flag to set
507     */
508    public void setPreload(String preload) {
509
510        m_preload = Boolean.valueOf(preload).booleanValue();
511    }
512
513    /**
514     * Sets the property.<p>
515     *
516     * @param property the property to set
517     */
518    public void setProperty(String property) {
519
520        m_property = property;
521    }
522
523    /**
524     * Load the next resource from the initialized list of resources.<p>
525     */
526    protected void doLoadNextResource() {
527
528        // get the next resource from the collector
529        CmsResource resource = getNextResource();
530        if (resource == null) {
531            m_resourceName = null;
532            m_resource = null;
533            return;
534        }
535
536        // set the resource name
537        m_resourceName = m_cms.getSitePath(resource);
538
539        // set the resource
540        m_resource = resource;
541    }
542
543    /**
544     * Returns the content info bean.<p>
545     *
546     * @return the content info bean
547     */
548    protected CmsContentInfoBean getContentInfoBean() {
549
550        return m_contentInfoBean;
551    }
552
553    /**
554     * Returns the next resource from the collector.<p>
555     *
556     * @return the next resource from the collector
557     */
558    protected CmsResource getNextResource() {
559
560        if ((m_collectorResult != null) && (m_collectorResult.size() > 0)) {
561
562            m_contentInfoBean.incResultIndex();
563            return m_collectorResult.remove(0);
564        }
565
566        return null;
567    }
568
569    /**
570     * Initializes this content load tag.<p>
571     *
572     * @param container the parent container (could be a preloader)
573     *
574     * @throws JspException in case something goes wrong
575     */
576    protected void init(I_CmsResourceContainer container) throws JspException {
577
578        // check if the tag contains a pageSize, pageIndex and pageNavLength attribute, or none of them
579        int pageAttribCount = 0;
580        pageAttribCount += CmsStringUtil.isNotEmpty(m_pageSize) ? 1 : 0;
581        pageAttribCount += CmsStringUtil.isNotEmpty(m_pageIndex) ? 1 : 0;
582
583        if ((pageAttribCount > 0) && (pageAttribCount < 2)) {
584            throw new CmsIllegalArgumentException(Messages.get().container(Messages.ERR_TAG_RESOURCELOAD_INDEX_SIZE_0));
585        }
586
587        I_CmsResourceContainer usedContainer;
588        if (container == null) {
589            // no preloading ancestor has been found
590            usedContainer = this;
591            if (CmsStringUtil.isEmpty(m_collector)) {
592                // check if the tag contains a collector attribute
593                throw new CmsIllegalArgumentException(
594                    Messages.get().container(Messages.ERR_TAG_RESOURCELOAD_MISSING_COLLECTOR_0));
595            }
596            if (CmsStringUtil.isEmpty(m_param)) {
597                // check if the tag contains a param attribute
598                throw new CmsIllegalArgumentException(
599                    Messages.get().container(Messages.ERR_TAG_RESOURCELOAD_MISSING_PARAM_0));
600            }
601        } else {
602            // use provided container (preloading ancestor)
603            usedContainer = container;
604        }
605
606        // initialize OpenCms access objects
607        m_controller = CmsFlexController.getController(pageContext.getRequest());
608        m_cms = m_controller.getCmsObject();
609
610        // get the resource name from the selected container
611        String resourcename = getResourceName(m_cms, usedContainer);
612
613        // initialize a string mapper to resolve EL like strings in tag attributes
614        CmsMacroResolver resolver = CmsMacroResolver.newInstance().setCmsObject(m_cms).setJspPageContext(
615            pageContext).setResourceName(resourcename).setKeepEmptyMacros(true);
616
617        // resolve the collector name
618        if (container == null) {
619            // no preload parent container, initialize new values
620            m_collectorName = resolver.resolveMacros(getCollector());
621            // resolve the parameter
622            m_collectorParam = resolver.resolveMacros(getParam());
623            m_collectorResult = null;
624        } else {
625            // preload parent content container available, use values from this container
626            m_collectorName = usedContainer.getCollectorName();
627            m_collectorParam = usedContainer.getCollectorParam();
628            m_collectorResult = usedContainer.getCollectorResult();
629        }
630
631        try {
632            // now collect the resources
633            I_CmsResourceCollector collector = OpenCms.getResourceManager().getContentCollector(m_collectorName);
634            if (collector == null) {
635                throw new CmsException(Messages.get().container(Messages.ERR_COLLECTOR_NOT_FOUND_1, m_collectorName));
636            }
637            // execute the collector if not already done in parent tag
638            if (m_collectorResult == null) {
639                m_collectorResult = collector.getResults(m_cms, m_collectorName, m_collectorParam);
640            }
641
642            m_contentInfoBean = new CmsContentInfoBean();
643            m_contentInfoBean.setPageSizeAsString(resolver.resolveMacros(m_pageSize));
644            m_contentInfoBean.setPageIndexAsString(resolver.resolveMacros(m_pageIndex));
645            m_contentInfoBean.setPageNavLengthAsString(resolver.resolveMacros(m_pageNavLength));
646            m_contentInfoBean.setResultSize(m_collectorResult.size());
647            m_contentInfoBean.initResultIndex();
648
649            if (!isPreloader()) {
650                // not required when only preloading
651                m_collectorResult = CmsJspTagResourceLoad.limitCollectorResult(m_contentInfoBean, m_collectorResult);
652                m_contentInfoBean.initPageNavIndexes();
653            } else if (isScopeVarSet()) {
654                // scope variable is set, store resource load bean in JSP context
655                CmsJspResourceLoadBean bean = new CmsJspResourceLoadBean(m_cms, m_collectorResult);
656                storeAttribute(bean);
657            }
658
659        } catch (CmsException e) {
660            m_controller.setThrowable(e, m_cms.getRequestContext().getUri());
661            throw new JspException(e);
662        }
663    }
664}