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