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 = trashService.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        }
265        navigationContext.setCurrentDocument(targetContext);
266
267        // Notify parents
268        if (parentRefs.isEmpty()) {
269            // Globally refresh content views
270            Events.instance().raiseEvent(DOCUMENT_CHILDREN_CHANGED);
271        } else {
272            for (DocumentRef parentRef : parentRefs) {
273                if (documentManager.hasPermission(parentRef, SecurityConstants.READ)) {
274                    DocumentModel parent = documentManager.getDocument(parentRef);
275                    if (parent != null) {
276                        Events.instance().raiseEvent(DOCUMENT_CHILDREN_CHANGED, parent);
277                    }
278                }
279            }
280        }
281
282        // User feedback
283        if (info.proxies > 0) {
284            facesMessages.add(StatusMessage.Severity.WARN, "can_not_delete_proxies");
285        }
286        Object[] params = { Integer.valueOf(info.docs.size()) };
287        facesMessages.add(StatusMessage.Severity.INFO, "#0 " + messages.get(msgid), params);
288
289        return null;
290    }
291
292    @Override
293    public boolean isTrashManagementEnabled() {
294        return trashManager.isTrashManagementEnabled();
295    }
296
297    public List<Action> getActionsForTrashSelection() {
298        return webActions.getActionsList(CURRENT_DOCUMENT_TRASH_SELECTION + "_LIST", false);
299    }
300
301    @Override
302    public void create() {
303    }
304
305    @Override
306    public void destroy() {
307    }
308
309    @Override
310    public void restoreCurrentDocument() {
311        List<DocumentModel> doc = new ArrayList<DocumentModel>();
312        doc.add(navigationContext.getCurrentDocument());
313        undeleteSelection(doc);
314    }
315
316    @Override
317    public boolean getCanRestoreCurrentDoc() {
318        DocumentModel doc = navigationContext.getCurrentDocument();
319        if (doc == null) {
320            // this shouldn't happen, if it happens probably there is a
321            // customization bug, we guard this though
322            log.warn("Null currentDocument in navigationContext");
323            return false;
324        }
325        return getTrashService().canPurgeOrUndelete(Collections.singletonList(doc), currentUser);
326    }
327
328    public boolean restoreActionDisplay() {
329        return getCanRestoreCurrentDoc() && isTrashManagementEnabled();
330    }
331}