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.clientsideelement; 017 018import java.util.ArrayList; 019import java.util.Arrays; 020import java.util.HashMap; 021import java.util.List; 022import java.util.Map; 023import java.util.Set; 024 025import org.apache.avalon.framework.context.Context; 026import org.apache.avalon.framework.context.ContextException; 027import org.apache.avalon.framework.context.Contextualizable; 028import org.apache.avalon.framework.service.ServiceException; 029import org.apache.avalon.framework.service.ServiceManager; 030import org.apache.commons.lang.StringUtils; 031 032import org.ametys.cms.repository.Content; 033import org.ametys.cms.repository.ModifiableContent; 034import org.ametys.cms.repository.WorkflowAwareContent; 035import org.ametys.cms.workflow.AbstractContentWorkflowComponent; 036import org.ametys.core.right.RightManager.RightResult; 037import org.ametys.core.ui.Callable; 038import org.ametys.core.ui.StaticClientSideElement; 039import org.ametys.core.user.CurrentUserProvider; 040import org.ametys.core.user.User; 041import org.ametys.core.user.UserIdentity; 042import org.ametys.core.user.UserManager; 043import org.ametys.plugins.repository.AmetysObjectResolver; 044import org.ametys.plugins.repository.lock.LockHelper; 045import org.ametys.plugins.repository.lock.LockableAmetysObject; 046import org.ametys.plugins.workflow.AbstractWorkflowComponent; 047import org.ametys.plugins.workflow.support.WorkflowProvider; 048import org.ametys.plugins.workflow.support.WorkflowProvider.AmetysObjectWorkflow; 049import org.ametys.runtime.i18n.I18nizableText; 050 051import com.opensymphony.workflow.spi.Step; 052 053/** 054 * Edit HMI item 055 */ 056public class SmartContentClientSideElement extends StaticClientSideElement implements Contextualizable 057{ 058 /** Runtime users manager */ 059 protected UserManager _userManager; 060 /** Workflow provider */ 061 protected WorkflowProvider _workflowProvider; 062 /** Ametys object resolver */ 063 protected AmetysObjectResolver _resolver; 064 /** The current user provider */ 065 protected CurrentUserProvider _userProvider; 066 /** The context */ 067 protected Context _context; 068 069 @Override 070 public void contextualize(Context context) throws ContextException 071 { 072 _context = context; 073 } 074 075 @Override 076 public void service(ServiceManager manager) throws ServiceException 077 { 078 super.service(manager); 079 _userManager = (UserManager) manager.lookup(UserManager.ROLE); 080 _workflowProvider = (WorkflowProvider) manager.lookup(WorkflowProvider.ROLE); 081 _resolver = (AmetysObjectResolver) manager.lookup(AmetysObjectResolver.ROLE); 082 _userProvider = (CurrentUserProvider) manager.lookup(CurrentUserProvider.ROLE); 083 } 084 085 /** 086 * Get informations on contents' state 087 * @param contentsId the ids of contents 088 * @return informations on contents' state 089 */ 090 @Callable 091 public Map<String, Object> getStatus(List<String> contentsId) 092 { 093 Map<String, Object> results = new HashMap<>(); 094 095 results.put("unmodifiable-contents", new ArrayList<Map<String, Object>>()); 096 results.put("locked-contents", new ArrayList<Map<String, Object>>()); 097 results.put("noright-contents", new ArrayList<Map<String, Object>>()); 098 results.put("invalidworkflowaction-contents", new ArrayList<Map<String, Object>>()); 099 results.put("invalidworkflowstep-contents", new ArrayList<Map<String, Object>>()); 100 results.put("allright-contents", new ArrayList<Map<String, Object>>()); 101 102 for (String contentId : contentsId) 103 { 104 Content content = _resolver.resolveById(contentId); 105 106 boolean error = false; 107 108 if (content instanceof WorkflowAwareContent) 109 { 110 // Is modifiable 111 String enabledOnModifiableOnly = (String) this._script.getParameters().get("enabled-on-modifiable-only"); 112 if ("true".equals(enabledOnModifiableOnly) && !_isModifiable(content)) 113 { 114 Map<String, Object> contentParams = getContentDefaultParameters (content); 115 contentParams.put("description", _getNoModifiableDescription(content)); 116 117 @SuppressWarnings("unchecked") 118 List<Map<String, Object>> unModifiableContents = (List<Map<String, Object>>) results.get("unmodifiable-contents"); 119 unModifiableContents.add(contentParams); 120 121 error = true; 122 } 123 124 // Is locked 125 String enabledOnUnlockOnly = (String) this._script.getParameters().get("enabled-on-unlock-only"); 126 if ("true".equals(enabledOnUnlockOnly) && _isLocked(content)) 127 { 128 Map<String, Object> contentParams = getContentDefaultParameters (content); 129 contentParams.put("description", _getLockedDescription(content)); 130 131 @SuppressWarnings("unchecked") 132 List<Map<String, Object>> lockedContents = (List<Map<String, Object>>) results.get("locked-contents"); 133 lockedContents.add(contentParams); 134 135 error = true; 136 } 137 138 // Has right correct 139 String enabledOnRightOnly = (String) this._script.getParameters().get("enabled-on-right-only"); 140 if ("true".equals(enabledOnRightOnly) && !_hasRight(content)) 141 { 142 Map<String, Object> contentParams = getContentDefaultParameters (content); 143 contentParams.put("description", _getNoRightDescription (content)); 144 145 @SuppressWarnings("unchecked") 146 List<Map<String, Object>> norightContents = (List<Map<String, Object>>) results.get("noright-contents"); 147 norightContents.add(contentParams); 148 149 error = true; 150 } 151 152 // Is workflow action correct 153 String enabledOnWorkflowActionOnly = (String) this._script.getParameters().get("enabled-on-workflow-action-only"); 154 if (enabledOnWorkflowActionOnly != null) 155 { 156 int actionId = _workflowAction(content); 157 if (actionId == -1) 158 { 159 Map<String, Object> contentParams = getContentDefaultParameters (content); 160 contentParams.put("description", _getWorkflowActionUnvailableDescription(content)); 161 162 @SuppressWarnings("unchecked") 163 List<Map<String, Object>> invalidActionContents = (List<Map<String, Object>>) results.get("invalidworkflowaction-contents"); 164 invalidActionContents.add(contentParams); 165 166 error = true; 167 } 168 else 169 { 170 results.put("workflowaction-content-actionId", actionId); 171 } 172 } 173 174 // Is workflow step correct 175 String enabledOnWorkflowStepOnly = (String) this._script.getParameters().get("enabled-on-workflow-step-only"); 176 if (enabledOnWorkflowStepOnly != null && !_isWorkflowStepCorrect(content)) 177 { 178 Map<String, Object> contentParams = getContentDefaultParameters (content); 179 contentParams.put("description", _getIncorrectWorkflowStepDescription(content)); 180 181 @SuppressWarnings("unchecked") 182 List<Map<String, Object>> invalidStepContents = (List<Map<String, Object>>) results.get("invalidworkflowstep-contents"); 183 invalidStepContents.add(contentParams); 184 185 error = true; 186 } 187 188 if (_isAllRight (content, error, results)) 189 { 190 Map<String, Object> contentParams = getContentDefaultParameters (content); 191 contentParams.put("description", _getAllRightDescription(content)); 192 193 @SuppressWarnings("unchecked") 194 List<Map<String, Object>> allrightContents = (List<Map<String, Object>>) results.get("allright-contents"); 195 allrightContents.add(contentParams); 196 } 197 } 198 } 199 200 return results; 201 } 202 203 /** 204 * Determines if the user can finally do action on content 205 * @param content The content 206 * @param hasError true if a error has already occurs 207 * @param results The result parameters to be passed to client side 208 * @return true if the user can finally do action on content 209 */ 210 protected boolean _isAllRight (Content content, boolean hasError, Map<String, Object> results) 211 { 212 return !hasError; 213 } 214 215 216 /** 217 * Get the default content's parameters 218 * @param content The content 219 * @return The default parameters 220 */ 221 protected Map<String, Object> getContentDefaultParameters (Content content) 222 { 223 Map<String, Object> contentParams = new HashMap<>(); 224 contentParams.put("id", content.getId()); 225 contentParams.put("title", content.getTitle()); 226 227 return contentParams; 228 } 229 230 /** 231 * Determines if the content is locked 232 * @param content the content 233 * @return true if the content is locked 234 */ 235 protected boolean _isLocked(Content content) 236 { 237 if (content instanceof LockableAmetysObject) 238 { 239 LockableAmetysObject lockableContent = (LockableAmetysObject) content; 240 if (lockableContent.isLocked() && !LockHelper.isLockOwner(lockableContent, _currentUserProvider.getUser())) 241 { 242 return true; 243 } 244 } 245 246 return false; 247 } 248 249 /** 250 * Determines if the content is modifiable 251 * @param content the content 252 * @return true if the content is modifiable 253 */ 254 protected boolean _isModifiable(Content content) 255 { 256 return content instanceof ModifiableContent; 257 } 258 259 /** 260 * Determines if the user has sufficient right for the given content 261 * @param content the content 262 * @return true if user has sufficient right 263 */ 264 protected boolean _hasRight (Content content) 265 { 266 if (_rights.isEmpty()) 267 { 268 return true; 269 } 270 271 Set<String> rightsToCheck = _rights.keySet(); 272 for (String rightToCheck : rightsToCheck) 273 { 274 if (StringUtils.isNotEmpty(rightToCheck)) 275 { 276 if (_rightManager.hasRight(_userProvider.getUser(), rightToCheck, content) == RightResult.RIGHT_ALLOW) 277 { 278 return true; 279 } 280 } 281 } 282 283 return false; 284 } 285 286 /** 287 * Determines if the workflow action is correct 288 * @param content the content 289 * @return true if the workflow action is incorrect 290 */ 291 protected int _workflowAction(Content content) 292 { 293 WorkflowAwareContent waContent = (WorkflowAwareContent) content; 294 AmetysObjectWorkflow workflow = _workflowProvider.getAmetysObjectWorkflow(waContent); 295 long wId = waContent.getWorkflowId(); 296 297 Map<String, Object> vars = new HashMap<>(); 298 vars.put(AbstractContentWorkflowComponent.CONTENT_KEY, content); 299 vars.put(AbstractWorkflowComponent.FAIL_CONDITIONS_KEY, new ArrayList<String>()); 300 int[] actionIds = workflow.getAvailableActions(wId, vars); 301 Arrays.sort(actionIds); 302 303 String correctActionsAsString = this._script.getParameters().get("enabled-on-workflow-action-only").toString(); 304 String[] correctActionsAsStringArray = correctActionsAsString.split(", ?"); 305 306 int correctActionId = -1; 307 for (String correctActionAsString : correctActionsAsStringArray) 308 { 309 int actionId = Integer.parseInt(correctActionAsString); 310 if (!StringUtils.isEmpty(correctActionAsString) && Arrays.binarySearch(actionIds, actionId) >= 0) 311 { 312 correctActionId = actionId; 313 break; 314 } 315 } 316 317 return correctActionId; 318 } 319 320 321 /** 322 * Determines if the workflow step is correct 323 * @param content the content 324 * @return true if the workflow step is incorrect 325 */ 326 protected boolean _isWorkflowStepCorrect(Content content) 327 { 328 WorkflowAwareContent waContent = (WorkflowAwareContent) content; 329 AmetysObjectWorkflow workflow = _workflowProvider.getAmetysObjectWorkflow(waContent); 330 long wId = waContent.getWorkflowId(); 331 332 List<Step> steps = workflow.getCurrentSteps(wId); 333 334 List<Integer> stepIds = new ArrayList<>(); 335 for (Step step : steps) 336 { 337 stepIds.add(step.getStepId()); 338 } 339 340 String correctStepsAsString = this._script.getParameters().get("enabled-on-workflow-step-only").toString(); 341 String[] correctStepsAsStringArray = correctStepsAsString.split(", ?"); 342 343 for (String correctStepAsString : correctStepsAsStringArray) 344 { 345 if (!StringUtils.isEmpty(correctStepAsString) && stepIds.contains(Integer.parseInt(correctStepAsString))) 346 { 347 return true; 348 } 349 } 350 351 return false; 352 } 353 354 /** 355 * Get i18n description when user can not do action because he has no right on content 356 * @param content The content 357 * @return The {@link I18nizableText} description 358 */ 359 protected I18nizableText _getNoRightDescription (Content content) 360 { 361 List<String> rightI18nParameters = new ArrayList<>(); 362 rightI18nParameters.add(content.getTitle()); 363 364 I18nizableText ed = (I18nizableText) this._script.getParameters().get("noright-content-description"); 365 return new I18nizableText(ed.getCatalogue(), ed.getKey(), rightI18nParameters); 366 } 367 368 /** 369 * Get i18n description when user can not do action because the content is locked 370 * @param content The content 371 * @return The {@link I18nizableText} description 372 */ 373 protected I18nizableText _getLockedDescription (Content content) 374 { 375 UserIdentity currentLockerIdentity = ((LockableAmetysObject) content).getLockOwner(); 376 User currentLocker = currentLockerIdentity != null ? _userManager.getUser(currentLockerIdentity.getPopulationId(), currentLockerIdentity.getLogin()) : null; 377 378 List<String> lockI18nParameters = new ArrayList<>(); 379 lockI18nParameters.add(content.getTitle()); 380 lockI18nParameters.add(currentLocker != null ? currentLocker.getFullName() : ""); 381 lockI18nParameters.add(currentLockerIdentity != null ? currentLockerIdentity.getLogin() : "Anonymous"); 382 383 I18nizableText ed = (I18nizableText) this._script.getParameters().get("locked-content-description"); 384 return new I18nizableText(ed.getCatalogue(), ed.getKey(), lockI18nParameters); 385 } 386 387 /** 388 * Get i18n description when user can not do action because there is no workflow action unvailable 389 * @param content The content 390 * @return The {@link I18nizableText} description 391 */ 392 protected I18nizableText _getWorkflowActionUnvailableDescription (Content content) 393 { 394 List<String> workflowI18nParameters = new ArrayList<>(); 395 workflowI18nParameters.add(content.getTitle()); 396 397 I18nizableText ed = (I18nizableText) this._script.getParameters().get("workflowaction-content-description"); 398 return new I18nizableText(ed.getCatalogue(), ed.getKey(), workflowI18nParameters); 399 } 400 401 /** 402 * Get i18n description when user can not do action because the workflow step is incorrect 403 * @param content The content 404 * @return The {@link I18nizableText} description 405 */ 406 protected I18nizableText _getIncorrectWorkflowStepDescription (Content content) 407 { 408 List<String> workflowI18nParameters = new ArrayList<>(); 409 workflowI18nParameters.add(content.getTitle()); 410 411 I18nizableText ed = (I18nizableText) this._script.getParameters().get("workflowstep-content-description"); 412 return new I18nizableText(ed.getCatalogue(), ed.getKey(), workflowI18nParameters); 413 } 414 415 /** 416 * Get i18n description when user can not do action because the content is not modifiable 417 * @param content The content 418 * @return The {@link I18nizableText} description 419 */ 420 protected I18nizableText _getNoModifiableDescription (Content content) 421 { 422 List<String> modifiableI18nParameters = new ArrayList<>(); 423 modifiableI18nParameters.add(content.getTitle()); 424 425 I18nizableText ed = (I18nizableText) this._script.getParameters().get("nomodifiable-content-description"); 426 return new I18nizableText(ed.getCatalogue(), ed.getKey(), modifiableI18nParameters); 427 } 428 429 /** 430 * Get i18n description when user can do action 431 * @param content The content 432 * @return The {@link I18nizableText} description 433 */ 434 protected I18nizableText _getAllRightDescription (Content content) 435 { 436 List<String> allrightI18nParameters = new ArrayList<>(); 437 allrightI18nParameters.add(content.getTitle()); 438 439 I18nizableText ed = (I18nizableText) this._script.getParameters().get("allright-content-description"); 440 return new I18nizableText(ed.getCatalogue(), ed.getKey(), allrightI18nParameters); 441 } 442}