001/* 002 * (C) Copyright 2006-2012 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 * Estelle Giuly <egiuly@nuxeo.com> 019 * 020 */ 021package org.nuxeo.template.adapters.doc; 022 023import java.io.IOException; 024import java.io.Serializable; 025import java.util.ArrayList; 026import java.util.List; 027 028import org.dom4j.DocumentException; 029import org.nuxeo.ecm.automation.AutomationService; 030import org.nuxeo.ecm.automation.OperationChain; 031import org.nuxeo.ecm.automation.OperationContext; 032import org.nuxeo.ecm.automation.OperationException; 033import org.nuxeo.ecm.automation.core.operations.blob.ConvertBlob; 034import org.nuxeo.ecm.core.api.Blob; 035import org.nuxeo.ecm.core.api.DocumentModel; 036import org.nuxeo.ecm.core.api.DocumentRef; 037import org.nuxeo.ecm.core.api.DocumentSecurityException; 038import org.nuxeo.ecm.core.api.IdRef; 039import org.nuxeo.ecm.core.api.NuxeoException; 040import org.nuxeo.ecm.core.api.blobholder.BlobHolder; 041import org.nuxeo.runtime.api.Framework; 042import org.nuxeo.template.XMLSerializer; 043import org.nuxeo.template.adapters.AbstractTemplateDocument; 044import org.nuxeo.template.api.TemplateInput; 045import org.nuxeo.template.api.TemplateProcessor; 046import org.nuxeo.template.api.TemplateProcessorService; 047import org.nuxeo.template.api.adapters.TemplateBasedDocument; 048import org.nuxeo.template.api.adapters.TemplateSourceDocument; 049import org.nuxeo.template.api.descriptor.OutputFormatDescriptor; 050 051/** 052 * Default implementation of {@link TemplateBasedDocument} adapter. This adapter mainly expect from the underlying 053 * {@link DocumentModel} to have the "TemplateBased" facet 054 * 055 * @author Tiry (tdelprat@nuxeo.com) 056 */ 057public class TemplateBasedDocumentAdapterImpl extends AbstractTemplateDocument implements Serializable, 058 TemplateBasedDocument { 059 060 private static final long serialVersionUID = 1L; 061 062 public static final String TEMPLATEBASED_FACET = "TemplateBased"; 063 064 protected final TemplateBindings bindings; 065 066 public TemplateBasedDocumentAdapterImpl(DocumentModel doc) { 067 adaptedDoc = doc; 068 bindings = new TemplateBindings(doc); 069 } 070 071 @Override 072 public DocumentModel setTemplate(DocumentModel template, boolean save) { 073 074 TemplateSourceDocument source = template.getAdapter(TemplateSourceDocument.class); 075 if (source == null) { 076 throw new NuxeoException("Can not bind to an non template document"); 077 } 078 String tid = source.getId(); 079 String templateName = source.getName(); 080 if (!bindings.containsTemplateId(tid)) { 081 if (templateName == null) { 082 templateName = TemplateBindings.DEFAULT_BINDING; 083 } 084 TemplateBinding tb = new TemplateBinding(); 085 tb.setTemplateId(tid); 086 tb.setName(templateName); 087 bindings.add(tb); 088 initializeFromTemplate(templateName, false); 089 bindings.save(adaptedDoc); 090 if (save) { 091 doSave(); 092 } 093 094 } 095 return adaptedDoc; 096 } 097 098 @Override 099 public DocumentModel removeTemplateBinding(String templateName, boolean save) { 100 if (bindings.containsTemplateName(templateName)) { 101 bindings.removeByName(templateName); 102 bindings.save(adaptedDoc); 103 if (save) { 104 doSave(); 105 } 106 } 107 return adaptedDoc; 108 } 109 110 @Override 111 public TemplateSourceDocument getSourceTemplate(String templateName) { 112 DocumentModel template = getSourceTemplateDoc(templateName); 113 if (template != null) { 114 return template.getAdapter(TemplateSourceDocument.class); 115 } 116 return null; 117 } 118 119 @Override 120 public DocumentRef getSourceTemplateDocRef(String templateName) { 121 TemplateBinding binding = null; 122 if (templateName == null) { 123 binding = bindings.get(); 124 } else { 125 binding = bindings.get(templateName); 126 } 127 if (binding == null) { 128 return null; 129 } 130 return new IdRef(binding.getTemplateId()); 131 } 132 133 @Override 134 public DocumentModel getSourceTemplateDoc(String templateName) { 135 TemplateBinding binding = null; 136 if (templateName == null) { 137 binding = bindings.get(); 138 } else { 139 binding = bindings.get(templateName); 140 } 141 if (binding == null) { 142 return null; 143 } 144 DocumentRef tRef = getSourceTemplateDocRef(templateName); 145 if (tRef == null) { 146 return null; 147 } 148 try { 149 return getSession().getDocument(tRef); 150 } catch (DocumentSecurityException e) { 151 return null; 152 } 153 } 154 155 @Override 156 public List<TemplateSourceDocument> getSourceTemplates() { 157 List<TemplateSourceDocument> result = new ArrayList<TemplateSourceDocument>(); 158 for (TemplateBinding binding : bindings) { 159 TemplateSourceDocument template = getSourceTemplate(binding.getName()); 160 if (template != null) { 161 result.add(template); 162 } 163 } 164 return result; 165 } 166 167 @Override 168 public String getTemplateType(String templateName) { 169 TemplateSourceDocument source = getSourceTemplate(templateName); 170 if (source != null) { 171 return source.getTemplateType(); 172 } 173 return null; 174 } 175 176 public DocumentModel initializeFromTemplate(boolean save) { 177 return initializeFromTemplate(TemplateBindings.DEFAULT_BINDING, save); 178 } 179 180 @Override 181 public DocumentModel initializeFromTemplate(String templateName, boolean save) { 182 183 TemplateSourceDocument tmpl = getSourceTemplate(templateName); 184 if (tmpl == null) { 185 throw new NuxeoException("No associated template for name " + templateName); 186 } 187 188 // copy Params but set as readonly all params set in template 189 List<TemplateInput> params = tmpl.getParams(); 190 List<TemplateInput> myParams = new ArrayList<TemplateInput>(); 191 for (TemplateInput param : params) { 192 boolean readOnly = param.isSet() && !tmpl.allowInstanceOverride(); 193 TemplateInput myParam = param.getCopy(readOnly); 194 myParams.add(myParam); 195 } 196 197 bindings.get(templateName).setData(myParams); 198 199 if (tmpl.useAsMainContent()) { 200 // copy the template as main blob 201 BlobHolder bh = adaptedDoc.getAdapter(BlobHolder.class); 202 if (bh != null) { 203 bh.setBlob(tmpl.getTemplateBlob()); 204 } 205 bindings.get(templateName).setUseMainContentAsTemplate(true); 206 } 207 208 if (save) { 209 doSave(); 210 } 211 return adaptedDoc; 212 } 213 214 @Override 215 protected void doSave() { 216 bindings.save(adaptedDoc); 217 super.doSave(); 218 } 219 220 protected void setBlob(Blob blob) { 221 adaptedDoc.getAdapter(BlobHolder.class).setBlob(blob); 222 } 223 224 @Override 225 public Blob renderWithTemplate(String templateName) { 226 TemplateProcessor processor = getTemplateProcessor(templateName); 227 if (processor != null) { 228 Blob blob; 229 try { 230 blob = processor.renderTemplate(this, templateName); 231 } catch (IOException e) { 232 throw new NuxeoException("Failed to render template: " + templateName, e); 233 } 234 TemplateSourceDocument template = getSourceTemplate(templateName); 235 if (template == null) { 236 throw new NuxeoException("No associated template for name " + templateName); 237 } 238 String format = template.getOutputFormat(); 239 if (blob != null && format != null && !format.isEmpty()) { 240 try { 241 return convertBlob(templateName, blob, format); 242 } catch (OperationException e) { 243 throw new NuxeoException(e); 244 } 245 } else { 246 return blob; 247 } 248 } else { 249 String templateType = getTemplateType(templateName); 250 if (templateType == null) { 251 throw new NuxeoException( 252 "Template type is null : if you don't set it explicitly, your template file should have an extension or a mimetype so that it can be automatically determined"); 253 } else { 254 throw new NuxeoException("No template processor found for template type=" + templateType); 255 } 256 } 257 } 258 259 private Blob convertBlob(String templateName, Blob blob, String outputFormat) throws OperationException { 260 OutputFormatDescriptor outFormat = getOutputFormatDescriptor(outputFormat); 261 String chainId = outFormat.getChainId(); 262 String mimeType = outFormat.getMimeType(); 263 AutomationService automationService = Framework.getService(AutomationService.class); 264 try (OperationContext ctx = initOperationContext(blob, templateName)) { 265 Object result = null; 266 if (chainId != null) { 267 ctx.put("templateSourceDocument", getSourceTemplateDoc(templateName)); 268 ctx.put("templateBasedDocument", adaptedDoc); 269 result = automationService.run(ctx, chainId); 270 } else if (mimeType != null) { 271 OperationChain chain = new OperationChain("convertToMimeType"); 272 chain.add(ConvertBlob.ID).set("mimeType", mimeType); 273 result = automationService.run(ctx, chain); 274 } 275 if (result != null && result instanceof Blob) { 276 return (Blob) result; 277 } else { 278 return blob; 279 } 280 } 281 } 282 283 protected OperationContext initOperationContext(Blob blob, String templateName) { 284 OperationContext ctx = new OperationContext(); 285 ctx.put("templateName", templateName); 286 ctx.setInput(blob); 287 ctx.setCommit(false); 288 ctx.setCoreSession(getSession()); 289 return ctx; 290 } 291 292 @Override 293 public Blob renderAndStoreAsAttachment(String templateName, boolean save) { 294 Blob blob = renderWithTemplate(templateName); 295 setBlob(blob); 296 if (save) { 297 adaptedDoc = getSession().saveDocument(adaptedDoc); 298 } 299 return blob; 300 } 301 302 public boolean isBidirectional() { 303 /* 304 * TemplateProcessor processor = getTemplateProcessor(); if (processor != null) { return processor instanceof 305 * BidirectionalTemplateProcessor; } 306 */ 307 return false; 308 } 309 310 @Override 311 public Blob getTemplateBlob(String templateName) { 312 TemplateSourceDocument source = getSourceTemplate(templateName); 313 if (source != null) { 314 if (source.useAsMainContent()) { 315 BlobHolder bh = getAdaptedDoc().getAdapter(BlobHolder.class); 316 if (bh != null) { 317 Blob blob = bh.getBlob(); 318 if (blob != null) { 319 return blob; 320 } 321 } 322 } 323 // get the template from the source 324 Blob blob = source.getTemplateBlob(); 325 return blob; 326 } 327 // fall back 328 BlobHolder bh = getAdaptedDoc().getAdapter(BlobHolder.class); 329 if (bh == null) { 330 return null; 331 } else { 332 return bh.getBlob(); 333 } 334 } 335 336 @Override 337 public boolean hasParams(String templateName) { 338 return getParams(templateName).size() > 0; 339 } 340 341 @Override 342 public List<TemplateInput> getParams(String templateName) { 343 344 TemplateBinding binding = bindings.get(templateName); 345 if (binding != null) { 346 String xml = binding.getData(); 347 try { 348 return XMLSerializer.readFromXml(xml); 349 } catch (DocumentException e) { 350 log.error("Unable to parse parameters", e); 351 return new ArrayList<TemplateInput>(); 352 } 353 } 354 return new ArrayList<TemplateInput>(); 355 } 356 357 @Override 358 public DocumentModel saveParams(String templateName, List<TemplateInput> params, boolean save) { 359 TemplateBinding binding = bindings.get(templateName); 360 if (binding != null) { 361 binding.setData(params); 362 bindings.save(adaptedDoc); 363 } 364 if (save) { 365 doSave(); 366 } 367 return adaptedDoc; 368 } 369 370 protected TemplateProcessor getTemplateProcessor(String templateName) { 371 TemplateProcessorService tps = Framework.getService(TemplateProcessorService.class); 372 return tps.getProcessor(getTemplateType(templateName)); 373 } 374 375 protected OutputFormatDescriptor getOutputFormatDescriptor(String outputFormat) { 376 TemplateProcessorService tps = Framework.getService(TemplateProcessorService.class); 377 return tps.getOutputFormatDescriptor(outputFormat); 378 } 379 380 @Override 381 public boolean hasEditableParams(String templateName) { 382 for (TemplateInput param : getParams(templateName)) { 383 if (!param.isReadOnly()) { 384 return true; 385 } 386 } 387 return false; 388 } 389 390 @Override 391 public String getTemplateNameForRendition(String renditionName) { 392 for (TemplateBinding binding : bindings) { 393 TemplateSourceDocument template = getSourceTemplate(binding.getName()); 394 if (template != null && renditionName.equals(template.getTargetRenditionName())) { 395 return binding.getName(); 396 } 397 } 398 return null; 399 } 400 401 @Override 402 public List<String> getTemplateNames() { 403 return bindings.getNames(); 404 } 405 406}