001/* 002 * This library is part of OpenCms - 003 * the Open Source Content Management System 004 * 005 * Copyright (c) Alkacon Software GmbH & Co. KG (https://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 GmbH & Co. KG, please see the 018 * company website: https://www.alkacon.com 019 * 020 * For further information about OpenCms, please see the 021 * project website: https://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.file.wrapper; 029 030import org.opencms.file.CmsFile; 031import org.opencms.file.CmsObject; 032import org.opencms.file.CmsProperty; 033import org.opencms.file.CmsPropertyDefinition; 034import org.opencms.file.CmsResource; 035import org.opencms.file.CmsResource.CmsResourceCopyMode; 036import org.opencms.file.CmsResource.CmsResourceDeleteMode; 037import org.opencms.file.CmsResourceFilter; 038import org.opencms.file.CmsVfsResourceNotFoundException; 039import org.opencms.file.types.CmsResourceTypeFolder; 040import org.opencms.file.types.CmsResourceTypePlain; 041import org.opencms.file.types.CmsResourceTypeXmlPage; 042import org.opencms.file.types.I_CmsResourceType; 043import org.opencms.i18n.CmsEncoder; 044import org.opencms.i18n.CmsLocaleManager; 045import org.opencms.loader.CmsLoaderException; 046import org.opencms.loader.CmsResourceManager; 047import org.opencms.lock.CmsLock; 048import org.opencms.main.CmsException; 049import org.opencms.main.CmsIllegalArgumentException; 050import org.opencms.main.OpenCms; 051import org.opencms.util.CmsFileUtil; 052import org.opencms.util.CmsStringUtil; 053import org.opencms.xml.page.CmsXmlPage; 054import org.opencms.xml.page.CmsXmlPageFactory; 055 056import java.io.UnsupportedEncodingException; 057import java.util.ArrayList; 058import java.util.Iterator; 059import java.util.List; 060import java.util.Locale; 061 062/** 063 * A resource type wrapper for xml page files, which explodes the xml pages to folders.<p> 064 * 065 * Every resource of type "xmlpage" becomes a folder with the same name. That folder 066 * contains the locales of the xml page as folders too. In the locale folder there are 067 * the elements for that locale as files. The files have the names of the elements with the 068 * extension "html". Additionaly there is a file in the root folder of that xml page that 069 * contains the controlcode of the xml page. This file has the name "controlcode.xml".<p> 070 * 071 * @since 6.5.6 072 */ 073public class CmsResourceWrapperXmlPage extends A_CmsResourceWrapper { 074 075 /** The extension to use for elements. */ 076 private static final String EXTENSION_ELEMENT = "html"; 077 078 /** The name of the element to use for the controlcode. */ 079 private static final String NAME_ELEMENT_CONTROLCODE = "controlcode.xml"; 080 081 /** Table with the states of the virtual files. */ 082 private static final List<String> TMP_FILE_TABLE = new ArrayList<String>(); 083 084 /** 085 * @see org.opencms.file.wrapper.A_CmsResourceWrapper#addResourcesToFolder(CmsObject, String, CmsResourceFilter) 086 */ 087 @Override 088 public List<CmsResource> addResourcesToFolder(CmsObject cms, String resourcename, CmsResourceFilter filter) 089 throws CmsException { 090 091 CmsResource xmlPage = findXmlPage(cms, resourcename); 092 if (xmlPage != null) { 093 String path = getSubPath(cms, xmlPage, resourcename); 094 String rootPath = cms.getRequestContext().removeSiteRoot(xmlPage.getRootPath()); 095 096 ArrayList<CmsResource> ret = new ArrayList<CmsResource>(); 097 098 CmsFile file = cms.readFile(xmlPage); 099 CmsXmlPage xml = CmsXmlPageFactory.unmarshal(cms, file); 100 101 if (CmsStringUtil.isEmptyOrWhitespaceOnly(path)) { 102 103 // sub path is empty -> return all existing locales for the resource 104 if (file.getLength() == 0) { 105 return ret; 106 } 107 108 List<Locale> locales = xml.getLocales(); 109 Iterator<Locale> iter1 = locales.iterator(); 110 while (iter1.hasNext()) { 111 Locale locale = iter1.next(); 112 ret.add(getResourceForLocale(xmlPage, locale)); 113 } 114 115 int plainId = OpenCms.getResourceManager().getResourceType( 116 CmsResourceTypePlain.getStaticTypeName()).getTypeId(); 117 // check temp file table to add virtual file 118 Iterator<String> iter2 = getVirtualFiles().iterator(); 119 while (iter2.hasNext()) { 120 121 String virtualFileName = iter2.next(); 122 String virtualFilePath = rootPath + "/" + virtualFileName; 123 124 if (!TMP_FILE_TABLE.contains(virtualFilePath)) { 125 126 // read the control code resource 127 if (virtualFileName.equals(NAME_ELEMENT_CONTROLCODE)) { 128 129 CmsWrappedResource wrap = new CmsWrappedResource(xmlPage); 130 wrap.setRootPath(xmlPage.getRootPath() + "/" + NAME_ELEMENT_CONTROLCODE); 131 wrap.setTypeId(plainId); 132 133 CmsFile tmpFile = wrap.getFile(); 134 tmpFile.setContents(file.getContents()); 135 ret.add(tmpFile); 136 } 137 } 138 } 139 } else { 140 // sub path is a locale -> return all elements for this locale 141 Locale locale = CmsLocaleManager.getLocale(path); 142 List<String> names = xml.getNames(locale); 143 Iterator<String> iter = names.iterator(); 144 while (iter.hasNext()) { 145 String name = iter.next(); 146 String content = xml.getStringValue(cms, name, locale); 147 String fullPath = xmlPage.getRootPath() + "/" + path + "/" + name + "." + EXTENSION_ELEMENT; 148 content = prepareContent(content, cms, xmlPage, fullPath); 149 150 int length = content.length(); 151 try { 152 length = content.getBytes(CmsLocaleManager.getResourceEncoding(cms, xmlPage)).length; 153 } catch (UnsupportedEncodingException e) { 154 // this will never happen since UTF-8 is always supported 155 } 156 157 ret.add(getResourceForElement(xmlPage, fullPath, length)); 158 } 159 160 } 161 162 return ret; 163 } 164 165 return null; 166 } 167 168 /** 169 * @see org.opencms.file.wrapper.A_CmsResourceWrapper#copyResource(org.opencms.file.CmsObject, java.lang.String, java.lang.String, org.opencms.file.CmsResource.CmsResourceCopyMode) 170 */ 171 @Override 172 public boolean copyResource(CmsObject cms, String source, String destination, CmsResourceCopyMode siblingMode) 173 throws CmsException, CmsIllegalArgumentException { 174 175 // only allow copying of xml pages at whole or locales and elements inside the same xml page 176 CmsResource srcXmlPage = findXmlPage(cms, source); 177 178 if (srcXmlPage != null) { 179 String srcPath = getSubPath(cms, srcXmlPage, source); 180 181 // if the source is the xml page itself just copy the resource 182 if (CmsStringUtil.isEmptyOrWhitespaceOnly(srcPath)) { 183 cms.copyResource(source, destination, siblingMode); 184 return true; 185 } else { 186 187 // only a locale or an element should be copied 188 CmsResource destXmlPage = findXmlPage(cms, destination); 189 if (srcXmlPage.equals(destXmlPage)) { 190 191 // copying inside the same xml page resource 192 String destPath = getSubPath(cms, destXmlPage, destination); 193 194 String[] srcTokens = srcPath.split("/"); 195 String[] destTokens = destPath.split("/"); 196 197 if (srcTokens.length == destTokens.length) { 198 199 CmsFile srcFile = cms.readFile(srcXmlPage); 200 CmsXmlPage srcXml = CmsXmlPageFactory.unmarshal(cms, srcFile); 201 202 if (srcTokens.length == 1) { 203 204 if (srcTokens[0].equals(NAME_ELEMENT_CONTROLCODE)) { 205 206 // do nothing 207 } else { 208 209 // copy locale 210 srcXml.copyLocale( 211 CmsLocaleManager.getLocale(srcTokens[0]), 212 CmsLocaleManager.getLocale(destTokens[0])); 213 } 214 } else if (srcTokens.length == 2) { 215 216 // TODO: copy element 217 } 218 219 // write files 220 srcFile.setContents(srcXml.marshal()); 221 cms.writeFile(srcFile); 222 223 return true; 224 } else { 225 226 // TODO: error: destination path is invalid 227 } 228 } else { 229 230 // TODO: error: copying only allowed inside the same xml page 231 } 232 } 233 } 234 235 return false; 236 } 237 238 /** 239 * @see org.opencms.file.wrapper.A_CmsResourceWrapper#createResource(org.opencms.file.CmsObject, java.lang.String, int, byte[], java.util.List) 240 */ 241 @Override 242 public CmsResource createResource( 243 CmsObject cms, 244 String resourcename, 245 int type, 246 byte[] content, 247 List<CmsProperty> properties) 248 throws CmsException, CmsIllegalArgumentException { 249 250 // cut off trailing slash 251 if (resourcename.endsWith("/")) { 252 resourcename = resourcename.substring(0, resourcename.length() - 1); 253 } 254 255 // creating new xml pages if type is a folder and the name ends with .html 256 if (resourcename.endsWith(".html") 257 && (type == OpenCms.getResourceManager().getResourceType( 258 CmsResourceTypeFolder.getStaticTypeName()).getTypeId())) { 259 260 // mark in temp file table that the visual files does not exist yet 261 Iterator<String> iter = getVirtualFiles().iterator(); 262 while (iter.hasNext()) { 263 TMP_FILE_TABLE.add(resourcename + "/" + iter.next()); 264 } 265 266 return cms.createResource( 267 resourcename, 268 OpenCms.getResourceManager().getResourceType(CmsResourceTypeXmlPage.getStaticTypeName()).getTypeId()); 269 } 270 271 // find the xml page this is for 272 CmsResource xmlPage = findXmlPage(cms, resourcename); 273 if (xmlPage != null) { 274 275 // get the path below the xml page 276 String path = getSubPath(cms, xmlPage, resourcename); 277 278 // and the path without the site root 279 String rootPath = cms.getRequestContext().removeSiteRoot(xmlPage.getRootPath()); 280 281 CmsFile file = cms.readFile(xmlPage); 282 CmsXmlPage xml = CmsXmlPageFactory.unmarshal(cms, file); 283 284 // mark virtual files as created in temp file table 285 if (getVirtualFiles().contains(path)) { 286 TMP_FILE_TABLE.remove(resourcename); 287 288 // at least lock file, because creating resources usually locks the resource 289 cms.lockResource(rootPath); 290 291 return file; 292 } 293 294 String[] tokens = path.split("/"); 295 if (tokens.length == 1) { 296 297 Locale locale = CmsLocaleManager.getLocale(tokens[0]); 298 299 // workaround: empty xmlpages always have the default locale "en" set 300 if (file.getLength() == 0) { 301 Iterator<Locale> iter = xml.getLocales().iterator(); 302 while (iter.hasNext()) { 303 xml.removeLocale(iter.next()); 304 } 305 } 306 307 // create new locale 308 xml.addLocale(cms, locale); 309 310 // save the xml page 311 file.setContents(xml.marshal()); 312 313 // lock the resource 314 cms.lockResource(rootPath); 315 316 // write file 317 cms.writeFile(file); 318 319 } else if (tokens.length == 2) { 320 321 String name = tokens[1]; 322 if (name.endsWith(EXTENSION_ELEMENT)) { 323 name = name.substring(0, name.length() - EXTENSION_ELEMENT.length() - 1); 324 } 325 326 // create new element 327 xml.addValue(name, CmsLocaleManager.getLocale(tokens[0])); 328 329 // set the content 330 xml.setStringValue( 331 cms, 332 name, 333 CmsLocaleManager.getLocale(tokens[0]), 334 getStringValue(cms, file, content)); 335 336 // save the xml page 337 file.setContents(xml.marshal()); 338 339 // lock the resource 340 cms.lockResource(rootPath); 341 342 // write file 343 cms.writeFile(file); 344 } 345 346 return file; 347 } 348 349 return null; 350 } 351 352 /** 353 * @see org.opencms.file.wrapper.A_CmsResourceWrapper#deleteResource(CmsObject, String, org.opencms.file.CmsResource.CmsResourceDeleteMode) 354 */ 355 @Override 356 public boolean deleteResource(CmsObject cms, String resourcename, CmsResourceDeleteMode siblingMode) 357 throws CmsException { 358 359 // find the xml page this is for 360 CmsResource xmlPage = findXmlPage(cms, resourcename); 361 if (xmlPage != null) { 362 363 // cut off trailing slash 364 if (resourcename.endsWith("/")) { 365 resourcename = resourcename.substring(0, resourcename.length() - 1); 366 } 367 368 // get the path below the xml page 369 String path = getSubPath(cms, xmlPage, resourcename); 370 371 // if sub path is empty 372 if (CmsStringUtil.isEmptyOrWhitespaceOnly(path)) { 373 374 // delete the xml page itself 375 cms.deleteResource(resourcename, siblingMode); 376 377 // remove all virtual files for this resource 378 Iterator<String> iter = getVirtualFiles().iterator(); 379 while (iter.hasNext()) { 380 TMP_FILE_TABLE.remove(resourcename + "/" + iter.next()); 381 } 382 383 return true; 384 } 385 386 CmsFile file = cms.readFile(xmlPage); 387 CmsXmlPage xml = CmsXmlPageFactory.unmarshal(cms, file); 388 389 String[] tokens = path.split("/"); 390 if (tokens.length == 1) { 391 392 // deleting a virtual file 393 if (getVirtualFiles().contains(tokens[0])) { 394 395 // mark the virtual file in the temp file table as deleted 396 TMP_FILE_TABLE.add(resourcename); 397 } else { 398 399 // delete locale 400 xml.removeLocale(CmsLocaleManager.getLocale(tokens[0])); 401 402 // save the xml page 403 file.setContents(xml.marshal()); 404 cms.writeFile(file); 405 } 406 407 } else if (tokens.length == 2) { 408 409 String name = tokens[1]; 410 if (name.endsWith(EXTENSION_ELEMENT)) { 411 name = name.substring(0, name.length() - EXTENSION_ELEMENT.length() - 1); 412 } 413 414 // delete element 415 xml.removeValue(name, CmsLocaleManager.getLocale(tokens[0])); 416 417 // save the xml page 418 file.setContents(xml.marshal()); 419 cms.writeFile(file); 420 } 421 422 return true; 423 } 424 425 return false; 426 } 427 428 /** 429 * @see org.opencms.file.wrapper.A_CmsResourceWrapper#getLock(org.opencms.file.CmsObject, org.opencms.file.CmsResource) 430 */ 431 @Override 432 public CmsLock getLock(CmsObject cms, CmsResource resource) throws CmsException { 433 434 CmsResource xmlPage = cms.readResource(resource.getStructureId()); 435 //CmsResource xmlPage = findXmlPage(cms, resource.getRootPath()); 436 if (xmlPage != null) { 437 438 I_CmsResourceType resType = OpenCms.getResourceManager().getResourceType(xmlPage.getTypeId()); 439 if (resType instanceof CmsResourceTypeXmlPage) { 440 return cms.getLock(xmlPage); 441 } 442 } 443 444 return null; 445 } 446 447 /** 448 * @see org.opencms.file.wrapper.I_CmsResourceWrapper#isWrappedResource(org.opencms.file.CmsObject, org.opencms.file.CmsResource) 449 */ 450 public boolean isWrappedResource(CmsObject cms, CmsResource res) { 451 452 try { 453 I_CmsResourceType resType = OpenCms.getResourceManager().getResourceType(res.getTypeId()); 454 if (resType instanceof CmsResourceTypeXmlPage) { 455 return true; 456 } 457 } catch (CmsException ex) { 458 // noop 459 } 460 461 return false; 462 } 463 464 /** 465 * @see org.opencms.file.wrapper.A_CmsResourceWrapper#lockResource(org.opencms.file.CmsObject, java.lang.String, boolean) 466 */ 467 @Override 468 public boolean lockResource(CmsObject cms, String resourcename, boolean temporary) throws CmsException { 469 470 CmsResource res = findXmlPage(cms, resourcename); 471 if (res != null) { 472 String path = cms.getRequestContext().removeSiteRoot(res.getRootPath()); 473 if (temporary) { 474 cms.lockResourceTemporary(path); 475 } else { 476 cms.lockResource(path); 477 } 478 return true; 479 } 480 481 return false; 482 } 483 484 /** 485 * @see org.opencms.file.wrapper.A_CmsResourceWrapper#moveResource(org.opencms.file.CmsObject, java.lang.String, java.lang.String) 486 */ 487 @Override 488 public boolean moveResource(CmsObject cms, String source, String destination) 489 throws CmsException, CmsIllegalArgumentException { 490 491 // only allow copying of xml pages at whole or locales and elements inside the same xml page 492 CmsResource srcXmlPage = findXmlPage(cms, source); 493 494 if (srcXmlPage != null) { 495 String srcPath = getSubPath(cms, srcXmlPage, source); 496 497 // if the source is the xml page itself just copy the resource 498 if (CmsStringUtil.isEmptyOrWhitespaceOnly(srcPath)) { 499 cms.moveResource(source, destination); 500 return true; 501 } else { 502 503 // only a locale or an element should be copied 504 CmsResource destXmlPage = findXmlPage(cms, destination); 505 if (srcXmlPage.equals(destXmlPage)) { 506 507 // copying inside the same xml page resource 508 String destPath = getSubPath(cms, destXmlPage, destination); 509 510 String[] srcTokens = srcPath.split("/"); 511 String[] destTokens = destPath.split("/"); 512 513 if (srcTokens.length == destTokens.length) { 514 515 CmsFile srcFile = cms.readFile(srcXmlPage); 516 CmsXmlPage srcXml = CmsXmlPageFactory.unmarshal(cms, srcFile); 517 518 if (srcTokens.length == 1) { 519 520 // copy locale 521 srcXml.moveLocale( 522 CmsLocaleManager.getLocale(srcTokens[0]), 523 CmsLocaleManager.getLocale(destTokens[0])); 524 } else if (srcTokens.length == 2) { 525 526 // TODO: move element 527 } 528 529 // write file 530 srcFile.setContents(srcXml.marshal()); 531 cms.writeFile(srcFile); 532 } else { 533 534 // TODO: error: destination path is invalid 535 } 536 } else { 537 538 // TODO: error: moving only allowed inside the same xml page 539 } 540 } 541 542 return true; 543 } 544 545 return false; 546 } 547 548 /** 549 * @see org.opencms.file.wrapper.A_CmsResourceWrapper#readFile(CmsObject, String, CmsResourceFilter) 550 */ 551 @Override 552 public CmsFile readFile(CmsObject cms, String resourcename, CmsResourceFilter filter) throws CmsException { 553 554 // find the xml page this is for 555 CmsResource xmlPage = findXmlPage(cms, resourcename); 556 if (xmlPage != null) { 557 558 // cut off trailing slash 559 if (resourcename.endsWith("/")) { 560 resourcename = resourcename.substring(0, resourcename.length() - 1); 561 } 562 563 // get the path below the xml page 564 String path = getSubPath(cms, xmlPage, resourcename); 565 566 String[] tokens = path.split("/"); 567 if (tokens.length == 1) { 568 569 CmsFile file = cms.readFile(xmlPage); 570 571 // check temp file table to remove deleted virtual files 572 if (TMP_FILE_TABLE.contains(resourcename)) { 573 return null; 574 } 575 576 // read the control code resource 577 if (tokens[0].equals(NAME_ELEMENT_CONTROLCODE)) { 578 579 CmsWrappedResource wrap = new CmsWrappedResource(xmlPage); 580 wrap.setRootPath(xmlPage.getRootPath() + "/" + NAME_ELEMENT_CONTROLCODE); 581 582 CmsFile ret = wrap.getFile(); 583 ret.setContents(file.getContents()); 584 return ret; 585 } 586 } else if (tokens.length == 2) { 587 588 CmsFile file = cms.readFile(xmlPage); 589 CmsXmlPage xml = CmsXmlPageFactory.unmarshal(cms, file); 590 591 // cut off the html suffix 592 String name = tokens[1]; 593 if (name.endsWith("." + EXTENSION_ELEMENT)) { 594 name = name.substring(0, name.length() - 5); 595 } 596 597 if (xml.hasValue(name, CmsLocaleManager.getLocale(tokens[0]))) { 598 599 String contentString = xml.getStringValue(cms, name, CmsLocaleManager.getLocale(tokens[0])); 600 String fullPath = xmlPage.getRootPath() + "/" + tokens[0] + "/" + name + "." + EXTENSION_ELEMENT; 601 contentString = prepareContent(contentString, cms, xmlPage, fullPath); 602 603 byte[] content; 604 try { 605 content = contentString.getBytes(CmsLocaleManager.getResourceEncoding(cms, xmlPage)); 606 } catch (UnsupportedEncodingException e) { 607 // should never happen 608 content = contentString.getBytes(); 609 } 610 CmsResource resElem = getResourceForElement(xmlPage, fullPath, content.length); 611 CmsFile fileElem = new CmsFile(resElem); 612 613 fileElem.setContents(content); 614 return fileElem; 615 } 616 } 617 } 618 619 return null; 620 } 621 622 /** 623 * @see org.opencms.file.wrapper.A_CmsResourceWrapper#readResource(CmsObject, String, CmsResourceFilter) 624 */ 625 @Override 626 public CmsResource readResource(CmsObject cms, String resourcename, CmsResourceFilter filter) throws CmsException { 627 628 try { 629 630 // try to read the resource for the resource name 631 CmsResource res = null; 632 try { 633 // catch this exception to try to read the resource again if it fails 634 res = cms.readResource(resourcename, filter); 635 } catch (CmsException e) { 636 // read resource failed, so check if the resource name ends with a slash 637 if (resourcename.endsWith("/")) { 638 // try to read resource without a slash 639 resourcename = CmsFileUtil.removeTrailingSeparator(resourcename); 640 // try to read the resource name without ending slash 641 res = cms.readResource(resourcename, filter); 642 } else { 643 // throw the exception which caused this catch block 644 throw e; 645 } 646 } 647 if (CmsResourceTypeXmlPage.isXmlPage(res)) { 648 // return the xml page resource as a folder 649 return wrapResource(cms, res); 650 } 651 652 return null; 653 } catch (CmsVfsResourceNotFoundException ex) { 654 655 // find the xml page this is for 656 CmsResource xmlPage = findXmlPage(cms, resourcename); 657 if (xmlPage != null) { 658 659 // cut off trailing slash 660 if (resourcename.endsWith("/")) { 661 resourcename = resourcename.substring(0, resourcename.length() - 1); 662 } 663 664 // get the path below the xml page 665 String path = getSubPath(cms, xmlPage, resourcename); 666 667 CmsFile file = cms.readFile(xmlPage); 668 CmsXmlPage xml = CmsXmlPageFactory.unmarshal(cms, file); 669 670 String[] tokens = path.split("/"); 671 if (tokens.length == 1) { 672 673 // check temp file table to remove deleted virtual files 674 if (TMP_FILE_TABLE.contains(resourcename)) { 675 return null; 676 } 677 678 // read the control code resource 679 if (tokens[0].equals(NAME_ELEMENT_CONTROLCODE)) { 680 681 CmsWrappedResource wrap = new CmsWrappedResource(xmlPage); 682 wrap.setRootPath(xmlPage.getRootPath() + "/" + NAME_ELEMENT_CONTROLCODE); 683 return wrap.getResource(); 684 } else { 685 686 Locale locale = CmsLocaleManager.getLocale(tokens[0]); 687 if (xml.hasLocale(locale) && (file.getLength() > 0)) { 688 return getResourceForLocale(xmlPage, locale); 689 } 690 } 691 } else if (tokens.length == 2) { 692 693 // cut off the html suffix 694 String name = tokens[1]; 695 if (name.endsWith("." + EXTENSION_ELEMENT)) { 696 name = name.substring(0, name.length() - 5); 697 } 698 699 Locale locale = CmsLocaleManager.getLocale(tokens[0]); 700 if (xml.hasValue(name, locale)) { 701 String content = xml.getStringValue(cms, name, locale); 702 String fullPath = xmlPage.getRootPath() 703 + "/" 704 + tokens[0] 705 + "/" 706 + name 707 + "." 708 + EXTENSION_ELEMENT; 709 content = prepareContent(content, cms, xmlPage, fullPath); 710 711 int length = content.length(); 712 try { 713 length = content.getBytes(CmsLocaleManager.getResourceEncoding(cms, xmlPage)).length; 714 } catch (UnsupportedEncodingException e) { 715 // this will never happen since UTF-8 is always supported 716 } 717 718 return getResourceForElement(xmlPage, fullPath, length); 719 } 720 } 721 722 } 723 724 return null; 725 } 726 } 727 728 /** 729 * @see org.opencms.file.wrapper.A_CmsResourceWrapper#restoreLink(org.opencms.file.CmsObject, java.lang.String) 730 */ 731 @Override 732 public String restoreLink(CmsObject cms, String uri) { 733 734 CmsResource res = findXmlPage(cms, uri); 735 if (res != null) { 736 return res.getRootPath(); 737 } 738 739 return null; 740 } 741 742 /** 743 * @see org.opencms.file.wrapper.A_CmsResourceWrapper#rewriteLink(CmsObject, CmsResource) 744 */ 745 @Override 746 public String rewriteLink(CmsObject cms, CmsResource res) { 747 748 if (isWrappedResource(cms, res)) { 749 String path = res.getRootPath(); 750 if (!path.endsWith("/")) { 751 path += "/"; 752 } 753 754 return path + NAME_ELEMENT_CONTROLCODE; 755 } 756 757 return null; 758 } 759 760 /** 761 * @see org.opencms.file.wrapper.A_CmsResourceWrapper#unlockResource(org.opencms.file.CmsObject, java.lang.String) 762 */ 763 @Override 764 public boolean unlockResource(CmsObject cms, String resourcename) throws CmsException { 765 766 CmsResource res = findXmlPage(cms, resourcename); 767 if (res != null) { 768 cms.unlockResource(cms.getRequestContext().removeSiteRoot(res.getRootPath())); 769 return true; 770 } 771 772 return false; 773 } 774 775 /** 776 * @see org.opencms.file.wrapper.A_CmsResourceWrapper#wrapResource(CmsObject, CmsResource) 777 */ 778 @Override 779 public CmsResource wrapResource(CmsObject cms, CmsResource res) { 780 781 CmsWrappedResource wrap = new CmsWrappedResource(res); 782 wrap.setFolder(true); 783 return wrap.getResource(); 784 } 785 786 /** 787 * @see org.opencms.file.wrapper.A_CmsResourceWrapper#writeFile(org.opencms.file.CmsObject, org.opencms.file.CmsFile) 788 */ 789 @Override 790 public CmsFile writeFile(CmsObject cms, CmsFile resource) throws CmsException { 791 792 CmsResource xmlPage = cms.readResource(resource.getStructureId()); 793 //CmsResource xmlPage = findXmlPage(cms, resource.getRootPath()); 794 if (xmlPage != null) { 795 796 I_CmsResourceType resType = OpenCms.getResourceManager().getResourceType(xmlPage.getTypeId()); 797 if (resType instanceof CmsResourceTypeXmlPage) { 798 799 String path = getSubPath(cms, xmlPage, cms.getRequestContext().removeSiteRoot(resource.getRootPath())); 800 801 CmsFile file = cms.readFile(xmlPage); 802 803 String[] tokens = path.split("/"); 804 if (tokens.length == 2) { 805 806 CmsXmlPage xml = CmsXmlPageFactory.unmarshal(cms, file); 807 808 // cut off the html suffix 809 String name = tokens[1]; 810 if (name.endsWith("." + EXTENSION_ELEMENT)) { 811 name = name.substring(0, name.length() - 5); 812 } 813 814 // set content 815 String content = getStringValue(cms, file, resource.getContents()); 816 content = CmsStringUtil.extractHtmlBody(content).trim(); 817 xml.setStringValue(cms, name, CmsLocaleManager.getLocale(tokens[0]), content); 818 819 // write file 820 file.setContents(xml.marshal()); 821 cms.writeFile(file); 822 823 } 824 825 return file; 826 } 827 } 828 829 return null; 830 } 831 832 /** 833 * Returns the OpenCms VFS uri of the style sheet of the resource.<p> 834 * 835 * @param cms the initialized CmsObject 836 * @param res the resource where to read the style sheet for 837 * 838 * @return the OpenCms VFS uri of the style sheet of resource 839 */ 840 protected String getUriStyleSheet(CmsObject cms, CmsResource res) { 841 842 String result = ""; 843 try { 844 String currentTemplate = getUriTemplate(cms, res); 845 if (!"".equals(currentTemplate)) { 846 // read the stylesheet from the template file 847 result = cms.readPropertyObject( 848 currentTemplate, 849 CmsPropertyDefinition.PROPERTY_TEMPLATE, 850 false).getValue(""); 851 } 852 } catch (CmsException e) { 853 // noop 854 } 855 return result; 856 } 857 858 /** 859 * Returns the OpenCms VFS uri of the template of the resource.<p> 860 * 861 * @param cms the initialized CmsObject 862 * @param res the resource where to read the template for 863 * 864 * @return the OpenCms VFS uri of the template of the resource 865 */ 866 protected String getUriTemplate(CmsObject cms, CmsResource res) { 867 868 String result = ""; 869 try { 870 result = cms.readPropertyObject( 871 cms.getRequestContext().removeSiteRoot(res.getRootPath()), 872 CmsPropertyDefinition.PROPERTY_TEMPLATE, 873 true).getValue(""); 874 } catch (CmsException e) { 875 // noop 876 } 877 return result; 878 } 879 880 /** 881 * Prepare the content of a xml page before returning.<p> 882 * 883 * Mainly adds the basic html structure and the css style sheet.<p> 884 * 885 * @param content the origin content of the xml page element 886 * @param cms the initialized CmsObject 887 * @param xmlPage the xml page resource 888 * @param path the full path to set as the title in the html head 889 * 890 * @return the prepared content with the added html structure 891 */ 892 protected String prepareContent(String content, CmsObject cms, CmsResource xmlPage, String path) { 893 894 // cut off eventually existing html skeleton 895 content = CmsStringUtil.extractHtmlBody(content); 896 897 // add tags for stylesheet 898 String stylesheet = getUriStyleSheet(cms, xmlPage); 899 900 // content-type 901 String encoding = CmsLocaleManager.getResourceEncoding(cms, xmlPage); 902 String contentType = CmsResourceManager.MIMETYPE_HTML + "; charset=" + encoding; 903 904 content = CmsEncoder.adjustHtmlEncoding(content, encoding); 905 906 // rewrite uri 907 Object obj = cms.getRequestContext().getAttribute(CmsObjectWrapper.ATTRIBUTE_NAME); 908 if (obj != null) { 909 CmsObjectWrapper wrapper = (CmsObjectWrapper)obj; 910 stylesheet = wrapper.rewriteLink(stylesheet); 911 } 912 913 String base = OpenCms.getSystemInfo().getOpenCmsContext(); 914 915 StringBuffer result = new StringBuffer(content.length() + 1024); 916 result.append("<html><head>"); 917 918 // meta: content-type 919 result.append("<meta http-equiv=\"content-type\" content=\""); 920 result.append(contentType); 921 result.append("\">"); 922 923 // title as full path 924 result.append("<title>"); 925 result.append(cms.getRequestContext().removeSiteRoot(path)); 926 result.append("</title>"); 927 928 // stylesheet 929 if (!"".equals(stylesheet)) { 930 result.append("<link href=\""); 931 result.append(base); 932 result.append(stylesheet); 933 result.append("\" rel=\"stylesheet\" type=\"text/css\">"); 934 } 935 936 result.append("</head><body>"); 937 result.append(content); 938 result.append("</body></html>"); 939 content = result.toString(); 940 941 return content.trim(); 942 } 943 944 /** 945 * Returns the {@link CmsResource} of the xml page which belongs to 946 * the given resource name (full path).<p> 947 * 948 * It works up the path till a resource for the path exists in the VFS. 949 * If the found resource is a xml page, this resource is returned. If 950 * the path does not belong to a xml page <code>null</code> will be returned.<p> 951 * 952 * @param cms the initialized CmsObject 953 * @param resourcename the name of the resource (full path) to check 954 * 955 * @return the found resource of type xml page or null if not found 956 */ 957 private CmsResource findXmlPage(CmsObject cms, String resourcename) { 958 959 // get the full folder path of the resource to start from 960 String path = cms.getRequestContext().removeSiteRoot(resourcename); 961 // the path without the trailing slash 962 // for example: .../xmlpage.xml/ -> .../xmlpagepage.xml 963 String reducedPath = CmsFileUtil.removeTrailingSeparator(path); 964 do { 965 966 // check if a resource without the trailing shalsh exists 967 boolean existResource = cms.existsResource(reducedPath); 968 // check if the current folder exists 969 if (cms.existsResource(path) || existResource) { 970 // prove if a resource without the trailing slash does exist 971 if (existResource) { 972 // a resource without the trailing slash does exist, so take the path without the trailing slash 973 path = reducedPath; 974 } 975 try { 976 CmsResource res = cms.readResource(path); 977 I_CmsResourceType resType = OpenCms.getResourceManager().getResourceType(res.getTypeId()); 978 if (resType instanceof CmsResourceTypeXmlPage) { 979 return res; 980 } else { 981 break; 982 } 983 } catch (CmsException ex) { 984 break; 985 } 986 987 } else { 988 989 // folder does not exist, go up one folder 990 path = CmsResource.getParentFolder(path); 991 if (path.endsWith("/")) { 992 path = path.substring(0, path.length() - 1); 993 } 994 } 995 996 if (CmsStringUtil.isEmpty(path)) { 997 998 // site root or root folder reached and no folder found 999 break; 1000 } 1001 } while (true); 1002 1003 return null; 1004 } 1005 1006 /** 1007 * Returns a virtual resource for an element inside a locale.<p> 1008 * 1009 * A new (virtual) resource is created with the given path and length. The 1010 * new created resource uses the values of the origin resource of the xml page 1011 * where it is possible.<p> 1012 * 1013 * @param xmlPage the xml page resource with the element to create a virtual resource 1014 * @param path the full path to set for the resource 1015 * @param length the length of the element content 1016 * 1017 * @return a new created virtual {@link CmsResource} 1018 */ 1019 private CmsResource getResourceForElement(CmsResource xmlPage, String path, int length) { 1020 1021 CmsWrappedResource wrap = new CmsWrappedResource(xmlPage); 1022 wrap.setRootPath(path); 1023 int plainId; 1024 try { 1025 plainId = OpenCms.getResourceManager().getResourceType( 1026 CmsResourceTypePlain.getStaticTypeName()).getTypeId(); 1027 } catch (CmsLoaderException e) { 1028 // this should really never happen 1029 plainId = CmsResourceTypePlain.getStaticTypeId(); 1030 } 1031 wrap.setTypeId(plainId); 1032 wrap.setFolder(false); 1033 wrap.setLength(length); 1034 1035 return wrap.getResource(); 1036 } 1037 1038 /** 1039 * Creates a new virtual resource for the locale in the xml page as a folder.<p> 1040 * 1041 * The new created resource uses the values of the origin resource of the xml page where it is possible.<p> 1042 * 1043 * @param xmlPage the xml page resource with the locale to create a resource of 1044 * @param locale the locale in the xml page to use for the new resource 1045 * 1046 * @return a new created CmsResource 1047 */ 1048 private CmsResource getResourceForLocale(CmsResource xmlPage, Locale locale) { 1049 1050 CmsWrappedResource wrap = new CmsWrappedResource(xmlPage); 1051 wrap.setRootPath(xmlPage.getRootPath() + "/" + locale.getLanguage() + "/"); 1052 int plainId; 1053 try { 1054 plainId = OpenCms.getResourceManager().getResourceType( 1055 CmsResourceTypePlain.getStaticTypeName()).getTypeId(); 1056 } catch (CmsLoaderException e) { 1057 // this should really never happen 1058 plainId = CmsResourceTypePlain.getStaticTypeId(); 1059 } 1060 wrap.setTypeId(plainId); 1061 wrap.setFolder(true); 1062 1063 return wrap.getResource(); 1064 } 1065 1066 /** 1067 * Returns the content as a string while using the correct encoding.<p> 1068 * 1069 * @param cms the initialized CmsObject 1070 * @param resource the resource where the content belongs to 1071 * @param content the byte array which should be converted into a string 1072 * 1073 * @return the content as a string 1074 * 1075 * @throws CmsException if something goes wrong 1076 */ 1077 private String getStringValue(CmsObject cms, CmsResource resource, byte[] content) throws CmsException { 1078 1079 // get the encoding for the resource 1080 CmsProperty prop = cms.readPropertyObject(resource, CmsPropertyDefinition.PROPERTY_CONTENT_ENCODING, true); 1081 String enc = prop.getValue(); 1082 if (enc == null) { 1083 enc = OpenCms.getSystemInfo().getDefaultEncoding(); 1084 } 1085 1086 // create a String with the right encoding 1087 return CmsEncoder.createString(content, enc); 1088 } 1089 1090 /** 1091 * Returns the path inside a xml page.<p> 1092 * 1093 * The remaining path inside a xml page can be the locale and the element name 1094 * or the name of the control code file.<p> 1095 * 1096 * @param cms the initialized CmsObject 1097 * @param xmlPage the xml page where the resourcename belongs to 1098 * @param resourcename the full path of the resource (pointing inside the xml page) 1099 * 1100 * @return the remaining path inside the xml page without the leading slash 1101 */ 1102 private String getSubPath(CmsObject cms, CmsResource xmlPage, String resourcename) { 1103 1104 if (xmlPage != null) { 1105 String rootPath = cms.getRequestContext().addSiteRoot(resourcename); 1106 String path = rootPath.substring(xmlPage.getRootPath().length()); 1107 1108 if (path.startsWith("/")) { 1109 path = path.substring(1); 1110 } 1111 1112 if (path.endsWith("/")) { 1113 path = path.substring(0, path.length() - 1); 1114 } 1115 1116 return path; 1117 } 1118 1119 return null; 1120 } 1121 1122 /** 1123 * Returns a list with virtual file names for the xml page.<p> 1124 * 1125 * Actually that is only the name of the control code file.<p> 1126 * 1127 * @return a list containing strings with the names of the virtual files 1128 */ 1129 private List<String> getVirtualFiles() { 1130 1131 ArrayList<String> list = new ArrayList<String>(); 1132 list.add(NAME_ELEMENT_CONTROLCODE); 1133 1134 return list; 1135 } 1136}