001/*
002 *  Copyright 2020 Anyware Services
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 */
016package org.ametys.plugins.workspaces.workflow;
017
018import java.io.ByteArrayInputStream;
019import java.io.IOException;
020import java.nio.charset.StandardCharsets;
021import java.time.ZonedDateTime;
022import java.util.List;
023import java.util.Map;
024
025import org.apache.avalon.framework.service.ServiceException;
026import org.apache.avalon.framework.service.ServiceManager;
027import org.apache.commons.collections.ListUtils;
028import org.apache.commons.lang3.StringUtils;
029
030import org.ametys.cms.data.RichText;
031import org.ametys.cms.data.type.AbstractRichTextElementType;
032import org.ametys.cms.repository.ModifiableContent;
033import org.ametys.cms.repository.WorkflowAwareContent;
034import org.ametys.cms.workflow.AbstractContentWorkflowComponent;
035import org.ametys.core.util.I18nUtils;
036import org.ametys.plugins.repository.version.VersionableAmetysObject;
037import org.ametys.plugins.workflow.EnhancedFunction;
038import org.ametys.runtime.i18n.I18nizableText;
039import org.ametys.runtime.model.type.ModelItemType;
040
041import com.opensymphony.module.propertyset.PropertySet;
042import com.opensymphony.workflow.WorkflowException;
043
044/**
045 * This workflow function initialize the rich-text's content with a configured default value
046 *
047 */
048public class InitContentFunction extends AbstractContentWorkflowComponent implements EnhancedFunction
049{
050    private static final String __RICHTEXT_ATTRIBUTE_KEY = "attribute";
051    private static final String __RICHTEXT_VALUE_KEY = "value";
052    private static final String __RICHTEXT_IS_I18N_KEY = "isI18n";
053    private static final String __RICHTEXT_I18N_CATALOG_KEY = "i18nCatalogue";
054    private static final String __RICHTEXT_ACTIVATE_COMMENTS_KEY = "activateComments";
055    
056    /** I18n Utils */
057    protected I18nUtils _i18nUtils;
058    
059    @Override
060    public void service(ServiceManager manager) throws ServiceException
061    {
062        super.service(manager);
063        _i18nUtils = (I18nUtils) manager.lookup(org.ametys.core.util.I18nUtils.ROLE);
064    }
065    
066    public void execute(Map transientVars, Map args, PropertySet ps) throws WorkflowException
067    {
068        _logger.info("Performing content initialisation");
069        
070        try
071        {
072            WorkflowAwareContent content = getContent(transientVars);
073            
074            String attributePath = (String) args.get(__RICHTEXT_ATTRIBUTE_KEY);
075            if (StringUtils.isEmpty(attributePath))
076            {
077                attributePath = "content";
078            }
079            
080            boolean isI18N = "true".equals(args.get(__RICHTEXT_IS_I18N_KEY));
081            String i18nCatalogue = (String) args.get(__RICHTEXT_I18N_CATALOG_KEY);
082            if (StringUtils.isEmpty(i18nCatalogue))
083            {
084                // Use application as default catalogue
085                i18nCatalogue = "application";
086            }
087            
088            String value = (String) args.get(__RICHTEXT_VALUE_KEY);
089            if (StringUtils.isBlank(value))
090            {
091                _logger.warn(String.format("Missing 'value' argument for workflow function %s, the content '%s' will not be initialized with default value", InitContentFunction.class.getName(), content.getId()));
092                return;
093            }
094            
095            if (!(content instanceof ModifiableContent))
096            {
097                _logger.warn(String.format("Content '%s' is not a modifiable content. It will not be initialized with default value", content.getId()));
098                return;
099            }
100            
101            String defaultValue = "";
102            if (isI18N)
103            {
104                I18nizableText text = new I18nizableText(i18nCatalogue, value);
105                defaultValue = _i18nUtils.translate(text, content.getLanguage());
106            }
107            else
108            {
109                defaultValue = value;
110            }
111            
112            if (content.hasDefinition(attributePath))
113            {
114                ModelItemType type = content.getDefinition(attributePath).getType();
115                if (type instanceof AbstractRichTextElementType)
116                {
117                    _setRichText((ModifiableContent) content, attributePath, defaultValue);
118                }
119                else
120                {
121                    _setText((ModifiableContent) content, attributePath, defaultValue);
122                }
123            }
124            
125            boolean activeComments = "true".equals(args.get(__RICHTEXT_ACTIVATE_COMMENTS_KEY));
126            if (activeComments && content.hasDefinition("comment"))
127            {
128                ((ModifiableContent) content).setValue("comment", true);
129            }
130            
131            // save changes
132            content.saveChanges();
133            
134            if (content instanceof VersionableAmetysObject)
135            {
136                // create version
137                ((VersionableAmetysObject) content).checkpoint();
138            }
139        }
140        catch (IOException e)
141        {
142            throw new WorkflowException("Unable to set the rich text in the content", e);
143        }
144    }
145    
146    private void _setText(ModifiableContent content, String attributePath, String value)
147    {
148        content.setValue(attributePath, value);
149    }
150    
151    private void _setRichText(ModifiableContent content, String attributePath, String value) throws IOException
152    {
153        RichText richText = new RichText();
154        
155        String docbook = _textToDocbook(value);
156        ByteArrayInputStream is = new ByteArrayInputStream(docbook.getBytes(StandardCharsets.UTF_8));
157        
158        richText.setInputStream(is);
159        richText.setEncoding("UTF-8");
160        richText.setMimeType("text/xml");
161        richText.setLastModificationDate(ZonedDateTime.now());
162        
163        content.setValue(attributePath, richText);
164    }
165    
166    private String _textToDocbook (String value)
167    {
168        StringBuilder sb = new StringBuilder();
169        
170        sb.append("<?xml version=\"1.0\" encoding=\"UTF-8\"?>");
171        sb.append("<article version=\"5.0\" xmlns=\"http://docbook.org/ns/docbook\">");
172        
173        sb.append("<para>");
174        sb.append(value.replaceAll("&", "&amp;").replaceAll("<", "&lt;").replaceAll("\r?\n", "<phrase role=\"linebreak\"/>"));
175        sb.append("</para>");
176        
177        sb.append("</article>");
178        
179        return sb.toString();
180    }
181
182    @Override
183    public List<FunctionArgument> getArguments()
184    {
185        return ListUtils.EMPTY_LIST;
186    }
187
188    @Override
189    public I18nizableText getDescription(Map<String, String> args)
190    {
191        return new I18nizableText("plugin.workspaces", "PLUGINS_WORKSPACES_INIT_CONTENT_FUNCTION_DESCRIPTION");
192    }
193}