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}