001/* 002 * Copyright 2019 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.plugins.odfweb.cart; 017 018import java.io.ByteArrayOutputStream; 019import java.io.IOException; 020import java.io.InputStream; 021import java.nio.charset.StandardCharsets; 022import java.util.ArrayList; 023import java.util.Arrays; 024import java.util.Collections; 025import java.util.HashMap; 026import java.util.List; 027import java.util.Map; 028import java.util.Objects; 029import java.util.stream.Collectors; 030 031import javax.mail.MessagingException; 032 033import org.apache.avalon.framework.component.Component; 034import org.apache.avalon.framework.context.Context; 035import org.apache.avalon.framework.context.ContextException; 036import org.apache.avalon.framework.context.Contextualizable; 037import org.apache.avalon.framework.service.ServiceException; 038import org.apache.avalon.framework.service.ServiceManager; 039import org.apache.avalon.framework.service.Serviceable; 040import org.apache.cocoon.components.ContextHelper; 041import org.apache.cocoon.components.source.impl.SitemapSource; 042import org.apache.cocoon.environment.Request; 043import org.apache.cocoon.xml.AttributesImpl; 044import org.apache.cocoon.xml.XMLUtils; 045import org.apache.commons.io.IOUtils; 046import org.apache.commons.lang.StringUtils; 047import org.apache.excalibur.source.Source; 048import org.apache.excalibur.source.SourceResolver; 049import org.apache.excalibur.source.SourceUtil; 050import org.xml.sax.ContentHandler; 051import org.xml.sax.SAXException; 052 053import org.ametys.cms.contenttype.ContentType; 054import org.ametys.cms.contenttype.ContentTypeExtensionPoint; 055import org.ametys.cms.contenttype.ContentTypesHelper; 056import org.ametys.cms.repository.Content; 057import org.ametys.cms.transformation.xslt.ResolveURIComponent; 058import org.ametys.core.user.CurrentUserProvider; 059import org.ametys.core.user.User; 060import org.ametys.core.user.UserIdentity; 061import org.ametys.core.userpref.UserPreferencesException; 062import org.ametys.core.userpref.UserPreferencesManager; 063import org.ametys.core.util.DateUtils; 064import org.ametys.core.util.I18nUtils; 065import org.ametys.core.util.IgnoreRootHandler; 066import org.ametys.core.util.mail.SendMailHelper; 067import org.ametys.odf.course.Course; 068import org.ametys.odf.coursepart.CoursePart; 069import org.ametys.odf.program.AbstractProgram; 070import org.ametys.odf.program.Program; 071import org.ametys.odf.program.SubProgram; 072import org.ametys.plugins.core.user.UserHelper; 073import org.ametys.plugins.odfweb.repository.OdfPageResolver; 074import org.ametys.plugins.repository.AmetysObjectResolver; 075import org.ametys.plugins.repository.UnknownAmetysObjectException; 076import org.ametys.plugins.repository.provider.RequestAttributeWorkspaceSelector; 077import org.ametys.runtime.i18n.I18nizableText; 078import org.ametys.runtime.i18n.I18nizableTextParameter; 079import org.ametys.runtime.plugin.component.AbstractLogEnabled; 080import org.ametys.web.WebConstants; 081import org.ametys.web.renderingcontext.RenderingContext; 082import org.ametys.web.renderingcontext.RenderingContextHandler; 083import org.ametys.web.repository.page.Page; 084import org.ametys.web.repository.site.Site; 085import org.ametys.web.repository.site.SiteManager; 086import org.ametys.web.userpref.FOUserPreferencesConstants; 087 088/** 089 * Component to handle ODF cart items 090 * 091 */ 092public class ODFCartManager extends AbstractLogEnabled implements Serviceable, Component, Contextualizable 093{ 094 /** The avalon role */ 095 public static final String ROLE = ODFCartManager.class.getName(); 096 097 /** The id of user preference for cart's elements */ 098 public static final String CART_USER_PREF_CONTENT_IDS = "cartOdfContentIds"; 099 100 /** The id of user preference for subscription */ 101 public static final String SUBSCRIPTION_USER_PREF_CONTENT_IDS = "subscriptionOdfContentIds"; 102 103 private UserPreferencesManager _userPrefManager; 104 private AmetysObjectResolver _resolver; 105 private SourceResolver _srcResolver; 106 private OdfPageResolver _odfPageResolver; 107 private ContentTypeExtensionPoint _cTypeEP; 108 private ContentTypesHelper _cTypesHelper; 109 private ODFCartUserPreferencesStorage _odfUserPrefStorage; 110 private I18nUtils _i18nUtils; 111 private UserHelper _userHelper; 112 private SiteManager _siteManager; 113 private RenderingContextHandler _renderingContextHandler; 114 private CurrentUserProvider _currentUserProvider; 115 116 private Context _context; 117 118 119 120 121 @Override 122 public void service(ServiceManager serviceManager) throws ServiceException 123 { 124 _userPrefManager = (UserPreferencesManager) serviceManager.lookup(UserPreferencesManager.ROLE + ".FO"); 125 _resolver = (AmetysObjectResolver) serviceManager.lookup(AmetysObjectResolver.ROLE); 126 _srcResolver = (SourceResolver) serviceManager.lookup(SourceResolver.ROLE); 127 _odfPageResolver = (OdfPageResolver) serviceManager.lookup(OdfPageResolver.ROLE); 128 _cTypeEP = (ContentTypeExtensionPoint) serviceManager.lookup(ContentTypeExtensionPoint.ROLE); 129 _cTypesHelper = (ContentTypesHelper) serviceManager.lookup(ContentTypesHelper.ROLE); 130 _odfUserPrefStorage = (ODFCartUserPreferencesStorage) serviceManager.lookup(ODFCartUserPreferencesStorage.ROLE); 131 _i18nUtils = (I18nUtils) serviceManager.lookup(I18nUtils.ROLE); 132 _userHelper = (UserHelper) serviceManager.lookup(UserHelper.ROLE); 133 _siteManager = (SiteManager) serviceManager.lookup(SiteManager.ROLE); 134 _renderingContextHandler = (RenderingContextHandler) serviceManager.lookup(RenderingContextHandler.ROLE); 135 _currentUserProvider = (CurrentUserProvider) serviceManager.lookup(CurrentUserProvider.ROLE); 136 } 137 138 public void contextualize(Context context) throws ContextException 139 { 140 _context = context; 141 } 142 143 /** 144 * Get the id of ODF's cart items for a given user 145 * @param user the user 146 * @param siteName the current site name 147 * @return the list of contents' id 148 * @throws UserPreferencesException if failed to get cart items 149 */ 150 public List<String> getCartItemIds(UserIdentity user, String siteName) throws UserPreferencesException 151 { 152 return getCartItemIds(user, siteName, CART_USER_PREF_CONTENT_IDS); 153 } 154 155 /** 156 * Get the id of ODF's cart items for which a given user is a subscriber 157 * @param user the user 158 * @param siteName the current site name 159 * @return the list of contents' id 160 * @throws UserPreferencesException if failed to get cart items 161 */ 162 public List<String> getItemIdsWithSubscription(UserIdentity user, String siteName) throws UserPreferencesException 163 { 164 return getCartItemIds(user, siteName, SUBSCRIPTION_USER_PREF_CONTENT_IDS); 165 } 166 167 /** 168 * Get the id of items for a given user 169 * @param user the user 170 * @param siteName the current site name 171 * @param userPrefsId The id of user preferences 172 * @return the list of contents' id 173 * @throws UserPreferencesException if failed to get cart items 174 */ 175 protected List<String> getCartItemIds(UserIdentity user, String siteName, String userPrefsId) throws UserPreferencesException 176 { 177 Map<String, String> contextVars = new HashMap<>(); 178 contextVars.put(FOUserPreferencesConstants.CONTEXT_VAR_SITENAME, siteName); 179 180 String contentIdsAsStr = _userPrefManager.getUserPreferenceAsString(user, "/sites/" + siteName, contextVars, userPrefsId); 181 if (StringUtils.isNotBlank(contentIdsAsStr)) 182 { 183 return Arrays.asList(StringUtils.split(contentIdsAsStr, ",")); 184 } 185 186 return Collections.emptyList(); 187 } 188 189 /** 190 * Get the ODF's cart items for a given user 191 * @param owner the user 192 * @param siteName the current site name 193 * @return the list of contents 194 * @throws UserPreferencesException if failed to get cart items 195 */ 196 public List<ODFCartItem> getCartItems(UserIdentity owner, String siteName) throws UserPreferencesException 197 { 198 List<ODFCartItem> items = new ArrayList<>(); 199 200 List<String> itemIds = getCartItemIds(owner, siteName); 201 for (String itemId : itemIds) 202 { 203 ODFCartItem item = getCartItem(itemId); 204 if (item != null) 205 { 206 items.add(item); 207 } 208 else 209 { 210 getLogger().warn("The item with id '{}' stored in cart of user {} does not match an existing content anymore. It will be ignored", itemId, owner); 211 } 212 } 213 214 return items; 215 } 216 217 /** 218 * Get the ODF's cart items for which the given user is a subscriber 219 * @param owner the user 220 * @param siteName the current site name 221 * @return the list of contents 222 * @throws UserPreferencesException if failed to get subscriptions 223 */ 224 public List<ODFCartItem> getItemsWithSubscription(UserIdentity owner, String siteName) throws UserPreferencesException 225 { 226 List<ODFCartItem> items = new ArrayList<>(); 227 228 List<String> itemIds = getItemIdsWithSubscription(owner, siteName); 229 for (String itemId : itemIds) 230 { 231 ODFCartItem item = getCartItem(itemId); 232 if (item != null) 233 { 234 items.add(item); 235 } 236 else 237 { 238 getLogger().warn("The item with id '{}' stored in subscription of user {} does not match an existing content anymore. It will be ignored", itemId, owner); 239 } 240 } 241 242 return items; 243 } 244 245 /** 246 * Get a cart item from its id 247 * @param itemId the item's id 248 * @return the cart item or null if no content was found 249 */ 250 public ODFCartItem getCartItem(String itemId) 251 { 252 int i = itemId.indexOf(';'); 253 254 String contentId = itemId; 255 String parentId = null; 256 257 if (i != -1) 258 { 259 contentId = itemId.substring(0, i); 260 parentId = itemId.substring(i + 1); 261 } 262 263 try 264 { 265 return new ODFCartItem(_resolver.resolveById(contentId), parentId != null ? _resolver.resolveById(parentId) : null); 266 } 267 catch (UnknownAmetysObjectException e) 268 { 269 return null; 270 } 271 } 272 273 /** 274 * Set the cart's items 275 * @param owner The cart owner 276 * @param itemIds the id of items to set in the cart 277 * @param siteName the site name 278 * @throws UserPreferencesException if failed to save cart 279 */ 280 public void setCartItems(UserIdentity owner, List<String> itemIds, String siteName) throws UserPreferencesException 281 { 282 saveItemsInUserPreference(owner, itemIds, siteName, CART_USER_PREF_CONTENT_IDS); 283 } 284 285 286 /** 287 * Subscribe to a list of items. All subscriptions are replaced by the given items. 288 * @param owner The cart owner 289 * @param itemIds the id of items to subscribe to. 290 * @param siteName the site name 291 * @throws UserPreferencesException if failed to save subscriptions 292 */ 293 public void setSubscription(UserIdentity owner, List<String> itemIds, String siteName) throws UserPreferencesException 294 { 295 saveItemsInUserPreference(owner, itemIds, siteName, SUBSCRIPTION_USER_PREF_CONTENT_IDS); 296 } 297 298 /** 299 * Save the given items into the given user preference 300 * @param owner the user 301 * @param itemIds the id of items 302 * @param siteName the site name 303 * @param userPrefsId the id of user preference 304 * @throws UserPreferencesException if failed to save user preference 305 */ 306 public void saveItemsInUserPreference(UserIdentity owner, List<String> itemIds, String siteName, String userPrefsId) throws UserPreferencesException 307 { 308 Map<String, String> contextVars = new HashMap<>(); 309 contextVars.put(FOUserPreferencesConstants.CONTEXT_VAR_SITENAME, siteName); 310 311 Map<String, String> preferences = new HashMap<>(); 312 preferences.put(userPrefsId, StringUtils.join(itemIds, ",")); 313 314 _odfUserPrefStorage.setUserPreferences(owner, "/sites/" + siteName, contextVars, preferences); 315 } 316 317 /** 318 * Add a content to the cart 319 * @param owner the cart owner 320 * @param itemId the id of content to add into the cart 321 * @param siteName the site name 322 * @return true if the content was successfully added 323 * @throws UserPreferencesException if failed to save cart 324 */ 325 public boolean addCartItem(UserIdentity owner, String itemId, String siteName) throws UserPreferencesException 326 { 327 List<ODFCartItem> items = getCartItems(owner, siteName); 328 329 ODFCartItem item = getCartItem(itemId); 330 if (item != null) 331 { 332 items.add(item); 333 } 334 else 335 { 336 getLogger().warn("Unknown item with id {}. It cannot be added to user cart", itemId); 337 return false; 338 } 339 340 List<String> itemIds = items.stream() 341 .map(c -> c.getId()) 342 .collect(Collectors.toList()); 343 344 setCartItems(owner, itemIds, siteName); 345 346 return true; 347 } 348 349 /** 350 * determines if the user subscribes to this item 351 * @param owner the user 352 * @param itemId the id of content 353 * @param siteName the site name 354 * @return true if the user subscribes to this item, false otherwise 355 * @throws UserPreferencesException if failed to check subscription 356 */ 357 public boolean isSubscriber(UserIdentity owner, String itemId, String siteName) throws UserPreferencesException 358 { 359 List<String> subscriptions = getItemIdsWithSubscription(owner, siteName); 360 return subscriptions.contains(itemId); 361 } 362 363 /** 364 * Subscribe to a content 365 * @param owner the cart owner 366 * @param itemId the id of content 367 * @param siteName the site name 368 * @return if the content was successfuly added to subscriptions 369 * @throws UserPreferencesException if failed to subscribe to content 370 */ 371 public boolean subscribe(UserIdentity owner, String itemId, String siteName) throws UserPreferencesException 372 { 373 List<ODFCartItem> items = getItemsWithSubscription(owner, siteName); 374 375 ODFCartItem item = getCartItem(itemId); 376 if (item != null) 377 { 378 items.add(item); 379 } 380 else 381 { 382 getLogger().warn("Unknown item with id {}. It cannot be added to user subscriptions", itemId); 383 return false; 384 } 385 386 List<String> itemIds = items.stream() 387 .map(c -> c.getId()) 388 .collect(Collectors.toList()); 389 390 setSubscription(owner, itemIds, siteName); 391 392 return true; 393 } 394 395 /** 396 * Unsubscribe to a content 397 * @param owner the cart owner 398 * @param itemId the id of content 399 * @param siteName the site name 400 * @return if the content was successfuly added to subscriptions 401 * @throws UserPreferencesException if failed to subscribe to content 402 */ 403 public boolean unsubscribe(UserIdentity owner, String itemId, String siteName) throws UserPreferencesException 404 { 405 List<ODFCartItem> items = getItemsWithSubscription(owner, siteName); 406 407 ODFCartItem item = getCartItem(itemId); 408 if (item != null) 409 { 410 items.remove(item); 411 } 412 else 413 { 414 getLogger().warn("Unknown item with id {}. It cannot be remove from user subscriptions", itemId); 415 return false; 416 } 417 418 List<String> itemIds = items.stream() 419 .map(c -> c.getId()) 420 .collect(Collectors.toList()); 421 422 setSubscription(owner, itemIds, siteName); 423 424 return true; 425 } 426 427 /** 428 * Share the cart's items by mail 429 * @param owner The cart owner 430 * @param itemIds the id of contents to set in the cart 431 * @param recipients the mails to share with 432 * @param siteName the site name 433 * @param language the language 434 * @param message the message to add to selection 435 * @return the results 436 */ 437 public Map<String, Object> shareCartItems(UserIdentity owner, List<String> itemIds, List<String> recipients, String siteName, String language, String message) 438 { 439 Map<String, Object> result = new HashMap<>(); 440 441 User user = _userHelper.getUser(owner); 442 String sender = user.getEmail(); 443 444 if (StringUtils.isEmpty(sender)) 445 { 446 getLogger().error("Cart's owner has no email, his ODF cart selection can not be shared"); 447 result.put("success", false); 448 result.put("error", "no-owner-mail"); 449 return result; 450 } 451 452 List<ODFCartItem> items = itemIds.stream() 453 .map(i -> getCartItem(i)) 454 .filter(Objects::nonNull) 455 .collect(Collectors.toList()); 456 457 Site site = _siteManager.getSite(siteName); 458 Map<String, I18nizableTextParameter> i18nparam = new HashMap<>(); 459 i18nparam.put("siteTitle", new I18nizableText(site.getTitle())); // {siteTitle} 460 461 I18nizableText i18nTextSubject = new I18nizableText("plugin.odf-web", "PLUGINS_ODFWEB_CART_SHARE_MAIL_SUBJECT", i18nparam); 462 String subject = _i18nUtils.translate(i18nTextSubject); 463 464 String htmlBody = null; 465 String textBody = null; 466 try 467 { 468 469 htmlBody = getMailBody(items, message, owner, siteName, language, false); 470 textBody = getMailBody(items, message, owner, siteName, language, true); 471 } 472 catch (IOException e) 473 { 474 getLogger().error("Fail to get mail body to share ODF cart selection", e); 475 result.put("success", false); 476 return result; 477 } 478 479 List<String> mailsInError = new ArrayList<>(); 480 481 for (String recipient : recipients) 482 { 483 try 484 { 485 SendMailHelper.sendMail(subject, htmlBody, textBody, Collections.singletonList(recipient), sender); 486 } 487 catch (MessagingException e) 488 { 489 getLogger().error("Failed to send ODF cart selection to '" + recipient + "'", e); 490 mailsInError.add(recipient); 491 } 492 } 493 494 if (mailsInError.size() > 0) 495 { 496 result.put("success", false); 497 result.put("mailsInError", mailsInError); 498 } 499 else 500 { 501 result.put("success", true); 502 } 503 504 return result; 505 } 506 507 /** 508 * Get the mail subject for sharing cart 509 * @param siteName The site name 510 * @param language the language 511 * @return the mail subject 512 */ 513 protected String getMailSubject(String siteName, String language) 514 { 515 Site site = _siteManager.getSite(siteName); 516 Map<String, I18nizableTextParameter> i18nparam = new HashMap<>(); 517 i18nparam.put("siteTitle", new I18nizableText(site.getTitle())); // {siteTitle} 518 519 I18nizableText i18nTextSubject = new I18nizableText("plugin.odf-web", "PLUGINS_ODFWEB_CART_SHARE_MAIL_SUBJECT", i18nparam); 520 return _i18nUtils.translate(i18nTextSubject, language); 521 } 522 523 /** 524 * Get the mail body to sharing cart 525 * @param items The cart's items 526 * @param message The message 527 * @param owner The cart's owner 528 * @param siteName The site name 529 * @param language the language 530 * @param text true to get the body to text body (html otherwise) 531 * @return the cart items to HTML format 532 * @throws IOException if failed to mail body 533 */ 534 protected String getMailBody(List<ODFCartItem> items, String message, UserIdentity owner, String siteName, String language, boolean text) throws IOException 535 { 536 Request request = ContextHelper.getRequest(_context); 537 538 String currentWorkspace = RequestAttributeWorkspaceSelector.getForcedWorkspace(request); 539 RenderingContext currentContext = _renderingContextHandler.getRenderingContext(); 540 541 Source source = null; 542 try 543 { 544 // Force live workspace and FRONT context to resolve page 545 RequestAttributeWorkspaceSelector.setForcedWorkspace(request, WebConstants.LIVE_WORKSPACE); 546 _renderingContextHandler.setRenderingContext(RenderingContext.FRONT); 547 548 Map<String, Object> parameters = new HashMap<>(); 549 550 parameters.put("items", items); 551 parameters.put("message", message); 552 parameters.put("owner", owner); 553 parameters.put("siteName", siteName); 554 parameters.put("lang", language); 555 parameters.put("format", text ? "text" : "html"); 556 557 source = _srcResolver.resolveURI("cocoon://_plugins/odf-web/cart/mail/body", null, parameters); 558 559 try (InputStream is = source.getInputStream()) 560 { 561 ByteArrayOutputStream bos = new ByteArrayOutputStream(); 562 SourceUtil.copy(is, bos); 563 564 return bos.toString("UTF-8"); 565 } 566 } 567 finally 568 { 569 RequestAttributeWorkspaceSelector.setForcedWorkspace(request, currentWorkspace); 570 _renderingContextHandler.setRenderingContext(currentContext); 571 572 if (source != null) 573 { 574 _srcResolver.release(source); 575 } 576 } 577 } 578 579 /** 580 * SAX the cart's items 581 * @param contentHandler The content handler to sax into 582 * @param owner the cart owner 583 * @param siteName the site name 584 * @throws SAXException if an error occurred while saxing 585 * @throws IOException if an I/O exception occurred 586 * @throws UserPreferencesException if failed to get cart items 587 */ 588 public void saxCartItems(ContentHandler contentHandler, UserIdentity owner, String siteName) throws SAXException, IOException, UserPreferencesException 589 { 590 List<ODFCartItem> items = getCartItems(owner, siteName); 591 592 XMLUtils.startElement(contentHandler, "items"); 593 for (ODFCartItem item : items) 594 { 595 saxCartItem(contentHandler, item, siteName); 596 597 } 598 XMLUtils.endElement(contentHandler, "items"); 599 600 } 601 602 /** 603 * SAX a cart's item 604 * @param contentHandler The content handler to sax into 605 * @param item the cart's item 606 * @param siteName the site name 607 * @throws SAXException if an error occurred while saxing 608 * @throws IOException if an I/O exception occurred 609 */ 610 public void saxCartItem(ContentHandler contentHandler, ODFCartItem item, String siteName) throws SAXException, IOException 611 { 612 AttributesImpl attrs = new AttributesImpl(); 613 attrs.addCDATAAttribute("id", item.getId()); 614 XMLUtils.startElement(contentHandler, "item", attrs); 615 616 Content content = item.getContent(); 617 saxTypes(contentHandler, content.getTypes()); 618 saxContent(contentHandler, content, "cart"); 619 saxPage(contentHandler, item, siteName); 620 621 Program parentProgram = item.getParentProgram(); 622 if (parentProgram != null) 623 { 624 attrs = new AttributesImpl(); 625 attrs.addCDATAAttribute("id", parentProgram.getId()); 626 attrs.addCDATAAttribute("title", parentProgram.getTitle()); 627 Page parentPage = _odfPageResolver.getProgramPage(parentProgram, siteName); 628 if (parentPage != null) 629 { 630 attrs.addCDATAAttribute("pageId", parentPage.getId()); 631 } 632 XMLUtils.createElement(contentHandler, "parent", attrs); 633 634 } 635 XMLUtils.endElement(contentHandler, "item"); 636 } 637 638 /** 639 * Get the JSON representation of a cart item 640 * @param item The cart's item 641 * @param siteName The site name 642 * @param viewName The name of content view to use 643 * @return The cart items properties 644 * @throws IOException if failed to read content view 645 */ 646 public Map<String, Object> cartItem2Json(ODFCartItem item, String siteName, String viewName) throws IOException 647 { 648 Map<String, Object> result = new HashMap<>(); 649 650 Content content = item.getContent(); 651 652 result.put("id", item.getId()); 653 result.put("contentId", content.getId()); 654 result.put("title", content.getTitle()); 655 result.put("name", content.getName()); 656 657 Program parentProgram = item.getParentProgram(); 658 if (parentProgram != null) 659 { 660 result.put("parentProgramId", parentProgram.getId()); 661 result.put("parentProgramTitle", parentProgram.getTitle()); 662 } 663 664 Page page = getPage(item, siteName); 665 if (page != null) 666 { 667 result.put("pageId", page.getId()); 668 result.put("pageTitle", page.getTitle()); 669 result.put("pagePath", page.getPathInSitemap()); 670 } 671 672 String cTypeId = content.getTypes()[0]; 673 ContentType cType = _cTypeEP.getExtension(cTypeId); 674 675 result.put("contentTypeId", cTypeId); 676 result.put("contentTypeLabel", cType.getLabel()); 677 678 if (viewName != null && _cTypesHelper.getView(viewName, content.getTypes(), content.getMixinTypes()) != null) 679 { 680 String uri = "cocoon://_content.html?contentId=" + content.getId() + "&viewName=" + viewName + (parentProgram != null ? "&parentProgramId=" + parentProgram.getId() : ""); 681 SitemapSource src = null; 682 683 try 684 { 685 src = (SitemapSource) _srcResolver.resolveURI(uri); 686 try (InputStream is = src.getInputStream()) 687 { 688 String view = IOUtils.toString(is, StandardCharsets.UTF_8); 689 result.put("view", view); 690 } 691 } 692 finally 693 { 694 _srcResolver.release(src); 695 } 696 } 697 698 if (content instanceof AbstractProgram) 699 { 700 try 701 { 702 boolean subscriber = _currentUserProvider.getUser() != null && isSubscriber(_currentUserProvider.getUser(), item.getId(), siteName); 703 result.put("subscriber", subscriber); 704 } 705 catch (UserPreferencesException e) 706 { 707 getLogger().error("Fail to check if current user subscribes to content {}. It supposes he is not.", content.getId(), e); 708 result.put("subscriber", false); 709 } 710 711 additionalItemInfo(item, (AbstractProgram) content, result); 712 } 713 else if (content instanceof Course) 714 { 715 additionalItemInfo(item, (Course) content, result); 716 } 717 718 return result; 719 } 720 721 /** 722 * Get the additional information for {@link AbstractProgram} cart item 723 * @param item the odf cart item 724 * @param abstractProgram the abstract program 725 * @param infos the information to be completed 726 */ 727 protected void additionalItemInfo(ODFCartItem item, AbstractProgram abstractProgram, Map<String, Object> infos) 728 { 729 // Nothing 730 } 731 732 /** 733 * Get the additional information for {@link Course} cart item 734 * @param item the odf cart item 735 * @param course the abstract program 736 * @param infos the information to be completed 737 */ 738 protected void additionalItemInfo(ODFCartItem item, Course course, Map<String, Object> infos) 739 { 740 double ects = course.getEcts(); 741 if (ects > 0D) 742 { 743 infos.put("ects", ects); 744 } 745 746 double numberOfHours = course.getNumberOfHours(); 747 if (numberOfHours > 0D) 748 { 749 infos.put("nbHours", numberOfHours); 750 } 751 752 List<CoursePart> courseParts = course.getCourseParts(); 753 if (!courseParts.isEmpty()) 754 { 755 List<Map<String, Object>> courseparts = new ArrayList<>(); 756 for (CoursePart coursePart : course.getCourseParts()) 757 { 758 Map<String, Object> coursepart = new HashMap<>(); 759 coursepart.put("nature", coursePart.getNature()); 760 coursepart.put("nbHours", coursePart.getNumberOfHours()); 761 } 762 763 infos.put("courseparts", courseparts); 764 } 765 } 766 767 /** 768 * Sax the content types 769 * @param handler The content handler to sax into 770 * @param types The content types 771 * @throws SAXException if an error occurred while saxing 772 */ 773 protected void saxTypes(ContentHandler handler, String[] types) throws SAXException 774 { 775 XMLUtils.startElement(handler, "types"); 776 777 for (String id : types) 778 { 779 ContentType cType = _cTypeEP.getExtension(id); 780 if (cType != null) 781 { 782 AttributesImpl attrs = new AttributesImpl(); 783 attrs.addCDATAAttribute("id", cType.getId()); 784 785 XMLUtils.startElement(handler, "type", attrs); 786 cType.getLabel().toSAX(handler); 787 XMLUtils.endElement(handler, "type"); 788 } 789 } 790 XMLUtils.endElement(handler, "types"); 791 } 792 793 /** 794 * SAX the content view 795 * @param handler The content handler to sax into 796 * @param content The content 797 * @param viewName The view name 798 * @throws SAXException if an error occurred while saxing 799 * @throws IOException if an I/O exception occurred 800 */ 801 protected void saxContent (ContentHandler handler, Content content, String viewName) throws SAXException, IOException 802 { 803 AttributesImpl attrs = new AttributesImpl(); 804 attrs.addCDATAAttribute("id", content.getId()); 805 attrs.addCDATAAttribute("name", content.getName()); 806 attrs.addCDATAAttribute("title", content.getTitle(null)); 807 attrs.addCDATAAttribute("lastModifiedAt", DateUtils.dateToString(content.getLastModified())); 808 809 XMLUtils.startElement(handler, "content", attrs); 810 811 if (_cTypesHelper.getView("cart", content.getTypes(), content.getMixinTypes()) != null) 812 { 813 String uri = "cocoon://_content.html?contentId=" + content.getId() + "&viewName=" + viewName; 814 SitemapSource src = null; 815 816 try 817 { 818 src = (SitemapSource) _srcResolver.resolveURI(uri); 819 src.toSAX(new IgnoreRootHandler(handler)); 820 } 821 finally 822 { 823 _srcResolver.release(src); 824 } 825 } 826 827 XMLUtils.endElement(handler, "content"); 828 } 829 830 /** 831 * Sax the content's page 832 * @param handler The content handler to sax into 833 * @param item The cart's item 834 * @param siteName The current site name 835 * @throws SAXException if an error occurred while saxing 836 */ 837 protected void saxPage(ContentHandler handler, ODFCartItem item, String siteName) throws SAXException 838 { 839 Page page = getPage(item, siteName); 840 if (page != null) 841 { 842 String pageId = page.getId(); 843 844 AttributesImpl attrs = new AttributesImpl(); 845 attrs.addCDATAAttribute("id", pageId); 846 attrs.addCDATAAttribute("path", ResolveURIComponent.resolve("page", pageId)); 847 XMLUtils.createElement(handler, "page", attrs, page.getTitle()); 848 } 849 } 850 851 /** 852 * Get the page associated to this cart's item 853 * @param item The item 854 * @param siteName The site name 855 * @return the page or <code>null</code> if not found 856 */ 857 protected Page getPage(ODFCartItem item, String siteName) 858 { 859 Content content = item.getContent(); 860 if (content instanceof Course) 861 { 862 return _odfPageResolver.getCoursePage((Course) content, (AbstractProgram) item.getParentProgram(), siteName); 863 } 864 else if (content instanceof Program) 865 { 866 return _odfPageResolver.getProgramPage((Program) content, siteName); 867 } 868 else if (content instanceof SubProgram) 869 { 870 return _odfPageResolver.getSubProgramPage((SubProgram) content, item.getParentProgram(), siteName); 871 } 872 873 getLogger().info("No page found of content {} in ODF cart", content.getId()); 874 return null; 875 } 876 877 class ODFCartItem 878 { 879 private Content _content; 880 private Program _parentProgram; 881 882 public ODFCartItem(Content content) 883 { 884 this(content, null); 885 } 886 887 public ODFCartItem(Content content, Program parentProgram) 888 { 889 _content = content; 890 _parentProgram = parentProgram; 891 } 892 893 String getId() 894 { 895 return _content.getId() + (_parentProgram != null ? ";" + _parentProgram.getId() : ""); 896 } 897 898 Content getContent() 899 { 900 return _content; 901 } 902 903 Program getParentProgram() 904 { 905 return _parentProgram; 906 } 907 908 @Override 909 public int hashCode() 910 { 911 return Objects.hash(getId()); 912 } 913 914 @Override 915 public boolean equals(Object other) 916 { 917 return other != null && getClass() == other.getClass() && getId().equals(((ODFCartItem) other).getId()); 918 } 919 } 920}