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