001/*
002 *  Copyright 2023 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.web.mail;
017
018import java.io.IOException;
019import java.io.InputStreamReader;
020import java.io.Reader;
021import java.time.ZonedDateTime;
022import java.util.ArrayList;
023import java.util.HashMap;
024import java.util.List;
025import java.util.Map;
026
027import org.apache.avalon.framework.activity.Initializable;
028import org.apache.avalon.framework.context.Context;
029import org.apache.avalon.framework.context.Contextualizable;
030import org.apache.avalon.framework.service.ServiceException;
031import org.apache.avalon.framework.service.ServiceManager;
032import org.apache.avalon.framework.service.Serviceable;
033import org.apache.cocoon.components.ContextHelper;
034import org.apache.cocoon.environment.Request;
035import org.apache.commons.io.IOUtils;
036import org.apache.commons.lang.StringUtils;
037import org.apache.excalibur.source.Source;
038import org.apache.excalibur.source.SourceResolver;
039
040import org.ametys.core.cocoon.ActionResultGenerator;
041import org.ametys.core.user.User;
042import org.ametys.plugins.core.user.UserHelper;
043import org.ametys.runtime.i18n.I18nizableText;
044import org.ametys.web.WebHelper;
045
046/**
047 * Component to build wrapped HTML mail body for report activities
048 */
049public class ReportActivitiesMailBodyHelper implements Contextualizable, Serviceable, Initializable
050{
051    /** The avalon context */
052    protected static Context _context;
053    
054    private static SourceResolver _srcResolver;
055    private static UserHelper _userHelper;
056
057    private static ReportActivitiesMailBodyHelper __instance;
058
059    @Override
060    public void contextualize(Context context)
061    {
062        _context = context;
063    }
064    
065    public void initialize() throws Exception
066    {
067        __instance = this;
068    }
069    
070    @Override
071    public void service(ServiceManager manager) throws ServiceException
072    {
073        _srcResolver = (SourceResolver) manager.lookup(SourceResolver.ROLE);
074        _userHelper = (UserHelper) manager.lookup(UserHelper.ROLE);
075    }
076
077    /**
078     * Get the URI to resolve wrapped HTML email body
079     * @return the uri
080     */
081    public String getMailBodyUri()
082    {
083        Request request = ContextHelper.getRequest(_context);
084        
085        String siteName = WebHelper.getSiteName(request);
086        
087        return "cocoon://_plugins/web/" + siteName + "/wrapped-mail/activities/body.html";
088    }
089    
090    /**
091     * Creates a new mail body builder.
092     * @return the newly created {@link MailBodyBuilder}.
093     */
094    public static MailBodyBuilder newHTMLBody()
095    {
096        return new MailBodyBuilder(__instance.getMailBodyUri());
097    }
098    
099    /**
100     * Implements the builder pattern for creating and sending emails.
101     */
102    public static class MailBodyBuilder
103    {
104        private Object _title;
105        private List<Object> _messages;
106        private List<Activity> _activities;
107        private I18nizableText _linkText;
108        private String _linkUrl;
109        private I18nizableText _footerLinkText;
110        private String _footerLinkUrl;
111        private String _footerImgPluginName;
112        private String _footerImgPath;
113        private String _lang;
114        private String _uri;
115        
116        MailBodyBuilder(String uri)
117        {
118            _uri = uri;
119        }
120        
121        /**
122         * Set the language
123         * @param lang the language
124         * @return this builder
125         */
126        public MailBodyBuilder withLanguage(String lang)
127        {
128            _lang = lang;
129            return this;
130        }
131        /**
132         * Set the title
133         * @param title the title as String or {@link I18nizableText}
134         * @return this builder
135         */
136        public MailBodyBuilder withTitle(Object title)
137        {
138            _title = title;
139            return this;
140        }
141        
142        /**
143         * Set the main message
144         * @param message the message as String or {@link I18nizableText}
145         * @return this builder
146         */
147        public MailBodyBuilder withMessage(Object message)
148        {
149            _messages = List.of(message);
150            return this;
151        }
152        
153        /**
154         * Add a new message
155         * @param message the message as String or {@link I18nizableText}
156         * @return this builder
157         */
158        public MailBodyBuilder addMessage(Object message)
159        {
160            if (_messages == null)
161            {
162                _messages = new ArrayList<>();
163            }
164            
165            _messages.add(message);
166            return this;
167        }
168        
169        /**
170         * Set the activities
171         * @param activities the activities
172         * @return this builder
173         */
174        public MailBodyBuilder withActivities(List<Activity> activities)
175        {
176            _activities = activities;
177            return this;
178        }
179        
180        /**
181         * Add an activity
182         * @param activity the activity
183         * @return this builder
184         */
185        public MailBodyBuilder addActivity(Activity activity)
186        {
187            if (_activities == null)
188            {
189                _activities = new ArrayList<>();
190            }
191            
192            _activities.add(activity);
193            return this;
194        }
195        
196        /**
197         * Add a main link to be displayed as a button
198         * @param linkUrl the link url
199         * @param linkText the link text
200         * @return this builder
201         */
202        public MailBodyBuilder withLink(String linkUrl, I18nizableText linkText)
203        {
204            _linkText = linkText;
205            _linkUrl = linkUrl;
206            return this;
207        }
208        
209        /**
210         * Add a link to be displayed on footer
211         * @param linkUrl the link url
212         * @param linkText the link text
213         * @return this builder
214         */
215        public MailBodyBuilder withFooterLink(String linkUrl, I18nizableText linkText)
216        {
217            return withFooterLink(linkUrl, linkText, null, null);
218        }
219        
220        /**
221         * Add a link to be displayed on footer
222         * @param linkUrl the link url
223         * @param linkText the link text
224         * @param imgPluginName the plugin name that constains the image
225         * @param imgPath the path of the image in the plugin
226         * @return this builder
227         */
228        public MailBodyBuilder withFooterLink(String linkUrl, I18nizableText linkText, String imgPluginName, String imgPath)
229        {
230            _footerLinkText = linkText;
231            _footerLinkUrl = linkUrl;
232            _footerImgPluginName = imgPluginName;
233            _footerImgPath = imgPath;
234            return this;
235        }
236        
237        /**
238         * Build the body of the email as HTML
239         * @return the HTML body
240         * @throws IOException if an error occurred when building the email
241         */
242        public String build() throws IOException
243        {
244            Source src = null;
245            Request request = ContextHelper.getRequest(_context);
246            
247            try
248            {
249                request.setAttribute(ActionResultGenerator.MAP_REQUEST_ATTR, toJson());
250                
251                Map<String, Object> parameters = new HashMap<>();
252                parameters.put("lang", _lang);
253                
254                src = _srcResolver.resolveURI(_uri, null, parameters);
255                Reader reader = new InputStreamReader(src.getInputStream(), "UTF-8");
256                return IOUtils.toString(reader);
257            }
258            finally
259            {
260                _srcResolver.release(src);
261            }
262        }
263        
264        /**
265         * Get the JSON representation of the email ingredients
266         * @return the JSON ingredients
267         */
268        public Map<String, Object> toJson()
269        {
270            Map<String, Object> json = new HashMap<>();
271            
272            json.put("title", _title);
273            
274            if (_messages != null && !_messages.isEmpty())
275            {
276                json.put("message", _messages);
277            }
278            
279            if (StringUtils.isNotEmpty(_linkUrl))
280            {
281                json.put("link", Map.of("url", _linkUrl, "text", _linkText));
282            }
283            
284            if (_activities != null && !_activities.isEmpty())
285            {
286                json.put("activity", _activities.stream()
287                        .map(this::_activity2json)
288                        .toList());
289            }
290            
291            json.put("footer", _footer2json());
292            
293            return json;
294        }
295        
296        private Map<String, Object> _footer2json()
297        {
298            Map<String, Object> footer2json = new HashMap<>();
299            
300            if (_footerLinkUrl != null && _footerLinkText != null)
301            {
302                Map<String, Object> link2json = new HashMap<>();
303                
304                link2json.put("text", _footerLinkText);
305                link2json.put("url", _footerLinkUrl);
306                if (_footerImgPath != null && _footerImgPluginName != null)
307                {
308                    link2json.put("imgPluginName", _footerImgPluginName);
309                    link2json.put("imgPath", _footerImgPath);
310                }
311                footer2json.put("link", link2json);
312            }
313            
314            return footer2json;
315        }
316        
317        private Map<String, Object> _activity2json(Activity activity)
318        {
319            Map<String, Object> activity2json = new HashMap<>();
320            activity2json.put("author", _userHelper.user2json(activity.author()));
321            activity2json.put("date", activity.date());
322            activity2json.put("description", activity.description());
323            return activity2json;
324        }
325        
326        /**
327         * Record for an activity
328         * @param author The author.
329         * @param date the date of the action
330         * @param description the description if the activity as String or {@link I18nizableText}
331         */
332        public record Activity(User author, ZonedDateTime date, Object description) { /* empty */ }
333    }
334    
335}