001/*
002 *  Copyright 2024 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.pagesubscription.schedulable.page;
017
018import java.io.IOException;
019import java.util.HashMap;
020import java.util.HashSet;
021import java.util.List;
022import java.util.Map;
023import java.util.Objects;
024import java.util.Optional;
025import java.util.Set;
026
027import org.apache.avalon.framework.configuration.Configuration;
028import org.apache.avalon.framework.configuration.ConfigurationException;
029import org.apache.avalon.framework.service.ServiceException;
030import org.apache.avalon.framework.service.ServiceManager;
031import org.apache.cocoon.components.ContextHelper;
032import org.apache.cocoon.environment.Request;
033import org.quartz.JobExecutionContext;
034
035import org.ametys.cms.transformation.xslt.ResolveURIComponent;
036import org.ametys.core.schedule.progression.ContainerProgressionTracker;
037import org.ametys.core.user.User;
038import org.ametys.core.user.UserIdentity;
039import org.ametys.core.util.I18nUtils;
040import org.ametys.core.util.mail.SendMailHelper;
041import org.ametys.plugins.core.impl.schedule.AbstractStaticSchedulable;
042import org.ametys.plugins.pagesubscription.BroadcastChannelHelper.BroadcastChannel;
043import org.ametys.plugins.pagesubscription.FrequencyHelper.Frequency;
044import org.ametys.plugins.pagesubscription.Subscription;
045import org.ametys.plugins.pagesubscription.notification.PageNotificationsHelper;
046import org.ametys.plugins.pagesubscription.type.PageSubscriptionType;
047import org.ametys.plugins.pagesubscription.type.SubscriptionTypeExtensionPoint;
048import org.ametys.plugins.repository.AmetysObjectResolver;
049import org.ametys.plugins.repository.activities.Activity;
050import org.ametys.runtime.i18n.I18nizableText;
051import org.ametys.runtime.i18n.I18nizableTextParameter;
052import org.ametys.web.WebConstants;
053import org.ametys.web.activities.AbstractPageActivityType;
054import org.ametys.web.mail.ReportActivitiesMailBodyHelper;
055import org.ametys.web.mail.ReportActivitiesMailBodyHelper.MailBodyBuilder;
056import org.ametys.web.renderingcontext.RenderingContext;
057import org.ametys.web.renderingcontext.RenderingContextHandler;
058import org.ametys.web.repository.page.Page;
059import org.ametys.web.repository.page.PageDAO;
060import org.ametys.web.repository.site.Site;
061import org.ametys.web.repository.site.SiteManager;
062
063/**
064 * Schedulable to send not forced page notification summary by mail
065 * The frequency is configurable. (DAILY, WEEKLY, MONTHLY)
066 */
067public class SendPageNotificationSummarySchedulable extends AbstractStaticSchedulable
068{
069    /** Tag for page that contains followed page configuration service */
070    public static final String FOLLOWED_PAGES_CONFIG_PAGE_TAG = "TAG_FOLLOWED_PAGES_CONFIG";
071    
072    /** The frequency */
073    protected Frequency _frequency;
074    
075    /** The mail subject */
076    protected I18nizableText _mailSubject;
077    
078    /** The mail title */
079    protected I18nizableText _mailTitle;
080    
081    /** The mail message */
082    protected I18nizableText _mailMessage;
083    
084    /** The subscription type EP */
085    protected SubscriptionTypeExtensionPoint _subscriptionTypeEP;
086    
087    /** The page subscription type */
088    protected PageSubscriptionType _pageSubscriptionType;
089    
090    /** The site manager */
091    protected SiteManager _siteManager;
092    
093    /** The i18n utils */
094    protected I18nUtils _i18nUtils;
095    
096    /** The page notification helper */
097    protected PageNotificationsHelper _pageNotificationHelper;
098    
099    /** The ametys object resolver */
100    protected AmetysObjectResolver _resolver;
101    
102    /** The page DAO */
103    protected PageDAO _pageDAO;
104    
105    /** The rendering context handler */
106    protected RenderingContextHandler _renderingContextHandler;
107    
108    @Override
109    public void service(ServiceManager manager) throws ServiceException
110    {
111        super.service(manager);
112        _i18nUtils = (I18nUtils) manager.lookup(I18nUtils.ROLE);
113        _subscriptionTypeEP = (SubscriptionTypeExtensionPoint) manager.lookup(SubscriptionTypeExtensionPoint.ROLE);
114        _pageSubscriptionType = (PageSubscriptionType) _subscriptionTypeEP.getExtension(PageSubscriptionType.ID);
115        _siteManager = (SiteManager) manager.lookup(SiteManager.ROLE);
116        _pageNotificationHelper = (PageNotificationsHelper) manager.lookup(PageNotificationsHelper.ROLE);
117        _resolver = (AmetysObjectResolver) manager.lookup(AmetysObjectResolver.ROLE);
118        _pageDAO = (PageDAO) manager.lookup(PageDAO.ROLE);
119        _renderingContextHandler = (RenderingContextHandler) manager.lookup(RenderingContextHandler.ROLE);
120    }
121    
122    @Override
123    public void configure(Configuration configuration) throws ConfigurationException
124    {
125        super.configure(configuration);
126        
127        _frequency = Frequency.valueOf(configuration.getChild("frequency").getValue("DAILY"));
128        _mailSubject = I18nizableText.parseI18nizableText(configuration.getChild("mail-subject"), "plugin." + _pluginName);
129        _mailTitle = I18nizableText.parseI18nizableText(configuration.getChild("mail-title"), "plugin." + _pluginName);
130        _mailMessage = I18nizableText.parseI18nizableText(configuration.getChild("mail-message"), "plugin." + _pluginName);
131    }
132    
133    public void execute(JobExecutionContext context, ContainerProgressionTracker progressionTracker) throws Exception
134    {
135        // For each site
136        for (Site site : _siteManager.getSites())
137        {
138            Request request = ContextHelper.getRequest(_context);
139            
140            // Retrieve the current context.
141            RenderingContext currentContext = _renderingContextHandler.getRenderingContext();
142            try
143            {
144                // Force rendering context.FRONT to resolve URI
145                _renderingContextHandler.setRenderingContext(RenderingContext.FRONT);
146                request.setAttribute(WebConstants.REQUEST_ATTR_SITE_NAME, site.getName());
147                
148                Map<UserIdentity, Set<Activity>> pageActivitiesBySubscriber = _getPageActivitiesBySubscriber(site);
149                for (UserIdentity subscriber : pageActivitiesBySubscriber.keySet())
150                {
151                    Set<Activity> pageActivities = pageActivitiesBySubscriber.get(subscriber);
152                    if (!pageActivities.isEmpty())
153                    {
154                        String lang = _getLanguage(pageActivities);
155                        
156                        String subject = _i18nUtils.translate(_getI18nSubject(site), lang);
157                        String body = _getMailBody(site, pageActivities, lang);
158                        
159                        User user = _userManager.getUser(subscriber);
160                        if (user != null)
161                        {
162                            SendMailHelper.newMail()
163                            .withRecipient(user.getEmail())
164                            .withSubject(subject)
165                            .withHTMLBody(body)
166                            .withInlineCSS(false)
167                            .withAsync(true)
168                            .sendMail();
169                        }
170                    }
171                }
172            }
173            finally 
174            {
175                // Restore current context
176                _renderingContextHandler.setRenderingContext(currentContext);
177            }
178        }
179    }
180    
181    private Map<UserIdentity, Set<Activity>> _getPageActivitiesBySubscriber(Site site)
182    {
183        Map<UserIdentity, Set<Activity>> pageActivitiesBySubscriber = new HashMap<>();
184        for (Subscription subscription : _pageSubscriptionType.getSubscriptions(site, _frequency, BroadcastChannel.MAIL, null))
185        {
186            UserIdentity subscriber = subscription.getSubscriber().get(); // For page subscription, there is only user subscription (not group) ... so subscriber always exists
187            Set<Activity> pages = pageActivitiesBySubscriber.getOrDefault(subscriber, new HashSet<>());
188            
189            String pageId = _pageSubscriptionType.getTarget(subscription);
190            Activity lastActivity = _pageNotificationHelper.getLastActivity(site.getName(), pageId, subscription.getFrequency());
191            if (lastActivity != null)
192            {
193                // Add page only if it has a recent activity for the current frequency
194                pages.add(lastActivity);
195            }
196            pageActivitiesBySubscriber.put(subscriber, pages);
197        }
198        return pageActivitiesBySubscriber;
199    }
200    
201    private String _getLanguage(Set<Activity> pageActivities)
202    {
203        // For the language, take the language of the first page
204        return pageActivities.stream()
205            .map(a -> a.<String>getValue(AbstractPageActivityType.PAGE_ID))
206            .map(id -> (Page) _resolver.resolveById(id))
207            .filter(Objects::nonNull)
208            .map(Page::getSitemapName)
209            .findFirst()
210            .get();
211    }
212    
213    /**
214     * Get the subject of the mail
215     * @param site 
216     * @return the subject of the mail
217     */
218    protected I18nizableText _getI18nSubject(Site site)
219    {
220        Map<String, I18nizableTextParameter> params = Map.of(
221                "site", new I18nizableText(site.getTitle())
222            );
223        return new I18nizableText(_mailSubject.getCatalogue(), _mailSubject.getKey(), params);
224    }
225    
226    /**
227     * Get the mail body of the mail
228     * @param site the site
229     * @param pages the set of pages with it last activity
230     * @param lang the language
231     * @return the mail body of the mail
232     * @throws IOException if an error occurred
233     */
234    protected String _getMailBody(Site site, Set<Activity> pages, String lang) throws IOException
235    {
236        List<org.ametys.web.mail.ReportActivitiesMailBodyHelper.MailBodyBuilder.Activity> activities = pages.stream()
237            .map(a -> this._wrapActivity(a, lang))
238            .toList();
239        
240        MailBodyBuilder bodyBuilder =  ReportActivitiesMailBodyHelper.newHTMLBody()
241            .withLanguage(lang)
242            .withTitle(_mailTitle)
243            .withMessage(_mailMessage)
244            .withActivities(activities)
245            .withLink(site.getUrl(), new I18nizableText("plugin.page-subscription", "PLUGINS_PAGE_SUBSCRIPTION_MAIL_NOTIFICATIONS_MAIL_SITE_LINK"));
246        
247        Optional<String> pageTagUrl = _pageDAO.findPagedIdsByTag(site.getName(), lang, FOLLOWED_PAGES_CONFIG_PAGE_TAG)
248                .stream()
249                .map(id -> ResolveURIComponent.resolve("page", id, false, true))
250                .findFirst();
251        if (pageTagUrl.isPresent())
252        {
253            bodyBuilder.withFooterLink(pageTagUrl.get(), new I18nizableText("plugin.page-subscription", "PLUGINS_PAGE_SUBSCRIPTION_MAIL_PAGE_NOTIFICATIONS_MAIL_PREFERENCE_LINK"), "core-ui", "img/mail/icon-letter.png");
254        }
255        
256        return bodyBuilder.build();
257    }
258    
259    private org.ametys.web.mail.ReportActivitiesMailBodyHelper.MailBodyBuilder.Activity _wrapActivity(Activity activity, String lang)
260    {
261        User user = _userManager.getUser(activity.getAuthor());
262        
263        Map<String, I18nizableTextParameter> parameters = new HashMap<>();
264        parameters.put("user", new I18nizableText(user.getFullName()));
265        
266        parameters.put("pageTitle", new I18nizableText(activity.getValue(AbstractPageActivityType.PAGE_TITLE)));
267        
268        String pageId = activity.getValue(AbstractPageActivityType.PAGE_ID);
269        String pageUrl = ResolveURIComponent.resolve("page", pageId, false, true);
270        parameters.put("pageUrl", new I18nizableText(pageUrl));
271        
272        I18nizableText messageI18n = new I18nizableText("plugin.page-subscription", "PLUGINS_PAGE_SUBSCRIPTION_MAIL_PAGE_NOTIFICATIONS_MAIL_CONTENT_MESSAGE", parameters);
273        String message = _i18nUtils.translate(messageI18n, lang);
274        return new org.ametys.web.mail.ReportActivitiesMailBodyHelper.MailBodyBuilder.Activity(user, activity.getDate(), message);
275    }
276    
277}