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}