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.ui.apps.sitemanager; 029 030import org.opencms.ade.configuration.CmsADEManager; 031import org.opencms.file.CmsDataAccessException; 032import org.opencms.file.CmsFile; 033import org.opencms.file.CmsObject; 034import org.opencms.file.CmsProject; 035import org.opencms.file.CmsProperty; 036import org.opencms.file.CmsPropertyDefinition; 037import org.opencms.file.CmsResource; 038import org.opencms.file.CmsVfsResourceAlreadyExistsException; 039import org.opencms.file.CmsVfsResourceNotFoundException; 040import org.opencms.file.types.CmsResourceTypeFolder; 041import org.opencms.file.types.CmsResourceTypeFolderSubSitemap; 042import org.opencms.file.types.CmsResourceTypeImage; 043import org.opencms.file.types.I_CmsResourceType; 044import org.opencms.i18n.CmsEncoder; 045import org.opencms.loader.CmsLoaderException; 046import org.opencms.lock.CmsLockException; 047import org.opencms.main.CmsException; 048import org.opencms.main.CmsIllegalArgumentException; 049import org.opencms.main.CmsLog; 050import org.opencms.main.OpenCms; 051import org.opencms.report.A_CmsReportThread; 052import org.opencms.report.I_CmsReport; 053import org.opencms.security.I_CmsPrincipal; 054import org.opencms.site.CmsSite; 055import org.opencms.ui.apps.I_CmsCRUDApp; 056import org.opencms.ui.apps.Messages; 057import org.opencms.util.CmsMacroResolver; 058import org.opencms.util.CmsStringUtil; 059import org.opencms.xml.content.CmsXmlContent; 060import org.opencms.xml.content.CmsXmlContentFactory; 061import org.opencms.xml.types.I_CmsXmlContentValue; 062 063import java.io.ByteArrayOutputStream; 064import java.util.Locale; 065import java.util.Map; 066 067import org.apache.commons.logging.Log; 068 069/** 070 * Report thread to save site configurations.<p> 071 */ 072public class CmsCreateSiteThread extends A_CmsReportThread { 073 074 /** The logger for this class. */ 075 static Log LOG = CmsLog.getLog(CmsCreateSiteThread.class.getName()); 076 077 /** Constant. */ 078 private static final String BLANK_HTML = "blank.html"; 079 080 /**default index.html which gets created.*/ 081 private static final String INDEX_HTML = "index.html"; 082 083 /** Constant. */ 084 private static final String MODEL_PAGE = "ModelPage"; 085 086 /** Constant. */ 087 private static final String MODEL_PAGE_PAGE = "ModelPage/Page"; 088 089 /** Constant. */ 090 private static final String NEW = ".templates/"; 091 092 /**Map holding key, values for macros.*/ 093 private Map<String, String> m_bundle; 094 095 /**CmsObject(root-site).*/ 096 private CmsObject m_cms; 097 098 /**CmsObject(root-site,online). */ 099 private CmsObject m_cmsOnline; 100 101 /**Indicates if a OU should be created. */ 102 private boolean m_createOU; 103 104 /**Runnable which gets used after finishing thread.*/ 105 private Runnable m_finished; 106 107 /**Manager app. */ 108 private I_CmsCRUDApp<CmsSite> m_manager; 109 110 /**Old version of site to overwrite. */ 111 private CmsSite m_oldSite; 112 113 /**FavIcon byte data. */ 114 private ByteArrayOutputStream m_os; 115 116 /**Parent OU. */ 117 private String m_parentOU; 118 119 /**Selected OU.*/ 120 private String m_selectedOU; 121 122 /**Site to save. */ 123 private CmsSite m_site; 124 125 /**Source to copy resources from. */ 126 private String m_source; 127 128 /**Template to set as property for the site. */ 129 private String m_template; 130 131 /** 132 * Constructor for Class. 133 * 134 * @param cms CmsObject 135 * @param manager manager 136 * @param site to be saved 137 * @param oldSite to be overwritten 138 * @param source to copy resources from 139 * @param template to be set as property to root-folder of site 140 * @param createOU indicates if OU should be generated 141 * @param parentOU if createOU==true, sets the parent of the new OU 142 * @param selectedOU set an existing OU 143 * @param os ByteOutputStream with FavIcon data 144 * @param bundle for macro resolving 145 * @param finished runnable which gets called when thread done 146 */ 147 protected CmsCreateSiteThread( 148 CmsObject cms, 149 I_CmsCRUDApp<CmsSite> manager, 150 CmsSite site, 151 CmsSite oldSite, 152 String source, 153 String template, 154 boolean createOU, 155 String parentOU, 156 String selectedOU, 157 ByteArrayOutputStream os, 158 Map<String, String> bundle, 159 Runnable finished) { 160 161 super(cms, "createSite"); 162 m_cms = cms; 163 m_cmsOnline = getOnlineCmsObject(cms); 164 m_source = source; 165 m_template = template; 166 m_bundle = bundle; 167 m_site = site; 168 m_createOU = createOU; 169 m_oldSite = oldSite; 170 m_parentOU = parentOU; 171 m_selectedOU = selectedOU; 172 m_os = os; 173 m_finished = finished; 174 m_manager = manager; 175 initHtmlReport(OpenCms.getWorkplaceManager().getWorkplaceLocale(cms.getRequestContext())); 176 } 177 178 /** 179 * @see org.opencms.report.A_CmsReportThread#getReportUpdate() 180 */ 181 @Override 182 public String getReportUpdate() { 183 184 return getReport().getReportUpdate(); 185 } 186 187 /** 188 * @see java.lang.Thread#run() 189 */ 190 @Override 191 public void run() { 192 193 try { 194 195 if (m_oldSite == null) { 196 getReport().println( 197 Messages.get().container(Messages.RPT_SITE_START_NEW_1, CmsEncoder.escapeXml(m_site.getTitle())), 198 I_CmsReport.FORMAT_HEADLINE); 199 } else { 200 getReport().println( 201 Messages.get().container(Messages.RPT_SITE_START_EDIT_1, CmsEncoder.escapeXml(m_site.getTitle())), 202 I_CmsReport.FORMAT_HEADLINE); 203 } 204 CmsResource siteRootResource = null; 205 206 if (m_source.isEmpty()) { 207 //Don't copy an existing folder, but create a new one 208 siteRootResource = createSiteRootIfNeeded(m_site.getSiteRoot()); 209 String sitePath = m_cms.getSitePath(siteRootResource); 210 211 // create sitemap configuration 212 String contentFolder = CmsStringUtil.joinPaths(sitePath, CmsADEManager.CONTENT_FOLDER_NAME + "/"); 213 String sitemapConfig = CmsStringUtil.joinPaths(contentFolder, CmsADEManager.CONFIG_FILE_NAME); 214 if (!m_cms.existsResource(sitemapConfig)) { 215 createSitemapContentFolder(m_cms, siteRootResource, contentFolder); 216 createIndexHTML(ensureFoldername(siteRootResource.getRootPath())); 217 } 218 } else { 219 //Copy existing folder to new siteroot and resolve macros 220 CmsMacroResolver.copyAndResolveMacro( 221 m_cms, 222 m_source, 223 m_site.getSiteRoot(), 224 m_bundle, 225 true, 226 CmsResource.COPY_AS_NEW, 227 getReport()); 228 229 siteRootResource = m_cms.readResource(m_site.getSiteRoot()); 230 231 adjustFolderType(siteRootResource); 232 setFolderTitle(siteRootResource); 233 } 234 235 setTemplate(siteRootResource); 236 237 saveFavIcon(ensureFoldername(m_site.getSiteRoot())); 238 239 handleOU(siteRootResource); 240 241 try { 242 m_cms.unlockResource(siteRootResource); 243 } catch (CmsLockException e) { 244 LOG.info("Unlock resource failed", e); 245 } 246 } catch (CmsException e) { 247 LOG.error("Error creating site", e); 248 getReport().println(Messages.get().container(Messages.RPT_SITE_ERROR_0), I_CmsReport.FORMAT_ERROR); 249 getReport().println(e); 250 getReport().println(); 251 getReport().println( 252 Messages.get().container(Messages.RPT_SITE_FINISH_WARNING_0), 253 I_CmsReport.FORMAT_WARNING); 254 m_finished.run(); 255 return; 256 } 257 getReport().println(Messages.get().container(Messages.RPT_SITE_FINISH_0), I_CmsReport.FORMAT_OK); 258 m_finished.run(); 259 m_manager.writeElement(m_site); 260 } 261 262 /** 263 * Checks if there are at least one character in the folder name, 264 * also ensures that it ends with a '/' and doesn't start with '/'.<p> 265 * 266 * @param resourcename folder name to check (complete path) 267 * @return the validated folder name 268 * @throws CmsIllegalArgumentException if the folder name is empty or <code>null</code> 269 */ 270 String ensureFoldername(String resourcename) { 271 272 if (CmsStringUtil.isEmpty(resourcename)) { 273 return ""; 274 } 275 if (!CmsResource.isFolder(resourcename)) { 276 resourcename = resourcename.concat("/"); 277 } 278 if (resourcename.charAt(0) == '/') { 279 resourcename = resourcename.substring(1); 280 } 281 return resourcename; 282 } 283 284 /** 285 * Changes the folder type if necessary:<p> 286 * 287 * Type Folder -> Type SubsitemapFolder.<p> 288 * others -> no change.<p> 289 * 290 * @param siteRootResource resource to be changed 291 * @throws CmsLoaderException exception 292 * @throws CmsException exception 293 */ 294 @SuppressWarnings("deprecation") 295 private void adjustFolderType(CmsResource siteRootResource) throws CmsLoaderException, CmsException { 296 297 if (OpenCms.getResourceManager().getResourceType( 298 CmsResourceTypeFolder.RESOURCE_TYPE_NAME) == OpenCms.getResourceManager().getResourceType( 299 siteRootResource)) { 300 301 siteRootResource.setType( 302 OpenCms.getResourceManager().getResourceType( 303 CmsResourceTypeFolderSubSitemap.TYPE_SUBSITEMAP).getTypeId()); 304 m_cms.writeResource(siteRootResource); 305 } 306 } 307 308 /** 309 * Creates new index html if no one was found.<p> 310 * 311 * @param siteRoot of new site 312 * @throws CmsIllegalArgumentException exception 313 * @throws CmsException exception 314 */ 315 private void createIndexHTML(String siteRoot) throws CmsIllegalArgumentException, CmsException { 316 317 if (!m_cms.existsResource(siteRoot + INDEX_HTML)) { 318 //Create index.html 319 I_CmsResourceType containerType = OpenCms.getResourceManager().getResourceType( 320 org.opencms.file.types.CmsResourceTypeXmlContainerPage.RESOURCE_TYPE_NAME); 321 m_cms.createResource(siteRoot + INDEX_HTML, containerType); 322 } 323 } 324 325 /** 326 * Helper method for creating the .content folder of a sub-sitemap.<p> 327 * 328 * @param cms the current CMS context 329 * @param subSitemapFolder the sub-sitemap folder in which the .content folder should be created 330 * @param contentFolder the content folder path 331 * @throws CmsException if something goes wrong 332 * @throws CmsLoaderException if something goes wrong 333 */ 334 private void createSitemapContentFolder(CmsObject cms, CmsResource subSitemapFolder, String contentFolder) 335 throws CmsException, CmsLoaderException { 336 337 CmsResource configFile = null; 338 String sitePath = cms.getSitePath(subSitemapFolder); 339 String folderName = CmsStringUtil.joinPaths(sitePath, CmsADEManager.CONTENT_FOLDER_NAME + "/"); 340 String sitemapConfigName = CmsStringUtil.joinPaths(folderName, CmsADEManager.CONFIG_FILE_NAME); 341 if (!cms.existsResource(folderName)) { 342 cms.createResource( 343 folderName, 344 OpenCms.getResourceManager().getResourceType(CmsADEManager.CONFIG_FOLDER_TYPE)); 345 } 346 I_CmsResourceType configType = OpenCms.getResourceManager().getResourceType(CmsADEManager.CONFIG_TYPE); 347 if (cms.existsResource(sitemapConfigName)) { 348 configFile = cms.readResource(sitemapConfigName); 349 if (!OpenCms.getResourceManager().getResourceType(configFile).getTypeName().equals( 350 configType.getTypeName())) { 351 throw new CmsException( 352 Messages.get().container( 353 Messages.ERR_CREATING_SUB_SITEMAP_WRONG_CONFIG_FILE_TYPE_2, 354 sitemapConfigName, 355 CmsADEManager.CONFIG_TYPE)); 356 } 357 } else { 358 configFile = cms.createResource( 359 sitemapConfigName, 360 OpenCms.getResourceManager().getResourceType(CmsADEManager.CONFIG_TYPE)); 361 } 362 363 if (configFile != null) { 364 try { 365 CmsResource newFolder = m_cms.createResource( 366 contentFolder + NEW, 367 OpenCms.getResourceManager().getResourceType(CmsResourceTypeFolder.RESOURCE_TYPE_NAME)); 368 I_CmsResourceType containerType = OpenCms.getResourceManager().getResourceType( 369 org.opencms.file.types.CmsResourceTypeXmlContainerPage.RESOURCE_TYPE_NAME); 370 CmsResource modelPage = m_cms.createResource(newFolder.getRootPath() + BLANK_HTML, containerType); 371 String defTitle = Messages.get().getBundle(m_cms.getRequestContext().getLocale()).key( 372 Messages.GUI_DEFAULT_MODEL_TITLE_1, 373 m_site.getTitle()); 374 String defDes = Messages.get().getBundle(m_cms.getRequestContext().getLocale()).key( 375 Messages.GUI_DEFAULT_MODEL_DESCRIPTION_1, 376 m_site.getTitle()); 377 CmsProperty prop = new CmsProperty(CmsPropertyDefinition.PROPERTY_TITLE, defTitle, defTitle); 378 m_cms.writePropertyObject(modelPage.getRootPath(), prop); 379 prop = new CmsProperty(CmsPropertyDefinition.PROPERTY_DESCRIPTION, defDes, defDes); 380 m_cms.writePropertyObject(modelPage.getRootPath(), prop); 381 CmsFile file = m_cms.readFile(configFile); 382 CmsXmlContent con = CmsXmlContentFactory.unmarshal(m_cms, file); 383 con.addValue(m_cms, MODEL_PAGE, Locale.ENGLISH, 0); 384 I_CmsXmlContentValue val = con.getValue(MODEL_PAGE_PAGE, Locale.ENGLISH); 385 val.setStringValue(m_cms, modelPage.getRootPath()); 386 file.setContents(con.marshal()); 387 m_cms.writeFile(file); 388 } catch (CmsException e) { 389 LOG.error(e.getLocalizedMessage(), e); 390 } 391 } 392 } 393 394 /** 395 * Creates root-folder for the new site.<p> 396 * If folder exist, the existing one will be returned.<p> 397 * 398 * @param siteRoot path to be created resp. read. 399 * @return site root folder 400 * @throws CmsException exception 401 */ 402 private CmsResource createSiteRootIfNeeded(String siteRoot) throws CmsException { 403 404 CmsResource siteRootResource = null; 405 406 // check if the site root already exists 407 try { 408 // take the existing site and do not perform any OU related actions 409 if (m_cms.existsResource(siteRoot)) { 410 siteRootResource = m_cms.readResource(siteRoot); 411 } else { 412 CmsResource onlineVersion = m_cmsOnline.readResource(siteRoot); 413 siteRootResource = m_cms.readResource(onlineVersion.getStructureId()); 414 } 415 416 } catch (CmsVfsResourceNotFoundException e) { 417 // not create a new site folder and the according OU if option is checked checked 418 getReport().println( 419 Messages.get().container(Messages.RPT_SITE_CREATE_RESOURCES_0), 420 I_CmsReport.FORMAT_DEFAULT); 421 I_CmsResourceType type = OpenCms.getResourceManager().getResourceType( 422 CmsResourceTypeFolderSubSitemap.TYPE_SUBSITEMAP); 423 siteRootResource = m_cms.createResource(siteRoot, type); 424 CmsProperty folderTitle = new CmsProperty( 425 CmsPropertyDefinition.PROPERTY_TITLE, 426 m_site.getTitle(), 427 m_site.getTitle()); 428 m_cms.writePropertyObject(siteRoot, folderTitle); 429 430 } 431 return siteRootResource; 432 } 433 434 /** 435 * Creates online version of given CmsObject.<p> 436 * 437 * @param cms given CmsObject 438 * @return online CmsObject 439 */ 440 private CmsObject getOnlineCmsObject(CmsObject cms) { 441 442 CmsObject res = null; 443 try { 444 res = OpenCms.initCmsObject(cms); 445 res.getRequestContext().setCurrentProject(cms.readProject(CmsProject.ONLINE_PROJECT_ID)); 446 } catch (CmsException e) { 447 LOG.error("Cannot create CmsObject", e); 448 } 449 return res; 450 } 451 452 /** 453 * Handles OU related operations.<p> 454 * Creates OU, adds site root to existing OU or does nothing.<p> 455 * 456 * @param siteRootResource Resource representing root folder 457 */ 458 private void handleOU(CmsResource siteRootResource) { 459 460 String ouName = null; 461 String ouDescription = "OU for: %(site)"; 462 463 if (m_createOU) { 464 getReport().println( 465 Messages.get().container(Messages.RPT_SITE_CREATE_OU_1, m_parentOU + siteRootResource.getName()), 466 I_CmsReport.FORMAT_DEFAULT); 467 try { 468 OpenCms.getOrgUnitManager().createOrganizationalUnit( 469 m_cms, 470 m_parentOU + siteRootResource.getName(), 471 ouDescription.replace("%(site)", m_site.getTitle() + " [" + siteRootResource.getRootPath() + "]"), 472 0, 473 siteRootResource.getRootPath()); 474 ouName = m_parentOU + siteRootResource.getName(); 475 } catch (CmsDataAccessException e) { 476 LOG.info("Can't create OU, an OU with same name exists. The existing OU is chosen for the new site"); 477 try { 478 OpenCms.getOrgUnitManager().addResourceToOrgUnit( 479 m_cms, 480 m_parentOU + siteRootResource.getName(), 481 siteRootResource.getRootPath()); 482 ouName = m_parentOU + siteRootResource.getName(); 483 } catch (CmsException e2) { 484 LOG.info("Resource is already added to OU"); 485 ouName = m_parentOU + siteRootResource.getName(); 486 } 487 } catch (CmsException e) { 488 LOG.error("Error on creating new OU", e); 489 } 490 } 491 492 if ((m_oldSite == null) & (m_selectedOU != null) & !m_selectedOU.equals("/")) { 493 getReport().println( 494 Messages.get().container(Messages.RPT_SITE_ADD_OU_1, m_selectedOU), 495 I_CmsReport.FORMAT_DEFAULT); 496 try { 497 OpenCms.getOrgUnitManager().addResourceToOrgUnit(m_cms, m_selectedOU, siteRootResource.getRootPath()); 498 ouName = m_selectedOU.substring(0, (m_selectedOU).length() - 1); 499 } catch (CmsException e) { 500 LOG.error("Error on adding resource to OU", e); 501 } 502 } 503 504 try { 505 m_cms.lockResource(siteRootResource); 506 } catch (CmsException e) { 507 LOG.error("unable to lock resource", e); 508 } 509 510 if (ouName != null) { 511 try { 512 513 m_cms.chacc( 514 siteRootResource.getRootPath(), 515 I_CmsPrincipal.PRINCIPAL_GROUP, 516 ouName + "/Users", 517 "+r+w+v+c+i+o+d"); 518 } catch (CmsException e) { 519 LOG.error("Error on setting permission for OU.", e); 520 } 521 } 522 try { 523 m_cms.unlockResource(siteRootResource); 524 } catch (CmsException e) { 525 LOG.error("unable to unlock resource"); 526 } 527 } 528 529 /** 530 * Saves outputstream of favicon as resource.<p> 531 * 532 * @param siteRoot site root of considered site. 533 */ 534 private void saveFavIcon(String siteRoot) { 535 536 if (m_os == null) { 537 return; 538 } 539 if (m_os.size() == 0) { 540 return; 541 } 542 543 getReport().println(Messages.get().container(Messages.RPT_SITE_SET_FAVICON_0), I_CmsReport.FORMAT_DEFAULT); 544 CmsResource favicon = null; 545 try { 546 favicon = m_cms.createResource( 547 siteRoot + CmsSiteManager.FAVICON, 548 OpenCms.getResourceManager().getResourceType(CmsResourceTypeImage.getStaticTypeName())); 549 } catch (CmsVfsResourceAlreadyExistsException e) { 550 //OK, Resource already there 551 try { 552 favicon = m_cms.readResource(siteRoot + CmsSiteManager.FAVICON); 553 } catch (CmsException e2) { 554 //no, it wasn't.. 555 getReport().println( 556 Messages.get().container(Messages.RPT_SITE_ERROR_FAVICON_0), 557 I_CmsReport.FORMAT_ERROR); 558 getReport().println(e); 559 getReport().println(e2); 560 return; 561 } 562 } catch (CmsIllegalArgumentException | CmsException e) { 563 getReport().println(Messages.get().container(Messages.RPT_SITE_ERROR_FAVICON_0), I_CmsReport.FORMAT_ERROR); 564 getReport().println(e); 565 return; 566 } 567 try { 568 m_cms.lockResource(siteRoot + CmsSiteManager.FAVICON); 569 CmsFile faviconFile = new CmsFile(favicon); 570 faviconFile.setContents(m_os.toByteArray()); 571 m_cms.writeFile(faviconFile); 572 m_cms.unlockResource(siteRoot + CmsSiteManager.FAVICON); 573 } catch (CmsException e) { 574 getReport().println(Messages.get().container(Messages.RPT_SITE_ERROR_FAVICON_0), I_CmsReport.FORMAT_ERROR); 575 getReport().println(e); 576 return; 577 } 578 579 } 580 581 /** 582 * Updates title property of site root resource in case of copy from template.<p> 583 * 584 * @param res root resource to set titel for 585 */ 586 private void setFolderTitle(CmsResource res) { 587 588 try { 589 CmsProperty titleProperty = m_cms.readPropertyObject(res, CmsPropertyDefinition.PROPERTY_TITLE, false); 590 if (!titleProperty.isNullProperty()) { 591 titleProperty.setValue(m_site.getTitle(), CmsProperty.TYPE_INDIVIDUAL); 592 m_cms.writePropertyObject(res.getRootPath(), titleProperty); 593 } else { 594 LOG.error("Editing title property of site root resource was not possible"); 595 getReport().println( 596 Messages.get().container(Messages.RPT_SITE_ERROR_TITLE_0), 597 I_CmsReport.FORMAT_ERROR); 598 } 599 } catch (CmsException e) { 600 LOG.error("Editing title property of site root resource was not possible", e); 601 getReport().println(Messages.get().container(Messages.RPT_SITE_ERROR_TITLE_0), I_CmsReport.FORMAT_ERROR); 602 getReport().println(e); 603 } 604 605 } 606 607 /** 608 * Sets the selected template as property to site root folder.<p> 609 * 610 * @param siteRootResource Resource representing root folder 611 */ 612 private void setTemplate(CmsResource siteRootResource) { 613 614 try { 615 m_cms.lockResource(siteRootResource); 616 // add template property 617 if (CmsStringUtil.isNotEmptyOrWhitespaceOnly(m_template)) { 618 CmsProperty prop = new CmsProperty(CmsPropertyDefinition.PROPERTY_TEMPLATE, m_template, null); 619 m_cms.writePropertyObject(siteRootResource.getRootPath(), prop); 620 } 621 m_cms.unlockResource(siteRootResource); 622 } catch (CmsException e) { 623 LOG.error("Error on adding template", e); 624 } 625 } 626 627}