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.Observer;
035import org.jboss.seam.annotations.Scope;
036import org.jboss.seam.annotations.intercept.BypassInterceptors;
037import org.jboss.seam.core.Events;
038import org.jboss.seam.faces.FacesMessages;
039import org.jboss.seam.international.StatusMessage;
040import org.nuxeo.ecm.core.api.DocumentModel;
041import org.nuxeo.ecm.core.api.DocumentModelComparator;
042import org.nuxeo.ecm.core.api.DocumentModelList;
043import org.nuxeo.ecm.core.api.NuxeoPrincipal;
044import org.nuxeo.ecm.core.api.impl.DocumentModelListImpl;
045import org.nuxeo.ecm.directory.BaseSession;
046import org.nuxeo.ecm.directory.Session;
047import org.nuxeo.ecm.directory.api.DirectoryService;
048import org.nuxeo.ecm.directory.api.ui.DirectoryUI;
049import org.nuxeo.ecm.directory.api.ui.DirectoryUIDeleteConstraint;
050import org.nuxeo.ecm.directory.api.ui.DirectoryUIManager;
051import org.nuxeo.ecm.platform.actions.ActionContext;
052import org.nuxeo.ecm.platform.actions.ejb.ActionManager;
053import org.nuxeo.ecm.platform.actions.jsf.JSFActionContext;
054import org.nuxeo.ecm.platform.ui.web.directory.DirectoryHelper;
055import org.nuxeo.ecm.platform.ui.web.util.SeamContextHelper;
056import org.nuxeo.ecm.webapp.helpers.EventNames;
057
058/**
059 * Manages directories editable by administrators.
060 *
061 * @author Anahide Tchertchian
062 */
063@Name("directoryUIActions")
064@Scope(ScopeType.CONVERSATION)
065public class DirectoryUIActionsBean implements Serializable {
066
067    private static final long serialVersionUID = 1L;
068
069    public static final String DIRECTORY_DEFAULT_VIEW = "view_directory";
070
071    @In(create = true)
072    protected transient DirectoryUIManager directoryUIManager;
073
074    // FIXME: use a business delegate
075    protected transient DirectoryService dirService;
076
077    @In(create = true, required = false)
078    protected transient FacesMessages facesMessages;
079
080    @In(create = true)
081    protected Map<String, String> messages;
082
083    @In(create = true, required = false)
084    protected transient ActionManager actionManager;
085
086    @In(create = true)
087    private transient NuxeoPrincipal currentNuxeoPrincipal;
088
089    protected List<String> directoryNames;
090
091    protected DirectoryUI currentDirectoryInfo;
092
093    protected DocumentModelList currentDirectoryEntries;
094
095    protected DocumentModel selectedDirectoryEntry;
096
097    protected boolean showAddForm = false;
098
099    protected DocumentModel creationDirectoryEntry;
100
101    protected String selectedDirectoryName;
102
103    @Begin(join = true)
104    @Create
105    public void initialize() {
106        initDirService();
107    }
108
109    private void initDirService() {
110        if (dirService == null) {
111            dirService = DirectoryHelper.getDirectoryService();
112        }
113    }
114
115    public List<String> getDirectoryNames() {
116        if (directoryNames == null) {
117            directoryNames = directoryUIManager.getDirectoryNames();
118            if (directoryNames.size() > 0) {
119                // preserve selected directory if present
120                if (selectedDirectoryName == null || !directoryNames.contains(selectedDirectoryName)) {
121                    selectedDirectoryName = directoryNames.get(0);
122                }
123                selectDirectory();
124            }
125        }
126        return directoryNames;
127    }
128
129    public String getSelectedDirectoryName() {
130        return selectedDirectoryName;
131    }
132
133    public void setSelectedDirectoryName(String selectedDirectoryName) {
134        this.selectedDirectoryName = selectedDirectoryName;
135    }
136
137    @Deprecated
138    public String selectDirectory(String directoryName) {
139        resetSelectedDirectoryData();
140        currentDirectoryInfo = directoryUIManager.getDirectoryInfo(directoryName);
141        String view = currentDirectoryInfo.getView();
142        if (view == null) {
143            view = DIRECTORY_DEFAULT_VIEW;
144        }
145        return view;
146    }
147
148    public void selectDirectory() {
149        resetSelectedDirectoryData();
150        currentDirectoryInfo = directoryUIManager.getDirectoryInfo(selectedDirectoryName);
151    }
152
153    public DirectoryUI getCurrentDirectory() {
154        return currentDirectoryInfo;
155    }
156
157    public DocumentModelList getCurrentDirectoryEntries() {
158        if (currentDirectoryEntries == null) {
159            currentDirectoryEntries = new DocumentModelListImpl();
160            String dirName = currentDirectoryInfo.getName();
161            try (Session dirSession = dirService.open(dirName)) {
162                Map<String, Serializable> emptyMap = Collections.emptyMap();
163                Set<String> emptySet = Collections.emptySet();
164                DocumentModelList entries = dirSession.query(emptyMap, emptySet, null, true);
165                if (entries != null && !entries.isEmpty()) {
166                    currentDirectoryEntries.addAll(entries);
167                }
168                // sort
169                String sortField = currentDirectoryInfo.getSortField();
170                if (sortField == null) {
171                    sortField = dirService.getDirectoryIdField(dirName);
172                }
173                // sort
174                Map<String, String> orderBy = new HashMap<String, String>();
175                orderBy.put(sortField, DocumentModelComparator.ORDER_ASC);
176                Collections.sort(currentDirectoryEntries,
177                        new DocumentModelComparator(dirService.getDirectorySchema(dirName), orderBy));
178            }
179        }
180        return currentDirectoryEntries;
181    }
182
183    public void resetSelectedDirectoryData() {
184        currentDirectoryInfo = null;
185        currentDirectoryEntries = null;
186        resetSelectedDirectoryEntry();
187        resetCreateDirectoryEntry();
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    @Observer(value = { EventNames.FLUSH_EVENT }, create = false)
334    @BypassInterceptors
335    public void onHotReloadFlush() {
336        directoryNames = null;
337        resetSelectedDirectoryData();
338    }
339
340}