001/*
002 * (C) Copyright 2006-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$
020 */
021
022package org.nuxeo.ecm.platform.ui.web.directory;
023
024import java.io.Serializable;
025import java.util.ArrayList;
026import java.util.Collections;
027import java.util.LinkedHashMap;
028import java.util.LinkedList;
029import java.util.List;
030import java.util.Locale;
031import java.util.Map;
032import java.util.Set;
033
034import javax.faces.context.FacesContext;
035
036import org.apache.commons.logging.Log;
037import org.apache.commons.logging.LogFactory;
038import org.nuxeo.common.utils.i18n.I18NUtils;
039import org.nuxeo.ecm.core.api.DocumentModel;
040import org.nuxeo.ecm.core.api.DocumentModelList;
041import org.nuxeo.ecm.core.api.NuxeoException;
042import org.nuxeo.ecm.directory.DirectoryException;
043import org.nuxeo.ecm.directory.Session;
044import org.nuxeo.ecm.directory.api.DirectoryService;
045import org.nuxeo.runtime.api.Framework;
046
047/**
048 * @author <a href="mailto:glefter@nuxeo.com">George Lefter</a>
049 */
050public final class DirectoryHelper {
051
052    private static final Log log = LogFactory.getLog(DirectoryHelper.class);
053
054    /** Directory with a parent column. */
055    public static final String XVOCABULARY_TYPE = "xvocabulary";
056
057    public static final String[] VOCABULARY_TYPES = { "vocabulary", XVOCABULARY_TYPE };
058
059    private static final String[] displayOptions = { "id", "label", "idAndLabel" };
060
061    private static DirectoryHelper instance;
062
063    private DirectoryHelper() {
064    }
065
066    public static DirectoryHelper instance() {
067        if (null == instance) {
068            instance = new DirectoryHelper();
069        }
070        return instance;
071    }
072
073    public boolean hasParentColumn(String directoryName) {
074        try {
075            return XVOCABULARY_TYPE.equals(getService().getDirectorySchema(directoryName));
076        } catch (DirectoryException e) {
077            // GR: our callers can't throw anything. Better to catch here, then
078            log.error("Could not retrieve schema name for directory: " + directoryName, e);
079            return false;
080        }
081    }
082
083    public DirectorySelectItem getSelectItem(String directoryName, Map<String, Serializable> filter) {
084        List<DirectorySelectItem> items = getSelectItems(directoryName, filter);
085        if (items.size() > 1) {
086            log.warn("More than one entry found in directory " + directoryName + " and filter:");
087            for (Map.Entry<String, Serializable> e : filter.entrySet()) {
088                log.warn(String.format("%s=%s", e.getKey(), e.getValue()));
089            }
090        } else if (items.isEmpty()) {
091            return null;
092        }
093        return items.get(0);
094    }
095
096    public List<DirectorySelectItem> getSelectItems(String directoryName, Map<String, Serializable> filter) {
097        return getSelectItems(directoryName, filter, Boolean.FALSE);
098    }
099
100    public List<DirectorySelectItem> getSelectItems(String directoryName, Map<String, Serializable> filter,
101            Boolean localize) {
102        List<DirectorySelectItem> list = new LinkedList<DirectorySelectItem>();
103
104        Set<String> emptySet = Collections.emptySet();
105
106        Map<String, String> orderBy = new LinkedHashMap<String, String>();
107
108        FacesContext context = FacesContext.getCurrentInstance();
109
110        // an extended schema also has parent field
111        try (Session session = getService().open(directoryName)) {
112            String schema = getService().getDirectorySchema(directoryName);
113            if (session == null) {
114                throw new NuxeoException("could not open session on directory: " + directoryName);
115            }
116
117            // adding sorting support
118            // XXX It seems that this ordering is obsolete as
119            // DirectoryAwareComponent made it's own (NXP-7349)
120            if (schema.equals(VOCABULARY_TYPES[0]) || schema.equals(VOCABULARY_TYPES[1])) {
121                orderBy.put("ordering", "asc");
122                orderBy.put("id", "asc");
123            }
124
125            DocumentModelList docModelList = session.query(filter, emptySet, orderBy);
126
127            for (DocumentModel docModel : docModelList) {
128                String id = (String) docModel.getProperty(schema, "id");
129                String label = (String) docModel.getProperty(schema, "label");
130                long ordering = (Long) docModel.getProperty(schema, "ordering");
131
132                if (Boolean.TRUE.equals(localize)) {
133                    label = translate(context, label);
134                }
135                DirectorySelectItem item = new DirectorySelectItem(id, label, ordering);
136                list.add(item);
137            }
138
139        }
140
141        return list;
142    }
143
144    public static DirectoryService getDirectoryService() {
145        return instance().getService();
146    }
147
148    public static List<DirectorySelectItem> getSelectItems(VocabularyEntryList directoryValues,
149            Map<String, Serializable> filter) {
150        return getSelectItems(directoryValues, filter, Boolean.FALSE);
151    }
152
153    public static List<DirectorySelectItem> getSelectItems(VocabularyEntryList directoryValues,
154            Map<String, Serializable> filter, Boolean localize) {
155        List<DirectorySelectItem> list = new ArrayList<DirectorySelectItem>();
156
157        // in obsolete filter we have either null (include obsoletes)
158        // or 0 (don't include obsoletes)
159        boolean obsolete = filter.get("obsolete") == null;
160        String parentFilter = (String) filter.get("parent");
161
162        FacesContext context = FacesContext.getCurrentInstance();
163        for (VocabularyEntry entry : directoryValues.getEntries()) {
164            if (!obsolete && Boolean.TRUE.equals(entry.getObsolete())) {
165                continue;
166            }
167            String parent = entry.getParent();
168            if (parentFilter == null) {
169                if (parent != null) {
170                    continue;
171                }
172            } else if (!parentFilter.equals(parent)) {
173                continue;
174            }
175
176            String idValue = (String) filter.get("id");
177            if (idValue != null && !idValue.equals(entry.getId())) {
178                continue;
179            }
180            String id = entry.getId();
181            String label = entry.getLabel();
182            if (Boolean.TRUE.equals(localize)) {
183                label = translate(context, label);
184            }
185            DirectorySelectItem item = new DirectorySelectItem(id, label);
186            list.add(item);
187        }
188        return list;
189    }
190
191    public static String getOptionValue(String optionId, String optionLabel, String display, boolean displayIdAndLabel,
192            String displayIdAndLabelSeparator) {
193        StringBuilder displayValue = new StringBuilder();
194        if (display != null && !"".equals(display)) {
195            if (display.equals(displayOptions[0])) {
196                displayValue.append(optionId);
197            } else if (display.equals(displayOptions[1])) {
198                displayValue.append(optionLabel);
199            } else if (display.equals(displayOptions[2])) {
200                displayValue.append(optionId).append(displayIdAndLabelSeparator).append(optionLabel);
201            } else {
202                displayValue.append(optionLabel);
203            }
204        } else if (displayIdAndLabel) {
205            displayValue.append(optionId).append(displayIdAndLabelSeparator).append(optionLabel);
206        } else {
207            displayValue.append(optionLabel);
208        }
209        return displayValue.toString();
210    }
211
212    private static DocumentModel getEntryThrows(String directoryName, String entryId) {
213        DirectoryService dirService = getDirectoryService();
214        try (Session session = dirService.open(directoryName)) {
215            return session.getEntry(entryId);
216        }
217    }
218
219    /**
220     * Returns the entry with given id from specified directory.
221     * <p>
222     * Method to use from components, since JSF base class that we extend don't allow to throw proper exceptions.
223     *
224     * @param directoryName
225     * @param entryId
226     * @return the entry, or null in case of exception in callees.
227     */
228    public static DocumentModel getEntry(String directoryName, String entryId) {
229        try {
230            return getEntryThrows(directoryName, entryId);
231        } catch (DirectoryException e) {
232            log.error(String.format("Error retrieving the entry (dirname=%s, entryId=%s)", directoryName, entryId), e);
233            return null;
234        }
235    }
236
237    protected static String translate(FacesContext context, String label) {
238        String bundleName = context.getApplication().getMessageBundle();
239        Locale locale = context.getViewRoot().getLocale();
240        label = I18NUtils.getMessageString(bundleName, label, null, locale);
241        return label;
242    }
243
244    protected DirectoryService getService() {
245        return Framework.getService(DirectoryService.class);
246    }
247
248}