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.file.CmsObject;
031import org.opencms.file.CmsUser;
032import org.opencms.main.CmsException;
033import org.opencms.main.CmsLog;
034import org.opencms.ui.CmsVaadinUtils;
035import org.opencms.ui.components.CmsBasicDialog;
036import org.opencms.ui.components.editablegroup.CmsEditableGroup;
037import org.opencms.ui.components.editablegroup.I_CmsEditableGroupRow;
038import org.opencms.util.CmsDataTypeUtil;
039import org.opencms.util.CmsStringUtil;
040import org.opencms.util.CmsUUID;
041
042import java.util.ArrayList;
043import java.util.Collections;
044import java.util.Iterator;
045import java.util.List;
046import java.util.Map.Entry;
047import java.util.SortedMap;
048import java.util.TreeMap;
049
050import org.apache.commons.logging.Log;
051
052import com.google.common.base.Supplier;
053import com.vaadin.ui.Button;
054import com.vaadin.ui.Button.ClickEvent;
055import com.vaadin.ui.Button.ClickListener;
056import com.vaadin.ui.Component;
057import com.vaadin.ui.FormLayout;
058import com.vaadin.ui.Window;
059import com.vaadin.v7.ui.HorizontalLayout;
060import com.vaadin.v7.ui.TextField;
061
062/**
063 * Class for the Additional User info dialog.<p>
064 */
065public class CmsAdditionalInfosDialog extends CmsBasicDialog {
066
067    /**vaadin serial id. */
068    private static final long serialVersionUID = 9007206105761103873L;
069
070    /** The logger of this class. */
071    private static final Log LOG = CmsLog.getLog(CmsAdditionalInfosDialog.class);
072
073    /**CmsUser to edit additional infos for. */
074    private CmsUser m_user;
075
076    /**vaadin component.*/
077    private FormLayout m_layout;
078
079    /**Layout for not editable info. */
080    private FormLayout m_layoutnoedit;
081
082    /**cancel button.*/
083    private Button m_cancel;
084
085    /**ok button.*/
086    private Button m_ok;
087
088    /** The map of editable additional info entries. */
089    private SortedMap<String, Object> m_addInfoEditable;
090
091    /** The map of non-editable additional info entries. */
092    private SortedMap<String, Object> m_addInfoReadOnly;
093
094    /** The group for the module resource fields. */
095    private CmsEditableGroup m_userinfoGroup;
096
097    /** The group for the module resource fields. */
098    private CmsEditableGroup m_userinfoNoEditGroup;
099
100    /** CmsObject.*/
101    private CmsObject m_cms;
102
103    /**
104     * public constructor.<p>
105     *
106     * @param cms CmsObject
107     * @param userID id of user
108     * @param window window
109     * @param app
110     */
111    public CmsAdditionalInfosDialog(CmsObject cms, CmsUUID userID, final Window window, CmsAccountsApp app) {
112
113        CmsVaadinUtils.readAndLocalizeDesign(this, CmsVaadinUtils.getWpMessagesForCurrentLocale(), null);
114
115        m_cms = cms;
116        try {
117            m_user = cms.readUser(userID);
118            displayResourceInfoDirectly(Collections.singletonList(CmsAccountsApp.getPrincipalInfo(m_user)));
119        } catch (CmsException e) {
120            //
121        }
122
123        setAddInfoMaps();
124
125        Supplier<Component> fieldFactory = new Supplier<Component>() {
126
127            public Component get() {
128
129                return addInfoLayout("", "", true);
130            }
131        };
132
133        m_userinfoGroup = new CmsEditableGroup(m_layout, fieldFactory, "Add");
134        m_userinfoGroup.init();
135
136        m_userinfoNoEditGroup = new CmsEditableGroup(m_layoutnoedit, fieldFactory, "Add");
137        m_userinfoNoEditGroup.setAddButtonVisible(false);
138        m_userinfoNoEditGroup.init();
139
140        for (String info : m_addInfoEditable.keySet()) {
141            addAddInfoLayout(info, m_addInfoEditable.get(info), true);
142        }
143
144        for (String info : m_addInfoReadOnly.keySet()) {
145            addAddInfoLayout(info, m_addInfoReadOnly.get(info), false);
146        }
147
148        window.setCaption(
149            CmsVaadinUtils.getMessageText(
150                org.opencms.ui.apps.Messages.GUI_USERMANAGEMENT_USER_ADDITIONAL_INFOS_WINDOW_CAPTION_1,
151                m_user.getSimpleName()));
152
153        m_cancel.addClickListener(new ClickListener() {
154
155            private static final long serialVersionUID = 4879585408645749L;
156
157            public void buttonClick(ClickEvent event) {
158
159                window.close();
160            }
161
162        });
163
164        m_ok.addClickListener(new ClickListener() {
165
166            /**vaadin serial id. */
167            private static final long serialVersionUID = -5168099515616397665L;
168
169            public void buttonClick(ClickEvent event) {
170
171                submit();
172                window.close();
173                app.reload();
174            }
175        });
176
177    }
178
179    /**
180     * Submit additional info to user.<p>
181     */
182    protected void submit() {
183
184        if (m_userinfoNoEditGroup.getRows().size() != m_addInfoReadOnly.size()) {
185            List<String> currentKeys = getKeyListFromGroup(m_userinfoNoEditGroup);
186            for (String key : m_addInfoReadOnly.keySet()) {
187                if (!currentKeys.contains(key)) {
188                    saveAddInfo(key, null);
189                }
190            }
191        }
192
193        for (I_CmsEditableGroupRow row : m_userinfoGroup.getRows()) {
194            String key = ((TextField)(((HorizontalLayout)row.getComponent()).getComponent(0))).getValue();
195            String value = ((TextField)(((HorizontalLayout)row.getComponent()).getComponent(1))).getValue();
196            saveAddInfo(key, value);
197            m_addInfoEditable.remove(key);
198        }
199
200        //Remaining items in list seem to be deleted by the user..
201        for (String key : m_addInfoEditable.keySet()) {
202            saveAddInfo(key, null);
203        }
204        try {
205            m_cms.writeUser(m_user);
206        } catch (CmsException e) {
207            LOG.error("Unable to write user.", e);
208        }
209    }
210
211    /**
212     * Get horizontal layout with key, value from additional info.<p>
213     *
214     * @param key key
215     * @param value value
216     * @param editable boolean
217     * @return HorizontalLayout
218     */
219    HorizontalLayout addInfoLayout(String key, Object value, boolean editable) {
220
221        HorizontalLayout res = new HorizontalLayout();
222        res.setWidth("100%");
223        res.setSpacing(true);
224
225        TextField keyField = new TextField();
226        keyField.setValue(key);
227        keyField.setEnabled(editable);
228        keyField.setWidth("100%");
229
230        TextField valueField = new TextField();
231        valueField.setValue(value.toString());
232        valueField.setEnabled(editable);
233        valueField.setWidth("100%");
234
235        res.addComponent(keyField);
236        res.addComponent(valueField);
237
238        res.setExpandRatio(keyField, 1);
239        res.setExpandRatio(valueField, 1);
240
241        return res;
242    }
243
244    /**
245     * Add key value pair as component to ui.<p>
246     *
247     * @param key string
248     * @param value object
249     * @param editable boolean
250     */
251    private void addAddInfoLayout(String key, Object value, boolean editable) {
252
253        HorizontalLayout res = addInfoLayout(key, value, editable);
254        if (editable) {
255            m_userinfoGroup.addRow(res);
256        } else {
257            m_userinfoNoEditGroup.addRow(res);
258        }
259    }
260
261    /**
262     * Get List of keys from UI.<p>
263     *
264     * @param group editablegroup object
265     * @return List of keys
266     */
267    private List<String> getKeyListFromGroup(CmsEditableGroup group) {
268
269        List<String> res = new ArrayList<String>();
270        for (I_CmsEditableGroupRow row : group.getRows()) {
271            res.add(((TextField)(((HorizontalLayout)row.getComponent()).getComponent(0))).getValue());
272        }
273        return res;
274    }
275
276    /**
277     * Adds additional info to user.<p>
278     * (Doesn't write the user)<p>
279     *
280     * @param key string
281     * @param value string
282     */
283    private void saveAddInfo(String key, String value) {
284
285        int pos = key.indexOf("@");
286
287        String className = "";
288        if (pos > -1) {
289            className = key.substring(pos + 1);
290            key = key.substring(0, pos);
291        }
292
293        if (CmsStringUtil.isEmptyOrWhitespaceOnly(value)) {
294            m_user.deleteAdditionalInfo(key);
295            return;
296        }
297
298        if (pos < 0) {
299            m_user.setAdditionalInfo(key, value);
300            return;
301        }
302
303        Class<?> clazz;
304        try {
305            // try the class name
306            clazz = Class.forName(className);
307        } catch (Throwable e) {
308            try {
309                // try the class in the java.lang package
310                clazz = Class.forName(Integer.class.getPackage().getName() + "." + className);
311            } catch (Throwable e1) {
312                clazz = String.class;
313            }
314        }
315        m_user.setAdditionalInfo(key, CmsDataTypeUtil.parse(value, clazz));
316    }
317
318    /**
319     * Builds the additional info maps.<p>
320     */
321    private void setAddInfoMaps() {
322
323        m_addInfoEditable = new TreeMap<String, Object>();
324        m_addInfoReadOnly = new TreeMap<String, Object>();
325        Iterator<Entry<String, Object>> itEntries = m_user.getAdditionalInfo().entrySet().iterator();
326        while (itEntries.hasNext()) {
327            Entry<String, Object> entry = itEntries.next();
328            String key = entry.getKey().toString();
329            if ((entry.getValue() == null) || CmsStringUtil.isEmptyOrWhitespaceOnly(entry.getValue().toString())) {
330                // skip empty entries
331                continue;
332            }
333            if (!entry.getValue().getClass().equals(String.class)) {
334                // only show type different to string
335                key += "@" + entry.getValue().getClass().getName();
336            }
337            if (CmsDataTypeUtil.isParseable(entry.getValue().getClass())) {
338                m_addInfoEditable.put(key, entry.getValue());
339            } else {
340                String value = entry.getValue().toString();
341                if (value.length() > (75 - key.length())) {
342                    if ((75 - key.length()) > 5) {
343                        value = value.substring(0, (75 - key.length())) + " ...";
344                    } else {
345                        value = "...";
346                    }
347                }
348                m_addInfoReadOnly.put(key, value);
349            }
350        }
351    }
352}