001/*
002 * (C) Copyright 2011 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 * Contributors:
014 *     Anahide Tchertchian <at@nuxeo.com>
015 *     Thomas Roger <troger@nuxeo.com>
016 */
017
018package org.nuxeo.ecm.webapp.directory;
019
020import java.io.Serializable;
021import java.util.Collections;
022import java.util.HashMap;
023import java.util.List;
024import java.util.Map;
025import java.util.Set;
026
027import javax.faces.context.FacesContext;
028
029import org.jboss.seam.ScopeType;
030import org.jboss.seam.annotations.Begin;
031import org.jboss.seam.annotations.Create;
032import org.jboss.seam.annotations.In;
033import org.jboss.seam.annotations.Name;
034import org.jboss.seam.annotations.Scope;
035import org.jboss.seam.core.Events;
036import org.jboss.seam.faces.FacesMessages;
037import org.jboss.seam.international.StatusMessage;
038import org.nuxeo.ecm.core.api.DocumentModel;
039import org.nuxeo.ecm.core.api.DocumentModelComparator;
040import org.nuxeo.ecm.core.api.DocumentModelList;
041import org.nuxeo.ecm.core.api.NuxeoPrincipal;
042import org.nuxeo.ecm.core.api.impl.DocumentModelListImpl;
043import org.nuxeo.ecm.directory.BaseSession;
044import org.nuxeo.ecm.directory.DirectoryException;
045import org.nuxeo.ecm.directory.Session;
046import org.nuxeo.ecm.directory.api.DirectoryService;
047import org.nuxeo.ecm.directory.api.ui.DirectoryUI;
048import org.nuxeo.ecm.directory.api.ui.DirectoryUIDeleteConstraint;
049import org.nuxeo.ecm.directory.api.ui.DirectoryUIManager;
050import org.nuxeo.ecm.platform.actions.ActionContext;
051import org.nuxeo.ecm.platform.actions.ejb.ActionManager;
052import org.nuxeo.ecm.platform.actions.jsf.JSFActionContext;
053import org.nuxeo.ecm.platform.ui.web.directory.DirectoryHelper;
054import org.nuxeo.ecm.platform.ui.web.util.SeamContextHelper;
055import org.nuxeo.ecm.webapp.helpers.EventNames;
056
057/**
058 * Manages directories editable by administrators.
059 *
060 * @author Anahide Tchertchian
061 */
062@Name("directoryUIActions")
063@Scope(ScopeType.CONVERSATION)
064public class DirectoryUIActionsBean implements Serializable {
065
066    private static final long serialVersionUID = 1L;
067
068    public static final String DIRECTORY_DEFAULT_VIEW = "view_directory";
069
070    @In(create = true)
071    protected transient DirectoryUIManager directoryUIManager;
072
073    // FIXME: use a business delegate
074    protected transient DirectoryService dirService;
075
076    @In(create = true, required = false)
077    protected transient FacesMessages facesMessages;
078
079    @In(create = true)
080    protected Map<String, String> messages;
081
082    @In(create = true, required = false)
083    protected transient ActionManager actionManager;
084
085    @In(create = true)
086    private transient NuxeoPrincipal currentNuxeoPrincipal;
087
088    protected List<String> directoryNames;
089
090    protected DirectoryUI currentDirectoryInfo;
091
092    protected DocumentModelList currentDirectoryEntries;
093
094    protected DocumentModel selectedDirectoryEntry;
095
096    protected boolean showAddForm = false;
097
098    protected DocumentModel creationDirectoryEntry;
099
100    protected String selectedDirectoryName;
101
102    @Begin(join = true)
103    @Create
104    public void initialize() {
105        initDirService();
106    }
107
108    private void initDirService() {
109        if (dirService == null) {
110            dirService = DirectoryHelper.getDirectoryService();
111        }
112    }
113
114    public List<String> getDirectoryNames() {
115        if (directoryNames == null) {
116            directoryNames = directoryUIManager.getDirectoryNames();
117            if (directoryNames.size() > 0) {
118                // preserve selected directory if present
119                if (selectedDirectoryName == null || !directoryNames.contains(selectedDirectoryName)) {
120                    selectedDirectoryName = directoryNames.get(0);
121                }
122                selectDirectory();
123            }
124        }
125        return directoryNames;
126    }
127
128    public String getSelectedDirectoryName() {
129        return selectedDirectoryName;
130    }
131
132    public void setSelectedDirectoryName(String selectedDirectoryName) {
133        this.selectedDirectoryName = selectedDirectoryName;
134    }
135
136    @Deprecated
137    public String selectDirectory(String directoryName) {
138        resetSelectedDirectoryData();
139        currentDirectoryInfo = directoryUIManager.getDirectoryInfo(directoryName);
140        String view = currentDirectoryInfo.getView();
141        if (view == null) {
142            view = DIRECTORY_DEFAULT_VIEW;
143        }
144        return view;
145    }
146
147    public void selectDirectory() {
148        resetSelectedDirectoryData();
149        currentDirectoryInfo = directoryUIManager.getDirectoryInfo(selectedDirectoryName);
150    }
151
152    public DirectoryUI getCurrentDirectory() {
153        return currentDirectoryInfo;
154    }
155
156    public DocumentModelList getCurrentDirectoryEntries() {
157        if (currentDirectoryEntries == null) {
158            currentDirectoryEntries = new DocumentModelListImpl();
159            String dirName = currentDirectoryInfo.getName();
160            try (Session dirSession = dirService.open(dirName)) {
161                Map<String, Serializable> emptyMap = Collections.emptyMap();
162                Set<String> emptySet = Collections.emptySet();
163                DocumentModelList entries = dirSession.query(emptyMap, emptySet, null, true);
164                if (entries != null && !entries.isEmpty()) {
165                    currentDirectoryEntries.addAll(entries);
166                }
167                // sort
168                String sortField = currentDirectoryInfo.getSortField();
169                if (sortField == null) {
170                    sortField = dirService.getDirectoryIdField(dirName);
171                }
172                // sort
173                Map<String, String> orderBy = new HashMap<String, String>();
174                orderBy.put(sortField, DocumentModelComparator.ORDER_ASC);
175                Collections.sort(currentDirectoryEntries,
176                        new DocumentModelComparator(dirService.getDirectorySchema(dirName), orderBy));
177            }
178        }
179        return currentDirectoryEntries;
180    }
181
182    public void resetSelectedDirectoryData() {
183        currentDirectoryInfo = null;
184        currentDirectoryEntries = null;
185        selectedDirectoryEntry = null;
186        showAddForm = false;
187        creationDirectoryEntry = null;
188    }
189
190    public boolean getShowAddForm() {
191        return showAddForm;
192    }
193
194    public void toggleShowAddForm() {
195        showAddForm = !showAddForm;
196    }
197
198    public DocumentModel getCreationDirectoryEntry() {
199        if (creationDirectoryEntry == null) {
200            String dirName = currentDirectoryInfo.getName();
201            String schema = dirService.getDirectorySchema(dirName);
202            creationDirectoryEntry = BaseSession.createEntryModel(null, schema, null, null);
203        }
204        return creationDirectoryEntry;
205    }
206
207    public void createDirectoryEntry() {
208        String dirName = currentDirectoryInfo.getName();
209        try (Session dirSession = dirService.open(dirName)) {
210            // check if entry already exists
211            String schema = dirService.getDirectorySchema(dirName);
212            String idField = dirService.getDirectoryIdField(dirName);
213            Object id = creationDirectoryEntry.getProperty(schema, idField);
214            if (id instanceof String && dirSession.hasEntry((String) id)) {
215                facesMessages.add(StatusMessage.Severity.ERROR,
216                        messages.get("vocabulary.entry.identifier.already.exists"));
217                return;
218            }
219            dirSession.createEntry(creationDirectoryEntry);
220
221            resetCreateDirectoryEntry();
222            // invalidate directory entries list
223            currentDirectoryEntries = null;
224            Events.instance().raiseEvent(EventNames.DIRECTORY_CHANGED, dirName);
225
226            facesMessages.add(StatusMessage.Severity.INFO, messages.get("vocabulary.entry.added"));
227        }
228    }
229
230    public void resetCreateDirectoryEntry() {
231        creationDirectoryEntry = null;
232        showAddForm = false;
233    }
234
235    public void selectDirectoryEntry(String entryId) {
236        String dirName = currentDirectoryInfo.getName();
237        try (Session dirSession = dirService.open(dirName)) {
238            selectedDirectoryEntry = dirSession.getEntry(entryId);
239        }
240    }
241
242    public DocumentModel getSelectedDirectoryEntry() {
243        return selectedDirectoryEntry;
244    }
245
246    public void resetSelectedDirectoryEntry() {
247        selectedDirectoryEntry = null;
248    }
249
250    public void editSelectedDirectoryEntry() {
251        String dirName = currentDirectoryInfo.getName();
252        try (Session dirSession = dirService.open(dirName)) {
253            dirSession.updateEntry(selectedDirectoryEntry);
254            selectedDirectoryEntry = null;
255            // invalidate directory entries list
256            currentDirectoryEntries = null;
257            Events.instance().raiseEvent(EventNames.DIRECTORY_CHANGED, dirName);
258
259            facesMessages.add(StatusMessage.Severity.INFO, messages.get("vocabulary.entry.edited"));
260        }
261    }
262
263    public void deleteDirectoryEntry(String entryId) {
264        String dirName = currentDirectoryInfo.getName();
265        List<DirectoryUIDeleteConstraint> deleteConstraints = currentDirectoryInfo.getDeleteConstraints();
266        if (deleteConstraints != null && !deleteConstraints.isEmpty()) {
267            for (DirectoryUIDeleteConstraint deleteConstraint : deleteConstraints) {
268                if (!deleteConstraint.canDelete(dirService, entryId)) {
269                    facesMessages.add(StatusMessage.Severity.ERROR,
270                            messages.get("feedback.directory.deleteEntry.constraintError"));
271                    return;
272                }
273            }
274        }
275        try (Session dirSession = dirService.open(dirName)) {
276            dirSession.deleteEntry(entryId);
277            // invalidate directory entries list
278            currentDirectoryEntries = null;
279            Events.instance().raiseEvent(EventNames.DIRECTORY_CHANGED, dirName);
280            facesMessages.add(StatusMessage.Severity.INFO, messages.get("vocabulary.entry.deleted"));
281        }
282    }
283
284    public boolean isReadOnly(String directoryName) {
285        boolean isReadOnly;
286
287        try (Session dirSession = dirService.open(directoryName)) {
288            // Check Directory ReadOnly Status
289            boolean dirReadOnly = dirSession.isReadOnly();
290
291            // Check DirectoryUI ReadOnly Status
292            boolean dirUIReadOnly;
293            DirectoryUI dirInfo = directoryUIManager.getDirectoryInfo(directoryName);
294            if (dirInfo == null) {
295                // assume read-only
296                dirUIReadOnly = true;
297            } else {
298                dirUIReadOnly = Boolean.TRUE.equals(dirInfo.isReadOnly());
299            }
300
301            isReadOnly = dirReadOnly || dirUIReadOnly;
302        }
303        return isReadOnly;
304    }
305
306    protected ActionContext createDirectoryActionContext() {
307        return createDirectoryActionContext(selectedDirectoryName);
308    }
309
310    protected ActionContext createDirectoryActionContext(String directoryName) {
311        FacesContext faces = FacesContext.getCurrentInstance();
312        if (faces == null) {
313            throw new IllegalArgumentException("Faces context is null");
314        }
315        ActionContext ctx = new JSFActionContext(faces);
316        ctx.putLocalVariable("SeamContext", new SeamContextHelper());
317        ctx.putLocalVariable("directoryName", directoryName);
318        ctx.setCurrentPrincipal(currentNuxeoPrincipal);
319        return ctx;
320    }
321
322    public boolean checkContextualDirectoryFilter(String filterName) {
323        return actionManager.checkFilter(filterName, createDirectoryActionContext());
324    }
325
326    /**
327     * @since 5.9.1
328     */
329    public boolean checkContextualDirectoryFilter(String filterName, String directoryName) {
330        return actionManager.checkFilter(filterName, createDirectoryActionContext(directoryName));
331    }
332
333}