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.plugins.workspaces.activities; 017 018import java.io.ByteArrayOutputStream; 019import java.io.IOException; 020import java.io.InputStream; 021import java.util.ArrayList; 022import java.util.List; 023import java.util.Map; 024import java.util.Objects; 025import java.util.Set; 026import java.util.stream.Collectors; 027 028import org.apache.avalon.framework.context.Context; 029import org.apache.avalon.framework.context.ContextException; 030import org.apache.avalon.framework.context.Contextualizable; 031import org.apache.avalon.framework.service.ServiceException; 032import org.apache.avalon.framework.service.ServiceManager; 033import org.apache.avalon.framework.service.Serviceable; 034import org.apache.cocoon.components.ContextHelper; 035import org.apache.cocoon.environment.Request; 036import org.apache.commons.lang3.StringUtils; 037import org.apache.excalibur.source.Source; 038import org.apache.excalibur.source.SourceResolver; 039import org.apache.excalibur.source.SourceUtil; 040 041import org.ametys.core.right.RightManager; 042import org.ametys.core.user.User; 043import org.ametys.core.user.UserIdentity; 044import org.ametys.core.user.UserManager; 045import org.ametys.core.util.I18nUtils; 046import org.ametys.plugins.repository.AmetysObject; 047import org.ametys.plugins.repository.AmetysObjectResolver; 048import org.ametys.plugins.repository.activities.Activity; 049import org.ametys.plugins.workspaces.project.ProjectManager; 050import org.ametys.plugins.workspaces.project.notification.preferences.NotificationPreferencesHelper; 051import org.ametys.plugins.workspaces.project.notification.preferences.NotificationPreferencesHelper.Frequency; 052import org.ametys.plugins.workspaces.project.objects.Project; 053import org.ametys.runtime.config.Config; 054import org.ametys.runtime.i18n.I18nizableText; 055import org.ametys.runtime.plugin.component.PluginAware; 056import org.ametys.web.WebConstants; 057import org.ametys.web.activities.notify.ActivityNotifier; 058import org.ametys.web.renderingcontext.RenderingContext; 059import org.ametys.web.renderingcontext.RenderingContextHandler; 060import org.ametys.web.repository.site.Site; 061import org.ametys.web.repository.sitemap.Sitemap; 062 063/** 064 * Abstract class representing a activity notifier for workspaces 065 */ 066public abstract class AbstractWorkspacesActivityNotifier implements ActivityNotifier, Serviceable, PluginAware, Contextualizable 067{ 068 /** The right manager */ 069 protected RightManager _rightManager; 070 071 /** The notification preference helper */ 072 protected NotificationPreferencesHelper _notificationPreferenceHelper; 073 074 /** The user manager */ 075 protected UserManager _userManager; 076 077 /** The project manager */ 078 protected ProjectManager _projectManager; 079 080 /** The i18n utils */ 081 protected I18nUtils _i18nUtils; 082 083 /** The rendering context handler */ 084 protected RenderingContextHandler _renderingContextHandler; 085 086 /** The source resolver */ 087 protected SourceResolver _srcResolver; 088 089 /** The Ametys Object resolver */ 090 protected AmetysObjectResolver _resolver; 091 092 /** The plugin name */ 093 protected String _pluginName; 094 095 /** The context */ 096 protected Context _context; 097 098 public void service(ServiceManager manager) throws ServiceException 099 { 100 _rightManager = (RightManager) manager.lookup(RightManager.ROLE); 101 _notificationPreferenceHelper = (NotificationPreferencesHelper) manager.lookup(NotificationPreferencesHelper.ROLE); 102 _userManager = (UserManager) manager.lookup(UserManager.ROLE); 103 _projectManager = (ProjectManager) manager.lookup(ProjectManager.ROLE); 104 _i18nUtils = (I18nUtils) manager.lookup(I18nUtils.ROLE); 105 _renderingContextHandler = (RenderingContextHandler) manager.lookup(RenderingContextHandler.ROLE); 106 _srcResolver = (SourceResolver) manager.lookup(SourceResolver.ROLE); 107 _resolver = (AmetysObjectResolver) manager.lookup(AmetysObjectResolver.ROLE); 108 } 109 110 public void contextualize(Context context) throws ContextException 111 { 112 _context = context; 113 } 114 115 public void setPluginInfo(String pluginName, String featureName, String id) 116 { 117 _pluginName = pluginName; 118 } 119 120 public List<String> getUsersEmailToNotify(Activity activity) 121 { 122 boolean returnAll = Config.getInstance().getValue("runtime.mail.massive.sending"); 123 124 // FIXME GG right should be provided directly by the activity 125 AmetysObject aObject = getTargetAmetysObject(activity); 126 Set<UserIdentity> readAccessUsers = _rightManager.getReadAccessAllowedUsers(aObject).resolveAllowedUsers(returnAll); 127 128 return readAccessUsers.stream() 129 .filter(userId -> _notificationPreferenceHelper.askedToBeNotified(userId, activity.getValue(AbstractWorkspacesActivityType.PROJECT_NAME), Frequency.EACH)) 130 .map(_userManager::getUser) 131 .filter(Objects::nonNull) 132 .map(User::getEmail) 133 .filter(StringUtils::isNotEmpty) 134 .collect(Collectors.toList()); 135 } 136 137 /** 138 * Retrieve the ametys object targeted by this activity. 139 * This method is intended for right computation purposes. 140 * @param activity the activity 141 * @return the target 142 */ 143 // FIXME this should not exist and the activity itself should provide the right. (probably by a right convertor) 144 public abstract AmetysObject getTargetAmetysObject(Activity activity); 145 146 public String getMailSubject(Activity activity) 147 { 148 String projectName = activity.getValue(AbstractWorkspacesActivityType.PROJECT_NAME); 149 Project project = _projectManager.getProject(projectName); 150 Site site = project.getSite(); 151 152 // Retrieve the lang through the activity to match summary implementation 153 String lang = site.getSitemaps().stream().findFirst().map(Sitemap::getName).orElse(null); 154 155 return _i18nUtils.translate(new I18nizableText("plugin." + _pluginName, _getSubjectI18nKey(activity), getSubjectI18nParams(activity)), lang); 156 } 157 158 /** 159 * Get the subject i18n key 160 * @param activity the activity 161 * @return the subject i18n key 162 */ 163 protected String _getSubjectI18nKey(Activity activity) 164 { 165 return "PROJECT_MAIL_NOTIFICATION_SUBJECT_" + StringUtils.replaceChars(activity.getEventType().toUpperCase(), '.', '_'); 166 } 167 168 /** 169 * Get the subject i18n parameters 170 * @param activity the activity 171 * @return the subject i18n parameters 172 */ 173 public List<String> getSubjectI18nParams(Activity activity) 174 { 175 List<String> i18nparams = new ArrayList<>(); 176 i18nparams.add(activity.getValue(AbstractWorkspacesActivityType.PROJECT_TITLE)); // {0} 177 return i18nparams; 178 } 179 180 public String getMailTextBody(Activity activity) 181 { 182 // No text body 183 return null; 184 } 185 186 public String getMailHtmlBody(Activity activity) 187 { 188 String projectName = activity.getValue(AbstractWorkspacesActivityType.PROJECT_NAME); 189 Project project = _projectManager.getProject(projectName); 190 Site site = project.getSite(); 191 192 // Retrieve the lang through the activity to match summary implementation 193 String lang = site.getSitemaps().stream().findFirst().map(Sitemap::getName).orElse(null); 194 195 String mailBody; 196 Source source = null; 197 RenderingContext currentContext = _renderingContextHandler.getRenderingContext(); 198 199 try 200 { 201 // Force rendering context.FRONT to resolve URI 202 _renderingContextHandler.setRenderingContext(RenderingContext.FRONT); 203 204 Request request = ContextHelper.getRequest(_context); 205 206 request.setAttribute("lang", lang); 207 request.setAttribute("forceAbsoluteUrl", true); 208 request.setAttribute(WebConstants.REQUEST_ATTR_SITE, site); 209 request.setAttribute(WebConstants.REQUEST_ATTR_SITE_NAME, site.getName()); 210 request.setAttribute(WebConstants.REQUEST_ATTR_SKIN_ID, site.getSkinId()); 211 212 source = _srcResolver.resolveURI(getMailBodyURI(activity), null, Map.of(AbstractWorkspacesActivityType.ACTIVITY_CONTEXT_PARAM, activity)); 213 214 try (InputStream is = source.getInputStream()) 215 { 216 ByteArrayOutputStream bos = new ByteArrayOutputStream(); 217 SourceUtil.copy(is, bos); 218 219 mailBody = bos.toString("UTF-8"); 220 } 221 } 222 catch (IOException e) 223 { 224 throw new RuntimeException("Failed to create mail body", e); 225 } 226 finally 227 { 228 _renderingContextHandler.setRenderingContext(currentContext); 229 230 if (source != null) 231 { 232 _srcResolver.release(source); 233 } 234 } 235 return mailBody; 236 } 237 238 /** 239 * Get the URI to resolve to get the mail body 240 * @param activity the activity 241 * @return the uri 242 */ 243 public abstract String getMailBodyURI(Activity activity); 244 245}