001/* 002 * Copyright 2010 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.cms.alerts; 017 018import java.util.ArrayList; 019import java.util.Collections; 020import java.util.Date; 021import java.util.HashMap; 022import java.util.List; 023import java.util.Map; 024 025import org.apache.avalon.framework.service.ServiceException; 026import org.apache.avalon.framework.service.ServiceManager; 027import org.apache.commons.lang.StringUtils; 028 029import org.ametys.cms.content.ContentHelper; 030import org.ametys.cms.lock.LockContentManager; 031import org.ametys.cms.repository.Content; 032import org.ametys.cms.repository.ModifiableContent; 033import org.ametys.core.ui.Callable; 034import org.ametys.core.ui.StaticClientSideElement; 035import org.ametys.core.util.DateUtils; 036import org.ametys.plugins.repository.AmetysObjectResolver; 037import org.ametys.plugins.repository.AmetysRepositoryException; 038import org.ametys.plugins.repository.lock.LockAwareAmetysObject; 039import org.ametys.plugins.repository.metadata.CompositeMetadata; 040import org.ametys.plugins.repository.metadata.ModifiableCompositeMetadata; 041import org.ametys.plugins.repository.version.MetadataAndVersionAwareAmetysObject; 042import org.ametys.plugins.repository.version.ModifiableMetadataAwareVersionableAmetysObject; 043import org.ametys.runtime.config.Config; 044import org.ametys.runtime.i18n.I18nizableText; 045 046/** 047 * This element creates a toggle button representing the reminders state. 048 */ 049public class ContentAlertsClientSideElement extends StaticClientSideElement 050{ 051 /** Repository content. */ 052 protected AmetysObjectResolver _resolver; 053 054 /** The lock manager. */ 055 protected LockContentManager _lockManager; 056 057 /** The service manager */ 058 protected ServiceManager _smanager; 059 060 /** The content helper */ 061 protected ContentHelper _contentHelper; 062 063 @Override 064 public void service(ServiceManager serviceManager) throws ServiceException 065 { 066 super.service(serviceManager); 067 _smanager = serviceManager; 068 _resolver = (AmetysObjectResolver) serviceManager.lookup(AmetysObjectResolver.ROLE); 069 _lockManager = (LockContentManager) serviceManager.lookup(LockContentManager.ROLE); 070 _contentHelper = (ContentHelper) serviceManager.lookup(ContentHelper.ROLE); 071 } 072 073 enum AlertsStatus 074 { 075 /** All the alerts are enabled */ 076 ENABLED, 077 078 /** All the alerts are disabled */ 079 DISABLED, 080 081 /** Some alerts are enabled, others are disabled */ 082 MIXED 083 } 084 085 /** 086 * Get information on reminders state. 087 * 088 * @param contentsId The list of contents' ids 089 * @return informations on reminders state. 090 */ 091 @Callable 092 public Map<String, Object> getAlertsInformations(List<String> contentsId) 093 { 094 Map<String, Object> results = new HashMap<>(); 095 096 Long validationAlertDelay = Config.getInstance().getValue("remind.content.validation.delay"); 097 Long unmodifiedAlertDelay = Config.getInstance().getValue("remind.unmodified.content.delay"); 098 099 results.put("alert-enabled-contents", new ArrayList<Map<String, Object>>()); 100 results.put("alert-disabled-contents", new ArrayList<Map<String, Object>>()); 101 results.put("all-right-contents", new ArrayList<Map<String, Object>>()); 102 results.put("not-unlockable-contents", new ArrayList<Map<String, Object>>()); 103 104 AlertsStatus unmodifiedAlertStatus = null; 105 AlertsStatus reminderAlertStatus = null; 106 107 for (String contentId : contentsId) 108 { 109 Content content = _resolver.resolveById(contentId); 110 111 if (content instanceof MetadataAndVersionAwareAmetysObject && content instanceof ModifiableContent) 112 { 113 Map<String, Object> contentParams = getContentDefaultParameters(content); 114 115 // Test if the content is locked and cannot be unlocked by the 116 // current user. 117 boolean isValid = !isUnlockable(content, results); 118 119 CompositeMetadata meta = ((MetadataAndVersionAwareAmetysObject) content).getUnversionedMetadataHolder(); 120 121 boolean unmodifiedAlertEnabled = meta.getBoolean(AlertsConstants.UNMODIFIED_ALERT_ENABLED, false); 122 if (unmodifiedAlertEnabled) 123 { 124 String unmodifiedAlertText = meta.getString(AlertsConstants.UNMODIFIED_ALERT_TEXT, ""); 125 results.put("unmodifiedAlertText", unmodifiedAlertText); 126 } 127 128 unmodifiedAlertStatus = _getUnmodifiedStatusAlertsInformations(unmodifiedAlertStatus, unmodifiedAlertEnabled); 129 130 boolean reminderEnabled = meta.getBoolean(AlertsConstants.REMINDER_ENABLED, false); 131 if (reminderEnabled) 132 { 133 String reminderDateStr = meta.getString(AlertsConstants.REMINDER_DATE, ""); 134 String reminderText = meta.getString(AlertsConstants.REMINDER_TEXT, ""); 135 136 results.put("reminderDate", reminderDateStr); 137 results.put("reminderText", reminderText); 138 } 139 140 reminderAlertStatus = _getUnmodifiedStatusAlertsInformations(reminderAlertStatus, reminderEnabled); 141 142 // Alerts enabled/disabled 143 _getEnabledDisabledAlertsInformations(results, unmodifiedAlertDelay, content, contentParams, unmodifiedAlertEnabled, reminderEnabled); 144 145 // All right 146 if (isValid) 147 { 148 @SuppressWarnings("unchecked") 149 List<Map<String, Object>> validContents = (List<Map<String, Object>>) results.get("all-right-contents"); 150 validContents.add(contentParams); 151 } 152 } 153 } 154 155 results.put("reminderEnabled", reminderAlertStatus == AlertsStatus.MIXED ? null : (reminderAlertStatus == AlertsStatus.ENABLED ? true : false)); 156 results.put("unmodifiedAlertEnabled", unmodifiedAlertStatus == AlertsStatus.MIXED ? null : (unmodifiedAlertStatus == AlertsStatus.ENABLED ? true : false)); 157 158 results.put("validation-alert-delay", validationAlertDelay); 159 results.put("unmodified-alert-delay", unmodifiedAlertDelay); 160 161 return results; 162 } 163 164 private AlertsStatus _getUnmodifiedStatusAlertsInformations(AlertsStatus unmodifiedAlertStatus, boolean unmodifiedAlertEnabled) 165 { 166 AlertsStatus localUnmodifiedAlertStatus = unmodifiedAlertStatus; 167 if (localUnmodifiedAlertStatus == null) 168 { 169 // Initialize the alert status with first content 170 localUnmodifiedAlertStatus = unmodifiedAlertEnabled ? AlertsStatus.ENABLED : AlertsStatus.DISABLED; 171 } 172 else if ((localUnmodifiedAlertStatus == AlertsStatus.ENABLED && !unmodifiedAlertEnabled) || (localUnmodifiedAlertStatus == AlertsStatus.DISABLED && unmodifiedAlertEnabled)) 173 { 174 // Alert status is different for at least one content 175 localUnmodifiedAlertStatus = AlertsStatus.MIXED; 176 } 177 return localUnmodifiedAlertStatus; 178 } 179 180 private void _getEnabledDisabledAlertsInformations(Map<String, Object> results, Long unmodifiedAlertDelay, Content content, Map<String, Object> contentParams, 181 boolean unmodifiedAlertEnabled, boolean reminderEnabled) 182 { 183 if (unmodifiedAlertEnabled && unmodifiedAlertDelay > 0 || reminderEnabled) 184 { 185 I18nizableText ed = (I18nizableText) this._script.getParameters().get("alerts-enabled-description"); 186 I18nizableText msg = new I18nizableText(ed.getCatalogue(), ed.getKey(), Collections.singletonList(_contentHelper.getTitle(content))); 187 188 contentParams.put("description", msg); 189 190 @SuppressWarnings("unchecked") 191 List<Map<String, Object>> enabledAlerts = (List<Map<String, Object>>) results.get("alert-enabled-contents"); 192 enabledAlerts.add(contentParams); 193 } 194 else 195 { 196 I18nizableText ed = (I18nizableText) this._script.getParameters().get("alerts-disabled-description"); 197 I18nizableText msg = new I18nizableText(ed.getCatalogue(), ed.getKey(), Collections.singletonList(_contentHelper.getTitle(content))); 198 199 contentParams.put("description", msg); 200 201 @SuppressWarnings("unchecked") 202 List<Map<String, Object>> disabledAlerts = (List<Map<String, Object>>) results.get("alert-disabled-contents"); 203 disabledAlerts.add(contentParams); 204 } 205 } 206 207 /** 208 * Test if the content is locked and cannot be unlocked by the current user. 209 * <br> 210 * If this is so, fill the "not-unlockable" list in the result map. 211 * 212 * @param content the content to test. 213 * @param results the result map. 214 * @return true if the content is locked and cannot be unlocked by the 215 * current user, false otherwise. 216 */ 217 @SuppressWarnings("unchecked") 218 protected boolean isUnlockable(Content content, Map<String, Object> results) 219 { 220 boolean isUnlockable = false; 221 222 if (content instanceof LockAwareAmetysObject) 223 { 224 LockAwareAmetysObject lockAwareContent = (LockAwareAmetysObject) content; 225 226 if (lockAwareContent.isLocked() && !_lockManager.canUnlock(lockAwareContent)) 227 { 228 Map<String, Object> contentParams = getContentDefaultParameters(content); 229 230 I18nizableText ed = (I18nizableText) this._script.getParameters().get("not-unlockable-description"); 231 I18nizableText msg = new I18nizableText(ed.getCatalogue(), ed.getKey(), Collections.singletonList(_contentHelper.getTitle(content))); 232 233 contentParams.put("description", msg); 234 235 List<Map<String, Object>> notUnlockable = (List<Map<String, Object>>) results.get("not-unlockable-contents"); 236 notUnlockable.add(contentParams); 237 238 isUnlockable = true; 239 } 240 } 241 242 return isUnlockable; 243 } 244 245 /** 246 * Get the default content's parameters 247 * 248 * @param content The content 249 * @return The default parameters 250 */ 251 protected Map<String, Object> getContentDefaultParameters(Content content) 252 { 253 Map<String, Object> contentParams = new HashMap<>(); 254 contentParams.put("id", content.getId()); 255 contentParams.put("title", _contentHelper.getTitle(content)); 256 257 return contentParams; 258 } 259 260 /** 261 * Set alerts on content 262 * 263 * @param contentIds the content's id 264 * @param params the alerts' parameters 265 * @throws ServiceException if an error occurred while retrieving the alert 266 * scheduler component 267 */ 268 @Callable 269 public void setAlertsOnContent(List<String> contentIds, Map<String, Object> params) throws ServiceException 270 { 271 for (String contentId : contentIds) 272 { 273 Content content = _resolver.resolveById(contentId); 274 if (content instanceof ModifiableMetadataAwareVersionableAmetysObject) 275 { 276 _setAlerts((ModifiableMetadataAwareVersionableAmetysObject) content, params); 277 } 278 } 279 280 String role = params.containsKey("role") ? (String) params.get("role") : AlertScheduler.ROLE; 281 AlertScheduler alertScheduler = (AlertScheduler) _smanager.lookup(role); 282 283 boolean instantAlertEnabled = (Boolean) params.get("instantAlertEnabled"); 284 if (instantAlertEnabled) 285 { 286 String instantAlertText = (String) params.get("instantAlertText"); 287 alertScheduler.sendInstantAlerts(contentIds, org.apache.commons.lang3.StringUtils.trimToEmpty(instantAlertText)); 288 } 289 } 290 291 /** 292 * Sets the alerts on the specified content. 293 * 294 * @param content the content to set the alerts on. 295 * @param params the alerts' parameters 296 * @throws AmetysRepositoryException if a repository error occurs. 297 */ 298 protected void _setAlerts(ModifiableMetadataAwareVersionableAmetysObject content, Map<String, Object> params) throws AmetysRepositoryException 299 { 300 ModifiableCompositeMetadata meta = content.getUnversionedMetadataHolder(); 301 302 // Set alert for unmodified contents 303 if (params.get("unmodifiedAlertEnabled") != null) 304 { 305 boolean unmodifiedAlertEnabled = (Boolean) params.get("unmodifiedAlertEnabled"); 306 String unmodifiedAlertText = (String) params.get("unmodifiedAlertText"); 307 308 meta.setMetadata(AlertsConstants.UNMODIFIED_ALERT_ENABLED, unmodifiedAlertEnabled); 309 meta.setMetadata(AlertsConstants.UNMODIFIED_ALERT_TEXT, org.apache.commons.lang3.StringUtils.trimToEmpty(unmodifiedAlertText)); 310 } 311 312 // Set reminders 313 if (params.get("reminderEnabled") != null) 314 { 315 boolean reminderEnabled = (Boolean) params.get("reminderEnabled"); 316 meta.setMetadata(AlertsConstants.REMINDER_ENABLED, reminderEnabled); 317 318 String reminderDateStr = (String) params.get("reminderDate"); 319 String reminderText = (String) params.get("reminderText"); 320 meta.setMetadata(AlertsConstants.REMINDER_TEXT, org.apache.commons.lang3.StringUtils.trimToEmpty(reminderText)); 321 322 // Parse the reminder date. 323 if (StringUtils.isNotBlank(reminderDateStr)) 324 { 325 Date date = DateUtils.parse(reminderDateStr); 326 if (date != null) 327 { 328 meta.setMetadata(AlertsConstants.REMINDER_DATE, date); 329 } 330 else 331 { 332 getLogger().error("Unable to parse reminder date " + reminderDateStr); 333 } 334 } 335 } 336 337 content.saveChanges(); 338 } 339 340 @Override 341 public List<Script> getScripts(boolean ignoreRights, Map<String, Object> contextParameters) 342 { 343 boolean enabled = Config.getInstance().getValue("remind.content.enabled"); 344 if (enabled) 345 { 346 return super.getScripts(ignoreRights, contextParameters); 347 } 348 349 return new ArrayList<>(); 350 } 351}