001/* 002 * Copyright 2017 Anyware Services 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 */ 016package org.ametys.plugins.frontedition; 017 018import java.io.IOException; 019import java.util.ArrayList; 020import java.util.Arrays; 021import java.util.HashMap; 022import java.util.List; 023import java.util.Locale; 024import java.util.Map; 025import java.util.Set; 026import java.util.stream.Collectors; 027 028import javax.jcr.Node; 029import javax.jcr.RepositoryException; 030import javax.jcr.lock.LockManager; 031 032import org.apache.avalon.framework.parameters.Parameters; 033import org.apache.avalon.framework.service.ServiceException; 034import org.apache.avalon.framework.service.ServiceManager; 035import org.apache.cocoon.ProcessingException; 036import org.apache.cocoon.environment.ObjectModelHelper; 037import org.apache.cocoon.environment.Redirector; 038import org.apache.cocoon.environment.Request; 039import org.apache.cocoon.environment.SourceResolver; 040import org.apache.commons.lang.StringUtils; 041 042import org.ametys.cms.content.GetMetadataSetDefAction; 043import org.ametys.cms.content.external.ExternalizableMetadataProviderExtensionPoint; 044import org.ametys.cms.contenttype.ContentTypesHelper; 045import org.ametys.cms.contenttype.MetadataDefinition; 046import org.ametys.cms.contenttype.MetadataType; 047import org.ametys.cms.lock.LockContentManager; 048import org.ametys.cms.repository.Content; 049import org.ametys.cms.transformation.RichTextTransformer; 050import org.ametys.core.cocoon.JSonReader; 051import org.ametys.core.right.RightManager; 052import org.ametys.core.user.CurrentUserProvider; 053import org.ametys.core.user.UserIdentity; 054import org.ametys.plugins.core.user.UserHelper; 055import org.ametys.plugins.explorer.resources.Resource; 056import org.ametys.plugins.repository.AmetysObject; 057import org.ametys.plugins.repository.AmetysObjectResolver; 058import org.ametys.plugins.repository.AmetysRepositoryException; 059import org.ametys.plugins.repository.jcr.JCRAmetysObject; 060import org.ametys.plugins.repository.lock.LockHelper; 061import org.ametys.plugins.repository.lock.LockableAmetysObject; 062import org.ametys.plugins.repository.metadata.BinaryMetadata; 063import org.ametys.plugins.repository.metadata.ModifiableRichText; 064import org.ametys.plugins.repository.version.VersionableAmetysObject; 065import org.ametys.runtime.parameter.ParameterHelper; 066import org.ametys.web.renderingcontext.RenderingContext; 067import org.ametys.web.renderingcontext.RenderingContextHandler; 068 069/** 070 * Check if the content can be edited, and return the value 071 */ 072public class GetServerValuesAction extends GetMetadataSetDefAction 073{ 074 /** The ametys object resolver */ 075 protected AmetysObjectResolver _resolver; 076 /** The rendering context handler */ 077 protected RenderingContextHandler _renderingContextHandler; 078 /** User helper */ 079 protected UserHelper _userHelper; 080 /** Lock Content Manager */ 081 protected LockContentManager _lockContentManager; 082 /** Our metatadata manager for file */ 083 protected FileMetadataManager _fileMetadataManager; 084 085 @Override 086 public void service(ServiceManager serviceManager) throws ServiceException 087 { 088 super.service(serviceManager); 089 _resolver = (AmetysObjectResolver) manager.lookup(AmetysObjectResolver.ROLE); 090 _rightManager = (RightManager) manager.lookup(RightManager.ROLE); 091 _currentUserProvider = (CurrentUserProvider) manager.lookup(CurrentUserProvider.ROLE); 092 _renderingContextHandler = (RenderingContextHandler) manager.lookup(RenderingContextHandler.ROLE); 093 _contentTypesHelper = (ContentTypesHelper) manager.lookup(ContentTypesHelper.ROLE); 094 _externalizableMetaProvider = (ExternalizableMetadataProviderExtensionPoint) manager.lookup(ExternalizableMetadataProviderExtensionPoint.ROLE); 095 _userHelper = (UserHelper) manager.lookup(UserHelper.ROLE); 096 _lockContentManager = (LockContentManager) manager.lookup(LockContentManager.ROLE); 097 _fileMetadataManager = (FileMetadataManager) manager.lookup(FileMetadataManager.ROLE); 098 } 099 100 @Override 101 public Map act(Redirector redirector, SourceResolver resolver, Map objectModel, String source, Parameters parameters) throws Exception 102 { 103 Request request = ObjectModelHelper.getRequest(objectModel); 104 105 String contentId = parameters.getParameter("contentId"); 106 String metadataPathsAsString = parameters.getParameter("metadataPaths"); 107 String workflowIdsAsString = parameters.getParameter("workflowIds", null); 108 109 Map<String, Object> jsonObject = new HashMap<>(); 110 boolean success = true; 111 112 if (metadataPathsAsString == null) 113 { 114 success = false; 115 jsonObject.put("error", "no metadata"); 116 request.setAttribute(JSonReader.OBJECT_TO_READ, jsonObject); 117 return EMPTY_MAP; 118 } 119 List<String> metadataPaths = Arrays.asList(metadataPathsAsString.split(";")); 120 121 boolean validateContent = parameters.getParameterAsBoolean("validateContent", false); 122 String metadataSetName = parameters.getParameter("metadataSetName", "main"); 123 124 boolean isEditionMode = "true".equals(request.getParameter("_edition")); 125 126 validateContent &= !isEditionMode; //no validation if in edition mode 127 128 Content content = _resolver.resolveById(contentId); 129 // lock validation 130 UserIdentity locker = isContentLocked(content); 131 if (locker != null) 132 { 133 success = false; 134 String userFullName = _userHelper.getUserFullName(locker); 135 jsonObject.put("error", "locked"); 136 Map<String, String> userIdentyJson = new HashMap<>(); 137 userIdentyJson.put("fullName", userFullName); 138 jsonObject.put("locker", userIdentyJson); 139 } 140 else if (validateContent) 141 { 142 // draft/live validation 143 RenderingContext context = _renderingContextHandler.getRenderingContext(); 144 if (context == RenderingContext.FRONT && content instanceof VersionableAmetysObject) 145 { 146 String[] labels = ((VersionableAmetysObject) content).getLabels(); 147 if (!Arrays.asList(labels).contains("Live")) 148 { 149 success = false; 150 jsonObject.put("error", "draft"); 151 } 152 } 153 } 154 155 // workflow validation 156 if (success) 157 { 158 if (workflowIdsAsString == null) 159 { 160 success = false; 161 jsonObject.put("error", "no workflow Ids"); 162 request.setAttribute(JSonReader.OBJECT_TO_READ, jsonObject); 163 return EMPTY_MAP; 164 } 165 List<String> workflowIdsAsStrings = Arrays.asList(workflowIdsAsString.split(";")); 166 List<Integer> workflowIds = new ArrayList<>(); 167 for (String workflowIdAsString : workflowIdsAsStrings) 168 { 169 workflowIds.add(Integer.parseInt(workflowIdAsString)); 170 } 171 boolean workflowRightsOk = AmetysFrontEditionHelper.hasWorkflowRight(workflowIds, contentId, false); 172 if (!workflowRightsOk) 173 { 174 success = false; 175 jsonObject.put("error", "workflow-rights"); 176 request.setAttribute(JSonReader.OBJECT_TO_READ, jsonObject); 177 return EMPTY_MAP; 178 } 179 } 180 181 if (success) 182 { 183 List<String> contentIds = new ArrayList<>(1); 184 contentIds.add(contentId); 185 _lockContentManager.unlockOrLock(contentIds, "lock"); 186 Map<String, Object> metadataJsonObject = new HashMap<>(); 187 for (String metadataPath : metadataPaths) 188 { 189 Map<String, Object> contentMetadata2Json = _contentMetadata2Json(metadataSetName, content, metadataPath); 190 metadataJsonObject.put(metadataPath, contentMetadata2Json); 191 } 192 jsonObject.put("data", metadataJsonObject); 193 } 194 195 request.setAttribute(JSonReader.OBJECT_TO_READ, jsonObject); 196 return EMPTY_MAP; 197 } 198 199 /** 200 * Check if the content is locked 201 * @param content The content 202 * @return UserIdentity of the locker, of null if not locked 203 */ 204 protected UserIdentity isContentLocked(Content content) 205 { 206 if (!(content instanceof JCRAmetysObject)) 207 { 208 return null; 209 } 210 211 try 212 { 213 Node node = ((JCRAmetysObject) content).getNode(); 214 LockManager lockManager = node.getSession().getWorkspace().getLockManager(); 215 216 if (lockManager.isLocked(node.getPath())) 217 { 218 Node lockHolder = lockManager.getLock(node.getPath()).getNode(); 219 220 AmetysObject ao = _resolver.resolve(lockHolder, false); 221 if (ao instanceof LockableAmetysObject) 222 { 223 LockableAmetysObject lockableAO = (LockableAmetysObject) ao; 224 if (!LockHelper.isLockOwner(lockableAO, _currentUserProvider.getUser())) 225 { 226 return lockableAO.getLockOwner(); 227 } 228 } 229 } 230 } 231 catch (RepositoryException e) 232 { 233 getLogger().error(String.format("Repository exception during lock checking for ametys object '%s'", content.getId()), e); 234 throw new AmetysRepositoryException(e); 235 } 236 return null; 237 } 238 239 private Map<String, Object> _contentMetadata2Json(String metadataSetName, Content content, String metadataPath) throws ProcessingException 240 { 241 MetadataDefinition metadataDef = _contentTypesHelper.getMetadataDefinition(metadataPath, content); 242 243 if (metadataDef == null) 244 { 245 throw new ProcessingException(String.format("Unknown metadata path '%s' in metadata set '%s' of type '%s' for content type(s) '%s'", 246 metadataPath, metadataSetName, "edition", StringUtils.join(content.getTypes(), ','))); 247 } 248 249 Set<String> externalAndLocalMetadata = _externalizableMetaProvider.getExternalAndLocalMetadata(content); 250 Map<String, Object> jsonObject = metadataDefinition2JsonObject(content, null, metadataDef, metadataPath, externalAndLocalMetadata); 251 252 if (_contentTypesHelper.canRead(content, metadataDef)) 253 { 254 List<Object> values = _contentHelper.getMetadataValues(content, metadataPath, new Locale(content.getLanguage()), false, false); 255 256 if (values.size() > 0) 257 { 258 if (MetadataType.RICH_TEXT.equals(metadataDef.getType())) 259 { 260 ModifiableRichText value = (ModifiableRichText) values.get(0); 261 262 StringBuilder result = new StringBuilder(2048); 263 try 264 { 265 RichTextTransformer richTextTransformer = metadataDef.getRichTextTransformer(); 266 richTextTransformer.transformForEditing(value, result); 267 jsonObject.put("value", result.toString()); 268 } 269 catch (IOException e) 270 { 271 throw new AmetysRepositoryException("Unable to transform a rich text into a string", e); 272 } 273 } 274 else if (MetadataType.FILE.equals(metadataDef.getType())) 275 { 276 Object value = values.get(0); 277 278 if (value instanceof BinaryMetadata) 279 { 280 jsonObject.put("value", _fileMetadataManager.readBinaryMetadata(content, (BinaryMetadata) value, metadataPath)); 281 } 282 else if (value instanceof Resource) 283 { 284 jsonObject.put("value", _fileMetadataManager.readResource((Resource) value)); 285 } 286 else if (value instanceof String) 287 { 288 try 289 { 290 Resource resource = _resolver.resolveById((String) value); 291 jsonObject.put("value", _fileMetadataManager.readResource(resource)); 292 } 293 catch (AmetysRepositoryException e) 294 { 295 getLogger().info("Cannot sax existing value for metadata '" + metadataPath + "' of content '" + content.getId() + "'. The user will see an empty value", e); 296 } 297 } 298 } 299 else 300 { 301 List<String> valuesAsStr = values.stream() 302 .map(v -> ParameterHelper.valueToString(v)) 303 .collect(Collectors.toList()); 304 305 jsonObject.put("value", metadataDef.isMultiple() ? valuesAsStr.toArray(new String[valuesAsStr.size()]) : valuesAsStr.get(0)); 306 } 307 } 308 } 309 310 return jsonObject; 311 } 312}