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.Optional; 024import java.util.Set; 025 026import org.apache.avalon.framework.configuration.Configuration; 027import org.apache.avalon.framework.configuration.ConfigurationException; 028import org.apache.avalon.framework.service.ServiceException; 029import org.apache.avalon.framework.service.ServiceManager; 030import org.apache.cocoon.components.ContextHelper; 031import org.apache.cocoon.environment.Request; 032import org.apache.commons.lang3.StringUtils; 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.PageDAO; 059import org.ametys.web.repository.site.Site; 060import org.ametys.web.repository.site.SiteManager; 061 062/** 063 * Schedulable to send not forced page notification summary by mail 064 * The frequency is configurable. (DAILY, WEEKLY, MONTHLY) 065 */ 066public class SendPageNotificationSummarySchedulable extends AbstractStaticSchedulable 067{ 068 /** Tag for page that contains followed page configuration service */ 069 public static final String FOLLOWED_PAGES_CONFIG_PAGE_TAG = "TAG_FOLLOWED_PAGES_CONFIG"; 070 071 /** The frequency */ 072 protected Frequency _frequency; 073 074 /** The mail subject */ 075 protected I18nizableText _mailSubject; 076 077 /** The mail title */ 078 protected I18nizableText _mailTitle; 079 080 /** The mail message */ 081 protected I18nizableText _mailMessage; 082 083 /** The subscription type EP */ 084 protected SubscriptionTypeExtensionPoint _subscriptionTypeEP; 085 086 /** The page subscription type */ 087 protected PageSubscriptionType _pageSubscriptionType; 088 089 /** The site manager */ 090 protected SiteManager _siteManager; 091 092 /** The i18n utils */ 093 protected I18nUtils _i18nUtils; 094 095 /** The page notification helper */ 096 protected PageNotificationsHelper _pageNotificationHelper; 097 098 /** The ametys object resolver */ 099 protected AmetysObjectResolver _resolver; 100 101 /** The page DAO */ 102 protected PageDAO _pageDAO; 103 104 /** The rendering context handler */ 105 protected RenderingContextHandler _renderingContextHandler; 106 107 @Override 108 public void service(ServiceManager manager) throws ServiceException 109 { 110 super.service(manager); 111 _i18nUtils = (I18nUtils) manager.lookup(I18nUtils.ROLE); 112 _subscriptionTypeEP = (SubscriptionTypeExtensionPoint) manager.lookup(SubscriptionTypeExtensionPoint.ROLE); 113 _pageSubscriptionType = (PageSubscriptionType) _subscriptionTypeEP.getExtension(PageSubscriptionType.ID); 114 _siteManager = (SiteManager) manager.lookup(SiteManager.ROLE); 115 _pageNotificationHelper = (PageNotificationsHelper) manager.lookup(PageNotificationsHelper.ROLE); 116 _resolver = (AmetysObjectResolver) manager.lookup(AmetysObjectResolver.ROLE); 117 _pageDAO = (PageDAO) manager.lookup(PageDAO.ROLE); 118 _renderingContextHandler = (RenderingContextHandler) manager.lookup(RenderingContextHandler.ROLE); 119 } 120 121 @Override 122 public void configure(Configuration configuration) throws ConfigurationException 123 { 124 super.configure(configuration); 125 126 _frequency = Frequency.valueOf(configuration.getChild("frequency").getValue("DAILY")); 127 _mailSubject = I18nizableText.parseI18nizableText(configuration.getChild("mail-subject"), "plugin." + _pluginName); 128 _mailTitle = I18nizableText.parseI18nizableText(configuration.getChild("mail-title"), "plugin." + _pluginName); 129 _mailMessage = I18nizableText.parseI18nizableText(configuration.getChild("mail-message"), "plugin." + _pluginName); 130 } 131 132 public void execute(JobExecutionContext context, ContainerProgressionTracker progressionTracker) throws Exception 133 { 134 // For each site 135 for (Site site : _siteManager.getSites()) 136 { 137 Request request = ContextHelper.getRequest(_context); 138 139 // Retrieve the current context. 140 RenderingContext currentContext = _renderingContextHandler.getRenderingContext(); 141 try 142 { 143 // Force rendering context.FRONT to resolve URI 144 _renderingContextHandler.setRenderingContext(RenderingContext.FRONT); 145 request.setAttribute(WebConstants.REQUEST_ATTR_SITE_NAME, site.getName()); 146 147 Map<UserIdentity, Set<Activity>> pageActivitiesBySubscriber = _getPageActivitiesBySubscriber(site); 148 for (UserIdentity subscriber : pageActivitiesBySubscriber.keySet()) 149 { 150 Set<Activity> pageActivities = pageActivitiesBySubscriber.get(subscriber); 151 if (!pageActivities.isEmpty()) 152 { 153 User user = _userManager.getUser(subscriber); 154 155 String lang = _getLanguage(user); 156 157 String subject = _i18nUtils.translate(_getI18nSubject(site), lang); 158 String body = _getMailBody(site, pageActivities, lang); 159 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(User user) 202 { 203 String language = null; 204 if (user != null) 205 { 206 // First try to get the user's language 207 language = user.getLanguage(); 208 } 209 210 language = StringUtils.defaultIfBlank(language, _userLanguagesManager.getDefaultLanguage()); 211 212 return language; 213 } 214 215 /** 216 * Get the subject of the mail 217 * @param site the site 218 * @return the subject of the mail 219 */ 220 protected I18nizableText _getI18nSubject(Site site) 221 { 222 Map<String, I18nizableTextParameter> params = Map.of( 223 "site", new I18nizableText(site.getTitle()) 224 ); 225 return new I18nizableText(_mailSubject.getCatalogue(), _mailSubject.getKey(), params); 226 } 227 228 /** 229 * Get the mail body of the mail 230 * @param site the site 231 * @param pages the set of pages with it last activity 232 * @param lang the language 233 * @return the mail body of the mail 234 * @throws IOException if an error occurred 235 */ 236 protected String _getMailBody(Site site, Set<Activity> pages, String lang) throws IOException 237 { 238 List<org.ametys.web.mail.ReportActivitiesMailBodyHelper.MailBodyBuilder.Activity> activities = pages.stream() 239 .map(a -> this._wrapActivity(a, lang)) 240 .toList(); 241 242 MailBodyBuilder bodyBuilder = ReportActivitiesMailBodyHelper.newHTMLBody() 243 .withLanguage(lang) 244 .withTitle(_mailTitle) 245 .withMessage(_mailMessage) 246 .withActivities(activities) 247 .withLink(site.getUrl(), new I18nizableText("plugin.page-subscription", "PLUGINS_PAGE_SUBSCRIPTION_MAIL_NOTIFICATIONS_MAIL_SITE_LINK")); 248 249 Optional<String> pageTagUrl = _pageDAO.findPagedIdsByTag(site.getName(), lang, FOLLOWED_PAGES_CONFIG_PAGE_TAG) 250 .stream() 251 .map(id -> ResolveURIComponent.resolve("page", id, false, true)) 252 .findFirst(); 253 if (pageTagUrl.isPresent()) 254 { 255 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"); 256 } 257 258 return bodyBuilder.build(); 259 } 260 261 private org.ametys.web.mail.ReportActivitiesMailBodyHelper.MailBodyBuilder.Activity _wrapActivity(Activity activity, String lang) 262 { 263 Map<String, I18nizableTextParameter> parameters = new HashMap<>(); 264 265 parameters.put("pageTitle", new I18nizableText(activity.getValue(AbstractPageActivityType.PAGE_TITLE))); 266 267 String pageId = activity.getValue(AbstractPageActivityType.PAGE_ID); 268 String pageUrl = ResolveURIComponent.resolve("page", pageId, false, true); 269 parameters.put("pageUrl", new I18nizableText(pageUrl)); 270 271 I18nizableText messageI18n = new I18nizableText("plugin.page-subscription", "PLUGINS_PAGE_SUBSCRIPTION_MAIL_PAGE_NOTIFICATIONS_MAIL_CONTENT_MESSAGE", parameters); 272 String message = _i18nUtils.translate(messageI18n, lang); 273 274 // Activity is anonymized (no user avatar nor name) 275 return new org.ametys.web.mail.ReportActivitiesMailBodyHelper.MailBodyBuilder.Activity(null, activity.getDate(), message); 276 } 277 278}