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.content; 017 018import java.io.IOException; 019import java.text.DateFormat; 020import java.text.SimpleDateFormat; 021import java.time.LocalDate; 022import java.time.format.DateTimeFormatter; 023import java.util.Collections; 024import java.util.Comparator; 025import java.util.Date; 026import java.util.List; 027import java.util.Locale; 028 029import javax.jcr.RepositoryException; 030 031import org.apache.avalon.framework.service.ServiceException; 032import org.apache.avalon.framework.service.ServiceManager; 033import org.apache.cocoon.ProcessingException; 034import org.apache.cocoon.environment.ObjectModelHelper; 035import org.apache.cocoon.environment.Request; 036import org.apache.cocoon.generation.Generator; 037import org.apache.cocoon.generation.ServiceableGenerator; 038import org.apache.cocoon.i18n.I18nUtils; 039import org.apache.cocoon.xml.AttributesImpl; 040import org.apache.cocoon.xml.XMLUtils; 041import org.apache.commons.lang.ArrayUtils; 042import org.apache.commons.lang.StringUtils; 043import org.xml.sax.SAXException; 044 045import org.ametys.cms.contenttype.ContentType; 046import org.ametys.cms.contenttype.ContentTypeExtensionPoint; 047import org.ametys.cms.contenttype.ContentTypesHelper; 048import org.ametys.cms.contenttype.MetadataManager; 049import org.ametys.cms.contenttype.MetadataSet; 050import org.ametys.cms.languages.Language; 051import org.ametys.cms.languages.LanguagesManager; 052import org.ametys.cms.repository.Content; 053import org.ametys.cms.repository.ReactionableObject; 054import org.ametys.cms.repository.ReactionableObject.ReactionType; 055import org.ametys.cms.repository.WorkflowAwareContent; 056import org.ametys.cms.repository.comment.Comment; 057import org.ametys.cms.repository.comment.CommentableContent; 058import org.ametys.core.user.CurrentUserProvider; 059import org.ametys.core.user.UserIdentity; 060import org.ametys.core.util.DateUtils; 061import org.ametys.plugins.core.user.UserHelper; 062import org.ametys.plugins.repository.AmetysRepositoryException; 063import org.ametys.plugins.repository.dublincore.DublinCoreAwareAmetysObject; 064import org.ametys.plugins.repository.jcr.JCRAmetysObject; 065import org.ametys.plugins.repository.version.VersionAwareAmetysObject; 066import org.ametys.plugins.workflow.store.AmetysStep; 067import org.ametys.plugins.workflow.support.WorkflowHelper; 068import org.ametys.plugins.workflow.support.WorkflowProvider; 069import org.ametys.plugins.workflow.support.WorkflowProvider.AmetysObjectWorkflow; 070import org.ametys.runtime.i18n.I18nizableText; 071 072import com.opensymphony.workflow.WorkflowException; 073import com.opensymphony.workflow.spi.Step; 074 075/** 076 * {@link Generator} for rendering raw content data. 077 */ 078public class ContentGenerator extends ServiceableGenerator 079{ 080 /** The display date format. */ 081 protected static final DateFormat _DC_DATE_FORMAT = new SimpleDateFormat("yyyy-MM-dd"); 082 083 /** Content type extension point. */ 084 protected ContentTypeExtensionPoint _contentTypeExtensionPoint; 085 /** Metadata manager. */ 086 protected MetadataManager _metadataManager; 087 /** The language manager */ 088 protected LanguagesManager _languageManager; 089 /** The workflow provider */ 090 protected WorkflowProvider _workflowProvider; 091 /** The workflow helper */ 092 protected WorkflowHelper _worklflowHelper; 093 /** Helper for content types */ 094 protected ContentTypesHelper _cTypesHelper; 095 /** The content helper */ 096 protected ContentHelper _contentHelper; 097 /** The current user provider */ 098 protected CurrentUserProvider _userProvider; 099 /** The user helper */ 100 protected UserHelper _userHelper; 101 102 @Override 103 public void service(ServiceManager serviceManager) throws ServiceException 104 { 105 super.service(serviceManager); 106 _contentHelper = (ContentHelper) serviceManager.lookup(ContentHelper.ROLE); 107 _contentTypeExtensionPoint = (ContentTypeExtensionPoint) serviceManager.lookup(ContentTypeExtensionPoint.ROLE); 108 _metadataManager = (MetadataManager) serviceManager.lookup(MetadataManager.ROLE); 109 _languageManager = (LanguagesManager) serviceManager.lookup(LanguagesManager.ROLE); 110 _workflowProvider = (WorkflowProvider) serviceManager.lookup(WorkflowProvider.ROLE); 111 _worklflowHelper = (WorkflowHelper) serviceManager.lookup(WorkflowHelper.ROLE); 112 _cTypesHelper = (ContentTypesHelper) serviceManager.lookup(ContentTypesHelper.ROLE); 113 _userProvider = (CurrentUserProvider) serviceManager.lookup(CurrentUserProvider.ROLE); 114 _userHelper = (UserHelper) serviceManager.lookup(UserHelper.ROLE); 115 } 116 117 public void generate() throws IOException, SAXException, ProcessingException 118 { 119 contentHandler.startDocument(); 120 _generateContent(); 121 contentHandler.endDocument(); 122 } 123 124 /** 125 * Generate the content (with the start/end document) 126 * @throws SAXException if an error occurs while SAXing 127 * @throws IOException if an error occurs 128 * @throws ProcessingException if an error occurs 129 */ 130 protected void _generateContent() throws SAXException, IOException, ProcessingException 131 { 132 Request request = ObjectModelHelper.getRequest(objectModel); 133 Content content = (Content) request.getAttribute(Content.class.getName()); 134 135 // SAX the content 136 _saxContent(content, getDefaultLocale(request)); 137 } 138 139 /** 140 * Get the default locale to use to sax localized values 141 * @param request the request 142 * @return the default locale 143 */ 144 protected Locale getDefaultLocale(Request request) 145 { 146 String lang = parameters.getParameter("lang", request.getParameter("lang")); 147 return StringUtils.isNotEmpty(lang) ? new Locale(lang) : I18nUtils.findLocale(objectModel, "locale", null, Locale.getDefault(), true); 148 } 149 150 /** 151 * SAX the content 152 * @param content The content to SAX 153 * @param defaultLocale The default locale to use to sax localized values if the content's language is null. 154 * @throws SAXException if an error occurs while SAXing 155 * @throws IOException if an error occurs 156 * @throws ProcessingException if an error occurs 157 */ 158 protected void _saxContent (Content content, Locale defaultLocale) throws SAXException, IOException, ProcessingException 159 { 160 AttributesImpl attrs = new AttributesImpl(); 161 attrs.addCDATAAttribute("id", content.getId()); 162 if (content instanceof JCRAmetysObject) 163 { 164 try 165 { 166 attrs.addCDATAAttribute("uuid", ((JCRAmetysObject) content).getNode().getIdentifier()); 167 } 168 catch (RepositoryException e) 169 { 170 throw new ProcessingException("Unable to get jcr UUID for content '" + content.getId() + "'", e); 171 } 172 } 173 attrs.addCDATAAttribute("id", content.getId()); 174 attrs.addCDATAAttribute("name", content.getName()); 175 attrs.addCDATAAttribute("title", content.getTitle(defaultLocale)); 176 if (content.getLanguage() != null) 177 { 178 attrs.addCDATAAttribute("language", content.getLanguage()); 179 } 180 attrs.addCDATAAttribute("createdAt", DateUtils.dateToString(content.getCreationDate())); 181 attrs.addCDATAAttribute("creator", UserIdentity.userIdentityToString(content.getCreator())); 182 attrs.addCDATAAttribute("lastModifiedAt", DateUtils.dateToString(content.getLastModified())); 183 Date lastValidatedAt = content.getLastValidationDate(); 184 if (lastValidatedAt != null) 185 { 186 attrs.addCDATAAttribute("lastValidatedAt", DateUtils.dateToString(lastValidatedAt)); 187 } 188 attrs.addCDATAAttribute("lastContributor", UserIdentity.userIdentityToString(content.getLastContributor())); 189 attrs.addCDATAAttribute("commentable", Boolean.toString(content instanceof CommentableContent)); 190 191 _addAttributeIfNotNull (attrs, "iconGlyph", _cTypesHelper.getIconGlyph(content)); 192 _addAttributeIfNotNull (attrs, "iconDecorator", _cTypesHelper.getIconDecorator(content)); 193 194 _addAttributeIfNotNull (attrs, "smallIcon", _cTypesHelper.getSmallIcon(content)); 195 _addAttributeIfNotNull (attrs, "mediumIcon", _cTypesHelper.getMediumIcon(content)); 196 _addAttributeIfNotNull (attrs, "largeIcon", _cTypesHelper.getLargeIcon(content)); 197 198 XMLUtils.startElement(contentHandler, "content", attrs); 199 200 MetadataSet metadataSet = _getMetadataSet(content); 201 XMLUtils.startElement(contentHandler, "metadata"); 202 _saxMetadata(content, metadataSet, defaultLocale); 203 XMLUtils.endElement(contentHandler, "metadata"); 204 205 if (metadataSet.isEdition()) 206 { 207 XMLUtils.startElement(contentHandler, "comments"); 208 _saxMetadataComments(content, metadataSet, defaultLocale); 209 XMLUtils.endElement(contentHandler, "comments"); 210 } 211 212 String[] cTypes = (String[]) ArrayUtils.addAll(content.getTypes(), content.getMixinTypes()); 213 for (String cTypeId : cTypes) 214 { 215 ContentType cType = _contentTypeExtensionPoint.getExtension(cTypeId); 216 cType.saxContentTypeAdditionalData(contentHandler, content); 217 } 218 219 // FIXME CMS-3057 220 Request request = ObjectModelHelper.getRequest(objectModel); 221 boolean displayWorkflow = !"true".equals(request.getParameter("ignore-workflow")); 222 223 if (displayWorkflow) 224 { 225 _saxWorkflowStep(content); 226 } 227 228 _saxLanguage (content); 229 230 _saxDublinCoreMetadata(content); 231 232 _saxContentReactions(content); 233 234 _saxContentComments(content); 235 236 _saxOtherData(content, defaultLocale); 237 238 XMLUtils.endElement(contentHandler, "content"); 239 } 240 241 /** 242 * Add attribute if value is not null 243 * @param attrs The attributes 244 * @param name The name of attribute 245 * @param value The value 246 */ 247 protected void _addAttributeIfNotNull (AttributesImpl attrs, String name, String value) 248 { 249 if (value != null) 250 { 251 attrs.addCDATAAttribute(name, value); 252 } 253 } 254 255 /** 256 * SAX the comments of the content 257 * @param content The content to consider. Cannot be null. 258 * @throws SAXException if an error occurs while SAXing. 259 */ 260 protected void _saxContentReactions(Content content) throws SAXException 261 { 262 if (content instanceof ReactionableObject) 263 { 264 XMLUtils.startElement(contentHandler, "reactions"); 265 266 List<UserIdentity> likers = ((ReactionableObject) content).getReactionUsers(ReactionType.LIKE); 267 if (!likers.isEmpty()) 268 { 269 AttributesImpl attrs = new AttributesImpl(); 270 attrs.addCDATAAttribute("type", ReactionType.LIKE.name()); 271 XMLUtils.startElement(contentHandler, "reaction", attrs); 272 273 for (UserIdentity liker : likers) 274 { 275 _userHelper.saxUserIdentity(liker, contentHandler, "actor"); 276 } 277 278 XMLUtils.endElement(contentHandler, "reaction"); 279 } 280 XMLUtils.endElement(contentHandler, "reactions"); 281 } 282 } 283 284 /** 285 * SAX the comments of the content 286 * @param content The content to consider. Cannot be null. 287 * @throws SAXException if an error occurs while SAXing. 288 */ 289 protected void _saxContentComments(Content content) throws SAXException 290 { 291 if (content instanceof CommentableContent) 292 { 293 CommentableContent cContent = (CommentableContent) content; 294 295 List<Comment> comments = cContent.getComments(false, true); 296 int level = 0; 297 _saxComments(comments, "comments", level); 298 } 299 } 300 301 /** 302 * Sax the comments 303 * @param comments the list of comments to sax 304 * @param elementName the name of the xml element 305 * @param level the level of comment (0 for parent comment, 1 for sub-comment, etc ....) 306 * @throws SAXException if an error occurs saxing 307 */ 308 protected void _saxComments(List<Comment> comments, String elementName, int level) throws SAXException 309 { 310 if (comments.size() > 0) 311 { 312 XMLUtils.startElement(contentHandler, elementName); 313 314 for (Comment comment : comments) 315 { 316 _saxComment(comment, level); 317 } 318 319 XMLUtils.endElement(contentHandler, elementName); 320 } 321 } 322 323 /** 324 * Sax the comment 325 * @param comment the comment 326 * @param level the level of comment (0 for parent comment, 1 for sub-comment, etc ....) 327 * @throws SAXException if an error occurs saxing 328 */ 329 protected void _saxComment(Comment comment, int level) throws SAXException 330 { 331 AttributesImpl attrs = new AttributesImpl(); 332 333 attrs.addCDATAAttribute("id", comment.getId()); 334 attrs.addCDATAAttribute("creation-date", DateUtils.dateToString(comment.getCreationDate())); 335 attrs.addCDATAAttribute("level", String.valueOf(level)); 336 337 if (!StringUtils.isBlank(comment.getAuthorName())) 338 { 339 attrs.addCDATAAttribute("author-name", comment.getAuthorName()); 340 } 341 342 if (!comment.isEmailHidden() && !StringUtils.isBlank(comment.getAuthorEmail())) 343 { 344 attrs.addCDATAAttribute("author-email", comment.getAuthorEmail()); 345 } 346 347 if (!StringUtils.isBlank(comment.getAuthorURL())) 348 { 349 attrs.addCDATAAttribute("author-url", comment.getAuthorURL()); 350 } 351 352 List<UserIdentity> likers = comment.getReactionUsers(ReactionType.LIKE); 353 attrs.addCDATAAttribute("nb-like", String.valueOf(likers.size())); 354 355 XMLUtils.startElement(contentHandler, "comment", attrs); 356 357 if (comment.getContent() != null) 358 { 359 String[] contents = comment.getContent().split("\r?\n"); 360 for (String c : contents) 361 { 362 XMLUtils.createElement(contentHandler, "p", c); 363 } 364 } 365 366 XMLUtils.startElement(contentHandler, "likers"); 367 for (UserIdentity liker : likers) 368 { 369 _userHelper.saxUserIdentity(liker, contentHandler, "liker"); 370 } 371 XMLUtils.endElement(contentHandler, "likers"); 372 373 _saxComments(comment.getSubComment(false, true), "sub-comments", level + 1); 374 375 XMLUtils.endElement(contentHandler, "comment"); 376 } 377 378 /** 379 * SAX the content language 380 * @param content The content 381 * @throws SAXException if an error occurs while SAXing. 382 */ 383 protected void _saxLanguage (Content content) throws SAXException 384 { 385 String code = content.getLanguage(); 386 if (code != null) 387 { 388 Language language = _languageManager.getLanguage(code); 389 390 AttributesImpl atts = new AttributesImpl(); 391 atts.addCDATAAttribute("code", code); 392 393 if (language != null) 394 { 395 atts.addCDATAAttribute("icon-small", language.getSmallIcon()); 396 atts.addCDATAAttribute("icon-medium", language.getMediumIcon()); 397 atts.addCDATAAttribute("icon-large", language.getLargeIcon()); 398 } 399 400 XMLUtils.startElement(contentHandler, "content-language", atts); 401 if (language != null) 402 { 403 language.getLabel().toSAX(contentHandler); 404 } 405 XMLUtils.endElement(contentHandler, "content-language"); 406 } 407 } 408 409 /** 410 * SAX the workflow step if the content is a <code>WorkflowAwareContent</code> 411 * @param content The content 412 * @throws SAXException if an error occurs while SAXing. 413 */ 414 protected void _saxWorkflowStep (Content content) throws SAXException 415 { 416 if (content instanceof WorkflowAwareContent) 417 { 418 WorkflowAwareContent waContent = (WorkflowAwareContent) content; 419 AmetysObjectWorkflow workflow = _workflowProvider.getAmetysObjectWorkflow(waContent); 420 421 try 422 { 423 long workflowId = waContent.getWorkflowId(); 424 String workflowName = workflow.getWorkflowName(workflowId); 425 426 Step currentStep = _getCurrentStep(waContent, workflow); 427 428 int currentStepId = currentStep.getStepId(); 429 430 I18nizableText workflowStepName = new I18nizableText("application", _worklflowHelper.getStepName(workflowName, currentStepId)); 431 432 AttributesImpl atts = new AttributesImpl(); 433 atts.addAttribute("", "id", "id", "CDATA", String.valueOf(currentStepId)); 434 if ("application".equals(workflowStepName.getCatalogue())) 435 { 436 atts.addAttribute("", "icon-small", "icon-small", "CDATA", "/plugins/cms/resources_workflow/" + workflowStepName.getKey() + "-small.png"); 437 atts.addAttribute("", "icon-medium", "icon-medium", "CDATA", "/plugins/cms/resources_workflow/" + workflowStepName.getKey() + "-medium.png"); 438 atts.addAttribute("", "icon-large", "icon-large", "CDATA", "/plugins/cms/resources_workflow/" + workflowStepName.getKey() + "-large.png"); 439 } 440 else 441 { 442 String pluginName = workflowStepName.getCatalogue().substring("plugin.".length()); 443 atts.addAttribute("", "icon-small", "icon-small", "CDATA", "/plugins/" + pluginName + "/resources/img/workflow/" + workflowStepName.getKey() + "-small.png"); 444 atts.addAttribute("", "icon-medium", "icon-medium", "CDATA", "/plugins/" + pluginName + "/resources/img/workflow/" + workflowStepName.getKey() + "-medium.png"); 445 atts.addAttribute("", "icon-large", "icon-large", "CDATA", "/plugins/" + pluginName + "/resources/img/workflow/" + workflowStepName.getKey() + "-large.png"); 446 } 447 448 XMLUtils.startElement(contentHandler, "workflow-step", atts); 449 workflowStepName.toSAX(contentHandler); 450 XMLUtils.endElement(contentHandler, "workflow-step"); 451 } 452 catch (AmetysRepositoryException e) 453 { 454 // Current step id was not positioned 455 } 456 catch (WorkflowException e) 457 { 458 // Ignore, just don't SAX the workflow step. 459 } 460 } 461 } 462 463 /** 464 * SAX content metadata. 465 * @param content the content. 466 * @param metadataSet the metadata set. 467 * @param defaultLocale The default locale to use to sax localized values if the content's language is null. 468 * @throws SAXException if an error occurs while SAXing. 469 * @throws IOException if an error occurs. 470 * @throws ProcessingException if an error occurs. 471 */ 472 protected void _saxMetadata(Content content, MetadataSet metadataSet, Locale defaultLocale) throws SAXException, ProcessingException, IOException 473 { 474 _metadataManager.saxMetadata(contentHandler, content, metadataSet, defaultLocale); 475 } 476 477 /** 478 * SAX metadata comments. 479 * @param content the content. 480 * @param metadataSet the metadata set. 481 * @param defaultLocale The default locale to use to sax localized values if the content's language is null. 482 * @throws SAXException if an error occurs while SAXing. 483 * @throws IOException if an error occurs. 484 * @throws ProcessingException if an error occurs. 485 */ 486 protected void _saxMetadataComments(Content content, MetadataSet metadataSet, Locale defaultLocale) throws SAXException, ProcessingException, IOException 487 { 488 _metadataManager.saxMetadataComments(contentHandler, content, metadataSet, defaultLocale); 489 } 490 491 /** 492 * SAX content Dublin Core metadata. 493 * @param dcObject the Dublin Core object. 494 * @throws SAXException if an error occurs while SAXing. 495 */ 496 protected void _saxDublinCoreMetadata(DublinCoreAwareAmetysObject dcObject) throws SAXException 497 { 498 XMLUtils.startElement(contentHandler, "dublin-core-metadata"); 499 _saxIfNotNull("title", dcObject.getDCTitle()); 500 _saxIfNotNull("creator", dcObject.getDCCreator()); 501 _saxIfNotNull("subject", dcObject.getDCSubject()); 502 _saxIfNotNull("description", dcObject.getDCDescription()); 503 _saxIfNotNull("publisher", dcObject.getDCPublisher()); 504 _saxIfNotNull("contributor", dcObject.getDCContributor()); 505 _saxIfNotNull("date", dcObject.getDCDate()); 506 _saxIfNotNull("type", dcObject.getDCType()); 507 _saxIfNotNull("format", dcObject.getDCFormat()); 508 _saxIfNotNull("identifier", dcObject.getDCIdentifier()); 509 _saxIfNotNull("source", dcObject.getDCSource()); 510 _saxIfNotNull("language", dcObject.getDCLanguage()); 511 _saxIfNotNull("relation", dcObject.getDCRelation()); 512 _saxIfNotNull("coverage", dcObject.getDCCoverage()); 513 _saxIfNotNull("rights", dcObject.getDCRights()); 514 XMLUtils.endElement(contentHandler, "dublin-core-metadata"); 515 } 516 517 /** 518 * SAX string Dublin Core metadata. 519 * @param name the metadata name. 520 * @param value the metadata value. 521 * @throws SAXException if an error occurs while SAXing. 522 */ 523 protected void _saxIfNotNull(String name, String value) throws SAXException 524 { 525 if (value != null) 526 { 527 XMLUtils.createElement(contentHandler, name, value); 528 } 529 } 530 531 /** 532 * SAX string Dublin Core metadata. 533 * @param name the metadata name. 534 * @param values the metadata values. 535 * @throws SAXException if an error occurs while SAXing. 536 */ 537 protected void _saxIfNotNull(String name, String[] values) throws SAXException 538 { 539 if (values != null) 540 { 541 for (String value : values) 542 { 543 XMLUtils.createElement(contentHandler, name, value); 544 } 545 } 546 } 547 548 /** 549 * SAX date Dublin Core metadata. 550 * @param name the metadata name. 551 * @param value the metadata value. 552 * @throws SAXException if an error occurs while SAXing. 553 */ 554 protected void _saxIfNotNull(String name, Date value) throws SAXException 555 { 556 if (value != null) 557 { 558 LocalDate ld = DateUtils.asLocalDate(value); 559 XMLUtils.createElement(contentHandler, name, ld.format(DateTimeFormatter.ISO_LOCAL_DATE)); 560 } 561 } 562 563 /** 564 * SAX any other data needed by the view.<p> 565 * Default implementation does nothing. 566 * @param content the content. 567 * @throws SAXException if an error occurs while SAXing. 568 * @throws ProcessingException if an error occurs. 569 * @deprecated Use and/or override {@link #_saxOtherData(Content, Locale)} instead 570 */ 571 @Deprecated 572 protected void _saxOtherData(Content content) throws SAXException, ProcessingException 573 { 574 // No other data to SAX 575 } 576 577 /** 578 * SAX any other data needed by the view.<p> 579 * Default implementation does nothing. 580 * @param content the content. 581 * @param defaultLocale The default locale 582 * @throws SAXException if an error occurs while SAXing. 583 * @throws ProcessingException if an error occurs. 584 * @throws IOException if an error occurs. 585 */ 586 protected void _saxOtherData(Content content, Locale defaultLocale) throws SAXException, ProcessingException, IOException 587 { 588 // For legacy purposes 589 _saxOtherData(content); 590 } 591 592 /** 593 * Retrieves the metadata set to be used when SAX'ing metadata and metadata comments. 594 * @param content The content to consider. Cannot be null. 595 * @return The retrieved metadata set 596 * @throws ProcessingException If the metadata set could not be retrieved 597 */ 598 protected MetadataSet _getMetadataSet(Content content) throws ProcessingException 599 { 600 boolean isEditionMetadataSet = parameters.getParameterAsBoolean("isEditionMetadataSet", false); 601 String metadataSetName = parameters.getParameter("metadataSetName", ""); 602 if (StringUtils.isBlank(metadataSetName)) 603 { 604 metadataSetName = "main"; 605 } 606 607 MetadataSet metadataSet = null; 608 609 if (isEditionMetadataSet) 610 { 611 metadataSet = _cTypesHelper.getMetadataSetForEdition(metadataSetName, content.getTypes(), content.getMixinTypes()); 612 } 613 else 614 { 615 metadataSet = _cTypesHelper.getMetadataSetForView(metadataSetName, content.getTypes(), content.getMixinTypes()); 616 } 617 618 if (metadataSet == null) 619 { 620 throw new ProcessingException(String.format("Unknown metadata set '%s' of type '%s' for content type(s) '%s'", 621 metadataSetName, isEditionMetadataSet ? "edition" : "view", StringUtils.join(content.getTypes(), ','))); 622 } 623 624 return metadataSet; 625 } 626 627 /** 628 * Get a content's step, wherever it works on the base version or not. 629 * @param content the content. 630 * @param workflow The workflow impl to use 631 * @return the content's workflow step. 632 * @throws WorkflowException if an error occurs. 633 */ 634 protected Step _getCurrentStep(WorkflowAwareContent content, AmetysObjectWorkflow workflow) throws WorkflowException 635 { 636 long workflowId = content.getWorkflowId(); 637 638 Step currentStep = (Step) workflow.getCurrentSteps(workflowId).get(0); 639 640 if (content instanceof VersionAwareAmetysObject) 641 { 642 VersionAwareAmetysObject vaContent = (VersionAwareAmetysObject) content; 643 String currentRevision = vaContent.getRevision(); 644 645 if (currentRevision != null) 646 { 647 648 String[] allRevisions = vaContent.getAllRevisions(); 649 int currentRevIndex = ArrayUtils.indexOf(allRevisions, currentRevision); 650 651 if (currentRevIndex > -1 && currentRevIndex < (allRevisions.length - 1)) 652 { 653 String nextRevision = allRevisions[currentRevIndex + 1]; 654 655 Date currentRevTimestamp = vaContent.getRevisionTimestamp(); 656 Date nextRevTimestamp = vaContent.getRevisionTimestamp(nextRevision); 657 658 // Get all steps between the two revisions. 659 List<Step> steps = _worklflowHelper.getStepsBetween(workflow, workflowId, currentRevTimestamp, nextRevTimestamp); 660 661 // In the old workflow structure 662 // We take the second, which is current revision's last step. 663 if (steps.size() > 0 && steps.get(0) instanceof AmetysStep) 664 { 665 AmetysStep amStep = (AmetysStep) steps.get(0); 666 if (amStep.getProperty("actionFinishDate") != null) 667 { 668 // New workflow structure detected: cut the first workflow step 669 // in the list, as it belongs to the next version. 670 steps = steps.subList(1, steps.size()); 671 } 672 } 673 674 // Order by step descendant. 675 Collections.sort(steps, new Comparator<Step>() 676 { 677 public int compare(Step step1, Step step2) 678 { 679 return -new Long(step1.getId()).compareTo(step2.getId()); 680 } 681 }); 682 683 // The first step in the list is the current version's last workflow step. 684 if (steps.size() > 0) 685 { 686 currentStep = steps.get(0); 687 } 688 } 689 } 690 } 691 return currentStep; 692 } 693 694}