001/*
002 * (C) Copyright 2011 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 *     Thierry Delprat
018 */
019package org.nuxeo.template.web;
020
021import java.util.ArrayList;
022import java.util.List;
023
024import javax.faces.model.SelectItem;
025
026import org.jboss.seam.ScopeType;
027import org.jboss.seam.annotations.Factory;
028import org.jboss.seam.annotations.In;
029import org.jboss.seam.annotations.Name;
030import org.jboss.seam.annotations.Observer;
031import org.jboss.seam.annotations.Scope;
032import org.jboss.seam.annotations.intercept.BypassInterceptors;
033import org.jboss.seam.faces.FacesMessages;
034import org.jboss.seam.international.StatusMessage;
035import org.nuxeo.ecm.core.api.Blob;
036import org.nuxeo.ecm.core.api.DocumentModel;
037import org.nuxeo.ecm.core.api.IdRef;
038import org.nuxeo.ecm.core.api.NuxeoException;
039import org.nuxeo.ecm.core.api.PropertyException;
040import org.nuxeo.ecm.core.api.blobholder.BlobHolder;
041import org.nuxeo.ecm.core.api.security.SecurityConstants;
042import org.nuxeo.ecm.platform.ui.web.api.WebActions;
043import org.nuxeo.ecm.platform.ui.web.util.ComponentUtils;
044import org.nuxeo.ecm.webapp.contentbrowser.DocumentActions;
045import org.nuxeo.ecm.webapp.helpers.EventManager;
046import org.nuxeo.ecm.webapp.helpers.EventNames;
047import org.nuxeo.ecm.webapp.helpers.ResourcesAccessor;
048import org.nuxeo.runtime.api.Framework;
049import org.nuxeo.template.api.TemplateInput;
050import org.nuxeo.template.api.TemplateProcessorService;
051import org.nuxeo.template.api.adapters.TemplateBasedDocument;
052import org.nuxeo.template.api.adapters.TemplateSourceDocument;
053
054@Name("templateBasedActions")
055@Scope(ScopeType.CONVERSATION)
056public class TemplateBasedActionBean extends BaseTemplateAction {
057
058    private static final long serialVersionUID = 1L;
059
060    @In(create = true)
061    protected transient DocumentActions documentActions;
062
063    @In(create = true)
064    protected transient WebActions webActions;
065
066    @In(create = true, required = false)
067    protected FacesMessages facesMessages;
068
069    @In(create = true)
070    protected ResourcesAccessor resourcesAccessor;
071
072    protected List<TemplateInput> templateInputs;
073
074    protected List<TemplateInput> templateEditableInputs;
075
076    protected TemplateInput newInput;
077
078    protected String templateIdToAssociate;
079
080    protected String editableTemplateName;
081
082    public String createTemplate() {
083        DocumentModel changeableDocument = navigationContext.getChangeableDocument();
084        TemplateSourceDocument sourceTemplate = changeableDocument.getAdapter(TemplateSourceDocument.class);
085        if (sourceTemplate != null && sourceTemplate.getTemplateBlob() != null) {
086            try {
087                sourceTemplate.initTemplate(false);
088                if (sourceTemplate.hasEditableParams()) {
089                    templateInputs = sourceTemplate.getParams();
090                    return "editTemplateRelatedData";
091                }
092            } catch (PropertyException e) {
093                log.error("Error during parameter automatic initialization", e);
094                facesMessages.add(StatusMessage.Severity.ERROR,
095                        resourcesAccessor.getMessages().get("label.template.err.parameterInit"));
096            }
097        }
098        return documentActions.saveDocument(changeableDocument);
099    }
100
101    public List<TemplateInput> getTemplateInputs() {
102        return templateInputs;
103    }
104
105    public void setTemplateInputs(List<TemplateInput> templateInputs) {
106        this.templateInputs = templateInputs;
107    }
108
109    public String saveDocument() {
110        DocumentModel changeableDocument = navigationContext.getChangeableDocument();
111
112        for (TemplateInput ti : templateInputs) {
113            log.info(ti.toString());
114        }
115        TemplateSourceDocument source = changeableDocument.getAdapter(TemplateSourceDocument.class);
116        if (source != null) {
117            source.saveParams(templateInputs, false);
118        }
119
120        return documentActions.saveDocument(changeableDocument);
121    }
122
123    @Observer(value = { EventNames.DOCUMENT_SELECTION_CHANGED, EventNames.NEW_DOCUMENT_CREATED,
124            EventNames.DOCUMENT_CHANGED }, create = false)
125    @BypassInterceptors
126    public void reset() {
127        templateInputs = null;
128        templateEditableInputs = null;
129        editableTemplateName = null;
130        templateIdToAssociate = null;
131    }
132
133    public List<TemplateInput> getTemplateEditableInputs() {
134        if (editableTemplateName == null) {
135            return new ArrayList<TemplateInput>();
136        }
137        if (templateEditableInputs == null) {
138            DocumentModel currentDocument = navigationContext.getCurrentDocument();
139
140            TemplateBasedDocument templateBasedDoc = currentDocument.getAdapter(TemplateBasedDocument.class);
141            templateEditableInputs = templateBasedDoc.getParams(editableTemplateName);
142        }
143        return templateEditableInputs;
144    }
145
146    public void setTemplateEditableInputs(List<TemplateInput> templateEditableInputs) {
147        this.templateEditableInputs = templateEditableInputs;
148    }
149
150    public String saveTemplateInputs() {
151
152        DocumentModel currentDocument = navigationContext.getCurrentDocument();
153
154        TemplateBasedDocument template = currentDocument.getAdapter(TemplateBasedDocument.class);
155        if (template != null) {
156            currentDocument = template.saveParams(editableTemplateName, templateEditableInputs, true);
157        }
158        reset();
159        return navigationContext.navigateToDocument(currentDocument);
160    }
161
162    public void cancelTemplateInputsEdit() {
163        reset();
164    }
165
166    public TemplateInput getNewInput() {
167        if (newInput == null) {
168            newInput = new TemplateInput("newField");
169        }
170        return newInput;
171    }
172
173    public void setNewInput(TemplateInput newInput) {
174        this.newInput = newInput;
175    }
176
177    public String addTemplateInput() {
178        DocumentModel currentDocument = navigationContext.getCurrentDocument();
179
180        TemplateSourceDocument template = currentDocument.getAdapter(TemplateSourceDocument.class);
181        if (template != null) {
182            template.addInput(newInput);
183            newInput = null;
184            templateEditableInputs = null;
185        } else {
186            return null;
187        }
188
189        return navigationContext.navigateToDocument(currentDocument);
190    }
191
192    public String render(String templateName) {
193        DocumentModel currentDocument = navigationContext.getCurrentDocument();
194        TemplateBasedDocument doc = currentDocument.getAdapter(TemplateBasedDocument.class);
195        if (doc == null) {
196            return null;
197        }
198        try {
199            Blob rendition = doc.renderWithTemplate(templateName);
200            String filename = rendition.getFilename();
201            ComponentUtils.download(currentDocument, null, rendition, filename, "templateRendition");
202            return null;
203        } catch (NuxeoException e) {
204            log.error("Unable to render template ", e);
205            facesMessages.add(StatusMessage.Severity.ERROR,
206                    resourcesAccessor.getMessages().get("label.template.err.renderingFailed"));
207            return null;
208        }
209    }
210
211    public String renderAndStore(String templateName) {
212
213        DocumentModel currentDocument = navigationContext.getCurrentDocument();
214        TemplateBasedDocument doc = currentDocument.getAdapter(TemplateBasedDocument.class);
215        if (doc == null) {
216            return null;
217        }
218        doc.renderAndStoreAsAttachment(templateName, true);
219        documentManager.save();
220        return navigationContext.navigateToDocument(doc.getAdaptedDoc());
221    }
222
223    public boolean canResetParameters() {
224        DocumentModel currentDocument = navigationContext.getCurrentDocument();
225        if (!documentManager.hasPermission(currentDocument.getRef(), SecurityConstants.WRITE)) {
226            return false;
227        }
228        TemplateBasedDocument templateBased = currentDocument.getAdapter(TemplateBasedDocument.class);
229        if (templateBased != null) {
230            return true;
231        }
232        return false;
233    }
234
235    public void resetParameters(String templateName) {
236        DocumentModel currentDocument = navigationContext.getCurrentDocument();
237        TemplateBasedDocument templateBased = currentDocument.getAdapter(TemplateBasedDocument.class);
238        if (templateBased != null) {
239            templateBased.initializeFromTemplate(templateName, true);
240            templateEditableInputs = null;
241        }
242    }
243
244    public boolean canDetachTemplate(String templateName) {
245        DocumentModel currentDocument = navigationContext.getCurrentDocument();
246        if (!documentManager.hasPermission(currentDocument.getRef(), SecurityConstants.WRITE)) {
247            return false;
248        }
249        TemplateBasedDocument templateBased = currentDocument.getAdapter(TemplateBasedDocument.class);
250        if (templateBased != null) {
251            return true;
252        }
253        return false;
254    }
255
256    public String detachTemplate(String templateName) {
257        DocumentModel currentDocument = navigationContext.getCurrentDocument();
258        TemplateProcessorService tps = Framework.getLocalService(TemplateProcessorService.class);
259        DocumentModel detachedDocument = tps.detachTemplateBasedDocument(currentDocument, templateName, true);
260        webActions.resetTabList();
261        // because of cacheKey issue
262        navigationContext.setCurrentDocument(null);
263        return navigationContext.navigateToDocument(detachedDocument);
264    }
265
266    public String getTemplateIdToAssociate() {
267        return templateIdToAssociate;
268    }
269
270    public void setTemplateIdToAssociate(String templateIdToAssociate) {
271        this.templateIdToAssociate = templateIdToAssociate;
272    }
273
274    public void associateDocumentToTemplate() {
275        if (templateIdToAssociate == null) {
276            // return null;
277            return;
278        }
279        DocumentModel currentDocument = navigationContext.getCurrentDocument();
280        DocumentModel sourceTemplate = documentManager.getDocument(new IdRef(templateIdToAssociate));
281        TemplateProcessorService tps = Framework.getLocalService(TemplateProcessorService.class);
282        try {
283            currentDocument = tps.makeTemplateBasedDocument(currentDocument, sourceTemplate, true);
284        } catch (NuxeoException e) {
285            log.error("Unable to do template association", e);
286            facesMessages.add(StatusMessage.Severity.ERROR,
287                    resourcesAccessor.getMessages().get("label.template.err.associationFailed"),
288                    sourceTemplate.getName());
289        }
290        navigationContext.invalidateCurrentDocument();
291        EventManager.raiseEventsOnDocumentChange(currentDocument);
292        templateIdToAssociate = null;
293    }
294
295    public boolean canRenderAndStore() {
296        DocumentModel currentDocument = navigationContext.getCurrentDocument();
297        // check that templating is supported
298        TemplateBasedDocument template = currentDocument.getAdapter(TemplateBasedDocument.class);
299        if (template == null) {
300            return false;
301        }
302        // check that we can store the result
303        BlobHolder bh = currentDocument.getAdapter(BlobHolder.class);
304        if (bh == null) {
305            return false;
306        }
307        return true;
308    }
309
310    public String getEditableTemplateName() {
311        return editableTemplateName;
312    }
313
314    public void setEditableTemplateName(String editableTemplateName) {
315        if (editableTemplateName == null || !editableTemplateName.equals(this.editableTemplateName)) {
316            this.editableTemplateName = editableTemplateName;
317            templateEditableInputs = null;
318        }
319    }
320
321    public List<TemplateSourceDocument> getBindableTemplatesForDocument() {
322        DocumentModel currentDocument = navigationContext.getCurrentDocument();
323        String targetType = currentDocument.getType();
324        TemplateProcessorService tps = Framework.getLocalService(TemplateProcessorService.class);
325        List<DocumentModel> templates = tps.getAvailableTemplateDocs(documentManager, targetType);
326
327        List<TemplateSourceDocument> result = new ArrayList<TemplateSourceDocument>();
328        TemplateBasedDocument currentTBD = currentDocument.getAdapter(TemplateBasedDocument.class);
329        List<String> alreadyBoundTemplateNames = new ArrayList<String>();
330        if (currentTBD != null) {
331            alreadyBoundTemplateNames = currentTBD.getTemplateNames();
332        }
333        for (DocumentModel doc : templates) {
334            TemplateSourceDocument source = doc.getAdapter(TemplateSourceDocument.class);
335            if (!alreadyBoundTemplateNames.contains(source.getName())) {
336                result.add(source);
337            }
338        }
339        return result;
340
341    }
342
343    public List<SelectItem> getBindableTemplatesForDocumentAsSelectItems() {
344
345        List<SelectItem> items = new ArrayList<SelectItem>();
346        List<TemplateSourceDocument> sources = getBindableTemplatesForDocument();
347        for (TemplateSourceDocument sd : sources) {
348            DocumentModel doc = sd.getAdaptedDoc();
349            String label = doc.getTitle();
350            if (doc.isVersion()) {
351                label = label + " (V " + doc.getVersionLabel() + ")";
352            }
353            items.add(new SelectItem(doc.getId(), label));
354        }
355
356        return items;
357    }
358
359    public boolean canBindNewTemplate() {
360        DocumentModel currentDocument = navigationContext.getCurrentDocument();
361        if (!currentDocument.getCoreSession().hasPermission(currentDocument.getRef(), SecurityConstants.WRITE)) {
362            return false;
363        }
364        if (getBindableTemplatesForDocument().size() == 0) {
365            return false;
366        }
367        return true;
368    }
369
370    @Factory(value = "currentTemplateBasedDocument", scope = ScopeType.EVENT)
371    public TemplateBasedDocument getCurrentDocumentAsTemplateBasedDocument() {
372        return navigationContext.getCurrentDocument().getAdapter(TemplateBasedDocument.class);
373    }
374
375    @Factory(value = "associatedRenderableTemplates", scope = ScopeType.EVENT)
376    public List<TemplateSourceDocument> getRenderableTemplates() {
377        List<TemplateSourceDocument> result = new ArrayList<TemplateSourceDocument>();
378        TemplateBasedDocument template = getCurrentDocumentAsTemplateBasedDocument();
379        if (template != null) {
380            List<TemplateSourceDocument> sources = template.getSourceTemplates();
381            for (TemplateSourceDocument source : sources) {
382                if (source.getTargetRenditionName() == null || source.getTargetRenditionName().isEmpty()) {
383                    result.add(source);
384                }
385            }
386        }
387        return result;
388    }
389}