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 static org.nuxeo.ecm.webapp.documentsLists.DocumentsListsManager.CURRENT_DOCUMENT_SECTION_SELECTION;
024import static org.nuxeo.ecm.webapp.documentsLists.DocumentsListsManager.CURRENT_DOCUMENT_SELECTION;
025import static org.nuxeo.ecm.webapp.documentsLists.DocumentsListsManager.CURRENT_DOCUMENT_TRASH_SELECTION;
026import static org.nuxeo.ecm.webapp.helpers.EventNames.DOCUMENT_CHILDREN_CHANGED;
027
028import java.io.Serializable;
029import java.security.Principal;
030import java.util.ArrayList;
031import java.util.List;
032import java.util.Map;
033import java.util.Set;
034
035import org.apache.commons.logging.Log;
036import org.apache.commons.logging.LogFactory;
037import org.jboss.seam.ScopeType;
038import org.jboss.seam.annotations.In;
039import org.jboss.seam.annotations.Install;
040import org.jboss.seam.annotations.Name;
041import org.jboss.seam.annotations.Scope;
042import org.jboss.seam.core.Events;
043import org.jboss.seam.faces.FacesMessages;
044import org.jboss.seam.international.StatusMessage;
045import org.nuxeo.ecm.core.api.CoreSession;
046import org.nuxeo.ecm.core.api.DocumentModel;
047import org.nuxeo.ecm.core.api.DocumentModelList;
048import org.nuxeo.ecm.core.api.DocumentRef;
049import org.nuxeo.ecm.core.api.IdRef;
050import org.nuxeo.ecm.core.api.security.SecurityConstants;
051import org.nuxeo.ecm.core.trash.TrashInfo;
052import org.nuxeo.ecm.core.trash.TrashService;
053import org.nuxeo.ecm.platform.actions.Action;
054import org.nuxeo.ecm.platform.ui.web.api.NavigationContext;
055import org.nuxeo.ecm.platform.ui.web.api.WebActions;
056import org.nuxeo.ecm.platform.util.RepositoryLocation;
057import org.nuxeo.ecm.webapp.documentsLists.DocumentsListsManager;
058import org.nuxeo.ecm.webapp.edit.lock.LockActions;
059import org.nuxeo.ecm.webapp.trashManagement.TrashManager;
060import org.nuxeo.runtime.api.Framework;
061
062@Name("deleteActions")
063@Scope(ScopeType.EVENT)
064@Install(precedence = Install.FRAMEWORK)
065public class DeleteActionsBean implements DeleteActions, Serializable {
066
067    private static final long serialVersionUID = 1L;
068
069    private static final Log log = LogFactory.getLog(DeleteActionsBean.class);
070
071    @In(create = true, required = false)
072    protected FacesMessages facesMessages;
073
074    @In(create = true)
075    protected Map<String, String> messages;
076
077    @In(create = true, required = false)
078    protected transient CoreSession documentManager;
079
080    @In(create = true, required = false)
081    protected RepositoryLocation currentServerLocation;
082
083    @In(create = true)
084    protected transient DocumentsListsManager documentsListsManager;
085
086    @In(create = true)
087    protected NavigationContext navigationContext;
088
089    @In(create = true)
090    protected transient TrashManager trashManager;
091
092    @In(create = true)
093    protected transient LockActions lockActions;
094
095    @In(create = true)
096    protected transient WebActions webActions;
097
098    @In
099    protected transient Principal currentUser;
100
101    protected transient TrashService trashService;
102
103    protected TrashService getTrashService() {
104        if (trashService == null) {
105            trashService = Framework.getService(TrashService.class);
106        }
107        return trashService;
108    }
109
110    @Override
111    public boolean getCanDeleteItem(DocumentModel container) {
112        if (container == null) {
113            return false;
114        }
115        return getTrashService().folderAllowsDelete(container);
116    }
117
118    @Override
119    public boolean getCanDelete() {
120        return getCanDelete(CURRENT_DOCUMENT_SELECTION);
121    }
122
123    @Override
124    public boolean getCanDelete(String listName) {
125        List<DocumentModel> docs = documentsListsManager.getWorkingList(listName);
126        return getTrashService().canDelete(docs, currentUser, false);
127    }
128
129    @Override
130    public boolean getCanDeleteSections() {
131        List<DocumentModel> docs = documentsListsManager.getWorkingList(CURRENT_DOCUMENT_SECTION_SELECTION);
132        return getTrashService().canDelete(docs, currentUser, true);
133    }
134
135    @Override
136    public boolean getCanPurge() {
137        List<DocumentModel> docs = documentsListsManager.getWorkingList(CURRENT_DOCUMENT_TRASH_SELECTION);
138        return getTrashService().canPurgeOrUntrash(docs, currentUser);
139    }
140
141    public boolean getCanEmptyTrash() {
142        List<DocumentModel> selectedDocuments = documentsListsManager.getWorkingList(CURRENT_DOCUMENT_TRASH_SELECTION);
143        if (selectedDocuments.size() == 0) {
144            DocumentModelList currentTrashDocuments = getTrashService().getDocuments(navigationContext.getCurrentDocument());
145            return getTrashService().canPurgeOrUntrash(currentTrashDocuments, currentUser);
146        }
147        return false;
148    }
149
150    @Override
151    public boolean checkDeletePermOnParents(List<DocumentModel> docs) {
152        return getTrashService().checkDeletePermOnParents(docs);
153    }
154
155    @Override
156    public String deleteSelection() {
157        if (!documentsListsManager.isWorkingListEmpty(CURRENT_DOCUMENT_SELECTION)) {
158            return deleteSelection(documentsListsManager.getWorkingList(CURRENT_DOCUMENT_SELECTION));
159        } else {
160            log.debug("No documents selection in context to process delete on...");
161            return null;
162        }
163    }
164
165    @Override
166    public String deleteSelectionSections() {
167        if (!documentsListsManager.isWorkingListEmpty(CURRENT_DOCUMENT_SECTION_SELECTION)) {
168            return deleteSelection(documentsListsManager.getWorkingList(CURRENT_DOCUMENT_SECTION_SELECTION));
169        } else {
170            log.debug("No documents selection in context to process delete on...");
171            return null;
172        }
173    }
174
175    protected static final int OP_DELETE = 1, OP_PURGE = 2, OP_UNDELETE = 3;
176
177    @Override
178    public String deleteSelection(List<DocumentModel> docs) {
179        int op = isTrashManagementEnabled() ? OP_DELETE : OP_PURGE;
180        return actOnSelection(op, docs);
181    }
182
183    public String emptyTrash() {
184        DocumentModelList currentTrashDocuments = trashService.getDocuments(navigationContext.getCurrentDocument());
185        return purgeSelection(currentTrashDocuments);
186    }
187
188    @Override
189    public String purgeSelection() {
190        return purgeSelection(CURRENT_DOCUMENT_TRASH_SELECTION);
191    }
192
193    @Override
194    public String purgeSelection(String listName) {
195        if (!documentsListsManager.isWorkingListEmpty(listName)) {
196            return purgeSelection(documentsListsManager.getWorkingList(listName));
197        } else {
198            log.debug("No documents selection in context to process delete on...");
199            return null;
200        }
201    }
202
203    @Override
204    public String purgeSelection(List<DocumentModel> docs) {
205        return actOnSelection(OP_PURGE, docs);
206    }
207
208    @Override
209    public String undeleteSelection() {
210        if (!documentsListsManager.isWorkingListEmpty(CURRENT_DOCUMENT_TRASH_SELECTION)) {
211            return undeleteSelection(documentsListsManager.getWorkingList(CURRENT_DOCUMENT_TRASH_SELECTION));
212        } else {
213            log.debug("No documents selection in context to process delete on...");
214            return null;
215        }
216    }
217
218    @Override
219    public String undeleteSelection(List<DocumentModel> docs) {
220        return actOnSelection(OP_UNDELETE, docs);
221
222    }
223
224    @SuppressWarnings("deprecation")
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().canPurgeOrUntrash(doc, currentUser);
329    }
330
331    public boolean restoreActionDisplay() {
332        return getCanRestoreCurrentDoc() && isTrashManagementEnabled();
333    }
334}