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.CmsGroup;
031import org.opencms.file.CmsObject;
032import org.opencms.main.CmsException;
033import org.opencms.main.CmsLog;
034import org.opencms.main.OpenCms;
035import org.opencms.security.CmsOrganizationalUnit;
036import org.opencms.security.CmsRole;
037import org.opencms.ui.A_CmsUI;
038import org.opencms.ui.CmsCssIcon;
039import org.opencms.ui.components.OpenCmsTheme;
040import org.opencms.util.CmsUUID;
041
042import java.util.ArrayList;
043import java.util.Collection;
044import java.util.List;
045
046import org.apache.commons.lang3.tuple.Pair;
047import org.apache.commons.logging.Log;
048
049import com.vaadin.v7.data.Item;
050import com.vaadin.v7.data.util.HierarchicalContainer;
051import com.vaadin.v7.event.ItemClickEvent;
052import com.vaadin.v7.event.ItemClickEvent.ItemClickListener;
053import com.vaadin.v7.ui.Tree;
054
055/**
056 * Class for the OU Tree.<p>
057 */
058@SuppressWarnings("deprecation")
059public class CmsOuTree extends Tree {
060
061    /** Log instance for this class. */
062    private static final Log LOG = CmsLog.getLog(CmsOuTree.class);
063
064    /**Root OU.*/
065    private static CmsOrganizationalUnit m_rootSystemOU;
066
067    /**name property. */
068    private static final String PROP_NAME = "name";
069
070    /**type property. */
071    private static final String PROP_TYPE = "type";
072
073    /**vaadin serial id.*/
074    private static final long serialVersionUID = -3532367333216144806L;
075
076    private static final String PROP_SID = "sid";
077
078    /**Calling app. */
079    private CmsAccountsApp m_app;
080
081    /**CmsObject. */
082    private CmsObject m_cms;
083
084    /**Root ou. */
085    private CmsOrganizationalUnit m_rootOu;
086
087    /**Container. */
088    private HierarchicalContainer m_treeContainer;
089
090    /**
091     * constructor.<p>
092     *
093     * @param cms CmsObject
094     * @param app app instance
095     * @param baseOU baseOu
096     */
097    public CmsOuTree(CmsObject cms, CmsAccountsApp app, String baseOU) {
098
099        m_cms = cms;
100        try {
101            m_rootSystemOU = OpenCms.getOrgUnitManager().readOrganizationalUnit(m_cms, "");
102        } catch (CmsException e1) {
103            //
104        }
105        m_app = app;
106        addStyleName(OpenCmsTheme.FULL_WIDTH_PADDING);
107        addStyleName(OpenCmsTheme.SIMPLE_DRAG);
108        setWidth("100%");
109        m_treeContainer = new HierarchicalContainer();
110        m_treeContainer.addContainerProperty(PROP_NAME, String.class, "");
111        m_treeContainer.addContainerProperty(PROP_TYPE, I_CmsOuTreeType.class, null);
112        m_treeContainer.addContainerProperty(PROP_SID, CmsUUID.class, null);
113        setContainerDataSource(m_treeContainer);
114
115        m_rootOu = null;
116        try {
117            m_rootOu = OpenCms.getOrgUnitManager().readOrganizationalUnit(m_cms, baseOU);
118            Item item = m_treeContainer.addItem(m_rootOu);
119            item.getItemProperty(PROP_NAME).setValue(getIconCaptionHTML(m_rootOu, CmsOuTreeType.OU));
120            item.getItemProperty(PROP_TYPE).setValue(CmsOuTreeType.OU);
121        } catch (CmsException e) {
122            LOG.error("Unable to read OU", e);
123        }
124        setItemCaptionPropertyId(PROP_NAME);
125        setHtmlContentAllowed(true);
126        setNullSelectionAllowed(false);
127        addChildrenForOUNode(m_rootOu);
128        expandItem(m_rootOu);
129        addItemClickListener(new ItemClickListener() {
130
131            private static final long serialVersionUID = -6475529853027436127L;
132
133            public void itemClick(ItemClickEvent event) {
134
135                handleItemClick(event.getItemId());
136
137            }
138        });
139        addExpandListener(new ExpandListener() {
140
141            private static final long serialVersionUID = 589297480547091120L;
142
143            public void nodeExpand(ExpandEvent event) {
144
145                handleExpand(event.getItemId());
146
147            }
148        });
149    }
150
151    /**
152     * Opens given path.<p>
153     *
154     * @param path ou path (=ou-name)
155     * @param type type (ou,group or user)
156     * @param groupID id of group (optional)
157     */
158    public void openPath(String path, I_CmsOuTreeType type, CmsUUID groupID) {
159
160        if (type == null) {
161            return;
162        }
163        try {
164            expandItem(m_rootOu);
165            String[] pathP = path.split("/");
166            String complPath = "";
167            for (String subP : pathP) {
168                complPath += subP + "/";
169                CmsOrganizationalUnit ou = OpenCms.getOrgUnitManager().readOrganizationalUnit(m_cms, complPath);
170                addChildrenForOUNode(ou);
171                expandItem(ou);
172            }
173
174            if (type.isGroup() || type.isRole()) {
175                String itemId = type.getId()
176                    + OpenCms.getOrgUnitManager().readOrganizationalUnit(m_cms, path).getName();
177                expandItem(itemId);
178                if (groupID == null) {
179                    setValue(itemId);
180                    return;
181                }
182                setValue(groupID);
183                return;
184            }
185            if (type.isUser()) {
186                setValue(type.getId() + OpenCms.getOrgUnitManager().readOrganizationalUnit(m_cms, path).getName());
187                return;
188            }
189
190            setValue(OpenCms.getOrgUnitManager().readOrganizationalUnit(m_cms, path));
191
192        } catch (CmsException e) {
193            LOG.error("Unable to read OU", e);
194        }
195    }
196
197    /**
198     * Handle expand action.<p>
199     *
200     * @param itemId which was expended
201     */
202    protected void handleExpand(Object itemId) {
203
204        I_CmsOuTreeType type = (I_CmsOuTreeType)getItem(itemId).getItemProperty(PROP_TYPE).getValue();
205        loadAndExpand(itemId, type);
206    }
207
208    /**
209     * Handle item click.<p>
210     *
211     * @param itemId item which was clicked
212     */
213
214    protected void handleItemClick(Object itemId) {
215
216        Item item = getItem(itemId);
217        I_CmsOuTreeType type = (I_CmsOuTreeType)getItem(itemId).getItemProperty(PROP_TYPE).getValue();
218        CmsUUID roleOrGroupID = null;
219        boolean idInItem = false;
220        if (itemId instanceof CmsUUID) {
221            roleOrGroupID = (CmsUUID)itemId;
222            idInItem = true;
223        } else if (item.getItemProperty(PROP_SID).getValue() != null) {
224            roleOrGroupID = (CmsUUID)(item.getItemProperty(PROP_SID).getValue());
225            idInItem = true;
226        }
227        if (type.equals(CmsOuTreeType.ROLE)) {
228            String ou = getOuFromItem(itemId, CmsOuTreeType.ROLE);
229            boolean isRoot = ou.isEmpty();
230            if (isRoot) {
231                ou = "/";
232            }
233            if (!((String)itemId).endsWith(ou)) {
234                if (isRoot) {
235                    if (((String)itemId).length() > (ou.length() + 1)) {
236                        roleOrGroupID = new CmsUUID(((String)itemId).substring(ou.length() + 1));
237                    }
238                } else {
239                    roleOrGroupID = new CmsUUID(((String)itemId).substring(ou.length() + 2));
240                }
241            }
242        }
243
244        m_app.update(getOuFromItem(itemId, type), type, roleOrGroupID, "");
245        if (isExpanded(itemId) || idInItem) {
246            return;
247        }
248        loadAndExpand(itemId, type);
249        setValue(itemId);
250
251    }
252
253    /**
254     * Updates items of current ou for item.<p>
255     *
256     * @param item to update ou for
257     */
258    void updateOU(CmsOrganizationalUnit item) {
259
260        //Check if ou has children ... vaadin returns null if not
261        if (m_treeContainer.getChildren(item) == null) {
262            return;
263        }
264        for (Object it : m_treeContainer.getChildren(item)) {
265            if (isExpanded(it)) {
266                I_CmsOuTreeType type = (I_CmsOuTreeType)getItem(it).getItemProperty(PROP_TYPE).getValue();
267                if (type.isGroup()) {
268                    addChildrenForGroupsNode(type, type.getId() + item.getName());
269                }
270            }
271        }
272    }
273
274    /**
275     * Add groups for given group parent item.
276     *
277     * @param type the tree type
278     * @param ouItem group parent item
279     */
280    private void addChildrenForGroupsNode(I_CmsOuTreeType type, String ouItem) {
281
282        try {
283            // Cut of type-specific prefix from ouItem with substring()
284            List<CmsGroup> groups = m_app.readGroupsForOu(m_cms, ouItem.substring(1), type, false);
285
286            List<Object> itemsToRemove = new ArrayList<Object>();
287
288            Collection<?> childCol = m_treeContainer.getChildren(ouItem);
289            if (childCol != null) {
290                itemsToRemove.addAll(childCol);
291            }
292            for (CmsGroup group : groups) {
293                Pair<String, CmsUUID> key = Pair.of(type.getId(), group.getId());
294                Item groupItem = m_treeContainer.addItem(key);
295                if (groupItem == null) {
296                    groupItem = getItem(key);
297                    itemsToRemove.remove(key);
298                }
299                groupItem.getItemProperty(PROP_SID).setValue(group.getId());
300                groupItem.getItemProperty(PROP_NAME).setValue(getIconCaptionHTML(group, CmsOuTreeType.GROUP));
301                groupItem.getItemProperty(PROP_TYPE).setValue(type);
302                setChildrenAllowed(key, false);
303                m_treeContainer.setParent(key, ouItem);
304            }
305
306            for (Object item : itemsToRemove) {
307                m_treeContainer.removeItem(item);
308            }
309
310        } catch (CmsException e) {
311            LOG.error("Can not read group", e);
312        }
313
314    }
315
316    /**
317     * Add children for ou.<p>
318     *
319     * @param item ou item
320     */
321    private void addChildrenForOUNode(CmsOrganizationalUnit item) {
322
323        List<Object> itemsToRemove = new ArrayList<Object>();
324
325        Collection<?> childCol = m_treeContainer.getChildren(item);
326        if (childCol != null) {
327            itemsToRemove.addAll(childCol);
328        }
329
330        try {
331            if (m_app.isOUManagable(item.getName())) {
332
333                List<I_CmsOuTreeType> types = m_app.getTreeTypeProvider().getTreeTypes();
334                for (I_CmsOuTreeType type : types) {
335                    if (!type.isValidForOu(m_cms, item.getName())) {
336                        continue;
337                    }
338                    if (type.isOrgUnit()) {
339                        continue;
340                    }
341                    String itemId = type.getId() + item.getName();
342                    Item newItem = m_treeContainer.addItem(itemId);
343                    itemsToRemove.remove(itemId);
344                    if (newItem != null) {
345                        newItem.getItemProperty(PROP_NAME).setValue(getIconCaptionHTML(itemId, type));
346                        newItem.getItemProperty(PROP_TYPE).setValue(type);
347                        m_treeContainer.setParent(itemId, item);
348                        setChildrenAllowed(itemId, type.isExpandable());
349                    }
350                }
351            }
352            List<CmsOrganizationalUnit> ous = OpenCms.getOrgUnitManager().getOrganizationalUnits(
353                m_cms,
354                item.getName(),
355                false);
356            List<CmsOrganizationalUnit> webOus = new ArrayList<CmsOrganizationalUnit>();
357            for (CmsOrganizationalUnit ou : ous) {
358                if (m_app.isParentOfManagableOU(ou.getName())) {
359                    itemsToRemove.remove(ou);
360                    if (ou.hasFlagWebuser()) {
361                        webOus.add(ou);
362                    } else {
363                        addOuToTree(ou, item);
364                    }
365                }
366            }
367            for (CmsOrganizationalUnit ou : webOus) {
368                if (m_app.isParentOfManagableOU(ou.getName())) {
369                    itemsToRemove.remove(ou);
370                    addOuToTree(ou, item);
371                }
372            }
373        } catch (CmsException e) {
374            LOG.error("Can't read ou", e);
375        }
376        for (Object it : itemsToRemove) {
377            m_treeContainer.removeItemRecursively(it);
378        }
379    }
380
381    /**
382     * Add roles for given role parent item.
383     *
384     * @param ouItem group parent item
385     */
386    private void addChildrenForRolesNode(String ouItem) {
387
388        try {
389            List<CmsRole> roles = OpenCms.getRoleManager().getRoles(m_cms, ouItem.substring(1), false);
390            CmsRole.applySystemRoleOrder(roles);
391            for (CmsRole role : roles) {
392                String roleId = ouItem + "/" + role.getId();
393                Item roleItem = m_treeContainer.addItem(roleId);
394                if (roleItem == null) {
395                    roleItem = getItem(roleId);
396                }
397                roleItem.getItemProperty(PROP_NAME).setValue(getIconCaptionHTML(role, CmsOuTreeType.ROLE));
398                roleItem.getItemProperty(PROP_TYPE).setValue(CmsOuTreeType.ROLE);
399                setChildrenAllowed(roleId, false);
400                m_treeContainer.setParent(roleId, ouItem);
401            }
402        } catch (CmsException e) {
403            LOG.error("Can not read group", e);
404        }
405    }
406
407    /**
408     * Adds an ou to the tree.<p>
409     *
410     * @param ou to be added
411     * @param parent_ou parent ou
412     */
413    private void addOuToTree(CmsOrganizationalUnit ou, CmsOrganizationalUnit parent_ou) {
414
415        Item containerItem;
416        containerItem = m_treeContainer.addItem(ou);
417        if (containerItem == null) {
418            containerItem = getItem(ou);
419        }
420        containerItem.getItemProperty(PROP_NAME).setValue(getIconCaptionHTML(ou, CmsOuTreeType.OU));
421        containerItem.getItemProperty(PROP_TYPE).setValue(CmsOuTreeType.OU);
422        m_treeContainer.setParent(ou, parent_ou);
423    }
424
425    /**
426     * Get HTML for icon and item caption.<p>
427     *
428     * @param item item to get icon and caption for
429     * @param type type
430     * @return html
431     */
432    private String getIconCaptionHTML(Object item, I_CmsOuTreeType type) {
433
434        CmsCssIcon icon = type.getIcon();
435        String caption = type.getName();
436        if (item instanceof CmsOrganizationalUnit) {
437            CmsOrganizationalUnit ou = (CmsOrganizationalUnit)item;
438            if (ou.hasFlagWebuser()) {
439                icon = new CmsCssIcon(OpenCmsTheme.ICON_OU_WEB);
440            }
441            caption = (ou.equals(m_rootSystemOU) ? ou.getDisplayName(A_CmsUI.get().getLocale()) : ou.getName());
442        }
443
444        if (item instanceof CmsGroup) {
445            //Real group shown under groups
446            caption = ((CmsGroup)item).getName();
447            icon = m_app.getGroupIcon((CmsGroup)item);
448        }
449
450        if (item instanceof CmsRole) {
451            //Real group shown under groups
452            caption = ((CmsRole)item).getName(A_CmsUI.get().getLocale());
453        }
454
455        if (icon != null) {
456            return "<span class=\"o-resource-icon\">"
457                + icon.getHtml()
458                + "</span>"
459                + "<span class=\"o-tree-caption\">"
460                + caption
461                + "</span>";
462        }
463        return "";
464    }
465
466    /**
467     * Gets ou from given item.<p>
468     *
469     * @param itemId to get ou for
470     * @param type of given item
471     * @return name of ou
472     */
473    private String getOuFromItem(Object itemId, I_CmsOuTreeType type) {
474
475        if (type.equals(CmsOuTreeType.OU)) {
476            return ((CmsOrganizationalUnit)itemId).getName();
477        }
478        Object o = m_treeContainer.getParent(itemId);
479        while (!(o instanceof CmsOrganizationalUnit)) {
480            o = m_treeContainer.getParent(o);
481        }
482        return ((CmsOrganizationalUnit)o).getName();
483    }
484
485    /**
486     * Load and expand given item.<p>
487     *
488     * @param itemId to be expanded
489     * @param type of item
490     */
491    private void loadAndExpand(Object itemId, I_CmsOuTreeType type) {
492
493        if (type.isOrgUnit()) {
494            addChildrenForOUNode((CmsOrganizationalUnit)itemId);
495        }
496        if (type.isGroup()) {
497            addChildrenForGroupsNode(type, (String)itemId);
498        }
499        if (type.isRole()) {
500            addChildrenForRolesNode((String)itemId);
501        }
502        expandItem(itemId);
503    }
504
505}