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.user;
029
030import org.opencms.db.CmsUserSettings;
031import org.opencms.file.CmsGroup;
032import org.opencms.file.CmsObject;
033import org.opencms.file.CmsProject;
034import org.opencms.file.CmsUser;
035import org.opencms.main.CmsException;
036import org.opencms.main.CmsIllegalArgumentException;
037import org.opencms.main.CmsIllegalStateException;
038import org.opencms.main.CmsLog;
039import org.opencms.main.OpenCms;
040import org.opencms.security.CmsDefaultValidationHandler;
041import org.opencms.security.CmsOrganizationalUnit;
042import org.opencms.security.CmsPasswordInfo;
043import org.opencms.security.CmsRole;
044import org.opencms.security.CmsSecurityException;
045import org.opencms.security.I_CmsPasswordHandler;
046import org.opencms.security.I_CmsPasswordSecurityEvaluator;
047import org.opencms.security.I_CmsPasswordSecurityEvaluator.SecurityLevel;
048import org.opencms.site.CmsSite;
049import org.opencms.ui.A_CmsUI;
050import org.opencms.ui.CmsVaadinUtils;
051import org.opencms.ui.apps.CmsPageEditorConfiguration;
052import org.opencms.ui.apps.CmsSitemapEditorConfiguration;
053import org.opencms.ui.apps.I_CmsWorkplaceAppConfiguration;
054import org.opencms.ui.apps.Messages;
055import org.opencms.ui.components.CmsBasicDialog;
056import org.opencms.ui.components.CmsUserDataFormLayout;
057import org.opencms.ui.components.CmsUserDataFormLayout.EditLevel;
058import org.opencms.ui.components.OpenCmsTheme;
059import org.opencms.ui.components.fileselect.CmsPathSelectField;
060import org.opencms.ui.dialogs.permissions.CmsPrincipalSelect;
061import org.opencms.ui.dialogs.permissions.CmsPrincipalSelect.WidgetType;
062import org.opencms.ui.login.CmsPasswordForm;
063import org.opencms.util.CmsStringUtil;
064import org.opencms.util.CmsUUID;
065import org.opencms.workplace.CmsWorkplaceLoginHandler;
066
067import java.util.ArrayList;
068import java.util.Collections;
069import java.util.Iterator;
070import java.util.List;
071import java.util.Locale;
072
073import org.apache.commons.logging.Log;
074import org.apache.commons.mail.EmailException;
075
076import com.vaadin.data.provider.DataProvider;
077import com.vaadin.data.provider.ListDataProvider;
078import com.vaadin.server.UserError;
079import com.vaadin.ui.Button;
080import com.vaadin.ui.Button.ClickEvent;
081import com.vaadin.ui.Button.ClickListener;
082import com.vaadin.ui.Component;
083import com.vaadin.ui.TabSheet;
084import com.vaadin.ui.TabSheet.SelectedTabChangeEvent;
085import com.vaadin.ui.TabSheet.SelectedTabChangeListener;
086import com.vaadin.ui.Window;
087import com.vaadin.v7.data.Item;
088import com.vaadin.v7.data.Property.ValueChangeEvent;
089import com.vaadin.v7.data.Property.ValueChangeListener;
090import com.vaadin.v7.data.Validator;
091import com.vaadin.v7.data.util.IndexedContainer;
092import com.vaadin.v7.event.FieldEvents.TextChangeEvent;
093import com.vaadin.v7.event.FieldEvents.TextChangeListener;
094import com.vaadin.v7.ui.CheckBox;
095import com.vaadin.v7.ui.ComboBox;
096import com.vaadin.v7.ui.Label;
097import com.vaadin.v7.ui.TextArea;
098import com.vaadin.v7.ui.TextField;
099
100/**
101 * Class for the dialog to edit user settings.<p>
102 */
103public class CmsUserEditDialog extends CmsBasicDialog implements I_CmsPasswordFetcher {
104
105    /**
106     * Validator for the eamil field.<p>
107     */
108    class EmailValidator implements Validator {
109
110        /**vaadin serial id.*/
111        private static final long serialVersionUID = 8943898736907290076L;
112
113        /**
114         * @see com.vaadin.data.Validator#validate(java.lang.Object)
115         */
116        public void validate(Object value) throws InvalidValueException {
117
118            if (value == null) {
119                throw new InvalidValueException(
120                    CmsVaadinUtils.getMessageText(Messages.GUI_USERMANAGEMENT_USER_VALIDATION_EMAIL_EMPTY_0));
121            }
122            try {
123                CmsUser.checkEmail((String)value);
124            } catch (CmsIllegalArgumentException e) {
125                throw new InvalidValueException(
126                    CmsVaadinUtils.getMessageText(Messages.GUI_USERMANAGEMENT_USER_VALIDATION_EMAIL_INVALID_0));
127            }
128        }
129    }
130
131    /**
132     * Validator for the login name field.<p>
133     */
134    class LoginNameValidator implements Validator {
135
136        /**vaadin serial id.*/
137        private static final long serialVersionUID = -6768717591898665618L;
138
139        /**
140         * @see com.vaadin.data.Validator#validate(java.lang.Object)
141         */
142        public void validate(Object value) throws InvalidValueException {
143
144            if (value == null) {
145                throw new InvalidValueException(
146                    CmsVaadinUtils.getMessageText(Messages.GUI_USERMANAGEMENT_USER_VALIDATION_LOGINNAME_EMPTY_0));
147            }
148
149            try {
150                CmsDefaultValidationHandler handler = new CmsDefaultValidationHandler();
151                handler.checkUserName((String)value);
152            } catch (CmsIllegalArgumentException e) {
153                throw new InvalidValueException(
154                    CmsVaadinUtils.getMessageText(Messages.GUI_USERMANAGEMENT_USER_VALIDATION_LOGINNAME_INVALID_0));
155            }
156
157            if (userAlreadyExists((String)value)) {
158                throw new InvalidValueException(
159                    CmsVaadinUtils.getMessageText(Messages.GUI_USERMANAGEMENT_USER_VALIDATION_LOGINNAME_DOUBLE_0));
160            }
161        }
162    }
163
164    /**
165     * Validator for password fields.<p>
166     */
167    class PasswordValidator implements Validator {
168
169        /**vaadin serial id.*/
170        private static final long serialVersionUID = 64216980175982548L;
171
172        /**
173         * @see com.vaadin.data.Validator#validate(java.lang.Object)
174         */
175        public void validate(Object value) throws InvalidValueException {
176
177            if (isPasswordMismatchingConfirm()) {
178                throw new InvalidValueException(
179                    CmsVaadinUtils.getMessageText(
180                        Messages.GUI_USERMANAGEMENT_USER_VALIDATION_PASSWORD_NOT_EQUAL_CONFIRM_0));
181            }
182
183            if (!isNewUser()) {
184                if ((value == null) | CmsStringUtil.isEmptyOrWhitespaceOnly((String)value)) {
185                    return; //ok, password was not changed for existing user
186                }
187            }
188
189            if (!isPasswordValid()) {
190                throw new InvalidValueException(
191                    CmsVaadinUtils.getMessageText(Messages.GUI_USERMANAGEMENT_USER_VALIDATION_PASSWORD_INVALID_0));
192            }
193        }
194    }
195
196    /**
197     * Validator for start view and start site field.<p>
198     */
199    class StartPathValidator implements Validator {
200
201        /**vaadin serial id.*/
202        private static final long serialVersionUID = -4257155941690487831L;
203
204        /**
205         * @see com.vaadin.data.Validator#validate(java.lang.Object)
206         */
207        public void validate(Object value) throws InvalidValueException {
208
209            if (!m_visSites) {
210                return;
211            }
212            if (!isSiteNull()) {
213                if (!isSitePathValid()) {
214                    throw new InvalidValueException(
215                        CmsVaadinUtils.getMessageText(
216                            Messages.GUI_USERMANAGEMENT_USER_VALIDATION_START_PATH_NOT_VALID_0));
217
218                }
219            }
220
221        }
222    }
223
224    /**
225     * Validator for start view and start site field.<p>
226     */
227    class StartSiteValidator implements Validator {
228
229        /**vaadin serial id.*/
230        private static final long serialVersionUID = -4257155941690487831L;
231
232        /**
233         * @see com.vaadin.data.Validator#validate(java.lang.Object)
234         */
235        public void validate(Object value) throws InvalidValueException {
236
237            if (!m_visSites) {
238                return;
239            }
240            if (isSiteNull()) {
241                throw new InvalidValueException(
242                    CmsVaadinUtils.getMessageText(Messages.GUI_USERMANAGEMENT_USER_VALIDATION_STARTSITE_EMPTY_0));
243
244            }
245
246        }
247    }
248
249    /**
250     * Validator for start view and start site field.<p>
251     */
252    class StartViewValidator implements Validator {
253
254        /**vaadin serial id.*/
255        private static final long serialVersionUID = -4257155941690487831L;
256
257        /**
258         * @see com.vaadin.data.Validator#validate(java.lang.Object)
259         */
260        public void validate(Object value) throws InvalidValueException {
261
262            if (!isSiteNull()) {
263
264                if (isRootSiteSelected() & !isStartViewAvailableOnRoot()) {
265                    throw new InvalidValueException(
266                        CmsVaadinUtils.getMessageText(
267                            Messages.GUI_USERMANAGEMENT_USER_VALIDATION_STARTVIEW_NOTFORROOT_0));
268                }
269
270            }
271        }
272    }
273
274    /** The log object for this class. */
275    private static final Log LOG = CmsLog.getLog(CmsUserEditDialog.class);
276
277    /**vaadin serial id.*/
278    private static final long serialVersionUID = -5198443053070008413L;
279
280    /**Flag indicates is user is in webou. */
281    boolean m_isWebOU;
282
283    /**Password form. */
284    CmsPasswordForm m_pw;
285
286    /**vaadin component.*/
287    ComboBox m_site;
288
289    /**vaadin component.*/
290    CmsPathSelectField m_startfolder;
291
292    /**Holder for authentification fields. */
293    //    private VerticalLayout m_authHolder;
294
295    /**vaadin component.*/
296    private Button m_cancel;
297
298    /**CmsObject. */
299    private CmsObject m_cms;
300
301    /**vaadin component.*/
302    private TextArea m_description;
303
304    /**vaadin component.*/
305    private CheckBox m_enabled;
306
307    /**vaadin component. */
308    private CheckBox m_forceResetPassword;
309
310    /**vaadin component. */
311    private CheckBox m_sendEmail;
312
313    /**Vaadin component. */
314    private Button m_generateButton;
315
316    /**Visible sites? */
317    protected boolean m_visSites = true;
318
319    /**Select view for principals.*/
320    private CmsPrincipalSelect m_group;
321
322    /**vaadin component.*/
323    private ComboBox m_language;
324
325    /**vaadin component.*/
326    private TextField m_loginname;
327
328    /**Flag indicates if name was empty. */
329    private boolean m_name_was_empty;
330
331    /**vaadin component. */
332    private Button m_next;
333
334    /**vaadin component.*/
335    private Button m_ok;
336
337    /**vaadin component.*/
338    private Label m_ou;
339
340    /**vaadin component.*/
341    private ComboBox m_project;
342
343    /**vaadin component. */
344    private ComboBox m_role;
345
346    /**vaadin component.*/
347    private CheckBox m_selfmanagement;
348
349    /** Label containing invisible dummy password fields to dissuade Firefox from saving the password *after* the user edit dialog. */
350    private Label m_dummyPasswordLabel;
351
352    /**vaadin component.*/
353    private ComboBox m_startview;
354
355    /**vaadin component.*/
356    private TabSheet m_tab;
357
358    /**vaadin component.*/
359    private CmsUser m_user;
360
361    /**User data form.<p>*/
362    private CmsUserDataFormLayout m_userdata;
363
364    private CmsUserEditParameters m_editParams = new CmsUserEditParameters();
365
366    /**
367     * public constructor.<p>
368     * @param callingOu
369     *
370     * @param cms CmsObject
371     * @param userId id of user
372     * @param window to be closed
373     * @param app
374     */
375    public CmsUserEditDialog(CmsObject cms, CmsUUID userId, final Window window, final CmsAccountsApp app) {
376
377        CmsVaadinUtils.readAndLocalizeDesign(this, CmsVaadinUtils.getWpMessagesForCurrentLocale(), null);
378        setPasswordFields();
379        try {
380            m_cms = OpenCms.initCmsObject(cms);
381            m_startfolder.disableSiteSwitch();
382            m_user = m_cms.readUser(userId);
383            m_editParams = app.getUserEditParameters(m_user);
384            if (m_user.isWebuser()) {
385                m_sendEmail.setVisible(false);
386                m_sendEmail.setValue(Boolean.FALSE);
387                m_forceResetPassword.setVisible(false);
388                m_forceResetPassword.setValue(Boolean.FALSE);
389                m_selfmanagement.setVisible(false);
390                m_selfmanagement.setValue(Boolean.FALSE);
391                m_isWebOU = true;
392            } else {
393                m_selfmanagement.setValue(new Boolean(true));
394            }
395
396            displayResourceInfoDirectly(Collections.singletonList(CmsAccountsApp.getPrincipalInfo(m_user)));
397            m_group.setVisible(false);
398            m_role.setVisible(false);
399            m_loginname.setValue(m_user.getSimpleName());
400            m_loginname.setEnabled(false);
401            m_ou.setValue(m_user.getOuFqn().isEmpty() ? "/" : m_user.getOuFqn());
402
403            m_description.setValue(m_user.getDescription());
404            m_selfmanagement.setValue(new Boolean(!m_user.isManaged()));
405            m_enabled.setValue(new Boolean(m_user.isEnabled()));
406            CmsUserSettings settings = new CmsUserSettings(m_user);
407            init(window, app, settings, m_editParams.isEditEnabled());
408            m_sendEmail.setEnabled(false);
409            m_forceResetPassword.setValue(
410                CmsUserTable.USER_PASSWORD_STATUS.get(m_user.getId()) == null
411                ? Boolean.FALSE
412                : CmsUserTable.USER_PASSWORD_STATUS.get(m_user.getId()));
413            m_next.setVisible(false);
414            setupStartFolder(settings.getStartFolder());
415
416            m_loginname.setEnabled(false);
417
418            if (!m_editParams.isEditEnabled()) {
419                m_description.setEnabled(false);
420            }
421            if (!m_editParams.isPasswordChangeEnabled()) {
422                m_pw.setVisible(false);
423                m_forceResetPassword.setVisible(false);
424                m_sendEmail.setVisible(false);
425                m_generateButton.setVisible(false);
426            }
427
428        } catch (CmsException e) {
429            LOG.error("Can't read user", e);
430        }
431    }
432
433    /**
434     * public constructor for new user case.<p>
435     *
436     * @param cms CmsObject
437     * @param window Window
438     * @param ou organizational unit
439     */
440    public CmsUserEditDialog(CmsObject cms, final Window window, String ou, final CmsAccountsApp app) {
441
442        CmsVaadinUtils.readAndLocalizeDesign(this, CmsVaadinUtils.getWpMessagesForCurrentLocale(), null);
443        CmsOrganizationalUnit myOu = null;
444        try {
445            m_cms = OpenCms.initCmsObject(cms);
446            myOu = OpenCms.getOrgUnitManager().readOrganizationalUnit(m_cms, ou);
447
448            m_isWebOU = false;
449            m_sendEmail.setValue(Boolean.TRUE);
450            m_forceResetPassword.setValue(Boolean.TRUE);
451            if (myOu.hasFlagWebuser()) {
452                m_role.setVisible(false);
453                m_sendEmail.setVisible(false);
454                m_sendEmail.setValue(Boolean.FALSE);
455                m_forceResetPassword.setVisible(false);
456                m_forceResetPassword.setValue(Boolean.FALSE);
457                m_selfmanagement.setVisible(false);
458                m_selfmanagement.setValue(Boolean.FALSE);
459                m_isWebOU = true;
460            } else {
461                iniRole(m_cms, ou, m_role, LOG);
462                m_role.select(CmsRole.EDITOR.forOrgUnit(ou));
463                m_selfmanagement.setValue(new Boolean(true));
464
465            }
466        } catch (CmsException e) {
467            LOG.error("Unable to read OU", e);
468        }
469        setPasswordFields();
470        m_ou.setValue(ou.isEmpty() ? "/" : ou);
471        m_group.setWidgetType(WidgetType.groupwidget);
472        m_group.setValue(ou + OpenCms.getDefaultUsers().getGroupUsers());
473        m_group.setRealPrincipalsOnly(true);
474        m_group.setOU(m_ou.getValue());
475        try {
476            m_group.setIncludeWebOus(OpenCms.getOrgUnitManager().readOrganizationalUnit(m_cms, ou).hasFlagWebuser());
477        } catch (CmsException e) {
478            //
479        }
480
481        m_enabled.setValue(Boolean.TRUE);
482
483        init(window, app, null, true);
484        setupStartFolder(null);
485
486        m_tab.addSelectedTabChangeListener(new SelectedTabChangeListener() {
487
488            private static final long serialVersionUID = -2579639520410382246L;
489
490            public void selectedTabChange(SelectedTabChangeEvent event) {
491
492                setButtonVisibility();
493
494            }
495        });
496        setButtonVisibility();
497    }
498
499    /**
500     * Initialized the role ComboBox.<p>
501     *
502     * @param cms CmsObject
503     * @param ou to load roles for
504     * @param roleComboBox ComboBox
505     * @param log LOG
506     */
507    protected static void iniRole(CmsObject cms, String ou, com.vaadin.ui.ComboBox<CmsRole> roleComboBox, Log log) {
508
509        try {
510            List<CmsRole> roles = OpenCms.getRoleManager().getRoles(cms, ou, false);
511            CmsRole.applySystemRoleOrder(roles);
512
513            DataProvider provider = new ListDataProvider<CmsRole>(roles);
514
515            roleComboBox.setDataProvider(provider);
516            roleComboBox.setItemCaptionGenerator(role -> {
517                try {
518                    return role.getDisplayName(cms, A_CmsUI.get().getLocale());
519                } catch (CmsException e) {
520                    return "";
521                }
522            });
523            roleComboBox.setEmptySelectionAllowed(false);
524
525        } catch (CmsException e) {
526            if (log != null) {
527                log.error("Unable to read roles.", e);
528            }
529        }
530    }
531
532    /**
533     * Initialized the role ComboBox.<p>
534     *
535     * @param cms CmsObject
536     * @param ou to load roles for
537     * @param roleComboBox ComboBox
538     * @param log LOG
539     */
540    protected static void iniRole(CmsObject cms, String ou, ComboBox roleComboBox, Log log) {
541
542        try {
543            List<CmsRole> roles = OpenCms.getRoleManager().getRoles(cms, ou, false);
544            CmsRole.applySystemRoleOrder(roles);
545            IndexedContainer container = new IndexedContainer();
546            container.addContainerProperty("caption", String.class, "");
547            for (CmsRole role : roles) {
548                Item item = container.addItem(role);
549                item.getItemProperty("caption").setValue(role.getDisplayName(cms, A_CmsUI.get().getLocale()));
550            }
551
552            roleComboBox.setContainerDataSource(container);
553            roleComboBox.setItemCaptionPropertyId("caption");
554            roleComboBox.setNullSelectionAllowed(false);
555            roleComboBox.setNewItemsAllowed(false);
556        } catch (CmsException e) {
557            if (log != null) {
558                log.error("Unable to read roles.", e);
559            }
560        }
561    }
562
563    /**
564     * Sends an email to the user.<p>
565     *
566     * @param newUser is the user new?
567     */
568    protected static void sendMail(
569        CmsObject cms,
570        String password,
571        CmsUser user,
572        boolean newUser,
573        boolean changePassword) {
574
575        sendMail(cms, password, user, user.getOuFqn(), newUser, changePassword);
576    }
577
578    protected static void sendMail(
579        CmsObject cms,
580        String password,
581        CmsUser user,
582        String ou,
583        boolean newUser,
584        boolean changePassword) {
585
586        if (CmsStringUtil.isEmptyOrWhitespaceOnly(user.getEmail())) {
587            return;
588        }
589        CmsSendPasswordNotification notification = new CmsSendPasswordNotification(
590            cms,
591            password,
592            user,
593            ou,
594            cms.getRequestContext().getCurrentUser(),
595            OpenCms.getLinkManager().getWorkplaceLink(cms, CmsWorkplaceLoginHandler.LOGIN_HANDLER, false),
596            newUser,
597            changePassword);
598        try {
599            notification.send();
600        } catch (EmailException e) {
601            LOG.error("Unable to send email with password", e);
602        }
603
604    }
605
606    /**
607     * @see org.opencms.ui.apps.user.I_CmsPasswordFetcher#fetchPassword(java.lang.String)
608     */
609    public void fetchPassword(String password) {
610
611        m_pw.getPassword1Field().setValue(password);
612        m_pw.getPassword2Field().setValue(password);
613        m_forceResetPassword.setValue(Boolean.TRUE);
614        m_sendEmail.setValue(Boolean.TRUE);
615        m_sendEmail.setEnabled(true);
616
617    }
618
619    /**
620     * Checks if a new user should be created.<p>
621     *
622     * @return true, if create user function
623     */
624    protected boolean isNewUser() {
625
626        return m_user == null;
627    }
628
629    /**m_next
630     * Is password not matching to confirm field?<p>
631     *
632     * @return true, if password not equal to confirm
633     */
634    protected boolean isPasswordMismatchingConfirm() {
635
636        return !m_pw.getPassword1().equals(m_pw.getPassword2());
637
638    }
639
640    /**
641     * Validates the password fields.<p>
642     *
643     * @return true if password is valid (and confirm field matches password field).<p>
644     */
645    protected boolean isPasswordValid() {
646
647        if ((m_pw.getPassword1() == null) | (m_pw.getPassword2() == null)) {
648            return false;
649        }
650        try {
651            CmsPasswordInfo pwdInfo = new CmsPasswordInfo();
652            pwdInfo.setNewPwd(m_pw.getPassword1());
653            pwdInfo.setConfirmation(m_pw.getPassword2());
654
655            pwdInfo.validate();
656            return true;
657        } catch (CmsIllegalArgumentException | CmsIllegalStateException e) {
658            LOG.error("New password is not ok", e);
659            return false;
660        }
661    }
662
663    /**
664     * Checks if currently the root site is chosen as start site.<p>
665     *
666     * @return true if root site was selected
667     */
668    protected boolean isRootSiteSelected() {
669
670        return m_site.getValue().equals("");
671    }
672
673    /**
674     * Checks if the chosen site is valid.<p>
675     *
676     * @return true if site is null
677     */
678    protected boolean isSiteNull() {
679
680        return m_site.getValue() == null;
681    }
682
683    /**
684     * Checks if the given path is valid resource in site.<p>
685     *
686     * @return true if the resource is valid
687     */
688    protected boolean isSitePathValid() {
689
690        try {
691            CmsObject cmsLocal = OpenCms.initCmsObject(m_cms);
692            cmsLocal.getRequestContext().setSiteRoot((String)m_site.getValue());
693            if (m_startfolder.getValue().length() <= ((String)m_site.getValue()).length()) {
694                return false;
695            }
696            return cmsLocal.existsResource(m_startfolder.getValue().substring(((String)m_site.getValue()).length()));
697        } catch (CmsException e) {
698            LOG.error("Unabel to ini CmsObject", e);
699            return false;
700        }
701    }
702
703    /**
704     * Checks if the currently chosen start view is visible for root site.<p>
705     *
706     * @return true if app is available for root site
707     */
708    protected boolean isStartViewAvailableOnRoot() {
709
710        if (!m_startview.isEnabled()) {
711            return false;
712        }
713
714        return !m_startview.getValue().equals(CmsPageEditorConfiguration.APP_ID)
715            & !m_startview.getValue().equals(CmsSitemapEditorConfiguration.APP_ID);
716    }
717
718    /**
719     * Checks if all fields are valid. If not the tab of the first invalid field gets chosen.<p>
720     *
721     * @return true, if everything is ok
722     */
723    protected boolean isValid() {
724
725        boolean[] ret = new boolean[4];
726        ret[0] = m_loginname.isValid();
727        ret[1] = m_isWebOU ? true : m_userdata.isValid() | m_name_was_empty;
728        ret[2] = m_isWebOU ? true : m_site.isValid() & m_startview.isValid() & m_startfolder.isValid();
729        ret[3] = m_pw.getPassword1Field().isValid();
730
731        for (int i = 0; i < ret.length; i++) {
732
733            if (!ret[i]) {
734                m_tab.setSelectedTab(i);
735                break;
736            }
737        }
738        return ret[0] & ret[1] & ret[2] & ret[3];
739    }
740
741    /**
742     * Saves the canged user data.<p>
743     */
744    protected void save() {
745
746        boolean newUser = false;
747        try {
748            if (m_user == null) {
749                createNewUser();
750                newUser = true;
751            } else {
752
753                saveUser();
754            }
755            saveUserSettings();
756            if (m_sendEmail.getValue().booleanValue() & m_sendEmail.isEnabled()) {
757                sendMail(m_cms, m_pw.getPassword1(), m_user, newUser, m_forceResetPassword.getValue().booleanValue());
758            }
759        } catch (CmsException e) {
760            LOG.error("Unable to save user", e);
761        }
762    }
763
764    /**
765     * Sets the visibility of the buttons.<p>
766     */
767    protected void setButtonVisibility() {
768
769        Component tab = m_tab.getSelectedTab();
770        int pos = m_tab.getTabPosition(m_tab.getTab(tab));
771        //TODO: check if necessary (when it was finally known which tabs web ou users should see..
772        int maxPos = m_isWebOU ? 3 : 3; //has to be changed if number of tabs is changed for web OU user
773        m_next.setVisible(pos < maxPos);
774        m_ok.setVisible(pos == maxPos);
775    }
776
777    /**
778     * En/Diables the email box.<p>
779     */
780    protected void setEmailBox() {
781
782        m_sendEmail.setEnabled(
783            !CmsStringUtil.isEmptyOrWhitespaceOnly(m_pw.getPassword1())
784                | !CmsStringUtil.isEmptyOrWhitespaceOnly(m_pw.getPassword2()));
785    }
786
787    /**
788     * Sets the start folder depending on current set site field.<p>
789     *
790     * @param startFolder default value or null
791     */
792    protected void setupStartFolder(String startFolder) {
793
794        try {
795            CmsObject cmsCopy = OpenCms.initCmsObject(m_cms);
796            if (m_site.getValue() != null) {
797                cmsCopy.getRequestContext().setSiteRoot((String)m_site.getValue());
798            } else {
799                cmsCopy.getRequestContext().setSiteRoot("");
800            }
801            m_startfolder.requireFolder();
802            m_startfolder.disableSiteSwitch();
803            m_startfolder.setValue(cmsCopy.getRequestContext().addSiteRoot(startFolder == null ? "/" : startFolder));
804            m_startfolder.setCmsObject(cmsCopy);
805            m_startfolder.setUseRootPaths(true);
806            if (!m_visSites) {
807                try {
808                    m_startfolder.setValue(
809                        OpenCms.getOrgUnitManager().getResourcesForOrganizationalUnit(cmsCopy, m_ou.getValue()).get(
810                            0).getRootPath());
811                } catch (CmsException e1) {
812                    LOG.error("unable to read resources for ou", e1);
813                }
814            }
815            m_startfolder.setEnabled(m_visSites);
816        } catch (CmsException e) {
817            LOG.error("Unable to ini CmsObject", e);
818        }
819    }
820
821    /**
822     * Sets up the validators.<p>
823     */
824    protected void setupValidators() {
825
826        if (m_loginname.getValidators().size() == 0) {
827            m_loginname.addValidator(new LoginNameValidator());
828            m_pw.getPassword1Field().addValidator(new PasswordValidator());
829            m_site.addValidator(new StartSiteValidator());
830            m_startview.addValidator(new StartViewValidator());
831            m_startfolder.addValidator(new StartPathValidator());
832        }
833    }
834
835    /**Switches to the next tab.*/
836    protected void switchTab() {
837
838        Component tab = m_tab.getSelectedTab();
839        int pos = m_tab.getTabPosition(m_tab.getTab(tab));
840        if (m_isWebOU) {
841            if (pos == 0) {
842                pos = 1;
843            }
844        }
845        m_tab.setSelectedTab(pos + 1);
846    }
847
848    /**
849     * Checks if given user exists.<p>
850     *
851     * @param username to check
852     * @return boolean
853     */
854    protected boolean userAlreadyExists(String username) {
855
856        if (m_user != null) {
857            return false;
858        }
859        CmsUser user = null;
860        try {
861            user = m_cms.readUser(m_ou.getValue() + username);
862        } catch (CmsException e) {
863            return false;
864        }
865
866        return user != null;
867
868    }
869
870    /**
871     * Checks whether the passwords match.<p>
872     *
873     * @param password2 the password 2 value
874     */
875    void checkPasswordMatch(String password2) {
876
877        if (CmsStringUtil.isNotEmptyOrWhitespaceOnly(password2)) {
878            showPasswordMatchError(!password2.equals(m_pw.getPassword1()));
879        }
880    }
881
882    /**
883     * Checks the security level of the given password.<p>
884     *
885     * @param password the password
886     */
887    void checkSecurity(String password) {
888
889        I_CmsPasswordHandler handler = OpenCms.getPasswordHandler();
890        try {
891            handler.validatePassword(password);
892            if (handler instanceof I_CmsPasswordSecurityEvaluator) {
893                SecurityLevel level = ((I_CmsPasswordSecurityEvaluator)handler).evaluatePasswordSecurity(password);
894                m_pw.setErrorPassword1(null, OpenCmsTheme.SECURITY + "-" + level.name());
895            } else {
896                m_pw.setErrorPassword1(null, OpenCmsTheme.SECURITY_STRONG);
897            }
898        } catch (CmsSecurityException e) {
899            m_pw.setErrorPassword1(
900                new UserError(e.getLocalizedMessage(A_CmsUI.get().getLocale())),
901                OpenCmsTheme.SECURITY_INVALID);
902        }
903        if (CmsStringUtil.isNotEmptyOrWhitespaceOnly(m_pw.getPassword2())) {
904            showPasswordMatchError(!password.equals(m_pw.getPassword2()));
905        }
906    }
907
908    /**
909     * Returns a CmsObject with request set to given site.<p>
910     *
911     * @param siteRoot to be used
912     * @return CmsObject
913     */
914    CmsObject getCmsObjectWithSite(String siteRoot) {
915
916        if (siteRoot == null) {
917            siteRoot = "/";
918        }
919        try {
920            CmsObject res = OpenCms.initCmsObject(m_cms);
921            res.getRequestContext().setSiteRoot(siteRoot);
922            return res;
923        } catch (CmsException e) {
924            LOG.error("Unable to initialize CmsObject", e);
925            return m_cms;
926        }
927    }
928
929    /**
930     * Initializes the site combo box.<p>
931     *
932     * @param settings user settings
933     */
934    void iniSite(CmsUserSettings settings) {
935
936        List<CmsSite> sitesList = OpenCms.getSiteManager().getAvailableSites(m_cms, false, false, m_ou.getValue());
937
938        IndexedContainer container = new IndexedContainer();
939        container.addContainerProperty("caption", String.class, "");
940        CmsSite firstNoRootSite = null;
941        for (CmsSite site : sitesList) {
942            if (CmsStringUtil.isEmptyOrWhitespaceOnly(site.getSiteRoot())) {
943                if (hasRole(CmsRole.VFS_MANAGER)
944                    | ((m_user == null)
945                        & m_group.getValue().equals(OpenCms.getDefaultUsers().getGroupAdministrators()))) {
946                    Item item = container.addItem(site.getSiteRoot());
947                    item.getItemProperty("caption").setValue(site.getTitle());
948                }
949            } else {
950                if (firstNoRootSite == null) {
951                    firstNoRootSite = site;
952                }
953                Item item = container.addItem(site.getSiteRoot());
954                item.getItemProperty("caption").setValue(site.getTitle());
955            }
956        }
957        if (container.size() == 0) {
958            if (!container.containsId(A_CmsUI.getCmsObject().getRequestContext().getSiteRoot())) {
959                Item defaultItem = container.addItem(A_CmsUI.getCmsObject().getRequestContext().getSiteRoot());
960                defaultItem.getItemProperty("caption").setValue(
961                    A_CmsUI.getCmsObject().getRequestContext().getSiteRoot());
962            }
963            m_visSites = false;
964        }
965        m_site.setContainerDataSource(container);
966        m_site.setItemCaptionPropertyId("caption");
967        m_site.setNewItemsAllowed(false);
968        m_site.setNullSelectionAllowed(false);
969        if (settings != null) {
970            if (settings.getStartSite().length() >= 1) {
971                m_site.select(settings.getStartSite().substring(0, settings.getStartSite().length() - 1));
972            } else {
973                LOG.error("The start site is unvalid configured");
974            }
975        } else {
976
977            if (firstNoRootSite != null) {
978                m_site.select(firstNoRootSite.getSiteRoot());
979            } else {
980                m_site.select(container.getItemIds().get(0));
981            }
982        }
983    }
984
985    /**
986     * Initializes the start view.<p>
987     *
988     * @param settings user settings
989     */
990    void iniStartView(CmsUserSettings settings) {
991
992        IndexedContainer container = getStartViewContainer("caption");
993        if (container.size() > 0) {
994            m_startview.setEnabled(true);
995            m_startview.setContainerDataSource(container);
996            m_startview.setItemCaptionPropertyId("caption");
997            m_startview.setNullSelectionAllowed(false);
998            m_startview.setNewItemsAllowed(false);
999            if (container.getItemIds().size() > 0) {
1000                m_startview.select(container.getItemIds().get(0));
1001                if (settings != null) {
1002                    m_startview.select(settings.getStartView());
1003                }
1004            }
1005        } else {
1006            m_startview.setEnabled(false);
1007        }
1008    }
1009
1010    /**
1011     * Shows or hides the not matching passwords error.<p>
1012     *
1013     * @param show <code>true</code> to show the error
1014     */
1015    void showPasswordMatchError(boolean show) {
1016
1017        if (show) {
1018
1019            m_pw.setErrorPassword2(
1020                new UserError(CmsVaadinUtils.getMessageText(org.opencms.ui.Messages.GUI_PWCHANGE_PASSWORD_MISMATCH_0)),
1021                OpenCmsTheme.SECURITY_INVALID);
1022        } else {
1023            m_pw.setErrorPassword2(null, OpenCmsTheme.SECURITY_STRONG);
1024        }
1025    }
1026
1027    /**
1028     * Creates new user.<p>
1029     *
1030     * @throws CmsException exception
1031     */
1032    private void createNewUser() throws CmsException {
1033
1034        //Password was checked by validator before
1035        String ou = m_ou.getValue();
1036        if (!ou.endsWith("/")) {
1037            ou += "/";
1038        }
1039        CmsUser user = m_cms.createUser(ou + m_loginname.getValue(), m_pw.getPassword1(), "", null);
1040        updateUser(user);
1041        m_cms.writeUser(user);
1042        if (!CmsStringUtil.isEmptyOrWhitespaceOnly(m_group.getValue())) {
1043            m_cms.addUserToGroup(user.getName(), m_group.getValue());
1044        }
1045        OpenCms.getRoleManager().addUserToRole(m_cms, (CmsRole)m_role.getValue(), user.getName());
1046        m_user = user;
1047
1048    }
1049
1050    /**
1051     * Returns the start view container.<p>
1052     *
1053     * @param caption of the container
1054     * @return indexed container
1055     */
1056    private IndexedContainer getStartViewContainer(String caption) {
1057
1058        List<I_CmsWorkplaceAppConfiguration> apps = OpenCms.getWorkplaceAppManager().getDefaultQuickLaunchConfigurations();
1059
1060        IndexedContainer res = new IndexedContainer();
1061
1062        res.addContainerProperty(caption, String.class, "");
1063
1064        for (I_CmsWorkplaceAppConfiguration app : apps) {
1065            if (hasRoleForApp(app)) {
1066                Item item = res.addItem(app.getId());
1067                item.getItemProperty(caption).setValue(app.getName(A_CmsUI.get().getLocale()));
1068            }
1069        }
1070        return res;
1071    }
1072
1073    /**
1074     * Checks if user, which gets edited, has given role.<p>
1075     *
1076     * @param role to be checked
1077     * @return true if user has role (or a parent role)
1078     */
1079    private boolean hasRole(CmsRole role) {
1080
1081        if (m_user != null) {
1082            return OpenCms.getRoleManager().hasRole(m_cms, m_user.getName(), CmsRole.VFS_MANAGER);
1083        }
1084        return false;
1085    }
1086
1087    /**
1088     * Checks if user, which gets edited, has role for given app.<p>
1089     *
1090     * @param app to be checked
1091     * @return true if user has role
1092     */
1093    private boolean hasRoleForApp(I_CmsWorkplaceAppConfiguration app) {
1094
1095        if (m_user != null) {
1096            return OpenCms.getRoleManager().hasRole(m_cms, m_user.getName(), app.getRequiredRole());
1097        }
1098
1099        if (!CmsStringUtil.isEmptyOrWhitespaceOnly(m_group.getValue())) {
1100            try {
1101                CmsGroup group = m_cms.readGroup(m_group.getValue());
1102                CmsRole roleFromGroup = CmsRole.valueOf(group);
1103                CmsRole roleFromField = (CmsRole)m_role.getValue();
1104                if (!roleFromGroup.getChildren(true).contains(roleFromField)) {
1105                    roleFromGroup = roleFromField;
1106                }
1107                if (roleFromGroup == null) {
1108                    return false;
1109                }
1110                List<CmsRole> groupRoles = roleFromGroup.getChildren(true);
1111                groupRoles.add(roleFromGroup);
1112                List<String> roleNames = new ArrayList<String>();
1113                for (CmsRole gr : groupRoles) {
1114                    roleNames.add(gr.getRoleName());
1115                }
1116                return roleNames.contains(app.getRequiredRole().getRoleName());
1117            } catch (CmsException e) {
1118                LOG.error("Unable to read group", e);
1119            }
1120        }
1121        return true;
1122    }
1123
1124    /**
1125     * Initializes the language combo box.<p>
1126     *
1127     * @param settings user settings
1128     */
1129    private void iniLanguage(CmsUserSettings settings) {
1130
1131        m_language.setContainerDataSource(CmsVaadinUtils.getLanguageContainer("caption"));
1132        m_language.setItemCaptionPropertyId("caption");
1133        m_language.setNewItemsAllowed(false);
1134        m_language.setNullSelectionAllowed(false);
1135        if (settings != null) {
1136            m_language.select(settings.getLocale());
1137        } else {
1138            m_language.select(m_language.getItemIds().iterator().next());
1139        }
1140    }
1141
1142    /**
1143     * Initializes the project combo box.<p>
1144     *
1145     * @param settings of user
1146     */
1147    private void iniProject(CmsUserSettings settings) {
1148
1149        try {
1150            List<CmsProject> projects = OpenCms.getOrgUnitManager().getAllAccessibleProjects(
1151                m_cms,
1152                m_ou.getValue(),
1153                false);
1154            for (CmsProject project : projects) {
1155                m_project.addItem(project.getSimpleName());
1156            }
1157            m_project.setNewItemsAllowed(false);
1158            m_project.setNullSelectionAllowed(false);
1159            if (settings != null) {
1160                String projString = settings.getStartProject();
1161                if (projString.contains("/")) {
1162                    projString = projString.split("/")[projString.split("/").length - 1];
1163                }
1164                m_project.select(projString);
1165            } else {
1166
1167                Iterator<?> it = m_project.getItemIds().iterator();
1168                if (m_project.containsId("Offline")) {
1169                    m_project.select("Offline");
1170                } else {
1171                    String p = (String)it.next();
1172                    while (p.equals(CmsProject.ONLINE_PROJECT_NAME) & it.hasNext()) {
1173                        p = (String)it.next();
1174                    }
1175                    m_project.select(p);
1176                }
1177            }
1178        } catch (CmsException e) {
1179            LOG.error("Unable to read projects", e);
1180        }
1181    }
1182
1183    /**
1184     * A initialization method.<p>
1185     *
1186     * @param window to be closed
1187     * @param app
1188     * @param settings user settings, null if new user
1189     */
1190    private void init(final Window window, final CmsAccountsApp app, final CmsUserSettings settings, boolean enabled) {
1191
1192        m_userdata.initFields(m_user, enabled ? EditLevel.all : EditLevel.none);
1193        if (m_user != null) {
1194            if (CmsStringUtil.isEmptyOrWhitespaceOnly(m_user.getFirstname())
1195                | CmsStringUtil.isEmptyOrWhitespaceOnly(m_user.getLastname())
1196                | CmsStringUtil.isEmptyOrWhitespaceOnly(m_user.getEmail())) {
1197                m_name_was_empty = true;
1198            }
1199        }
1200        iniLanguage(settings);
1201        iniProject(settings);
1202        iniSite(settings);
1203        iniStartView(settings);
1204
1205        m_site.addValueChangeListener(new ValueChangeListener() {
1206
1207            private static final long serialVersionUID = 5111762655156037899L;
1208
1209            public void valueChange(ValueChangeEvent event) {
1210
1211                setupStartFolder(null);
1212
1213            }
1214
1215        });
1216
1217        m_ok.addClickListener(new ClickListener() {
1218
1219            private static final long serialVersionUID = -2579639520410382246L;
1220
1221            public void buttonClick(ClickEvent event) {
1222
1223                setupValidators();
1224                if (isValid()) {
1225                    save();
1226                    window.close();
1227                    app.reload();
1228                }
1229            }
1230        });
1231
1232        m_next.addClickListener(new ClickListener() {
1233
1234            private static final long serialVersionUID = -8584899970290349959L;
1235
1236            public void buttonClick(ClickEvent event) {
1237
1238                setupValidators();
1239                switchTab();
1240
1241            }
1242        });
1243
1244        m_cancel.addClickListener(new ClickListener() {
1245
1246            private static final long serialVersionUID = 5803825104722705175L;
1247
1248            public void buttonClick(ClickEvent event) {
1249
1250                window.close();
1251            }
1252        });
1253
1254        if (m_user == null) {
1255            m_role.addValueChangeListener(new ValueChangeListener() {
1256
1257                private static final long serialVersionUID = 5697126133686172725L;
1258
1259                public void valueChange(ValueChangeEvent event) {
1260
1261                    iniSite(settings);
1262                    iniStartView(settings);
1263                }
1264            });
1265            m_group.addValueChangeListener(new ValueChangeListener() {
1266
1267                private static final long serialVersionUID = 1512940002751242094L;
1268
1269                public void valueChange(ValueChangeEvent event) {
1270
1271                    iniStartView(settings);
1272                    iniSite(settings);
1273                }
1274
1275            });
1276        }
1277
1278        m_site.addValueChangeListener(new ValueChangeListener() {
1279
1280            private static final long serialVersionUID = -169973382455098800L;
1281
1282            public void valueChange(ValueChangeEvent event) {
1283
1284                m_startfolder.setCmsObject(getCmsObjectWithSite((String)m_site.getValue()));
1285
1286            }
1287
1288        });
1289
1290        m_generateButton.addClickListener(new Button.ClickListener() {
1291
1292            private static final long serialVersionUID = 4128513094772586752L;
1293
1294            public void buttonClick(ClickEvent event) {
1295
1296                final Window windowDialog = CmsBasicDialog.prepareWindow(CmsBasicDialog.DialogWidth.content);
1297                windowDialog.setCaption(
1298                    CmsVaadinUtils.getMessageText(Messages.GUI_USERMANAGEMENT_GEN_PASSWORD_CAPTION_0));
1299                CmsGeneratePasswordDialog dialog = new CmsGeneratePasswordDialog(
1300                    CmsUserEditDialog.this,
1301                    new Runnable() {
1302
1303                        public void run() {
1304
1305                            windowDialog.close();
1306
1307                        }
1308                    });
1309                windowDialog.setContent(dialog);
1310                A_CmsUI.get().addWindow(windowDialog);
1311            }
1312        });
1313    }
1314
1315    /**
1316     * Saves changes to an existing user.<p>
1317     *
1318     * @throws CmsException exception
1319     */
1320    private void saveUser() throws CmsException {
1321
1322        updateUser(m_user);
1323
1324        if (!CmsStringUtil.isEmptyOrWhitespaceOnly(m_pw.getPassword1())) {
1325            if (isPasswordValid()) {
1326                m_cms.setPassword(m_user.getName(), m_pw.getPassword1());
1327            }
1328        }
1329
1330        m_cms.writeUser(m_user);
1331
1332    }
1333
1334    /**
1335     * Saves the user settings.<p>
1336     *
1337     * @throws CmsException exception
1338     */
1339    private void saveUserSettings() throws CmsException {
1340
1341        CmsUserSettings settings = new CmsUserSettings(m_user);
1342        settings.setLocale((Locale)m_language.getValue());
1343        settings.setStartSite((String)m_site.getValue() + "/");
1344        settings.setStartProject(m_ou.getValue() + (String)m_project.getValue());
1345        if (m_visSites) {
1346            settings.setStartFolder(m_startfolder.getValue().substring(((String)m_site.getValue()).length()));
1347            if (!CmsStringUtil.isEmptyOrWhitespaceOnly((String)m_startview.getValue())) {
1348                settings.setStartView((String)m_startview.getValue());
1349            }
1350        }
1351        settings.save(m_cms);
1352    }
1353
1354    /**
1355     * Sets the password fields.<p>
1356     */
1357    private void setPasswordFields() {
1358
1359        m_dummyPasswordLabel.setContentMode(com.vaadin.v7.shared.ui.label.ContentMode.HTML);
1360
1361        // ugly hack to prevent Firefox from asking user to save password on every click which causes the history token to change after editing a user
1362        String pwd = "<input type=\"password\" value=\"password\">";
1363        m_dummyPasswordLabel.setValue("<div style=\"display: none;\">" + pwd + pwd + "</div>");
1364
1365        m_pw.hideOldPassword();
1366        m_pw.setHeaderVisible(false);
1367        if (OpenCms.getPasswordHandler() instanceof I_CmsPasswordSecurityEvaluator) {
1368            m_pw.setSecurityHint(
1369                ((I_CmsPasswordSecurityEvaluator)OpenCms.getPasswordHandler()).getPasswordSecurityHint(
1370                    A_CmsUI.get().getLocale()));
1371        }
1372        m_pw.getOldPasswordField().setImmediate(true);
1373        m_pw.getPassword1Field().setImmediate(true);
1374        m_pw.getPassword2Field().setImmediate(true);
1375
1376        m_pw.getPassword1Field().addTextChangeListener(new TextChangeListener() {
1377
1378            private static final long serialVersionUID = 1L;
1379
1380            public void textChange(TextChangeEvent event) {
1381
1382                checkSecurity(event.getText());
1383                setEmailBox();
1384            }
1385        });
1386        m_pw.getPassword2Field().addTextChangeListener(new TextChangeListener() {
1387
1388            private static final long serialVersionUID = 1L;
1389
1390            public void textChange(TextChangeEvent event) {
1391
1392                checkSecurity(m_pw.getPassword1());
1393                checkPasswordMatch(event.getText());
1394                setEmailBox();
1395            }
1396        });
1397
1398    }
1399
1400    /**
1401     * Sets the password status for the user.<p>
1402     *
1403     * @param user CmsUser
1404     * @param reset true or false
1405     */
1406    private void setUserPasswordStatus(CmsUser user, boolean reset) {
1407
1408        if (reset) {
1409            user.setAdditionalInfo(CmsUserSettings.ADDITIONAL_INFO_PASSWORD_RESET, "true");
1410        } else {
1411            user.deleteAdditionalInfo(CmsUserSettings.ADDITIONAL_INFO_PASSWORD_RESET);
1412        }
1413        CmsUserTable.USER_PASSWORD_STATUS.put(user.getId(), new Boolean(reset));
1414    }
1415
1416    /**
1417     *  Read form and updates a given user according to form.<p>
1418     *
1419     * @param user to be updated
1420     */
1421    private void updateUser(CmsUser user) {
1422
1423        setUserPasswordStatus(user, m_forceResetPassword.getValue().booleanValue());
1424        user.setDescription(m_description.getValue());
1425        user.setManaged(!m_selfmanagement.getValue().booleanValue());
1426        user.setEnabled(m_enabled.getValue().booleanValue());
1427        m_userdata.submit(user, m_cms, new Runnable() {
1428
1429            public void run() {
1430                //
1431            }
1432        }, true);
1433    }
1434}