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 * Opens the delete dialog for the given sites.<p> 294 * 295 * @param data the site roots 296 */ 297 public void openDeleteDialog(Set<String> data) { 298 299 CmsDeleteSiteDialog form = new CmsDeleteSiteDialog(this, data); 300 openDialog(form, CmsVaadinUtils.getMessageText(Messages.GUI_SITE_DELETE_0)); 301 } 302 303 /** 304 * Opens the edit site dialog.<p> 305 * 306 * @param siteRoot the site root of the site to edit, if <code>null</code> 307 */ 308 public void openEditDialog(String siteRoot) { 309 310 CmsEditSiteForm form; 311 String caption; 312 if (siteRoot != null) { 313 form = new CmsEditSiteForm(m_rootCms, this, siteRoot); 314 caption = CmsVaadinUtils.getMessageText( 315 Messages.GUI_SITE_CONFIGURATION_EDIT_1, 316 m_sitesTable.getContainer().getItem(siteRoot).getItemProperty(TableProperty.Title).getValue()); 317 } else { 318 form = new CmsEditSiteForm(m_rootCms, this); 319 caption = CmsVaadinUtils.getMessageText(Messages.GUI_SITE_ADD_0); 320 } 321 openDialog(form, caption); 322 } 323 324 /** 325 * Opens the global settings dialog.<p> 326 */ 327 public void openSettingsDailog() { 328 329 CmsGlobalForm form = new CmsGlobalForm(this); 330 openDialog(form, CmsVaadinUtils.getMessageText(Messages.GUI_SITE_GLOBAL_CONFIGURATION_0)); 331 } 332 333 /** 334 * Opens the update server configuration dialog.<p> 335 */ 336 public void openUpdateServerConfigDailog() { 337 338 CmsWebServerConfigForm form = new CmsWebServerConfigForm(this); 339 openDialog(form, CmsVaadinUtils.getMessageText(Messages.GUI_SITE_WEBSERVERCONFIG_0)); 340 } 341 342 /** 343 * Updates the general settings.<p> 344 * 345 * @param cms the cms to use 346 * @param defaultUri the default URI 347 * @param workplaceServers the workplace server URLs 348 * @param sharedFolder the shared folder URI 349 */ 350 public void updateGeneralSettings( 351 CmsObject cms, 352 String defaultUri, 353 Map<String, CmsSSLMode> workplaceServers, 354 String sharedFolder) { 355 356 try { 357 OpenCms.getSiteManager().updateGeneralSettings(cms, defaultUri, workplaceServers, sharedFolder); 358 OpenCms.writeConfiguration(CmsSitesConfiguration.class); 359 } catch (Exception e) { 360 LOG.error(e.getLocalizedMessage(), e); 361 CmsErrorDialog.showErrorDialog(e); 362 } 363 } 364 365 /** 366 * @see org.opencms.ui.apps.I_CmsCRUDApp#writeElement(java.lang.Object) 367 */ 368 public void writeElement(CmsSite element) { 369 370 try { 371 OpenCms.getSiteManager().updateSite(m_rootCms, getElement(element.getSiteRoot()), element); 372 } catch (CmsException e) { 373 LOG.error("Unabel to update site", e); 374 } 375 //updateInfo(); 376 //m_sitesTable.loadSites(); 377 } 378 379 /** 380 * Creates the table holdings all available sites. 381 * @return a vaadin table component 382 */ 383 384 protected CmsSitesTable createSitesTable() { 385 386 CmsSitesTable table = new CmsSitesTable(this); 387 table.loadSites(); 388 return table; 389 } 390 391 /** 392 * @see org.opencms.ui.apps.A_CmsWorkplaceApp#getBreadCrumbForState(java.lang.String) 393 */ 394 @Override 395 protected LinkedHashMap<String, String> getBreadCrumbForState(String state) { 396 397 LinkedHashMap<String, String> crumbs = new LinkedHashMap<String, String>(); 398 crumbs.put("", CmsVaadinUtils.getMessageText(Messages.GUI_SITE_MANAGER_TITLE_SHORT_0)); 399 return crumbs; 400 } 401 402 /** 403 * @see org.opencms.ui.apps.A_CmsWorkplaceApp#getComponentForState(java.lang.String) 404 */ 405 @Override 406 protected Component getComponentForState(String state) { 407 408 m_sitesTable = createSitesTable(); 409 410 m_rootLayout.setMainHeightFull(true); 411 m_siteTableFilter = new TextField(); 412 m_siteTableFilter.setIcon(FontOpenCms.FILTER); 413 m_siteTableFilter.setInputPrompt( 414 Messages.get().getBundle(UI.getCurrent().getLocale()).key(Messages.GUI_EXPLORER_FILTER_0)); 415 m_siteTableFilter.addStyleName(ValoTheme.TEXTFIELD_INLINE_ICON); 416 m_siteTableFilter.setWidth("200px"); 417 m_siteTableFilter.addTextChangeListener(new TextChangeListener() { 418 419 private static final long serialVersionUID = 1L; 420 421 public void textChange(TextChangeEvent event) { 422 423 m_sitesTable.filter(event.getText()); 424 } 425 }); 426 m_infoLayout.addComponent(m_siteTableFilter); 427 addToolbarButtons(); 428 return m_sitesTable; 429 } 430 431 /** 432 * Returns the root cms object.<p> 433 * 434 * @return the root cms object 435 */ 436 protected CmsObject getRootCmsObject() { 437 438 if (m_rootCms == null) { 439 440 m_rootCms = getOfflineCmsObject(A_CmsUI.getCmsObject()); 441 m_rootCms.getRequestContext().setSiteRoot(""); 442 443 } 444 return m_rootCms; 445 } 446 447 /** 448 * @see org.opencms.ui.apps.A_CmsWorkplaceApp#getSubNavEntries(java.lang.String) 449 */ 450 @Override 451 protected List<NavEntry> getSubNavEntries(String state) { 452 453 return null; 454 } 455 456 /** 457 * Opens a given dialog.<p> 458 * 459 * @param dialog to be shown 460 * @param windowCaption caption of window 461 */ 462 protected void openDialog(CmsBasicDialog dialog, String windowCaption) { 463 464 if (m_dialogWindow != null) { 465 m_dialogWindow.close(); 466 } 467 468 m_dialogWindow = CmsBasicDialog.prepareWindow(DialogWidth.wide); 469 m_dialogWindow.setContent(dialog); 470 m_dialogWindow.setCaption(windowCaption); 471 472 A_CmsUI.get().addWindow(m_dialogWindow); 473 m_dialogWindow.center(); 474 } 475 476 /** 477 * Update the info button.<p> 478 */ 479 protected void updateInfo() { 480 481 m_infoButton.replaceData(getInfoMap()); 482 } 483 484 /** 485 * Adds the toolbar buttons.<p> 486 */ 487 private void addToolbarButtons() { 488 489 Button add = CmsToolBar.createButton(FontOpenCms.WAND, CmsVaadinUtils.getMessageText(Messages.GUI_SITE_ADD_0)); 490 add.addClickListener(new ClickListener() { 491 492 private static final long serialVersionUID = 1L; 493 494 public void buttonClick(ClickEvent event) { 495 496 openEditDialog(null); 497 } 498 }); 499 m_uiContext.addToolbarButton(add); 500 501 Button settings = CmsToolBar.createButton( 502 FontOpenCms.SETTINGS, 503 CmsVaadinUtils.getMessageText(Messages.GUI_SITE_GLOBAL_0)); 504 settings.addClickListener(new ClickListener() { 505 506 private static final long serialVersionUID = 1L; 507 508 public void buttonClick(ClickEvent event) { 509 510 openSettingsDailog(); 511 } 512 }); 513 m_uiContext.addToolbarButton(settings); 514 if (OpenCms.getSiteManager().isConfigurableWebServer() || isLetsEncryptConfiguredForWebserverThread()) { 515 Button webServer = CmsToolBar.createButton( 516 FontAwesome.SERVER, 517 CmsVaadinUtils.getMessageText(Messages.GUI_SITE_WEBSERVERCONFIG_0)); 518 webServer.addClickListener(new ClickListener() { 519 520 private static final long serialVersionUID = 1L; 521 522 public void buttonClick(ClickEvent event) { 523 524 openUpdateServerConfigDailog(); 525 } 526 }); 527 m_uiContext.addToolbarButton(webServer); 528 } 529 530 m_infoButton = new CmsInfoButton(getInfoMap()); 531 532 m_infoButton.setWindowCaption(CmsVaadinUtils.getMessageText(Messages.GUI_SITE_STATISTICS_CAPTION_0)); 533 m_infoButton.setDescription(CmsVaadinUtils.getMessageText(Messages.GUI_SITE_STATISTICS_CAPTION_0)); 534 m_uiContext.addToolbarButton(m_infoButton); 535 } 536 537 /** 538 * Get info map.<p> 539 * 540 * @return map of sites info 541 */ 542 private Map<String, String> getInfoMap() { 543 544 Map<String, String> infos = new LinkedHashMap<String, String>(); 545 int corruptedSites = getCorruptedSites().size(); 546 infos.put( 547 CmsVaadinUtils.getMessageText(Messages.GUI_SITE_STATISTICS_NUM_WEBSITES_0), 548 String.valueOf(getAllElements().size() + corruptedSites)); 549 550 if (corruptedSites > 0) { 551 infos.put( 552 CmsVaadinUtils.getMessageText(Messages.GUI_SITE_STATISTICS_NUM_CORRUPTED_WEBSITES_0), 553 String.valueOf(corruptedSites)); 554 } 555 556 return infos; 557 } 558}