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