001/* 002 * (C) Copyright 2006-2010 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 * Thierry Delprat 018 * Florent Guillaume 019 */ 020 021package org.nuxeo.ecm.webapp.action; 022 023import java.io.Serializable; 024import java.security.Principal; 025import java.util.ArrayList; 026import java.util.Collections; 027import java.util.List; 028import java.util.Map; 029import java.util.Set; 030 031import org.apache.commons.logging.Log; 032import org.apache.commons.logging.LogFactory; 033import org.jboss.seam.ScopeType; 034import org.jboss.seam.annotations.In; 035import org.jboss.seam.annotations.Install; 036import org.jboss.seam.annotations.Name; 037import org.jboss.seam.annotations.Scope; 038import org.jboss.seam.core.Events; 039import org.jboss.seam.faces.FacesMessages; 040import org.jboss.seam.international.StatusMessage; 041import org.nuxeo.ecm.core.api.CoreSession; 042import org.nuxeo.ecm.core.api.DocumentModel; 043import org.nuxeo.ecm.core.api.DocumentModelList; 044import org.nuxeo.ecm.core.api.DocumentRef; 045import org.nuxeo.ecm.core.api.IdRef; 046import org.nuxeo.ecm.core.api.security.SecurityConstants; 047import org.nuxeo.ecm.core.trash.TrashInfo; 048import org.nuxeo.ecm.core.trash.TrashService; 049import org.nuxeo.ecm.platform.actions.Action; 050import org.nuxeo.ecm.platform.ui.web.api.NavigationContext; 051import org.nuxeo.ecm.platform.ui.web.api.WebActions; 052import org.nuxeo.ecm.platform.util.RepositoryLocation; 053import org.nuxeo.ecm.webapp.documentsLists.DocumentsListsManager; 054import org.nuxeo.ecm.webapp.edit.lock.LockActions; 055import org.nuxeo.ecm.webapp.trashManagement.TrashManager; 056import org.nuxeo.runtime.api.Framework; 057 058import static org.nuxeo.ecm.webapp.documentsLists.DocumentsListsManager.CURRENT_DOCUMENT_SECTION_SELECTION; 059import static org.nuxeo.ecm.webapp.documentsLists.DocumentsListsManager.CURRENT_DOCUMENT_SELECTION; 060import static org.nuxeo.ecm.webapp.documentsLists.DocumentsListsManager.CURRENT_DOCUMENT_TRASH_SELECTION; 061import static org.nuxeo.ecm.webapp.helpers.EventNames.DOCUMENT_CHILDREN_CHANGED; 062 063@Name("deleteActions") 064@Scope(ScopeType.EVENT) 065@Install(precedence = Install.FRAMEWORK) 066public class DeleteActionsBean implements DeleteActions, Serializable { 067 068 private static final long serialVersionUID = 1L; 069 070 private static final Log log = LogFactory.getLog(DeleteActionsBean.class); 071 072 @In(create = true, required = false) 073 protected FacesMessages facesMessages; 074 075 @In(create = true) 076 protected Map<String, String> messages; 077 078 @In(create = true, required = false) 079 protected transient CoreSession documentManager; 080 081 @In(create = true, required = false) 082 protected RepositoryLocation currentServerLocation; 083 084 @In(create = true) 085 protected transient DocumentsListsManager documentsListsManager; 086 087 @In(create = true) 088 protected NavigationContext navigationContext; 089 090 @In(create = true) 091 protected transient TrashManager trashManager; 092 093 @In(create = true) 094 protected transient LockActions lockActions; 095 096 @In(create = true) 097 protected transient WebActions webActions; 098 099 @In 100 protected transient Principal currentUser; 101 102 protected transient TrashService trashService; 103 104 protected TrashService getTrashService() { 105 if (trashService == null) { 106 trashService = Framework.getService(TrashService.class); 107 } 108 return trashService; 109 } 110 111 @Override 112 public boolean getCanDeleteItem(DocumentModel container) { 113 if (container == null) { 114 return false; 115 } 116 return getTrashService().folderAllowsDelete(container); 117 } 118 119 @Override 120 public boolean getCanDelete() { 121 return getCanDelete(CURRENT_DOCUMENT_SELECTION); 122 } 123 124 @Override 125 public boolean getCanDelete(String listName) { 126 List<DocumentModel> docs = documentsListsManager.getWorkingList(listName); 127 return getTrashService().canDelete(docs, currentUser, false); 128 } 129 130 @Override 131 public boolean getCanDeleteSections() { 132 List<DocumentModel> docs = documentsListsManager.getWorkingList(CURRENT_DOCUMENT_SECTION_SELECTION); 133 return getTrashService().canDelete(docs, currentUser, true); 134 } 135 136 @Override 137 public boolean getCanPurge() { 138 List<DocumentModel> docs = documentsListsManager.getWorkingList(CURRENT_DOCUMENT_TRASH_SELECTION); 139 return getTrashService().canPurgeOrUndelete(docs, currentUser); 140 } 141 142 public boolean getCanEmptyTrash() { 143 List<DocumentModel> selectedDocuments = documentsListsManager.getWorkingList(CURRENT_DOCUMENT_TRASH_SELECTION); 144 if (selectedDocuments.size() == 0) { 145 DocumentModelList currentTrashDocuments = getTrashService().getDocuments(navigationContext.getCurrentDocument()); 146 return getTrashService().canPurgeOrUndelete(currentTrashDocuments, currentUser); 147 } 148 return false; 149 } 150 151 @Override 152 public boolean checkDeletePermOnParents(List<DocumentModel> docs) { 153 return getTrashService().checkDeletePermOnParents(docs); 154 } 155 156 @Override 157 public String deleteSelection() { 158 if (!documentsListsManager.isWorkingListEmpty(CURRENT_DOCUMENT_SELECTION)) { 159 return deleteSelection(documentsListsManager.getWorkingList(CURRENT_DOCUMENT_SELECTION)); 160 } else { 161 log.debug("No documents selection in context to process delete on..."); 162 return null; 163 } 164 } 165 166 @Override 167 public String deleteSelectionSections() { 168 if (!documentsListsManager.isWorkingListEmpty(CURRENT_DOCUMENT_SECTION_SELECTION)) { 169 return deleteSelection(documentsListsManager.getWorkingList(CURRENT_DOCUMENT_SECTION_SELECTION)); 170 } else { 171 log.debug("No documents selection in context to process delete on..."); 172 return null; 173 } 174 } 175 176 protected static final int OP_DELETE = 1, OP_PURGE = 2, OP_UNDELETE = 3; 177 178 @Override 179 public String deleteSelection(List<DocumentModel> docs) { 180 int op = isTrashManagementEnabled() ? OP_DELETE : OP_PURGE; 181 return actOnSelection(op, docs); 182 } 183 184 public String emptyTrash() { 185 DocumentModelList currentTrashDocuments = trashService.getDocuments(navigationContext.getCurrentDocument()); 186 return purgeSelection(currentTrashDocuments); 187 } 188 189 @Override 190 public String purgeSelection() { 191 return purgeSelection(CURRENT_DOCUMENT_TRASH_SELECTION); 192 } 193 194 @Override 195 public String purgeSelection(String listName) { 196 if (!documentsListsManager.isWorkingListEmpty(listName)) { 197 return purgeSelection(documentsListsManager.getWorkingList(listName)); 198 } else { 199 log.debug("No documents selection in context to process delete on..."); 200 return null; 201 } 202 } 203 204 @Override 205 public String purgeSelection(List<DocumentModel> docs) { 206 return actOnSelection(OP_PURGE, docs); 207 } 208 209 @Override 210 public String undeleteSelection() { 211 if (!documentsListsManager.isWorkingListEmpty(CURRENT_DOCUMENT_TRASH_SELECTION)) { 212 return undeleteSelection(documentsListsManager.getWorkingList(CURRENT_DOCUMENT_TRASH_SELECTION)); 213 } else { 214 log.debug("No documents selection in context to process delete on..."); 215 return null; 216 } 217 } 218 219 @Override 220 public String undeleteSelection(List<DocumentModel> docs) { 221 return actOnSelection(OP_UNDELETE, docs); 222 223 } 224 225 protected String actOnSelection(int op, List<DocumentModel> docs) { 226 if (docs == null) { 227 return null; 228 } 229 TrashInfo info = getTrashService().getTrashInfo(docs, currentUser, false, false); 230 231 DocumentModel targetContext = getTrashService().getAboveDocument(navigationContext.getCurrentDocument(), 232 info.rootPaths); 233 234 // remove from all lists 235 documentsListsManager.removeFromAllLists(info.docs); 236 237 Set<DocumentRef> parentRefs; 238 String msgid; 239 // operation to do 240 switch (op) { 241 case OP_PURGE: 242 getTrashService().purgeDocuments(documentManager, info.rootRefs); 243 parentRefs = info.rootParentRefs; 244 msgid = "n_deleted_docs"; 245 break; 246 case OP_DELETE: 247 getTrashService().trashDocuments(info.docs); 248 parentRefs = info.rootParentRefs; 249 msgid = "n_deleted_docs"; 250 break; 251 case OP_UNDELETE: 252 parentRefs = getTrashService().undeleteDocuments(info.docs); 253 msgid = "n_undeleted_docs"; 254 break; 255 default: 256 throw new AssertionError(op); 257 } 258 259 // Update context if needed 260 if (op == OP_UNDELETE) { 261 // undelete is problematic because it may change undeleted 262 // parent's paths... so we refetch the new context 263 targetContext = documentManager.getDocument(new IdRef(targetContext.getId())); 264 } else if (targetContext == null) { 265 // handle placeless document 266 targetContext = documentManager.getRootDocument(); 267 } 268 navigationContext.setCurrentDocument(targetContext); 269 270 // Notify parents 271 if (parentRefs.isEmpty()) { 272 // Globally refresh content views 273 Events.instance().raiseEvent(DOCUMENT_CHILDREN_CHANGED); 274 } else { 275 for (DocumentRef parentRef : parentRefs) { 276 if (documentManager.hasPermission(parentRef, SecurityConstants.READ)) { 277 DocumentModel parent = documentManager.getDocument(parentRef); 278 if (parent != null) { 279 Events.instance().raiseEvent(DOCUMENT_CHILDREN_CHANGED, parent); 280 } 281 } 282 } 283 } 284 285 // User feedback 286 if (info.proxies > 0) { 287 facesMessages.add(StatusMessage.Severity.WARN, "can_not_delete_proxies"); 288 } 289 Object[] params = { Integer.valueOf(info.docs.size()) }; 290 facesMessages.add(StatusMessage.Severity.INFO, "#0 " + messages.get(msgid), params); 291 292 return null; 293 } 294 295 @Override 296 public boolean isTrashManagementEnabled() { 297 return trashManager.isTrashManagementEnabled(); 298 } 299 300 public List<Action> getActionsForTrashSelection() { 301 return webActions.getActionsList(CURRENT_DOCUMENT_TRASH_SELECTION + "_LIST", false); 302 } 303 304 @Override 305 public void create() { 306 } 307 308 @Override 309 public void destroy() { 310 } 311 312 @Override 313 public void restoreCurrentDocument() { 314 List<DocumentModel> doc = new ArrayList<DocumentModel>(); 315 doc.add(navigationContext.getCurrentDocument()); 316 undeleteSelection(doc); 317 } 318 319 @Override 320 public boolean getCanRestoreCurrentDoc() { 321 DocumentModel doc = navigationContext.getCurrentDocument(); 322 if (doc == null) { 323 // this shouldn't happen, if it happens probably there is a 324 // customization bug, we guard this though 325 log.warn("Null currentDocument in navigationContext"); 326 return false; 327 } 328 return getTrashService().canPurgeOrUndelete(Collections.singletonList(doc), currentUser); 329 } 330 331 public boolean restoreActionDisplay() { 332 return getCanRestoreCurrentDoc() && isTrashManagementEnabled(); 333 } 334}