001/*
002 * (C) Copyright 2006-2007 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 *     Nuxeo - initial API and implementation
018 *
019 * $Id$
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;
026
027import java.io.Serializable;
028import java.util.Collection;
029import java.util.Collections;
030import java.util.LinkedHashMap;
031import java.util.List;
032import java.util.Locale;
033import java.util.Map;
034import java.util.Map.Entry;
035
036import javax.faces.application.FacesMessage;
037import javax.faces.component.UIComponent;
038import javax.faces.context.FacesContext;
039import javax.faces.validator.ValidatorException;
040
041import org.apache.commons.logging.Log;
042import org.apache.commons.logging.LogFactory;
043import org.jboss.seam.annotations.Factory;
044import org.jboss.seam.annotations.In;
045import org.jboss.seam.annotations.Install;
046import org.jboss.seam.annotations.Name;
047import org.jboss.seam.annotations.Observer;
048import org.jboss.seam.annotations.Scope;
049import org.jboss.seam.annotations.intercept.BypassInterceptors;
050import org.nuxeo.common.utils.i18n.I18NUtils;
051import org.nuxeo.ecm.core.api.CoreSession;
052import org.nuxeo.ecm.core.api.DocumentModel;
053import org.nuxeo.ecm.core.api.VersionModel;
054import org.nuxeo.ecm.core.api.VersioningOption;
055import org.nuxeo.ecm.core.api.facet.VersioningDocument;
056import org.nuxeo.ecm.core.versioning.VersioningService;
057import org.nuxeo.ecm.platform.ui.web.api.NavigationContext;
058import org.nuxeo.ecm.platform.versioning.api.VersionIncEditOptions;
059import org.nuxeo.ecm.platform.versioning.api.VersioningActions;
060import org.nuxeo.ecm.platform.versioning.api.VersioningManager;
061import org.nuxeo.ecm.webapp.helpers.EventNames;
062import org.nuxeo.ecm.webapp.helpers.ResourcesAccessor;
063
064/**
065 * Web action bean for document versioning. Used also by other seam components through injection.
066 *
067 * @author Dragos Mihalache
068 */
069@Name("documentVersioning")
070@Scope(CONVERSATION)
071@Install(precedence = FRAMEWORK)
072public class DocumentVersioningBean implements DocumentVersioning, Serializable {
073
074    private static final long serialVersionUID = 75409841629876L;
075
076    private static final Log log = LogFactory.getLog(DocumentVersioningBean.class);
077
078    @In(create = true)
079    protected transient ResourcesAccessor resourcesAccessor;
080
081    @In(create = true, required = false)
082    protected transient CoreSession documentManager;
083
084    /**
085     * @deprecated since 5.7.3: versioning options map does not need to be kept in cache anymore here
086     */
087    @Deprecated
088    protected Map<String, String> availableVersioningOptionsMap;
089
090    @In(create = true)
091    protected transient NavigationContext navigationContext;
092
093    @In(create = true)
094    private transient VersioningManager versioningManager;
095
096    /**
097     * field used for deciding whether or not to display versioning controls section (in document editing)
098     */
099    private Boolean rendered;
100
101    private VersioningActions selectedOption;
102
103    @Override
104    public Collection<VersionModel> getItemVersioningHistory(DocumentModel document) {
105        List<VersionModel> versions = Collections.emptyList();
106        versions = documentManager.getVersionsForDocument(document.getRef());
107        for (VersionModel model : versions) {
108            DocumentModel ver = documentManager.getDocumentWithVersion(document.getRef(), model);
109            if (ver != null) {
110                model.setDescription(ver.getAdapter(VersioningDocument.class).getVersionLabel());
111            }
112        }
113        return versions;
114    }
115
116    @Override
117    public Collection<VersionModel> getCurrentItemVersioningHistory() {
118        return getItemVersioningHistory(navigationContext.getCurrentDocument());
119    }
120
121    @Factory(autoCreate = true, value = "currentDocumentVersionInfo", scope = EVENT)
122    public VersionInfo getCurrentDocumentVersionInfo() {
123        DocumentModel doc = navigationContext.getCurrentDocument();
124        if (doc == null) {
125            return null;
126        }
127        String versionLabel = versioningManager.getVersionLabel(doc);
128        boolean available = versionLabel != null && versionLabel.length() != 0;
129        return new VersionInfo(versionLabel, available);
130    }
131
132    @Observer(value = { EventNames.DOCUMENT_SELECTION_CHANGED, EventNames.DOCUMENT_CHANGED }, create = false)
133    @BypassInterceptors
134    public void resetVersioningOption() {
135        availableVersioningOptionsMap = null;
136        selectedOption = null;
137        rendered = null;
138    }
139
140    @Deprecated
141    @Override
142    public Map<String, String> getAvailableVersioningOptionsMap() {
143        // FIXME: should cache the map and invalidate it correctly as it refers
144        // to current document
145        DocumentModel doc = navigationContext.getCurrentDocument();
146        LinkedHashMap<String, String> map = new LinkedHashMap<String, String>();
147        for (Entry<String, String> en : getVersioningOptionsMap(doc).entrySet()) {
148            // reverse keys with values for the jsf controls
149            map.put(en.getValue(), en.getKey());
150        }
151        availableVersioningOptionsMap = map;
152        return availableVersioningOptionsMap;
153    }
154
155    @Override
156    public Map<String, String> getVersioningOptionsMap(DocumentModel doc) {
157        Map<String, String> map = new LinkedHashMap<String, String>();
158        VersionIncEditOptions options = getAvailableVersioningOptions(doc);
159        if (options != null) {
160            for (VersioningActions option : options.getOptions()) {
161                String label = "label.versioning.option." + option.toString();
162                if (resourcesAccessor != null) {
163                    label = resourcesAccessor.getMessages().get(label);
164                }
165                map.put(option.name(), label);
166            }
167        }
168        return map;
169    }
170
171    /**
172     * @deprecated since 5.7.3: options are resolved from the layout value instead now that a generic widget definition
173     *             handles it
174     */
175    @Deprecated
176    private VersionIncEditOptions getCurrentAvailableVersioningOptions() {
177        return getAvailableVersioningOptions(navigationContext.getCurrentDocument());
178    }
179
180    public VersionIncEditOptions getAvailableVersioningOptions(DocumentModel doc) {
181        return versioningManager.getVersionIncEditOptions(doc);
182    }
183
184    @Override
185    public String getVersionLabel(DocumentModel doc) {
186        return versioningManager.getVersionLabel(doc);
187    }
188
189    @Deprecated
190    @Override
191    public String getVersioningOptionInstanceId() {
192        if (selectedOption == null) {
193            // FIXME: should cache the versioning options and invalidate them
194            // correctly as it refers to current document
195            VersionIncEditOptions options = getCurrentAvailableVersioningOptions();
196            if (options != null) {
197                selectedOption = options.getDefaultVersioningAction();
198            }
199            if (selectedOption == null) {
200                selectedOption = VersioningActions.ACTION_NO_INCREMENT;
201            }
202        }
203        return selectedOption.name();
204    }
205
206    @Deprecated
207    @Override
208    public void setVersioningOptionInstanceId(String optionId) {
209        setVersioningOptionInstanceId(navigationContext.getCurrentDocument(), optionId);
210    }
211
212    @Deprecated
213    @Override
214    public void setVersioningOptionInstanceId(DocumentModel docModel, String optionId) {
215        if (optionId != null) {
216            selectedOption = VersioningActions.valueOf(optionId);
217            setVersioningOptionInstanceId(docModel, selectedOption);
218        } else {
219            // component is present but no option has been selected
220            // should not reach here...
221        }
222    }
223
224    @Deprecated
225    @Override
226    public void setVersioningOptionInstanceId(DocumentModel docModel, VersioningActions option) {
227        // add version inc option to document context so it will be
228        // taken into consideration on the server side
229        VersioningOption vo;
230        if (option == VersioningActions.ACTION_INCREMENT_MAJOR) {
231            vo = VersioningOption.MAJOR;
232        } else if (option == VersioningActions.ACTION_INCREMENT_MINOR) {
233            vo = VersioningOption.MINOR;
234        } else if (option == VersioningActions.ACTION_NO_INCREMENT) {
235            vo = VersioningOption.NONE;
236        } else {
237            vo = null;
238        }
239        docModel.putContextData(VersioningService.VERSIONING_OPTION, vo);
240    }
241
242    @Override
243    public void validateOptionSelection(FacesContext context, UIComponent component, Object value) {
244        if (value != null) {
245            // ok
246            return;
247        }
248        String bundleName = context.getApplication().getMessageBundle();
249        Locale locale = context.getViewRoot().getLocale();
250        String msg = I18NUtils.getMessageString(bundleName, "error.versioning.none_selected", null, locale);
251        FacesMessage message = new FacesMessage(FacesMessage.SEVERITY_ERROR, msg, msg);
252
253        throw new ValidatorException(message);
254    }
255
256    @Deprecated
257    @Override
258    @Factory(value = "renderVersioningOptionsForCurrentDocument", scope = EVENT)
259    public boolean factoryForRenderVersioningOption() {
260        return getRendered();
261    }
262
263    /**
264     * @deprecated since 5.7.3: rendered clause is now evaluated on the widget definition instead
265     */
266    @Deprecated
267    public boolean getRendered() {
268        if (rendered == null) {
269            rendered = Boolean.FALSE;
270            if (navigationContext.getCurrentDocument() != null) {
271                Map<String, String> options = getAvailableVersioningOptionsMap();
272                // do not display the versioning options if there is only one
273                // choice
274                if (options != null && options.size() > 1) {
275                    rendered = Boolean.TRUE;
276                }
277            }
278        }
279        return rendered.booleanValue();
280    }
281
282    /**
283     * @deprecated since 5.7.3: rendered clause is now evaluated on the widget definition instead
284     */
285    @Deprecated
286    public void setRendered(Boolean rendered) {
287        this.rendered = rendered;
288    }
289
290}