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("&", "&").replaceAll("<", "<").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}