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.StringExpression; 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); 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 * @throws SAXException If an error occurred 306 * @throws RepositoryException If an error occurred 307 */ 308 protected void _saxContent(ContentHandler ch, Content content) throws SAXException, RepositoryException 309 { 310 UserIdentity userIdentity = content.getLastContributor(); 311 User authorUser = _userManager.getUser(userIdentity.getPopulationId(), userIdentity.getLogin()); 312 313 AttributesImpl attrs = new AttributesImpl(); 314 attrs.addCDATAAttribute("id", content.getId()); 315 attrs.addCDATAAttribute("name", content.getName()); 316 attrs.addCDATAAttribute("title", _contentHelper.getTitle(content)); 317 attrs.addCDATAAttribute("lastModified", DateUtils.dateToString(content.getLastModified())); 318 attrs.addCDATAAttribute("contentType", StringUtils.join(content.getTypes(), ',')); 319 if (content.getLanguage() != null) 320 { 321 attrs.addCDATAAttribute("language", content.getLanguage()); 322 Language language = _languagesManager.getAvailableLanguages().get(content.getLanguage()); 323 if (language != null) 324 { 325 attrs.addCDATAAttribute("language-image", language.getSmallIcon()); 326 } 327 } 328 329 if (authorUser != null) 330 { 331 attrs.addCDATAAttribute("author", authorUser.getFullName()); 332 } 333 334 _saxAdditionalAttributes (content, attrs); 335 336 String iconGlyph = _contentTypesHelper.getIconGlyph(content); 337 if (iconGlyph != null) 338 { 339 attrs.addCDATAAttribute("iconGlyph", iconGlyph); 340 } 341 String iconDecorator = _contentTypesHelper.getIconDecorator(content); 342 if (iconDecorator != null) 343 { 344 attrs.addCDATAAttribute("iconDecorator", iconDecorator); 345 } 346 attrs.addCDATAAttribute("smallIcon", _contentTypesHelper.getSmallIcon(content)); 347 attrs.addCDATAAttribute("mediumIcon", _contentTypesHelper.getMediumIcon(content)); 348 attrs.addCDATAAttribute("largeIcon", _contentTypesHelper.getLargeIcon(content)); 349 350 XMLUtils.startElement(ch, "content", attrs); 351 if (content instanceof WorkflowAwareContent) 352 { 353 _saxContentCurrentState(ch, (WorkflowAwareContent) content); 354 } 355 XMLUtils.endElement(ch, "content"); 356 } 357 358 /** 359 * SAX additional attributes 360 * @param content the content 361 * @param attrs the attributes 362 * @throws SAXException If an error occurred 363 */ 364 protected void _saxAdditionalAttributes (Content content, AttributesImpl attrs) throws SAXException 365 { 366 // Nothing 367 } 368 369 /** 370 * Get the workflows for a user. 371 * @param user the user. 372 * @return the list of contents for each task. 373 */ 374 protected Map<Task, Collection<Content>> _getCorrespondingWorkflows(User user) 375 { 376 Map<Task, Collection<Content>> workflows = new LinkedHashMap<>(); 377 378 for (Task task : _tasks.values()) 379 { 380 workflows.put(task, _getTaskContents(user, task)); 381 } 382 383 return workflows; 384 } 385 386 /** 387 * Get a task's contents. 388 * @param user the user. 389 * @param task the task. 390 * @return the content collection. 391 */ 392 protected Collection<Content> _getTaskContents(User user, Task task) 393 { 394 List<Content> contents = new ArrayList<>(); 395 396 int length = task.getLength(); 397 int count = 0; 398 399 for (TaskStep step : task.getSteps()) 400 { 401 if (length != 0 && count == length) 402 { 403 break; 404 } 405 406 Set<String> rights = step.getRights(); 407 408 for (Content content : _getContents(step, user)) 409 { 410 if (_testContent(content, user.getIdentity(), rights)) 411 { 412 if (length != 0 && count == length) 413 { 414 break; 415 } 416 417 contents.add(content); 418 count++; 419 } 420 } 421 } 422 423 Collections.sort(contents, new LastModifiedDateComparator()); 424 425 return contents; 426 } 427 428 /** 429 * Get a task's contents. 430 * @param user the user. 431 * @param task the task. 432 * @param limit the maximum number of contents (0 to return all). 433 * @return the content collection. 434 */ 435 protected Collection<Content> _getTaskContents(User user, Task task, int limit) 436 { 437 List<Content> contents = new ArrayList<>(); 438 for (TaskStep step : task.getSteps()) 439 { 440 Set<String> rights = step.getRights(); 441 442 Iterator<Content> contentIt = _getContents(step, user).iterator(); 443 int stepContents = 0; 444 while (contentIt.hasNext() && (limit < 1 || stepContents < limit)) 445 { 446 Content content = contentIt.next(); 447 if (_testContent(content, user.getIdentity(), rights) && !contents.contains(content)) 448 { 449 contents.add(content); 450 stepContents++; 451 } 452 } 453 } 454 455 Collections.sort(contents, new LastModifiedDateComparator()); 456 457 if (limit > 0 && contents.size() > limit) 458 { 459 contents = contents.subList(0, limit); 460 } 461 462 return contents; 463 } 464 465 /** 466 * Get the contents for a step. 467 * @param step the step. 468 * @param user the user. 469 * @return an iterable collection of contents. 470 */ 471 protected AmetysObjectIterable<Content> _getContents(TaskStep step, User user) 472 { 473 Expression expr = null; 474 Set<Integer> stepIds = step.getStepIds(); 475 for (Integer stepId : stepIds) 476 { 477 Expression stepExpr = new WorkflowStepExpression(Operator.EQ, stepId); 478 expr = expr == null ? stepExpr : new OrExpression(expr, stepExpr); 479 } 480 481 Expression contentExpr = null; 482 if (step.getUserContentsOnly()) 483 { 484 contentExpr = new StringExpression(DefaultContent.METADATA_CONTRIBUTOR, Operator.EQ, user.getIdentity().getLogin()); 485 expr = new AndExpression(expr, contentExpr); 486 } 487 488 SortCriteria sort = new SortCriteria(); 489 sort.addCriterion(DefaultContent.METADATA_MODIFIED, false, false); 490 String xpathQuery = org.ametys.plugins.repository.query.QueryHelper.getXPathQuery(null, "ametys:content", expr, sort); 491 492 return _objectResolver.query(xpathQuery); 493 } 494 495 /** 496 * Test if the content is valid. 497 * @param content the content to test. 498 * @param user the user. 499 * @param rights the rights to test. 500 * @return true/false. 501 */ 502 protected boolean _testContent(Content content, UserIdentity user, Set<String> rights) 503 { 504 // Supposer que nous avons tous les droits 505 boolean hasAllRights = true; 506 507 Iterator<String> itRights = rights.iterator(); 508 509 // VĂ©rifier que nous avons tous les droits 510 while (itRights.hasNext() && hasAllRights) 511 { 512 String right = itRights.next(); 513 514 hasAllRights &= _rightManager.hasRight(user, right, content) == RightResult.RIGHT_ALLOW; 515 } 516 517 return hasAllRights; 518 } 519 520 /** 521 * Configure the tasks. 522 * @param tasksConf the tasks root configuration. 523 * @return the task list. 524 * @throws ConfigurationException if a configuration error occurs. 525 */ 526 protected Map<String, Task> _configureTasks(Configuration tasksConf) throws ConfigurationException 527 { 528 Map<String, Task> tasks = new LinkedHashMap<>(); 529 for (Configuration taskConf : tasksConf.getChildren("task")) 530 { 531 Task task = new Task(); 532 533 String taskId = taskConf.getAttribute("id", "").trim(); 534 535 task.setId(taskId); 536 task.setLabel(new I18nizableText(null, taskConf.getAttribute("label", "").trim())); 537 538 int length = taskConf.getAttributeAsInteger("length", 0); 539 task.setLength(length); 540 541 List<TaskStep> steps = new ArrayList<>(); 542 for (Configuration stepConf : taskConf.getChildren("step")) 543 { 544 TaskStep step = new TaskStep(); 545 546 String[] rightArray = StringUtils.split(stepConf.getAttribute("rights", ""), ", "); 547 step.setStepIds(_configureStepIds(stepConf)); 548 step.setUserContents(stepConf.getAttributeAsBoolean("userContents", false)); 549 step.setRights(new HashSet<>(Arrays.asList(rightArray))); 550 551 steps.add(step); 552 } 553 554 task.setSteps(steps); 555 556 tasks.put(taskId, task); 557 } 558 return tasks; 559 } 560 561 /** 562 * Configure the step IDs. 563 * @param stepConf the step configuration. 564 * @return the step IDs as a Set of Integer. 565 * @throws ConfigurationException If an error occurred 566 */ 567 protected Set<Integer> _configureStepIds(Configuration stepConf) throws ConfigurationException 568 { 569 String[] stepIdArray = StringUtils.split(stepConf.getAttribute("id", ""), ", "); 570 Set<Integer> stepIds = new HashSet<>(stepIdArray.length); 571 for (String stepId : stepIdArray) 572 { 573 try 574 { 575 stepIds.add(new Integer(stepId)); 576 } 577 catch (NumberFormatException e) 578 { 579 // Ignore. 580 } 581 } 582 return stepIds; 583 } 584 585 /** 586 * SAX workflow current step of a {@link Content} 587 * @param ch the content handler. 588 * @param content The content 589 * @throws SAXException if an error occurs while SAXing 590 * @throws RepositoryException if an error occurs while retrieving current step 591 */ 592 protected void _saxContentCurrentState(ContentHandler ch, WorkflowAwareContent content) throws SAXException, RepositoryException 593 { 594 int currentStepId = 0; 595 596 AmetysObjectWorkflow workflow = _workflowProvider.getAmetysObjectWorkflow(content); 597 long workflowId = content.getWorkflowId(); 598 599 List<Step> steps = workflow.getCurrentSteps(workflowId); 600 if (!steps.isEmpty()) 601 { 602 currentStepId = steps.get(steps.size() - 1).getStepId(); 603 } 604 605 String workflowName = workflow.getWorkflowName(workflowId); 606 WorkflowDescriptor workflowDescriptor = workflow.getWorkflowDescriptor(workflowName); 607 StepDescriptor stepDescriptor = workflowDescriptor.getStep(currentStepId); 608 609 I18nizableText workflowStepName = new I18nizableText("application", stepDescriptor.getName()); 610 611 AttributesImpl attr = new AttributesImpl(); 612 attr.addCDATAAttribute("id", Integer.toString(currentStepId)); 613 614 XMLUtils.startElement(ch, "workflow-step", attr); 615 workflowStepName.toSAX(ch); 616 XMLUtils.endElement(ch, "workflow-step"); 617 618 String[] icons = new String[]{"-small", "-medium", "-large"}; 619 for (String icon : icons) 620 { 621 if ("application".equals(workflowStepName.getCatalogue())) 622 { 623 XMLUtils.createElement(ch, "workflow-icon" + icon, "/plugins/cms/resources_workflow/" + workflowStepName.getKey() + icon + ".png"); 624 } 625 else 626 { 627 String pluginName = workflowStepName.getCatalogue().substring("plugin.".length()); 628 XMLUtils.createElement(ch, "workflow-icon" + icon, "/plugins/" + pluginName + "/resources/img/workflow/" + workflowStepName.getKey() + icon + ".png"); 629 } 630 } 631 } 632 633 /** 634 * Class representing a task. 635 */ 636 public class Task 637 { 638 /** The task ID. */ 639 protected String _id; 640 641 /** The task label. */ 642 protected I18nizableText _label; 643 644 /** The task step configuration. */ 645 protected Collection<TaskStep> _steps; 646 647 /** The max number of result */ 648 protected int _length; 649 650 /** 651 * Class representing a task. 652 */ 653 public Task() 654 { 655 this(new I18nizableText(""), new ArrayList<TaskStep>()); 656 } 657 658 /** 659 * Class representing a task. 660 * @param label The label of the task 661 * @param steps The steps of the task 662 */ 663 public Task(I18nizableText label, Collection<TaskStep> steps) 664 { 665 _label = label; 666 _steps = new ArrayList<>(steps); 667 } 668 669 /** 670 * Get the id. 671 * @return the id 672 */ 673 public String getId() 674 { 675 return _id; 676 } 677 678 /** 679 * Set the id. 680 * @param id the id to set 681 */ 682 public void setId(String id) 683 { 684 this._id = id; 685 } 686 687 /** 688 * Get the length. 689 * @return the length 690 */ 691 public int getLength() 692 { 693 return _length; 694 } 695 696 /** 697 * Set the length. 698 * @param length the length to set 699 */ 700 public void setLength(int length) 701 { 702 this._length = length; 703 } 704 705 /** 706 * Get the label. 707 * @return the label 708 */ 709 public I18nizableText getLabel() 710 { 711 return _label; 712 } 713 714 /** 715 * Set the label. 716 * @param label the label to set 717 */ 718 public void setLabel(I18nizableText label) 719 { 720 this._label = label; 721 } 722 723 /** 724 * Get the steps. 725 * @return the steps 726 */ 727 public Collection<TaskStep> getSteps() 728 { 729 return _steps; 730 } 731 732 /** 733 * Set the steps. 734 * @param steps the steps to set 735 */ 736 public void setSteps(Collection<TaskStep> steps) 737 { 738 this._steps = steps; 739 } 740 } 741 742 /** 743 * Class representing a task step. 744 */ 745 public class TaskStep 746 { 747 748 /** The step ID. */ 749 protected Set<Integer> _stepIds; 750 751 /** Only return user contents. */ 752 protected boolean _userContents; 753 754 /** The rights to have. */ 755 protected Set<String> _rights; 756 757 /** 758 * Construct a TaskStep object. 759 */ 760 public TaskStep() 761 { 762 this(new HashSet<Integer>(), false, new HashSet<String>()); 763 } 764 765 /** 766 * Construct a TaskStep object with parameters. 767 * @param stepIds the step ID. 768 * @param userContents the user contents. 769 * @param rights the rights. 770 */ 771 public TaskStep(Set<Integer> stepIds, boolean userContents, Set<String> rights) 772 { 773 super(); 774 _stepIds = stepIds; 775 _userContents = userContents; 776 _rights = new HashSet<>(rights); 777 } 778 779 /** 780 * Get the stepId. 781 * @return the stepId 782 */ 783 public Set<Integer> getStepIds() 784 { 785 return _stepIds; 786 } 787 788 /** 789 * Set the stepId. 790 * @param stepIds the stepId to set 791 */ 792 public void setStepIds(Set<Integer> stepIds) 793 { 794 this._stepIds = stepIds; 795 } 796 797 /** 798 * Get the userContents. 799 * @return the userContents 800 */ 801 public boolean getUserContentsOnly() 802 { 803 return _userContents; 804 } 805 806 /** 807 * Set the userContents. 808 * @param userContents the userContents to set 809 */ 810 public void setUserContents(boolean userContents) 811 { 812 this._userContents = userContents; 813 } 814 815 /** 816 * Get the rights. 817 * @return the rights 818 */ 819 public Set<String> getRights() 820 { 821 return _rights; 822 } 823 824 /** 825 * Set the rights. 826 * @param rights the rights to set 827 */ 828 public void setRights(Set<String> rights) 829 { 830 this._rights = rights; 831 } 832 833 } 834 835 /** 836 * Compares two contents on the last modified date. 837 */ 838 public class LastModifiedDateComparator implements Comparator<Content> 839 { 840 @Override 841 public int compare(Content c1, Content c2) 842 { 843 // The content is before if 844 return c2.getLastModified().compareTo(c1.getLastModified()); 845 } 846 } 847}