001/* 002 * Copyright 2011 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.translationflagging; 017 018import java.util.ArrayList; 019import java.util.Collection; 020import java.util.HashMap; 021import java.util.HashSet; 022import java.util.List; 023import java.util.Locale; 024import java.util.Map; 025import java.util.Set; 026 027import javax.mail.MessagingException; 028 029import org.apache.avalon.framework.activity.Initializable; 030import org.apache.avalon.framework.service.ServiceException; 031import org.apache.avalon.framework.service.ServiceManager; 032import org.apache.commons.lang.StringUtils; 033 034import org.ametys.cms.repository.WorkflowAwareContent; 035import org.ametys.cms.workflow.AbstractContentWorkflowComponent; 036import org.ametys.core.right.RightManager; 037import org.ametys.core.user.User; 038import org.ametys.core.user.UserIdentity; 039import org.ametys.core.user.UserManager; 040import org.ametys.core.util.I18nUtils; 041import org.ametys.core.util.mail.SendMailHelper; 042import org.ametys.plugins.repository.AmetysObjectResolver; 043import org.ametys.plugins.repository.metadata.CompositeMetadata; 044import org.ametys.plugins.repository.metadata.UnknownMetadataException; 045import org.ametys.runtime.config.Config; 046import org.ametys.runtime.i18n.I18nizableText; 047import org.ametys.runtime.plugin.component.PluginAware; 048import org.ametys.web.repository.content.WebContent; 049import org.ametys.web.repository.page.Page; 050import org.ametys.web.site.SiteConfigurationExtensionPoint; 051 052import com.opensymphony.module.propertyset.PropertySet; 053import com.opensymphony.workflow.FunctionProvider; 054import com.opensymphony.workflow.WorkflowException; 055 056/** 057 * When a content is saved, this workflow function looks if the pages it belongs to are translated in other languages. 058 * If this is the case, an alert e-mail is sent to all the persons who are responsible for modifying the translated pages, 059 * to inform them that a new version is available. 060 */ 061public class TranslationAlertFunction extends AbstractContentWorkflowComponent implements FunctionProvider, Initializable, PluginAware 062{ 063 064 /** The e-mail subject i18n key. */ 065 public static final String I18N_KEY_SUBJECT = "PLUGINS_TRANSLATIONFLAGGING_ALERT_EMAIL_SUBJECT"; 066 067 /** The e-mail body i18n key. */ 068 public static final String I18N_KEY_BODY = "PLUGINS_TRANSLATIONFLAGGING_ALERT_EMAIL_BODY"; 069 070 /** The users manager. */ 071 protected UserManager _userManager; 072 073 /** The rights manager. */ 074 protected RightManager _rightManager; 075 076 /** The i18n utils. */ 077 protected I18nUtils _i18nUtils; 078 079 /** The ametys object resolver. */ 080 protected AmetysObjectResolver _resolver; 081 082 /** The site configuration. */ 083 protected SiteConfigurationExtensionPoint _siteConf; 084 085 /** The plugin name. */ 086 protected String _pluginName; 087 088 /** The server base URL. */ 089 protected String _baseUrl; 090 091 @Override 092 public void setPluginInfo(String pluginName, String featureName, String id) 093 { 094 _pluginName = pluginName; 095 } 096 097 @Override 098 public void initialize() throws Exception 099 { 100 _baseUrl = StringUtils.removeEndIgnoreCase(Config.getInstance().getValueAsString("cms.url"), "index.html"); 101 if (!_baseUrl.endsWith("/")) 102 { 103 _baseUrl = _baseUrl + "/"; 104 } 105 } 106 107 @Override 108 public void service(ServiceManager serviceManager) throws ServiceException 109 { 110 super.service(serviceManager); 111 _userManager = (UserManager) serviceManager.lookup(UserManager.ROLE); 112 _rightManager = (RightManager) serviceManager.lookup(RightManager.ROLE); 113 _i18nUtils = (I18nUtils) serviceManager.lookup(I18nUtils.ROLE); 114 _resolver = (AmetysObjectResolver) serviceManager.lookup(AmetysObjectResolver.ROLE); 115 _siteConf = (SiteConfigurationExtensionPoint) serviceManager.lookup(SiteConfigurationExtensionPoint.ROLE); 116 } 117 118 @Override 119 public void execute(Map transientVars, Map args, PropertySet ps) throws WorkflowException 120 { 121 _logger.info("Performing translation alerts workflow function."); 122 123 // Retrieve current content. 124 WorkflowAwareContent content = getContent(transientVars); 125 126 if (content instanceof WebContent) 127 { 128 WebContent webContent = (WebContent) content; 129 String siteName = webContent.getSiteName(); 130 131 Boolean enabled = _siteConf.getValueAsBoolean(siteName, "translationflagging-enable-alerts"); 132 133 // The content has to be a web content to be referenced by pages. 134 if (enabled != null && enabled) 135 { 136 sendAlerts((WebContent) content); 137 } 138 } 139 } 140 141 /** 142 * Send the alerts to tell users the translated content was modified. 143 * @param content the modified content. 144 */ 145 protected void sendAlerts(WebContent content) 146 { 147 // Process all the pages which reference the content. 148 for (Page page : content.getReferencingPages()) 149 { 150 // Get the master language for this site. 151 String masterLanguage = _siteConf.getValueAsString(page.getSiteName(), "master-language"); 152 153 // Process the page only if it's in the master language, or there is no master language. 154 if (StringUtils.isEmpty(masterLanguage) || page.getSitemapName().equals(masterLanguage)) 155 { 156 // Get the translated versions of the page. 157 Collection<Page> translatedPages = getTranslations(page).values(); 158 159 for (Page translatedPage : translatedPages) 160 { 161 // Get the users to sent the alert to. 162 HashSet<UserIdentity> users = getUsersToNotify(translatedPage); 163 164 // Build and send the alert. 165 sendAlert(page, content, translatedPage, users); 166 } 167 } 168 } 169 } 170 171 /** 172 * Build and send an alert e-mail to inform of a translation to a list of users. 173 * @param page the modified page. 174 * @param content the content which was modified. 175 * @param translatedPage the translated page. 176 * @param users the users to send the e-mail to. 177 */ 178 protected void sendAlert(Page page, WebContent content, Page translatedPage, Set<UserIdentity> users) 179 { 180 List<String> params = new ArrayList<>(); 181 182 String mailFrom = _siteConf.getValueAsString(page.getSiteName(), "site-mail-from"); 183 184 // Get a human-readable version of the languages. 185 String pageLang = _i18nUtils.translate(new I18nizableText("plugin.web", "I18NKEY_LANGUAGE_" + page.getSitemapName().toUpperCase())); 186 String translatedLang = _i18nUtils.translate(new I18nizableText("plugin.web", "I18NKEY_LANGUAGE_" + translatedPage.getSitemapName().toUpperCase())); 187 188 // Build a list of the parameters. 189 params.add(page.getSite().getTitle()); 190 params.add(content.getTitle(new Locale(page.getSitemapName()))); 191 params.add(page.getTitle()); 192 params.add(pageLang.toLowerCase()); 193 params.add(translatedPage.getTitle()); 194 params.add(translatedLang.toLowerCase()); 195 params.add(getPageUrl(page)); 196 params.add(getPageUrl(translatedPage)); 197 198 String catalogue = "plugin." + _pluginName; 199 200 // Get the e-mail subject and body. 201 I18nizableText i18nSubject = new I18nizableText(catalogue, I18N_KEY_SUBJECT, params); 202 I18nizableText i18nBody = new I18nizableText(catalogue, I18N_KEY_BODY, params); 203 204 String subject = _i18nUtils.translate(i18nSubject); 205 String body = _i18nUtils.translate(i18nBody); 206 207 // Send the e-mails. 208 sendMails(subject, body, users, mailFrom); 209 } 210 211 /** 212 * Send a translation alert e-mail to the specified users. 213 * @param subject the e-mail subject. 214 * @param body the e-mail body. 215 * @param users the users to send the e-mail to. 216 * @param from the e-mail will be sent with this "from" header. 217 */ 218 protected void sendMails(String subject, String body, Set<UserIdentity> users, String from) 219 { 220 for (UserIdentity userIdentity : users) 221 { 222 User user = _userManager.getUser(userIdentity.getPopulationId(), userIdentity.getLogin()); 223 224 if (user != null && StringUtils.isNotEmpty(user.getEmail())) 225 { 226 String mail = user.getEmail(); 227 228 try 229 { 230 SendMailHelper.sendMail(subject, null, body, mail, from); 231 } 232 catch (MessagingException e) 233 { 234 if (_logger.isWarnEnabled()) 235 { 236 _logger.warn("Could not send a translation alert e-mail to " + mail, e); 237 } 238 } 239 } 240 } 241 } 242 243 /** 244 * Get the users to notify about the page translation. 245 * @param translatedPage the translated version of the page. 246 * @return the logins of the users to notify. 247 */ 248 protected HashSet<UserIdentity> getUsersToNotify(Page translatedPage) 249 { 250 HashSet<UserIdentity> users = new HashSet<>(); 251 252 // Get the users which have the right to modify the page AND to receive the notification. 253 Set<UserIdentity> editors = _rightManager.getAllowedUsers("Workflow_Rights_Edition_Online", translatedPage).resolveAllowedUsers(Config.getInstance().getValueAsBoolean("runtime.mail.massive.sending")); 254 Set<UserIdentity> usersToNotify = _rightManager.getAllowedUsers("TranslationFlagging_Rights_Notification", translatedPage).resolveAllowedUsers(Config.getInstance().getValueAsBoolean("runtime.mail.massive.sending")); 255 256 users.addAll(editors); 257 users.retainAll(usersToNotify); 258 259 return users; 260 } 261 262 /** 263 * Get the translations of a given page. 264 * @param page the page. 265 * @return the translated pages as a Map of pages, indexed by sitemap name (language). 266 */ 267 protected Map<String, Page> getTranslations(Page page) 268 { 269 Map<String, Page> translations = new HashMap<>(); 270 271 CompositeMetadata meta = page.getMetadataHolder(); 272 273 try 274 { 275 CompositeMetadata transMeta = meta.getCompositeMetadata(TranslationFlaggingClientSideElement.TRANSLATIONS_META); 276 for (String lang : transMeta.getMetadataNames()) 277 { 278 String translatedPageId = transMeta.getString(lang); 279 Page translatedPage = _resolver.resolveById(translatedPageId); 280 281 translations.put(lang, translatedPage); 282 } 283 } 284 catch (UnknownMetadataException e) 285 { 286 // Ignore : the translations composite metadata doesn't exist, just return an empty map. 287 } 288 289 return translations; 290 } 291 292 /** 293 * Get the URL of the back-office, opening on the page tool. 294 * @param page the page to open on. 295 * @return the page URL. 296 */ 297 protected String getPageUrl(Page page) 298 { 299 StringBuilder url = new StringBuilder(_baseUrl); 300 url.append(page.getSite().getName()).append("/index.html?uitool=uitool-page,id:%27").append(page.getId()).append("%27"); 301 return url.toString(); 302 } 303 304}