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