001/*
002 * This library is part of OpenCms -
003 * the Open Source Content Management System
004 *
005 * Copyright (c) Alkacon Software GmbH & Co. KG (https://www.alkacon.com)
006 *
007 * This library is free software; you can redistribute it and/or
008 * modify it under the terms of the GNU Lesser General Public
009 * License as published by the Free Software Foundation; either
010 * version 2.1 of the License, or (at your option) any later version.
011 *
012 * This library is distributed in the hope that it will be useful,
013 * but WITHOUT ANY WARRANTY; without even the implied warranty of
014 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
015 * Lesser General Public License for more details.
016 *
017 * For further information about Alkacon Software GmbH & Co. KG, please see the
018 * company website: https://www.alkacon.com
019 *
020 * For further information about OpenCms, please see the
021 * project website: https://www.opencms.org
022 *
023 * You should have received a copy of the GNU Lesser General Public
024 * License along with this library; if not, write to the Free Software
025 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
026 */
027
028package org.opencms.workplace.explorer;
029
030import org.opencms.db.CmsResourceState;
031import org.opencms.file.CmsFolder;
032import org.opencms.file.CmsObject;
033import org.opencms.file.CmsProject;
034import org.opencms.file.CmsProperty;
035import org.opencms.file.CmsPropertyDefinition;
036import org.opencms.file.CmsResource;
037import org.opencms.file.CmsResourceFilter;
038import org.opencms.file.types.CmsResourceTypePlain;
039import org.opencms.file.types.I_CmsResourceType;
040import org.opencms.i18n.CmsMessages;
041import org.opencms.jsp.CmsJspActionElement;
042import org.opencms.loader.CmsLoaderException;
043import org.opencms.main.CmsException;
044import org.opencms.main.CmsLog;
045import org.opencms.main.OpenCms;
046import org.opencms.site.CmsSite;
047import org.opencms.util.CmsStringUtil;
048import org.opencms.workplace.CmsWorkplace;
049import org.opencms.workplace.CmsWorkplaceSettings;
050
051import java.util.ArrayList;
052import java.util.Iterator;
053import java.util.List;
054import java.util.Locale;
055import java.util.StringTokenizer;
056
057import org.apache.commons.logging.Log;
058
059import jakarta.servlet.http.HttpServletRequest;
060
061/**
062 * Generates the tree view for the OpenCms Workplace.<p>
063 *
064 * The following Workplace files use this class:
065 * <ul>
066 * <li>/views/explorer/tree_fs.jsp
067 * <li>/views/explorer/tree_files.jsp
068 * </ul>
069 * <p>
070 *
071 * @since 6.0.0
072 */
073public class CmsTree extends CmsWorkplace {
074
075    /** Request parameter name for the includesfiles parameter. */
076    public static final String PARAM_INCLUDEFILES = "includefiles";
077
078    /** Request parameter name for the lastknown parameter. */
079    public static final String PARAM_LASTKNOWN = "lastknown";
080
081    /** Request parameter name for the projectaware parameter. */
082    public static final String PARAM_PROJECTAWARE = "projectaware";
083
084    /** Request parameter name for the resource parameter. */
085    public static final String PARAM_RESOURCE = "resource";
086
087    /** Request parameter name for the rootloaded parameter. */
088    public static final String PARAM_ROOTLOADED = "rootloaded";
089
090    /** Request parameter name for the showsiteselector parameter. */
091    public static final String PARAM_SHOWSITESELECTOR = "showsiteselector";
092
093    /** Request parameter name for the integrator parameter. */
094    public static final String PARAM_INTEGRATOR = "integrator";
095
096    /** Request parameter name for the treesite parameter. */
097    public static final String PARAM_TREESITE = "treesite";
098
099    /** Request parameter name for the type parameter. */
100    public static final String PARAM_TYPE = "type";
101
102    /** The log object for this class. */
103    private static final Log LOG = CmsLog.getLog(CmsTree.class);
104
105    /** Type name for showing the tree when copying resources. */
106    private static final String TYPE_COPY = "copy";
107
108    /** Type name for showing the tree when creating page links in the editor. */
109    private static final String TYPE_PAGELINK = "pagelink";
110
111    /** Type name for showing the tree in preferences dialog. */
112    private static final String TYPE_PREFERENCES = "preferences";
113
114    /** Type name for showing the tree when creating siblings. */
115    private static final String TYPE_SIBLING = "sibling";
116
117    /** Type name for showing the tree in a widget dialog. */
118    private static final String TYPE_VFSWIDGET = "vfswidget";
119
120    /** Indicates if only folders or files and folders should be included in the tree. */
121    private boolean m_includeFiles;
122
123    /** Indicates if a complete new tree should be created. */
124    private boolean m_newTree;
125
126    /** Indicates project awareness, ie. if resources outside of the current project should be displayed as normal. */
127    private boolean m_projectAware = true;
128
129    /** The name of the root folder to display the tree from, usually "/". */
130    private String m_rootFolder;
131
132    /** Flag to indicate if the site selector should be shown in popup tree window. */
133    private boolean m_showSiteSelector;
134
135    /** The name of the start folder (or "last known" folder) to be loaded. */
136    private String m_startFolder;
137
138    /** The name of the target folder to be loaded. */
139    private String m_targetFolder;
140
141    /** The type of the tree (e.g. "copy", "project" etc.). */
142    private String m_treeType;
143
144    /**
145     * Public constructor.<p>
146     *
147     * @param jsp an initialized JSP action element
148     */
149    public CmsTree(CmsJspActionElement jsp) {
150
151        super(jsp);
152    }
153
154    /**
155     * Returns the HTML for the tree initialization.<p>
156     *
157     * @param cms the CmsObject
158     * @param encoding the current encoding
159     * @param skinUri the current skin URI
160     * @return the HTML for the tree initialization
161     */
162    public static String initTree(CmsObject cms, String encoding, String skinUri) {
163
164        StringBuffer retValue = new StringBuffer(512);
165        String servletUrl = OpenCms.getSystemInfo().getOpenCmsContext();
166
167        // get the localized workplace messages
168        // TODO: Why a new message object, can it not be obtained from session?
169        Locale locale = cms.getRequestContext().getLocale();
170        CmsMessages messages = OpenCms.getWorkplaceManager().getMessages(locale);
171
172        retValue.append("function initTreeResources() {\n");
173        retValue.append("\tinitResources(\"");
174        retValue.append(encoding);
175        retValue.append("\", \"");
176        retValue.append(PATH_WORKPLACE);
177        retValue.append("\", \"");
178        retValue.append(skinUri);
179        retValue.append("\", \"");
180        retValue.append(servletUrl);
181        retValue.append("\");\n");
182
183        // get all available resource types
184        List<I_CmsResourceType> allResTypes = OpenCms.getResourceManager().getResourceTypes();
185        for (int i = 0; i < allResTypes.size(); i++) {
186            // loop through all types
187            I_CmsResourceType type = allResTypes.get(i);
188            int curTypeId = type.getTypeId();
189            String curTypeName = type.getTypeName();
190            // get the settings for the resource type
191            CmsExplorerTypeSettings settings = OpenCms.getWorkplaceManager().getExplorerTypeSetting(curTypeName);
192            if (settings == null) {
193                if (LOG.isWarnEnabled()) {
194                    LOG.warn(Messages.get().getBundle().key(Messages.LOG_MISSING_SETTINGS_ENTRY_1, curTypeName));
195                }
196                settings = OpenCms.getWorkplaceManager().getExplorerTypeSetting(
197                    CmsResourceTypePlain.getStaticTypeName());
198            }
199            retValue.append("\taddResourceType(");
200            retValue.append(curTypeId);
201            retValue.append(", \"");
202            retValue.append(curTypeName);
203            retValue.append("\",\t\"");
204            retValue.append(messages.key(settings.getKey()));
205            retValue.append("\",\t\"" + CmsWorkplace.RES_PATH_FILETYPES);
206            retValue.append(settings.getIcon());
207            retValue.append("\");\n");
208        }
209
210        retValue.append("}\n\n");
211        retValue.append("initTreeResources();\n");
212        String sharedFolder = OpenCms.getSiteManager().getSharedFolder();
213        if (sharedFolder != null) {
214            retValue.append("sharedFolderName = '" + sharedFolder + "';");
215        }
216        return retValue.toString();
217    }
218
219    /**
220     * Determines the root folder of the current tree dependent on users setting of explorer view restriction.<p>
221     *
222     * @return the root folder resource name to display
223     */
224    public String getRootFolder() {
225
226        if (m_rootFolder == null) {
227            String folder = "/";
228            if ((getTreeType() == null) && getSettings().getUserSettings().getRestrictExplorerView()) {
229                folder = getSettings().getUserSettings().getStartFolder();
230            }
231            try {
232                getCms().readFolder(folder, CmsResourceFilter.IGNORE_EXPIRATION);
233            } catch (CmsException e) {
234                if (LOG.isInfoEnabled()) {
235                    LOG.info(e.getLocalizedMessage(), e);
236                }
237                folder = "/";
238            }
239            m_rootFolder = folder;
240        }
241        return m_rootFolder;
242    }
243
244    /**
245     * Returns the HTML for the site selector box for the explorer tree window.<p>
246     *
247     * @param htmlAttributes optional attributes for the &lt;select&gt; tag
248     * @return HTML code for the site selector box
249     */
250    public String getSiteSelector(String htmlAttributes) {
251
252        List<String> options = new ArrayList<String>();
253        List<String> values = new ArrayList<String>();
254        int selectedIndex = 0;
255        String preSelection = getSettings().getTreeSite(getTreeType());
256        if (preSelection == null) {
257            if ("".equals(getCms().getRequestContext().getSiteRoot())) {
258                // we are in the root site, getCurrentSite(CmsObject) includes NOT the root site
259                preSelection = "";
260            } else {
261                // get the site root of the current site
262                preSelection = OpenCms.getSiteManager().getCurrentSite(getCms()).getSiteRoot();
263            }
264            // set the tree site to avoid discrepancies between selector and tree
265            getSettings().setTreeSite(getTreeType(), preSelection);
266        }
267
268        boolean includeRootSite = true;
269        boolean showSiteUrls = false;
270        if (TYPE_PAGELINK.equals(getTreeType())) {
271            // in wysiwyg editor link dialog, don't show root site, but show site URLs
272            includeRootSite = false;
273            showSiteUrls = true;
274        }
275        List<CmsSite> sites = OpenCms.getSiteManager().getAvailableSites(
276            getCms(),
277            includeRootSite,
278            true,
279            getCms().getRequestContext().getOuFqn());
280
281        Iterator<CmsSite> i = sites.iterator();
282        int pos = 0;
283        while (i.hasNext()) {
284            CmsSite site = i.next();
285            values.add(site.getSiteRoot());
286            String curOption = substituteSiteTitle(site.getTitle());
287            if (showSiteUrls && (site.getSiteMatcher() != null)) {
288                // show the site URL in editor link dialog tree
289                curOption = site.getUrl() + " (" + curOption + ")";
290                if (getCms().getRequestContext().getSiteRoot().equals(site.getSiteRoot())) {
291                    // mark the current workplace site in selector
292                    curOption = "*" + curOption;
293                }
294            }
295
296            if (site.getSiteRoot().equals(preSelection)) {
297                // this is the user's currently selected site
298                selectedIndex = pos;
299            }
300            options.add(curOption);
301            pos++;
302        }
303
304        return buildSelect(htmlAttributes, options, values, selectedIndex);
305    }
306
307    /**
308     * Returns the html for the explorer tree.<p>
309     *
310     * @return the html for the explorer tree
311     */
312    public String getTree() {
313
314        StringBuffer result = new StringBuffer(2048);
315
316        List<String> targetFolderList = new ArrayList<String>();
317        if (getTargetFolder() != null) {
318            // check if there is more than one folder to update (e.g. move operation)
319            StringTokenizer T = new StringTokenizer(getTargetFolder(), "|");
320            while (T.hasMoreTokens()) {
321                String currentFolder = T.nextToken().trim();
322                //                if (OpenCms.getSiteManager().startsWithShared(currentFolder)) {
323                //                    currentFolder = OpenCms.getSiteManager().cutShared(currentFolder);
324                //                }
325                targetFolderList.add(currentFolder);
326            }
327        } else {
328            targetFolderList.add(null);
329        }
330
331        String storedSiteRoot = null;
332        try {
333            CmsFolder folder = null;
334            List<CmsResource> resources = new ArrayList<CmsResource>();
335            String oldSiteRoot = getCms().getRequestContext().getSiteRoot();
336
337            Iterator<String> targets = targetFolderList.iterator();
338            while (targets.hasNext()) {
339                // iterate over all given target folders
340                String currentTargetFolder = targets.next();
341
342                if (getSettings().getTreeSite(getTreeType()) != null) {
343                    // change the site root for popup window with site selector
344                    storedSiteRoot = getCms().getRequestContext().getSiteRoot();
345                    if (newTree() && (currentTargetFolder == null)) {
346                        currentTargetFolder = "/";
347                    }
348                    getCms().getRequestContext().setSiteRoot(getSettings().getTreeSite(getTreeType()));
349                    try {
350                        // check presence of target folder
351                        getCms().readFolder(currentTargetFolder, CmsResourceFilter.ONLY_VISIBLE_NO_DELETED);
352                    } catch (CmsException e) {
353                        // target folder not found, set it to "/"
354                        if (LOG.isInfoEnabled()) {
355                            LOG.info(e.getLocalizedMessage(), e);
356                        }
357                        currentTargetFolder = "/";
358                    }
359                }
360
361                // read the selected folder
362                try {
363                    folder = getCms().readFolder(currentTargetFolder, CmsResourceFilter.ONLY_VISIBLE_NO_DELETED);
364                } catch (CmsException e) {
365                    // return with error
366                    return printError(e);
367                }
368
369                String startFolder = getStartFolder();
370                String rcSite = getCms().getRequestContext().getSiteRoot();
371                if (OpenCms.getSiteManager().startsWithShared(currentTargetFolder)
372                    && OpenCms.getSiteManager().startsWithShared(rcSite)) {
373                    currentTargetFolder = currentTargetFolder.substring(
374                        OpenCms.getSiteManager().getSharedFolder().length() - 1);
375                }
376                if ((startFolder == null) || (!currentTargetFolder.startsWith(startFolder))) {
377                    // no (valid) start folder given, just load current folder
378                    try {
379                        if (includeFiles()) {
380                            resources.addAll(
381                                getCms().getResourcesInFolder(
382                                    currentTargetFolder,
383                                    CmsResourceFilter.ONLY_VISIBLE_NO_DELETED));
384                        } else {
385                            resources.addAll(
386                                getCms().getSubFolders(currentTargetFolder, CmsResourceFilter.ONLY_VISIBLE_NO_DELETED));
387                        }
388                    } catch (CmsException e) {
389                        // return with error
390                        return printError(e);
391                    }
392                } else {
393                    // valid start folder given, load all folders between start and current folder
394                    try {
395                        if (includeFiles()) {
396                            resources.addAll(
397                                getCms().getResourcesInFolder(startFolder, CmsResourceFilter.ONLY_VISIBLE_NO_DELETED));
398                        } else {
399                            resources.addAll(
400                                getCms().getSubFolders(startFolder, CmsResourceFilter.ONLY_VISIBLE_NO_DELETED));
401                        }
402                        StringTokenizer tok = new StringTokenizer(
403                            currentTargetFolder.substring(startFolder.length()),
404                            "/");
405                        while (tok.hasMoreTokens()) {
406                            startFolder += tok.nextToken() + "/";
407                            if (includeFiles()) {
408                                resources.addAll(
409                                    getCms().getResourcesInFolder(
410                                        startFolder,
411                                        CmsResourceFilter.ONLY_VISIBLE_NO_DELETED));
412                            } else {
413                                resources.addAll(
414                                    getCms().getSubFolders(startFolder, CmsResourceFilter.ONLY_VISIBLE_NO_DELETED));
415                            }
416                        }
417                    } catch (CmsException e) {
418                        // return with error
419                        return printError(e);
420                    }
421                }
422            }
423
424            result.append("function init() {\n");
425
426            if (newTree()) {
427                // new tree must be reloaded
428                result.append("parent.initTree();\n");
429                result.append(getRootNode());
430            }
431
432            // read the list of project resource to select which resource is "inside" or "outside"
433            List<String> projectResources = new ArrayList<String>();
434            if (isProjectAware()) {
435                try {
436                    projectResources = getCms().readProjectResources(getCms().getRequestContext().getCurrentProject());
437                } catch (CmsException e) {
438                    // use an empty list (all resources are "outside")
439                    if (LOG.isInfoEnabled()) {
440                        LOG.info(e.getLocalizedMessage(), e);
441                    }
442                }
443            }
444
445            // now output all the tree nodes
446            Iterator<CmsResource> i = resources.iterator();
447            while (i.hasNext()) {
448                CmsResource resource = i.next();
449                boolean grey = false;
450                if (isProjectAware()) {
451                    grey = !CmsProject.isInsideProject(projectResources, resource);
452                }
453                if (!grey) {
454                    try {
455                        OpenCms.getResourceManager().getResourceType(resource.getTypeId());
456                    } catch (CmsLoaderException e) {
457                        // if resource type is not found
458                        grey = true;
459                    }
460                }
461
462                result.append(
463                    getNode(
464                        resource.getRootPath(),
465                        resource.getName(),
466                        resource.getTypeId(),
467                        resource.isFolder(),
468                        resource.getState(),
469                        grey));
470            }
471
472            if (includeFiles()) {
473                result.append("parent.setIncludeFiles(true);\n");
474            }
475            result.append("parent.setProjectAware(").append(isProjectAware()).append(");\n");
476            if (getTreeType() != null) {
477                // this is a popup window tree
478                result.append("parent.setTreeType(\"");
479                result.append(getTreeType());
480                result.append("\");\n");
481                String curSite = getSettings().getTreeSite(getTreeType());
482                if (curSite != null) {
483                    // add the current site as prefix if present
484                    result.append("parent.setSitePrefix(\"");
485                    result.append(getSitePrefix(curSite, oldSiteRoot));
486                    result.append("\");\n");
487                }
488            }
489            // set the root folder in javascript
490            result.append("parent.setRootFolder(\"");
491            result.append(getRootFolder());
492            result.append("\");\n");
493
494            if (folder != null) {
495                if (newTree()) {
496                    // new tree
497                    result.append("parent.showTree(parent.tree_display.document, \"");
498                    result.append(folder.getRootPath().hashCode());
499                    result.append("\");\n");
500                } else {
501                    // update the current tree with the children of the selected node
502                    if (resources.size() == 0) {
503                        // the node had no children
504                        result.append("parent.setNoChilds(\"");
505                        result.append(folder.getRootPath().hashCode());
506                        result.append("\");\n");
507                    }
508                    result.append("parent.showLoadedNodes(parent.tree_display.document,\"");
509                    result.append(folder.getRootPath().hashCode());
510                    result.append("\");\n");
511                }
512            }
513
514            result.append("}\n");
515        } finally {
516            if (storedSiteRoot != null) {
517                getCms().getRequestContext().setSiteRoot(storedSiteRoot);
518            }
519        }
520        return result.toString();
521    }
522
523    /**
524     * Returns the type of this tree (e.g. "copy", "project" etc.),
525     * if null this is the default explorer version.<p>
526     *
527     * @return the current type of the tree (e.g. "copy", "project" etc.)
528     */
529    public String getTreeType() {
530
531        return m_treeType;
532    }
533
534    /**
535     * Indicates if only folders or files and folders should be included in the tree.<p>
536     *
537     * @return true if files and folders should be included in the tree
538     */
539    public boolean includeFiles() {
540
541        return m_includeFiles;
542    }
543
544    /**
545     * Returns the HTML for the tree initialization.<p>
546     *
547     * @return the HTML for the tree initialization
548     */
549    public String initTree() {
550
551        return initTree(getCms(), getEncoding(), getSkinUri());
552    }
553
554    /**
555     * Returns the project awareness flag.<p>
556     *
557     * @return the project awareness flag
558     */
559    public boolean isProjectAware() {
560
561        return m_projectAware;
562    }
563
564    /**
565     * Sets the project awareness flag.<p>
566     *
567     * @param projectAware the project awareness flag to set
568     */
569    public void setProjectAware(boolean projectAware) {
570
571        m_projectAware = projectAware;
572    }
573
574    /**
575     * Indicates if the site selector should be shown depending on the tree type, initial settings and the count of accessible sites.<p>
576     *
577     * @return true if site selector should be shown, otherwise false
578     */
579    public boolean showSiteSelector() {
580
581        return m_showSiteSelector;
582    }
583
584    /**
585     * @see org.opencms.workplace.CmsWorkplace#initWorkplaceRequestValues(org.opencms.workplace.CmsWorkplaceSettings, jakarta.servlet.http.HttpServletRequest)
586     */
587    @Override
588    protected void initWorkplaceRequestValues(CmsWorkplaceSettings settings, HttpServletRequest request) {
589
590        setIncludeFiles(Boolean.valueOf(request.getParameter(PARAM_INCLUDEFILES)).booleanValue());
591        setProjectAware(Boolean.valueOf(request.getParameter(PARAM_PROJECTAWARE)).booleanValue());
592        boolean rootloaded = Boolean.valueOf(request.getParameter(PARAM_ROOTLOADED)).booleanValue();
593        String resource = request.getParameter(PARAM_RESOURCE);
594
595        setTreeType(request.getParameter(PARAM_TYPE));
596        String treeSite = request.getParameter(PARAM_TREESITE);
597        if ((getTreeType() != null) && (treeSite != null)) {
598            getSettings().setTreeSite(getTreeType(), treeSite);
599        }
600
601        if (getSettings().getTreeSite(getTreeType()) != null) {
602            String site = getCms().getRequestContext().getSiteRoot();
603            try {
604                getCms().getRequestContext().setSiteRoot(getSettings().getTreeSite(getTreeType()));
605                if (!getCms().existsResource(resource)) {
606                    resource = null;
607                }
608            } finally {
609                getCms().getRequestContext().setSiteRoot(site);
610            }
611        } else {
612            if (!getCms().existsResource(resource)) {
613                resource = null;
614            }
615        }
616
617        computeSiteSelector(request);
618
619        String currentResource;
620        if (getTreeType() == null) {
621            currentResource = getSettings().getExplorerResource();
622        } else {
623            // get the current tree resource from the settings for a special tree type
624            currentResource = getSettings().getTreeResource(getTreeType());
625        }
626
627        String lastknown = request.getParameter(PARAM_LASTKNOWN);
628        // both "resource" and "lastknown" must be folders
629
630        if (resource != null) {
631            resource = CmsResource.getFolderPath(resource);
632        }
633        if ((lastknown != null) && (!lastknown.endsWith("/"))) {
634            lastknown += "/";
635        }
636
637        String rootFolder = getRootFolder();
638        if (rootFolder.equals(resource) && !rootFolder.equals(currentResource) && (lastknown == null) && !rootloaded) {
639            // direct load of a new tree with subtree (e.g. when returning from an editor)
640            lastknown = getRootFolder();
641            resource = CmsResource.getFolderPath(currentResource);
642            setNewTree(true);
643        } else if (rootFolder.equals(resource)) {
644            // load new tree if not already loaded
645            setNewTree(!rootloaded);
646        } else {
647            setNewTree(false);
648        }
649
650        if (getTreeType() != null) {
651            getSettings().setTreeResource(getTreeType(), resource);
652        }
653
654        setTargetFolder(resource);
655        setStartFolder(lastknown);
656    }
657
658    /**
659     * Determines if the site selector frame should be shown depending on the tree type or the value of a request parameter.<p>
660     *
661     * If only one site is available, the site selector is not displayed.<p>
662     *
663     * @param request the HttpServletRequest to check
664     */
665    private void computeSiteSelector(HttpServletRequest request) {
666
667        boolean selectorForType = TYPE_SIBLING.equals(getTreeType())
668            || TYPE_COPY.equals(getTreeType())
669            || TYPE_PAGELINK.equals(getTreeType())
670            || TYPE_PREFERENCES.equals(getTreeType());
671        boolean showFromRequest = Boolean.valueOf(request.getParameter(PARAM_SHOWSITESELECTOR)).booleanValue();
672        if (selectorForType || showFromRequest) {
673            // get all available sites
674            int siteCount = OpenCms.getSiteManager().getAvailableSites(getCms(), true).size();
675            setShowSiteSelector(siteCount > 1);
676            return;
677        }
678        setShowSiteSelector(false);
679    }
680
681    /**
682     * Creates the output for a tree node.<p>
683     *
684     * @param path the path of the resource represented by this tree node
685     * @param title the resource name
686     * @param type the resource type
687     * @param folder if the resource is a folder
688     * @param state the resource state
689     * @param grey if true, the node is displayed in grey
690     *
691     * @return the output for a tree node
692     */
693    private String getNode(String path, String title, int type, boolean folder, CmsResourceState state, boolean grey) {
694
695        StringBuffer result = new StringBuffer(64);
696        String parent = CmsResource.getParentFolder(path);
697        result.append("parent.aC(\"");
698        // name
699        result.append(title);
700        result.append("\",");
701        // type
702        result.append(type);
703        result.append(",");
704        // folder
705        if (folder) {
706            result.append(1);
707        } else {
708            result.append(0);
709        }
710        result.append(",");
711        // hashcode of path
712        result.append(path.hashCode());
713        result.append(",");
714        // hashcode of parent path
715        result.append((parent != null) ? parent.hashCode() : 0);
716        result.append(",");
717        // resource state
718        result.append(state);
719        result.append(",");
720        // project status
721        if (grey) {
722            result.append(1);
723        } else {
724            result.append(0);
725        }
726        result.append(");\n");
727        return result.toString();
728    }
729
730    /**
731     * Creates a node entry for the root node of the current site.<p>
732     *
733     * @return a node entry for the root node of the current site
734     */
735    private String getRootNode() {
736
737        CmsResource resource = null;
738        String title = null;
739        String folder = getRootFolder();
740        try {
741            resource = getCms().readFolder(folder, CmsResourceFilter.IGNORE_EXPIRATION);
742            // get the title information of the folder
743            CmsProperty titleProperty = getCms().readPropertyObject(
744                folder,
745                CmsPropertyDefinition.PROPERTY_TITLE,
746                false);
747
748            if ((titleProperty == null) || titleProperty.isNullProperty()) {
749                getCms().getSitePath(resource);
750                title = resource.getRootPath();
751            } else {
752                title = titleProperty.getValue();
753            }
754            return getNode(resource.getRootPath(), title, resource.getTypeId(), true, resource.getState(), false);
755        } catch (CmsException e) {
756            // should usually never happen
757            if (LOG.isInfoEnabled()) {
758                LOG.info(e.getLocalizedMessage(), e);
759            }
760        }
761        return "";
762    }
763
764    /**
765     * Calculates the prefix that has to be added when selecting a resource in a popup tree window.<p>
766     *
767     * This is needed for the link dialog in editors
768     * as well as the copy, move and link popup dialogs for resources in the VFS.<p>
769     *
770     * @param prefix the current prefix of the resource
771     * @param storedSiteRoot the site root in which the workplace (not the tree!) is
772     * @return the prefix which is added to the resource name
773     */
774    private String getSitePrefix(String prefix, String storedSiteRoot) {
775
776        if (OpenCms.getSiteManager().isSharedFolder(prefix)) {
777            return prefix;
778        }
779        if (TYPE_PAGELINK.equals(getTreeType())) {
780            // in editor link dialog, create a special prefix for internal links
781            if (!storedSiteRoot.equals(prefix)) {
782                // stored site is not selected site, create complete URL as prefix
783                CmsSite site = OpenCms.getSiteManager().getSiteForSiteRoot(prefix);
784                prefix = getCms().getRequestContext().removeSiteRoot(prefix);
785                prefix = site.getUrl() + OpenCms.getSystemInfo().getOpenCmsContext() + prefix;
786            } else {
787                // stored site is selected site, don't show prefix at all
788                prefix = "";
789            }
790
791        } else if (TYPE_COPY.equals(getTreeType())
792            || TYPE_SIBLING.equals(getTreeType())
793            || TYPE_VFSWIDGET.equals(getTreeType())) {
794            // in vfs copy|move|link or vfs widget mode, don't add the prefix for the current workplace site
795            if (storedSiteRoot.equals(prefix)) {
796                prefix = "";
797            }
798        } else if (TYPE_PREFERENCES.equals(getTreeType())) {
799            prefix = "";
800        }
801
802        return prefix;
803    }
804
805    /**
806     * Returns the name of the start folder (or "last known" folder) to be loaded.<p>
807     *
808     * @return the name of the start folder (or "last known" folder) to be loaded
809     */
810    private String getStartFolder() {
811
812        return m_startFolder;
813    }
814
815    /**
816     * Returns the target folder name.<p>
817     *
818     * @return the target folder name
819     */
820    private String getTargetFolder() {
821
822        return m_targetFolder;
823    }
824
825    /**
826     * Returns true if a complete new tree must be loaded, false if an existing
827     * tree is updated or extended.<p>
828     *
829     * @return true if a complete new tree must be loaded
830     */
831    private boolean newTree() {
832
833        return m_newTree;
834    }
835
836    /**
837     * Creates error information output.<p>
838     *
839     * @param t an error that occurred
840     * @return error information output
841     */
842    private String printError(Throwable t) {
843
844        StringBuffer result = new StringBuffer(1024);
845        result.append("/*\n");
846        result.append(CmsStringUtil.escapeHtml(t.getMessage()));
847        result.append("\n*/\n");
848        result.append("function init() {\n");
849        result.append("}\n");
850        return result.toString();
851    }
852
853    /**
854     * Sets the value to indicate if only folders or files and folders should be included in the tree.<p>
855     *
856     * @param includeFiles if true if files and folders should be included in the tree
857     */
858    private void setIncludeFiles(boolean includeFiles) {
859
860        m_includeFiles = includeFiles;
861    }
862
863    /**
864     * Sets if a complete tree must be loaded.<p>
865     *
866     * @param newTree if true, a complete tree must be loaded
867     */
868    private void setNewTree(boolean newTree) {
869
870        m_newTree = newTree;
871    }
872
873    /**
874     * Sets if the site selector should be shown depending on the tree type and the count of accessible sites.<p>
875     *
876     * @param showSiteSelector true if site selector should be shown, otherwise false
877     */
878    private void setShowSiteSelector(boolean showSiteSelector) {
879
880        m_showSiteSelector = showSiteSelector;
881    }
882
883    /**
884     * Sets the name of the start folder (or "last known" folder) to be loaded.<p>
885     *
886     * @param startFolder the name of the start folder (or "last known" folder) to be loaded
887     */
888    private void setStartFolder(String startFolder) {
889
890        m_startFolder = startFolder;
891    }
892
893    /**
894     * Sets the target folder name.<p>
895     *
896     * @param targetFolder the target folder name
897     */
898    private void setTargetFolder(String targetFolder) {
899
900        m_targetFolder = targetFolder;
901    }
902
903    /**
904     * Sets the type of this tree.<p>
905     *
906     * @param type the type of this tree
907     */
908    private void setTreeType(String type) {
909
910        m_treeType = type;
911    }
912}