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.projects;
029
030import org.opencms.file.CmsObject;
031import org.opencms.file.CmsProject;
032import org.opencms.file.CmsResource;
033import org.opencms.main.CmsException;
034import org.opencms.main.CmsLog;
035import org.opencms.main.OpenCms;
036import org.opencms.security.CmsOrganizationalUnit;
037import org.opencms.security.CmsRole;
038import org.opencms.ui.A_CmsUI;
039import org.opencms.ui.CmsCssIcon;
040import org.opencms.ui.CmsVaadinUtils;
041import org.opencms.ui.apps.Messages;
042import org.opencms.ui.components.CmsBasicDialog;
043import org.opencms.ui.components.CmsErrorDialog;
044import org.opencms.ui.components.CmsRemovableFormRow;
045import org.opencms.ui.components.CmsResourceInfo;
046import org.opencms.ui.components.OpenCmsTheme;
047import org.opencms.ui.components.fileselect.CmsPathSelectField;
048import org.opencms.ui.dialogs.permissions.CmsPrincipalSelect;
049import org.opencms.ui.dialogs.permissions.CmsPrincipalSelect.WidgetType;
050import org.opencms.util.CmsStringUtil;
051import org.opencms.util.CmsUUID;
052
053import java.util.Collections;
054import java.util.HashSet;
055import java.util.List;
056import java.util.Set;
057
058import org.apache.commons.logging.Log;
059
060import com.vaadin.v7.data.Property.ValueChangeEvent;
061import com.vaadin.v7.data.Property.ValueChangeListener;
062import com.vaadin.v7.data.Validator;
063import com.vaadin.ui.Button;
064import com.vaadin.ui.Button.ClickEvent;
065import com.vaadin.ui.Button.ClickListener;
066import com.vaadin.v7.ui.CheckBox;
067import com.vaadin.ui.Component;
068import com.vaadin.v7.ui.Field;
069import com.vaadin.ui.FormLayout;
070import com.vaadin.v7.ui.TextField;
071import com.vaadin.ui.UI;
072import com.vaadin.ui.Window;
073
074/**
075 * The edit project form component.<p>
076 */
077public class CmsEditProjectForm extends CmsBasicDialog {
078
079    /**
080     * The OU validator.<p>
081     */
082    protected class OUValidator implements Validator {
083
084        /** The serial version id. */
085        private static final long serialVersionUID = 1L;
086
087        /**
088         * @see com.vaadin.data.Validator#validate(java.lang.Object)
089         */
090        public void validate(Object value) throws InvalidValueException {
091
092            if (m_fieldOU.isEnabled() && CmsStringUtil.isNotEmptyOrWhitespaceOnly((String)value)) {
093                try {
094                    OpenCms.getOrgUnitManager().readOrganizationalUnit(A_CmsUI.getCmsObject(), (String)value);
095                } catch (CmsException e) {
096                    throw new InvalidValueException(e.getLocalizedMessage(UI.getCurrent().getLocale()));
097                }
098            }
099        }
100    }
101
102    /**
103     * The resource field validator. Checks whether the resource is part of the project OU.<p>
104     */
105    protected class ResourceValidator implements Validator {
106
107        /** The serial version id. */
108        private static final long serialVersionUID = 1L;
109
110        /**
111         * @see com.vaadin.data.Validator#validate(java.lang.Object)
112         */
113        public void validate(Object value) throws InvalidValueException {
114
115            if (CmsStringUtil.isNotEmptyOrWhitespaceOnly((String)value)
116                && CmsStringUtil.isNotEmptyOrWhitespaceOnly(m_fieldOU.getValue())) {
117                try {
118                    List<CmsResource> ouRes = OpenCms.getOrgUnitManager().getResourcesForOrganizationalUnit(
119                        A_CmsUI.getCmsObject(),
120                        m_fieldOU.getValue());
121
122                    String resPath = (String)value;
123                    for (CmsResource res : ouRes) {
124                        if (resPath.startsWith(res.getRootPath())) {
125                            return;
126                        }
127                    }
128                    throw new InvalidValueException(
129                        "The resource path "
130                            + value
131                            + " is not part of the project OU '"
132                            + m_fieldOU.getValue()
133                            + "'.");
134                } catch (CmsException e) {
135                    // ignore
136                }
137            }
138        }
139    }
140
141    /** The logger for this class. */
142    private static Log LOG = CmsLog.getLog(CmsEditProjectForm.class.getName());
143
144    /** The serial version id. */
145    private static final long serialVersionUID = 2345799706922671537L;
146
147    /** The OU field. */
148    TextField m_fieldOU;
149
150    /** The add resources button. */
151    private Button m_addResource;
152
153    /** The cancel button. */
154    private Button m_cancel;
155
156    /** The delete after publish check box. */
157    private CheckBox m_fieldDeleteAfterPublish;
158
159    /** The project description field. */
160    private TextField m_fieldDescription;
161
162    /** The manager group field. */
163    private CmsPrincipalSelect m_fieldManager;
164
165    /** The project name field. */
166    private TextField m_fieldName;
167
168    /** The form fileds. */
169    private Field<?>[] m_fields;
170
171    /** The user group field. */
172    private CmsPrincipalSelect m_fieldUser;
173
174    /** The projects table instance. */
175    private CmsProjectsTable m_table;
176
177    /** The OK button. */
178    private Button m_ok;
179
180    /** The edited project. */
181    private CmsProject m_project;
182
183    /** The resources form layout. */
184    private FormLayout m_resources;
185
186    /** The resource field validator. */
187    private ResourceValidator m_resourceValidator;
188
189    /** The window this form is displayed in. */
190    private Window m_window;
191
192    /**
193     * Constructor.<p>
194     * Used to edit existing projects.<p>
195     *
196     * @param table the projects table
197     * @param projectId the project to edit
198     * @param window the window this form is displayed in
199     */
200    public CmsEditProjectForm(CmsProjectsTable table, CmsUUID projectId, Window window) {
201        this(table, window);
202        CmsObject cms = A_CmsUI.getCmsObject();
203        try {
204            m_project = cms.readProject(projectId);
205            displayResourceInfoDirectly(
206                Collections.singletonList(
207                    new CmsResourceInfo(
208                        m_project.getName(),
209                        m_project.getDescription(),
210                        new CmsCssIcon(OpenCmsTheme.ICON_PROJECT))));
211            m_fieldName.setValue(m_project.getName());
212            m_fieldName.setEnabled(false);
213            m_fieldDescription.setValue(m_project.getDescription());
214            m_fieldUser.setValue(cms.readGroup(m_project.getGroupId()).getName());
215            m_fieldManager.setValue(cms.readGroup(m_project.getManagerGroupId()).getName());
216            try {
217                CmsOrganizationalUnit ou;
218                ou = OpenCms.getOrgUnitManager().readOrganizationalUnit(cms, m_project.getOuFqn());
219                m_fieldOU.setValue(ou.getDisplayName(UI.getCurrent().getLocale()));
220            } catch (CmsException e) {
221                LOG.error(e.getLocalizedMessage(), e);
222                m_fieldOU.setValue(null);
223            }
224            m_fieldOU.setEnabled(false);
225            for (String resName : cms.readProjectResources(m_project)) {
226                addResourceField(resName);
227            }
228        } catch (CmsException e) {
229            CmsErrorDialog.showErrorDialog(e);
230        }
231
232    }
233
234    /**
235     * Constructor.<p>
236     * Use this to create a new project.<p>
237     *
238     * @param table the projects table
239     * @param window the window this form is displayed in
240     */
241    public CmsEditProjectForm(CmsProjectsTable table, Window window) {
242        m_window = window;
243        m_table = table;
244        CmsVaadinUtils.readAndLocalizeDesign(this, CmsVaadinUtils.getWpMessagesForCurrentLocale(), null);
245        m_resourceValidator = new ResourceValidator();
246        m_fieldManager.setWidgetType(WidgetType.groupwidget);
247        m_fieldManager.setRealPrincipalsOnly(true);
248        m_fieldUser.setWidgetType(WidgetType.groupwidget);
249        m_fieldUser.setRealPrincipalsOnly(true);
250
251        try {
252            CmsOrganizationalUnit ou = OpenCms.getRoleManager().getOrgUnitsForRole(
253                A_CmsUI.getCmsObject(),
254                CmsRole.PROJECT_MANAGER,
255                true).get(0);
256            m_fieldOU.setValue(ou.getName());
257        } catch (CmsException e) {
258            LOG.error(e.getLocalizedMessage(), e);
259            m_fieldOU.setValue(null);
260        }
261        m_fieldOU.setImmediate(true);
262        m_fieldOU.addValidator(new OUValidator());
263        m_fieldOU.addValueChangeListener(new ValueChangeListener() {
264
265            private static final long serialVersionUID = 1L;
266
267            public void valueChange(ValueChangeEvent event) {
268
269                validateResourceFields();
270            }
271        });
272
273        m_addResource.addClickListener(new ClickListener() {
274
275            private static final long serialVersionUID = 1L;
276
277            public void buttonClick(ClickEvent event) {
278
279                addResourceField(null);
280            }
281        });
282        m_ok.addClickListener(new ClickListener() {
283
284            private static final long serialVersionUID = 1L;
285
286            public void buttonClick(ClickEvent event) {
287
288                submit();
289            }
290        });
291        m_cancel.addClickListener(new ClickListener() {
292
293            private static final long serialVersionUID = 1L;
294
295            public void buttonClick(ClickEvent event) {
296
297                cancel();
298            }
299        });
300        m_fields = new Field<?>[] {m_fieldName, m_fieldDescription, m_fieldManager, m_fieldUser, m_fieldOU};
301    }
302
303    /**
304     * Adds a new resource field.<p>
305     *
306     * @param value the value to set
307     */
308    void addResourceField(String value) {
309
310        CmsPathSelectField field = new CmsPathSelectField();
311        field.setUseRootPaths(true);
312        if (value != null) {
313            field.setValue(value);
314        }
315        field.addValidator(m_resourceValidator);
316        CmsRemovableFormRow<CmsPathSelectField> row = new CmsRemovableFormRow<CmsPathSelectField>(
317            field,
318            CmsVaadinUtils.getMessageText(Messages.GUI_PROJECTS_REMOVE_RESOURCE_0));
319        row.setCaption(CmsVaadinUtils.getMessageText(Messages.GUI_PROJECTS_RESOURCE_0));
320        row.setDescription(CmsVaadinUtils.getMessageText(Messages.GUI_PROJECTS_RESOURCE_HELP_0));
321        m_resources.addComponent(row);
322    }
323
324    /**
325     * Cancels project edit.<p>
326     */
327    void cancel() {
328
329        m_window.close();
330    }
331
332    /**
333     * Submits the form.<p>
334     */
335    void submit() {
336
337        if (isValid()) {
338            if (m_project == null) {
339                createProject();
340            } else {
341                saveProject();
342            }
343            m_table.loadProjects();
344            m_window.close();
345        }
346    }
347
348    /**
349     * Validates the resource fields.<p>
350     */
351    @SuppressWarnings("unchecked")
352    void validateResourceFields() {
353
354        for (Component c : m_resources) {
355            if (c instanceof CmsRemovableFormRow<?>) {
356                ((CmsRemovableFormRow<CmsPathSelectField>)c).getInput().validate();
357            }
358        }
359    }
360
361    /**
362     * Creates a new project.<p>
363     */
364    private void createProject() {
365
366        CmsObject cms = A_CmsUI.getCmsObject();
367        try {
368            String name = "/";
369            if (CmsStringUtil.isNotEmptyOrWhitespaceOnly(m_fieldOU.getValue())) {
370                name = CmsStringUtil.joinPaths(name, m_fieldOU.getValue());
371            }
372            if (CmsStringUtil.isNotEmptyOrWhitespaceOnly(m_fieldName.getValue())) {
373                name = CmsStringUtil.joinPaths(name, m_fieldName.getValue());
374            } else {
375                name = CmsStringUtil.joinPaths(name, "/");
376            }
377
378            m_project = cms.createProject(
379                name,
380                m_fieldDescription.getValue(),
381                m_fieldUser.getValue(),
382                m_fieldManager.getValue(),
383                m_fieldDeleteAfterPublish.getValue().booleanValue()
384                ? CmsProject.PROJECT_TYPE_TEMPORARY
385                : CmsProject.PROJECT_TYPE_NORMAL);
386            updateProjectResources();
387
388        } catch (Throwable t) {
389            CmsErrorDialog.showErrorDialog(t);
390        }
391    }
392
393    /**
394     * Returns the selected resource paths.<p>
395     *
396     * @return the resource paths
397     */
398    @SuppressWarnings("unchecked")
399    private Set<String> getResourcePaths() {
400
401        Set<String> resources = new HashSet<String>();
402        for (Component c : m_resources) {
403            if (c instanceof CmsRemovableFormRow<?>) {
404                String value = ((CmsRemovableFormRow<CmsPathSelectField>)c).getInput().getValue();
405                if (CmsStringUtil.isNotEmptyOrWhitespaceOnly(value)) {
406                    resources.add(value);
407                }
408            }
409        }
410        return resources;
411    }
412
413    /**
414     * Validates the form fields.<p>
415     *
416     * @return <code>true</code> in case all fields are valid
417     */
418    @SuppressWarnings("unchecked")
419    private boolean isValid() {
420
421        for (Field<?> field : m_fields) {
422            if (!field.isValid()) {
423                field.focus();
424
425                return false;
426            }
427        }
428        for (Component c : m_resources) {
429            if (c instanceof CmsRemovableFormRow<?>) {
430                if (!((CmsRemovableFormRow<CmsPathSelectField>)c).getInput().isValid()) {
431                    ((CmsRemovableFormRow<CmsPathSelectField>)c).getInput().focus();
432                    return false;
433                }
434            }
435        }
436        return true;
437    }
438
439    /**
440     * Saves an existing project.<p>
441     */
442    private void saveProject() {
443
444        CmsObject cms = A_CmsUI.getCmsObject();
445        try {
446            m_project.setDescription(m_fieldDescription.getValue());
447            m_project.setGroupId(cms.readGroup(m_fieldUser.getValue()).getId());
448            m_project.setManagerGroupId(cms.readGroup(m_fieldManager.getValue()).getId());
449            m_project.setDeleteAfterPublishing(m_fieldDeleteAfterPublish.getValue().booleanValue());
450            cms.writeProject(m_project);
451            updateProjectResources();
452        } catch (Throwable t) {
453            CmsErrorDialog.showErrorDialog(t);
454        }
455    }
456
457    /**
458     * Updates the project resources.<p>
459     *
460     * @throws CmsException in case writing the project fails
461     */
462    private void updateProjectResources() throws CmsException {
463
464        CmsObject cms = A_CmsUI.getCmsObject();
465        Set<String> resourceRootPaths = getResourcePaths();
466        // write the edited project resources
467        CmsProject currentProject = cms.getRequestContext().getCurrentProject();
468        // change the current project
469        cms.getRequestContext().setCurrentProject(m_project);
470        // store the current site root
471        String currentSite = cms.getRequestContext().getSiteRoot();
472        // copy the resources to the current project
473        try {
474            // switch to the root site
475            cms.getRequestContext().setSiteRoot("");
476            // remove deleted resources
477            for (String resName : cms.readProjectResources(m_project)) {
478                if (!resourceRootPaths.contains(resName)) {
479                    cms.removeResourceFromProject(resName);
480                }
481            }
482            // read project resources again!
483            List<String> currentResNames = cms.readProjectResources(m_project);
484            // copy missing resources
485            for (String resName : resourceRootPaths) {
486                if (!currentResNames.contains(resName)) {
487                    cms.copyResourceToProject(resName);
488                }
489            }
490        } finally {
491            // switch back to current site and project
492            cms.getRequestContext().setSiteRoot(currentSite);
493            cms.getRequestContext().setCurrentProject(currentProject);
494        }
495    }
496}