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