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}