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