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