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