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.configuration.CmsSitesConfiguration; 031import org.opencms.file.CmsObject; 032import org.opencms.file.CmsResource; 033import org.opencms.letsencrypt.CmsLetsEncryptConfiguration.Trigger; 034import org.opencms.main.CmsException; 035import org.opencms.main.CmsLog; 036import org.opencms.main.OpenCms; 037import org.opencms.site.CmsSSLMode; 038import org.opencms.site.CmsSite; 039import org.opencms.ui.A_CmsUI; 040import org.opencms.ui.CmsCssIcon; 041import org.opencms.ui.CmsVaadinUtils; 042import org.opencms.ui.FontOpenCms; 043import org.opencms.ui.apps.A_CmsWorkplaceApp; 044import org.opencms.ui.apps.I_CmsAppUIContext; 045import org.opencms.ui.apps.I_CmsCRUDApp; 046import org.opencms.ui.apps.Messages; 047import org.opencms.ui.apps.sitemanager.CmsSitesTable.TableProperty; 048import org.opencms.ui.components.CmsBasicDialog; 049import org.opencms.ui.components.CmsBasicDialog.DialogWidth; 050import org.opencms.ui.components.CmsErrorDialog; 051import org.opencms.ui.components.CmsInfoButton; 052import org.opencms.ui.components.CmsToolBar; 053import org.opencms.ui.components.OpenCmsTheme; 054 055import java.util.LinkedHashMap; 056import java.util.List; 057import java.util.Map; 058import java.util.Set; 059import java.util.stream.Collectors; 060 061import org.apache.commons.logging.Log; 062 063import com.vaadin.server.ExternalResource; 064import com.vaadin.server.FontAwesome; 065import com.vaadin.server.Resource; 066import com.vaadin.ui.Button; 067import com.vaadin.ui.Button.ClickEvent; 068import com.vaadin.ui.Button.ClickListener; 069import com.vaadin.ui.Component; 070import com.vaadin.ui.UI; 071import com.vaadin.ui.Window; 072import com.vaadin.ui.themes.ValoTheme; 073import com.vaadin.v7.event.FieldEvents.TextChangeEvent; 074import com.vaadin.v7.event.FieldEvents.TextChangeListener; 075import com.vaadin.v7.ui.TextField; 076 077/** 078 * Manager class for the Site manager app. 079 */ 080 081public class CmsSiteManager extends A_CmsWorkplaceApp implements I_CmsCRUDApp<CmsSite> { 082 083 /**Bundel name for the sites which are used as templates for new sites.*/ 084 public static final String BUNDLE_NAME = "siteMacroBundle"; 085 086 /**Constant.*/ 087 public static final String FAVICON = "favicon.ico"; 088 089 /** Name of the macros folder for site templates.*/ 090 public static final String MACRO_FOLDER = ".macros"; 091 092 /** The add project path name. */ 093 public static final String PATH_NAME_ADD = "newSite"; 094 095 /** The edit project path name. */ 096 public static final String PATH_NAME_EDIT = "editSite"; 097 098 /**The global settings path name. */ 099 public static final String PATH_NAME_GLOBAL = "global"; 100 101 /**The webserver setting path name. */ 102 public static final String PATH_NAME_WEBSERVER = "webserver"; 103 104 /**path attribute to transmit root of a site to be edited. */ 105 public static final String SITE_ROOT = "siteRoot"; 106 107 /** The logger for this class. */ 108 static Log LOG = CmsLog.getLog(CmsSiteManager.class.getName()); 109 110 /**Path to the sites folder.*/ 111 static final String PATH_SITES = "/sites/"; 112 113 /** The currently opened dialog window. */ 114 protected Window m_dialogWindow; 115 116 /** The site table. */ 117 protected CmsSitesTable m_sitesTable; 118 119 /** The file table filter input. */ 120 protected TextField m_siteTableFilter; 121 122 /**Info Button. */ 123 private CmsInfoButton m_infoButton; 124 125 /**The publish button.*/ 126 private Button m_publishButton; 127 128 /** The root cms object. */ 129 private CmsObject m_rootCms; 130 131 /** 132 * Method to check if a folder under given path contains a bundle for macro resolving.<p> 133 * 134 * @param cms CmsObject 135 * @param folderPathRoot root path of folder 136 * @return true if macros bundle found 137 */ 138 public static boolean isFolderWithMacros(CmsObject cms, String folderPathRoot) { 139 140 if (!CmsResource.isFolder(folderPathRoot)) { 141 folderPathRoot = folderPathRoot.concat("/"); 142 } 143 try { 144 cms.readResource(folderPathRoot + MACRO_FOLDER); 145 cms.readResource(folderPathRoot + MACRO_FOLDER + "/" + BUNDLE_NAME + "_desc"); 146 } catch (CmsException e) { 147 return false; 148 } 149 return true; 150 } 151 152 /** 153 * Check if LetsEncrypt updates are configured to be triggered by webserver configuration updates.<p> 154 * 155 * @return true if LetsEncrypt updates are configured to be triggered by webserver configuration updates 156 */ 157 public static boolean isLetsEncryptConfiguredForWebserverThread() { 158 159 return (OpenCms.getLetsEncryptConfig() != null) 160 && OpenCms.getLetsEncryptConfig().isValidAndEnabled() 161 && (OpenCms.getLetsEncryptConfig().getTrigger() == Trigger.webserverThread); 162 } 163 164 /** 165 * Centers the currently open window. 166 */ 167 public void centerWindow() { 168 169 if (m_dialogWindow != null) { 170 m_dialogWindow.center(); 171 } 172 } 173 174 /** 175 * Closes the current dialog window and updates the sites table if requested.<p> 176 * 177 * @param updateTable <code>true</code> to update the sites table 178 */ 179 public void closeDialogWindow(boolean updateTable) { 180 181 if (m_dialogWindow != null) { 182 m_dialogWindow.close(); 183 m_dialogWindow = null; 184 } 185 if (updateTable) { 186 A_CmsUI.get().reload(); 187 } 188 } 189 190 /** 191 * @see org.opencms.ui.apps.I_CmsCRUDApp#createElement(java.lang.Object) 192 */ 193 public void createElement(CmsSite element) { 194 195 try { 196 OpenCms.getSiteManager().addSite(getRootCmsObject(), element); 197 } catch (CmsException e) { 198 LOG.error("unable to save site", e); 199 } 200 201 } 202 203 /** 204 * @see org.opencms.ui.apps.I_CmsCRUDApp#defaultAction(java.lang.String) 205 */ 206 public void defaultAction(String elementId) { 207 208 openEditDialog(elementId); 209 210 } 211 212 /** 213 * @see org.opencms.ui.apps.I_CmsCRUDApp#deleteElements(java.util.List) 214 */ 215 public void deleteElements(List<String> elementId) { 216 217 for (String siteRoot : elementId) { 218 try { 219 CmsSite site = getElement(siteRoot); 220 if (site != null) { 221 OpenCms.getSiteManager().removeSite(getRootCmsObject(), site); 222 } 223 } catch (CmsException e) { 224 LOG.error("Unable to delete site", e); 225 } 226 } 227 updateInfo(); 228 } 229 230 /** 231 * @see org.opencms.ui.apps.I_CmsCRUDApp#getAllElements() 232 */ 233 public List<CmsSite> getAllElements() { 234 235 List<CmsSite> res = OpenCms.getSiteManager().getAvailableSites(getRootCmsObject(), false).stream().filter( 236 site -> !site.isGenerated()).collect(Collectors.toList()); 237 return res; 238 } 239 240 /** 241 * Get corrupted sites.<p> 242 * 243 * @return List<CmsSite> 244 */ 245 public List<CmsSite> getCorruptedSites() { 246 247 return OpenCms.getSiteManager().getAvailableCorruptedSites(getRootCmsObject(), true); 248 } 249 250 /** 251 * @see org.opencms.ui.apps.I_CmsCRUDApp#getElement(java.lang.String) 252 */ 253 public CmsSite getElement(String elementId) { 254 255 return OpenCms.getSiteManager().getSiteForSiteRoot(elementId); 256 } 257 258 /** 259 * Returns the fav icon path for the given site.<p> 260 * 261 * @param siteRoot the site root 262 * 263 * @return the icon path 264 */ 265 public Resource getFavIcon(String siteRoot) { 266 267 CmsResource iconResource = null; 268 try { 269 iconResource = getRootCmsObject().readResource(siteRoot + "/" + CmsSiteManager.FAVICON); 270 } catch (CmsException e) { 271 //no favicon there 272 } 273 if (iconResource != null) { 274 return new ExternalResource( 275 OpenCms.getLinkManager().getPermalink(getRootCmsObject(), iconResource.getRootPath())); 276 } 277 return new CmsCssIcon(OpenCmsTheme.ICON_SITE); 278 } 279 280 /** 281 * @see org.opencms.ui.apps.A_CmsWorkplaceApp#initUI(org.opencms.ui.apps.I_CmsAppUIContext) 282 */ 283 @Override 284 public void initUI(I_CmsAppUIContext context) { 285 286 context.addPublishButton(changes -> { 287 A_CmsUI.get().reload(); 288 }); 289 super.initUI(context); 290 } 291 292 /** 293 * Checks if site export is enabled. 294 * @return true if site export is enabled 295 */ 296 public boolean isExportEnabled() { 297 298 // Classes that extend CmsSiteManager must explicitly override this method if the feature should be enabled for them 299 return this.getClass() == CmsSiteManager.class; 300 } 301 302 /** 303 * Opens the delete dialog for the given sites.<p> 304 * 305 * @param data the site roots 306 */ 307 public void openDeleteDialog(Set<String> data) { 308 309 CmsDeleteSiteDialog form = new CmsDeleteSiteDialog(this, data); 310 openDialog(form, CmsVaadinUtils.getMessageText(Messages.GUI_SITE_DELETE_0)); 311 } 312 313 /** 314 * Opens the edit site dialog.<p> 315 * 316 * @param siteRoot the site root of the site to edit, if <code>null</code> 317 */ 318 public void openEditDialog(String siteRoot) { 319 320 CmsEditSiteForm form; 321 String caption; 322 if (siteRoot != null) { 323 form = new CmsEditSiteForm(m_rootCms, this, siteRoot); 324 caption = CmsVaadinUtils.getMessageText( 325 Messages.GUI_SITE_CONFIGURATION_EDIT_1, 326 m_sitesTable.getContainer().getItem(siteRoot).getItemProperty(TableProperty.Title).getValue()); 327 } else { 328 form = new CmsEditSiteForm(m_rootCms, this); 329 caption = CmsVaadinUtils.getMessageText(Messages.GUI_SITE_ADD_0); 330 } 331 openDialog(form, caption); 332 } 333 334 /** 335 * Opens the global settings dialog.<p> 336 */ 337 public void openSettingsDailog() { 338 339 CmsGlobalForm form = new CmsGlobalForm(this); 340 openDialog(form, CmsVaadinUtils.getMessageText(Messages.GUI_SITE_GLOBAL_CONFIGURATION_0)); 341 } 342 343 /** 344 * Opens the update server configuration dialog.<p> 345 */ 346 public void openUpdateServerConfigDailog() { 347 348 CmsWebServerConfigForm form = new CmsWebServerConfigForm(this); 349 openDialog(form, CmsVaadinUtils.getMessageText(Messages.GUI_SITE_WEBSERVERCONFIG_0)); 350 } 351 352 /** 353 * Updates the general settings.<p> 354 * 355 * @param cms the cms to use 356 * @param defaultUri the default URI 357 * @param workplaceServers the workplace server URLs 358 * @param sharedFolder the shared folder URI 359 */ 360 public void updateGeneralSettings( 361 CmsObject cms, 362 String defaultUri, 363 Map<String, CmsSSLMode> workplaceServers, 364 String sharedFolder) { 365 366 try { 367 OpenCms.getSiteManager().updateGeneralSettings(cms, defaultUri, workplaceServers, sharedFolder); 368 OpenCms.writeConfiguration(CmsSitesConfiguration.class); 369 } catch (Exception e) { 370 LOG.error(e.getLocalizedMessage(), e); 371 CmsErrorDialog.showErrorDialog(e); 372 } 373 } 374 375 /** 376 * @see org.opencms.ui.apps.I_CmsCRUDApp#writeElement(java.lang.Object) 377 */ 378 public void writeElement(CmsSite element) { 379 380 try { 381 OpenCms.getSiteManager().updateSite(m_rootCms, getElement(element.getSiteRoot()), element); 382 } catch (CmsException e) { 383 LOG.error("Unabel to update site", e); 384 } 385 //updateInfo(); 386 //m_sitesTable.loadSites(); 387 } 388 389 /** 390 * Creates the table holdings all available sites. 391 * @return a vaadin table component 392 */ 393 394 protected CmsSitesTable createSitesTable() { 395 396 CmsSitesTable table = new CmsSitesTable(this); 397 table.loadSites(); 398 return table; 399 } 400 401 /** 402 * @see org.opencms.ui.apps.A_CmsWorkplaceApp#getBreadCrumbForState(java.lang.String) 403 */ 404 @Override 405 protected LinkedHashMap<String, String> getBreadCrumbForState(String state) { 406 407 LinkedHashMap<String, String> crumbs = new LinkedHashMap<String, String>(); 408 crumbs.put("", CmsVaadinUtils.getMessageText(Messages.GUI_SITE_MANAGER_TITLE_SHORT_0)); 409 return crumbs; 410 } 411 412 /** 413 * @see org.opencms.ui.apps.A_CmsWorkplaceApp#getComponentForState(java.lang.String) 414 */ 415 @Override 416 protected Component getComponentForState(String state) { 417 418 m_sitesTable = createSitesTable(); 419 420 m_rootLayout.setMainHeightFull(true); 421 m_siteTableFilter = new TextField(); 422 m_siteTableFilter.setIcon(FontOpenCms.FILTER); 423 m_siteTableFilter.setInputPrompt( 424 Messages.get().getBundle(UI.getCurrent().getLocale()).key(Messages.GUI_EXPLORER_FILTER_0)); 425 m_siteTableFilter.addStyleName(ValoTheme.TEXTFIELD_INLINE_ICON); 426 m_siteTableFilter.setWidth("200px"); 427 m_siteTableFilter.addTextChangeListener(new TextChangeListener() { 428 429 private static final long serialVersionUID = 1L; 430 431 public void textChange(TextChangeEvent event) { 432 433 m_sitesTable.filter(event.getText()); 434 } 435 }); 436 m_infoLayout.addComponent(m_siteTableFilter); 437 addToolbarButtons(); 438 return m_sitesTable; 439 } 440 441 /** 442 * Returns the root cms object.<p> 443 * 444 * @return the root cms object 445 */ 446 protected CmsObject getRootCmsObject() { 447 448 if (m_rootCms == null) { 449 450 m_rootCms = getOfflineCmsObject(A_CmsUI.getCmsObject()); 451 m_rootCms.getRequestContext().setSiteRoot(""); 452 453 } 454 return m_rootCms; 455 } 456 457 /** 458 * @see org.opencms.ui.apps.A_CmsWorkplaceApp#getSubNavEntries(java.lang.String) 459 */ 460 @Override 461 protected List<NavEntry> getSubNavEntries(String state) { 462 463 return null; 464 } 465 466 /** 467 * Opens a given dialog.<p> 468 * 469 * @param dialog to be shown 470 * @param windowCaption caption of window 471 */ 472 protected void openDialog(CmsBasicDialog dialog, String windowCaption) { 473 474 if (m_dialogWindow != null) { 475 m_dialogWindow.close(); 476 } 477 478 m_dialogWindow = CmsBasicDialog.prepareWindow(DialogWidth.wide); 479 m_dialogWindow.setContent(dialog); 480 m_dialogWindow.setCaption(windowCaption); 481 482 A_CmsUI.get().addWindow(m_dialogWindow); 483 m_dialogWindow.center(); 484 } 485 486 /** 487 * Update the info button.<p> 488 */ 489 protected void updateInfo() { 490 491 m_infoButton.replaceData(getInfoMap()); 492 } 493 494 /** 495 * Adds the toolbar buttons.<p> 496 */ 497 private void addToolbarButtons() { 498 499 Button add = CmsToolBar.createButton(FontOpenCms.WAND, CmsVaadinUtils.getMessageText(Messages.GUI_SITE_ADD_0)); 500 add.addClickListener(new ClickListener() { 501 502 private static final long serialVersionUID = 1L; 503 504 public void buttonClick(ClickEvent event) { 505 506 openEditDialog(null); 507 } 508 }); 509 m_uiContext.addToolbarButton(add); 510 511 Button settings = CmsToolBar.createButton( 512 FontOpenCms.SETTINGS, 513 CmsVaadinUtils.getMessageText(Messages.GUI_SITE_GLOBAL_0)); 514 settings.addClickListener(new ClickListener() { 515 516 private static final long serialVersionUID = 1L; 517 518 public void buttonClick(ClickEvent event) { 519 520 openSettingsDailog(); 521 } 522 }); 523 m_uiContext.addToolbarButton(settings); 524 if (OpenCms.getSiteManager().isConfigurableWebServer() || isLetsEncryptConfiguredForWebserverThread()) { 525 Button webServer = CmsToolBar.createButton( 526 FontAwesome.SERVER, 527 CmsVaadinUtils.getMessageText(Messages.GUI_SITE_WEBSERVERCONFIG_0)); 528 webServer.addClickListener(new ClickListener() { 529 530 private static final long serialVersionUID = 1L; 531 532 public void buttonClick(ClickEvent event) { 533 534 openUpdateServerConfigDailog(); 535 } 536 }); 537 m_uiContext.addToolbarButton(webServer); 538 } 539 540 m_infoButton = new CmsInfoButton(getInfoMap()); 541 542 m_infoButton.setWindowCaption(CmsVaadinUtils.getMessageText(Messages.GUI_SITE_STATISTICS_CAPTION_0)); 543 m_infoButton.setDescription(CmsVaadinUtils.getMessageText(Messages.GUI_SITE_STATISTICS_CAPTION_0)); 544 m_uiContext.addToolbarButton(m_infoButton); 545 } 546 547 /** 548 * Get info map.<p> 549 * 550 * @return map of sites info 551 */ 552 private Map<String, String> getInfoMap() { 553 554 Map<String, String> infos = new LinkedHashMap<String, String>(); 555 int corruptedSites = getCorruptedSites().size(); 556 infos.put( 557 CmsVaadinUtils.getMessageText(Messages.GUI_SITE_STATISTICS_NUM_WEBSITES_0), 558 String.valueOf(getAllElements().size() + corruptedSites)); 559 560 if (corruptedSites > 0) { 561 infos.put( 562 CmsVaadinUtils.getMessageText(Messages.GUI_SITE_STATISTICS_NUM_CORRUPTED_WEBSITES_0), 563 String.valueOf(corruptedSites)); 564 } 565 566 return infos; 567 } 568}