001/*
002 * (C) Copyright 2006-2016 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 *
019 */
020package org.nuxeo.template.adapters.source;
021
022import java.io.IOException;
023import java.io.Serializable;
024import java.util.ArrayList;
025import java.util.Arrays;
026import java.util.List;
027
028import org.dom4j.DocumentException;
029import org.nuxeo.ecm.core.api.Blob;
030import org.nuxeo.ecm.core.api.DocumentModel;
031import org.nuxeo.ecm.core.api.NuxeoException;
032import org.nuxeo.ecm.core.api.PropertyException;
033import org.nuxeo.ecm.core.api.blobholder.BlobHolder;
034import org.nuxeo.runtime.api.Framework;
035import org.nuxeo.template.XMLSerializer;
036import org.nuxeo.template.adapters.AbstractTemplateDocument;
037import org.nuxeo.template.api.TemplateInput;
038import org.nuxeo.template.api.TemplateProcessor;
039import org.nuxeo.template.api.TemplateProcessorService;
040import org.nuxeo.template.api.adapters.TemplateBasedDocument;
041import org.nuxeo.template.api.adapters.TemplateSourceDocument;
042
043/**
044 * Default implementation of {@link TemplateSourceDocument}. It mainly expect from the underlying DocumentModel to have
045 * the "Template" facet.
046 *
047 * @author Tiry (tdelprat@nuxeo.com)
048 */
049public class TemplateSourceDocumentAdapterImpl extends AbstractTemplateDocument implements Serializable,
050        TemplateSourceDocument {
051
052    public static final String TEMPLATE_DATA_PROP = "tmpl:templateData";
053
054    public static final String TEMPLATE_NAME_PROP = "tmpl:templateName";
055
056    public static final String TEMPLATE_TYPE_PROP = "tmpl:templateType";
057
058    public static final String TEMPLATE_TYPE_AUTO = "auto";
059
060    public static final String TEMPLATE_APPLICABLE_TYPES_PROP = "tmpl:applicableTypes";
061
062    public static final String TEMPLATE_APPLICABLE_TYPES_ALL = "all";
063
064    public static final String TEMPLATE_FORCED_TYPES_PROP = "tmpl:forcedTypes";
065
066    public static final String TEMPLATE_FORCED_TYPES_ITEM_PROP = "tmpl:forcedTypes/*";
067
068    public static final String TEMPLATE_FORCED_TYPES_NONE = "none";
069
070    public static final String TEMPLATE_RENDITION_NONE = "none";
071
072    public static final String TEMPLATE_OUTPUT_PROP = "tmpl:outputFormat";
073
074    public static final String TEMPLATE_OVERRIDE_PROP = "tmpl:allowOverride";
075
076    public static final String TEMPLATE_USEASMAIN_PROP = "tmpl:useAsMainContent";
077
078    public static final String TEMPLATE_RENDITION_PROP = "tmpl:targetRenditionName";
079
080    public static final String TEMPLATE_FACET = "Template";
081
082    private static final long serialVersionUID = 1L;
083
084    public TemplateSourceDocumentAdapterImpl(DocumentModel doc) {
085        this.adaptedDoc = doc;
086    }
087
088    protected String getTemplateParamsXPath() {
089        return TEMPLATE_DATA_PROP;
090    }
091
092    @Override
093    public List<TemplateInput> getParams() {
094        String dataPath = getTemplateParamsXPath();
095
096        if (adaptedDoc.getPropertyValue(dataPath) == null) {
097            return new ArrayList<>();
098        }
099        String xml = adaptedDoc.getPropertyValue(dataPath).toString();
100
101        try {
102            return XMLSerializer.readFromXml(xml);
103        } catch (DocumentException e) {
104            log.error("Unable to parse parameters", e);
105            return new ArrayList<>();
106        }
107    }
108
109    @Override
110    public boolean hasEditableParams() {
111        for (TemplateInput param : getParams()) {
112            if (!param.isReadOnly()) {
113                return true;
114            }
115        }
116        return false;
117    }
118
119    @Override
120    public DocumentModel saveParams(List<TemplateInput> params, boolean save) {
121        String dataPath = getTemplateParamsXPath();
122        String xml = XMLSerializer.serialize(params);
123        adaptedDoc.setPropertyValue(dataPath, xml);
124        adaptedDoc.putContextData(TemplateSourceDocument.INIT_DONE_FLAG, true);
125        if (save) {
126            doSave();
127        }
128        return adaptedDoc;
129    }
130
131    protected TemplateProcessor getTemplateProcessor() {
132        TemplateProcessorService tps = Framework.getLocalService(TemplateProcessorService.class);
133        return tps.getProcessor(getTemplateType());
134    }
135
136    @Override
137    public String getParamsAsString() throws PropertyException {
138        String dataPath = getTemplateParamsXPath();
139
140        if (adaptedDoc.getPropertyValue(dataPath) == null) {
141            return null;
142        }
143        return adaptedDoc.getPropertyValue(dataPath).toString();
144    }
145
146    @Override
147    public List<TemplateInput> addInput(TemplateInput input) {
148
149        List<TemplateInput> params = getParams();
150        if (input == null) {
151            return params;
152        }
153
154        boolean newParam = true;
155        if (params == null) {
156            params = new ArrayList<>();
157        }
158        for (TemplateInput param : params) {
159            if (param.getName().equals(input.getName())) {
160                newParam = false;
161                param.update(input);
162                break;
163            }
164        }
165        if (newParam) {
166            params.add(input);
167        }
168        saveParams(params, false);
169
170        return params;
171    }
172
173    @Override
174    public boolean hasInput(String inputName) {
175        List<TemplateInput> params = getParams();
176        return params != null && params.stream().map(TemplateInput::getName).anyMatch(inputName::equals);
177    }
178
179    @Override
180    public String getTemplateType() {
181        String ttype = (String) getAdaptedDoc().getPropertyValue(TEMPLATE_TYPE_PROP);
182        if (TEMPLATE_TYPE_AUTO.equals(ttype)) {
183            return null;
184        }
185        return ttype;
186    }
187
188    @Override
189    public void initTemplate(boolean save) {
190        // avoid duplicate init
191        if (getAdaptedDoc().getContextData(TemplateSourceDocument.INIT_DONE_FLAG) == null) {
192            Blob blob = getTemplateBlob();
193            if (blob != null) {
194                if (getTemplateType() == null) {
195                    TemplateProcessorService tps = Framework.getLocalService(TemplateProcessorService.class);
196                    String templateType = tps.findProcessorName(blob);
197                    if (templateType != null) {
198                        getAdaptedDoc().setPropertyValue(TEMPLATE_TYPE_PROP, templateType);
199                    }
200                }
201
202                String tmplName = (String) getAdaptedDoc().getPropertyValue(TEMPLATE_NAME_PROP);
203                if (tmplName == null || tmplName.isEmpty()) {
204                    tmplName = computeTemplateName();
205                    getAdaptedDoc().setPropertyValue(TEMPLATE_NAME_PROP, tmplName);
206                }
207
208                TemplateProcessor processor = getTemplateProcessor();
209                if (processor != null) {
210                    List<TemplateInput> params;
211                    try {
212                        params = processor.getInitialParametersDefinition(blob);
213                    } catch (IOException e) {
214                        throw new NuxeoException(e);
215                    }
216                    saveParams(params, save);
217                }
218                getAdaptedDoc().getContextData().put(TemplateSourceDocument.INIT_DONE_FLAG, true);
219            }
220        }
221    }
222
223    protected String computeTemplateName() {
224        return getAdaptedDoc().getTitle();
225    }
226
227    @Override
228    public boolean allowInstanceOverride() {
229        Boolean allowOverride = (Boolean) getAdaptedDoc().getPropertyValue(TEMPLATE_OVERRIDE_PROP);
230        if (allowOverride == null) {
231            allowOverride = true;
232        }
233        return allowOverride;
234    }
235
236    @Override
237    public void initTypesBindings() {
238
239        // manage applicable types
240        String[] applicableTypesArray = (String[]) getAdaptedDoc().getPropertyValue(TEMPLATE_APPLICABLE_TYPES_PROP);
241
242        String[] newApplicableTypesArray = null;
243
244        if (applicableTypesArray == null || applicableTypesArray.length == 0) {
245            newApplicableTypesArray = new String[] { TEMPLATE_APPLICABLE_TYPES_ALL };
246        } else if (applicableTypesArray.length > 1) {
247            if (TEMPLATE_APPLICABLE_TYPES_ALL.equals(applicableTypesArray[0])) {
248                List<String> at = Arrays.asList(applicableTypesArray);
249                at.remove(0);
250                newApplicableTypesArray = at.toArray(new String[at.size()]);
251            }
252        }
253        if (newApplicableTypesArray != null) {
254            getAdaptedDoc().setPropertyValue(TEMPLATE_APPLICABLE_TYPES_PROP, newApplicableTypesArray);
255        }
256
257        // manage forcedTypes
258        String[] forcedTypesArray = (String[]) getAdaptedDoc().getPropertyValue(TEMPLATE_FORCED_TYPES_PROP);
259        String[] newForcedTypesArray = null;
260        if (forcedTypesArray == null || forcedTypesArray.length == 0) {
261            newForcedTypesArray = new String[] { TEMPLATE_FORCED_TYPES_NONE };
262        } else if (forcedTypesArray.length > 1) {
263            if (TEMPLATE_FORCED_TYPES_NONE.equals(forcedTypesArray[0])) {
264                List<String> ft = Arrays.asList(forcedTypesArray);
265                ft.remove(0);
266                newForcedTypesArray = ft.toArray(new String[ft.size()]);
267            }
268        }
269        if (newForcedTypesArray != null) {
270            getAdaptedDoc().setPropertyValue(TEMPLATE_FORCED_TYPES_PROP, newForcedTypesArray);
271        }
272
273    }
274
275    @Override
276    public List<String> getApplicableTypes() {
277        String[] applicableTypesArray = (String[]) getAdaptedDoc().getPropertyValue(TEMPLATE_APPLICABLE_TYPES_PROP);
278        List<String> applicableTypes = new ArrayList<>();
279        if (applicableTypesArray != null) {
280            applicableTypes.addAll((Arrays.asList(applicableTypesArray)));
281        }
282        if (applicableTypes.size() > 0 && applicableTypes.get(0).equals(TEMPLATE_APPLICABLE_TYPES_ALL)) {
283            applicableTypes.remove(0);
284        }
285        return applicableTypes;
286    }
287
288    @Override
289    public List<String> getForcedTypes() {
290        String[] forcedTypesArray = (String[]) getAdaptedDoc().getPropertyValue(TEMPLATE_FORCED_TYPES_PROP);
291        List<String> applicableTypes = new ArrayList<>();
292        if (forcedTypesArray != null) {
293            applicableTypes.addAll((Arrays.asList(forcedTypesArray)));
294        }
295        if (applicableTypes.size() > 0 && applicableTypes.get(0).equals(TEMPLATE_FORCED_TYPES_NONE)) {
296            applicableTypes.remove(0);
297        }
298        return applicableTypes;
299    }
300
301    @Override
302    public void removeForcedType(String type, boolean save) {
303        List<String> types = getForcedTypes();
304        if (types.contains(type)) {
305            types.remove(type);
306            String[] typesArray = types.toArray(new String[types.size()]);
307            getAdaptedDoc().setPropertyValue(TemplateSourceDocumentAdapterImpl.TEMPLATE_FORCED_TYPES_PROP, typesArray);
308            if (save) {
309                adaptedDoc = getAdaptedDoc().getCoreSession().saveDocument(getAdaptedDoc());
310            }
311        }
312    }
313
314    @Override
315    public void setForcedTypes(String[] forcedTypes, boolean save) {
316        getAdaptedDoc().setPropertyValue(TemplateSourceDocumentAdapterImpl.TEMPLATE_FORCED_TYPES_PROP, forcedTypes);
317        if (save) {
318            adaptedDoc = getAdaptedDoc().getCoreSession().saveDocument(getAdaptedDoc());
319        }
320    }
321
322    @Override
323    public List<TemplateBasedDocument> getTemplateBasedDocuments() {
324        return Framework.getLocalService(TemplateProcessorService.class).getLinkedTemplateBasedDocuments(adaptedDoc);
325    }
326
327    @Override
328    public String getOutputFormat() {
329        return (String) getAdaptedDoc().getPropertyValue(TEMPLATE_OUTPUT_PROP);
330    }
331
332    @Override
333    public void setOutputFormat(String mimetype, boolean save) {
334        getAdaptedDoc().setPropertyValue(TEMPLATE_OUTPUT_PROP, mimetype);
335        if (save) {
336            doSave();
337        }
338    }
339
340    @Override
341    public boolean useAsMainContent() {
342        Boolean useAsMain = (Boolean) getAdaptedDoc().getPropertyValue(TEMPLATE_USEASMAIN_PROP);
343        if (useAsMain == null) {
344            useAsMain = false;
345        }
346        return useAsMain;
347    }
348
349    @Override
350    public Blob getTemplateBlob() {
351        BlobHolder bh = getAdaptedDoc().getAdapter(BlobHolder.class);
352        if (bh != null) {
353            return bh.getBlob();
354        }
355        return null;
356    }
357
358    @Override
359    public void setTemplateBlob(Blob blob, boolean save) {
360        BlobHolder bh = getAdaptedDoc().getAdapter(BlobHolder.class);
361        if (bh != null) {
362            bh.setBlob(blob);
363            initTemplate(false);
364            if (save) {
365                doSave();
366            }
367        }
368    }
369
370    @Override
371    public String getName() {
372        String name = (String) getAdaptedDoc().getPropertyValue(TEMPLATE_NAME_PROP);
373        if (name == null) {
374            name = getAdaptedDoc().getTitle();
375        }
376        return name;
377    }
378
379    @Override
380    public String getFileName() {
381        Blob blob = getTemplateBlob();
382        if (blob != null) {
383            return blob.getFilename();
384        }
385        return null;
386    }
387
388    @Override
389    public String getTitle() {
390        return getAdaptedDoc().getTitle();
391    }
392
393    @Override
394    public String getVersionLabel() {
395        return getAdaptedDoc().getVersionLabel();
396    }
397
398    @Override
399    public String getId() {
400        return getAdaptedDoc().getId();
401    }
402
403    @Override
404    public String getLabel() {
405        StringBuilder sb = new StringBuilder(getTitle());
406        if (!getTitle().equals(getFileName())) {
407            sb.append(" (").append(getFileName()).append(")");
408        }
409        if (getVersionLabel() != null) {
410            sb.append(" [").append(getVersionLabel()).append("]");
411        }
412        return sb.toString();
413    }
414
415    @Override
416    public String getTargetRenditionName() {
417        String targetRendition = (String) getAdaptedDoc().getPropertyValue(TEMPLATE_RENDITION_PROP);
418        if (TEMPLATE_RENDITION_NONE.equals(targetRendition)) {
419            return null;
420        }
421        return targetRendition;
422    }
423
424    @Override
425    public void setTargetRenditioName(String renditionName, boolean save) {
426        getAdaptedDoc().setPropertyValue(TEMPLATE_RENDITION_PROP, renditionName);
427        if (save) {
428            doSave();
429        }
430    }
431
432}