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, 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.galleries;
029
030import org.opencms.db.CmsResourceState;
031import org.opencms.file.CmsObject;
032import org.opencms.file.CmsProperty;
033import org.opencms.file.CmsPropertyDefinition;
034import org.opencms.file.CmsResource;
035import org.opencms.file.CmsResourceFilter;
036import org.opencms.file.types.CmsResourceTypeFolderExtended;
037import org.opencms.i18n.CmsMessageContainer;
038import org.opencms.json.JSONArray;
039import org.opencms.json.JSONException;
040import org.opencms.json.JSONObject;
041import org.opencms.jsp.CmsJspActionElement;
042import org.opencms.lock.CmsLock;
043import org.opencms.main.CmsException;
044import org.opencms.main.CmsLog;
045import org.opencms.main.CmsRuntimeException;
046import org.opencms.main.OpenCms;
047import org.opencms.relations.CmsCategory;
048import org.opencms.relations.CmsCategoryService;
049import org.opencms.security.CmsPermissionSet;
050import org.opencms.staticexport.CmsLinkManager;
051import org.opencms.util.CmsStringUtil;
052import org.opencms.workplace.CmsDialog;
053import org.opencms.workplace.CmsWorkplace;
054import org.opencms.workplace.CmsWorkplaceManager;
055import org.opencms.workplace.CmsWorkplaceSettings;
056
057import java.io.IOException;
058import java.util.ArrayList;
059import java.util.Collections;
060import java.util.Iterator;
061import java.util.List;
062import java.util.Map;
063import java.util.TreeMap;
064
065import javax.servlet.http.HttpServletRequest;
066import javax.servlet.http.HttpServletResponse;
067import javax.servlet.http.HttpSession;
068import javax.servlet.jsp.JspWriter;
069import javax.servlet.jsp.PageContext;
070
071import org.apache.commons.logging.Log;
072
073/**
074 * Provides the general helper methods to generate the content of a gallery dialog used in the XML content editors,
075 * WYSIWYG editors and context menu. It <p>
076 *
077 * It is also used for AJAX requests to dynamically switch galleries or categories and get additional information
078 * for the currently active item of the dialog.<p>
079 *
080 * Extend this class for every gallery type (e.g. image gallery or download gallery) to build.<p>
081 *
082 * @since 7.5.0
083 */
084public abstract class A_CmsAjaxGallery extends CmsDialog {
085
086    /** Request parameter value for the action: change the item link url value. */
087    public static final String DIALOG_CHANGEITEMLINKURL = "changeitemlinkurl";
088
089    /** Request parameter value for the action: change the item title property value. */
090    public static final String DIALOG_CHANGEITEMTITLE = "changeitemtitle";
091
092    /** Request parameter value for the action: get the currently active item object. */
093    public static final String DIALOG_GETACTIVEITEM = "getactiveitem";
094
095    /** Request parameter value for the action: get the category selection list. */
096    public static final String DIALOG_GETCATEGORIES = "getcategories";
097
098    /** Request parameter value for the action: get the gallery selection list. */
099    public static final String DIALOG_GETGALLERIES = "getgalleries";
100
101    /** Request parameter value for the action: get a specific gallery. */
102    public static final String DIALOG_GETGALLERY = "getgallery";
103
104    /** Request parameter value for the action: get the items for a gallery or category. */
105    public static final String DIALOG_GETITEMS = "getitems";
106
107    /** Request parameter value for the action: list gallery items. */
108    public static final String DIALOG_LIST = "list";
109
110    /** The list mode name "category" for getting the items. */
111    public static final String LISTMODE_CATEGORY = "category";
112
113    /** The list mode name "gallery" for getting the items. */
114    public static final String LISTMODE_GALLERY = "gallery";
115
116    /** Request parameter value for the dialog mode: editor. */
117    public static final String MODE_EDITOR = "editor";
118
119    /** Request parameter value for the dialog mode: view. */
120    public static final String MODE_VIEW = "view";
121
122    /** Request parameter value for the dialog mode: widget. */
123    public static final String MODE_WIDGET = "widget";
124
125    /** Request parameter name for the dialog mode (widget or editor). */
126    public static final String PARAM_DIALOGMODE = "dialogmode";
127
128    /** Request parameter name for the edited resource. */
129    public static final String PARAM_EDITEDRESOURCE = "editedresource";
130
131    /** Request parameter name for the input field id. */
132    public static final String PARAM_FIELDID = "fieldid";
133
134    /** Request parameter name for the gallery path. */
135    public static final String PARAM_GALLERYPATH = "gallerypath";
136
137    /** Request parameter name for the active item path. */
138    public static final String PARAM_ITEMPATH = "itempath";
139
140    /** Request parameter name for the dialog initialization parameters. */
141    public static final String PARAM_PARAMS = "params";
142
143    /** Request parameter name for the startup folder. */
144    public static final String PARAM_STARTUPFOLDER = "startupfolder";
145
146    /** Request parameter name for the startup type. */
147    public static final String PARAM_STARTUPTYPE = "startuptype";
148
149    /** The galleries path in the workplace containing the JSPs. */
150    public static final String PATH_GALLERIES = CmsWorkplace.VFS_PATH_WORKPLACE + "galleries/";
151
152    /** Value that is returned if no result was found. */
153    public static final String RETURNVALUE_NONE = "none";
154
155    /** The log object for this class. */
156    private static final Log LOG = CmsLog.getLog(A_CmsAjaxGallery.class);
157
158    /** The optional parameters for the gallery from the XML configuration. */
159    protected String m_galleryTypeParams;
160
161    /** The gallery items to display. */
162    private List<CmsResource> m_galleryItems;
163
164    /** The dialog mode the gallery is running in. */
165    private String m_paramDialogMode;
166
167    /** The input field id that is required when in widget mode. */
168    private String m_paramFieldId;
169
170    /** The current gallery path. */
171    private String m_paramGalleryPath;
172
173    /** The list mode to get the item either from a gallery or by a category. */
174    private String m_paramListMode;
175
176    /** The value of the property (current property definition: Title). */
177    private String m_paramPropertyValue;
178
179    /** The gallery base resource type. */
180    private CmsResourceTypeFolderExtended m_resourceType;
181
182    /**
183     * Public empty constructor, required for {@link A_CmsAjaxGallery#createInstance(String, CmsJspActionElement)}.<p>
184     */
185    public A_CmsAjaxGallery() {
186
187        this(null);
188    }
189
190    /**
191     * Public constructor with JSP action element.<p>
192     *
193     * @param jsp an initialized JSP action element
194     */
195    public A_CmsAjaxGallery(CmsJspActionElement jsp) {
196
197        super(jsp);
198        // perform other initialization
199        init();
200
201    }
202
203    /**
204     * Public constructor with JSP variables.<p>
205     *
206     * @param context the JSP page context
207     * @param req the JSP request
208     * @param res the JSP response
209     */
210    public A_CmsAjaxGallery(PageContext context, HttpServletRequest req, HttpServletResponse res) {
211
212        this(new CmsJspActionElement(context, req, res));
213    }
214
215    /**
216     * Creates a new gallery instance of the given gallery type name.<p>
217     *
218     * @param galleryTypeName the gallery type name to create the instance for
219     * @param jsp an initialized JSP action element
220     *
221     * @return a new gallery instance of the given gallery type name
222     */
223    public static A_CmsAjaxGallery createInstance(String galleryTypeName, CmsJspActionElement jsp) {
224
225        if (jsp != null) {
226            // must have a valid JSP in order to read from the user session
227            HttpSession session = jsp.getRequest().getSession();
228            // lookup the workplace settings
229            CmsWorkplaceSettings settings = (CmsWorkplaceSettings)session.getAttribute(
230                CmsWorkplaceManager.SESSION_WORKPLACE_SETTINGS);
231            if (settings != null) {
232                if (CmsStringUtil.isEmpty(galleryTypeName)) {
233                    // look up the gallery type from the settings
234                    galleryTypeName = settings.getGalleryType();
235                } else {
236                    // store the last used gallery type name
237                    settings.setGalleryType(galleryTypeName);
238                }
239            }
240        }
241        // get the gallery class name for the type
242        A_CmsAjaxGallery template = OpenCms.getWorkplaceManager().getGalleries().get(galleryTypeName);
243
244        if (template == null) {
245            // requested gallery type is not configured
246            CmsMessageContainer message;
247            if (jsp == null) {
248                message = Messages.get().container(Messages.LOG_UNKNOWN_GALLERY_TYPE_REQ_1, galleryTypeName);
249            } else {
250                message = Messages.get().container(
251                    Messages.LOG_UNKNOWN_GALLERY_TYPE_REQ_JSP_2,
252                    galleryTypeName,
253                    jsp.info("opencms.request.element.uri"));
254            }
255            LOG.error(message.key());
256            throw new CmsRuntimeException(message);
257        }
258
259        try {
260            // first get the class of the gallery
261            Class<?> galleryClass = Class.forName(template.getResourceType().getFolderClassName());
262            // create a new instance and cast to a gallery
263            A_CmsAjaxGallery galleryInstance = (A_CmsAjaxGallery)galleryClass.newInstance();
264            // set the type name and id
265            galleryInstance.m_resourceType = template.getResourceType();
266            galleryInstance.m_galleryTypeParams = template.getResourceType().getFolderClassParams();
267            // initialize the members
268            galleryInstance.initWorkplaceMembers(jsp);
269            // perform other initialization
270            galleryInstance.init();
271            // return the result
272            return galleryInstance;
273        } catch (Exception e) {
274            // requested type is not configured
275            CmsMessageContainer message;
276            if (jsp == null) {
277                message = Messages.get().container(
278                    Messages.LOG_CREATE_GALLERY_INSTANCE_FAILED_2,
279                    template.getResourceType().getFolderClassName(),
280                    galleryTypeName);
281            } else {
282                message = Messages.get().container(
283                    Messages.LOG_CREATE_GALLERY_INSTANCE_FAILED_JSP_3,
284                    template.getResourceType().getFolderClassName(),
285                    galleryTypeName,
286                    jsp.info("opencms.request.element.uri"));
287            }
288            LOG.error(message.key());
289            throw new CmsRuntimeException(message);
290        }
291    }
292
293    /**
294     * Returns a list of galleries which have the required gallery type id.<p>
295     *
296     * @param galleryTypeId type id of the gallery
297     * @param cms the initialized CmsObject for the current user
298     * @return a list of galleries
299     */
300    public static List<CmsResource> getGalleries(int galleryTypeId, CmsObject cms) {
301
302        List<CmsResource> galleries = new ArrayList<CmsResource>();
303        try {
304            // get the galleries of the current site
305            galleries = cms.readResources("/", CmsResourceFilter.ONLY_VISIBLE_NO_DELETED.addRequireType(galleryTypeId));
306        } catch (CmsException e) {
307            // error reading resources with filter
308            LOG.error(e.getLocalizedMessage(), e);
309        }
310
311        // if the current site is NOT the root site - add all other galleries from the system path
312        if (!cms.getRequestContext().getSiteRoot().equals("")) {
313            List<CmsResource> systemGalleries = null;
314            try {
315                // get the galleries in the /system/ folder
316                systemGalleries = cms.readResources(
317                    CmsWorkplace.VFS_PATH_SYSTEM,
318                    CmsResourceFilter.ONLY_VISIBLE_NO_DELETED.addRequireType(galleryTypeId));
319            } catch (CmsException e) {
320                // error reading resources with filter
321                LOG.error(e.getLocalizedMessage(), e);
322            }
323
324            if ((systemGalleries != null) && (systemGalleries.size() > 0)) {
325                // add the found system galleries to the result
326                galleries.addAll(systemGalleries);
327            }
328        }
329
330        // return the found galleries
331        return galleries;
332    }
333
334    /**
335     * Initializes the gallery dialog before redirecting.<p>
336     *
337     * @param wp the workplace object
338     */
339    public static void initGallery(CmsDialog wp) {
340
341        // 1. get "gallerytypename" by reading the folderpath
342        String galleryTypeName = null;
343        if (wp.useNewStyle()) {
344            galleryTypeName = CmsResource.getName(CmsResource.getFolderPath(wp.getAdminTool().getHandler().getLink()));
345        } else {
346            galleryTypeName = CmsResource.getName(CmsResource.getFolderPath(wp.getJsp().getRequestContext().getUri()));
347        }
348        if (galleryTypeName.endsWith("/")) {
349            galleryTypeName = galleryTypeName.substring(0, galleryTypeName.length() - 1);
350        }
351        if (!galleryTypeName.equals("commons")) {
352            // 2. set in user settings
353            wp.getSettings().setGalleryType(galleryTypeName);
354        }
355    }
356
357    /**
358     * Called from the JSP that is used for the AJAX requests to OpenCms.<p>
359     */
360    public void displayDialog() {
361
362        if (DIALOG_CHANGEITEMTITLE.equals(getParamAction())) {
363            // build the JSON object for the current item with changed title property
364            changeItemTitle(getJsp().getRequest().getParameter(PARAM_ITEMPATH));
365        } else if (DIALOG_CHANGEITEMLINKURL.equals(getParamAction())) {
366            // build the JSON object for the current item with changed resource content (CmsResourcePointer)
367            changeItemLinkUrl(getJsp().getRequest().getParameter(PARAM_ITEMPATH));
368        } else if (DIALOG_GETCATEGORIES.equals(getParamAction())) {
369            // get the available categories as JSON array
370            buildJsonCategoryList();
371        } else if (DIALOG_GETGALLERIES.equals(getParamAction())) {
372            // get the available galleries as JSON array
373            buildJsonGalleryList();
374        } else if (DIALOG_GETGALLERY.equals(getParamAction())) {
375            // get the desired gallery as JSON object
376            buildJsonGalleryItem(getJsp().getRequest().getParameter(PARAM_GALLERYPATH));
377        } else if (DIALOG_GETITEMS.equals(getParamAction())) {
378            if (LISTMODE_CATEGORY.equals(getParamListMode())) {
379                // get the items of selected category
380                buildJsonResourceItems(getCategoryItems(), null);
381            } else {
382                // get the items of a selected gallery
383                buildJsonResourceItems(getGalleryItems(), getParamGalleryPath());
384            }
385        } else if (DIALOG_GETACTIVEITEM.equals(getParamAction())) {
386            // build the JSON object for the currently active item
387            buildJsonActiveItem(getJsp().getRequest().getParameter(PARAM_ITEMPATH));
388        }
389    }
390
391    /**
392     * Returns a list of galleries which have the required gallery type id.<p>
393     *
394     * @return a list of galleries
395     */
396    public List<CmsResource> getGalleries() {
397
398        return getGalleries(getGalleryTypeId(), getCms());
399    }
400
401    /**
402     * Returns a list of gallery items (resources) for the currently selected gallery and resource type id.<p>
403     *
404     * @return a list of gallery items (resources)
405     */
406    public List<CmsResource> getGalleryItems() {
407
408        if (m_galleryItems == null) {
409            // gallery items have not been read yet
410            int resTypeId = getGalleryItemsTypeId();
411            if (CmsStringUtil.isNotEmpty(getParamGalleryPath())) {
412                try {
413                    // set last used gallery in settings to current gallery
414                    getSettings().setLastUsedGallery("" + getGalleryTypeId(), getParamGalleryPath());
415                    CmsResourceFilter filter;
416                    if (resTypeId == -1) {
417                        // filter all resources that are files
418                        filter = CmsResourceFilter.ONLY_VISIBLE_NO_DELETED.addRequireTimerange().addRequireFile();
419                    } else {
420                        // filter all resources of the required type
421                        filter = CmsResourceFilter.ONLY_VISIBLE_NO_DELETED.addRequireTimerange().addRequireType(
422                            resTypeId);
423                    }
424                    m_galleryItems = getCms().readResources(getParamGalleryPath(), filter, false);
425                } catch (CmsException e) {
426                    // error reading resources
427                    LOG.error(e.getLocalizedMessage(), e);
428                } catch (NullPointerException e) {
429                    // ignore this exception
430                }
431            }
432        }
433        return m_galleryItems;
434    }
435
436    /**
437     * Returns the type id of the gallery items that should be listed.<p>
438     *
439     * In case of downloadgallery use '-1' to list all resources excluding folders.<p>
440     *
441     * @return the type id of the gallery items that should be listed
442     */
443    public abstract int getGalleryItemsTypeId();
444
445    /**
446     * Returns the type id of this gallery instance.<p>
447     *
448     * @return the type id of this gallery instance
449     */
450    public abstract int getGalleryTypeId();
451
452    /**
453     * Returns the type name of this gallery instance.<p>
454     *
455     * @return the type name of this gallery instance
456     */
457    public abstract String getGalleryTypeName();
458
459    /**
460     * Returns the (optional) parameters of this gallery instance.<p>
461     *
462     * @return the (optional) parameters of this gallery instance
463     */
464    public String getGalleryTypeParams() {
465
466        return m_galleryTypeParams;
467    }
468
469    /**
470     * Returns the current mode of the dialog.<p>
471     *
472     * This is necessary to distinguish between widget mode, view mode and editor mode.<p>
473     *
474     * @return the current mode of the dialog
475     */
476    public String getParamDialogMode() {
477
478        if (m_paramDialogMode == null) {
479            return "";
480        }
481        return m_paramDialogMode;
482    }
483
484    /**
485     * Returns the input field ID when in widget mode.<p>
486     *
487     * @return the input field ID
488     */
489    public String getParamFieldId() {
490
491        return m_paramFieldId;
492    }
493
494    /**
495     * Returns the path of the gallery to display.<p>
496     *
497     * @return the path of the gallery to display
498     */
499    public String getParamGalleryPath() {
500
501        if (CmsStringUtil.isEmpty(m_paramGalleryPath)) {
502            m_paramGalleryPath = "";
503        }
504        return m_paramGalleryPath;
505    }
506
507    /**
508     * Returns the list mode for getting the items, either {@link #LISTMODE_CATEGORY} or {@link #LISTMODE_GALLERY}.<p>
509     *
510     * @return the list mode for getting the item
511     */
512    public String getParamListMode() {
513
514        return m_paramListMode;
515    }
516
517    /**
518     * Returns the property value parameter.<p>
519     *
520     * @return the property value parameter
521     */
522    public String getParamPropertyValue() {
523
524        return m_paramPropertyValue;
525    }
526
527    /**
528     * Returns the extended folder resource type this gallery is based on.<p>
529     *
530     * @return the extended folder resource type this gallery is based on
531     */
532    public CmsResourceTypeFolderExtended getResourceType() {
533
534        return m_resourceType;
535    }
536
537    /**
538     * Initialization method that is called after the gallery instance has been created.<p>
539     *
540     * It can be overwritten in the inherited class, e.g. {@link org.opencms.workplace.galleries.CmsAjaxImageGallery#init()}.<p>
541     */
542    public void init() {
543
544        // default gallery does not require initialization
545    }
546
547    /**
548     * Returns if the dialog mode is the "editor" mode.<p>
549     *
550     * @return <code>true</code> if the dialog mode is the "editor" mode, otherwise <code>false</code>
551     */
552    public boolean isModeEditor() {
553
554        return MODE_EDITOR.equals(getParamDialogMode());
555    }
556
557    /**
558     * Returns if the dialog mode is the "view" mode.<p>
559     *
560     * @return <code>true</code> if the dialog mode is the "view" mode, otherwise <code>false</code>
561     */
562    public boolean isModeView() {
563
564        return MODE_VIEW.equals(getParamDialogMode());
565    }
566
567    /**
568     * Returns if the dialog mode is the "widget" mode.<p>
569     *
570     * @return <code>true</code> if the dialog mode is the "editor" mode, otherwise <code>false</code>
571     */
572    public boolean isModeWidget() {
573
574        return MODE_WIDGET.equals(getParamDialogMode());
575    }
576
577    /**
578     * Sets the current mode of the dialog.<p>
579     *
580     * This is necessary to distinguish between widget mode and editor mode.<p>
581     *
582     * @param dialogMode the current mode of the dialog
583     */
584    public void setParamDialogMode(String dialogMode) {
585
586        m_paramDialogMode = dialogMode;
587    }
588
589    /**
590     * Sets the input field ID if in widget mode.<p>
591     *
592     * @param fieldId the input field ID
593     */
594    public void setParamFieldId(String fieldId) {
595
596        m_paramFieldId = fieldId;
597    }
598
599    /**
600     * Sets the path of the gallery to display.<p>
601     *
602     * @param galleryPath the path of the gallery to display
603     */
604    public void setParamGalleryPath(String galleryPath) {
605
606        m_paramGalleryPath = galleryPath;
607    }
608
609    /**
610     * Sets the list mode for getting the items, either {@link #LISTMODE_CATEGORY} or {@link #LISTMODE_GALLERY}.<p>
611     *
612     * @param paramListMode the list mode for getting the items
613     */
614    public void setParamListMode(String paramListMode) {
615
616        m_paramListMode = paramListMode;
617    }
618
619    /**
620     * Sets the property value parameter.<p>
621     *
622     * @param paramPropertyValue the property value parameter to set
623     */
624    public void setParamPropertyValue(String paramPropertyValue) {
625
626        m_paramPropertyValue = paramPropertyValue;
627    }
628
629    /**
630     * Sets the extended folder resource type this gallery is based on.<p>
631     *
632     * @param type the extended folder resource type this gallery is based on
633     */
634    public void setResourceType(CmsResourceTypeFolderExtended type) {
635
636        m_resourceType = type;
637    }
638
639    /**
640     * Builds the Javascript to set the currently active item object.<p>
641     *
642     * @param itemUrl the URL of the currently selected item
643     */
644    protected void buildJsonActiveItem(String itemUrl) {
645
646        if (itemUrl.startsWith(OpenCms.getSiteManager().getWorkplaceServer())) {
647            // remove workplace server prefix
648            itemUrl = itemUrl.substring(OpenCms.getSiteManager().getWorkplaceServer().length());
649        }
650        // remove context prefix to read resource from VFS
651        itemUrl = CmsLinkManager.removeOpenCmsContext(itemUrl);
652        try {
653            JspWriter out = getJsp().getJspContext().getOut();
654            if (getCms().existsResource(itemUrl)) {
655                try {
656                    out.print(buildJsonItemObject(getCms().readResource(itemUrl)));
657                } catch (CmsException e) {
658                    // can not happen in theory, because we used existsResource() before...
659                }
660            } else {
661                out.print(RETURNVALUE_NONE);
662            }
663        } catch (IOException e) {
664            if (LOG.isErrorEnabled()) {
665                LOG.error(e.getLocalizedMessage(), e);
666            }
667        }
668    }
669
670    /**
671     * Builds the JSON code for the category list as JSON array.<p>
672     */
673    protected void buildJsonCategoryList() {
674
675        CmsCategoryService catService = CmsCategoryService.getInstance();
676        List<CmsCategory> foundCategories = Collections.emptyList();
677        String editedResource = null;
678        if (CmsStringUtil.isNotEmpty(getParamResource())) {
679            editedResource = getParamResource();
680        }
681        try {
682            foundCategories = catService.readCategories(getCms(), "", true, editedResource);
683        } catch (CmsException e) {
684            // error reading categories
685        }
686
687        // the next lines sort the categories according to their path
688        Map<String, CmsCategory> sorted = new TreeMap<String, CmsCategory>();
689
690        Iterator<CmsCategory> i = foundCategories.iterator();
691        while (i.hasNext()) {
692            CmsCategory category = i.next();
693            String categoryPath = category.getPath();
694            if (sorted.get(categoryPath) != null) {
695                continue;
696            }
697            sorted.put(categoryPath, category);
698        }
699
700        foundCategories = new ArrayList<CmsCategory>(sorted.values());
701        JSONArray categories = new JSONArray();
702        i = foundCategories.iterator();
703        while (i.hasNext()) {
704            CmsCategory cat = i.next();
705
706            JSONObject jsonObj = new JSONObject();
707            try {
708                // 1: category title
709                jsonObj.put("title", cat.getTitle());
710                // 2: category path
711                jsonObj.put("path", cat.getPath());
712                // 3: category root path
713                jsonObj.put("rootpath", cat.getRootPath());
714                // 4 category level
715                jsonObj.put("level", CmsResource.getPathLevel(cat.getPath()));
716                // 4: active flag
717                jsonObj.put("active", false);
718                categories.put(jsonObj);
719            } catch (JSONException e) {
720                // TODO: error handling
721            }
722        }
723        JspWriter out = getJsp().getJspContext().getOut();
724        try {
725            out.print(categories.toString());
726        } catch (IOException e) {
727            if (LOG.isErrorEnabled()) {
728                LOG.error(e.getLocalizedMessage(), e);
729            }
730        }
731
732    }
733
734    /**
735     * Creates a JSON object with the information found on the given gallery URL.<p>
736     *
737     * @param galleryUrl the given gallery URL
738     */
739    protected void buildJsonGalleryItem(String galleryUrl) {
740
741        try {
742            JspWriter out = getJsp().getJspContext().getOut();
743            if (getCms().existsResource(galleryUrl)) {
744                JSONObject jsonObj = new JSONObject();
745                try {
746                    CmsResource res = getCms().readResource(galleryUrl);
747                    String path = getCms().getSitePath(res);
748                    // read the gallery title
749                    String title = getCms().readPropertyObject(
750                        res,
751                        CmsPropertyDefinition.PROPERTY_TITLE,
752                        false).getValue("");
753                    try {
754                        // 1: gallery title
755                        jsonObj.put("title", title);
756                        // 2: gallery path
757                        jsonObj.put("path", path);
758                        // 3: active flag
759                        jsonObj.put("active", true);
760                        out.print(jsonObj);
761                    } catch (JSONException e) {
762                        if (LOG.isErrorEnabled()) {
763                            LOG.error(e.getLocalizedMessage(), e);
764                        }
765                    }
766                } catch (CmsException e) {
767                    // error reading title property
768                    if (LOG.isErrorEnabled()) {
769                        LOG.error(e.getLocalizedMessage(), e);
770                    }
771                }
772            } else {
773                out.print(RETURNVALUE_NONE);
774            }
775        } catch (IOException e) {
776            if (LOG.isErrorEnabled()) {
777                LOG.error(e.getLocalizedMessage(), e);
778            }
779        }
780    }
781
782    /**
783     * Builds the JSON code for the gallery list as JSON array.<p>
784     */
785    protected void buildJsonGalleryList() {
786
787        String lastUsed = getSettings().getLastUsedGallery("" + getGalleryTypeId());
788        // check the value of last Used, if gallery is opened for the first time
789        if (CmsStringUtil.isEmpty(lastUsed)) {
790            // start gallery settings for this gallery type for the current user
791            String startGallerySetting = getSettings().getUserSettings().getStartGallery(
792                getGalleryTypeName(),
793                getCms());
794            if (startGallerySetting != null) {
795                // handle the case, "global settings" are selected
796                if (startGallerySetting.equals(CmsWorkplace.INPUT_DEFAULT)) {
797                    // get selected value from workplace xml settings
798                    String preselectedValue = OpenCms.getWorkplaceManager().getDefaultUserSettings().getStartGallery(
799                        getGalleryTypeName());
800                    if (preselectedValue != null) {
801                        startGallerySetting = preselectedValue;
802                    }
803                }
804                // checks if the resource exists
805                String sitePath = getCms().getRequestContext().removeSiteRoot(startGallerySetting);
806                if (getCms().existsResource(sitePath)) {
807                    lastUsed = sitePath;
808                }
809            }
810        }
811        JSONArray galleries = new JSONArray();
812        Iterator<CmsResource> i = getGalleries().iterator();
813        boolean isFirst = true;
814        while (i.hasNext()) {
815            CmsResource res = i.next();
816            String path = getCms().getSitePath(res);
817            JSONObject jsonObj = new JSONObject();
818            // 1: gallery title
819            String title = "";
820            try {
821                // read the gallery title
822                title = getCms().readPropertyObject(path, CmsPropertyDefinition.PROPERTY_TITLE, false).getValue("");
823            } catch (CmsException e) {
824                // error reading title property
825                if (LOG.isErrorEnabled()) {
826                    LOG.error(e.getLocalizedMessage(), e);
827                }
828            }
829            try {
830                jsonObj.put("title", title);
831                // 2: gallery path
832                jsonObj.put("path", path);
833                // 3: active flag
834                boolean active = false;
835                if ((CmsStringUtil.isEmpty(lastUsed) && isFirst) || path.equals(lastUsed)) {
836                    // TODO: adjust logic to get active gallery
837                    active = true;
838                }
839                jsonObj.put("active", active);
840                galleries.put(jsonObj);
841            } catch (JSONException e) {
842                if (LOG.isErrorEnabled()) {
843                    LOG.error(e.getLocalizedMessage(), e);
844                }
845            }
846            isFirst = false;
847        }
848        JspWriter out = getJsp().getJspContext().getOut();
849        try {
850            out.print(galleries.toString());
851        } catch (IOException e) {
852            if (LOG.isErrorEnabled()) {
853                LOG.error(e.getLocalizedMessage(), e);
854            }
855        }
856    }
857
858    /**
859     * Fills the JSON object with the information used for all resource types.<p>
860     *
861     * <ul>
862     * <li><code>sitepath</code>: site path to the resource.</li>
863     * <li><code>linkpath</code>: substituted url of the resource.</li>
864     * <li><code>title</code>: title property of the resource.</li>
865     * <li><code>size</code>: size of the resource in kb.</li>
866     * <li><code>datecreated</code>: the creation date of the resource.</li>
867     * <li><code>datelastmodified</code>: the modification date.</li>
868     * <li><code>state</code>: the state of the resource, new or changed.</li>
869     * <li><code>lockedby</code>: indicates if the resource is locked by another user.</li>
870     * <li><code>editable</code>: editable flag to determine if item is editable and can be lockes by the user.</li>
871     * <li><code>writepermission</code>: flag to indicate if the user has write permissions for given resource.</li>
872     * <li><code>directpublish</code>: flag to indicate if the user has write direct publish permission for given resource.</li>
873     * <li><code>description</code>: description property of the resource.</li>
874     * </ul>
875     *
876     * @param jsonObj containing information used by all possible resource
877     * @param res the resource to create the object from
878     * @param sitePath site path to the object
879     */
880    protected void buildJsonItemCommonPart(JSONObject jsonObj, CmsResource res, String sitePath) {
881
882        try {
883            // 1: file item site path
884            jsonObj.put("sitepath", sitePath);
885            // 2: substituted file item url
886            jsonObj.put("linkpath", getJsp().link(sitePath));
887            // 3: file item title
888            jsonObj.put(
889                "title",
890                CmsStringUtil.escapeJavaScript(
891                    getJsp().property(CmsPropertyDefinition.PROPERTY_TITLE, sitePath, res.getName())));
892            // 4: file size (in kb)
893            jsonObj.put(
894                "size",
895                (res.getLength() / 1024) + " " + key(org.opencms.workplace.galleries.Messages.GUI_LABEL_KILOBYTES_0));
896            // 5: file creation date (formatted)
897            jsonObj.put("datecreated", getMessages().getDateTime(res.getDateCreated()));
898            // 6: file modification date (formatted)
899            jsonObj.put("datelastmodified", getMessages().getDateTime(res.getDateLastModified()));
900            // 7: file state, if the item is new or changed
901            CmsResourceState state = res.getState();
902            CmsLock lock = CmsLock.getNullLock();
903            try {
904                // obtain current lock state to determine correct resource state and editable flag
905                lock = getCms().getLock(res);
906            } catch (CmsException e) {
907                // ignore, lock state could not be determined
908            }
909            if (!lock.isNullLock() && lock.getType().isPublish()) {
910                state = CmsResourceState.STATE_UNCHANGED;
911            }
912            jsonObj.put("state", state);
913            // 8: determine if the item is locked by another user
914            String locked = "";
915            if (!lock.isNullLock()
916                && !lock.getType().isPublish()
917                && !lock.getUserId().equals(getCms().getRequestContext().getCurrentUser().getId())) {
918                try {
919                    locked = getCms().readUser(lock.getUserId()).getName();
920                } catch (CmsException e) {
921                    // failed to read user, use ID as user name
922                    locked = lock.getUserId().toString();
923                }
924            }
925            jsonObj.put("lockedby", locked);
926            // 9: item editable flag to determine if item is editable (offline project and can be locked by the current user)
927            boolean editable = false;
928            // 10: item write permissions to determine if the user has write permission
929            boolean writePermission = false;
930            // 10: item direct publish flag to determine if the user has direct publish permission
931            boolean directPublishPermission = false;
932            try {
933                // test if the resource is in the offline project and it can be locked by the user
934                if (!getCms().getRequestContext().getCurrentProject().isOnlineProject()
935                    && lock.isLockableBy(getCms().getRequestContext().getCurrentUser())) {
936                    editable = true;
937                    // test if the user has the write permission
938                    if (getCms().hasPermissions(res, CmsPermissionSet.ACCESS_WRITE, false, CmsResourceFilter.ALL)) {
939                        writePermission = true;
940                    }
941                    // test if the user has direct publish permission
942                    if (getCms().hasPermissions(
943                        res,
944                        CmsPermissionSet.ACCESS_DIRECT_PUBLISH,
945                        false,
946                        CmsResourceFilter.ALL)) {
947                        directPublishPermission = true;
948                    }
949
950                }
951            } catch (CmsException e) {
952                if (LOG.isErrorEnabled()) {
953                    LOG.error(e.getLocalizedMessage(), e);
954                }
955            }
956            jsonObj.put("editable", editable);
957            jsonObj.put("writepermission", writePermission);
958            jsonObj.put("directpublish", directPublishPermission);
959            // 11: item description
960            String desc = getJsp().property(CmsPropertyDefinition.PROPERTY_DESCRIPTION, sitePath, "");
961            jsonObj.put("description", CmsStringUtil.escapeJavaScript(desc));
962        } catch (JSONException e) {
963            if (LOG.isErrorEnabled()) {
964                LOG.error(e.getLocalizedMessage(), e);
965            }
966        }
967    }
968
969    /**
970     * Returns a JSON object containing information of the given resource for usage in the gallery.<p>
971     *
972     * The content of the JSON object consists of a common and a specific part of the given resource.<p>
973     *
974     * @param res the resource to create the object from
975     * @return the JSON object containing information from the given resource
976     */
977    protected JSONObject buildJsonItemObject(CmsResource res) {
978
979        // create a new JSON object
980        JSONObject jsonObj = new JSONObject();
981        String sitePath = getCms().getRequestContext().getSitePath(res);
982        // fill JSON object with common information
983        buildJsonItemCommonPart(jsonObj, res, sitePath);
984        // fill JSON object with specific information
985        buildJsonItemSpecificPart(jsonObj, res, sitePath);
986
987        return jsonObj;
988    }
989
990    /**
991     * Fills the JSON object with the specific information used for this resource type.<p>
992     *
993     * @param jsonObj containing information used by all possible resource
994     * @param res the resource to create the object from
995     * @param sitePath site path to the object
996     */
997    protected abstract void buildJsonItemSpecificPart(JSONObject jsonObj, CmsResource res, String sitePath);
998
999    /**
1000     * Builds the JSON code to create items for the folder.<p>
1001     *
1002     * @param resourceitems the file resource to build the displayed items
1003     * @param parentFolder the parent folder of the collected files (for a gallery)
1004     */
1005    protected void buildJsonResourceItems(List<CmsResource> resourceitems, String parentFolder) {
1006
1007        if (resourceitems == null) {
1008            resourceitems = new ArrayList<CmsResource>();
1009        }
1010
1011        boolean isPublishEnabled = false;
1012        boolean hasDirectPublish = false;
1013        boolean hasWritePermission = false;
1014        if (CmsStringUtil.isNotEmpty(parentFolder)) {
1015            // check if there are changes in the currently selected gallery and the user has direct edit permissions
1016            try {
1017                if ((OpenCms.getPublishManager().getPublishList(
1018                    getCms(),
1019                    getCms().readResource(parentFolder),
1020                    false).size() > 0)) {
1021                    isPublishEnabled = true;
1022                }
1023            } catch (CmsException e) {
1024                // ignore, gallery can not be published
1025            }
1026            // check if the user has direst publish permissions,
1027            // used to enable the gallerypublish button, if user has enough permissions
1028            try {
1029                if (getCms().hasPermissions(
1030                    getCms().readResource(parentFolder),
1031                    CmsPermissionSet.ACCESS_DIRECT_PUBLISH,
1032                    false,
1033                    CmsResourceFilter.ALL)) {
1034                    hasDirectPublish = true;
1035                }
1036            } catch (CmsException e) {
1037                // ignore, no publish permissions for gallery
1038            }
1039            try {
1040                // check if the user has write permissions,
1041                // used to display the upload buttons
1042                if (getCms().hasPermissions(
1043                    getCms().readResource(parentFolder),
1044                    CmsPermissionSet.ACCESS_WRITE,
1045                    false,
1046                    CmsResourceFilter.ALL)) {
1047                    hasWritePermission = true;
1048                }
1049            } catch (CmsException e) {
1050                // ignore, no write permissions for gallery
1051            }
1052        }
1053        JSONObject publishInfo = new JSONObject();
1054        try {
1055            publishInfo.put("publishable", isPublishEnabled);
1056            publishInfo.put("directpublish", hasDirectPublish);
1057            publishInfo.put("writepermission", hasWritePermission);
1058        } catch (JSONException e) {
1059            // ignore
1060        }
1061        JSONArray items = new JSONArray();
1062        items.put(publishInfo);
1063        Iterator<CmsResource> i = resourceitems.iterator();
1064        while (i.hasNext()) {
1065            CmsResource res = i.next();
1066            // build a JSON object from the item and add it to the list
1067            items.put(buildJsonItemObject(res));
1068        }
1069        JspWriter out = getJsp().getJspContext().getOut();
1070        try {
1071            // print the JSON array
1072            out.print(items.toString());
1073        } catch (IOException e) {
1074            if (LOG.isErrorEnabled()) {
1075                LOG.error(e.getLocalizedMessage(), e);
1076            }
1077        }
1078    }
1079
1080    /**
1081     * Changes the content of the CmsResource.<p>
1082     * This function should be overwritten in {@link org.opencms.workplace.galleries.CmsAjaxLinkGallery}.<p>
1083     *
1084     * @param itemUrl the item URL
1085     *
1086     */
1087    protected void changeItemLinkUrl(String itemUrl) {
1088
1089        // the most galleries do not provide this method
1090
1091    }
1092
1093    /**
1094     * Changes the title property value of the given item.<p>
1095     *
1096     * @param itemUrl the item URL on which the title is changed
1097     */
1098    protected void changeItemTitle(String itemUrl) {
1099
1100        try {
1101            JspWriter out = getJsp().getJspContext().getOut();
1102            if (getCms().existsResource(itemUrl)) {
1103                try {
1104                    writeTitleProperty(getCms().readResource(itemUrl));
1105                    out.print(buildJsonItemObject(getCms().readResource(itemUrl)));
1106                } catch (CmsException e) {
1107                    // can not happen in theory, because we used existsResource() before...
1108                }
1109            } else {
1110                out.print(RETURNVALUE_NONE);
1111            }
1112        } catch (IOException e) {
1113            if (LOG.isErrorEnabled()) {
1114                LOG.error(e.getLocalizedMessage(), e);
1115            }
1116        }
1117    }
1118
1119    /**
1120     * Returns the resource items for the selected category.<p>
1121     *
1122     * @return the resource items for the selected category
1123     */
1124    protected List<CmsResource> getCategoryItems() {
1125
1126        List<CmsResource> result = Collections.emptyList();
1127        if (CmsStringUtil.isNotEmpty(getParamGalleryPath())) {
1128            try {
1129                CmsCategoryService service = CmsCategoryService.getInstance();
1130                // get the edited resource if present
1131                String editedResource = "/";
1132                if (CmsStringUtil.isNotEmpty(getParamResource())) {
1133                    editedResource = CmsResource.getFolderPath(getParamResource());
1134                }
1135                // read the matching resources for the category
1136                result = service.readCategoryResources(getCms(), getParamGalleryPath(), true, editedResource);
1137                // filter the matched resources to get only the specific items as result
1138                int resTypeId = getGalleryItemsTypeId();
1139                if (resTypeId != -1) {
1140                    List<CmsResource> unfiltered = new ArrayList<CmsResource>(result);
1141                    result = new ArrayList<CmsResource>(unfiltered.size());
1142                    Iterator<CmsResource> i = unfiltered.iterator();
1143                    while (i.hasNext()) {
1144                        CmsResource res = i.next();
1145                        if (res.getTypeId() == resTypeId) {
1146                            result.add(res);
1147                        }
1148                    }
1149                }
1150            } catch (CmsException e) {
1151                // error reading resources
1152                if (LOG.isErrorEnabled()) {
1153                    LOG.error(e.getLocalizedMessage(), e);
1154                }
1155            } catch (NullPointerException e) {
1156                // ignore this exception
1157                if (LOG.isErrorEnabled()) {
1158                    LOG.error(e.getLocalizedMessage(), e);
1159                }
1160            }
1161        }
1162        return result;
1163    }
1164
1165    /**
1166     * @see org.opencms.workplace.CmsWorkplace#initWorkplaceRequestValues(org.opencms.workplace.CmsWorkplaceSettings, javax.servlet.http.HttpServletRequest)
1167     */
1168    @Override
1169    protected void initWorkplaceRequestValues(CmsWorkplaceSettings settings, HttpServletRequest request) {
1170
1171        // fill the parameter values in the get/set methods
1172        fillParamValues(request);
1173        // set the dialog type
1174        setParamDialogtype(LISTMODE_GALLERY);
1175        if (CmsStringUtil.isEmpty(getParamGalleryPath())) {
1176            String lastUsedGallery = getSettings().getLastUsedGallery("" + getGalleryTypeId());
1177            if (CmsStringUtil.isNotEmpty(lastUsedGallery)) {
1178                // set the resourcepath of the last used gallery if the resource is not deleted
1179                try {
1180                    getCms().readResource(lastUsedGallery, CmsResourceFilter.ONLY_VISIBLE_NO_DELETED);
1181                    setParamGalleryPath(lastUsedGallery);
1182                } catch (CmsException e) {
1183                    // reading the last used gallery failed, may be deleted
1184                }
1185            }
1186        }
1187    }
1188
1189    /**
1190     * Changes the value of the property title for the specified resource.<p>
1191     *
1192     * @param res the resource to change the property value
1193     */
1194    protected void writeTitleProperty(CmsResource res) {
1195
1196        String resPath = getCms().getSitePath(res);
1197        String currentPropertyValue = getParamPropertyValue();
1198        try {
1199            CmsProperty currentProperty = getCms().readPropertyObject(
1200                resPath,
1201                CmsPropertyDefinition.PROPERTY_TITLE,
1202                false);
1203            // detect if property is a null property or not
1204            if (currentProperty.isNullProperty()) {
1205                // create new property object and set key and value
1206                currentProperty = new CmsProperty();
1207                currentProperty.setName(CmsPropertyDefinition.PROPERTY_TITLE);
1208                if (OpenCms.getWorkplaceManager().isDefaultPropertiesOnStructure()) {
1209                    // set structure value
1210                    currentProperty.setStructureValue(currentPropertyValue);
1211                    currentProperty.setResourceValue(null);
1212                } else {
1213                    // set resource value
1214                    currentProperty.setStructureValue(null);
1215                    currentProperty.setResourceValue(currentPropertyValue);
1216                }
1217            } else if (currentProperty.getStructureValue() != null) {
1218                // structure value has to be updated
1219                currentProperty.setStructureValue(currentPropertyValue);
1220                currentProperty.setResourceValue(null);
1221            } else {
1222                // resource value has to be updated
1223                currentProperty.setStructureValue(null);
1224                currentProperty.setResourceValue(currentPropertyValue);
1225            }
1226            CmsLock lock = getCms().getLock(res);
1227            if (lock.isUnlocked()) {
1228                // lock resource before operation
1229                getCms().lockResource(resPath);
1230            }
1231            // write the property to the resource
1232            getCms().writePropertyObject(resPath, currentProperty);
1233            // unlock the resource
1234            getCms().unlockResource(resPath);
1235        } catch (CmsException e) {
1236            // writing the property failed, log error
1237            LOG.error(e.getLocalizedMessage(), e);
1238        }
1239    }
1240}