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