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