001/*
002 * (C) Copyright 2007 Nuxeo SAS (http://nuxeo.com/) and contributors.
003 *
004 * All rights reserved. This program and the accompanying materials
005 * are made available under the terms of the GNU Lesser General Public License
006 * (LGPL) version 2.1 which accompanies this distribution, and is available at
007 * http://www.gnu.org/licenses/lgpl.html
008 *
009 * This library is distributed in the hope that it will be useful,
010 * but WITHOUT ANY WARRANTY; without even the implied warranty of
011 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
012 * Lesser General Public License for more details.
013 *
014 * Contributors:
015 *     Nuxeo - initial API and implementation
016 *
017 * $Id: DirectoryTreeManagerBean.java 28950 2008-01-11 13:35:06Z tdelprat $
018 */
019package org.nuxeo.ecm.webapp.directory;
020
021import static org.jboss.seam.ScopeType.CONVERSATION;
022import static org.nuxeo.ecm.webapp.directory.DirectoryTreeNode.PARENT_FIELD_ID;
023
024import java.util.ArrayList;
025import java.util.HashMap;
026import java.util.LinkedList;
027import java.util.List;
028import java.util.Map;
029
030import org.apache.commons.lang.StringUtils;
031import org.apache.commons.logging.Log;
032import org.apache.commons.logging.LogFactory;
033import org.jboss.seam.annotations.In;
034import org.jboss.seam.annotations.Name;
035import org.jboss.seam.annotations.Scope;
036import org.nuxeo.ecm.core.api.CoreSession;
037import org.nuxeo.ecm.core.schema.SchemaManager;
038import org.nuxeo.ecm.core.schema.types.Schema;
039import org.nuxeo.ecm.directory.Directory;
040import org.nuxeo.ecm.directory.DirectoryException;
041import org.nuxeo.ecm.directory.api.DirectoryService;
042import org.nuxeo.ecm.platform.ui.web.directory.DirectoryHelper;
043import org.nuxeo.ecm.webapp.seam.NuxeoSeamHotReloader;
044import org.nuxeo.runtime.api.Framework;
045
046/**
047 * Manage trees defined by xvocabulary directories. Update the associated QueryModel when a node is selected and return
048 * a parameterized faces navigation case.
049 *
050 * @author <a href="mailto:ogrisel@nuxeo.com">Olivier Grisel</a>
051 */
052@Scope(CONVERSATION)
053@Name("directoryTreeManager")
054public class DirectoryTreeManagerBean implements DirectoryTreeManager {
055
056    private static final long serialVersionUID = -5250556791009032616L;
057
058    private static final Log log = LogFactory.getLog(DirectoryTreeManagerBean.class);
059
060    public static final String NODE_SELECTED_MARKER = DirectoryTreeManagerBean.class.getName()
061            + "_NODE_SELECTED_MARKER";
062
063    @In(create = true, required = false)
064    protected transient CoreSession documentManager;
065
066    @In(create = true)
067    protected NuxeoSeamHotReloader seamReload;
068
069    @In(create = true)
070    protected Map<String, String> messages;
071
072    protected transient Map<String, DirectoryTreeNode> treeModels;
073
074    protected Long treeModelsTimestamp;
075
076    protected transient DirectoryTreeService directoryTreeService;
077
078    protected String selectedTree;
079
080    private transient List<DirectoryTreeNode> directoryTrees;
081
082    /*
083     * The directoryTrees need a working core session in order to perform search actions.
084     */
085    public boolean isInitialized() {
086        return documentManager != null;
087    }
088
089    public DirectoryTreeNode get(String treeName) {
090        DirectoryTreeService dirTreeService = getDirectoryTreeService();
091        if (seamReload.isDevModeSet() && seamReload.shouldResetCache(dirTreeService, treeModelsTimestamp)) {
092            treeModels = null;
093        }
094        if (treeModels == null) {
095            treeModels = new HashMap<String, DirectoryTreeNode>();
096            treeModelsTimestamp = dirTreeService.getLastModified();
097        }
098        // lazy loading of tree models
099        DirectoryTreeNode treeModel = treeModels.get(treeName);
100        if (treeModel != null) {
101            // return cached model
102            return treeModel;
103        }
104        DirectoryTreeDescriptor config = dirTreeService.getDirectoryTreeDescriptor(treeName);
105        if (config == null) {
106            log.error("no DirectoryTreeDescriptor registered as " + treeName);
107            return null;
108        }
109
110        // check that each required directory exists and has the xvocabulary
111        // schema
112        String[] directories = config.getDirectories();
113        DirectoryService directoryService = DirectoryHelper.getDirectoryService();
114        SchemaManager schemaManager = Framework.getLocalService(SchemaManager.class);
115        try {
116            boolean isFirst = true;
117            for (String directoryName : directories) {
118                Directory directory = directoryService.getDirectory(directoryName);
119                if (directory == null) {
120                    throw new DirectoryException(directoryName + " is not a registered directory");
121                }
122                if (!isFirst) {
123                    Schema schema = schemaManager.getSchema(directory.getSchema());
124                    if (!schema.hasField(PARENT_FIELD_ID)) {
125                        throw new DirectoryException(directoryName + "does not have the required field: "
126                                + PARENT_FIELD_ID);
127                    }
128                }
129                isFirst = false;
130            }
131        } catch (DirectoryException e) {
132            throw new RuntimeException(e);
133        }
134
135        treeModel = new DirectoryTreeNode(0, config, config.getName(), config.getLabel(), "", null);
136
137        // store the build tree to reuse it the next time in the same state
138        treeModels.put(treeName, treeModel);
139        return treeModel;
140    }
141
142    public List<String> getDirectoryTreeNames() {
143        return getDirectoryTreeService().getDirectoryTrees();
144    }
145
146    public List<DirectoryTreeNode> getDirectoryTrees() {
147        if (directoryTrees == null) {
148            directoryTrees = new LinkedList<DirectoryTreeNode>();
149            for (String treeName : getDirectoryTreeNames()) {
150                directoryTrees.add(get(treeName));
151            }
152        }
153        return directoryTrees;
154    }
155
156    public String getSelectedTreeName() {
157        if (selectedTree == null) {
158            List<String> names = getDirectoryTreeNames();
159            if (!names.isEmpty()) {
160                selectedTree = names.get(0);
161            }
162        }
163        return selectedTree;
164    }
165
166    public void setSelectedTreeName(String treeName) {
167        selectedTree = treeName;
168    }
169
170    public List<DirectoryTreeNode> getSelectedTreeAsList() {
171        List<DirectoryTreeNode> res = new ArrayList<>();
172        DirectoryTreeNode selected = getSelectedTree();
173        if (selected != null) {
174            res.add(selected);
175        }
176        return res;
177    }
178
179    public DirectoryTreeNode getSelectedTree() {
180        return get(getSelectedTreeName());
181    }
182
183    protected DirectoryTreeService getDirectoryTreeService() {
184        if (directoryTreeService != null) {
185            return directoryTreeService;
186        }
187        directoryTreeService = (DirectoryTreeService) Framework.getRuntime().getComponent(DirectoryTreeService.NAME);
188        return directoryTreeService;
189    }
190
191    public String getLabelFor(String directoryTreeName, String fullPath) {
192        return getLabelFor(directoryTreeName, fullPath, false);
193    }
194
195    public String getLabelFor(String directoryTreeName, String fullPath, boolean includeDirectoryTreeLabel) {
196        DirectoryTreeNode rootNode = get(directoryTreeName);
197        List<String> labels = new ArrayList<String>();
198        computeLabels(labels, rootNode, fullPath, includeDirectoryTreeLabel);
199        List<String> translatedLabels = translateLabels(labels);
200        return StringUtils.join(translatedLabels, "/");
201    }
202
203    protected void computeLabels(List<String> labels, DirectoryTreeNode node, String fullPath,
204            boolean includeDirectoryTreeLabel) {
205        // add label for the root path only if specified
206        if (!node.getPath().isEmpty() || (node.getPath().isEmpty() && includeDirectoryTreeLabel)) {
207            labels.add(node.getDescription());
208        }
209        if (fullPath.equals(node.getPath())) {
210            return;
211        }
212        for (DirectoryTreeNode treeNode : node.getChildren()) {
213            if (fullPath.startsWith(treeNode.getPath())) {
214                computeLabels(labels, treeNode, fullPath, includeDirectoryTreeLabel);
215            }
216        }
217    }
218
219    protected List<String> translateLabels(List<String> labels) {
220        List<String> translatedLabels = new ArrayList<String>(labels.size());
221        for (String label : labels) {
222            translatedLabels.add(messages.get(label));
223        }
224        return translatedLabels;
225    }
226
227    public void resetCurrentTree() {
228        if (treeModels != null && selectedTree != null) {
229            treeModels.remove(selectedTree);
230        }
231    }
232
233}