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