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}