001/* 002 * Copyright 2010 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.repository.workspace; 017 018import java.io.File; 019import java.io.FileInputStream; 020import java.io.InputStream; 021import java.text.ParseException; 022import java.text.SimpleDateFormat; 023import java.util.Arrays; 024import java.util.Collections; 025import java.util.Date; 026import java.util.HashMap; 027import java.util.Map; 028 029import org.apache.avalon.framework.parameters.ParameterException; 030import org.apache.avalon.framework.parameters.Parameters; 031import org.apache.avalon.framework.service.ServiceException; 032import org.apache.avalon.framework.service.ServiceManager; 033import org.apache.cocoon.ProcessingException; 034import org.apache.cocoon.acting.ServiceableAction; 035import org.apache.cocoon.environment.ObjectModelHelper; 036import org.apache.cocoon.environment.Redirector; 037import org.apache.cocoon.environment.Request; 038import org.apache.cocoon.environment.SourceResolver; 039import org.apache.cocoon.servlet.multipart.PartOnDisk; 040import org.apache.commons.lang3.exception.ExceptionUtils; 041 042import org.ametys.core.cocoon.JSonReader; 043import org.ametys.core.util.LambdaUtils; 044import org.ametys.plugins.repository.AmetysObject; 045import org.ametys.plugins.repository.AmetysObjectResolver; 046import org.ametys.plugins.repository.metadata.CompositeMetadata.MetadataType; 047import org.ametys.plugins.repository.metadata.ModifiableCompositeMetadata; 048import org.ametys.plugins.repository.metadata.ModifiableFile; 049import org.ametys.plugins.repository.metadata.ModifiableFolder; 050import org.ametys.plugins.repository.metadata.ModifiableMetadataAwareAmetysObject; 051import org.ametys.plugins.repository.metadata.ModifiableRichText; 052import org.ametys.runtime.i18n.I18nizableText; 053 054/** 055 * Set value for a metadata 056 */ 057public class SetMetadataAction extends ServiceableAction 058{ 059 static SimpleDateFormat _DATE_FORMAT = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss"); 060 private AmetysObjectResolver _resolver; 061 062 @Override 063 public void service(ServiceManager serviceManager) throws ServiceException 064 { 065 super.service(serviceManager); 066 _resolver = (AmetysObjectResolver) serviceManager.lookup(AmetysObjectResolver.ROLE); 067 } 068 069 public Map act(Redirector redirector, SourceResolver sourceResolver, Map objectModel, String source, Parameters parameters) throws Exception 070 { 071 Request request = ObjectModelHelper.getRequest(objectModel); 072 073 String id = null; 074 String name = null; 075 String compositePath = null; 076 String richtextPath = null; 077 boolean multiple; 078 079 String type = null; 080 String fileFieldId = null; 081 082 try 083 { 084 id = parameters.getParameter("id"); 085 name = parameters.getParameter("name"); 086 compositePath = parameters.getParameter("cPath"); 087 richtextPath = parameters.getParameter("rtPath"); 088 type = parameters.getParameter("type"); 089 fileFieldId = parameters.getParameter("filefieldid"); 090 multiple = parameters.getParameterAsBoolean("multiple", false); 091 } 092 catch (ParameterException e) 093 { 094 throw new ProcessingException("Missing at least one parameter", e); 095 } 096 097 if (getLogger().isInfoEnabled()) 098 { 099 getLogger().info("Setting metadata '" + name + "' of the AmetysObject of id '" + id + "'"); 100 } 101 102 AmetysObject ao = null; 103 104 if (id.equals("/")) 105 { 106 ao = _resolver.resolveByPath("/"); 107 } 108 else 109 { 110 ao = _resolver.resolveById(id); 111 } 112 113 String[] values = request.getParameterValues("value"); 114 115 Map<String, Object> result = new HashMap<>(); 116 117 try 118 { 119 ModifiableCompositeMetadata holder = _getHolderInComposite((ModifiableMetadataAwareAmetysObject) ao, compositePath); 120 121 //changing value of a metadata in a composite 122 if (richtextPath.isEmpty()) 123 { 124 _setMetadataOutsideRT(holder, type, name, values, multiple); 125 } 126 //changing a value inside a richtext 127 else if (richtextPath.split("/").length == 1) 128 { 129 _setMetadataInsideRT(request, holder, name, values[0], fileFieldId, richtextPath); 130 } 131 else //TODO this part is not tested !! 132 { 133 _setMetadataInsideRTDataFolder(request, holder, name, values[0], fileFieldId, richtextPath); 134 } 135 136 ((ModifiableMetadataAwareAmetysObject) ao).saveChanges(); 137 138 result.put("success", true); 139 } 140 catch (Exception e) 141 { 142 getLogger().error("Unable to set value " + values[0] + " for metadata '" + name + "'", e); 143 result.put("success", false); 144 145 Throwable rootCause = ExceptionUtils.getRootCause(e); 146 147 if (rootCause instanceof ParseException) 148 { 149 Map<String, I18nizableText> params = Collections.singletonMap("error", new I18nizableText(e.getMessage())); 150 result.put("message", new I18nizableText("plugin.repositoryapp", "PLUGINS_REPOSITORYAPP_CREATE_PROP_ERROR_DATE", params)); 151 } 152 else 153 { 154 result.put("message", e.toString()); 155 } 156 } 157 158 request.setAttribute(JSonReader.OBJECT_TO_READ, result); 159 return EMPTY_MAP; 160 } 161 162 private ModifiableCompositeMetadata _getHolderInComposite(ModifiableMetadataAwareAmetysObject ao, String compositePath) 163 { 164 ModifiableCompositeMetadata holder = ao.getMetadataHolder(); 165 166 // deep search in composite metadata 167 if (!compositePath.isEmpty()) 168 { 169 String[] tokens = compositePath.split("/"); 170 for (int i = 0; i < tokens.length; i++) 171 { 172 holder = holder.getCompositeMetadata(tokens[i]); 173 } 174 } 175 176 return holder; 177 } 178 179 private void _setMetadataOutsideRT(ModifiableCompositeMetadata holder, String type, String name, String[] values, boolean multiple) throws Exception 180 { 181 //switch according to type 182 //convert value to correct type if not string and set new value 183 MetadataType mdType = MetadataType.valueOf(type.toUpperCase()); 184 switch (mdType) 185 { 186 case DATE: 187 if (multiple) 188 { 189 Date[] dateValues = Arrays.stream(values).map(LambdaUtils.wrap(_DATE_FORMAT::parse)).toArray(Date[]::new); 190 holder.setMetadata(name, dateValues); 191 } 192 else 193 { 194 Date dateVal = _DATE_FORMAT.parse(values[0]); 195 holder.setMetadata(name, dateVal); 196 } 197 break; 198 case STRING: 199 if (multiple) 200 { 201 holder.setMetadata(name, values); 202 } 203 else 204 { 205 holder.setMetadata(name, values[0]); 206 } 207 break; 208 case LONG: 209 if (multiple) 210 { 211 long[] longValues = Arrays.stream(values).mapToLong(Long::parseLong).toArray(); 212 holder.setMetadata(name, longValues); 213 } 214 else 215 { 216 Long longVal = Long.parseLong(values[0]); 217 holder.setMetadata(name, longVal); 218 } 219 break; 220 case DOUBLE: 221 if (multiple) 222 { 223 double[] doubleValues = Arrays.stream(values).mapToDouble(Double::parseDouble).toArray(); 224 holder.setMetadata(name, doubleValues); 225 } 226 else 227 { 228 Double doubleVal = Double.parseDouble(values[0]); 229 holder.setMetadata(name, doubleVal); 230 } 231 232 break; 233 case BOOLEAN: 234 Boolean booleanVal = Boolean.parseBoolean(values[0]); 235 holder.setMetadata(name, booleanVal); 236 break; 237 case COMPOSITE: 238 239 break; 240 case BINARY: 241 242 break; 243 case RICHTEXT: 244 245 break; 246 case OBJECT_COLLECTION: 247 // TODO? 248 break; 249 default: 250 break; 251 } 252 } 253 254 private void _setMetadataInsideRT(Request request, ModifiableCompositeMetadata holder, String name, String value, String fileFieldId, String richtextPath) throws Exception 255 { 256 String[] rtTokens = richtextPath.split("/"); 257 ModifiableRichText rt = holder.getRichText(rtTokens[0]); 258 259 // update correct property 260 if (name.equals("mime-type")) 261 { 262 rt.setMimeType(value); 263 } 264 else if (name.equals("last-modified-date")) 265 { 266 Date dateVal = _DATE_FORMAT.parse(value); 267 rt.setLastModified(dateVal); 268 } 269 else if (name.equals("encoding")) 270 { 271 rt.setEncoding(value); 272 } 273 else if (name.equals("data")) 274 { 275 PartOnDisk uploadedFilePart = (PartOnDisk) request.get(fileFieldId); 276 File uploadedFile = (uploadedFilePart != null) ? uploadedFilePart.getFile() : null; 277 278 if (uploadedFile != null) 279 { 280 InputStream is = new FileInputStream(uploadedFile); 281 rt.setInputStream(is); 282 } 283 } 284 else if (name.equals("length")) 285 { 286 //not possible to change length 287 } 288 } 289 290 private void _setMetadataInsideRTDataFolder(Request request, ModifiableCompositeMetadata holder, String name, String value, String fileFieldId, String richtextPath) throws Exception 291 { 292 String[] rtTokens = richtextPath.split("/"); 293 ModifiableRichText rt = holder.getRichText(rtTokens[0]); 294 ModifiableFolder folder = rt.getAdditionalDataFolder(); 295 296 for (int i = 2; i < rtTokens.length - 1; i++) 297 { 298 folder = folder.getFolder(rtTokens[i]); 299 } 300 301 ModifiableFile file = folder.getFile(rtTokens[rtTokens.length - 1]); 302 303 // update correct property 304 if (name.equals("mime-type")) 305 { 306 file.getResource().setMimeType(value); 307 } 308 else if (name.equals("last-modified-date")) 309 { 310 Date dateVal = _DATE_FORMAT.parse(value); 311 file.getResource().setLastModified(dateVal); 312 } 313 else if (name.equals("encoding")) 314 { 315 file.getResource().setEncoding(value); 316 } 317 else if (name.equals("data")) 318 { 319 PartOnDisk uploadedFilePart = (PartOnDisk) request.get(fileFieldId); 320 File uploadedFile = (uploadedFilePart != null) ? uploadedFilePart.getFile() : null; 321 InputStream is = new FileInputStream(uploadedFile); 322 file.getResource().setInputStream(is); 323 } 324 else if (name.equals("length")) 325 { 326 //not possible to change length 327 } 328 } 329}