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