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