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