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.workflow; 017 018import java.util.ArrayList; 019import java.util.Arrays; 020import java.util.Collection; 021import java.util.Collections; 022import java.util.Comparator; 023import java.util.HashSet; 024import java.util.Iterator; 025import java.util.LinkedHashMap; 026import java.util.List; 027import java.util.Map; 028import java.util.Set; 029 030import javax.jcr.RepositoryException; 031 032import org.apache.avalon.framework.component.Component; 033import org.apache.avalon.framework.configuration.Configurable; 034import org.apache.avalon.framework.configuration.Configuration; 035import org.apache.avalon.framework.configuration.ConfigurationException; 036import org.apache.avalon.framework.logger.AbstractLogEnabled; 037import org.apache.avalon.framework.service.ServiceException; 038import org.apache.avalon.framework.service.ServiceManager; 039import org.apache.avalon.framework.service.Serviceable; 040import org.apache.avalon.framework.thread.ThreadSafe; 041import org.apache.cocoon.xml.AttributesImpl; 042import org.apache.cocoon.xml.XMLUtils; 043import org.apache.commons.lang3.StringUtils; 044import org.xml.sax.ContentHandler; 045import org.xml.sax.SAXException; 046 047import org.ametys.cms.content.ContentHelper; 048import org.ametys.cms.contenttype.ContentTypeExtensionPoint; 049import org.ametys.cms.contenttype.ContentTypesHelper; 050import org.ametys.cms.languages.Language; 051import org.ametys.cms.languages.LanguagesManager; 052import org.ametys.cms.repository.Content; 053import org.ametys.cms.repository.DefaultContent; 054import org.ametys.cms.repository.WorkflowAwareContent; 055import org.ametys.cms.repository.WorkflowStepExpression; 056import org.ametys.core.right.RightManager; 057import org.ametys.core.right.RightManager.RightResult; 058import org.ametys.core.user.User; 059import org.ametys.core.user.UserIdentity; 060import org.ametys.core.user.UserManager; 061import org.ametys.core.util.DateUtils; 062import org.ametys.plugins.repository.AmetysObjectIterable; 063import org.ametys.plugins.repository.AmetysObjectResolver; 064import org.ametys.plugins.repository.AmetysRepositoryException; 065import org.ametys.plugins.repository.RepositoryConstants; 066import org.ametys.plugins.repository.query.SortCriteria; 067import org.ametys.plugins.repository.query.expression.AndExpression; 068import org.ametys.plugins.repository.query.expression.Expression; 069import org.ametys.plugins.repository.query.expression.Expression.Operator; 070import org.ametys.plugins.repository.query.expression.ExpressionContext; 071import org.ametys.plugins.repository.query.expression.OrExpression; 072import org.ametys.plugins.repository.query.expression.UserExpression; 073import org.ametys.plugins.workflow.support.WorkflowProvider; 074import org.ametys.plugins.workflow.support.WorkflowProvider.AmetysObjectWorkflow; 075import org.ametys.runtime.i18n.I18nizableText; 076 077import com.opensymphony.workflow.loader.StepDescriptor; 078import com.opensymphony.workflow.loader.WorkflowDescriptor; 079import com.opensymphony.workflow.spi.Step; 080 081/** 082 * Component for saxing tasks specific to an user.<br> 083 * The algorithm is the following : 084 * <ul> 085 * <li>First, we get all granted sites for the user with the right manager. 086 * <li>If there is at least one site allowed, we get all workflows 087 * associated with the granted sites. 088 * <li>Then for each step of each task from the configuration, we get 089 * all workflows where this step is in current steps and where 090 * the workflow is contains in the previous list. 091 * <li>For each workflow matching the previous conditions we 092 * test if the user has all the rights associated with the step (from 093 * the configuration) and then we get the content from this workflow. 094 * <li>Finally, for each content, we sax its first page in order to access 095 * it directly from an URL. 096 * </ul> 097 */ 098public class WorkflowTasksComponent extends AbstractLogEnabled implements Component, ThreadSafe, Configurable, Serviceable 099{ 100 /** The avalon role. */ 101 public static final String ROLE = WorkflowTasksComponent.class.getName(); 102 103 /** The ametys object resolver. */ 104 protected AmetysObjectResolver _objectResolver; 105 106 /** The rights manager. */ 107 protected RightManager _rightManager; 108 109 /** The users manager. */ 110 protected UserManager _userManager; 111 112 /** The content type extension point. */ 113 protected ContentTypeExtensionPoint _cTypeEP; 114 115 /** Helper for content types */ 116 protected ContentTypesHelper _contentTypesHelper; 117 118 /** The {@link WorkflowProvider} */ 119 protected WorkflowProvider _workflowProvider; 120 121 /** The content helper */ 122 protected ContentHelper _contentHelper; 123 124 /** The languages manager */ 125 protected LanguagesManager _languagesManager; 126 127 /** The configured task map, indexed by id. */ 128 protected Map<String, Task> _tasks; 129 130 /** Allow user querying ? */ 131 protected boolean _allowUserQuery; 132 133 @Override 134 public void service(ServiceManager manager) throws ServiceException 135 { 136 _objectResolver = (AmetysObjectResolver) manager.lookup(AmetysObjectResolver.ROLE); 137 _rightManager = (RightManager) manager.lookup(RightManager.ROLE); 138 _userManager = (UserManager) manager.lookup(UserManager.ROLE); 139 _cTypeEP = (ContentTypeExtensionPoint) manager.lookup(ContentTypeExtensionPoint.ROLE); 140 _contentTypesHelper = (ContentTypesHelper) manager.lookup(ContentTypesHelper.ROLE); 141 _workflowProvider = (WorkflowProvider) manager.lookup(WorkflowProvider.ROLE); 142 _languagesManager = (LanguagesManager) manager.lookup(LanguagesManager.ROLE); 143 _contentHelper = (ContentHelper) manager.lookup(ContentHelper.ROLE); 144 } 145 146 /** 147 * Configure the tasks and steps handled by this component.<br> 148 * Need the following configuration:<br> 149 * <tasks><br> 150 *   <task label="i18n-label"><br> 151 *     <step id="3" rights="proposal,validation"><br> 152 *     <step id="4" rights="validation"><br> 153 *     ...<br> 154 *   </task><br> 155 *   ...<br> 156 * </tasks><br> 157 * @param configuration The configuration as described above. 158 * @throws ConfigurationException If the configuration is invalid. 159 */ 160 public void configure(Configuration configuration) throws ConfigurationException 161 { 162 Configuration tasksConf = configuration.getChild("tasks"); 163 _tasks = _configureTasks(tasksConf); 164 _allowUserQuery = tasksConf.getAttributeAsBoolean("allowUserQuery", false); 165 } 166 167 /** 168 * Allow user query ? 169 * @return true if allowed user querying, false otherwise. 170 */ 171 public boolean allowUserQuery() 172 { 173 return _allowUserQuery; 174 } 175 176 /** 177 * SAX the contents for given user 178 * @param ch the content handler to SAX into 179 * @param user the user 180 * @throws SAXException If an error occurred 181 */ 182 public void toSAX(ContentHandler ch, User user) throws SAXException 183 { 184 XMLUtils.startElement(ch, "tasks"); 185 186 long start = System.currentTimeMillis(); 187 188 // Get the workflow corresponding to the configuration. 189 Map<Task, Collection<Content>> workflows = _getCorrespondingWorkflows(user); 190 191 long wfEnd = System.currentTimeMillis(); 192 getLogger().info("Contents retrieved in " + (wfEnd - start) + "ms."); 193 194 for (Task task : workflows.keySet()) 195 { 196 _saxTask(ch, task, workflows.get(task)); 197 } 198 199 long end = System.currentTimeMillis(); 200 201 // Display performance indicators. 202 AttributesImpl attrs = new AttributesImpl(); 203 204 attrs.addCDATAAttribute("Total", Long.toString(end - start)); 205 attrs.addCDATAAttribute("WF", Long.toString(wfEnd - start)); 206 attrs.addCDATAAttribute("SAX", Long.toString(end - wfEnd)); 207 208 XMLUtils.createElement(ch, "render", attrs); 209 210 XMLUtils.endElement(ch, "tasks"); 211 } 212 213 /** 214 * SAX the contents for given user and task 215 * @param ch the content handler to SAX into 216 * @param user the user 217 * @param taskId the task id 218 * @throws SAXException If an error occurred 219 */ 220 public void toSAX(ContentHandler ch, User user, String taskId) throws SAXException 221 { 222 Task task = _tasks.get(taskId); 223 224 Collection<Content> getTaskContents = _getTaskContents (user, task); 225 _saxTask(ch, task, getTaskContents); 226 } 227 228 /** 229 * Get the list of tasks managed by this component. 230 * @return the task list. 231 */ 232 public Map<String, Task> getTasks() 233 { 234 return Collections.unmodifiableMap(_tasks); 235 } 236 237 /** 238 * Get the list of contents for a given user and task. 239 * @param user the user. 240 * @param taskId the task ID. 241 * @param limit the maximum number of results, 0 for all. 242 * @return the contents as an iterable collection of contents. 243 * @throws AmetysRepositoryException If an error occurred 244 */ 245 public Collection<Content> getContents(User user, String taskId, int limit) throws AmetysRepositoryException 246 { 247 if (_tasks.containsKey(taskId)) 248 { 249 Task task = _tasks.get(taskId); 250 251 return _getTaskContents(user, task, limit); 252 } 253 else 254 { 255 return Collections.emptyList(); 256 } 257 } 258 259 /** 260 * SAX a task 261 * @param ch the content handler to SAX into 262 * @param task the task to SAX 263 * @param contents the contents 264 * @throws SAXException if an error occurred while SAXing 265 */ 266 protected void _saxTask (ContentHandler ch, Task task, Collection<Content> contents) throws SAXException 267 { 268 AttributesImpl taskAttr = new AttributesImpl(); 269 taskAttr.addCDATAAttribute("id", task.getId()); 270 _saxAdditionalAttributes (task, taskAttr); 271 272 XMLUtils.startElement(ch, "task", taskAttr); 273 274 task.getLabel().toSAX(ch, "label"); 275 276 for (Content content : contents) 277 { 278 // Generate the first page on which the user has all the rights. 279 try 280 { 281 _saxContent(ch, content, task); 282 } 283 catch (RepositoryException e) 284 { 285 throw new SAXException("Error trying to generate a content.", e); 286 } 287 } 288 289 XMLUtils.endElement(ch, "task"); 290 } 291 292 /** 293 * SAX additional attributes 294 * @param task the task 295 * @param attrs the attributes 296 * @throws SAXException If an error occurred 297 */ 298 protected void _saxAdditionalAttributes (Task task, AttributesImpl attrs) throws SAXException 299 { 300 // Nothing 301 } 302 303 /** 304 * SAX a content. 305 * @param ch the content handler. 306 * @param content the content to sax. 307 * @param task the task 308 * @throws SAXException If an error occurred 309 * @throws RepositoryException If an error occurred 310 */ 311 protected void _saxContent(ContentHandler ch, Content content, Task task) throws SAXException, RepositoryException 312 { 313 UserIdentity userIdentity = content.getLastContributor(); 314 User authorUser = _userManager.getUser(userIdentity.getPopulationId(), userIdentity.getLogin()); 315 316 AttributesImpl attrs = new AttributesImpl(); 317 attrs.addCDATAAttribute("id", content.getId()); 318 attrs.addCDATAAttribute("name", content.getName()); 319 attrs.addCDATAAttribute("title", _contentHelper.getTitle(content)); 320 attrs.addCDATAAttribute("lastModified", DateUtils.zonedDateTimeToString(content.getLastModified())); 321 attrs.addCDATAAttribute("contentType", StringUtils.join(content.getTypes(), ',')); 322 if (content.getLanguage() != null) 323 { 324 attrs.addCDATAAttribute("language", content.getLanguage()); 325 Language language = _languagesManager.getAvailableLanguages().get(content.getLanguage()); 326 if (language != null) 327 { 328 attrs.addCDATAAttribute("language-image", language.getSmallIcon()); 329 } 330 } 331 332 if (authorUser != null) 333 { 334 attrs.addCDATAAttribute("author", authorUser.getFullName()); 335 } 336 337 _saxAdditionalAttributes (content, task, attrs); 338 339 String iconGlyph = _contentTypesHelper.getIconGlyph(content); 340 if (iconGlyph != null) 341 { 342 attrs.addCDATAAttribute("iconGlyph", iconGlyph); 343 } 344 String iconDecorator = _contentTypesHelper.getIconDecorator(content); 345 if (iconDecorator != null) 346 { 347 attrs.addCDATAAttribute("iconDecorator", iconDecorator); 348 } 349 attrs.addCDATAAttribute("smallIcon", _contentTypesHelper.getSmallIcon(content)); 350 attrs.addCDATAAttribute("mediumIcon", _contentTypesHelper.getMediumIcon(content)); 351 attrs.addCDATAAttribute("largeIcon", _contentTypesHelper.getLargeIcon(content)); 352 353 XMLUtils.startElement(ch, "content", attrs); 354 if (content instanceof WorkflowAwareContent) 355 { 356 _saxContentCurrentState(ch, (WorkflowAwareContent) content); 357 } 358 _saxAdditionalData(ch, content, task); 359 XMLUtils.endElement(ch, "content"); 360 } 361 362 /** 363 * SAX additional attributes 364 * @param content the content 365 * @param task the task 366 * @param attrs the attributes 367 * @throws SAXException If an error occurred 368 */ 369 protected void _saxAdditionalAttributes (Content content, Task task, AttributesImpl attrs) throws SAXException 370 { 371 // Nothing 372 } 373 374 /** 375 * SAX additional data 376 * @param ch the content handler. 377 * @param content the content 378 * @param task the task 379 * @throws SAXException If an error occurred 380 */ 381 protected void _saxAdditionalData(ContentHandler ch, Content content, Task task) throws SAXException 382 { 383 // Nothing 384 } 385 386 /** 387 * Get the workflows for a user. 388 * @param user the user. 389 * @return the list of contents for each task. 390 */ 391 protected Map<Task, Collection<Content>> _getCorrespondingWorkflows(User user) 392 { 393 Map<Task, Collection<Content>> workflows = new LinkedHashMap<>(); 394 395 for (Task task : _tasks.values()) 396 { 397 workflows.put(task, _getTaskContents(user, task)); 398 } 399 400 return workflows; 401 } 402 403 /** 404 * Get a task's contents. 405 * @param user the user. 406 * @param task the task. 407 * @return the content collection. 408 */ 409 protected Collection<Content> _getTaskContents(User user, Task task) 410 { 411 List<Content> contents = new ArrayList<>(); 412 413 int length = task.getLength(); 414 int count = 0; 415 416 for (TaskStep step : task.getSteps()) 417 { 418 if (length != 0 && count == length) 419 { 420 break; 421 } 422 423 Set<String> rights = step.getRights(); 424 425 for (Content content : _getContents(step, user)) 426 { 427 if (_testContent(content, user.getIdentity(), rights)) 428 { 429 if (length != 0 && count == length) 430 { 431 break; 432 } 433 434 contents.add(content); 435 count++; 436 } 437 } 438 } 439 440 Collections.sort(contents, new LastModifiedDateComparator()); 441 442 return contents; 443 } 444 445 /** 446 * Get a task's contents. 447 * @param user the user. 448 * @param task the task. 449 * @param limit the maximum number of contents (0 to return all). 450 * @return the content collection. 451 */ 452 protected Collection<Content> _getTaskContents(User user, Task task, int limit) 453 { 454 List<Content> contents = new ArrayList<>(); 455 for (TaskStep step : task.getSteps()) 456 { 457 Set<String> rights = step.getRights(); 458 459 Iterator<Content> contentIt = _getContents(step, user).iterator(); 460 int stepContents = 0; 461 while (contentIt.hasNext() && (limit < 1 || stepContents < limit)) 462 { 463 Content content = contentIt.next(); 464 if (_testContent(content, user.getIdentity(), rights) && !contents.contains(content)) 465 { 466 contents.add(content); 467 stepContents++; 468 } 469 } 470 } 471 472 Collections.sort(contents, new LastModifiedDateComparator()); 473 474 if (limit > 0 && contents.size() > limit) 475 { 476 contents = contents.subList(0, limit); 477 } 478 479 return contents; 480 } 481 482 /** 483 * Get the contents for a step. 484 * @param step the step. 485 * @param user the user. 486 * @return an iterable collection of contents. 487 */ 488 protected AmetysObjectIterable<Content> _getContents(TaskStep step, User user) 489 { 490 List<Expression> exprs = _getContentsAndExpressions(step, user); 491 Expression contentExpr = new AndExpression(exprs.toArray(new Expression[exprs.size()])); 492 493 SortCriteria sort = new SortCriteria(); 494 sort.addJCRPropertyCriterion(RepositoryConstants.NAMESPACE_PREFIX_INTERNAL + ":" + DefaultContent.METADATA_MODIFIED, false, false); 495 String xpathQuery = org.ametys.plugins.repository.query.QueryHelper.getXPathQuery(null, "ametys:content", contentExpr, sort); 496 497 return _objectResolver.query(xpathQuery); 498 } 499 500 /** 501 * Get the AND expressions to request on contents 502 * @param step the step. 503 * @param user the user. 504 * @return The content's expressions 505 */ 506 protected List<Expression> _getContentsAndExpressions(TaskStep step, User user) 507 { 508 List<Expression> exprs = new ArrayList<>(); 509 510 List<Expression> stepExprs = new ArrayList<>(); 511 for (Integer stepId : step.getStepIds()) 512 { 513 stepExprs.add(new WorkflowStepExpression(Operator.EQ, stepId)); 514 } 515 if (stepExprs.size() > 0) 516 { 517 exprs.add(new OrExpression(stepExprs.toArray(new Expression[stepExprs.size()]))); 518 } 519 520 if (step.getUserContentsOnly()) 521 { 522 exprs.add(new UserExpression(DefaultContent.METADATA_CONTRIBUTOR, Operator.EQ, user.getIdentity(), false, ExpressionContext.newInstance().withInternal(true))); 523 } 524 525 return exprs; 526 } 527 528 /** 529 * Test if the content is valid. 530 * @param content the content to test. 531 * @param user the user. 532 * @param rights the rights to test. 533 * @return true/false. 534 */ 535 protected boolean _testContent(Content content, UserIdentity user, Set<String> rights) 536 { 537 // Supposer que nous avons tous les droits 538 boolean hasAllRights = true; 539 540 Iterator<String> itRights = rights.iterator(); 541 542 // VĂ©rifier que nous avons tous les droits 543 while (itRights.hasNext() && hasAllRights) 544 { 545 String right = itRights.next(); 546 547 hasAllRights &= _rightManager.hasRight(user, right, content) == RightResult.RIGHT_ALLOW; 548 } 549 550 return hasAllRights; 551 } 552 553 /** 554 * Configure the tasks. 555 * @param tasksConf the tasks root configuration. 556 * @return the task list. 557 * @throws ConfigurationException if a configuration error occurs. 558 */ 559 protected Map<String, Task> _configureTasks(Configuration tasksConf) throws ConfigurationException 560 { 561 Map<String, Task> tasks = new LinkedHashMap<>(); 562 for (Configuration taskConf : tasksConf.getChildren("task")) 563 { 564 Task task = new Task(); 565 566 String taskId = taskConf.getAttribute("id", "").trim(); 567 568 task.setId(taskId); 569 task.setLabel(new I18nizableText(null, taskConf.getAttribute("label", "").trim())); 570 571 int length = taskConf.getAttributeAsInteger("length", 0); 572 task.setLength(length); 573 574 _configureStep(task, taskConf); 575 576 _configureAdditional(task, taskConf); 577 578 tasks.put(taskId, task); 579 } 580 return tasks; 581 } 582 583 /** 584 * Configure the steps. 585 * @param task the current task 586 * @param taskConf the task configuration. 587 * @throws ConfigurationException if a configuration error occurs. 588 */ 589 protected void _configureStep(Task task, Configuration taskConf) throws ConfigurationException 590 { 591 List<TaskStep> steps = new ArrayList<>(); 592 for (Configuration stepConf : taskConf.getChildren("step")) 593 { 594 TaskStep step = new TaskStep(); 595 596 String[] rightArray = StringUtils.split(stepConf.getAttribute("rights", ""), ", "); 597 step.setStepIds(_configureStepIds(stepConf)); 598 step.setUserContents(stepConf.getAttributeAsBoolean("userContents", false)); 599 step.setRights(new HashSet<>(Arrays.asList(rightArray))); 600 601 steps.add(step); 602 } 603 604 task.setSteps(steps); 605 } 606 607 /** 608 * Configure additional configuration 609 * @param task task the current task 610 * @param taskConf the task configuration. 611 * @throws ConfigurationException if a configuration error occurs. 612 */ 613 protected void _configureAdditional(Task task, Configuration taskConf) throws ConfigurationException 614 { 615 // Nothing 616 } 617 618 /** 619 * Configure the step IDs. 620 * @param stepConf the step configuration. 621 * @return the step IDs as a Set of Integer. 622 * @throws ConfigurationException If an error occurred 623 */ 624 protected Set<Integer> _configureStepIds(Configuration stepConf) throws ConfigurationException 625 { 626 String[] stepIdArray = StringUtils.split(stepConf.getAttribute("id", ""), ", "); 627 Set<Integer> stepIds = new HashSet<>(stepIdArray.length); 628 for (String stepId : stepIdArray) 629 { 630 try 631 { 632 stepIds.add(Integer.valueOf(stepId)); 633 } 634 catch (NumberFormatException e) 635 { 636 // Ignore. 637 } 638 } 639 return stepIds; 640 } 641 642 /** 643 * SAX workflow current step of a {@link Content} 644 * @param ch the content handler. 645 * @param content The content 646 * @throws SAXException if an error occurs while SAXing 647 * @throws RepositoryException if an error occurs while retrieving current step 648 */ 649 protected void _saxContentCurrentState(ContentHandler ch, WorkflowAwareContent content) throws SAXException, RepositoryException 650 { 651 int currentStepId = 0; 652 653 AmetysObjectWorkflow workflow = _workflowProvider.getAmetysObjectWorkflow(content); 654 long workflowId = content.getWorkflowId(); 655 656 List<Step> steps = workflow.getCurrentSteps(workflowId); 657 if (!steps.isEmpty()) 658 { 659 currentStepId = steps.get(steps.size() - 1).getStepId(); 660 } 661 662 String workflowName = workflow.getWorkflowName(workflowId); 663 WorkflowDescriptor workflowDescriptor = workflow.getWorkflowDescriptor(workflowName); 664 StepDescriptor stepDescriptor = workflowDescriptor.getStep(currentStepId); 665 666 I18nizableText workflowStepName = new I18nizableText("application", stepDescriptor.getName()); 667 668 AttributesImpl attr = new AttributesImpl(); 669 attr.addCDATAAttribute("id", Integer.toString(currentStepId)); 670 671 XMLUtils.startElement(ch, "workflow-step", attr); 672 workflowStepName.toSAX(ch); 673 XMLUtils.endElement(ch, "workflow-step"); 674 675 String[] icons = new String[]{"-small", "-medium", "-large"}; 676 for (String icon : icons) 677 { 678 if ("application".equals(workflowStepName.getCatalogue())) 679 { 680 XMLUtils.createElement(ch, "workflow-icon" + icon, "/plugins/cms/resources_workflow/" + workflowStepName.getKey() + icon + ".png"); 681 } 682 else 683 { 684 String pluginName = workflowStepName.getCatalogue().substring("plugin.".length()); 685 XMLUtils.createElement(ch, "workflow-icon" + icon, "/plugins/" + pluginName + "/resources/img/workflow/" + workflowStepName.getKey() + icon + ".png"); 686 } 687 } 688 } 689 690 /** 691 * Class representing a task. 692 */ 693 public class Task 694 { 695 /** The task ID. */ 696 protected String _id; 697 698 /** The task label. */ 699 protected I18nizableText _label; 700 701 /** The task step configuration. */ 702 protected Collection<TaskStep> _steps; 703 704 /** The max number of result */ 705 protected int _length; 706 707 /** 708 * Class representing a task. 709 */ 710 public Task() 711 { 712 this(new I18nizableText(""), new ArrayList<>()); 713 } 714 715 /** 716 * Class representing a task. 717 * @param label The label of the task 718 * @param steps The steps of the task 719 */ 720 public Task(I18nizableText label, Collection<TaskStep> steps) 721 { 722 _label = label; 723 _steps = new ArrayList<>(steps); 724 } 725 726 /** 727 * Get the id. 728 * @return the id 729 */ 730 public String getId() 731 { 732 return _id; 733 } 734 735 /** 736 * Set the id. 737 * @param id the id to set 738 */ 739 public void setId(String id) 740 { 741 this._id = id; 742 } 743 744 /** 745 * Get the length. 746 * @return the length 747 */ 748 public int getLength() 749 { 750 return _length; 751 } 752 753 /** 754 * Set the length. 755 * @param length the length to set 756 */ 757 public void setLength(int length) 758 { 759 this._length = length; 760 } 761 762 /** 763 * Get the label. 764 * @return the label 765 */ 766 public I18nizableText getLabel() 767 { 768 return _label; 769 } 770 771 /** 772 * Set the label. 773 * @param label the label to set 774 */ 775 public void setLabel(I18nizableText label) 776 { 777 this._label = label; 778 } 779 780 /** 781 * Get the steps. 782 * @return the steps 783 */ 784 public Collection<TaskStep> getSteps() 785 { 786 return _steps; 787 } 788 789 /** 790 * Set the steps. 791 * @param steps the steps to set 792 */ 793 public void setSteps(Collection<TaskStep> steps) 794 { 795 this._steps = steps; 796 } 797 } 798 799 /** 800 * Class representing a task step. 801 */ 802 public class TaskStep 803 { 804 805 /** The step ID. */ 806 protected Set<Integer> _stepIds; 807 808 /** Only return user contents. */ 809 protected boolean _userContents; 810 811 /** The rights to have. */ 812 protected Set<String> _rights; 813 814 /** 815 * Construct a TaskStep object. 816 */ 817 public TaskStep() 818 { 819 this(new HashSet<>(), false, new HashSet<>()); 820 } 821 822 /** 823 * Construct a TaskStep object with parameters. 824 * @param stepIds the step ID. 825 * @param userContents the user contents. 826 * @param rights the rights. 827 */ 828 public TaskStep(Set<Integer> stepIds, boolean userContents, Set<String> rights) 829 { 830 super(); 831 _stepIds = stepIds; 832 _userContents = userContents; 833 _rights = new HashSet<>(rights); 834 } 835 836 /** 837 * Get the stepId. 838 * @return the stepId 839 */ 840 public Set<Integer> getStepIds() 841 { 842 return _stepIds; 843 } 844 845 /** 846 * Set the stepId. 847 * @param stepIds the stepId to set 848 */ 849 public void setStepIds(Set<Integer> stepIds) 850 { 851 this._stepIds = stepIds; 852 } 853 854 /** 855 * Get the userContents. 856 * @return the userContents 857 */ 858 public boolean getUserContentsOnly() 859 { 860 return _userContents; 861 } 862 863 /** 864 * Set the userContents. 865 * @param userContents the userContents to set 866 */ 867 public void setUserContents(boolean userContents) 868 { 869 this._userContents = userContents; 870 } 871 872 /** 873 * Get the rights. 874 * @return the rights 875 */ 876 public Set<String> getRights() 877 { 878 return _rights; 879 } 880 881 /** 882 * Set the rights. 883 * @param rights the rights to set 884 */ 885 public void setRights(Set<String> rights) 886 { 887 this._rights = rights; 888 } 889 890 } 891 892 /** 893 * Compares two contents on the last modified date. 894 */ 895 public class LastModifiedDateComparator implements Comparator<Content> 896 { 897 @Override 898 public int compare(Content c1, Content c2) 899 { 900 // The content is before if 901 return c2.getLastModified().compareTo(c1.getLastModified()); 902 } 903 } 904}