001/*
002 * (C) Copyright 2006-2012 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 *     Razvan Caraghin
016 *     Florent Guillaume
017 *     Thierry Martins
018 *     Antoine Taillefer
019 */
020
021package org.nuxeo.ecm.webapp.versioning;
022
023import static org.jboss.seam.ScopeType.CONVERSATION;
024import static org.jboss.seam.ScopeType.EVENT;
025import static org.jboss.seam.annotations.Install.FRAMEWORK;
026import static org.nuxeo.ecm.webapp.helpers.EventNames.DOCUMENT_CHANGED;
027import static org.nuxeo.ecm.webapp.helpers.EventNames.DOCUMENT_PUBLISHED;
028import static org.nuxeo.ecm.webapp.helpers.EventNames.DOCUMENT_SELECTION_CHANGED;
029import static org.nuxeo.ecm.webapp.helpers.EventNames.DOCUMENT_SUBMITED_FOR_PUBLICATION;
030
031import java.io.Serializable;
032import java.util.ArrayList;
033import java.util.List;
034
035import org.apache.commons.logging.Log;
036import org.apache.commons.logging.LogFactory;
037import org.jboss.seam.annotations.Create;
038import org.jboss.seam.annotations.Factory;
039import org.jboss.seam.annotations.In;
040import org.jboss.seam.annotations.Install;
041import org.jboss.seam.annotations.Name;
042import org.jboss.seam.annotations.Observer;
043import org.jboss.seam.annotations.Scope;
044import org.jboss.seam.annotations.intercept.BypassInterceptors;
045import org.jboss.seam.contexts.Context;
046import org.jboss.seam.faces.FacesMessages;
047import org.jboss.seam.international.StatusMessage;
048import org.nuxeo.ecm.core.api.CoreSession;
049import org.nuxeo.ecm.core.api.DocumentModel;
050import org.nuxeo.ecm.core.api.DocumentRef;
051import org.nuxeo.ecm.core.api.IdRef;
052import org.nuxeo.ecm.core.api.VersionModel;
053import org.nuxeo.ecm.core.api.impl.VersionModelImpl;
054import org.nuxeo.ecm.core.api.security.SecurityConstants;
055import org.nuxeo.ecm.platform.query.api.PageSelection;
056import org.nuxeo.ecm.platform.query.api.PageSelections;
057import org.nuxeo.ecm.platform.ui.web.api.NavigationContext;
058import org.nuxeo.ecm.platform.ui.web.api.UserAction;
059import org.nuxeo.ecm.webapp.documentsLists.DocumentsListsManager;
060import org.nuxeo.ecm.webapp.helpers.EventManager;
061import org.nuxeo.ecm.webapp.helpers.ResourcesAccessor;
062
063/**
064 * Deals with versioning actions.
065 *
066 * @author Razvan Caraghin
067 * @author Florent Guillaume
068 * @author Thierry Martins
069 */
070@Name("versionedActions")
071@Scope(CONVERSATION)
072@Install(precedence = FRAMEWORK)
073public class VersionedActionsBean implements VersionedActions, Serializable {
074
075    private static final long serialVersionUID = 4472648747609642493L;
076
077    private static final Log log = LogFactory.getLog(VersionedActionsBean.class);
078
079    @In(create = true)
080    protected transient NavigationContext navigationContext;
081
082    /** @since 5.6 */
083    @In(create = true, required = false)
084    protected transient DocumentsListsManager documentsListsManager;
085
086    @In(create = true, required = false)
087    protected transient CoreSession documentManager;
088
089    @In(create = true, required = false)
090    protected transient FacesMessages facesMessages;
091
092    @In(create = true, required = false)
093    protected transient ResourcesAccessor resourcesAccessor;
094
095    @In
096    protected transient Context sessionContext;
097
098    @In(create = true)
099    protected transient DocumentVersioning documentVersioning;
100
101    protected transient PageSelections<VersionModel> versionModelList;
102
103    protected String selectedVersionId;
104
105    protected String checkedOut;
106
107    @Create
108    @Override
109    public void initialize() {
110    }
111
112    @Observer(value = { DOCUMENT_SELECTION_CHANGED, DOCUMENT_CHANGED, DOCUMENT_SUBMITED_FOR_PUBLICATION,
113            DOCUMENT_PUBLISHED }, create = false)
114    @BypassInterceptors
115    @Override
116    public void resetVersions() {
117        versionModelList = null;
118    }
119
120    @Override
121    @Factory(value = "versionList", scope = EVENT)
122    public PageSelections<VersionModel> getVersionList() {
123        if (versionModelList == null || versionModelList.getEntries() == null
124                || versionModelList.getEntries().isEmpty()) {
125            retrieveVersions();
126        }
127        return versionModelList;
128    }
129
130    @Override
131    public void retrieveVersions() {
132        /**
133         * in case the document is a proxy,meaning is the result of a publishing,to have the history of the document
134         * from which this proxy was created,first we have to get to the version that was created when the document was
135         * publish,and to which the proxy document indicates,and then from that version we have to get to the root
136         * document.
137         */
138        DocumentModel currentDocument = navigationContext.getCurrentDocument();
139        DocumentModel doc;
140        if (currentDocument.isProxy()) {
141            DocumentRef ref = currentDocument.getRef();
142            DocumentModel version = documentManager.getSourceDocument(ref);
143            doc = documentManager.getSourceDocument(version.getRef());
144        } else {
145            doc = currentDocument;
146        }
147        List<PageSelection<VersionModel>> versionModelSelections = new ArrayList<PageSelection<VersionModel>>();
148        for (VersionModel versionModel : documentVersioning.getItemVersioningHistory(doc)) {
149            versionModelSelections.add(new PageSelection<VersionModel>(versionModel, isVersionSelected(versionModel)));
150        }
151        versionModelList = new PageSelections<VersionModel>(versionModelSelections);
152    }
153
154    /**
155     * Checks if the {@code versionModel} is selected.
156     *
157     * @param versionModel the version model
158     * @return true, if the {@versionModel} is selected
159     */
160    protected boolean isVersionSelected(VersionModel versionModel) {
161
162        List<DocumentModel> currentVersionSelection = documentsListsManager.getWorkingList(DocumentsListsManager.CURRENT_VERSION_SELECTION);
163        if (currentVersionSelection != null) {
164            DocumentModel currentDocument = navigationContext.getCurrentDocument();
165            if (currentDocument != null) {
166                DocumentModel version = documentManager.getDocumentWithVersion(currentDocument.getRef(), versionModel);
167                if (version != null) {
168                    return currentVersionSelection.contains(version);
169                }
170            }
171        }
172        return false;
173    }
174
175    /**
176     * Restores the document to the selected version. If there is no selected version it does nothing.
177     *
178     * @return the page that needs to be displayed next
179     */
180    @Override
181    public String restoreToVersion(VersionModel selectedVersion) {
182        DocumentModel restoredDocument = documentManager.restoreToVersion(
183                navigationContext.getCurrentDocument().getRef(), new IdRef(selectedVersion.getId()), true, true);
184        documentManager.save();
185
186        // same as edit basically
187        // XXX AT: do edit events need to be sent?
188        EventManager.raiseEventsOnDocumentChange(restoredDocument);
189        return navigationContext.navigateToDocument(restoredDocument, "after-edit");
190    }
191
192    @Override
193    public String restoreToVersion() {
194        if (getSelectedVersionId() != null) {
195            VersionModel selectedVersion = new VersionModelImpl();
196            selectedVersion.setId(getSelectedVersionId());
197            return restoreToVersion(selectedVersion);
198        }
199        return null;
200    }
201
202    @Override
203    public String viewArchivedVersion(VersionModel selectedVersion) {
204        return navigationContext.navigateToDocument(navigationContext.getCurrentDocument(), selectedVersion);
205    }
206
207    @Override
208    public String viewArchivedVersion() {
209        if (getSelectedVersionId() != null) {
210            VersionModel selectedVersion = new VersionModelImpl();
211            selectedVersion.setId(getSelectedVersionId());
212            return viewArchivedVersion(selectedVersion);
213        }
214        return null;
215    }
216
217    @Override
218    public boolean getCanRestore() {
219        // TODO: should check for a specific RESTORE permission instead
220        return documentManager.hasPermission(navigationContext.getCurrentDocument().getRef(),
221                SecurityConstants.WRITE_VERSION);
222    }
223
224    /**
225     * Tells if the current selected document is checked out or not.
226     */
227    @Override
228    public String getCheckedOut() {
229        if (documentManager.isCheckedOut(navigationContext.getCurrentDocument().getRef())) {
230            checkedOut = "Checked-out";
231        } else {
232            checkedOut = "Checked-in";
233        }
234        return checkedOut;
235    }
236
237    @Override
238    public void setCheckedOut(String checkedOut) {
239        this.checkedOut = checkedOut;
240    }
241
242    /**
243     * Checks the document out.
244     *
245     * @return the next page
246     */
247    @Override
248    public String checkOut() {
249        documentManager.checkOut(navigationContext.getCurrentDocument().getRef());
250        return null;
251    }
252
253    /**
254     * Checks the selected document in, with the selected version.
255     */
256    @Override
257    public String checkIn() {
258        DocumentModel currentDocument = navigationContext.getCurrentDocument();
259        documentManager.checkIn(currentDocument.getRef(), null, null);
260        retrieveVersions();
261        return navigationContext.getActionResult(currentDocument, UserAction.AFTER_EDIT);
262    }
263
264    @Override
265    public DocumentModel getSourceDocument() {
266        return getSourceDocument(navigationContext.getCurrentDocument());
267    }
268
269    /**
270     * @since 5.4
271     */
272    @Override
273    public DocumentModel getSourceDocument(DocumentModel document) {
274        return documentManager.getSourceDocument(document.getRef());
275    }
276
277    @Override
278    public boolean canRemoveArchivedVersion(VersionModel selectedVersion) {
279        DocumentRef docRef = navigationContext.getCurrentDocument().getRef();
280        DocumentModel docVersion = documentManager.getDocumentWithVersion(docRef, selectedVersion);
281        if (docVersion == null) {
282            // it doesn't exist? don't remove. Still it is a problem
283            log.warn("Unexpectedly couldn't find the version " + selectedVersion.getLabel());
284            return false;
285        }
286        return documentManager.canRemoveDocument(docVersion.getRef());
287    }
288
289    /**
290     * @since 5.6
291     */
292    @Override
293    public boolean getCanRemoveSelectedArchivedVersions() {
294        List<DocumentModel> currentVersionSelection = documentsListsManager.getWorkingList(DocumentsListsManager.CURRENT_VERSION_SELECTION);
295        if (currentVersionSelection != null && currentVersionSelection.size() > 0) {
296            for (DocumentModel version : currentVersionSelection) {
297                if (!documentManager.canRemoveDocument(version.getRef())) {
298                    return false;
299                }
300            }
301            return true;
302        }
303        return false;
304    }
305
306    @Override
307    public String removeArchivedVersion(VersionModel selectedVersion) {
308        DocumentRef docRef = navigationContext.getCurrentDocument().getRef();
309        DocumentModel docVersion = documentManager.getDocumentWithVersion(docRef, selectedVersion);
310        if (docVersion == null) {
311            // it doesn't exist? consider removed
312            log.warn("Unexpectedly couldn't find the version " + selectedVersion.getLabel());
313            return null;
314        }
315        documentManager.removeDocument(docVersion.getRef());
316        documentManager.save();
317        resetVersions();
318        facesMessages.add(StatusMessage.Severity.INFO,
319                resourcesAccessor.getMessages().get("feedback.versioning.versionRemoved"));
320        return null;
321    }
322
323    /**
324     * @since 5.6
325     */
326    @Override
327    public String removeSelectedArchivedVersions() {
328
329        List<DocumentModel> currentVersionSelection = documentsListsManager.getWorkingList(DocumentsListsManager.CURRENT_VERSION_SELECTION);
330        if (currentVersionSelection == null || currentVersionSelection.isEmpty()) {
331            log.warn("Currently selected version list is null or empty, cannot remove any version.");
332            return null;
333        }
334        for (DocumentModel version : currentVersionSelection) {
335            if (version != null) {
336                documentManager.removeDocument(version.getRef());
337            }
338        }
339        documentManager.save();
340        resetVersions();
341        // remove from all lists
342        documentsListsManager.removeFromAllLists(new ArrayList<DocumentModel>(currentVersionSelection));
343        facesMessages.add(StatusMessage.Severity.INFO,
344                resourcesAccessor.getMessages().get("feedback.versioning.versionsRemoved"));
345        return null;
346    }
347
348    @Override
349    public String getSelectedVersionId() {
350        return selectedVersionId;
351    }
352
353    @Override
354    public void setSelectedVersionId(String selectedVersionId) {
355        this.selectedVersionId = selectedVersionId;
356    }
357
358}