001/*
002 * This library is part of OpenCms -
003 * the Open Source Content Management System
004 *
005 * Copyright (c) Alkacon Software GmbH (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, 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.widgets;
029
030import org.opencms.ade.configuration.CmsADEConfigData;
031import org.opencms.ade.configuration.CmsResourceTypeConfig;
032import org.opencms.ade.galleries.shared.I_CmsGalleryProviderConstants;
033import org.opencms.ade.galleries.shared.I_CmsGalleryProviderConstants.GalleryMode;
034import org.opencms.file.CmsObject;
035import org.opencms.file.CmsResource;
036import org.opencms.file.types.CmsResourceTypeBinary;
037import org.opencms.file.types.CmsResourceTypeImage;
038import org.opencms.file.types.CmsResourceTypePlain;
039import org.opencms.i18n.CmsMessages;
040import org.opencms.json.JSONException;
041import org.opencms.json.JSONObject;
042import org.opencms.main.CmsLog;
043import org.opencms.main.OpenCms;
044import org.opencms.util.CmsMacroResolver;
045import org.opencms.util.CmsStringUtil;
046import org.opencms.workplace.CmsWorkplace;
047import org.opencms.xml.content.I_CmsXmlContentHandler.DisplayType;
048import org.opencms.xml.types.A_CmsXmlContentValue;
049
050import java.util.List;
051import java.util.Locale;
052import java.util.Set;
053
054import org.apache.commons.collections.Factory;
055import org.apache.commons.logging.Log;
056
057import com.google.common.base.Objects;
058
059/**
060 * Provides a OpenCms VFS file selection widget, for use on a widget dialog.<p>
061 * 
062 * @since 6.0.0 
063 */
064public class CmsVfsFileWidget extends A_CmsWidget implements I_CmsADEWidget {
065
066    /** Macro resolver factory to get the default searchable types. */
067    protected class SearchTypesFactory implements Factory {
068
069        /** The CMS context. */
070        private CmsObject m_cms;
071
072        /** The resource. */
073        private CmsResource m_resource;
074
075        /**
076         * Constructor.<p>
077         * 
078         * @param cms the CMS context
079         * @param resource the resource
080         */
081        public SearchTypesFactory(CmsObject cms, CmsResource resource) {
082
083            m_cms = cms;
084            m_resource = resource;
085        }
086
087        /**
088         * @see org.apache.commons.collections.Factory#create()
089         */
090        public Object create() {
091
092            return getDefaultSearchTypes(m_cms, m_resource);
093        }
094    }
095
096    /** Configuration parameter to set the flag to include files in popup resource tree. */
097    public static final String CONFIGURATION_EXCLUDEFILES = "excludefiles";
098
099    /** Configuration parameter to set the flag to show the site selector in popup resource tree. */
100    public static final String CONFIGURATION_HIDESITESELECTOR = "hidesiteselector";
101
102    /** Configuration parameter to set the flag to include files in popup resource tree. */
103    public static final String CONFIGURATION_INCLUDEFILES = "includefiles";
104
105    /** Configuration parameter to prevent the project awareness flag in the popup resource tree. */
106    public static final String CONFIGURATION_NOTPROJECTAWARE = "notprojectaware";
107
108    /** Configuration parameter to set the project awareness flag in the popup resource tree. */
109    public static final String CONFIGURATION_PROJECTAWARE = "projectaware";
110
111    /** Configuration parameter to set search types of the gallery widget. */
112    public static final String CONFIGURATION_SEARCHTYPES = "searchtypes";
113
114    /** Configuration parameter to set the selectable types of the gallery widget. */
115    public static final String CONFIGURATION_SELECTABLETYPES = "selectabletypes";
116
117    /** Configuration parameter to set the flag to show the site selector in popup resource tree. */
118    public static final String CONFIGURATION_SHOWSITESELECTOR = "showsiteselector";
119
120    /** Configuration parameter to set start folder. */
121    public static final String CONFIGURATION_STARTFOLDER = "startfolder";
122
123    /** Configuration parameter to set start site of the popup resource tree. */
124    public static final String CONFIGURATION_STARTSITE = "startsite";
125
126    /** The default search types macro name. */
127    public static final String DEFAULT_SEARCH_TYPES_MACRO = "defaultSearchTypes";
128
129    /** The logger instance for this class. */
130    private static final Log LOG = CmsLog.getLog(CmsVfsFileWidget.class);
131
132    /** Flag to determine if files should be shown in popup window. */
133    private boolean m_includeFiles;
134
135    /** Flag to determine project awareness, ie. if resources outside of the current project should be displayed as normal. */
136    private boolean m_projectAware;
137
138    /** The type shown in the gallery types tab. */
139    private String m_searchTypes;
140
141    /** The types that may be selected through the gallery widget. */
142    private String m_selectableTypes;
143
144    /** Flag to determine if the site selector should be shown in popup window. */
145    private boolean m_showSiteSelector;
146
147    /** The start folder. */
148    private String m_startFolder;
149
150    /** The start site used in the popup window. */
151    private String m_startSite;
152
153    /**
154     * Creates a new vfs file widget.<p>
155     */
156    public CmsVfsFileWidget() {
157
158        // empty constructor is required for class registration
159        this("");
160    }
161
162    /**
163     * Creates a new vfs file widget with the parameters to configure the popup tree window behavior.<p>
164     * 
165     * @param showSiteSelector true if the site selector should be shown in the popup window
166     * @param startSite the start site root for the popup window
167     */
168    public CmsVfsFileWidget(boolean showSiteSelector, String startSite) {
169
170        this(showSiteSelector, startSite, true);
171    }
172
173    /**
174     * Creates a new vfs file widget with the parameters to configure the popup tree window behavior.<p>
175     * 
176     * @param showSiteSelector true if the site selector should be shown in the popup window
177     * @param startSite the start site root for the popup window
178     * @param includeFiles true if files should be shown in the popup window
179     */
180    public CmsVfsFileWidget(boolean showSiteSelector, String startSite, boolean includeFiles) {
181
182        this(showSiteSelector, startSite, includeFiles, true);
183    }
184
185    /**
186     * Creates a new vfs file widget with the parameters to configure the popup tree window behavior.<p>
187     * 
188     * @param showSiteSelector true if the site selector should be shown in the popup window
189     * @param startSite the start site root for the popup window
190     * @param includeFiles <code>true</code> if files should be shown in the popup window
191     * @param projectAware <code>true</code> if resources outside of the current project should be displayed as normal
192     */
193    public CmsVfsFileWidget(boolean showSiteSelector, String startSite, boolean includeFiles, boolean projectAware) {
194
195        m_showSiteSelector = showSiteSelector;
196        m_startSite = startSite;
197        m_includeFiles = includeFiles;
198        m_projectAware = projectAware;
199    }
200
201    /**
202     * Creates a new vfs file widget with the given configuration.<p>
203     * 
204     * @param configuration the configuration to use
205     */
206    public CmsVfsFileWidget(String configuration) {
207
208        super(configuration);
209    }
210
211    /**
212     * Returns a comma separated list of the default search type names.<p>
213     * 
214     * @param cms the CMS context
215     * @param resource the edited resource
216     * 
217     * @return a comma separated list of the default search type names 
218     */
219    protected static String getDefaultSearchTypes(CmsObject cms, CmsResource resource) {
220
221        StringBuffer result = new StringBuffer();
222        String referenceSitePath = cms.getSitePath(resource);
223        CmsADEConfigData config = OpenCms.getADEManager().lookupConfiguration(
224            cms,
225            cms.getRequestContext().addSiteRoot(cms.getRequestContext().getUri()));
226        Set<String> detailPageTypes = OpenCms.getADEManager().getDetailPageTypes(cms);
227        for (CmsResourceTypeConfig typeConfig : config.getResourceTypes()) {
228            String typeName = typeConfig.getTypeName();
229            if (!detailPageTypes.contains(typeName)) {
230                continue;
231            }
232            if (typeConfig.checkViewable(cms, referenceSitePath)) {
233                result.append(typeName).append(",");
234            }
235        }
236        result.append(CmsResourceTypeBinary.getStaticTypeName()).append(",");
237        result.append(CmsResourceTypeImage.getStaticTypeName()).append(",");
238        result.append(CmsResourceTypePlain.getStaticTypeName());
239        return result.toString();
240    }
241
242    /**
243     * @see org.opencms.widgets.A_CmsWidget#getConfiguration()
244     */
245    @Override
246    public String getConfiguration() {
247
248        StringBuffer result = new StringBuffer(8);
249
250        // append site selector flag to configuration
251        if (m_showSiteSelector) {
252            result.append(CONFIGURATION_SHOWSITESELECTOR);
253        } else {
254            result.append(CONFIGURATION_HIDESITESELECTOR);
255        }
256
257        // append start site to configuration
258        if (m_startSite != null) {
259            result.append("|");
260            result.append(CONFIGURATION_STARTSITE);
261            result.append("=");
262            result.append(m_startSite);
263        }
264
265        // append flag for including files
266        result.append("|");
267        if (m_includeFiles) {
268            result.append(CONFIGURATION_INCLUDEFILES);
269        } else {
270            result.append(CONFIGURATION_EXCLUDEFILES);
271        }
272
273        // append flag for project awareness
274        result.append("|");
275        if (m_projectAware) {
276            result.append(CONFIGURATION_PROJECTAWARE);
277        } else {
278            result.append(CONFIGURATION_NOTPROJECTAWARE);
279        }
280        if (m_searchTypes != null) {
281            result.append("|");
282            result.append(CONFIGURATION_SEARCHTYPES);
283            result.append("=");
284            result.append(m_searchTypes);
285        }
286        if (m_selectableTypes != null) {
287            result.append("|");
288            result.append(CONFIGURATION_SELECTABLETYPES);
289            result.append("=");
290            result.append(m_selectableTypes);
291        }
292        return result.toString();
293    }
294
295    /**
296     * @see org.opencms.widgets.I_CmsADEWidget#getConfiguration(org.opencms.file.CmsObject, org.opencms.xml.types.A_CmsXmlContentValue, org.opencms.i18n.CmsMessages, org.opencms.file.CmsResource, java.util.Locale)
297     */
298    public String getConfiguration(
299        CmsObject cms,
300        A_CmsXmlContentValue schemaType,
301        CmsMessages messages,
302        CmsResource resource,
303        Locale contentLocale) {
304
305        JSONObject config = getJsonConfig(cms, schemaType, messages, resource, contentLocale);
306        return config.toString();
307    }
308
309    /**
310     * @see org.opencms.widgets.I_CmsADEWidget#getCssResourceLinks(org.opencms.file.CmsObject)
311     */
312    public List<String> getCssResourceLinks(CmsObject cms) {
313
314        return null;
315    }
316
317    /**
318     * @see org.opencms.widgets.I_CmsADEWidget#getDefaultDisplayType()
319     */
320    public DisplayType getDefaultDisplayType() {
321
322        return DisplayType.wide;
323    }
324
325    /**
326     * @see org.opencms.widgets.I_CmsWidget#getDialogIncludes(org.opencms.file.CmsObject, org.opencms.widgets.I_CmsWidgetDialog)
327     */
328    @Override
329    public String getDialogIncludes(CmsObject cms, I_CmsWidgetDialog widgetDialog) {
330
331        StringBuffer result = new StringBuffer(16);
332        result.append(getJSIncludeFile(CmsWorkplace.getSkinUri() + "commons/tree.js"));
333        result.append("\n");
334        result.append(getJSIncludeFile(CmsWorkplace.getSkinUri() + "components/widgets/fileselector.js"));
335        return result.toString();
336    }
337
338    /**
339     * @see org.opencms.widgets.I_CmsWidget#getDialogInitCall(org.opencms.file.CmsObject, org.opencms.widgets.I_CmsWidgetDialog)
340     */
341    @Override
342    public String getDialogInitCall(CmsObject cms, I_CmsWidgetDialog widgetDialog) {
343
344        return "\tinitVfsFileSelector();\n";
345    }
346
347    /**
348     * @see org.opencms.widgets.I_CmsWidget#getDialogInitMethod(org.opencms.file.CmsObject, org.opencms.widgets.I_CmsWidgetDialog)
349     */
350    @Override
351    public String getDialogInitMethod(CmsObject cms, I_CmsWidgetDialog widgetDialog) {
352
353        StringBuffer result = new StringBuffer(16);
354        result.append("function initVfsFileSelector() {\n");
355        //initialize tree javascript, does parts of <code>CmsTree.initTree(CmsObject, encoding, skinuri);</code>
356        result.append("\tinitResources(\"");
357        result.append(OpenCms.getWorkplaceManager().getEncoding());
358        result.append("\", \"");
359        result.append(CmsWorkplace.VFS_PATH_WORKPLACE);
360        result.append("\", \"");
361        result.append(CmsWorkplace.getSkinUri());
362        result.append("\", \"");
363        result.append(OpenCms.getSystemInfo().getOpenCmsContext());
364        result.append("\");\n");
365        result.append("}\n");
366        return result.toString();
367    }
368
369    /**
370     * @see org.opencms.widgets.I_CmsWidget#getDialogWidget(org.opencms.file.CmsObject, org.opencms.widgets.I_CmsWidgetDialog, org.opencms.widgets.I_CmsWidgetParameter)
371     */
372    public String getDialogWidget(CmsObject cms, I_CmsWidgetDialog widgetDialog, I_CmsWidgetParameter param) {
373
374        String id = param.getId();
375        StringBuffer result = new StringBuffer(128);
376
377        result.append("<td class=\"xmlTd\">");
378        result.append("<table border=\"0\" cellpadding=\"0\" cellspacing=\"0\" class=\"maxwidth\"><tr><td style=\"width: 100%;\">");
379        result.append("<input style=\"width: 99%;\" class=\"xmlInput");
380        if (param.hasError()) {
381            result.append(" xmlInputError");
382        }
383        result.append("\" value=\"");
384        result.append(param.getStringValue(cms));
385        result.append("\" name=\"");
386        result.append(id);
387        result.append("\" id=\"");
388        result.append(id);
389        result.append("\"></td>");
390        result.append(widgetDialog.dialogHorizontalSpacer(10));
391        result.append("<td><table class=\"editorbuttonbackground\" border=\"0\" cellpadding=\"0\" cellspacing=\"0\"><tr>");
392
393        StringBuffer buttonJs = new StringBuffer(8);
394        buttonJs.append("javascript:openTreeWin('EDITOR',  '");
395        buttonJs.append(id);
396        buttonJs.append("', document, ");
397        buttonJs.append(m_showSiteSelector);
398        buttonJs.append(", '");
399        if (m_startSite != null) {
400            buttonJs.append(m_startSite);
401        } else {
402            buttonJs.append(cms.getRequestContext().getSiteRoot());
403        }
404        buttonJs.append("', ");
405        // include files
406        buttonJs.append(m_includeFiles);
407        // project awareness
408        buttonJs.append(", ");
409        buttonJs.append(m_projectAware);
410        buttonJs.append(");return false;");
411
412        result.append(widgetDialog.button(
413            buttonJs.toString(),
414            null,
415            "folder",
416            org.opencms.workplace.Messages.GUI_DIALOG_BUTTON_SEARCH_0,
417            widgetDialog.getButtonStyle()));
418        result.append("</tr></table>");
419        result.append("</td></tr></table>");
420
421        result.append("</td>");
422
423        return result.toString();
424    }
425
426    /**
427     * @see org.opencms.widgets.I_CmsADEWidget#getInitCall()
428     */
429    public String getInitCall() {
430
431        return null;
432    }
433
434    /**
435     * @see org.opencms.widgets.I_CmsADEWidget#getJavaScriptResourceLinks(org.opencms.file.CmsObject)
436     */
437    public List<String> getJavaScriptResourceLinks(CmsObject cms) {
438
439        return null;
440    }
441
442    /**
443     * Returns the start site root shown by the widget when first displayed.<p>
444     *
445     * If <code>null</code> is returned, the dialog will display the current site of 
446     * the current user.<p>
447     *
448     * @return the start site root shown by the widget when first displayed
449     */
450    public String getStartSite() {
451
452        return m_startSite;
453    }
454
455    /**
456     * @see org.opencms.widgets.I_CmsADEWidget#getWidgetName()
457     */
458    public String getWidgetName() {
459
460        return CmsVfsFileWidget.class.getName();
461    }
462
463    /**
464     * @see org.opencms.widgets.I_CmsADEWidget#isInternal()
465     */
466    public boolean isInternal() {
467
468        return true;
469    }
470
471    /**
472     * Returns <code>true</code> if the site selector is shown.<p>
473     *
474     * The default is <code>true</code>.<p>
475     *
476     * @return <code>true</code> if the site selector is shown
477     */
478    public boolean isShowingSiteSelector() {
479
480        return m_showSiteSelector;
481    }
482
483    /**
484     * @see org.opencms.widgets.I_CmsWidget#newInstance()
485     */
486    public I_CmsWidget newInstance() {
487
488        return new CmsVfsFileWidget(getConfiguration());
489    }
490
491    /**
492     * @see org.opencms.widgets.A_CmsWidget#setConfiguration(java.lang.String)
493     */
494    @Override
495    public void setConfiguration(String configuration) {
496
497        m_showSiteSelector = true;
498        m_includeFiles = true;
499        m_projectAware = true;
500
501        if (CmsStringUtil.isNotEmptyOrWhitespaceOnly(configuration)) {
502            if (configuration.contains(CONFIGURATION_HIDESITESELECTOR)) {
503                // site selector should be hidden
504                m_showSiteSelector = false;
505            }
506            int siteIndex = configuration.indexOf(CONFIGURATION_STARTSITE);
507            if (siteIndex != -1) {
508                // start site is given
509                String site = configuration.substring(siteIndex + CONFIGURATION_STARTSITE.length() + 1);
510                if (site.indexOf('|') != -1) {
511                    // cut eventual following configuration values
512                    site = site.substring(0, site.indexOf('|'));
513                }
514                m_startSite = site;
515            }
516            if (configuration.contains(CONFIGURATION_EXCLUDEFILES)) {
517                // files should not be included
518                m_includeFiles = false;
519            }
520            if (configuration.contains(CONFIGURATION_NOTPROJECTAWARE)) {
521                // resources outside of the current project should not be disabled
522                m_projectAware = false;
523            }
524            int searchTypesIndex = configuration.indexOf(CONFIGURATION_SEARCHTYPES);
525            if (searchTypesIndex != -1) {
526                String searchTypes = configuration.substring(searchTypesIndex + CONFIGURATION_SEARCHTYPES.length() + 1);
527                if (searchTypes.contains("|")) {
528                    m_searchTypes = searchTypes.substring(0, searchTypes.indexOf("|"));
529                } else {
530                    m_searchTypes = searchTypes;
531                }
532            }
533            int selectableTypesIndex = configuration.indexOf(CONFIGURATION_SELECTABLETYPES);
534            if (selectableTypesIndex != -1) {
535                String selectableTypes = configuration.substring(selectableTypesIndex
536                    + CONFIGURATION_SELECTABLETYPES.length()
537                    + 1);
538                if (selectableTypes.contains("|")) {
539                    m_selectableTypes = selectableTypes.substring(0, selectableTypes.indexOf("|"));
540                } else {
541                    m_selectableTypes = selectableTypes;
542                }
543            }
544            int startFolderIndex = configuration.indexOf(CONFIGURATION_STARTFOLDER);
545            if (startFolderIndex != -1) {
546                String startFolder = configuration.substring(startFolderIndex + CONFIGURATION_STARTFOLDER.length() + 1);
547                if (startFolder.contains("|")) {
548                    m_startFolder = startFolder.substring(0, startFolder.indexOf("|"));
549                } else {
550                    m_startFolder = startFolder;
551                }
552            }
553        }
554        super.setConfiguration(configuration);
555    }
556
557    /**
558     * Gets the JSON configuration.<p>
559     * 
560     * @param cms the current CMS context 
561     * @param schemaType the schema type
562     * @param messages the messages 
563     * @param resource the content resource 
564     * @param contentLocale the content locale 
565     * 
566     * @return the JSON configuration object 
567     */
568    protected JSONObject getJsonConfig(
569        CmsObject cms,
570        A_CmsXmlContentValue schemaType,
571        CmsMessages messages,
572        CmsResource resource,
573        Locale contentLocale) {
574
575        JSONObject config = new JSONObject();
576        try {
577            config.put(I_CmsGalleryProviderConstants.CONFIG_START_SITE, m_startSite);
578            String tabConfig = null;
579            if (m_includeFiles) {
580                tabConfig = "selectAll";
581            } else {
582                tabConfig = "folders";
583            }
584            config.put(I_CmsGalleryProviderConstants.CONFIG_TAB_CONFIG, tabConfig);
585            config.put(I_CmsGalleryProviderConstants.CONFIG_SHOW_SITE_SELECTOR, m_showSiteSelector);
586            config.put(I_CmsGalleryProviderConstants.CONFIG_REFERENCE_PATH, cms.getSitePath(resource));
587            config.put(I_CmsGalleryProviderConstants.CONFIG_LOCALE, contentLocale.toString());
588            config.put(I_CmsGalleryProviderConstants.CONFIG_GALLERY_MODE, GalleryMode.widget.name());
589            if (CmsStringUtil.isNotEmptyOrWhitespaceOnly(m_selectableTypes)) {
590                config.put(I_CmsGalleryProviderConstants.CONFIG_RESOURCE_TYPES, m_selectableTypes.trim());
591            }
592            if (CmsStringUtil.isNotEmptyOrWhitespaceOnly(m_searchTypes)) {
593                CmsMacroResolver resolver = CmsMacroResolver.newInstance();
594                resolver.addDynamicMacro(DEFAULT_SEARCH_TYPES_MACRO, new SearchTypesFactory(cms, resource));
595                String searchTypes = resolver.resolveMacros(m_searchTypes.trim());
596                config.put(I_CmsGalleryProviderConstants.CONFIG_SEARCH_TYPES, searchTypes);
597            } else if (CmsStringUtil.isEmptyOrWhitespaceOnly(m_selectableTypes)) {
598                config.put(I_CmsGalleryProviderConstants.CONFIG_SEARCH_TYPES, getDefaultSearchTypes(cms, resource));
599            }
600            if (CmsStringUtil.isNotEmptyOrWhitespaceOnly(m_startFolder)) {
601                config.put(I_CmsGalleryProviderConstants.CONFIG_START_FOLDER, m_startFolder);
602            }
603            String treeToken = ""
604                + Objects.hashCode(m_startSite, cms.getRequestContext().getSiteRoot(), "" + m_selectableTypes);
605            config.put(I_CmsGalleryProviderConstants.CONFIG_TREE_TOKEN, treeToken);
606        } catch (JSONException e) {
607            LOG.error(e.getLocalizedMessage(), e);
608        }
609        return config;
610    }
611
612    /**
613     * Computes the tree token, which is used to decide which preloaded tree, if any, to load for the VFS/sitemap tabs.<p>
614     * 
615     * @param cms the current CMS context 
616     * @param value the content value 
617     * @param resource the content resource
618     * @param contentLocale the content locale
619     *  
620     * @return the tree token
621     */
622    protected String getTreeToken(CmsObject cms, A_CmsXmlContentValue value, CmsResource resource, Locale contentLocale) {
623
624        return cms.getRequestContext().getSiteRoot();
625    }
626}