001/* 002 * This library is part of OpenCms - 003 * the Open Source Content Management System 004 * 005 * Copyright (c) Alkacon Software GmbH & Co. KG (http://www.alkacon.com) 006 * 007 * This library is free software; you can redistribute it and/or 008 * modify it under the terms of the GNU Lesser General Public 009 * License as published by the Free Software Foundation; either 010 * version 2.1 of the License, or (at your option) any later version. 011 * 012 * This library is distributed in the hope that it will be useful, 013 * but WITHOUT ANY WARRANTY; without even the implied warranty of 014 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 015 * Lesser General Public License for more details. 016 * 017 * For further information about Alkacon Software, please see the 018 * company website: http://www.alkacon.com 019 * 020 * For further information about OpenCms, please see the 021 * project website: http://www.opencms.org 022 * 023 * You should have received a copy of the GNU Lesser General Public 024 * License along with this library; if not, write to the Free Software 025 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 026 */ 027 028package org.opencms.jsp.util; 029 030import org.opencms.ade.contenteditor.CmsContentService; 031import org.opencms.file.CmsFile; 032import org.opencms.file.CmsObject; 033import org.opencms.file.CmsProject; 034import org.opencms.file.CmsResource; 035import org.opencms.file.CmsResourceFilter; 036import org.opencms.file.types.CmsResourceTypeXmlPage; 037import org.opencms.i18n.CmsEncoder; 038import org.opencms.i18n.CmsLocaleManager; 039import org.opencms.jsp.CmsJspResourceWrapper; 040import org.opencms.lock.CmsLock; 041import org.opencms.main.CmsException; 042import org.opencms.main.CmsRuntimeException; 043import org.opencms.main.OpenCms; 044import org.opencms.security.CmsPermissionSet; 045import org.opencms.util.CmsCollectionsGenericWrapper; 046import org.opencms.util.CmsConstantMap; 047import org.opencms.util.CmsUUID; 048import org.opencms.xml.I_CmsXmlDocument; 049import org.opencms.xml.content.CmsXmlContent; 050import org.opencms.xml.content.CmsXmlContentFactory; 051import org.opencms.xml.page.CmsXmlPageFactory; 052import org.opencms.xml.types.I_CmsXmlContentValue; 053import org.opencms.xml.xml2json.renderer.CmsJsonRendererXmlContent; 054 055import java.util.ArrayList; 056import java.util.Collections; 057import java.util.Comparator; 058import java.util.Iterator; 059import java.util.List; 060import java.util.Locale; 061import java.util.Map; 062 063import org.apache.commons.collections.Transformer; 064 065/** 066 * Allows access to the individual elements of an XML content, usually used inside a loop of a 067 * <code><cms:contentload></code> tag.<p> 068 * 069 * The implementation is optimized for performance and uses lazy initializing of the 070 * requested values as much as possible.<p> 071 * 072 * @since 7.0.2 073 * 074 * @see org.opencms.jsp.CmsJspTagContentAccess 075 */ 076public class CmsJspContentAccessBean { 077 078 /** 079 * Provides Booleans that indicate if a specified locale is available in the XML content, 080 * the input is assumed to be a String that represents a Locale.<p> 081 */ 082 public class CmsHasLocaleTransformer implements Transformer { 083 084 /** 085 * @see org.apache.commons.collections.Transformer#transform(java.lang.Object) 086 */ 087 public Object transform(Object input) { 088 089 return Boolean.valueOf(getRawContent().hasLocale(CmsJspElFunctions.convertLocale(input))); 090 } 091 } 092 093 /** 094 * Provides Booleans that indicate if a specified path exists in the XML content, 095 * the input is assumed to be a String that represents an xpath in the XML content.<p> 096 */ 097 public class CmsHasLocaleValueTransformer implements Transformer { 098 099 /** 100 * @see org.apache.commons.collections.Transformer#transform(java.lang.Object) 101 */ 102 public Object transform(Object input) { 103 104 Locale locale = CmsJspElFunctions.convertLocale(input); 105 Map<String, Boolean> result; 106 if (getRawContent().hasLocale(locale)) { 107 result = CmsCollectionsGenericWrapper.createLazyMap(new CmsHasValueTransformer(locale)); 108 } else { 109 result = CmsConstantMap.CONSTANT_BOOLEAN_FALSE_MAP; 110 } 111 return result; 112 } 113 } 114 115 /** 116 * Provides a Map with Booleans that indicate if a specified path exists in the XML content in the selected Locale, 117 * the input is assumed to be a String that represents an xpath in the XML content.<p> 118 */ 119 public class CmsHasValueTransformer implements Transformer { 120 121 /** The selected locale. */ 122 private Locale m_selectedLocale; 123 124 /** 125 * Constructor with a locale.<p> 126 * 127 * @param locale the locale to use 128 */ 129 public CmsHasValueTransformer(Locale locale) { 130 131 m_selectedLocale = locale; 132 } 133 134 /** 135 * @see org.apache.commons.collections.Transformer#transform(java.lang.Object) 136 */ 137 public Object transform(Object input) { 138 139 return Boolean.valueOf(getRawContent().hasValue(String.valueOf(input), m_selectedLocale)); 140 } 141 } 142 143 /** 144 * Transformer used for the 'imageDnd' EL attribute which is used to annotate images which can be replaced by drag and drop.<p> 145 */ 146 public class CmsImageDndTransformer implements Transformer { 147 148 /** 149 * Creates a new instance.<p> 150 */ 151 public CmsImageDndTransformer() { 152 153 // do nothing 154 155 } 156 157 /** 158 * @see org.apache.commons.collections.Transformer#transform(java.lang.Object) 159 */ 160 public Object transform(Object input) { 161 162 String result; 163 if (CmsJspContentAccessValueWrapper.isDirectEditEnabled(getCmsObject())) { 164 result = createImageDndAttr( 165 getRawContent().getFile().getStructureId(), 166 String.valueOf(input), 167 String.valueOf(getLocale())); 168 } else { 169 result = ""; 170 } 171 return result; 172 } 173 } 174 175 /** 176 * Provides a Map which lets the user access the list of element names from the selected locale in an XML content, 177 * the input is assumed to be a String that represents a Locale.<p> 178 */ 179 public class CmsLocaleNamesTransformer implements Transformer { 180 181 /** 182 * @see org.apache.commons.collections.Transformer#transform(java.lang.Object) 183 */ 184 public Object transform(Object input) { 185 186 Locale locale = CmsLocaleManager.getLocale(String.valueOf(input)); 187 188 return getRawContent().getNames(locale); 189 } 190 } 191 192 /** 193 * Provides a Map which lets the user access the RDFA tags for all values in the selected locale in an XML content, 194 * the input is assumed to be a String that represents a Locale.<p> 195 */ 196 public class CmsLocaleRdfaTransformer implements Transformer { 197 198 /** 199 * @see org.apache.commons.collections.Transformer#transform(java.lang.Object) 200 */ 201 public Object transform(Object input) { 202 203 Locale locale = CmsLocaleManager.getLocale(String.valueOf(input)); 204 Map<String, String> result; 205 if (getRawContent().hasLocale(locale)) { 206 result = CmsCollectionsGenericWrapper.createLazyMap(new CmsRdfaTransformer(locale)); 207 } else { 208 // return a map that always returns an empty string 209 result = CmsConstantMap.CONSTANT_EMPTY_STRING_MAP; 210 } 211 return result; 212 } 213 } 214 215 /** 216 * Provides a Map which lets the user access sub value Lists from the selected locale in an XML content, 217 * the input is assumed to be a String that represents a Locale.<p> 218 */ 219 public class CmsLocaleSubValueListTransformer implements Transformer { 220 221 /** 222 * @see org.apache.commons.collections.Transformer#transform(java.lang.Object) 223 */ 224 public Object transform(Object input) { 225 226 Locale locale = CmsJspElFunctions.convertLocale(input); 227 Map<String, List<CmsJspContentAccessValueWrapper>> result; 228 if (getRawContent().hasLocale(locale)) { 229 result = CmsCollectionsGenericWrapper.createLazyMap(new CmsSubValueListTransformer(locale)); 230 } else { 231 result = CmsConstantMap.CONSTANT_EMPTY_LIST_MAP; 232 } 233 return result; 234 } 235 } 236 237 /** 238 * Provides a Map which lets the user access value Lists from the selected locale in an XML content, 239 * the input is assumed to be a String that represents a Locale.<p> 240 */ 241 public class CmsLocaleValueListTransformer implements Transformer { 242 243 /** 244 * @see org.apache.commons.collections.Transformer#transform(java.lang.Object) 245 */ 246 public Object transform(Object input) { 247 248 Locale locale = CmsJspElFunctions.convertLocale(input); 249 Map<String, List<CmsJspContentAccessValueWrapper>> result; 250 if (getRawContent().hasLocale(locale)) { 251 result = CmsCollectionsGenericWrapper.createLazyMap(new CmsValueListTransformer(locale)); 252 } else { 253 result = CmsConstantMap.CONSTANT_EMPTY_LIST_MAP; 254 } 255 return result; 256 } 257 } 258 259 /** 260 * Provides a Map which lets the user access a value from the selected locale in an XML content, 261 * the input is assumed to be a String that represents a Locale.<p> 262 */ 263 public class CmsLocaleValueTransformer implements Transformer { 264 265 /** 266 * @see org.apache.commons.collections.Transformer#transform(java.lang.Object) 267 */ 268 public Object transform(Object input) { 269 270 Locale locale = CmsLocaleManager.getLocale(String.valueOf(input)); 271 Map<String, CmsJspContentAccessValueWrapper> result; 272 if (getRawContent().hasLocale(locale)) { 273 result = CmsCollectionsGenericWrapper.createLazyMap(new CmsValueTransformer(locale)); 274 } else { 275 result = CONSTANT_NULL_VALUE_WRAPPER_MAP; 276 } 277 return result; 278 } 279 } 280 281 /** 282 * Provides a Map which lets the user access the RDFA tag for a value in an XML content, 283 * the input is assumed to be a String that represents an xpath in the XML content.<p> 284 */ 285 public class CmsRdfaTransformer implements Transformer { 286 287 /** The selected locale. */ 288 private Locale m_selectedLocale; 289 290 /** 291 * Constructor with a locale.<p> 292 * 293 * @param locale the locale to use 294 */ 295 public CmsRdfaTransformer(Locale locale) { 296 297 m_selectedLocale = locale; 298 } 299 300 /** 301 * @see org.apache.commons.collections.Transformer#transform(java.lang.Object) 302 */ 303 public Object transform(Object input) { 304 305 if (CmsJspContentAccessValueWrapper.isDirectEditEnabled(getCmsObject())) { 306 return CmsContentService.getRdfaAttributes(getRawContent(), m_selectedLocale, String.valueOf(input)); 307 } else { 308 return ""; 309 } 310 } 311 } 312 313 /** 314 * Provides a Map which lets the user access sub value Lists in an XML content, 315 * the input is assumed to be a String that represents an xpath in the XML content.<p> 316 */ 317 public class CmsSubValueListTransformer implements Transformer { 318 319 /** The selected locale. */ 320 private Locale m_selectedLocale; 321 322 /** 323 * Constructor with a locale.<p> 324 * 325 * @param locale the locale to use 326 */ 327 public CmsSubValueListTransformer(Locale locale) { 328 329 m_selectedLocale = locale; 330 } 331 332 /** 333 * @see org.apache.commons.collections.Transformer#transform(java.lang.Object) 334 */ 335 public Object transform(Object input) { 336 337 List<I_CmsXmlContentValue> values = getRawContent().getSubValues(String.valueOf(input), m_selectedLocale); 338 List<CmsJspContentAccessValueWrapper> result = new ArrayList<CmsJspContentAccessValueWrapper>(); 339 Iterator<I_CmsXmlContentValue> i = values.iterator(); 340 while (i.hasNext()) { 341 // XML content API offers List of values only as Objects, must iterate them and create Strings 342 I_CmsXmlContentValue value = i.next(); 343 result.add(CmsJspContentAccessValueWrapper.createWrapper(getCmsObject(), value, null, null)); 344 } 345 return result; 346 } 347 } 348 349 /** 350 * Provides a Map which lets the user access value Lists in an XML content, 351 * the input is assumed to be a String that represents an xpath in the XML content.<p> 352 */ 353 public class CmsValueListTransformer implements Transformer { 354 355 /** The selected locale. */ 356 private Locale m_selectedLocale; 357 358 /** 359 * Constructor with a locale.<p> 360 * 361 * @param locale the locale to use 362 */ 363 public CmsValueListTransformer(Locale locale) { 364 365 m_selectedLocale = locale; 366 } 367 368 /** 369 * @see org.apache.commons.collections.Transformer#transform(java.lang.Object) 370 */ 371 public Object transform(Object input) { 372 373 List<I_CmsXmlContentValue> values = getRawContent().getValues(String.valueOf(input), m_selectedLocale); 374 List<CmsJspContentAccessValueWrapper> result = new ArrayList<CmsJspContentAccessValueWrapper>(); 375 Iterator<I_CmsXmlContentValue> i = values.iterator(); 376 while (i.hasNext()) { 377 // XML content API offers List of values only as Objects, must iterate them and create Strings 378 I_CmsXmlContentValue value = i.next(); 379 result.add(CmsJspContentAccessValueWrapper.createWrapper(getCmsObject(), value, null, null)); 380 } 381 return result; 382 } 383 } 384 385 /** 386 * Provides a Map which lets the user access a value in an XML content, 387 * the input is assumed to be a String that represents an xpath in the XML content.<p> 388 */ 389 public class CmsValueTransformer implements Transformer { 390 391 /** The selected locale. */ 392 private Locale m_selectedLocale; 393 394 /** 395 * Constructor with a locale.<p> 396 * 397 * @param locale the locale to use 398 */ 399 public CmsValueTransformer(Locale locale) { 400 401 m_selectedLocale = locale; 402 } 403 404 /** 405 * @see org.apache.commons.collections.Transformer#transform(java.lang.Object) 406 */ 407 public Object transform(Object input) { 408 409 I_CmsXmlContentValue value = getRawContent().getValue(String.valueOf(input), m_selectedLocale); 410 return CmsJspContentAccessValueWrapper.createWrapper( 411 getCmsObject(), 412 value, 413 getRawContent(), 414 (String)input, 415 m_selectedLocale); 416 } 417 } 418 419 /** Constant Map that always returns the {@link CmsJspContentAccessValueWrapper#NULL_VALUE_WRAPPER}.*/ 420 protected static final Map<String, CmsJspContentAccessValueWrapper> CONSTANT_NULL_VALUE_WRAPPER_MAP = new CmsConstantMap<String, CmsJspContentAccessValueWrapper>( 421 CmsJspContentAccessValueWrapper.NULL_VALUE_WRAPPER); 422 423 /** The categories assigned to the resource. */ 424 private CmsJspCategoryAccessBean m_categories; 425 426 /** The OpenCms context of the current user. */ 427 private CmsObject m_cms; 428 429 /** The XML content to access. */ 430 private I_CmsXmlDocument m_content; 431 432 /** Lazy map for the "has locale" check. */ 433 private Map<String, Boolean> m_hasLocale; 434 435 /** Lazy map for the "has locale value" check. */ 436 private Map<String, Map<String, Boolean>> m_hasLocaleValue; 437 438 /** Lazy map for imageDnd annotations. */ 439 private Map<String, String> m_imageDnd; 440 441 /** Lazy map from locales to JSON representations of the content. */ 442 private Map<Object, CmsJspJsonWrapper> m_json; 443 444 /** The locale used for accessing entries from the XML content, this may be a fallback default locale. */ 445 private Locale m_locale; 446 447 /** Lazy map with the locale names. */ 448 private Map<String, List<String>> m_localeNames; 449 450 /** Lazy map of RDFA maps by locale. */ 451 private Map<String, Map<String, String>> m_localeRdfa; 452 453 /** Lazy map with the locale sub value lists. */ 454 private Map<String, Map<String, List<CmsJspContentAccessValueWrapper>>> m_localeSubValueList; 455 456 /** Lazy map with the locale value. */ 457 private Map<String, Map<String, CmsJspContentAccessValueWrapper>> m_localeValue; 458 459 /** Lazy map with the locale value lists. */ 460 private Map<String, Map<String, List<CmsJspContentAccessValueWrapper>>> m_localeValueList; 461 462 /** The original locale requested for accessing entries from the XML content. */ 463 private Locale m_requestedLocale; 464 465 /** Resource the XML content is created from. */ 466 private CmsResource m_resource; 467 468 /** JSP EL access wrapped version of the resource the XML content is created from. */ 469 private CmsJspResourceWrapper m_resourceWrapper; 470 471 /** 472 * No argument constructor, required for a JavaBean.<p> 473 * 474 * You must call {@link #init(CmsObject, Locale, I_CmsXmlDocument, CmsResource)} and provide the 475 * required values when you use this constructor.<p> 476 * 477 * @see #init(CmsObject, Locale, I_CmsXmlDocument, CmsResource) 478 */ 479 public CmsJspContentAccessBean() { 480 481 // must call init() manually later 482 } 483 484 /** 485 * Creates a content access bean based on a Resource, using the current request context locale.<p> 486 * 487 * @param cms the OpenCms context of the current user 488 * @param resource the resource to create the content from 489 */ 490 public CmsJspContentAccessBean(CmsObject cms, CmsResource resource) { 491 492 this(cms, cms.getRequestContext().getLocale(), resource); 493 } 494 495 /** 496 * Creates a content access bean based on a Resource.<p> 497 * 498 * @param cms the OpenCms context of the current user 499 * @param locale the Locale to use when accessing the content 500 * @param resource the resource to create the content from 501 */ 502 public CmsJspContentAccessBean(CmsObject cms, Locale locale, CmsResource resource) { 503 504 init(cms, locale, null, resource); 505 } 506 507 /** 508 * Creates a content access bean based on an XML content object.<p> 509 * 510 * @param cms the OpenCms context of the current user 511 * @param locale the Locale to use when accessing the content 512 * @param content the content to access 513 */ 514 public CmsJspContentAccessBean(CmsObject cms, Locale locale, I_CmsXmlDocument content) { 515 516 init(cms, locale, content, content.getFile()); 517 } 518 519 /** 520 * Generates the HTML attribute "data-imagednd" that enables the ADE image drag and drop feature.<p> 521 * 522 * @param structureId the structure ID of the XML document to insert the image 523 * @param locale the locale to generate the image in 524 * @param imagePath the XML path to the image source node. 525 * 526 * @return the HTML attribute "data-imagednd" that enables the ADE image drag and drop feature 527 */ 528 protected static String createImageDndAttr(CmsUUID structureId, String imagePath, String locale) { 529 530 String attrValue = structureId + "|" + imagePath + "|" + locale; 531 String escapedAttrValue = CmsEncoder.escapeXml(attrValue); 532 return ("data-imagednd=\"" + escapedAttrValue + "\""); 533 } 534 535 /** 536 * Gets the list of locales for which there are attachments.<p> 537 * 538 * @return the list of locales for which there are attachments 539 */ 540 public List<String> getAttachmentLocales() { 541 542 return CmsJspContentAttachmentsBean.getAttachmentLocales(m_cms, m_resource); 543 } 544 545 /** 546 * Gets an attachment bean, trying to automatically find the right locale for the current page.<p> 547 * 548 * @return the attachments bean for the current page's locale 549 * @throws CmsException if something goes wrong 550 */ 551 public CmsJspContentAttachmentsBean getAttachments() throws CmsException { 552 553 return CmsJspContentAttachmentsBean.getAttachmentsForCurrentPage(m_cms, m_resource); 554 } 555 556 /** 557 * Gets a lazy map which maps locales to attachment beans for that locale.<p> 558 * 559 * @return the lazy map 560 */ 561 public Map<?, ?> getAttachmentsForLocale() { 562 563 return CmsCollectionsGenericWrapper.createLazyMap(new Transformer() { 564 565 @SuppressWarnings("synthetic-access") 566 public Object transform(Object arg0) { 567 568 String localeStr = (String)arg0; 569 return CmsJspContentAttachmentsBean.getAttachmentsForLocale(m_cms, m_resource, localeStr); 570 } 571 }); 572 } 573 574 /** 575 * Returns the OpenCms user context this bean was initialized with.<p> 576 * 577 * @return the OpenCms user context this bean was initialized with 578 */ 579 public CmsObject getCmsObject() { 580 581 return m_cms; 582 } 583 584 /** 585 * Returns the raw VFS file object the content accessed by this bean was created from.<p> 586 * 587 * This can be used to access information from the raw file on a JSP.<p> 588 * 589 * Usage example on a JSP with the JSTL:<pre> 590 * <cms:contentload ... > 591 * <cms:contentaccess var="content" /> 592 * Root path of the resource: ${content.file.rootPath} 593 * </cms:contentload></pre> 594 * 595 * @return the raw VFS file object the content accessed by this bean was created from 596 */ 597 public CmsFile getFile() { 598 599 return getRawContent().getFile(); 600 } 601 602 /** 603 * Returns the substituted link to the content file.<p> 604 * Use for detail page links.<p> 605 * 606 * @return the substituted link 607 */ 608 public String getFileLink() { 609 610 if (m_resource != null) { 611 return A_CmsJspValueWrapper.substituteLink(m_cms, m_cms.getSitePath(m_resource)); 612 } else { 613 return ""; 614 } 615 } 616 617 /** 618 * Returns the site path of the current resource, that is the result of 619 * {@link CmsObject#getSitePath(CmsResource)} with the resource 620 * obtained by {@link #getFile()}.<p> 621 * 622 * Usage example on a JSP with the JSTL:<pre> 623 * <cms:contentload ... > 624 * <cms:contentaccess var="content" /> 625 * Site path of the resource: "${content.filename}"; 626 * </cms:contentload></pre> 627 * 628 * @return the site path of the current resource 629 * 630 * @see CmsObject#getSitePath(CmsResource) 631 */ 632 public String getFilename() { 633 634 return m_cms.getSitePath(getRawContent().getFile()); 635 } 636 637 /** 638 * Returns a lazy initialized Map that provides Booleans that indicate if a specified Locale is available 639 * in the XML content.<p> 640 * 641 * The provided Map key is assumed to be a String that represents a Locale.<p> 642 * 643 * Usage example on a JSP with the JSTL:<pre> 644 * <cms:contentload ... > 645 * <cms:contentaccess var="content" /> 646 * <c:if test="${content.hasLocale['de']}" > 647 * The content has a "de" Locale! 648 * </c:if> 649 * </cms:contentload></pre> 650 * 651 * @return a lazy initialized Map that provides Booleans that indicate if a specified Locale is available 652 * in the XML content 653 */ 654 public Map<String, Boolean> getHasLocale() { 655 656 if (m_hasLocale == null) { 657 m_hasLocale = CmsCollectionsGenericWrapper.createLazyMap(new CmsHasLocaleTransformer()); 658 } 659 return m_hasLocale; 660 } 661 662 /** 663 * Returns a lazy initialized Map that provides a Map that provides Booleans that 664 * indicate if a value (xpath) is available in the XML content in the selected locale.<p> 665 * 666 * The first provided Map key is assumed to be a String that represents the Locale, 667 * the second provided Map key is assumed to be a String that represents the xpath to the value.<p> 668 * 669 * Usage example on a JSP with the JSTL:<pre> 670 * <cms:contentload ... > 671 * <cms:contentaccess var="content" /> 672 * <c:if test="${content.hasLocaleValue['de']['Title']}" > 673 * The content has a "Title" value in the "de" Locale! 674 * </c:if> 675 * </cms:contentload></pre> 676 * 677 * Please note that you can also test if a locale value exists like this:<pre> 678 * <c:if test="${content.value['de']['Title'].exists}" > ... </c:if></pre> 679 * 680 * @return a lazy initialized Map that provides a Map that provides Booleans that 681 * indicate if a value (xpath) is available in the XML content in the selected locale 682 * 683 * @see #getHasValue() 684 */ 685 public Map<String, Map<String, Boolean>> getHasLocaleValue() { 686 687 if (m_hasLocaleValue == null) { 688 m_hasLocaleValue = CmsCollectionsGenericWrapper.createLazyMap(new CmsHasLocaleValueTransformer()); 689 } 690 return m_hasLocaleValue; 691 } 692 693 /** 694 * Returns a lazy initialized Map that provides Booleans that 695 * indicate if a value (xpath) is available in the XML content in the current locale.<p> 696 * 697 * The provided Map key is assumed to be a String that represents the xpath to the value.<p> 698 * 699 * Usage example on a JSP with the JSTL:<pre> 700 * <cms:contentload ... > 701 * <cms:contentaccess var="content" /> 702 * <c:if test="${content.hasValue['Title']}" > 703 * The content has a "Title" value in the current locale! 704 * </c:if> 705 * </cms:contentload></pre> 706 * 707 * Please note that you can also test if a value exists like this:<pre> 708 * <c:if test="${content.value['Title'].exists}" > ... </c:if></pre> 709 * 710 * @return a lazy initialized Map that provides Booleans that 711 * indicate if a value (xpath) is available in the XML content in the current locale 712 * 713 * @see #getHasLocaleValue() 714 */ 715 public Map<String, Boolean> getHasValue() { 716 717 return getHasLocaleValue().get(getLocale()); 718 } 719 720 /** 721 * Returns the structure ID of the current resource, that is the ID of 722 * the resource obtained by {@link #getFile()}.<p> 723 * 724 * Usage example on a JSP with the JSTL:<pre> 725 * <cms:contentload ... > 726 * <cms:contentaccess var="content" /> 727 * Site path of the resource: "${content.id}"; 728 * </cms:contentload></pre> 729 * 730 * @return the structure ID of the current resource 731 * 732 * @see CmsResource#getStructureId() 733 */ 734 public CmsUUID getId() { 735 736 return getRawContent().getFile().getStructureId(); 737 } 738 739 /** 740 * Gets the lazy imageDnd map.<p> 741 * 742 * @return the lazy imageDnd map 743 */ 744 public Map<String, String> getImageDnd() { 745 746 if (m_imageDnd == null) { 747 m_imageDnd = CmsCollectionsGenericWrapper.createLazyMap(new CmsImageDndTransformer()); 748 } 749 return m_imageDnd; 750 } 751 752 /** 753 * Returns <code>true</code> in case the current user is allowed to edit the XML content.<p> 754 * 755 * If the check is performed from the online project, the user context is internally switched to an offline 756 * project. So this may return <code>true</code> even if the user is currently in the online project. 757 * 758 * "Allowed to edit" here requires "read" and "write" permission for the VFS resource the XML content was created from. 759 * It also requires that the VFS resource is not locked by another user. 760 * Moreover, the user must be able to access at least one "offline" project.<p> 761 * 762 * Intended for quick checks to for example show / hide edit buttons for user generated content.<p> 763 * 764 * @return <code>true</code> in case the current user is allowed to edit the XML content 765 */ 766 public boolean getIsEditable() { 767 768 boolean result = false; 769 try { 770 CmsObject cms; 771 if (m_cms.getRequestContext().getCurrentProject().isOnlineProject()) { 772 // we are in the online project, which means we must first switch to an offline project 773 // otherwise write permission checks will always return false 774 cms = OpenCms.initCmsObject(m_cms); 775 List<CmsProject> projects = OpenCms.getOrgUnitManager().getAllAccessibleProjects( 776 cms, 777 cms.getRequestContext().getOuFqn(), 778 false); 779 if ((projects != null) && (projects.size() > 0)) { 780 // there is at least one project available 781 for (CmsProject p : projects) { 782 // need to iterate because the online project will be part of the result list 783 if (!p.isOnlineProject()) { 784 cms.getRequestContext().setCurrentProject(p); 785 break; 786 } 787 } 788 } 789 } else { 790 // not in the online project, so just use the current project 791 cms = m_cms; 792 } 793 794 result = cms.hasPermissions( 795 m_resource, 796 CmsPermissionSet.ACCESS_WRITE, 797 false, 798 CmsResourceFilter.ONLY_VISIBLE_NO_DELETED); 799 if (result) { 800 // still need to check the lock status 801 CmsLock lock = cms.getLock(m_resource); 802 if (!lock.isLockableBy(cms.getRequestContext().getCurrentUser())) { 803 // resource is locked from a different user 804 result = false; 805 } 806 } 807 } catch (CmsException e) { 808 // should not happen, in case it does just assume not editable 809 } 810 return result; 811 } 812 813 /** 814 * Gets the JSON for the current locale. 815 * 816 * @return the JSON for the current locale 817 */ 818 public CmsJspJsonWrapper getJson() { 819 820 Locale locale = getLocale(); 821 return getLocaleJson().get(locale); 822 823 } 824 825 /** 826 * Returns the Locale this bean is using for content access, this may be a default fall back Locale.<p> 827 * 828 * @return the Locale this bean is using for content access, this may be a default fall back Locale 829 */ 830 public Locale getLocale() { 831 832 // check the content if the locale has not been set yet 833 if (m_locale == null) { 834 getRawContent(); 835 } 836 return m_locale; 837 } 838 839 /** 840 * Gets a lazy map from locales (can be provided as strings or java.util.Locale) to the (default) JSON representation of an XML content. 841 * 842 * @return the lazy map from locales to JSON strings 843 */ 844 public Map<Object, CmsJspJsonWrapper> getLocaleJson() { 845 846 if (m_json == null) { 847 m_json = CmsCollectionsGenericWrapper.createLazyMap(key -> { 848 CmsXmlContent content = (CmsXmlContent)getRawContent(); 849 850 Locale locale; 851 if (key instanceof Locale) { 852 locale = (Locale)key; 853 } else { 854 locale = CmsLocaleManager.getLocale("" + key); 855 } 856 if (content.hasLocale(locale)) { 857 CmsJsonRendererXmlContent renderer; 858 try { 859 renderer = new CmsJsonRendererXmlContent(m_cms); 860 Object jsonObj = renderer.render(content, locale); 861 return new CmsJspJsonWrapper(jsonObj); 862 } catch (Exception e) { 863 throw new RuntimeException(e); 864 } 865 } else { 866 return new CmsJspJsonWrapper(null); 867 } 868 }); 869 } 870 return m_json; 871 } 872 873 /** 874 * Returns a lazy initialized Map that provides a List with all available elements paths (Strings) 875 * used in this document in the selected locale.<p> 876 * 877 * The provided Map key is assumed to be a String that represents the Locale.<p> 878 * 879 * Usage example on a JSP with the JSTL:<pre> 880 * <cms:contentload ... > 881 * <cms:contentaccess var="content" /> 882 * <c:forEach items="${content.localeNames['de']}" var="elem"> 883 * <c:out value="${elem}" /> 884 * </c:forEach> 885 * </cms:contentload></pre> 886 * 887 * @return a lazy initialized Map that provides a Map that provides 888 * values from the XML content in the selected locale 889 * 890 * @see #getNames() 891 */ 892 public Map<String, List<String>> getLocaleNames() { 893 894 if (m_localeNames == null) { 895 m_localeNames = CmsCollectionsGenericWrapper.createLazyMap(new CmsLocaleNamesTransformer()); 896 } 897 return m_localeNames; 898 } 899 900 /** 901 * Returns the map of RDFA maps by locale.<p> 902 * 903 * @return the map of RDFA maps by locale 904 */ 905 public Map<String, Map<String, String>> getLocaleRdfa() { 906 907 if (m_localeRdfa == null) { 908 m_localeRdfa = CmsCollectionsGenericWrapper.createLazyMap(new CmsLocaleRdfaTransformer()); 909 } 910 return m_localeRdfa; 911 } 912 913 /** 914 * Returns a lazy initialized Map that provides a Map that provides Lists of direct sub values 915 * from the XML content in the selected locale.<p> 916 * 917 * The first provided Map key is assumed to be a String that represents the Locale, 918 * the second provided Map key is assumed to be a String that represents the xpath to the value.<p> 919 * 920 * Usage example on a JSP with the JSTL:<pre> 921 * <cms:contentload ... > 922 * <cms:contentaccess var="content" /> 923 * <c:forEach var="item" items="${content.localeSubValueList['de']['Items']}"> 924 * ${item} 925 * </c:forEach> 926 * </cms:contentload></pre> 927 * 928 * @return a lazy initialized Map that provides a Map that provides Lists of direct sub values 929 * from the XML content in the selected locale 930 * 931 * @see #getLocaleValue() 932 */ 933 public Map<String, Map<String, List<CmsJspContentAccessValueWrapper>>> getLocaleSubValueList() { 934 935 if (m_localeSubValueList == null) { 936 m_localeSubValueList = CmsCollectionsGenericWrapper.createLazyMap(new CmsLocaleSubValueListTransformer()); 937 } 938 return m_localeSubValueList; 939 } 940 941 /** 942 * Returns a lazy initialized Map that provides a Map that provides 943 * values from the XML content in the selected locale.<p> 944 * 945 * The first provided Map key is assumed to be a String that represents the Locale, 946 * the second provided Map key is assumed to be a String that represents the xpath to the value.<p> 947 * 948 * Usage example on a JSP with the JSTL:<pre> 949 * <cms:contentload ... > 950 * <cms:contentaccess var="content" /> 951 * The Title in Locale "de": ${content.localeValue['de']['Title']} 952 * </cms:contentload></pre> 953 * 954 * @return a lazy initialized Map that provides a Map that provides 955 * values from the XML content in the selected locale 956 * 957 * @see #getValue() 958 */ 959 public Map<String, Map<String, CmsJspContentAccessValueWrapper>> getLocaleValue() { 960 961 if (m_localeValue == null) { 962 m_localeValue = CmsCollectionsGenericWrapper.createLazyMap(new CmsLocaleValueTransformer()); 963 } 964 return m_localeValue; 965 } 966 967 /** 968 * Returns a lazy initialized Map that provides a Map that provides Lists of values 969 * from the XML content in the selected locale.<p> 970 * 971 * The first provided Map key is assumed to be a String that represents the Locale, 972 * the second provided Map key is assumed to be a String that represents the xpath to the value.<p> 973 * 974 * Usage example on a JSP with the JSTL:<pre> 975 * <cms:contentload ... > 976 * <cms:contentaccess var="content" /> 977 * <c:forEach var="teaser" items="${content.localeValueList['de']['Teaser']}"> 978 * ${teaser} 979 * </c:forEach> 980 * </cms:contentload></pre> 981 * 982 * @return a lazy initialized Map that provides a Map that provides Lists of values 983 * from the XML content in the selected locale 984 * 985 * @see #getLocaleValue() 986 */ 987 public Map<String, Map<String, List<CmsJspContentAccessValueWrapper>>> getLocaleValueList() { 988 989 if (m_localeValueList == null) { 990 m_localeValueList = CmsCollectionsGenericWrapper.createLazyMap(new CmsLocaleValueListTransformer()); 991 } 992 return m_localeValueList; 993 } 994 995 /** 996 * Returns a list with all available elements paths (Strings) used in this document 997 * in the current locale.<p> 998 * 999 * Usage example on a JSP with the JSTL:<pre> 1000 * <cms:contentload ... > 1001 * <cms:contentaccess var="content" /> 1002 * <c:forEach items="${content.names}" var="elem"> 1003 * <c:out value="${elem}" /> 1004 * </c:forEach> 1005 * </cms:contentload></pre> 1006 * 1007 * @return a list with all available elements paths (Strings) used in this document in the current locale 1008 * 1009 * @see #getLocaleNames() 1010 */ 1011 public List<String> getNames() { 1012 1013 return getLocaleNames().get(getLocale()); 1014 } 1015 1016 /** 1017 * Prints the XPaths of the available content values into an HTML ul list.<p> 1018 * 1019 * @return the HTML list of XPaths 1020 */ 1021 public String getPrintStructure() { 1022 1023 if (getRawContent().hasLocale(m_requestedLocale)) { 1024 List<I_CmsXmlContentValue> values = new ArrayList<I_CmsXmlContentValue>( 1025 getRawContent().getValues(m_requestedLocale)); 1026 Collections.sort(values, new Comparator<I_CmsXmlContentValue>() { 1027 1028 public int compare(I_CmsXmlContentValue arg0, I_CmsXmlContentValue arg1) { 1029 1030 return arg0.getPath().compareTo(arg1.getPath()); 1031 } 1032 }); 1033 1034 StringBuffer buffer = new StringBuffer("<ul>\n"); 1035 for (I_CmsXmlContentValue value : values) { 1036 buffer.append("<li>").append(value.getPath()).append("</li>\n"); 1037 } 1038 buffer.append("</ul>"); 1039 return buffer.toString(); 1040 } else { 1041 return ""; 1042 } 1043 } 1044 1045 /** 1046 * Returns the raw XML content object that is accessed by this bean.<p> 1047 * 1048 * @return the raw XML content object that is accessed by this bean 1049 */ 1050 public I_CmsXmlDocument getRawContent() { 1051 1052 if (m_content == null) { 1053 // content has not been provided, must unmarshal XML first 1054 CmsFile file; 1055 try { 1056 file = m_cms.readFile(m_resource); 1057 if (CmsResourceTypeXmlPage.isXmlPage(file)) { 1058 // this is an XML page 1059 m_content = CmsXmlPageFactory.unmarshal(m_cms, file); 1060 } else { 1061 // this is an XML content 1062 m_content = CmsXmlContentFactory.unmarshal(m_cms, file); 1063 } 1064 } catch (CmsException e) { 1065 // this usually should not happen, as the resource already has been read by the current user 1066 // and we just upgrade it to a File 1067 throw new CmsRuntimeException( 1068 Messages.get().container(Messages.ERR_XML_CONTENT_UNMARSHAL_1, m_resource.getRootPath()), 1069 e); 1070 } 1071 } 1072 1073 // make sure a valid locale is used 1074 if (m_locale == null) { 1075 m_locale = OpenCms.getLocaleManager().getBestMatchingLocale( 1076 m_requestedLocale, 1077 OpenCms.getLocaleManager().getDefaultLocales(m_cms, m_cms.getRequestContext().getUri()), 1078 m_content.getLocales()); 1079 } 1080 1081 return m_content; 1082 } 1083 1084 /** 1085 * Returns RDFA by value name map.<p> 1086 * 1087 * @return RDFA by value name map 1088 */ 1089 public Map<String, String> getRdfa() { 1090 1091 return getLocaleRdfa().get(getLocale()); 1092 } 1093 1094 /** 1095 * Reads and returns the categories assigned to the content's VFS resource. 1096 * @return the categories assigned to the content's VFS resource. 1097 */ 1098 public CmsJspCategoryAccessBean getReadCategories() { 1099 1100 if (null == m_categories) { 1101 m_categories = readCategories(); 1102 } 1103 return m_categories; 1104 } 1105 1106 /** 1107 * Returns the (wrapped) content resource for the content accessed by this bean.<p> 1108 * 1109 * @return the (wrapped) content resource for the content accessed by this bean 1110 */ 1111 public CmsJspResourceWrapper getResource() { 1112 1113 if (m_resourceWrapper == null) { 1114 m_resourceWrapper = CmsJspResourceWrapper.wrap(m_cms, m_resource); 1115 } 1116 return m_resourceWrapper; 1117 } 1118 1119 /** 1120 * Returns a lazy initialized Map that provides Lists of direct sub values 1121 * of the given value from the XML content in the current locale.<p> 1122 * 1123 * The provided Map key is assumed to be a String that represents the xpath to the value. 1124 * Use this method in case you want to iterate over a List of sub values from the XML content.<p> 1125 * 1126 * Usage example on a JSP with the JSTL:<pre> 1127 * <cms:contentload ... > 1128 * <cms:contentaccess var="content" /> 1129 * <c:forEach var="teaser" items="${content.subValueList['Items']}"> 1130 * ${item} 1131 * </c:forEach> 1132 * </cms:contentload></pre> 1133 * 1134 * @return a lazy initialized Map that provides Lists of values from the XML content in the current locale 1135 * 1136 * @see #getLocaleValueList() 1137 */ 1138 public Map<String, List<CmsJspContentAccessValueWrapper>> getSubValueList() { 1139 1140 return getLocaleSubValueList().get(getLocale()); 1141 } 1142 1143 /** 1144 * Returns the resource type name.<p> 1145 * 1146 * @return the resource type name 1147 */ 1148 public String getTypeName() { 1149 1150 if (m_resource != null) { 1151 return OpenCms.getResourceManager().getResourceType(m_resource).getTypeName(); 1152 } else { 1153 return ""; 1154 } 1155 } 1156 1157 /** 1158 * Returns a lazy initialized Map that provides values from the XML content in the current locale.<p> 1159 * 1160 * The provided Map key is assumed to be a String that represents the xpath to the value.<p> 1161 * 1162 * Usage example on a JSP with the JSTL:<pre> 1163 * <cms:contentload ... > 1164 * <cms:contentaccess var="content" /> 1165 * The Title: ${content.value['Title']} 1166 * </cms:contentload></pre> 1167 * 1168 * @return a lazy initialized Map that provides values from the XML content in the current locale 1169 * 1170 * @see #getLocaleValue() 1171 */ 1172 public Map<String, CmsJspContentAccessValueWrapper> getValue() { 1173 1174 return getLocaleValue().get(getLocale()); 1175 } 1176 1177 /** 1178 * Returns a lazy initialized Map that provides Lists of values from the XML content in the current locale.<p> 1179 * 1180 * The provided Map key is assumed to be a String that represents the xpath to the value. 1181 * Use this method in case you want to iterate over a List of values form the XML content.<p> 1182 * 1183 * Usage example on a JSP with the JSTL:<pre> 1184 * <cms:contentload ... > 1185 * <cms:contentaccess var="content" /> 1186 * <c:forEach var="teaser" items="${content.valueList['Teaser']}"> 1187 * ${teaser} 1188 * </c:forEach> 1189 * </cms:contentload></pre> 1190 * 1191 * @return a lazy initialized Map that provides Lists of values from the XML content in the current locale 1192 * 1193 * @see #getLocaleValueList() 1194 */ 1195 public Map<String, List<CmsJspContentAccessValueWrapper>> getValueList() { 1196 1197 return getLocaleValueList().get(getLocale()); 1198 } 1199 1200 /** 1201 * Returns an instance of a VFS access bean, 1202 * initialized with the OpenCms user context this bean was created with.<p> 1203 * 1204 * @return an instance of a VFS access bean, 1205 * initialized with the OpenCms user context this bean was created with 1206 */ 1207 public CmsJspVfsAccessBean getVfs() { 1208 1209 return CmsJspVfsAccessBean.create(m_cms); 1210 } 1211 1212 /** 1213 * Returns the (wrapped) content resource for the content accessed by this bean.<p> 1214 * 1215 * @return the (wrapped) content resource for the content accessed by this bean 1216 * 1217 * @deprecated use {@link #getResource()} instead 1218 */ 1219 @Deprecated 1220 public CmsJspResourceWrapper getWrap() { 1221 1222 return getResource(); 1223 } 1224 1225 /** 1226 * Initialize this instance.<p> 1227 * 1228 * @param cms the OpenCms context of the current user 1229 * @param locale the Locale to use when accessing the content 1230 * @param content the XML content to access 1231 * @param resource the resource to create the content from 1232 */ 1233 public void init(CmsObject cms, Locale locale, I_CmsXmlDocument content, CmsResource resource) { 1234 1235 m_cms = cms; 1236 m_requestedLocale = locale; 1237 m_content = content; 1238 m_resource = resource; 1239 } 1240 1241 /** 1242 * Reads the categories assigned to the content's VFS resource. 1243 * @return the categories assigned to the content's VFS resource. 1244 */ 1245 private CmsJspCategoryAccessBean readCategories() { 1246 1247 return new CmsJspCategoryAccessBean(getCmsObject(), m_resource); 1248 } 1249}