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.ade.galleries.client;
029
030import org.opencms.ade.galleries.client.preview.I_CmsPreviewFactory;
031import org.opencms.ade.galleries.client.preview.I_CmsResourcePreview;
032import org.opencms.ade.galleries.client.ui.CmsSearchTab.ParamType;
033import org.opencms.ade.galleries.shared.CmsGalleryConfiguration;
034import org.opencms.ade.galleries.shared.CmsGalleryDataBean;
035import org.opencms.ade.galleries.shared.CmsGalleryFolderBean;
036import org.opencms.ade.galleries.shared.CmsGallerySearchBean;
037import org.opencms.ade.galleries.shared.CmsGallerySearchScope;
038import org.opencms.ade.galleries.shared.CmsGalleryTreeEntry;
039import org.opencms.ade.galleries.shared.CmsResourceTypeBean;
040import org.opencms.ade.galleries.shared.CmsResourceTypeBean.TypeVisibility;
041import org.opencms.ade.galleries.shared.CmsSiteSelectorOption;
042import org.opencms.ade.galleries.shared.CmsSitemapEntryBean;
043import org.opencms.ade.galleries.shared.CmsVfsEntryBean;
044import org.opencms.ade.galleries.shared.I_CmsBinaryPreviewProvider;
045import org.opencms.ade.galleries.shared.I_CmsGalleryConfiguration;
046import org.opencms.ade.galleries.shared.I_CmsGalleryProviderConstants;
047import org.opencms.ade.galleries.shared.I_CmsGalleryProviderConstants.GalleryMode;
048import org.opencms.ade.galleries.shared.I_CmsGalleryProviderConstants.GalleryTabId;
049import org.opencms.ade.galleries.shared.I_CmsGalleryProviderConstants.SortParams;
050import org.opencms.ade.galleries.shared.rpc.I_CmsGalleryService;
051import org.opencms.ade.galleries.shared.rpc.I_CmsGalleryServiceAsync;
052import org.opencms.gwt.client.CmsCoreProvider;
053import org.opencms.gwt.client.rpc.CmsRpcAction;
054import org.opencms.gwt.client.ui.CmsDeleteWarningDialog;
055import org.opencms.gwt.client.ui.CmsErrorDialog;
056import org.opencms.gwt.client.util.CmsClientCollectionUtil;
057import org.opencms.gwt.client.util.CmsDebugLog;
058import org.opencms.gwt.client.util.CmsJsUtil;
059import org.opencms.gwt.client.util.I_CmsSimpleCallback;
060import org.opencms.gwt.shared.CmsCategoryBean;
061import org.opencms.gwt.shared.CmsCategoryTreeEntry;
062import org.opencms.gwt.shared.CmsGalleryContainerInfo;
063import org.opencms.gwt.shared.CmsTemplateContextInfo;
064import org.opencms.gwt.shared.rpc.I_CmsVfsServiceAsync;
065import org.opencms.gwt.shared.sort.CmsComparatorPath;
066import org.opencms.gwt.shared.sort.CmsComparatorTitle;
067import org.opencms.gwt.shared.sort.CmsComparatorType;
068import org.opencms.util.CmsStringUtil;
069import org.opencms.util.CmsUUID;
070
071import java.util.ArrayList;
072import java.util.Arrays;
073import java.util.Collections;
074import java.util.HashMap;
075import java.util.HashSet;
076import java.util.List;
077import java.util.Map;
078import java.util.Set;
079import java.util.function.Supplier;
080
081import com.google.common.collect.ComparisonChain;
082import com.google.common.collect.Lists;
083import com.google.gwt.core.client.GWT;
084import com.google.gwt.core.client.JavaScriptObject;
085import com.google.gwt.event.logical.shared.HasValueChangeHandlers;
086import com.google.gwt.event.logical.shared.ValueChangeEvent;
087import com.google.gwt.event.logical.shared.ValueChangeHandler;
088import com.google.gwt.event.shared.GwtEvent;
089import com.google.gwt.event.shared.HandlerRegistration;
090import com.google.gwt.event.shared.SimpleEventBus;
091import com.google.gwt.user.client.Command;
092import com.google.gwt.user.client.rpc.AsyncCallback;
093import com.google.gwt.user.client.rpc.ServiceDefTarget;
094
095/**
096 * Gallery dialog controller.<p>
097 *
098 * This class handles the communication between gallery dialog and the server.
099 * It contains the gallery data, but no references to the gallery dialog widget.
100 *
101 * @since 8.0.0
102 */
103public class CmsGalleryController implements HasValueChangeHandlers<CmsGallerySearchBean> {
104
105    /** The gallery service instance. */
106    private static I_CmsGalleryServiceAsync m_gallerySvc;
107
108    /** The preview factory registration. */
109    private static Map<String, I_CmsPreviewFactory> m_previewFactoryRegistration = new HashMap<String, I_CmsPreviewFactory>();
110
111    /** The current load results call id. */
112    protected int m_currentCallId;
113
114    /** The gallery dialog bean. */
115    protected CmsGalleryDataBean m_dialogBean;
116
117    /** The gallery dialog mode. */
118    protected I_CmsGalleryProviderConstants.GalleryMode m_dialogMode;
119
120    /** The event bus. */
121    protected SimpleEventBus m_eventBus;
122
123    /** The gallery controller handler. */
124    protected CmsGalleryControllerHandler m_handler;
125
126    /** Flag to indicate that a load results request is currently running. */
127    protected boolean m_loading;
128
129    /** The gallery search object. */
130    protected CmsGallerySearchBean m_searchObject;
131
132    /** The gallery configuration. */
133    private I_CmsGalleryConfiguration m_configuration;
134
135    /** Provides container information for the gallery dialog. */
136    private Supplier<CmsGalleryContainerInfo> m_containerInfoProvider = () -> null;
137
138    /** The current resource preview. */
139    private I_CmsResourcePreview<?> m_currentPreview;
140
141    /** Flag to record whether the user changed the gallery selection. */
142    private boolean m_galleriesChanged;
143
144    /** Flag which controls whether the galleries in the gallery tab should be selectable. */
145    private boolean m_galleriesSelectable;
146
147    /** Flag which indicates whether the site selector should be shown. */
148    private boolean m_isShowSiteSelector = true;
149
150    /** Flag which controls whether the results should be selectable. If this is false, the results will not be selectable, but if it is true, the results may still be unselectable for a different reason. */
151    private boolean m_resultsSelectable = true;
152
153    /** If <code>true</code> the search object is changed <code>false</code> otherwise.  */
154    private boolean m_searchObjectChanged = true;
155
156    /** The search able resource types. */
157    private List<CmsResourceTypeBean> m_searchTypes;
158
159    /** The start site to set for the site selector. */
160    private String m_startSite;
161
162    /** The configured tabs. */
163    private GalleryTabId[] m_tabIds;
164
165    /** Provides the template context information. */
166    private Supplier<CmsTemplateContextInfo> m_templateContextInfoProvider = () -> null;
167
168    /** The tree token for this gallery instance (determines which tree open state to use). */
169    private String m_treeToken;
170
171    /** The vfs service. */
172    private I_CmsVfsServiceAsync m_vfsService;
173
174    /**
175     * Constructor.<p>
176     *
177     * @param handler the controller handler
178     * @param dialogBean the gallery data
179     * @param searchBean the prefetched search
180     */
181    public CmsGalleryController(
182        CmsGalleryControllerHandler handler,
183        CmsGalleryDataBean dialogBean,
184        CmsGallerySearchBean searchBean) {
185
186        m_handler = handler;
187        m_eventBus = new SimpleEventBus();
188        addValueChangeHandler(m_handler);
189        JavaScriptObject embeddedConfig = CmsJsUtil.getAttribute(CmsJsUtil.getWindow(), "embeddedConfiguration");
190        if (embeddedConfig != null) {
191            CmsGalleryConfigurationJSO config = embeddedConfig.cast();
192            m_handler.m_galleryDialog.setOverrideFormats(true);
193            m_handler.m_galleryDialog.setUseFormats(config.isUseFormats());
194            m_handler.m_galleryDialog.setImageFormats(config.getImageFormats());
195            m_handler.m_galleryDialog.setImageFormatNames(config.getImageFormatNames());
196        }
197        m_dialogBean = dialogBean;
198        m_searchObject = searchBean;
199        m_dialogMode = m_dialogBean.getMode();
200
201        if (m_searchObject == null) {
202            m_searchObject = new CmsGallerySearchBean();
203            m_searchObject.setOriginalGalleryData(dialogBean);
204            m_searchObject.setGalleryMode(m_dialogMode);
205            m_searchObject.setIgnoreSearchExclude(m_dialogMode != GalleryMode.ade);
206            m_searchObject.setLocale(m_dialogBean.getLocale());
207            m_searchObject.setScope(m_dialogBean.getScope());
208            m_searchObject.setSortOrder(m_dialogBean.getSortOrder().name());
209            m_searchObject.setGalleryStoragePrefix(m_dialogBean.getGalleryStoragePrefix());
210            if (m_dialogBean.getStartFolderFilter() != null) {
211                m_searchObject.setFolders(m_dialogBean.getStartFolderFilter());
212            }
213        }
214        if (m_dialogBean != null) {
215            m_tabIds = m_dialogBean.getTabIds();
216            m_handler.onInitialSearch(m_searchObject, m_dialogBean, this, true);
217        }
218    }
219
220    /**
221     * Constructor.<p>
222     *
223     * @param handler the controller handler
224     * @param conf the gallery configuration
225     */
226    public CmsGalleryController(CmsGalleryControllerHandler handler, final I_CmsGalleryConfiguration conf) {
227
228        m_configuration = conf;
229        m_resultsSelectable = conf.isResultsSelectable();
230        m_galleriesSelectable = conf.isGalleriesSelectable();
231        m_handler = handler;
232        m_handler.m_galleryDialog.setUseFormats(m_configuration.isUseFormats());
233        m_handler.m_galleryDialog.setImageFormats(m_configuration.getImageFormats());
234        m_handler.m_galleryDialog.setImageFormatNames(m_configuration.getImageFormatNames());
235        m_eventBus = new SimpleEventBus();
236        addValueChangeHandler(m_handler);
237        m_treeToken = m_configuration.getTreeToken();
238        CmsRpcAction<CmsGalleryDataBean> initAction = new CmsRpcAction<CmsGalleryDataBean>() {
239
240            @Override
241            public void execute() {
242
243                try {
244                    CmsGalleryConfiguration config = new CmsGalleryConfiguration(conf);
245                    getGalleryService().getInitialSettings(config, this);
246                } catch (Throwable t) {
247                    CmsErrorDialog.handleException(t);
248                }
249            }
250
251            @Override
252            protected void onResponse(CmsGalleryDataBean result) {
253
254                m_dialogBean = result;
255                m_dialogMode = m_dialogBean.getMode();
256                if (m_dialogBean.getStartTab() != GalleryTabId.cms_tab_results) {
257                    List<GalleryTabId> tabs = Arrays.asList(getTabIds());
258                    // in case the selected start tab is not present, choose another one
259                    if (!tabs.contains(m_dialogBean.getStartTab())) {
260                        if ((m_dialogMode == GalleryMode.widget) && tabs.contains(GalleryTabId.cms_tab_vfstree)) {
261                            m_dialogBean.setStartTab(GalleryTabId.cms_tab_vfstree);
262                        } else {
263                            m_dialogBean.setStartTab(tabs.get(0));
264                        }
265                    }
266                }
267                initialSearch();
268            }
269        };
270        initAction.execute();
271        m_tabIds = m_configuration.getTabConfiguration().getTabs().toArray(new GalleryTabId[] {});
272        setShowSiteSelector(m_configuration.isShowSiteSelector());
273        if (CmsStringUtil.isNotEmptyOrWhitespaceOnly(m_configuration.getStartSite())) {
274            setStartSite(m_configuration.getStartSite());
275        }
276    }
277
278    /**
279     * Creates a gallery service instance.<p>
280     *
281     * @return the gallery service instance
282     */
283    public static I_CmsGalleryServiceAsync createGalleryService() {
284
285        I_CmsGalleryServiceAsync service;
286        service = GWT.create(I_CmsGalleryService.class);
287        String serviceUrl = CmsCoreProvider.get().link("org.opencms.ade.galleries.CmsGalleryService.gwt");
288        ((ServiceDefTarget)service).setServiceEntryPoint(serviceUrl);
289        return service;
290    }
291
292    /**
293     * Returns the gallery service instance.<p>
294     *
295     * @return the gallery service instance
296     */
297    public static I_CmsGalleryServiceAsync getGalleryService() {
298
299        if (m_gallerySvc == null) {
300            I_CmsGalleryServiceAsync service = createGalleryService();
301            m_gallerySvc = service;
302        }
303        return m_gallerySvc;
304    }
305
306    /**
307     * Registers a preview factory for the given name.
308     *
309     * @param previewProviderName the preview provider name
310     * @param factory the preview factory
311     */
312    public static void registerPreviewFactory(String previewProviderName, I_CmsPreviewFactory factory) {
313
314        m_previewFactoryRegistration.put(previewProviderName, factory);
315    }
316
317    /**
318     * Add category to search object.<p>
319     *
320     * @param categoryPath the id of the category to add
321     */
322    public void addCategory(String categoryPath) {
323
324        m_searchObject.addCategory(categoryPath);
325        m_searchObjectChanged = true;
326        ValueChangeEvent.fire(this, m_searchObject);
327    }
328
329    /**
330     * Sets the created until date to the search object.<p>
331     *
332     * @param end the created until date as long
333     */
334    public void addDateCreatedEnd(long end) {
335
336        m_searchObject.setDateCreatedEnd(end);
337        m_searchObjectChanged = true;
338        ValueChangeEvent.fire(this, m_searchObject);
339    }
340
341    /**
342     * Sets the created since date to the search object.<p>
343     *
344     * @param start the created since date as long
345     */
346    public void addDateCreatedStart(long start) {
347
348        m_searchObject.setDateCreatedStart(start);
349        m_searchObjectChanged = true;
350        ValueChangeEvent.fire(this, m_searchObject);
351    }
352
353    /**
354     * Sets the modified until date to the search object.<p>
355     *
356     * @param end the modified until date as long
357     */
358    public void addDateModifiedEnd(long end) {
359
360        m_searchObject.setDateModifiedEnd(end);
361        m_searchObjectChanged = true;
362        ValueChangeEvent.fire(this, m_searchObject);
363    }
364
365    /**
366     * Sets the modified since date to the search object.<p>
367     *
368     * @param start the modified since date as long
369     */
370    public void addDateModifiedStart(long start) {
371
372        m_searchObject.setDateModifiedStart(start);
373        m_searchObjectChanged = true;
374        ValueChangeEvent.fire(this, m_searchObject);
375    }
376
377    /**
378     * Adds a folder to the current search object.<p>
379     *
380     * @param folder the folder to add
381     */
382    public void addFolder(String folder) {
383
384        m_searchObject.addFolder(folder);
385        m_searchObjectChanged = true;
386        ValueChangeEvent.fire(this, m_searchObject);
387    }
388
389    /**
390     * Add gallery to search object.<p>
391     *
392     * @param galleryPath the id of the gallery to add
393     */
394    public void addGallery(String galleryPath) {
395
396        m_searchObject.addGallery(galleryPath);
397        m_searchObjectChanged = true;
398        m_galleriesChanged = true;
399        ValueChangeEvent.fire(this, m_searchObject);
400    }
401
402    /**
403     * Sets the locale to the search object.<p>
404     *
405     * @param locale the locale to set
406     */
407    public void addLocale(String locale) {
408
409        m_searchObject.setLocale(locale);
410        m_searchObjectChanged = true;
411        ValueChangeEvent.fire(this, m_searchObject);
412    }
413
414    /**
415     * Sets the search scope in the search object.<p>
416     *
417     * @param scope the search scope
418     */
419    public void addScope(CmsGallerySearchScope scope) {
420
421        m_searchObject.setScope(scope);
422        m_searchObjectChanged = true;
423        ValueChangeEvent.fire(this, m_searchObject);
424    }
425
426    /**
427     * Adds the search query from the search tab.<p>
428     *
429     * @param searchQuery the search query
430     */
431    public void addSearchQuery(String searchQuery) {
432
433        m_searchObject.setQuery(searchQuery);
434        m_searchObjectChanged = true;
435        ValueChangeEvent.fire(this, m_searchObject);
436    }
437
438    /**
439     * Add type to search object.<p>
440     *
441     * @param resourceType the id(name?) of the resource type to add
442     */
443    public void addType(String resourceType) {
444
445        m_searchObject.addType(resourceType);
446        m_searchObjectChanged = true;
447        ValueChangeEvent.fire(this, m_searchObject);
448    }
449
450    /**
451     * @see com.google.gwt.event.logical.shared.HasValueChangeHandlers#addValueChangeHandler(com.google.gwt.event.logical.shared.ValueChangeHandler)
452     */
453    public HandlerRegistration addValueChangeHandler(ValueChangeHandler<CmsGallerySearchBean> handler) {
454
455        return m_eventBus.addHandlerToSource(ValueChangeEvent.getType(), this, handler);
456    }
457
458    /**
459     * Removes all selected categories from the search object.<p>
460     */
461    public void clearCategories() {
462
463        List<String> selectedCategories = m_searchObject.getCategories();
464        m_handler.onClearCategories(selectedCategories);
465        m_searchObject.clearCategories();
466        updateResultsTab(false);
467        ValueChangeEvent.fire(this, m_searchObject);
468    }
469
470    /**
471     * Removes all selected folders from the search object.<p>
472     *
473     * @param searchChanged if true, marks the search parameters as changed
474     */
475    public void clearFolders(boolean searchChanged) {
476
477        Set<String> selectedFolders = m_searchObject.getFolders();
478        if (searchChanged) {
479            m_searchObjectChanged = true;
480        }
481        m_handler.onClearFolders(selectedFolders);
482        m_searchObject.clearFolders();
483        updateResultsTab(false);
484        ValueChangeEvent.fire(this, m_searchObject);
485    }
486
487    /**
488     * Removes all selected galleries from the search object.<p>
489     */
490    public void clearGalleries() {
491
492        List<String> selectedGalleries = m_searchObject.getGalleries();
493        m_handler.onClearGalleries(selectedGalleries);
494        m_searchObject.clearGalleries();
495        updateResultsTab(false);
496        ValueChangeEvent.fire(this, m_searchObject);
497    }
498
499    /**
500     * Removes all full text search criteria from the search object.<p>
501     */
502    public void clearTextSearch() {
503
504        m_searchObject.clearFullTextSearch();
505        m_handler.onClearFullTextSearch();
506        updateResultsTab(false);
507        ValueChangeEvent.fire(this, m_searchObject);
508    }
509
510    /**
511     * Removes all selected types from the search object.<p>
512     */
513    public void clearTypes() {
514
515        List<String> selectedTypes = m_searchObject.getTypes();
516        m_handler.onClearTypes(selectedTypes);
517        m_searchObject.clearTypes();
518        updateResultsTab(false);
519        ValueChangeEvent.fire(this, m_searchObject);
520    }
521
522    /**
523     * Checks for broken links, ask for confirmation and finally deletes the given resource.<p>
524     *
525     * @param resourcePath the resource path of the resource to delete
526     */
527    public void deleteResource(final String resourcePath) {
528
529        CmsDeleteWarningDialog dialog = new CmsDeleteWarningDialog(resourcePath);
530        Command callback = new Command() {
531
532            /**
533             * @see com.google.gwt.user.client.Command#execute()
534             */
535            public void execute() {
536
537                updateResultsTab(false);
538            }
539        };
540        dialog.loadAndShow(callback);
541    }
542
543    /**
544     * @see com.google.gwt.event.shared.HasHandlers#fireEvent(com.google.gwt.event.shared.GwtEvent)
545     */
546    public void fireEvent(GwtEvent<?> event) {
547
548        m_eventBus.fireEventFromSource(event, this);
549    }
550
551    /**
552     * Gets the available galleries.<p>
553     *
554     * @return the list of available galleries
555     */
556    public List<CmsGalleryFolderBean> getAvailableGalleries() {
557
558        return m_dialogBean.getGalleries();
559    }
560
561    /**
562     * Returns the available locales.<p>
563     *
564     * @return the available locales
565     */
566    public Map<String, String> getAvailableLocales() {
567
568        return m_dialogBean.getLocales();
569    }
570
571    /**
572     * Gets the default search scope.<p>
573     *
574     * @return the default search scope
575     */
576    public CmsGallerySearchScope getDefaultScope() {
577
578        return m_dialogBean.getDefaultScope();
579    }
580
581    /**
582     * Gets the default site root for the sitemap tab.<p>
583     *
584     * @return the default site root for the sitemap tab
585     */
586    public String getDefaultSitemapTabSiteRoot() {
587
588        return getDefaultSiteRoot(m_dialogBean.getSitemapSiteSelectorOptions());
589    }
590
591    /**
592     * Gets the default site root for the VFS tab.<p>
593     *
594     * @return the default site root for the VFS tab
595     */
596    public String getDefaultVfsTabSiteRoot() {
597
598        return getDefaultSiteRoot(m_dialogBean.getVfsSiteSelectorOptions());
599    }
600
601    /**
602     * Returns the gallery dialog mode.<p>
603     *
604     * @return the gallery dialog mode
605     */
606    public I_CmsGalleryProviderConstants.GalleryMode getDialogMode() {
607
608        return m_dialogMode;
609    }
610
611    /**
612     * Returns the gallery folder info to the given path.<p>
613     *
614     * @param galleryPath the gallery folder path
615     *
616     * @return the gallery folder info
617     */
618    public CmsGalleryFolderBean getGalleryInfo(String galleryPath) {
619
620        CmsGalleryFolderBean result = null;
621        for (CmsGalleryFolderBean folderBean : getAvailableGalleries()) {
622            if (folderBean.getPath().equals(galleryPath)) {
623                result = folderBean;
624                break;
625            }
626        }
627        return result;
628    }
629
630    /**
631     * Gets the option which should be preselected for the site selector, or null.<p>
632     *
633     * @param siteRoot the site root
634     * @param options the list of options
635     *
636     * @return the key for the option to preselect
637     */
638    public String getPreselectOption(String siteRoot, List<CmsSiteSelectorOption> options) {
639
640        if ((siteRoot == null) || options.isEmpty()) {
641            return null;
642        }
643        for (CmsSiteSelectorOption option : options) {
644            if (CmsStringUtil.joinPaths(siteRoot, "/").equals(CmsStringUtil.joinPaths(option.getSiteRoot(), "/"))) {
645                return option.getSiteRoot();
646            }
647        }
648        return options.get(0).getSiteRoot();
649    }
650
651    /**
652     * Returns the result view type.<p>
653     *
654     * @return the result view type
655     */
656    public String getResultViewType() {
657
658        return m_dialogBean.getResultViewType();
659    }
660
661    /**
662     * Returns the search locale.<p>
663     *
664     * @return the search locale
665     */
666    public String getSearchLocale() {
667
668        return m_searchObject.getLocale();
669    }
670
671    /**
672     * Returns the gallery search scope.<p>
673     *
674     * @return the gallery search scope
675     */
676    public CmsGallerySearchScope getSearchScope() {
677
678        return m_dialogBean.getScope();
679    }
680
681    /**
682     * Returns the searchable resource types.<p>
683     *
684     * @return the searchable resource types
685     */
686    public List<CmsResourceTypeBean> getSearchTypes() {
687
688        if (m_searchTypes != null) {
689            return m_searchTypes;
690        }
691        m_searchTypes = Lists.newArrayList();
692        for (CmsResourceTypeBean type : m_dialogBean.getTypes()) {
693            if (type.getVisibility() != TypeVisibility.hidden) {
694                m_searchTypes.add(type);
695            }
696        }
697        return m_searchTypes;
698    }
699
700    /**
701     * Returns the default value for the "show expired" check box.<p>
702     *
703     * @return the default value for "show expired"
704     */
705    public boolean getShowExpiredDefault() {
706
707        return m_dialogBean.getIncludeExpiredDefault();
708    }
709
710    /**
711     * Gets the sitemap site selector options.<p>
712     *
713     * @return the sitemap site selector options
714     */
715    public List<CmsSiteSelectorOption> getSitemapSiteSelectorOptions() {
716
717        return m_dialogBean.getSitemapSiteSelectorOptions();
718    }
719
720    /**
721     * Returns the start locale.<p>
722     *
723     * @return the start locale
724     */
725    public String getStartLocale() {
726
727        return m_dialogBean.getLocale();
728    }
729
730    /**
731     * Gets the start site root.<p>
732     *
733     * @return the start site root
734     */
735    public String getStartSiteRoot() {
736
737        return m_startSite;
738    }
739
740    /**
741     * Loads the sub entries for the given path.<p>
742     *
743     * @param rootPath the root path
744     * @param isRoot <code>true</code> if the requested entry is the root entry
745     * @param filter the sitemap filter string
746     * @param callback the callback to execute with the result
747     */
748    public void getSubEntries(
749        final String rootPath,
750        final boolean isRoot,
751        final String filter,
752        final I_CmsSimpleCallback<List<CmsSitemapEntryBean>> callback) {
753
754        CmsRpcAction<List<CmsSitemapEntryBean>> action = new CmsRpcAction<List<CmsSitemapEntryBean>>() {
755
756            @Override
757            public void execute() {
758
759                start(0, false);
760                getGalleryService().getSubEntries(rootPath, isRoot, filter, this);
761            }
762
763            @Override
764            protected void onResponse(List<CmsSitemapEntryBean> result) {
765
766                stop(false);
767                callback.execute(result);
768            }
769
770        };
771        action.execute();
772    }
773
774    /**
775     * Retrieves the sub-folders of a given folder.<p>
776     *
777     * @param folder the folder whose sub-folders should be retrieved
778     * @param callback the callback for processing the sub-folders
779     */
780    public void getSubFolders(final String folder, final AsyncCallback<List<CmsVfsEntryBean>> callback) {
781
782        CmsRpcAction<List<CmsVfsEntryBean>> action = new CmsRpcAction<List<CmsVfsEntryBean>>() {
783
784            @Override
785            public void execute() {
786
787                start(0, false);
788                getGalleryService().getSubFolders(folder, this);
789            }
790
791            @Override
792            protected void onResponse(List<CmsVfsEntryBean> result) {
793
794                stop(false);
795                callback.onSuccess(result);
796            }
797
798        };
799        action.execute();
800    }
801
802    /**
803     * Returns the configured tab id's.<p>
804     *
805     * @return the configured tab id's
806     */
807    public GalleryTabId[] getTabIds() {
808
809        if (m_tabIds == null) {
810            return m_dialogMode.getTabs();
811        } else {
812            return m_tabIds;
813        }
814    }
815
816    /**
817     * Gets the tree token, which is used to determine which tree state is loaded/saved for the VFS and sitemap tabs.<p>
818     *
819     * @return the tree token
820     */
821    public String getTreeToken() {
822
823        return m_treeToken;
824    }
825
826    /**
827     * Returns the resource type info for the given resource type name.<p>
828     *
829     * @param typeName the resource type name
830     *
831     * @return the type info
832     */
833    public CmsResourceTypeBean getTypeInfo(String typeName) {
834
835        CmsResourceTypeBean result = null;
836        for (CmsResourceTypeBean typeBean : m_dialogBean.getTypes()) {
837            if (typeBean.getType().equals(typeName)) {
838                result = typeBean;
839                break;
840            }
841        }
842        return result;
843    }
844
845    /**
846     * Gets the site selector options.<p>
847     *
848     * @return the site selector options
849     */
850    public List<CmsSiteSelectorOption> getVfsSiteSelectorOptions() {
851
852        return m_dialogBean.getVfsSiteSelectorOptions();
853    }
854
855    /**
856     * Returns true if the galleries in the gallery tab should be selectable.<p>
857     *
858     * @return true if the galleries should be selectable
859     */
860    public boolean hasGalleriesSelectable() {
861
862        return m_galleriesSelectable;
863    }
864
865    /**
866     * Returns if a preview is available for the given resource type.<p>
867     *
868     * @param resourceType the requested resource type
869     *
870     * @return <code>true</code> if a preview is available for the given resource type
871     */
872    public boolean hasPreview(String resourceType) {
873
874        return getProviderName(resourceType) != null;
875    }
876
877    /**
878     * Returns false if the results in the result tab should not be selectable.<p>
879     *
880     * @return false if the results in the result tab should not be selectable
881     */
882    public boolean hasResultsSelectable() {
883
884        return m_resultsSelectable;
885    }
886
887    /**
888     * Returns if folders should be selectable.<p>
889     *
890     * @return <code>true</code> if folders should be selectable
891     */
892    public boolean hasSelectFolder() {
893
894        if (hasSelectResource()) {
895            for (CmsResourceTypeBean type : m_dialogBean.getTypes()) {
896                if (type.getType().contains(I_CmsGalleryProviderConstants.RESOURCE_TYPE_FOLDER)) {
897                    return true;
898                }
899            }
900        }
901        return false;
902    }
903
904    /**
905     * Returns if resource entries in the search result are selectable.<p>
906     *
907     * @return if resource entries in the search result are selectable
908     */
909    public boolean hasSelectResource() {
910
911        return (m_dialogMode == GalleryMode.editor) || (m_dialogMode == GalleryMode.widget);
912    }
913
914    /**
915     * Returns if files are included.<p>
916     *
917     * @return <code>true</code> if files are included
918     */
919    public boolean isIncludeFiles() {
920
921        return (m_configuration == null) || m_configuration.isIncludeFiles();
922    }
923
924    /**
925     * Returns if a load results request is currently running.<p>
926     *
927     * @return <code>true</code> if a load results request is currently running
928     */
929    public boolean isLoading() {
930
931        return m_loading;
932    }
933
934    /**
935     * Checks if the gallery is first opened in results tab.<p>
936     *
937     * @return true if gallery is first opened in results tab, false otherwise
938     */
939    public boolean isOpenInResults() {
940
941        if (I_CmsGalleryProviderConstants.GalleryTabId.cms_tab_results.name().equals(m_searchObject.getTabId())) {
942            return true;
943        }
944        return false;
945    }
946
947    /**
948     * Returns <code>true</code>, if the search object was manipulated by the controller
949     * <code>false</code> otherwise.<p>
950     *
951     * @return the search object changed flag
952     */
953    public boolean isSearchObjectChanged() {
954
955        return m_searchObjectChanged;
956    }
957
958    /**
959     * Checks if any search parameter are selected.<p>
960     *
961     * @return <code>false</code> if any search parameter is selected, <code>true</code>
962     * if there are no search parameter selected
963     */
964    public boolean isSearchObjectEmpty() {
965
966        return m_searchObject.isEmpty();
967    }
968
969    /**
970     * Returns true if the site selector should be shown.<p>
971     *
972     * @return true if the site selector should be shown
973     */
974    public boolean isShowSiteSelector() {
975
976        return m_isShowSiteSelector;
977    }
978
979    /**
980     * Loads the root VFS entry bean for a given site selector option.<p>
981     *
982     * @param siteRoot the site root for which the VFS entry should be loaded
983     * @param filter the search filter
984     *
985     * @param asyncCallback the callback to call with the result
986     */
987    public void loadVfsEntryBean(
988        final String siteRoot,
989        final String filter,
990
991        final AsyncCallback<CmsVfsEntryBean> asyncCallback) {
992
993        CmsRpcAction<CmsVfsEntryBean> action = new CmsRpcAction<CmsVfsEntryBean>() {
994
995            @Override
996            public void execute() {
997
998                start(200, false);
999                getGalleryService().loadVfsEntryBean(siteRoot, filter, this);
1000            }
1001
1002            @Override
1003            public void onResponse(CmsVfsEntryBean result) {
1004
1005                stop(false);
1006                asyncCallback.onSuccess(result);
1007            }
1008
1009        };
1010        action.execute();
1011    }
1012
1013    /**
1014     * Opens the preview for the given resource by the given resource type.<p>
1015     *
1016     * @param resourcePath the resource path
1017     * @param resourceType the resource type name
1018     */
1019    public void openPreview(String resourcePath, String resourceType) {
1020
1021        if (m_currentPreview != null) {
1022            m_currentPreview.removePreview();
1023        }
1024        String provider = getProviderName(resourceType);
1025        if (m_previewFactoryRegistration.containsKey(provider)) {
1026            m_handler.m_galleryDialog.useMaxDimensions();
1027            m_currentPreview = m_previewFactoryRegistration.get(provider).getPreview(m_handler.m_galleryDialog);
1028            m_currentPreview.openPreview(resourcePath, !m_resultsSelectable);
1029            m_handler.hideShowPreviewButton(false);
1030        } else {
1031            CmsDebugLog.getInstance().printLine(
1032                "Preview provider \"" + provider + "\" has not been registered properly.");
1033        }
1034    }
1035
1036    /**
1037     * Remove the category from the search object.<p>
1038     *
1039     * @param categoryPath the category path as id
1040     */
1041    public void removeCategory(String categoryPath) {
1042
1043        m_searchObject.removeCategory(categoryPath);
1044        m_searchObjectChanged = true;
1045        ValueChangeEvent.fire(this, m_searchObject);
1046    }
1047
1048    /**
1049     * Removes the category from the search object.<p>
1050     *
1051     * @param key the category
1052     */
1053    public void removeCategoryParam(String key) {
1054
1055        if (m_searchObject.getCategories().contains(key)) {
1056            m_handler.onClearCategories(Collections.singletonList(key));
1057            m_searchObject.removeCategory(key);
1058            updateResultsTab(false);
1059            ValueChangeEvent.fire(this, m_searchObject);
1060        }
1061    }
1062
1063    /**
1064     * Removes a folder from the current search object.<p>
1065     *
1066     * @param folder the folder to remove
1067     */
1068    public void removeFolder(String folder) {
1069
1070        m_searchObject.removeFolder(folder);
1071        m_searchObjectChanged = true;
1072        ValueChangeEvent.fire(this, m_searchObject);
1073    }
1074
1075    /**
1076     * Removes the folder from the search object.<p>
1077     *
1078     * @param key the folder
1079     */
1080    public void removeFolderParam(String key) {
1081
1082        if (m_searchObject.getFolders().contains(key)) {
1083            m_handler.onClearFolders(Collections.singletonList(key));
1084            m_searchObject.removeFolder(key);
1085            updateResultsTab(false);
1086            ValueChangeEvent.fire(this, m_searchObject);
1087        }
1088    }
1089
1090    /**
1091     * Remove the gallery from the search object.<p>
1092     *
1093     * @param galleryPath the gallery path as id
1094     */
1095    public void removeGallery(String galleryPath) {
1096
1097        m_searchObject.removeGallery(galleryPath);
1098        m_searchObjectChanged = true;
1099        m_galleriesChanged = true;
1100        ValueChangeEvent.fire(this, m_searchObject);
1101    }
1102
1103    /**
1104     * Removes a selected gallery from the search object.<p>
1105     *
1106     * @param key the gallery key
1107     */
1108    public void removeGalleryParam(String key) {
1109
1110        if (m_searchObject.getGalleries().contains(key)) {
1111            m_galleriesChanged = true;
1112            m_handler.onClearGalleries(Collections.singletonList(key));
1113            m_searchObject.removeGallery(key);
1114            updateResultsTab(false);
1115            ValueChangeEvent.fire(this, m_searchObject);
1116        }
1117    }
1118
1119    /**
1120     * Removes the query.
1121     */
1122    public void removeQuery() {
1123
1124        m_searchObject.setQuery(null);
1125        m_handler.onRemoveQuery();
1126        updateResultsTab(false);
1127        ValueChangeEvent.fire(this, m_searchObject);
1128    }
1129
1130    /**
1131     * Removes the search scope.
1132     */
1133    public void removeScope() {
1134
1135        m_searchObject.setScope(null);
1136        m_handler.onRemoveScope();
1137        updateResultsTab(false);
1138        ValueChangeEvent.fire(this, m_searchObject);
1139    }
1140
1141    /**
1142     * Removes the given full text search criteria from the search object.<p>
1143     *
1144     * @param key the key of the parameter to remove
1145     */
1146    public void removeTextSearchParameter(String key) {
1147
1148        try {
1149            ParamType type = ParamType.valueOf(key);
1150            switch (type) {
1151                case language:
1152                    m_searchObject.setLocale(getStartLocale());
1153                    break;
1154                case expired:
1155                    m_searchObject.setIncludeExpired(false);
1156                    break;
1157                case creation:
1158                    m_searchObject.setDateCreatedEnd(-1L);
1159                    m_searchObject.setDateCreatedStart(-1L);
1160                    break;
1161                case modification:
1162                    m_searchObject.setDateModifiedEnd(-1L);
1163                    m_searchObject.setDateModifiedStart(-1L);
1164                    break;
1165                default:
1166            }
1167            m_handler.onRemoveSearchParam(type);
1168            updateResultsTab(false);
1169            ValueChangeEvent.fire(this, m_searchObject);
1170        } catch (IllegalArgumentException e) {
1171            // should not happen
1172        }
1173    }
1174
1175    /**
1176     * Remove the type from the search object.<p>
1177     *
1178     * @param resourceType the resource type as id
1179     */
1180    public void removeType(String resourceType) {
1181
1182        m_searchObject.removeType(resourceType);
1183        m_searchObjectChanged = true;
1184        ValueChangeEvent.fire(this, m_searchObject);
1185    }
1186
1187    /**
1188     * Removes the type from the search object.<p>
1189     *
1190     * @param key the type
1191     */
1192    public void removeTypeParam(String key) {
1193
1194        List<String> selectedTypes = m_searchObject.getTypes();
1195        if (selectedTypes.contains(key)) {
1196            m_handler.onClearTypes(Collections.singletonList(key));
1197            selectedTypes.remove(key);
1198            updateResultsTab(false);
1199            ValueChangeEvent.fire(this, m_searchObject);
1200        }
1201    }
1202
1203    /**
1204     * Saves the tree state for a given tree on the server.<p>
1205     *
1206     * @param treeName the tree name
1207     * @param siteRoot the site root
1208     * @param openItemIds the structure ids of opened items
1209     */
1210    public void saveTreeState(final String treeName, final String siteRoot, final Set<CmsUUID> openItemIds) {
1211
1212        CmsRpcAction<Void> treeStateAction = new CmsRpcAction<Void>() {
1213
1214            @Override
1215            public void execute() {
1216
1217                start(600, false);
1218                getGalleryService().saveTreeOpenState(treeName, getTreeToken(), siteRoot, openItemIds, this);
1219            }
1220
1221            @Override
1222            protected void onResponse(Void result) {
1223
1224                stop(false);
1225            }
1226        };
1227        treeStateAction.execute();
1228
1229    }
1230
1231    /**
1232     * Searches for a specific element and opens it's preview if found.<p>
1233     *
1234     * @param path the element path
1235     * @param nextAction the next action to execute after the search data for the element has been loaded into the gallery dialog
1236     */
1237    public void searchElement(final String path, final Runnable nextAction) {
1238
1239        m_dialogBean.setCurrentElement(path);
1240        m_dialogBean.setStartTab(GalleryTabId.cms_tab_results);
1241        m_dialogBean.setTreeToken(getTreeToken());
1242
1243        CmsRpcAction<CmsGallerySearchBean> searchAction = new CmsRpcAction<CmsGallerySearchBean>() {
1244
1245            @Override
1246            public void execute() {
1247
1248                start(200, true);
1249                getGalleryService().getSearch(m_dialogBean, this);
1250            }
1251
1252            @Override
1253            protected void onResponse(CmsGallerySearchBean result) {
1254
1255                stop(false);
1256                m_searchObject = result;
1257                m_handler.onInitialSearch(result, m_dialogBean, CmsGalleryController.this, false);
1258                if (nextAction != null) {
1259                    nextAction.run();
1260                }
1261            }
1262
1263        };
1264        searchAction.execute();
1265    }
1266
1267    /**
1268     * Selects the given resource and sets its path into the xml-content field or editor link.<p>
1269     *
1270     * @param resourcePath the resource path
1271     * @param structureId the structure id
1272     * @param title the resource title
1273     * @param resourceType the resource type
1274     */
1275    public void selectResource(String resourcePath, CmsUUID structureId, String title, String resourceType) {
1276
1277        String provider = getProviderName(resourceType);
1278        if (provider == null) {
1279            // use {@link org.opencms.ade.galleries.client.preview.CmsBinaryPreviewProvider} as default to select a resource
1280            provider = I_CmsBinaryPreviewProvider.PREVIEW_NAME;
1281        }
1282        if (m_previewFactoryRegistration.containsKey(provider)) {
1283            m_previewFactoryRegistration.get(provider).getPreview(m_handler.m_galleryDialog).selectResource(
1284                resourcePath,
1285                structureId,
1286                title);
1287
1288        } else {
1289            CmsDebugLog.getInstance().printLine("No provider available");
1290        }
1291    }
1292
1293    /**
1294     * Selects the result tab.<p>
1295     */
1296    public void selectResultTab() {
1297
1298        m_handler.selectResultTab();
1299    }
1300
1301    /**
1302     * Sets the function that provides the container information.
1303     *
1304     * @param containerInfoProvider the container info provider
1305     */
1306    public void setContainerInfoProvider(Supplier<CmsGalleryContainerInfo> containerInfoProvider) {
1307
1308        if (containerInfoProvider == null) {
1309            containerInfoProvider = () -> null;
1310        }
1311        m_containerInfoProvider = containerInfoProvider;
1312    }
1313
1314    /**
1315     * Sets the controller handler for gallery dialog.<p>
1316     *
1317     * @param handler the handler to set
1318     */
1319    public void setHandler(CmsGalleryControllerHandler handler) {
1320
1321        m_handler = handler;
1322    }
1323
1324    /**
1325     * Sets if the search should include expired or unreleased resources.<p>
1326     *
1327     * @param includeExpired if the search should include expired or unreleased resources
1328     * @param fireEvent true if a change event should be fired after setting the value
1329     */
1330    public void setIncludeExpired(boolean includeExpired, boolean fireEvent) {
1331
1332        m_searchObject.setIncludeExpired(includeExpired);
1333        m_searchObjectChanged = true;
1334        if (fireEvent) {
1335            ValueChangeEvent.fire(this, m_searchObject);
1336        }
1337
1338    }
1339
1340    /**
1341     * Stores the result view type.<p>
1342     *
1343     * @param resultViewType the result view type
1344     */
1345    public void setResultViewType(final String resultViewType) {
1346
1347        CmsRpcAction<Void> action = new CmsRpcAction<Void>() {
1348
1349            @Override
1350            public void execute() {
1351
1352                getGalleryService().saveResultViewType(resultViewType, this);
1353            }
1354
1355            @Override
1356            protected void onResponse(Void result) {
1357
1358                // nothing to do
1359            }
1360
1361        };
1362        action.execute();
1363    }
1364
1365    /**
1366     * Sets the search object changed flag to <code>true</code>.<p>
1367     */
1368    public void setSearchObjectChanged() {
1369
1370        m_searchObjectChanged = true;
1371    }
1372
1373    /**
1374     * Sets the "Show site selector" option.<p>
1375     *
1376     * @param isShowSiteSelector the new value for the option
1377     */
1378    public void setShowSiteSelector(boolean isShowSiteSelector) {
1379
1380        m_isShowSiteSelector = isShowSiteSelector;
1381    }
1382
1383    /**
1384     * Sets the start site.<p>
1385     *
1386     * @param startSite the start site
1387     */
1388    public void setStartSite(String startSite) {
1389
1390        m_startSite = startSite;
1391    }
1392
1393    /**
1394     * Sets the template context info provider.
1395     *
1396     * @param provider the template context info provider
1397     */
1398    public void setTemplateContextInfoProvider(Supplier<CmsTemplateContextInfo> provider) {
1399
1400        m_templateContextInfoProvider = provider;
1401    }
1402
1403    /**
1404     * Sorts the categories according to given parameters and updates the list.<p>
1405     *
1406     * @param sortParams the sort parameters
1407     * @param filter the filter to apply before sorting
1408     */
1409    public void sortCategories(String sortParams, String filter) {
1410
1411        List<CmsCategoryBean> categories;
1412        SortParams sort = SortParams.valueOf(sortParams);
1413        switch (sort) {
1414            case tree:
1415                m_handler.onUpdateCategoriesTree(m_dialogBean.getCategories(), m_searchObject.getCategories());
1416                break;
1417            case title_asc:
1418                categories = getFilteredCategories(filter);
1419                Collections.sort(categories, new CmsComparatorTitle(true));
1420                m_handler.onUpdateCategoriesList(categories, m_searchObject.getCategories());
1421                break;
1422            case title_desc:
1423                categories = getFilteredCategories(filter);
1424                Collections.sort(categories, new CmsComparatorTitle(false));
1425                m_handler.onUpdateCategoriesList(categories, m_searchObject.getCategories());
1426                break;
1427            case type_asc:
1428            case type_desc:
1429            case path_asc:
1430            case path_desc:
1431            case dateLastModified_asc:
1432            case dateLastModified_desc:
1433
1434            default:
1435        }
1436    }
1437
1438    /**
1439     * Sorts the galleries according to given parameters and updates the list.<p>
1440     *
1441     * @param sortParams the sort parameters
1442     * @param filter the filter to apply before sorting
1443     */
1444    public void sortGalleries(String sortParams, String filter) {
1445
1446        List<CmsGalleryFolderBean> galleries;
1447        if (CmsStringUtil.isNotEmptyOrWhitespaceOnly(filter)) {
1448            galleries = new ArrayList<CmsGalleryFolderBean>();
1449            for (CmsGalleryFolderBean galleryBean : m_dialogBean.getGalleries()) {
1450                if (galleryBean.matchesFilter(filter)) {
1451                    galleries.add(galleryBean);
1452                }
1453            }
1454        } else {
1455            galleries = m_dialogBean.getGalleries();
1456        }
1457        SortParams sort = SortParams.valueOf(sortParams);
1458        boolean grouped = false;
1459        switch (sort) {
1460            case title_asc:
1461                Collections.sort(galleries, new CmsComparatorTitle(true));
1462                break;
1463            case title_desc:
1464                Collections.sort(galleries, new CmsComparatorTitle(false));
1465                break;
1466            case type_asc:
1467                Collections.sort(galleries, new CmsComparatorType(true));
1468                break;
1469            case type_desc:
1470                Collections.sort(galleries, new CmsComparatorType(false));
1471                break;
1472            case path_asc:
1473                Collections.sort(galleries, new CmsComparatorPath(true));
1474                break;
1475            case path_desc:
1476                Collections.sort(galleries, new CmsComparatorPath(false));
1477                break;
1478            case grouped:
1479                Collections.sort(
1480                    galleries,
1481                    (
1482                        a,
1483                        b) -> ComparisonChain.start().compare(a.getGroup(), b.getGroup()).compare(
1484                            a.getPath(),
1485                            b.getPath()).result());
1486                grouped = true;
1487                break;
1488            case grouped_title:
1489                Collections.sort(
1490                    galleries,
1491                    (
1492                        a,
1493                        b) -> ComparisonChain.start().compare(a.getGroup(), b.getGroup()).compare(
1494                            a.getTitle(),
1495                            b.getTitle()).result());
1496                grouped = true;
1497                break;
1498            case tree:
1499                m_handler.onUpdateGalleryTree(galleryListToTree(galleries), m_searchObject.getGalleries());
1500                return;
1501            case dateLastModified_asc:
1502            case dateLastModified_desc:
1503            default:
1504                // not supported
1505                return;
1506        }
1507        m_handler.onUpdateGalleries(galleries, m_searchObject.getGalleries(), grouped);
1508    }
1509
1510    /**
1511     * Sorts the results according to given parameters and updates the list.<p>
1512     *
1513     * @param sortParams the sort parameters
1514     */
1515    public void sortResults(final String sortParams) {
1516
1517        m_searchObject.setSortOrder(sortParams);
1518        updateResultsTab(false);
1519    }
1520
1521    /**
1522     * Sorts the types according to given parameters and updates the list.<p>
1523     *
1524     * @param sortParams the sort parameters
1525     */
1526    public void sortTypes(String sortParams) {
1527
1528        List<CmsResourceTypeBean> types = getSearchTypes();
1529        SortParams sort = SortParams.valueOf(sortParams);
1530        switch (sort) {
1531            case title_asc:
1532                Collections.sort(types, new CmsComparatorTitle(true));
1533                break;
1534            case title_desc:
1535                Collections.sort(types, new CmsComparatorTitle(false));
1536                break;
1537            case type_asc:
1538                Collections.sort(types, new CmsComparatorType(true));
1539                break;
1540            case type_desc:
1541                Collections.sort(types, new CmsComparatorType(false));
1542                break;
1543            case dateLastModified_asc:
1544            case dateLastModified_desc:
1545            case path_asc:
1546            case path_desc:
1547            case tree:
1548            default:
1549                // not supported
1550                return;
1551        }
1552        m_handler.onUpdateTypes(types, m_searchObject.getTypes());
1553    }
1554
1555    /**
1556     * Updates the size of the active tab.<p>
1557     */
1558    public void updateActiveTabSize() {
1559
1560        m_handler.m_galleryDialog.updateSizes();
1561
1562    }
1563
1564    /**
1565     * Updates the content of the categories tab.<p>
1566     */
1567    public void updateCategoriesTab() {
1568
1569        if (m_dialogBean.getCategories() == null) {
1570            loadCategories();
1571        } else {
1572            m_handler.onCategoriesTabSelection();
1573        }
1574    }
1575
1576    /**
1577     * Updates the content of the galleries(folders) tab.<p>
1578     */
1579    public void updateGalleriesTab() {
1580
1581        if (m_dialogBean.getGalleries() == null) {
1582            loadGalleries();
1583        } else {
1584            m_handler.onGalleriesTabSelection();
1585        }
1586    }
1587
1588    /**
1589     * Updates the gallery data.<p>
1590     *
1591     * @param data the gallery data
1592     */
1593    public void updateGalleryData(CmsGalleryDataBean data) {
1594
1595        m_dialogBean = data;
1596        m_handler.updateGalleryData(m_searchObject, data, this);
1597    }
1598
1599    /**
1600     * Updates the gallery index and triggers a new search afterwards.<p>
1601     */
1602    public void updateIndex() {
1603
1604        CmsRpcAction<Void> action = new CmsRpcAction<Void>() {
1605
1606            @Override
1607            public void execute() {
1608
1609                start(200, true);
1610                getGalleryService().updateIndex(this);
1611            }
1612
1613            @Override
1614            protected void onResponse(Void result) {
1615
1616                stop(false);
1617                updateResultsTab(false);
1618                m_handler.hideShowPreviewButton(true);
1619            }
1620        };
1621        action.execute();
1622    }
1623
1624    /**
1625     * Updates the content of the results tab.<p>
1626     *
1627     * @param isNextPage signals if the next page should be loaded
1628     */
1629    public void updateResultsTab(final boolean isNextPage) {
1630
1631        // if the RPC call will be sent the search object is in a unchanged state
1632        m_searchObjectChanged = false;
1633        if (!m_handler.hasResultsTab()) {
1634
1635            return;
1636        }
1637        if (m_searchObject.isEmpty()) {
1638
1639            // don't search: notify the user that at least one search criteria should be selected
1640            if ((m_handler.m_galleryDialog.getResultsTab() == null)
1641                || m_handler.m_galleryDialog.getResultsTab().isSelected()) {
1642
1643                m_handler.showFirstTab();
1644            }
1645        } else {
1646
1647            // perform the search
1648
1649            /** The RPC search action for the gallery dialog. */
1650            CmsRpcAction<CmsGallerySearchBean> searchAction = new CmsRpcAction<CmsGallerySearchBean>() {
1651
1652                private int m_callId;
1653
1654                /**
1655                * @see org.opencms.gwt.client.rpc.CmsRpcAction#execute()
1656                */
1657                @Override
1658                public void execute() {
1659
1660                    start(0, true);
1661                    m_currentCallId++;
1662                    m_callId = m_currentCallId;
1663                    m_loading = true;
1664
1665                    CmsGallerySearchBean preparedObject = prepareSearchObject();
1666
1667                    if (isNextPage) {
1668                        preparedObject.setPage(preparedObject.getLastPage() + 1);
1669                    } else {
1670                        preparedObject.setPage(1);
1671                    }
1672                    getGalleryService().getSearch(preparedObject, this);
1673                }
1674
1675                /**
1676                * @see org.opencms.gwt.client.rpc.CmsRpcAction#onResponse(java.lang.Object)
1677                */
1678                @Override
1679                public void onResponse(CmsGallerySearchBean searchObj) {
1680
1681                    stop(false);
1682                    if (m_callId != m_currentCallId) {
1683                        return;
1684                    }
1685                    if (!isNextPage) {
1686                        m_handler.hideShowPreviewButton(true);
1687                    }
1688                    m_loading = false;
1689                    m_searchObject.setResults(searchObj.getResults());
1690                    m_searchObject.setResultCount(searchObj.getResultCount());
1691                    m_searchObject.setReplacedResults(searchObj.hasReplacedResults());
1692                    m_searchObject.setSortOrder(searchObj.getSortOrder());
1693                    m_searchObject.setPage(searchObj.getPage());
1694                    m_searchObject.setLastPage(searchObj.getLastPage());
1695                    m_searchObject.setNoUploadReason(searchObj.getNoUploadReason());
1696                    m_handler.onResultTabSelection(m_searchObject);
1697
1698                }
1699            };
1700            searchAction.execute();
1701        }
1702    }
1703
1704    /**
1705     * Updates the content of the types tab.<p>
1706     */
1707    public void updatesTypesTab() {
1708
1709        m_handler.onTypesTabSelection();
1710    }
1711
1712    /**
1713     * Returns the sitemap service instance.<p>
1714     *
1715     * @return the sitemap service instance
1716     */
1717    protected I_CmsVfsServiceAsync getVfsService() {
1718
1719        if (m_vfsService == null) {
1720            m_vfsService = CmsCoreProvider.getVfsService();
1721        }
1722        return m_vfsService;
1723    }
1724
1725    /**
1726     * Deletes a resource.<p>
1727     *
1728     * @param resourcePath the path of the resource to delete
1729     */
1730    protected void internalDeleteResource(final String resourcePath) {
1731
1732        CmsRpcAction<Void> action = new CmsRpcAction<Void>() {
1733
1734            @Override
1735            public void execute() {
1736
1737                start(0, false);
1738                getGalleryService().deleteResource(resourcePath, this);
1739            }
1740
1741            @Override
1742            protected void onResponse(Void result) {
1743
1744                stop(false);
1745                updateResultsTab(false);
1746            }
1747        };
1748        action.execute();
1749    }
1750
1751    /**
1752     * Removes a tab id from the internal list of tab ids.<p>
1753     *
1754     * @param tabId the id of the tab to remove
1755     */
1756    protected void removeTab(GalleryTabId tabId) {
1757
1758        if (m_tabIds != null) {
1759            List<GalleryTabId> tabs = new ArrayList<GalleryTabId>(Arrays.asList(m_tabIds));
1760            if (tabs.contains(tabId)) {
1761                m_tabIds = new GalleryTabId[tabs.size() - 1];
1762                tabs.remove(tabId);
1763                m_tabIds = tabs.toArray(new GalleryTabId[tabs.size()]);
1764            }
1765        }
1766    }
1767
1768    /**
1769     * Removes the types tab from the list of configured tabs.<p>
1770     * This will only take effect when executed before tab initialization.<p>
1771     */
1772    protected void removeTypesTab() {
1773
1774        removeTab(GalleryTabId.cms_tab_types);
1775    }
1776
1777    /**
1778     * Does the initial search if not already pre-fetched.<p>
1779     */
1780    void initialSearch() {
1781
1782        CmsRpcAction<CmsGallerySearchBean> searchAction = new CmsRpcAction<CmsGallerySearchBean>() {
1783
1784            @Override
1785            public void execute() {
1786
1787                start(0, true);
1788                m_dialogBean.setTreeToken(getTreeToken());
1789                getGalleryService().getSearch(m_dialogBean, this);
1790            }
1791
1792            @Override
1793            protected void onResponse(CmsGallerySearchBean result) {
1794
1795                stop(false);
1796                m_searchObject = result;
1797                m_handler.onInitialSearch(m_searchObject, m_dialogBean, CmsGalleryController.this, true);
1798            }
1799        };
1800        searchAction.execute();
1801    }
1802
1803    /**
1804     * Returns a consistent search object to be used for the search.<p>
1805     *
1806     * For the search at least one resource type should be provided.
1807     * The corresponding resource types will be added to the search object, if no or only gallery folder are selected.
1808     *
1809     * @return the search object
1810     */
1811    CmsGallerySearchBean prepareSearchObject() {
1812
1813        CmsGallerySearchBean preparedSearchObj = new CmsGallerySearchBean(m_searchObject);
1814        preparedSearchObj.setReferencePath(m_dialogBean.getReferenceSitePath());
1815        // add the available types to the search object used for next search,
1816        // if the criteria for types are empty
1817        if (CmsClientCollectionUtil.isEmptyOrNull(m_searchObject.getTypes())) {
1818            // no galleries is selected, provide all available types
1819            if (CmsClientCollectionUtil.isEmptyOrNull(m_searchObject.getGalleries())) {
1820                // additionally provide all available gallery folders if in 'widget' and 'editor' dialog-mode and no folder has been selected
1821                if (((m_dialogMode == I_CmsGalleryProviderConstants.GalleryMode.widget)
1822                    || (m_dialogMode == I_CmsGalleryProviderConstants.GalleryMode.editor))
1823                    && CmsClientCollectionUtil.isEmptyOrNull(m_searchObject.getFolders())) {
1824                    ArrayList<String> availableGalleries = new ArrayList<String>();
1825                    for (CmsGalleryFolderBean galleryPath : m_dialogBean.getGalleries()) {
1826                        availableGalleries.add(galleryPath.getPath());
1827                    }
1828                    preparedSearchObj.setGalleries(availableGalleries);
1829                }
1830                ArrayList<String> availableTypes = new ArrayList<String>();
1831                for (CmsResourceTypeBean type : m_dialogBean.getTypes()) {
1832                    // exclude deactivated types
1833                    if (!type.isDeactivated()) {
1834                        availableTypes.add(type.getType());
1835                    }
1836                }
1837                preparedSearchObj.setServerSearchTypes(availableTypes);
1838                // at least one gallery is selected
1839            } else {
1840
1841                // get the resource types associated with the selected galleries
1842                HashSet<String> galleryTypes = new HashSet<String>();
1843                for (CmsGalleryFolderBean gallery : m_dialogBean.getGalleries()) {
1844                    if (m_searchObject.getGalleries().contains(gallery.getPath())) {
1845                        galleryTypes.addAll(gallery.getContentTypes());
1846                    }
1847                }
1848
1849                HashSet<String> availableTypes = new HashSet<String>();
1850                for (CmsResourceTypeBean type : m_dialogBean.getTypes()) {
1851                    availableTypes.add(type.getType());
1852                }
1853
1854                preparedSearchObj.setServerSearchTypes(
1855                    new ArrayList<String>(CmsClientCollectionUtil.intersection(availableTypes, galleryTypes)));
1856            }
1857        } else {
1858            preparedSearchObj.setServerSearchTypes(m_searchObject.getTypes());
1859        }
1860        if (m_galleriesChanged) {
1861            preparedSearchObj.setGalleriesChanged(true);
1862            m_galleriesChanged = false;
1863        }
1864        preparedSearchObj.setContainerInfo(m_containerInfoProvider.get());
1865        preparedSearchObj.setTemplateContextInfo(m_templateContextInfoProvider.get());
1866        return preparedSearchObj;
1867
1868    }
1869
1870    /**
1871     * Converts categories tree to a list of info beans.<p>
1872     *
1873     * @param categoryList the category list
1874     * @param entries the tree entries
1875     */
1876    private void categoryTreeToList(List<CmsCategoryBean> categoryList, List<CmsCategoryTreeEntry> entries) {
1877
1878        if (entries == null) {
1879            return;
1880        }
1881        // skipping the root tree entry where the path property is empty
1882        for (CmsCategoryTreeEntry entry : entries) {
1883            CmsCategoryBean bean = new CmsCategoryBean(entry);
1884            categoryList.add(bean);
1885            categoryTreeToList(categoryList, entry.getChildren());
1886        }
1887    }
1888
1889    /**
1890     * Creates a tree structure from the given gallery folder list.<p>
1891     * The tree may have several entries at root level.<p>
1892     *
1893     * @param galleries the gallery folder list
1894     *
1895     * @return the list of tree entries
1896     */
1897    private List<CmsGalleryTreeEntry> galleryListToTree(List<CmsGalleryFolderBean> galleries) {
1898
1899        List<CmsGalleryTreeEntry> result = new ArrayList<CmsGalleryTreeEntry>();
1900        Collections.sort(galleries, new CmsComparatorPath(true));
1901        CmsGalleryTreeEntry previous = null;
1902        for (CmsGalleryFolderBean folderBean : galleries) {
1903            CmsGalleryTreeEntry current = new CmsGalleryTreeEntry(folderBean);
1904            CmsGalleryTreeEntry parent = null;
1905            if (previous != null) {
1906                parent = lookForParent(previous, current.getPath());
1907            }
1908            if (parent != null) {
1909                parent.addChild(current);
1910            } else {
1911                result.add(current);
1912            }
1913            previous = current;
1914        }
1915        return result;
1916    }
1917
1918    /**
1919     * Gets the list of categories.<p>
1920     *
1921     * @return a list of category beans
1922     */
1923    private List<CmsCategoryBean> getCategoryList() {
1924
1925        List<CmsCategoryBean> result = new ArrayList<CmsCategoryBean>();
1926        categoryTreeToList(result, m_dialogBean.getCategories());
1927        return result;
1928    }
1929
1930    /**
1931     * Helper method for getting the default (sub)site root for a list of site selector options.<p>
1932     *
1933     * @param options the list of options
1934     *
1935     * @return the default (sub)site root
1936     */
1937    private String getDefaultSiteRoot(List<CmsSiteSelectorOption> options) {
1938
1939        if (m_startSite != null) {
1940            return m_startSite;
1941        } else if ((options != null) && (!options.isEmpty())) {
1942            String defaultOption = options.get(0).getSiteRoot();
1943            for (CmsSiteSelectorOption option : options) {
1944                if (option.getType().equals(CmsSiteSelectorOption.Type.currentSubsite)) {
1945                    return option.getSiteRoot();
1946                }
1947                if (option.isCurrentSite()) {
1948                    defaultOption = option.getSiteRoot();
1949                }
1950            }
1951            return defaultOption;
1952        } else {
1953            return CmsCoreProvider.get().getSiteRoot();
1954        }
1955    }
1956
1957    /**
1958    * Gets the filtered list of categories.<p>
1959    *
1960    * @param filter the search string to use for filtering
1961    *
1962    * @return the filtered category beans
1963    */
1964    private List<CmsCategoryBean> getFilteredCategories(String filter) {
1965
1966        List<CmsCategoryBean> result;
1967        if (CmsStringUtil.isNotEmptyOrWhitespaceOnly(filter)) {
1968            result = new ArrayList<CmsCategoryBean>();
1969            for (CmsCategoryBean category : getCategoryList()) {
1970                if (category.matchesFilter(filter)) {
1971                    result.add(category);
1972                }
1973            }
1974        } else {
1975            result = getCategoryList();
1976        }
1977        return result;
1978    }
1979
1980    /**
1981     * Returns the preview provider name for the given resource type, or <code>null</code> if none available.<p>
1982     *
1983     * @param resourceType the resource type
1984     *
1985     * @return the preview provider name
1986     */
1987    private String getProviderName(String resourceType) {
1988
1989        for (CmsResourceTypeBean typeBean : m_dialogBean.getTypes()) {
1990            if (typeBean.getType().equals(resourceType)) {
1991                return typeBean.getPreviewProviderName();
1992            }
1993        }
1994        return null;
1995    }
1996
1997    /**
1998     * Loading all available categories.<p>
1999     */
2000    private void loadCategories() {
2001
2002        CmsRpcAction<List<CmsCategoryTreeEntry>> action = new CmsRpcAction<List<CmsCategoryTreeEntry>>() {
2003
2004            @Override
2005            public void execute() {
2006
2007                start(200, true);
2008                CmsCoreProvider.getService().getCategoriesForSitePath(m_dialogBean.getReferenceSitePath(), this);
2009            }
2010
2011            @Override
2012            protected void onResponse(List<CmsCategoryTreeEntry> result) {
2013
2014                m_dialogBean.setCategories(result);
2015                m_handler.setCategoriesTabContent(result, new ArrayList<String>());
2016                m_handler.onCategoriesTabSelection();
2017                stop(false);
2018            }
2019        };
2020        action.execute();
2021    }
2022
2023    /**
2024     * Loading all available galleries.<p>
2025     */
2026    private void loadGalleries() {
2027
2028        CmsRpcAction<List<CmsGalleryFolderBean>> action = new CmsRpcAction<List<CmsGalleryFolderBean>>() {
2029
2030            @Override
2031            public void execute() {
2032
2033                start(200, true);
2034                List<String> types = new ArrayList<String>();
2035                for (CmsResourceTypeBean type : m_dialogBean.getTypes()) {
2036                    types.add(type.getType());
2037                }
2038
2039                // not sure if this ever gets called in practice
2040                getGalleryService().getGalleries("/", types, this);
2041            }
2042
2043            @Override
2044            protected void onResponse(List<CmsGalleryFolderBean> result) {
2045
2046                m_dialogBean.setGalleries(result);
2047                m_handler.setGalleriesTabContent(result, m_searchObject.getGalleries(), false);
2048                m_handler.onGalleriesTabSelection();
2049                stop(false);
2050            }
2051        };
2052        action.execute();
2053    }
2054
2055    /**
2056     * Looks for an ancestor tree entry for the given path.<p>
2057     *
2058     * @param possibleParent the possible parent entry
2059     * @param targetPath the target path
2060     *
2061     * @return the parent entry or <code>null</code> if there is none
2062     */
2063    private CmsGalleryTreeEntry lookForParent(CmsGalleryTreeEntry possibleParent, String targetPath) {
2064
2065        if (targetPath.startsWith(possibleParent.getPath())) {
2066            return possibleParent;
2067        }
2068        if (possibleParent.getParent() != null) {
2069            return lookForParent(possibleParent.getParent(), targetPath);
2070        }
2071        return null;
2072    }
2073
2074}