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.tools.content.propertyviewer;
029
030import org.opencms.file.CmsObject;
031import org.opencms.file.CmsProperty;
032import org.opencms.file.CmsResource;
033import org.opencms.file.CmsResourceFilter;
034import org.opencms.file.types.I_CmsResourceType;
035import org.opencms.i18n.CmsMessageContainer;
036import org.opencms.i18n.CmsMessages;
037import org.opencms.jsp.CmsJspActionElement;
038import org.opencms.main.CmsException;
039import org.opencms.main.CmsLog;
040import org.opencms.main.CmsRuntimeException;
041import org.opencms.main.OpenCms;
042import org.opencms.util.CmsStringUtil;
043import org.opencms.workplace.CmsWorkplace;
044import org.opencms.workplace.CmsWorkplaceSettings;
045import org.opencms.workplace.list.A_CmsListDialog;
046import org.opencms.workplace.list.CmsListColumnAlignEnum;
047import org.opencms.workplace.list.CmsListColumnDefinition;
048import org.opencms.workplace.list.CmsListItem;
049import org.opencms.workplace.list.CmsListMetadata;
050import org.opencms.workplace.list.CmsListOrderEnum;
051
052import java.io.FileNotFoundException;
053import java.io.IOException;
054import java.text.DecimalFormat;
055import java.text.NumberFormat;
056import java.util.ArrayList;
057import java.util.Collections;
058import java.util.LinkedList;
059import java.util.List;
060
061import javax.servlet.ServletException;
062import javax.servlet.http.HttpServletRequest;
063import javax.servlet.http.HttpServletResponse;
064import javax.servlet.jsp.PageContext;
065
066import org.apache.commons.logging.Log;
067
068/**
069 * A list that displays properties .
070 * <p>
071 *
072 * Caution: The list ID argument has to be dynamic to prevent caching causing exception in case of varying collumns.
073 * <p>
074 *
075 * @since 7.5.1
076 */
077public class CmsPropertyviewList extends A_CmsListDialog {
078
079    /** Used for ID column formatting. */
080    public static final NumberFormat ID_NUMBER_FORMAT = new DecimalFormat("00000");
081
082    /** list action id constant. */
083    public static final String LIST_ACTION_NONE = "an";
084
085    /** list column id constant. */
086    public static final String LIST_COLUMN_ICON = "lcic";
087
088    /** list column id constant. */
089    public static final String LIST_COLUMN_ID = "lcid";
090
091    /** list column id constant. */
092    public static final String LIST_COLUMN_PATH = "lcp";
093
094    /** list column id constant. */
095    public static final String LIST_COLUMN_PREFIX_PROPERTY = "cnp-";
096
097    /** List detail all properties info. */
098    public static final String LIST_DETAIL_ALL_PROPERTIES = "allpropertiesinfo";
099
100    /** list item detail id constant. */
101    public static final String LIST_DETAIL_FULLPATH = "df";
102
103    /** The request parameter for the properties to work on. */
104    public static final String PARAM_PROPERTIES = "props";
105
106    /** The request parameter for the property value to search for. */
107    public static final String PARAM_PROPERTY_VALUE = "propvalue";
108
109    /** The request parameter for the paths to work on. */
110    public static final String PARAM_RESOURCES = "paths";
111
112    /** The request parameter for the paths to work on. */
113    public static final String PARAM_SIBLINGS = "siblings";
114
115    /** The log object for this class. */
116    private static final Log LOG = CmsLog.getLog(CmsPropertyviewList.class);
117
118    /** Message for translation. */
119    private CmsMessages m_messages;
120
121    /** The paths. */
122    private String[] m_paths;
123
124    /** The properties. */
125    private String[] m_props;
126
127    /** The value of the properties to check. */
128    private String m_propvalue;
129
130    /** Flag for showing siblings. */
131    private boolean m_siblings;
132
133    /**
134     * Public constructor.
135     * <p>
136     *
137     * @param jsp an initialized JSP action element
138     *
139     * @throws CmsException if something goes wrong.
140     * @throws FileNotFoundException if something goes wrong.
141     */
142    public CmsPropertyviewList(CmsJspActionElement jsp)
143    throws FileNotFoundException, CmsException {
144
145        this(jsp, "proplist", Messages.get().container(Messages.GUI_LIST_PROPERTYVIEW_NAME_0));
146    }
147
148    /**
149     * Public constructor.
150     * <p>
151     * @param jsp an initialized JSP action element
152     * @param listId the id of the list
153     * @param listName the list name
154     *
155     * @throws CmsException if something goes wrong.
156     * @throws FileNotFoundException if something goes wrong.
157     */
158    public CmsPropertyviewList(CmsJspActionElement jsp, String listId, CmsMessageContainer listName)
159    throws FileNotFoundException, CmsException {
160
161        this(jsp, listId, listName, LIST_COLUMN_ID, CmsListOrderEnum.ORDER_ASCENDING, null);
162    }
163
164    /**
165     * Public constructor.
166     * <p>
167     * @param jsp an initialized JSP action element
168     * @param listId the id of the displayed list
169     * @param listName the name of the list
170     * @param sortedColId the a priory sorted column
171     * @param sortOrder the order of the sorted column
172     * @param searchableColId the column to search into
173     *
174     * @throws CmsException if something goes wrong.
175     * @throws FileNotFoundException if something goes wrong.
176     */
177    @SuppressWarnings("unused")
178    public CmsPropertyviewList(
179        CmsJspActionElement jsp,
180        String listId,
181        CmsMessageContainer listName,
182        String sortedColId,
183        CmsListOrderEnum sortOrder,
184        String searchableColId)
185        throws FileNotFoundException, CmsException {
186
187        super(jsp, listId, listName, sortedColId, sortOrder, searchableColId);
188        m_messages = new CmsMessages(
189            "org.opencms.workplace.tools.content.propertyviewer.messages",
190            jsp.getRequestContext().getLocale());
191    }
192
193    /**
194     * Public constructor.
195     * <p>
196     *
197     * Public constructor with JSP variables.
198     * <p>
199     * @param context the JSP page context
200     * @param req the JSP request
201     * @param res the JSP response
202     *
203     * @throws CmsException if something goes wrong.
204     * @throws FileNotFoundException if something goes wrong.
205     */
206    public CmsPropertyviewList(final PageContext context, final HttpServletRequest req, final HttpServletResponse res)
207    throws FileNotFoundException, CmsException {
208
209        this(new CmsJspActionElement(context, req, res));
210    }
211
212    /**
213     * @see org.opencms.workplace.list.A_CmsListDialog#executeListMultiActions()
214     */
215    @SuppressWarnings("unused")
216    @Override
217    public void executeListMultiActions() throws IOException, ServletException, CmsRuntimeException {
218
219        // nothing to do
220    }
221
222    /**
223     * @see org.opencms.workplace.list.A_CmsListDialog#executeListSingleActions()
224     */
225    @SuppressWarnings("unused")
226    @Override
227    public void executeListSingleActions() throws IOException, ServletException, CmsRuntimeException {
228
229        // nothing to do
230    }
231
232    /**
233     * @return the paths
234     */
235    public String getParamPaths() {
236
237        return CmsStringUtil.arrayAsString(m_paths, ",");
238    }
239
240    /**
241     * @return the props
242     */
243    public String getParamProps() {
244
245        return CmsStringUtil.arrayAsString(m_props, ",");
246    }
247
248    /**
249     * Returns the property value parameter.<p>
250     *
251     * @return the property value
252     *
253     */
254    public String getParamPropvalue() {
255
256        return m_propvalue;
257    }
258
259    /**
260     * Returns true if siblings are shown.
261     * <p>
262     *
263     * @return true if siblings are shown.
264     */
265    public String getParamSiblings() {
266
267        return Boolean.toString(m_siblings);
268    }
269
270    /**
271     * @param paths the paths to set
272     */
273    public void setParamPaths(final String paths) {
274
275        m_paths = CmsStringUtil.splitAsArray(paths, ',');
276    }
277
278    /**
279     * @param props the props to set
280     */
281    public void setParamProps(final String props) {
282
283        m_props = CmsStringUtil.splitAsArray(props, ',');
284    }
285
286    /**
287     * Sets the property value parameter.<p>
288     *
289     * @param propvalue the property value to set
290     */
291    public void setParamPropvalue(final String propvalue) {
292
293        m_propvalue = propvalue;
294    }
295
296    /**
297     * Set if siblings should be shown.
298     * <p>
299     *
300     * @param showSiblings if siblings should be shown.
301     */
302    public void setParamSiblings(final String showSiblings) {
303
304        m_siblings = Boolean.parseBoolean(showSiblings);
305    }
306
307    /**
308     * @see org.opencms.workplace.list.A_CmsListDialog#fillDetails(java.lang.String)
309     */
310    @Override
311    protected void fillDetails(final String detailId) {
312
313        // nothing to do
314    }
315
316    /**
317     * @see org.opencms.workplace.list.A_CmsListDialog#getListItems()
318     */
319    @Override
320    protected List<CmsListItem> getListItems() {
321
322        List<CmsListItem> result = new ArrayList<CmsListItem>();
323        // get content
324        CmsListItem item;
325        int idCounter = 0;
326        CmsObject cms = getCms();
327        for (CmsResource resource : getResources()) {
328            item = getList().newItem(resource.getRootPath());
329            if (fillItem(resource, item, false, idCounter)) {
330                // there is at least one property to display with content
331                idCounter++;
332                result.add(item);
333            }
334
335            if (m_siblings) {
336                try {
337                    List<CmsResource> siblings = cms.readSiblings(cms.getSitePath(resource), CmsResourceFilter.ALL);
338                    for (CmsResource sibling : siblings) {
339                        // Don't render siblings that are in the path:
340                        if (!isInPaths(sibling)) {
341                            item = getList().newItem(sibling.getRootPath());
342                            if (fillItem(sibling, item, true, idCounter)) {
343                                // there is at least one property to display with content
344                                idCounter++;
345                                result.add(item);
346                            }
347                        }
348                    }
349                } catch (CmsException e) {
350                    if (LOG.isErrorEnabled()) {
351                        LOG.error(
352                            Messages.get().getBundle().key(
353                                Messages.LOG_ERR_PROPERTYVIEWER_READSIBL_1,
354                                resource.getRootPath()),
355                            e);
356                    }
357                }
358            }
359        }
360        return result;
361    }
362
363    /**
364     * @see org.opencms.workplace.CmsWorkplace#initMessages()
365     */
366    @Override
367    protected void initMessages() {
368
369        // add specific dialog resource bundle
370        addMessages(Messages.get().getBundleName());
371        // add default resource bundles
372        super.initMessages();
373    }
374
375    /**
376     * @see org.opencms.workplace.list.A_CmsListDialog#initWorkplaceRequestValues(org.opencms.workplace.CmsWorkplaceSettings,
377     *      javax.servlet.http.HttpServletRequest)
378     */
379    @Override
380    protected void initWorkplaceRequestValues(final CmsWorkplaceSettings settings, final HttpServletRequest request) {
381
382        super.initWorkplaceRequestValues(settings, request);
383    }
384
385    /**
386     * @see org.opencms.workplace.list.A_CmsListDialog#listRecovery(java.lang.String)
387     */
388    @Override
389    protected synchronized void listRecovery(final String listId) {
390
391        // nothing to do
392    }
393
394    /**
395     * @see org.opencms.workplace.list.A_CmsListDialog#setColumns(org.opencms.workplace.list.CmsListMetadata)
396     */
397    @Override
398    protected void setColumns(CmsListMetadata metadata) {
399
400        // enforce re-invocation of this method because columns are varying and must not be cached:
401        metadata.setVolatile(true);
402
403        // add column for icon
404        CmsListColumnDefinition iconCol = new CmsListColumnDefinition(LIST_COLUMN_ICON);
405        iconCol.setName(Messages.get().container(Messages.GUI_LIST_PROPERTYVIEW_COL_ICON_NAME_0));
406        iconCol.setHelpText(Messages.get().container(Messages.GUI_LIST_PROPERTYVIEW_COL_ICON_HELP_0));
407        iconCol.setAlign(CmsListColumnAlignEnum.ALIGN_LEFT);
408        iconCol.setWidth("16");
409        iconCol.setSorteable(false);
410        metadata.addColumn(iconCol);
411        iconCol.setPrintable(true);
412
413        // add column for invisible ID (needed for sorting to show siblings below each other:
414        CmsListColumnDefinition idCol = new CmsListColumnDefinition(LIST_COLUMN_ID);
415        idCol.setName(Messages.get().container(Messages.GUI_LIST_PROPERTYVIEW_COL_ID_NAME_0));
416        idCol.setHelpText(Messages.get().container(Messages.GUI_LIST_PROPERTYVIEW_COL_ID_HELP_0));
417        idCol.setAlign(CmsListColumnAlignEnum.ALIGN_LEFT);
418        idCol.setSorteable(false);
419        idCol.setVisible(false);
420        metadata.addColumn(idCol);
421        idCol.setPrintable(true);
422
423        // add column for name
424        CmsListColumnDefinition nameCol = new CmsListColumnDefinition(LIST_COLUMN_PATH);
425        nameCol.setName(Messages.get().container(Messages.GUI_LIST_PROPERTYVIEW_COL_PATH_NAME_0));
426        nameCol.setHelpText(Messages.get().container(Messages.GUI_LIST_PROPERTYVIEW_COL_PATH_HELP_0));
427        nameCol.setAlign(CmsListColumnAlignEnum.ALIGN_LEFT);
428        nameCol.setSorteable(false);
429        metadata.addColumn(nameCol);
430        nameCol.setPrintable(true);
431
432        // add columns for properties:
433        CmsListColumnDefinition propCol;
434        for (String property : m_props) {
435            propCol = new CmsListColumnDefinition(getPropertyColumnID(property));
436            propCol.setName(
437                Messages.get().container(Messages.GUI_LIST_PROPERTYVIEW_COL_PROPERTY_NAME_1, new Object[] {property}));
438            propCol.setHelpText(Messages.get().container(Messages.GUI_LIST_PROPERTYVIEW_COL_PROPERTY_HELP_0));
439            propCol.setAlign(CmsListColumnAlignEnum.ALIGN_LEFT);
440            propCol.setSorteable(false);
441            metadata.addColumn(propCol);
442            propCol.setPrintable(true);
443        }
444
445    }
446
447    /**
448     * @see org.opencms.workplace.list.A_CmsListDialog#setIndependentActions(org.opencms.workplace.list.CmsListMetadata)
449     */
450    @Override
451    protected void setIndependentActions(CmsListMetadata metadata) {
452
453        // nothing to do here
454    }
455
456    /**
457     * @see org.opencms.workplace.list.A_CmsListDialog#setMultiActions(org.opencms.workplace.list.CmsListMetadata)
458     */
459    @Override
460    protected void setMultiActions(final CmsListMetadata metadata) {
461
462        // do nothing here
463    }
464
465    /**
466     * Fills a single item.
467     * <p>
468     * @param resource the corresponding resource.
469     * @param item the item to fill
470     * @param isSibling if false no boldface markup will be marked.
471     * @param id used for the ID column.
472     *
473     * @return true if the item contains at least one property with content, false if there is no property with content
474     */
475    private boolean fillItem(
476        final CmsResource resource,
477        final CmsListItem item,
478        final boolean isSibling,
479        final int id) {
480
481        item.set(LIST_COLUMN_ID, ID_NUMBER_FORMAT.format(id));
482        I_CmsResourceType type;
483        CmsObject cms = getCms();
484        String iconPath;
485        String pathValue;
486        String sitePath = cms.getSitePath(resource);
487        if (!isSibling) {
488            sitePath = "<b>" + sitePath + "</b>";
489        }
490        item.set(LIST_COLUMN_PATH, sitePath);
491        // flag is true, if there is at least one property to display with content
492        boolean onePropCont = false;
493        for (String property : m_props) {
494            CmsProperty prop;
495            try {
496
497                prop = cms.readPropertyObject(resource, property, false);
498                if (prop.isNullProperty()) {
499                    pathValue = m_messages.key("GUI_LIST_PROPERTYVIEW_NOTFOUND_0");
500                } else {
501                    pathValue = prop.getValue();
502                    if (CmsStringUtil.isNotEmptyOrWhitespaceOnly(getParamPropvalue())) {
503                        if (prop.getValue().contains(getParamPropvalue())) {
504                            onePropCont = true;
505                        }
506                    } else {
507                        onePropCont = true;
508                    }
509                }
510                item.set(getPropertyColumnID(property), pathValue);
511
512            } catch (CmsException e) {
513                if (LOG.isErrorEnabled()) {
514                    LOG.error(
515                        Messages.get().getBundle().key(
516                            Messages.LOG_ERR_PROPERTYVIEWER_READONEPROP_2,
517                            property,
518                            resource.getRootPath()),
519                        e);
520                }
521                item.set(getPropertyColumnID(property), "n/a");
522            }
523        }
524        // check if there is at least one property to display with content
525        if (!onePropCont) {
526            // there is no property to display with content
527            return false;
528        }
529
530        type = OpenCms.getResourceManager().getResourceType(resource);
531        iconPath = getSkinUri()
532            + CmsWorkplace.RES_PATH_FILETYPES
533            + OpenCms.getWorkplaceManager().getExplorerTypeSetting(type.getTypeName()).getIcon();
534        String iconImage;
535
536        if (isSibling) {
537            iconImage = "<div style=\"background-image: url("
538                + iconPath
539                + "); padding:0px;background-position: 0px; margin:0px; background-repeat: no-repeat;\">"
540                + "<img height=\"16\" border=\"0\" width=\"16\" src=\""
541                + getSkinUri()
542                + "/explorer/link.gif\"/></div>";
543        } else {
544            iconImage = "<img src=\"" + iconPath + "\" alt=\"icon\" />";
545        }
546        item.set(LIST_COLUMN_ICON, iconImage);
547        // there is at least one property to display with content
548        return true;
549    }
550
551    /**
552     * Returns the list column constant for the given property.
553     * <p>
554     * @param property the property that will be shown in the column.
555     *
556     * @return the list column constant for the given property.
557     */
558    private String getPropertyColumnID(final String property) {
559
560        String result = LIST_COLUMN_PREFIX_PROPERTY + property;
561        return result;
562    }
563
564    /**
565     * Internally reads the resources to use.<p>
566     *
567     * @return the resources to use.
568     */
569    private List<CmsResource> getResources() {
570
571        List<CmsResource> result = new LinkedList<CmsResource>();
572        CmsObject cms = getCms();
573        CmsResourceFilter filter = CmsResourceFilter.ALL;
574        try {
575            for (String path : m_paths) {
576                List<CmsResource> resources = cms.readResources(path, filter, true);
577                result.addAll(resources);
578            }
579        } catch (CmsException e) {
580            if (LOG.isErrorEnabled()) {
581                LOG.error(Messages.get().getBundle().key(Messages.LOG_ERR_PROPERTYVIEWER_READRESOURCES_0), e);
582            }
583            result = Collections.emptyList();
584        }
585        return result;
586    }
587
588    /**
589     * Checks if the resource is in the selected path.<p>
590     *
591     * @param resource the resource to check
592     *
593     * @return true, if the resource is in the selected path, otherwise false
594     */
595    private boolean isInPaths(final CmsResource resource) {
596
597        boolean result = false;
598        String resourcePath = getCms().getSitePath(resource);
599        for (String path : m_paths) {
600            if (resourcePath.startsWith(path)) {
601                result = true;
602                break;
603            }
604        }
605        return result;
606
607    }
608
609}