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