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.ade.sitemap.client;
029
030import org.opencms.ade.sitemap.client.control.CmsSitemapController;
031import org.opencms.ade.sitemap.client.hoverbar.CmsSitemapHoverbar;
032import org.opencms.ade.sitemap.client.ui.css.I_CmsSitemapItemCss;
033import org.opencms.ade.sitemap.client.ui.css.I_CmsSitemapLayoutBundle;
034import org.opencms.ade.sitemap.shared.CmsClientSitemapEntry;
035import org.opencms.ade.sitemap.shared.CmsClientSitemapEntry.EntryType;
036import org.opencms.ade.sitemap.shared.CmsDetailPageTable;
037import org.opencms.ade.sitemap.shared.CmsSitemapData.EditorMode;
038import org.opencms.file.CmsResource;
039import org.opencms.gwt.client.CmsCoreProvider;
040import org.opencms.gwt.client.dnd.I_CmsDragHandle;
041import org.opencms.gwt.client.dnd.I_CmsDropTarget;
042import org.opencms.gwt.client.property.CmsReloadMode;
043import org.opencms.gwt.client.ui.CmsAlertDialog;
044import org.opencms.gwt.client.ui.CmsListItemWidget;
045import org.opencms.gwt.client.ui.CmsListItemWidget.Background;
046import org.opencms.gwt.client.ui.CmsListItemWidget.I_CmsTitleEditHandler;
047import org.opencms.gwt.client.ui.css.I_CmsInputLayoutBundle;
048import org.opencms.gwt.client.ui.input.CmsLabel;
049import org.opencms.gwt.client.ui.input.CmsLabel.I_TitleGenerator;
050import org.opencms.gwt.client.ui.tree.CmsLazyTreeItem;
051import org.opencms.gwt.client.ui.tree.CmsTreeItem;
052import org.opencms.gwt.client.util.CmsMessages;
053import org.opencms.gwt.client.util.CmsStyleVariable;
054import org.opencms.gwt.client.util.I_CmsSimpleCallback;
055import org.opencms.gwt.shared.CmsAdditionalInfoBean;
056import org.opencms.gwt.shared.CmsListInfoBean;
057import org.opencms.gwt.shared.CmsListInfoBean.LockIcon;
058import org.opencms.gwt.shared.CmsListInfoBean.StateIcon;
059import org.opencms.gwt.shared.property.CmsClientProperty;
060import org.opencms.gwt.shared.property.CmsPropertyModification;
061import org.opencms.util.CmsStringUtil;
062import org.opencms.util.CmsUUID;
063
064import java.util.ArrayList;
065import java.util.HashMap;
066import java.util.List;
067import java.util.Map;
068
069import com.google.gwt.core.client.Scheduler;
070import com.google.gwt.core.client.Scheduler.RepeatingCommand;
071import com.google.gwt.dom.client.Element;
072import com.google.gwt.event.dom.client.ClickEvent;
073import com.google.gwt.event.dom.client.ClickHandler;
074import com.google.gwt.event.logical.shared.OpenEvent;
075import com.google.gwt.event.logical.shared.OpenHandler;
076import com.google.gwt.user.client.ui.Label;
077import com.google.gwt.user.client.ui.TextBox;
078import com.google.gwt.user.client.ui.Widget;
079
080/**
081 * Sitemap entry tree item implementation.<p>
082 *
083 * @since 8.0.0
084 *
085 * @see org.opencms.gwt.client.ui.tree.CmsLazyTreeItem
086 * @see org.opencms.ade.sitemap.shared.CmsClientSitemapEntry
087 */
088public class CmsSitemapTreeItem extends CmsLazyTreeItem {
089
090    /**
091     * Label generator for the detail page info label.<p>
092     */
093    protected class DetailPageLabelTitleGenerator implements I_TitleGenerator {
094
095        /** The title to use for the detail page label.*/
096        private String m_detailPageTitle;
097
098        /**
099         * @see org.opencms.gwt.client.ui.input.CmsLabel.I_TitleGenerator#getTitle(java.lang.String)
100         */
101        public String getTitle(String originalText) {
102
103            return m_detailPageTitle;
104        }
105
106        /**
107         * Sets the title to use for the detail page label.
108         *
109         * @param detailPageTitle the title to use
110         */
111        public void setDetailPageTitle(String detailPageTitle) {
112
113            m_detailPageTitle = detailPageTitle;
114        }
115    }
116
117    /** The CSS bundle used by this widget. */
118    private static final I_CmsSitemapItemCss CSS = I_CmsSitemapLayoutBundle.INSTANCE.sitemapItemCss();
119
120    /** A map of sitemap tree items by entry id. */
121    private static Map<CmsUUID, CmsSitemapTreeItem> m_itemsById = new HashMap<CmsUUID, CmsSitemapTreeItem>();
122
123    /** The current sitemap entry id. */
124    protected CmsUUID m_entryId;
125
126    /** The detail page label title generator. */
127    private DetailPageLabelTitleGenerator m_detailPageLabelTitleGenerator;
128
129    /** Style variable for to toggle in navigation style. */
130    private CmsStyleVariable m_inNavigationStyle;
131
132    /** Style variable for opener. */
133    private CmsStyleVariable m_openerForNonNavigationStyle;
134
135    /** Style variable for switching between 'children / no children' styles. */
136    private CmsStyleVariable m_styleHasChildren = new CmsStyleVariable(this);
137
138    /** Style variable for switching between 'navigation children / no navigation children' styles. */
139    private CmsStyleVariable m_styleHasNavChildren = new CmsStyleVariable(this);
140
141    /**
142     * Default constructor.<p>
143     *
144     * @param entry the sitemap entry
145     */
146    public CmsSitemapTreeItem(CmsClientSitemapEntry entry) {
147
148        super(generateItemWidget(entry), false);
149        m_opener.addStyleName(CSS.treeItemOpener());
150        m_styleHasChildren.setValue(CSS.hasChildren());
151        m_styleHasNavChildren.setValue(CSS.hasNavChildren());
152        m_entryId = entry.getId();
153        m_decoratedPanel.addDecorationBoxStyle(CSS.sitemapEntryDecoration());
154        m_detailPageLabelTitleGenerator = new DetailPageLabelTitleGenerator();
155        getListItemWidget().setUnselectable();
156        getListItemWidget().addOpenHandler(new OpenHandler<CmsListItemWidget>() {
157
158            public void onOpen(OpenEvent<CmsListItemWidget> event) {
159
160                CmsSitemapView.getInstance().getController().updateSingleEntry(m_entryId);
161            }
162        });
163        getListItemWidget().addIconClickHandler(new ClickHandler() {
164
165            public void onClick(ClickEvent event) {
166
167                CmsSitemapController controller = CmsSitemapView.getInstance().getController();
168                CmsClientSitemapEntry sitemapEntry = controller.getEntryById(m_entryId);
169                if (sitemapEntry != null) {
170                    if (sitemapEntry.isSubSitemapType()) {
171                        controller.openSiteMap(sitemapEntry.getSitePath());
172                    } else if (sitemapEntry.isNavigationLevelType()) {
173                        if (!sitemapEntry.getSubEntries().isEmpty()) {
174                            CmsClientSitemapEntry subEntry = sitemapEntry.getSubEntries().get(0);
175                            if (!subEntry.isNavigationLevelType()) {
176                                controller.leaveEditor(subEntry.getSitePath());
177                                return;
178                            }
179                        }
180                        getListItemWidget().setIconTitle(
181                            Messages.get().key(Messages.GUI_NAVIGATION_LEVEL_UNKOWN_TARGET_0));
182                    } else {
183                        controller.leaveEditor(sitemapEntry.getSitePath());
184                    }
185                }
186            }
187        });
188        m_inNavigationStyle = new CmsStyleVariable(this);
189        m_openerForNonNavigationStyle = new CmsStyleVariable(m_opener);
190        getListItemWidget().addTitleStyleName(CSS.itemTitle());
191        updateInNavigation(entry);
192        m_itemsById.put(m_entryId, this);
193        setId(getName(entry.getSitePath()));
194        updateSitePath(entry.getSitePath());
195        updateDetailPageStatus();
196        updateLock(entry);
197        if (!entry.isFolderType()) {
198            hideOpeners();
199        }
200        setDropEnabled(entry.isFolderType() && !entry.hasForeignFolderLock());
201        getListItemWidget().setTitleEditHandler(new I_CmsTitleEditHandler() {
202
203            /**
204             * @see org.opencms.gwt.client.ui.CmsListItemWidget.I_CmsTitleEditHandler#handleEdit(org.opencms.gwt.client.ui.input.CmsLabel, com.google.gwt.user.client.ui.TextBox)
205             */
206            public void handleEdit(CmsLabel titleLabel, TextBox box) {
207
208                final CmsClientSitemapEntry editEntry = getSitemapEntry();
209                final String newTitle = box.getText();
210                box.removeFromParent();
211                if (CmsStringUtil.isEmpty(newTitle)) {
212                    titleLabel.setVisible(true);
213                    String dialogTitle = Messages.get().key(Messages.GUI_EDIT_TITLE_ERROR_DIALOG_TITLE_0);
214                    String dialogText = Messages.get().key(Messages.GUI_TITLE_CANT_BE_EMPTY_0);
215                    CmsAlertDialog alert = new CmsAlertDialog(dialogTitle, dialogText);
216                    alert.center();
217                    return;
218                }
219                String oldTitle = editEntry.getTitle();
220                if (!oldTitle.equals(newTitle)) {
221                    CmsPropertyModification propMod = new CmsPropertyModification(
222                        editEntry.getId(),
223                        CmsClientProperty.PROPERTY_NAVTEXT,
224                        newTitle,
225                        true);
226                    final List<CmsPropertyModification> propChanges = new ArrayList<CmsPropertyModification>();
227                    propChanges.add(propMod);
228                    final CmsSitemapController controller = CmsSitemapView.getInstance().getController();
229                    if (editEntry.isNew() && !editEntry.isRoot() && !oldTitle.equalsIgnoreCase(newTitle)) {
230                        if (oldTitle.equals(editEntry.getPropertyValue(CmsClientProperty.PROPERTY_TITLE))) {
231                            CmsPropertyModification titleMod = new CmsPropertyModification(
232                                editEntry.getId(),
233                                CmsClientProperty.PROPERTY_TITLE,
234                                newTitle,
235                                true);
236                            propChanges.add(titleMod);
237                        }
238                        controller.ensureUniqueName(
239                            CmsResource.getParentFolder(editEntry.getSitePath()),
240                            newTitle,
241                            new I_CmsSimpleCallback<String>() {
242
243                                public void execute(String urlName) {
244
245                                    controller.editAndChangeName(
246                                        editEntry,
247                                        urlName,
248                                        propChanges,
249                                        true,
250                                        CmsReloadMode.reloadEntry);
251                                }
252                            });
253                    } else {
254                        controller.edit(editEntry, propChanges, CmsReloadMode.reloadEntry);
255                    }
256                }
257                titleLabel.setVisible(true);
258            }
259        });
260    }
261
262    /**
263     * Looks up a sitemap tree item by entry id.<p>
264     *
265     * @param id the sitemap entry id
266     * @return the corresponding sitemap tree item, or null if there is none
267     */
268    public static CmsSitemapTreeItem getItemById(CmsUUID id) {
269
270        return m_itemsById.get(id);
271    }
272
273    /**
274     * Helper method to add an additional info bean to a list.<p>
275     *
276     * @param infos the list of additional info beans
277     * @param label the label for the new bean
278     * @param value the value for the new bean
279     */
280    protected static void addInfo(List<CmsAdditionalInfoBean> infos, String label, String value) {
281
282        infos.add(new CmsAdditionalInfoBean(label, value, null));
283    }
284
285    /**
286     * Returns the list info bean for the given entry.<p>
287     *
288     * @param entry the sitemap entry
289     * @param navMode a flag indicating whether we want the info bean for navigation mode or VFS mode
290     *
291     * @return the list info bean
292     */
293    static CmsListInfoBean getInfoBean(CmsClientSitemapEntry entry, boolean navMode) {
294
295        CmsListInfoBean infoBean = new CmsListInfoBean();
296        infoBean.setTitle(entry.getTitle());
297        infoBean.setSubTitle(entry.getSitePath());
298        // showing the resource type icon of the default file in navigation mode
299        infoBean.setResourceType(
300            CmsStringUtil.isNotEmptyOrWhitespaceOnly(entry.getDefaultFileType())
301            ? entry.getDefaultFileType()
302            : entry.getResourceTypeName());
303        infoBean.setResourceState(entry.getResourceState());
304        CmsMessages msg = Messages.get();
305        List<CmsAdditionalInfoBean> infos = new ArrayList<CmsAdditionalInfoBean>();
306        addInfo(infos, msg.key(Messages.GUI_NAME_0), entry.getName());
307
308        boolean isTitleSet = false;
309        if (navMode) {
310            // in nav mode, display the title of the page rather than that of the folder, if available
311            Map<String, CmsClientProperty> defaultFileProps = entry.getDefaultFileProperties();
312            CmsClientProperty titleProperty = defaultFileProps == null
313            ? null
314            : defaultFileProps.get(CmsClientProperty.PROPERTY_TITLE);
315            if ((titleProperty != null) && !titleProperty.isEmpty()) {
316                addInfo(infos, msg.key(Messages.GUI_TITLE_PROPERTY_0), titleProperty.getEffectiveValue());
317                isTitleSet = true;
318            }
319        }
320        if (!isTitleSet) {
321            CmsClientProperty titleProperty = entry.getOwnProperties().get(CmsClientProperty.PROPERTY_TITLE);
322            if ((titleProperty != null) && !titleProperty.isEmpty()) {
323                addInfo(infos, msg.key(Messages.GUI_TITLE_PROPERTY_0), titleProperty.getEffectiveValue());
324                isTitleSet = true;
325            }
326        }
327
328        String shownPath = entry.getVfsPath();
329        if (CmsStringUtil.isEmptyOrWhitespaceOnly(shownPath)) {
330            shownPath = "-";
331        }
332        addInfo(infos, msg.key(Messages.GUI_VFS_PATH_0), shownPath);
333        if (CmsStringUtil.isNotEmptyOrWhitespaceOnly(entry.getDateReleased())) {
334            addInfo(infos, msg.key(Messages.GUI_LABEL_DATE_RELEASED_0), entry.getDateReleased());
335
336        }
337        if (CmsStringUtil.isNotEmptyOrWhitespaceOnly(entry.getDateExpired())) {
338            addInfo(infos, msg.key(Messages.GUI_LABEL_DATE_EXPIRED_0), entry.getDateExpired());
339
340        }
341        if (entry.getEntryType() == EntryType.redirect) {
342            addInfo(infos, msg.key(Messages.GUI_LABEL_TARGET_0), entry.getRedirectTarget());
343
344        }
345        if (entry.getAliases() != null) {
346            int counter = 1;
347            for (String aliasPath : entry.getAliases()) {
348                addInfo(infos, msg.key(Messages.GUI_LABEL_ALIAS_1, counter + ""), aliasPath);
349                counter += 1;
350            }
351        }
352        infoBean.setAdditionalInfo(infos);
353        return infoBean;
354    }
355
356    /**
357     * Generates the list item widget for the tree item.<p>
358     *
359     * @param entry the sitemap entry
360     *
361     * @return the list item widget
362     */
363    private static CmsListItemWidget generateItemWidget(final CmsClientSitemapEntry entry) {
364
365        CmsListInfoBean infoBean = getInfoBean(entry, true);
366        final CmsListItemWidget itemWidget = new CmsListItemWidget(infoBean);
367        itemWidget.setUnselectable();
368        itemWidget.setIcon(CmsSitemapView.getInstance().getIconForEntry(entry));
369        itemWidget.setTopRightIcon(null, "");
370        itemWidget.setIconTitle(
371            entry.isSubSitemapType()
372            ? Messages.get().key(Messages.GUI_HOVERBAR_GOTO_SUB_0)
373            : Messages.get().key(Messages.GUI_HOVERBAR_GOTO_0));
374        setAdditionalStyles(entry, itemWidget);
375        return itemWidget;
376    }
377
378    /**
379     * Sets the additional style to mark expired entries or those that have the hide in navigation property set.<p>
380     *
381     * @param entry the sitemap entry
382     * @param itemWidget the item widget
383     */
384    private static void setAdditionalStyles(CmsClientSitemapEntry entry, CmsListItemWidget itemWidget) {
385
386        if (!entry.isResleasedAndNotExpired()
387            || ((CmsSitemapView.getInstance().getEditorMode() == EditorMode.navigation)
388                && !entry.isDefaultFileReleased())) {
389            itemWidget.getContentPanel().addStyleName(
390                I_CmsSitemapLayoutBundle.INSTANCE.sitemapItemCss().expiredOrNotReleased());
391        } else {
392            itemWidget.getContentPanel().removeStyleName(
393                I_CmsSitemapLayoutBundle.INSTANCE.sitemapItemCss().expiredOrNotReleased());
394        }
395        if (entry.isHiddenNavigationEntry()) {
396            itemWidget.getContentPanel().addStyleName(
397                I_CmsSitemapLayoutBundle.INSTANCE.sitemapItemCss().hiddenNavEntry());
398        } else {
399            itemWidget.getContentPanel().removeStyleName(
400                I_CmsSitemapLayoutBundle.INSTANCE.sitemapItemCss().hiddenNavEntry());
401        }
402    }
403
404    /**
405     * Given the path of a sitemap entry, this method returns the URL which should be displayed to the user.<p>
406     *
407     * @param sitePath the site path of a sitemap entry
408     *
409     * @return the URL which should be displayed to the user
410     */
411    public String getDisplayedUrl(String sitePath) {
412
413        if (getSitemapEntry().isLeafType() && sitePath.endsWith("/")) {
414            sitePath = sitePath.substring(0, sitePath.length() - 1);
415        }
416        CmsSitemapController controller = CmsSitemapView.getInstance().getController();
417        String exportProp = controller.getEffectiveProperty(getSitemapEntry(), "export");
418        if ("true".equals(exportProp)) {
419            String exportName = getSitemapEntry().getExportName();
420            if (exportName == null) {
421                exportName = CmsCoreProvider.get().getSiteRoot();
422            }
423            String rfsPrefix = CmsSitemapView.getInstance().getController().getData().getExportRfsPrefix();
424            if (rfsPrefix != null) {
425                return CmsStringUtil.joinPaths(rfsPrefix, exportName, sitePath);
426            }
427        }
428        return CmsCoreProvider.get().link(sitePath);
429    }
430
431    /**
432     * @see org.opencms.gwt.client.dnd.I_CmsDraggable#getDragHelper(I_CmsDropTarget)
433     */
434    @Override
435    public Element getDragHelper(I_CmsDropTarget target) {
436
437        Element helper = super.getDragHelper(target);
438        // ensure the proper CSS context for the drag helper
439        m_provisionalParent.addClassName(I_CmsSitemapLayoutBundle.INSTANCE.sitemapItemCss().navMode());
440        return helper;
441    }
442
443    /**
444     * Returns the entry id.<p>
445     *
446     * @return the entry id
447     */
448    public CmsUUID getEntryId() {
449
450        return m_entryId;
451    }
452
453    /**
454     * @see org.opencms.gwt.client.ui.CmsListItem#getMoveHandle()
455     */
456    @Override
457    public I_CmsDragHandle getMoveHandle() {
458
459        CmsSitemapHoverbar hoverbar = getHoverbar();
460        if (hoverbar != null) {
461            int count = hoverbar.getWidgetCount();
462            if (count > 0) {
463                for (int i = 0; i < count; i++) {
464                    Widget w = hoverbar.getWidget(i);
465                    if (w instanceof I_CmsDragHandle) {
466                        return (I_CmsDragHandle)w;
467                    }
468                }
469            }
470        }
471        return null;
472    }
473
474    /**
475     * @see org.opencms.gwt.client.ui.tree.CmsTreeItem#getPath()
476     */
477    @Override
478    public String getPath() {
479
480        String result = getSitePath();
481        // ensure that the path of a folder ends with a '/'
482        if (getSitemapEntry().isFolderType() && !result.endsWith("/")) {
483            result += "/";
484        }
485        return result;
486    }
487
488    /**
489     * Returns the sitemap entry.<p>
490     *
491     * @return the sitemap entry
492     */
493    public CmsClientSitemapEntry getSitemapEntry() {
494
495        return CmsSitemapView.getInstance().getController().getEntryById(m_entryId);
496    }
497
498    /**
499     * Returns the site path.<p>
500     *
501     * @return the site path
502     */
503    public String getSitePath() {
504
505        return getSitemapEntry().getSitePath();
506    }
507
508    /**
509     * Turns the highlighting for this item on or off.<p>
510     *
511     * @param highlightOn if true, the highlighting is turned on, else off
512     */
513    public void highlight(boolean highlightOn) {
514
515        if (highlightOn) {
516            setBackgroundColor(Background.YELLOW);
517        } else {
518            setBackgroundColor(Background.DEFAULT);
519        }
520    }
521
522    /**
523     * Temporarily highlights an item.<p>
524     *
525     * @param duration the duration for which
526     * @param background the background to color to set when finished
527     */
528    public void highlightTemporarily(int duration, final Background background) {
529
530        int blinkInterval = 300;
531        final int blinkCount = duration / blinkInterval;
532
533        Scheduler.get().scheduleFixedPeriod(new RepeatingCommand() {
534
535            private int m_counter;
536
537            /**
538             * @see com.google.gwt.core.client.Scheduler.RepeatingCommand#execute()
539             */
540            public boolean execute() {
541
542                boolean finish = m_counter > blinkCount;
543                highlight(((m_counter % 2) == 0) && !finish);
544                m_counter += 1;
545                if (finish) {
546                    setBackgroundColor(background);
547                }
548                return !finish;
549            }
550        }, blinkInterval);
551    }
552
553    /**
554     * @see org.opencms.gwt.client.ui.tree.CmsTreeItem#isDropEnabled()
555     */
556    @Override
557    public boolean isDropEnabled() {
558
559        CmsClientSitemapEntry entry = getSitemapEntry();
560        return !entry.hasForeignFolderLock() && entry.isInNavigation() && entry.isFolderType() && super.isDropEnabled();
561    }
562
563    /**
564     * @see org.opencms.gwt.client.dnd.I_CmsDraggable#onDragCancel()
565     */
566    @Override
567    public void onDragCancel() {
568
569        removeStyleName(I_CmsSitemapLayoutBundle.INSTANCE.sitemapItemCss().positionIndicator());
570        super.onDragCancel();
571    }
572
573    /**
574     * @see org.opencms.gwt.client.dnd.I_CmsDraggable#onDrop(org.opencms.gwt.client.dnd.I_CmsDropTarget)
575     */
576    @Override
577    public void onDrop(I_CmsDropTarget target) {
578
579        removeStyleName(I_CmsSitemapLayoutBundle.INSTANCE.sitemapItemCss().positionIndicator());
580        super.onDrop(target);
581    }
582
583    /**
584     * @see org.opencms.gwt.client.ui.CmsListItem#onStartDrag(org.opencms.gwt.client.dnd.I_CmsDropTarget)
585     */
586    @Override
587    public void onStartDrag(I_CmsDropTarget target) {
588
589        setOpen(false);
590        // transform the widget into a position indicator
591        addStyleName(I_CmsSitemapLayoutBundle.INSTANCE.sitemapItemCss().positionIndicator());
592        CmsSitemapHoverbar hoverbar = getHoverbar();
593        if (hoverbar != null) {
594            hoverbar.hide();
595        }
596    }
597
598    /**
599     * Resets entry appearance.<p>
600     */
601    public void resetEntry() {
602
603        updateEntry(getSitemapEntry());
604    }
605
606    /**
607     * Sets the background color of the list item widget.<p>
608     *
609     * If the background is <code>null</code>, the widget will be shown with its default style.<p>
610     *
611     * @param background the background color to set
612     */
613    public void setBackgroundColor(Background background) {
614
615        getListItemWidget().setBackground(background);
616    }
617
618    /**
619     * Sets the icon.<p>
620     *
621     * @param icon the icon to set
622     */
623    public void setStateIcon(StateIcon icon) {
624
625        getListItemWidget().setStateIcon(icon);
626    }
627
628    /**
629     * @see com.google.gwt.user.client.ui.UIObject#toString()
630     */
631    @Override
632    public String toString() {
633
634        StringBuffer sb = new StringBuffer();
635        sb.append(getSitemapEntry().getSitePath()).append("\n");
636        for (int i = 0; i < getChildCount(); i++) {
637            CmsTreeItem child = getChild(i);
638            if (child instanceof CmsLazyTreeItem.LoadingItem) {
639                continue;
640            }
641            sb.append(child.toString());
642        }
643        return sb.toString();
644    }
645
646    /**
647     * Updates the detail page description.<p>
648     */
649    public void updateDetailPageStatus() {
650
651        CmsDetailPageTable detailPageTable = CmsSitemapView.getInstance().getController().getDetailPageTable();
652        String type;
653        String text = null;
654        String suffixTitle = null;
655        switch (detailPageTable.getStatus(m_entryId)) {
656            case firstDetailPage:
657                type = detailPageTable.get(m_entryId).getDisplayType();
658                suffixTitle = Messages.get().key(Messages.GUI_MAIN_DETAIL_PAGE_TITLE_1, type);
659                text = "(*" + type + ")";
660                break;
661            case otherDetailPage:
662                type = detailPageTable.get(m_entryId).getDisplayType();
663                suffixTitle = Messages.get().key(Messages.GUI_DETAIL_PAGE_TITLE_1, type);
664                text = "(" + type + ")";
665                break;
666            case noDetailPage:
667            default:
668        }
669        m_detailPageLabelTitleGenerator.setDetailPageTitle(suffixTitle);
670        getListItemWidget().updateTruncation();
671        Widget label = getListItemWidget().getShortExtraInfoLabel();
672        label.addStyleName(I_CmsInputLayoutBundle.INSTANCE.inputCss().subtitleSuffix());
673        getListItemWidget().setExtraInfo(text);
674    }
675
676    /**
677     * Updates the sitemap editor mode.<p>
678     */
679    public void updateEditorMode() {
680
681        CmsClientSitemapEntry entry = getSitemapEntry();
682        getListItemWidget().setIcon(CmsSitemapView.getInstance().getIconForEntry(entry));
683        setAdditionalStyles(entry, getListItemWidget());
684        for (Widget child : m_children) {
685            if (child instanceof CmsSitemapTreeItem) {
686                ((CmsSitemapTreeItem)child).updateEditorMode();
687            }
688        }
689    }
690
691    /**
692     * Refreshes the displayed data from the given sitemap entry.<p>
693     *
694     * @param entry the sitemap entry to update
695     */
696    public void updateEntry(CmsClientSitemapEntry entry) {
697
698        getListItemWidget().setTitleLabel(entry.getTitle());
699        getListItemWidget().reInitAdditionalInfo(getInfoBean(entry, CmsSitemapView.getInstance().isNavigationMode()));
700        updateSitePath(entry.getSitePath());
701        updateDetailPageStatus();
702        updateLock(entry);
703        updateInNavigation(entry);
704        getListItemWidget().setIcon(CmsSitemapView.getInstance().getIconForEntry(entry));
705        setAdditionalStyles(entry, getListItemWidget());
706        setDropEnabled(getSitemapEntry().isFolderType() && !getSitemapEntry().hasForeignFolderLock());
707        if (entry.isSubSitemapType() || entry.isLeafType()) {
708            hideOpeners();
709        } else {
710            showOpeners();
711        }
712        getListItemWidget().updateTruncation();
713    }
714
715    /**
716     * Updates the in navigation properties of the displayed entry.<p>
717     *
718     * @param entry the sitemap entry
719     */
720    public void updateInNavigation(CmsClientSitemapEntry entry) {
721
722        if (entry.isInNavigation()) {
723            m_inNavigationStyle.setValue(null);
724            getListItemWidget().setTitleEditable(true);
725        } else {
726            m_inNavigationStyle.setValue(CSS.notInNavigationEntry());
727            getListItemWidget().setTitleEditable(false);
728        }
729    }
730
731    /**
732     * Updates the site path using the current site entry's data.<p>
733     */
734    public void updateSitePath() {
735
736        updateSitePath(getSitemapEntry().getSitePath());
737    }
738
739    /**
740     * Updates the recursively the site path.<p>
741     *
742     * @param sitePath the new site path to set
743     */
744    public void updateSitePath(String sitePath) {
745
746        String newSubTitle = getDisplayedUrl(sitePath);
747        removeInvalidChildren();
748        getListItemWidget().setSubtitleLabel(newSubTitle);
749        String name = getName(sitePath);
750        setId(name);
751        getListItemWidget().setAdditionalInfoValue(1, name);
752        if (getLoadState() == LoadState.LOADED) {
753            for (int i = 0; i < getChildCount(); i++) {
754                CmsSitemapTreeItem item = (CmsSitemapTreeItem)getChild(i);
755                if ((item != null)
756                    && (CmsSitemapView.getInstance().getController().getEntryById(item.getEntryId()) != null)) {
757                    String path = CmsStringUtil.joinPaths(sitePath, CmsResource.getName(item.getSitePath()));
758                    item.updateSitePath(path);
759                }
760            }
761        }
762        getListItemWidget().updateTruncation();
763    }
764
765    /**
766    * Helper method for adding the marker widget.<p>
767    *
768    * @param text the text for the marker widget
769    *
770    * @return the new marker widget
771    */
772    protected Widget addMarker(String text) {
773
774        Label label = new Label(text);
775        label.addStyleName(CSS.marker());
776        getListItemWidget().addButton(label);
777        return label;
778    }
779
780    /**
781     * Return the name of this item, which can differ from the entry name for root nodes.<p>
782     *
783     * @param sitePath the sitemap entry's site path
784     *
785     * @return the name
786     */
787    protected String getName(String sitePath) {
788
789        String name = CmsResource.getName(sitePath);
790        if (name.endsWith("/")) {
791            name = name.substring(0, name.length() - 1);
792        }
793        return name;
794
795    }
796
797    /**
798     * @see org.opencms.gwt.client.ui.tree.CmsLazyTreeItem#onChangeChildren()
799     */
800    @Override
801    protected void onChangeChildren() {
802
803        super.onChangeChildren();
804
805        if (m_openerForNonNavigationStyle == null) {
806            // happens when initializing
807            return;
808        }
809        removeInvalidChildren();
810        boolean hasChildren = false;
811        boolean hasNavChildren = false;
812        for (Widget childWidget : m_children) {
813            if (childWidget instanceof CmsSitemapTreeItem) {
814                hasChildren = true;
815                CmsSitemapTreeItem treeItem = (CmsSitemapTreeItem)childWidget;
816                if (treeItem.getSitemapEntry().isInNavigation()) {
817                    hasNavChildren = true;
818                    break; // both flags set to true, no more iterations needed
819                }
820            }
821        }
822        m_styleHasChildren.setValue(hasChildren ? CSS.hasChildren() : CSS.hasNoChildren());
823        m_styleHasNavChildren.setValue(hasNavChildren ? CSS.hasNavChildren() : CSS.hasNoNavChildren());
824    }
825
826    /**
827    * Helper method to remove invalid children that don't have a corresponding CmsSitemapClientEntry.
828    */
829    protected void removeInvalidChildren() {
830
831        if (getLoadState() == LoadState.LOADED) {
832            List<CmsSitemapTreeItem> toDelete = new ArrayList<CmsSitemapTreeItem>();
833            for (int i = 0; i < getChildCount(); i++) {
834                CmsSitemapTreeItem item = (CmsSitemapTreeItem)getChild(i);
835                CmsUUID id = item.getEntryId();
836                if ((id != null) && (CmsSitemapView.getInstance().getController().getEntryById(id) == null)) {
837                    toDelete.add(item);
838                }
839            }
840            for (CmsSitemapTreeItem deleteItem : toDelete) {
841                m_children.removeItem(deleteItem);
842            }
843        }
844    }
845
846    /**
847     * Retrieves the hoverbar, can be <code>null</code> if not attached.<p>
848     *
849     * @return the hoverbar, or <code>null</code> if not attached
850     */
851    private CmsSitemapHoverbar getHoverbar() {
852
853        for (Widget w : getListItemWidget().getContentPanel()) {
854            if (!(w instanceof CmsSitemapHoverbar)) {
855                continue;
856            }
857            return (CmsSitemapHoverbar)w;
858        }
859        return null;
860    }
861
862    /**
863     * Updates the lock icon according to the entry information.<p>
864     *
865     * @param entry the entry
866     */
867    private void updateLock(CmsClientSitemapEntry entry) {
868
869        LockIcon icon = LockIcon.NONE;
870        String iconTitle = null;
871        if (entry.hasBlockingLockedChildren()) {
872            icon = LockIcon.CLOSED;
873            iconTitle = Messages.get().key(Messages.GUI_BLOCKING_LOCKED_CHILDREN_0);
874        }
875        if (!entry.getLock().isOwnedByUser()) {
876            switch (entry.getLock().getLockType()) {
877                case EXCLUSIVE:
878                case INHERITED:
879                case TEMPORARY:
880                    icon = LockIcon.CLOSED;
881                    break;
882                case SHARED_EXCLUSIVE:
883                case SHARED_INHERITED:
884                    icon = LockIcon.SHARED_CLOSED;
885                    break;
886                default:
887            }
888        } else {
889            switch (entry.getLock().getLockType()) {
890                case EXCLUSIVE:
891                case INHERITED:
892                case TEMPORARY:
893                    icon = LockIcon.OPEN;
894                    break;
895                case SHARED_EXCLUSIVE:
896                case SHARED_INHERITED:
897                    icon = LockIcon.SHARED_OPEN;
898                    break;
899                default:
900            }
901        }
902        if (entry.getLock().getLockOwner() != null) {
903            iconTitle = org.opencms.gwt.client.Messages.get().key(
904                org.opencms.gwt.client.Messages.GUI_LOCK_OWNED_BY_1,
905                entry.getLock().getLockOwner());
906        }
907
908        getListItemWidget().setLockIcon(icon, iconTitle);
909    }
910}