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.dbmanager;
029
030import org.opencms.file.CmsObject;
031import org.opencms.file.CmsResource;
032import org.opencms.file.CmsResourceFilter;
033import org.opencms.file.CmsVfsResourceNotFoundException;
034import org.opencms.importexport.CmsExportParameters;
035import org.opencms.importexport.CmsVfsImportExportHandler;
036import org.opencms.main.CmsException;
037import org.opencms.main.CmsLog;
038import org.opencms.main.OpenCms;
039import org.opencms.module.CmsModule.ExportMode;
040import org.opencms.report.A_CmsReportThread;
041import org.opencms.ui.A_CmsUI;
042import org.opencms.ui.CmsVaadinUtils;
043import org.opencms.ui.apps.Messages;
044import org.opencms.ui.components.CmsBasicDialog;
045import org.opencms.ui.components.CmsBasicDialog.DialogWidth;
046import org.opencms.ui.components.CmsDateField;
047import org.opencms.ui.components.editablegroup.CmsEditableGroup;
048import org.opencms.ui.components.editablegroup.I_CmsEditableGroupRow;
049import org.opencms.ui.components.fileselect.CmsPathSelectField;
050import org.opencms.util.CmsStringUtil;
051import org.opencms.util.CmsUUID;
052import org.opencms.workplace.threads.CmsExportThread;
053
054import java.io.File;
055import java.util.ArrayList;
056import java.util.Arrays;
057import java.util.List;
058import java.util.stream.Collectors;
059
060import org.apache.commons.logging.Log;
061
062import com.google.common.base.Supplier;
063import com.vaadin.ui.Button;
064import com.vaadin.ui.Button.ClickEvent;
065import com.vaadin.ui.Button.ClickListener;
066import com.vaadin.ui.Component;
067import com.vaadin.ui.FormLayout;
068import com.vaadin.ui.Window;
069import com.vaadin.v7.data.Property.ValueChangeEvent;
070import com.vaadin.v7.data.Property.ValueChangeListener;
071import com.vaadin.v7.data.Validator;
072import com.vaadin.v7.data.util.IndexedContainer;
073import com.vaadin.v7.shared.ui.combobox.FilteringMode;
074import com.vaadin.v7.ui.AbstractSelect.ItemCaptionMode;
075import com.vaadin.v7.ui.CheckBox;
076import com.vaadin.v7.ui.ComboBox;
077import com.vaadin.v7.ui.VerticalLayout;
078
079/**
080 * Class for the Export dialog.<p>
081 */
082public class CmsDbExportView extends VerticalLayout {
083
084    /**
085     * Validator for entered resources.<p>
086     */
087    class ResourceValidator implements Validator {
088
089        /**vaadin serial id.*/
090        private static final long serialVersionUID = -4341247963641286345L;
091
092        /**
093         * @see com.vaadin.data.Validator#validate(java.lang.Object)
094         */
095        public void validate(Object value) throws InvalidValueException {
096
097            String resourcePath = (String)value;
098            if ((value == null)) {
099                throw new InvalidValueException(
100                    CmsVaadinUtils.getMessageText(Messages.GUI_DATABASEAPP_EXPORT_INVALID_RESOURCE_EMPTY_0));
101            }
102
103            if (resourcePath.isEmpty()) {
104                throw new InvalidValueException(
105                    CmsVaadinUtils.getMessageText(Messages.GUI_DATABASEAPP_EXPORT_INVALID_RESOURCE_EMPTY_0));
106            }
107
108            if (!m_cms.existsResource(resourcePath, CmsResourceFilter.IGNORE_EXPIRATION)) {
109                throw new InvalidValueException(
110                    CmsVaadinUtils.getMessageText(Messages.GUI_DATABASEAPP_EXPORT_INVALID_RESOURCE_NOTFOUND_0));
111            }
112        }
113    }
114
115    /**
116     * Validator for the target field.<p>
117     */
118    class TargetValidator implements Validator {
119
120        /**vaadin serial id.*/
121        private static final long serialVersionUID = 7530400504930612299L;
122
123        /**
124         * @see com.vaadin.data.Validator#validate(java.lang.Object)
125         */
126        public void validate(Object value) throws InvalidValueException {
127
128            if (value == null) {
129                throw new InvalidValueException(
130                    CmsVaadinUtils.getMessageText(Messages.GUI_DATABASEAPP_EXPORT_INVALID_TARGET_0));
131            }
132        }
133    }
134
135    /** The logger for this class. */
136    private static final Log LOG = CmsLog.getLog(CmsDbExportView.class.getName());
137
138    /**vaadin serial id.*/
139    private static final long serialVersionUID = -2571459807662862053L;
140
141    /**Copy of current CmsObject.*/
142    protected CmsObject m_cms;
143
144    /** Button to add a list of resources through a text area. */
145    private Button m_addResources;
146
147    /**vaadin component.*/
148    private CheckBox m_asFiles;
149
150    /**vaadin component.*/
151    private CmsDateField m_changedSince;
152
153    /** The export parameters object that is edited on this dialog. */
154    private CmsExportParameters m_exportParams;
155
156    /**vaadin component.*/
157    private CheckBox m_includeAccount;
158
159    /**vaadin component.*/
160    private CheckBox m_includeProject;
161
162    /**vaadin component.*/
163    private CheckBox m_includeResource;
164
165    /**vaadin component.*/
166    private CheckBox m_includeSystem;
167
168    /**vaadin component.*/
169    private CheckBox m_includeUnchanged;
170
171    /**vaadin component.*/
172    private CheckBox m_modified;
173
174    /**vaadin component.*/
175    private Button m_ok;
176
177    private ComboBox m_project;
178
179    /**vaadin component.*/
180    private CheckBox m_recursive;
181
182    /**Vaadin component. */
183    private CheckBox m_reducedMetadata;
184
185    /**vaadin component.*/
186    private VerticalLayout m_resources;
187
188    private CmsEditableGroup m_resourcesGroup;
189
190    /**vaadin component.*/
191    private ComboBox m_site;
192
193    /**vaadin component.*/
194    private CheckBox m_skipParentFolders;
195
196    /**vaadin component.*/
197    private ComboBox m_target;
198
199    /**
200     * public constructor.<p>
201     */
202    public CmsDbExportView() {
203
204        CmsVaadinUtils.readAndLocalizeDesign(this, CmsVaadinUtils.getWpMessagesForCurrentLocale(), null);
205        setHeightUndefined();
206        try {
207            m_cms = OpenCms.initCmsObject(A_CmsUI.getCmsObject());
208        } catch (CmsException e) {
209            LOG.error("Failed to clone CmsObject", e);
210        }
211
212        m_resourcesGroup = new CmsEditableGroup(m_resources, new Supplier<Component>() {
213
214            public Component get() {
215
216                return getResourceRow("");
217
218            }
219
220        }, CmsVaadinUtils.getMessageText(Messages.GUI_DATABASEAPP_EXPORT_ADD_RESOURCE_0));
221        m_resourcesGroup.init();
222        m_resourcesGroup.addRow(getResourceRow(""));
223        m_exportParams = new CmsExportParameters();
224
225        setupCheckBoxes();
226        setupComboBoxFile();
227        setupComboBoxSite();
228
229        m_ok.addClickListener(new ClickListener() {
230
231            private static final long serialVersionUID = -4224924796312615674L;
232
233            public void buttonClick(ClickEvent event) {
234
235                addResourceIfEmpty();
236                addValidators();
237                if (isFormValid()) {
238                    startThread();
239                }
240            }
241        });
242        m_addResources.addClickListener(e -> openAddResourcesDialog());
243    }
244
245    protected void addResourceIfEmpty() {
246
247        if (m_resourcesGroup.getRows().size() == 0) {
248            m_resourcesGroup.addRow(getResourceRow(""));
249        }
250    }
251
252    /**
253     * Adds all validators to the formular.<p>
254     */
255    protected void addValidators() {
256
257        //Target file ComboBox
258        m_target.removeAllValidators();
259        m_target.addValidator(new TargetValidator());
260
261        for (I_CmsEditableGroupRow row : m_resourcesGroup.getRows()) {
262            FormLayout layout = (FormLayout)(row.getComponent());
263            CmsPathSelectField field = (CmsPathSelectField)layout.getComponent(0);
264            field.removeAllValidators();
265            field.addValidator(new ResourceValidator());
266        }
267    }
268
269    /**
270     * Changes the site of the cms object.<p>
271     */
272    protected void changeSite() {
273
274        m_cms.getRequestContext().setSiteRoot((String)m_site.getValue());
275    }
276
277    protected Component getResourceRow(String path) {
278
279        FormLayout res = new FormLayout();
280        CmsPathSelectField field = new CmsPathSelectField();
281        if (!CmsStringUtil.isEmptyOrWhitespaceOnly(path)) {
282            field.setValue(path);
283        }
284        field.setCaption(CmsVaadinUtils.getMessageText(Messages.GUI_DATABASEAPP_EXPORT_RESOURCES_0));
285        field.setDescription(CmsVaadinUtils.getMessageText(Messages.GUI_DATABASEAPP_EXPORT_RESOURCES_HELP_0));
286        field.setCmsObject(m_cms);
287        res.addComponent(field);
288        return res;
289    }
290
291    /**
292     * Checks if form is valid.<p>
293     *
294     * @return true if all fields are valid
295     */
296    protected boolean isFormValid() {
297
298        return m_target.isValid() & allResourcesValid();
299    }
300
301    /**
302     * Checks if resources exist in site. if not the row gets removed.<p>
303     */
304    protected void removeUnvalidPathFields() {
305
306        int counter = 0;
307        List<I_CmsEditableGroupRow> rowsToRemove = new ArrayList<I_CmsEditableGroupRow>();
308        for (I_CmsEditableGroupRow row : m_resourcesGroup.getRows()) {
309            FormLayout layout = (FormLayout)(row.getComponent());
310            CmsPathSelectField field = (CmsPathSelectField)layout.getComponent(0);
311            if (!m_cms.existsResource(field.getValue(), CmsResourceFilter.IGNORE_EXPIRATION)) {
312                rowsToRemove.add(row);
313            }
314        }
315
316        for (I_CmsEditableGroupRow row : rowsToRemove) {
317            m_resourcesGroup.remove(row);
318        }
319    }
320
321    /**
322     * Starts the export thread and displays it's report.<p>
323     */
324    protected void startThread() {
325
326        try {
327            m_cms.getRequestContext().setCurrentProject(m_cms.readProject((CmsUUID)m_project.getValue()));
328        } catch (CmsException e) {
329            LOG.error("Unable to set project", e);
330        }
331        updateExportParams();
332
333        CmsVfsImportExportHandler handler = new CmsVfsImportExportHandler();
334        handler.setExportParams(m_exportParams);
335        A_CmsReportThread exportThread = new CmsExportThread(m_cms, handler, false);
336
337        Window window = CmsBasicDialog.prepareWindow(DialogWidth.max);
338        window.setContent(new CmsExportThreadDialog(handler, exportThread, window));
339        A_CmsUI.get().addWindow(window);
340        exportThread.start();
341    }
342
343    /**
344     * Checks if all resources are valid.<p>
345     *
346     * @return true if resources are valid
347     */
348    private boolean allResourcesValid() {
349
350        boolean valid = true;
351
352        for (I_CmsEditableGroupRow row : m_resourcesGroup.getRows()) {
353            FormLayout layout = (FormLayout)(row.getComponent());
354            CmsPathSelectField field = (CmsPathSelectField)layout.getComponent(0);
355            if (!field.isValid()) {
356                valid = false;
357            }
358        }
359
360        return valid;
361    }
362
363    /**
364     * Reads out resources from form.<p>
365     *
366     * @return List with site-relative paths of resources
367     */
368    private List<String> getResources() {
369
370        List<String> res = new ArrayList<String>();
371
372        for (I_CmsEditableGroupRow row : m_resourcesGroup.getRows()) {
373            FormLayout layout = (FormLayout)(row.getComponent());
374            CmsPathSelectField field = (CmsPathSelectField)layout.getComponent(0);
375            String value = field.getValue();
376            if (!value.isEmpty()) {
377                if (!value.endsWith("/")) {
378                    try {
379                        CmsResource resource = m_cms.readResource(value, CmsResourceFilter.IGNORE_EXPIRATION);
380                        if (resource.isFolder()) {
381                            value = value + "/";
382                        }
383                    } catch (CmsException e) {
384                        if (!(e instanceof CmsVfsResourceNotFoundException)) {
385                            LOG.error(e.getLocalizedMessage());
386                        }
387                    }
388                }
389                if (!res.contains(value)) {
390                    res.add(value);
391                }
392            }
393        }
394
395        return res;
396    }
397
398    /**
399     * Opens the dialog for adding new resources via a text area.
400     */
401    private void openAddResourcesDialog() {
402
403        Window window = CmsBasicDialog.prepareWindow(DialogWidth.wide);
404        window.setCaption(CmsVaadinUtils.getMessageText(Messages.GUI_DATABASEAPP_OPEN_ADD_RESOURCES_DIALOG_0));
405        window.setContent(new CmsAddExportResourcesDialog(result -> updateExportResources(result)));
406        A_CmsUI.get().addWindow(window);
407    }
408
409    /**
410     * Sets the init values for check boxes.<p>
411     */
412    private void setupCheckBoxes() {
413
414        m_includeResource.setValue(Boolean.valueOf(true));
415        m_includeUnchanged.setValue(Boolean.valueOf(true));
416        m_includeSystem.setValue(Boolean.valueOf(true));
417        m_recursive.setValue(Boolean.valueOf(true));
418    }
419
420    /**
421     * Sets up the combo box for the target file.<p>
422     */
423    private void setupComboBoxFile() {
424
425        m_target.setInputPrompt(CmsVaadinUtils.getMessageText(Messages.GUI_DATABASEAPP_EXPORT_FILE_NAME_EMPTY_0));
426        m_target.setNewItemsAllowed(true);
427        List<String> files = CmsDbManager.getFileListFromServer(true);
428        for (String file : files) {
429            m_target.addItem(file);
430        }
431    }
432
433    /**
434     * Sets up the combo box for the site choice.<p>
435     */
436    private void setupComboBoxSite() {
437
438        IndexedContainer container = CmsVaadinUtils.getAvailableSitesContainer(A_CmsUI.getCmsObject(), "title");
439        m_site.setContainerDataSource(container);
440        m_site.setItemCaptionMode(ItemCaptionMode.PROPERTY);
441        m_site.setItemCaptionPropertyId("title");
442        m_site.setFilteringMode(FilteringMode.CONTAINS);
443        m_site.setNullSelectionAllowed(false);
444        m_site.setValue(A_CmsUI.getCmsObject().getRequestContext().getSiteRoot());
445        m_site.addValueChangeListener(new ValueChangeListener() {
446
447            private static final long serialVersionUID = -1019243885633462477L;
448
449            public void valueChange(ValueChangeEvent event) {
450
451                changeSite();
452                removeUnvalidPathFields();
453            }
454        });
455
456        m_project.setContainerDataSource(CmsVaadinUtils.getProjectsContainer(A_CmsUI.getCmsObject(), "caption"));
457        m_project.setItemCaptionPropertyId("caption");
458        m_project.select(A_CmsUI.getCmsObject().getRequestContext().getCurrentProject().getUuid());
459        m_project.setNewItemsAllowed(false);
460        m_project.setNullSelectionAllowed(false);
461        m_project.setTextInputAllowed(false);
462    }
463
464    /**
465     * Updates the Export parameter based on user input.<p>
466     */
467    private void updateExportParams() {
468
469        m_exportParams.setExportAccountData(m_includeAccount.getValue().booleanValue());
470        m_exportParams.setExportAsFiles(m_asFiles.getValue().booleanValue());
471        m_exportParams.setExportProjectData(m_includeProject.getValue().booleanValue());
472        m_exportParams.setExportResourceData(m_includeResource.getValue().booleanValue());
473        m_exportParams.setInProject(m_modified.getValue().booleanValue());
474        m_exportParams.setIncludeSystemFolder(m_includeSystem.getValue().booleanValue());
475        m_exportParams.setIncludeUnchangedResources(m_includeUnchanged.getValue().booleanValue());
476        m_exportParams.setSkipParentFolders(m_skipParentFolders.getValue().booleanValue());
477        String exportFileName = OpenCms.getSystemInfo().getAbsoluteRfsPathRelativeToWebInf(
478            OpenCms.getSystemInfo().getPackagesRfsPath() + File.separator + (String)m_target.getValue());
479        m_exportParams.setPath(exportFileName);
480        m_exportParams.setRecursive(m_recursive.getValue().booleanValue());
481        m_exportParams.setResources(getResources());
482        ExportMode exportMode = m_reducedMetadata.getValue().booleanValue() ? ExportMode.REDUCED : ExportMode.DEFAULT;
483        m_exportParams.setExportMode(exportMode);
484        if (m_changedSince.getValue() != null) {
485            m_exportParams.setContentAge(m_changedSince.getDate().getTime());
486        } else {
487            m_exportParams.setContentAge(0);
488        }
489    }
490
491    /**
492     * Updates the export resource fields from a newline-separated list of paths.
493     *
494     * @param resourceListing a newline-separated list of paths
495     */
496    private void updateExportResources(String resourceListing) {
497
498        List<String> exResources = getResources();
499        List<String> lines = Arrays.stream(resourceListing.trim().split("\n")).filter(
500            r -> !CmsStringUtil.isEmptyOrWhitespaceOnly(r)).map(r -> r.trim()).collect(Collectors.toList());
501
502        if ((exResources.size() == 0) && (lines.size() > 0)) {
503            // We have paths to add from the resource listing, but only empty fields in the form,
504            // so remove the existing form fields first, because they aren't needed anymore and would
505            // cause validation errors when clicking OK because they're empty.
506
507            m_resourcesGroup.init();
508        }
509
510        for (String line : lines) {
511            if (!exResources.contains(line)) {
512                // folders may have been entered without trailing slashes,
513                // but to correct that, we have to read the resources
514                try {
515                    CmsResource res = m_cms.readResource(line, CmsResourceFilter.IGNORE_EXPIRATION);
516                    line = m_cms.getSitePath(res);
517                } catch (CmsException e) {
518                    LOG.debug(e.getLocalizedMessage(), e);
519                }
520                m_resourcesGroup.addRow(getResourceRow(line));
521            }
522        }
523
524    }
525
526}