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.containerpage.client.ui.groupeditor;
029
030import org.opencms.ade.containerpage.client.CmsContainerpageController;
031import org.opencms.ade.containerpage.client.CmsContainerpageController.ElementRemoveMode;
032import org.opencms.ade.containerpage.client.CmsContainerpageHandler;
033import org.opencms.ade.containerpage.client.CmsContainerpageUtil;
034import org.opencms.ade.containerpage.client.Messages;
035import org.opencms.ade.containerpage.client.ui.CmsContainerPageElementPanel;
036import org.opencms.ade.containerpage.client.ui.CmsElementOptionBar;
037import org.opencms.ade.containerpage.client.ui.CmsGroupContainerElementPanel;
038import org.opencms.ade.containerpage.client.ui.css.I_CmsLayoutBundle;
039import org.opencms.ade.containerpage.shared.CmsContainerElement;
040import org.opencms.ade.containerpage.shared.CmsContainerElementData;
041import org.opencms.ade.containerpage.shared.CmsInheritanceContainer;
042import org.opencms.ade.containerpage.shared.CmsInheritanceInfo;
043import org.opencms.gwt.client.ui.CmsPushButton;
044import org.opencms.gwt.client.ui.CmsToggleButton;
045import org.opencms.gwt.client.ui.I_CmsButton.ButtonColor;
046import org.opencms.gwt.client.ui.I_CmsButton.ButtonStyle;
047import org.opencms.gwt.client.ui.input.CmsTextBox;
048import org.opencms.gwt.client.util.CmsDebugLog;
049import org.opencms.gwt.client.util.CmsDomUtil;
050import org.opencms.gwt.client.util.CmsDomUtil.Tag;
051import org.opencms.gwt.client.util.I_CmsSimpleCallback;
052import org.opencms.util.CmsUUID;
053
054import java.util.ArrayList;
055import java.util.Iterator;
056import java.util.List;
057import java.util.Map;
058
059import com.google.gwt.dom.client.Element;
060import com.google.gwt.dom.client.Style.Display;
061import com.google.gwt.dom.client.Style.Float;
062import com.google.gwt.dom.client.Style.Unit;
063import com.google.gwt.event.dom.client.ClickEvent;
064import com.google.gwt.event.dom.client.ClickHandler;
065import com.google.gwt.user.client.DOM;
066import com.google.gwt.user.client.ui.RootPanel;
067import com.google.gwt.user.client.ui.Widget;
068import com.google.web.bindery.event.shared.HandlerRegistration;
069
070/**
071 * The inheritance container editor.<p>
072 *
073 * @since 8.5.0
074 */
075public class CmsInheritanceContainerEditor extends A_CmsGroupEditor {
076
077    /** Css class to mark hidden elements. */
078    private static final String HIDDEN_ELEMENT_CLASS = I_CmsLayoutBundle.INSTANCE.containerpageCss().hiddenElement();
079
080    /** Css class to hide elements. */
081    private static final String HIDDEN_ELEMENT_OVERLAY_CLASS = I_CmsLayoutBundle.INSTANCE.containerpageCss().hiddenElementOverlay();
082
083    /** Css class to hide elements. */
084    private static final String HIDE_ELEMENTS_CLASS = I_CmsLayoutBundle.INSTANCE.containerpageCss().hideElements();
085
086    /** The editor instance. */
087    private static CmsInheritanceContainerEditor INSTANCE;
088
089    /** A flag which indicates whether the inheritance configuration needs to be updated. */
090    private boolean m_changedInheritanceInfo;
091
092    /** Flag which indicates whether the settings of an inheritance group element have been edited. */
093    private boolean m_editedSettings;
094
095    /** The description input. */
096    private CmsTextBox m_inputDescription;
097
098    /** The title input. */
099    private CmsTextBox m_inputTitle;
100
101    /** A handler which keeps track of whether elements have been dropped. */
102    private CmsDropListener m_moveHandler;
103
104    /** The handler registration to remove the drop handler. */
105    private HandlerRegistration m_moveHandlerRegistration;
106
107    /** Click handler for the option buttons. */
108    private ClickHandler m_optionClickHandler;
109
110    /** The show removed elements button. */
111    private CmsToggleButton m_showElementsButton;
112
113    /**
114     * Constructor.<p>
115     *
116     * @param groupContainer the group container widget
117     * @param controller the container page controller
118     * @param handler the container page handler
119     */
120    protected CmsInheritanceContainerEditor(
121        CmsGroupContainerElementPanel groupContainer,
122        CmsContainerpageController controller,
123        CmsContainerpageHandler handler) {
124
125        super(groupContainer, controller, handler);
126        m_optionClickHandler = new ClickHandler() {
127
128            public void onClick(ClickEvent event) {
129
130                I_CmsGroupEditorOption optionButton = (I_CmsGroupEditorOption)event.getSource();
131                ((CmsPushButton)optionButton).clearHoverState();
132                CmsDomUtil.ensureMouseOut(((CmsPushButton)optionButton).getElement().getParentElement());
133                optionButton.onClick(event);
134            }
135        };
136        // Loading data of all contained elements including inherit container element
137        getController().getElements(getElementIds(), new I_CmsSimpleCallback<Map<String, CmsContainerElementData>>() {
138
139            public void execute(Map<String, CmsContainerElementData> arg) {
140
141                setInheritContainerData(arg);
142            }
143        });
144        getGroupContainerWidget().addStyleName(HIDE_ELEMENTS_CLASS);
145        m_moveHandler = new CmsDropListener();
146        m_moveHandlerRegistration = getController().getDndController().addController(m_moveHandler);
147
148    }
149
150    /**
151     * Returns the inheritance container editor instance.<p>
152     *
153     * @return the editor instance
154     */
155    public static CmsInheritanceContainerEditor getInstance() {
156
157        return INSTANCE;
158    }
159
160    /**
161     * Opens the inheritance container editor.<p>
162     *
163     * @param groupContainer the group-container
164     * @param controller the container-page controller
165     * @param handler the container-page handler
166     *
167     * @return the editor instance
168     */
169    public static CmsInheritanceContainerEditor openInheritanceContainerEditor(
170        CmsGroupContainerElementPanel groupContainer,
171        CmsContainerpageController controller,
172        CmsContainerpageHandler handler) {
173
174        // making sure only a single instance of the group-container editor is open
175        if (INSTANCE != null) {
176            CmsDebugLog.getInstance().printLine("group-container editor already open");
177        } else {
178            CmsInheritanceContainerEditor editor = new CmsInheritanceContainerEditor(
179                groupContainer,
180                controller,
181                handler);
182            RootPanel.get().add(editor);
183            editor.openDialog(Messages.get().key(Messages.GUI_INHERITANCECONTAINER_CAPTION_0));
184            groupContainer.refreshHighlighting();
185            INSTANCE = editor;
186        }
187        return INSTANCE;
188    }
189
190    /**
191     * Clears the instance reference.<p>
192     */
193    private static void clear() {
194
195        INSTANCE = null;
196    }
197
198    /**
199     * Method which should be called after the settings of an element in the inheritance containerhave been edited.<p>
200     */
201    public void onSettingsEdited() {
202
203        m_editedSettings = true;
204    }
205
206    /**
207     * @see org.opencms.ade.containerpage.client.ui.groupeditor.A_CmsGroupEditor#reinitializeButtons()
208     */
209    @Override
210    public void reinitializeButtons() {
211
212        // nothing to do, will be handled else where
213    }
214
215    /**
216     * Either removes the locally configured element or hides the inherited element.<p>
217     *
218     * @param elementWidget the element widget
219     */
220    public void removeElement(CmsContainerPageElementPanel elementWidget) {
221
222        if (elementWidget.getInheritanceInfo().isNew()) {
223            getHandler().removeElement(elementWidget, ElementRemoveMode.saveAndCheckReferences);
224        } else {
225            elementWidget.getInheritanceInfo().setVisible(false);
226            elementWidget.addStyleName(HIDDEN_ELEMENT_CLASS);
227            Element elementOverlay = DOM.createDiv();
228            elementOverlay.setClassName(HIDDEN_ELEMENT_OVERLAY_CLASS);
229            elementWidget.getElement().appendChild(elementOverlay);
230            getGroupContainerWidget().add(elementWidget);
231            updateButtonVisibility(elementWidget);
232            m_showElementsButton.enable();
233            getGroupContainerWidget().refreshHighlighting();
234        }
235        m_changedInheritanceInfo = true;
236    }
237
238    /**
239     * Sets the option bar on the element widget.<p>
240     *
241     * @param elementWidget the element widget
242     */
243    public void setOptionBar(CmsContainerPageElementPanel elementWidget) {
244
245        if (elementWidget.hasViewPermission()) {
246            elementWidget.setElementOptionBar(createOptionBar(elementWidget));
247            updateButtonVisibility(elementWidget);
248        } else {
249            elementWidget.setElementOptionBar(null);
250        }
251    }
252
253    /**
254     * Shows a formerly hidden element and sets the visibility info to true.<p>
255     *
256     * @param elementWidget the element widget
257     */
258    public void showElement(CmsContainerPageElementPanel elementWidget) {
259
260        int index = 0;
261        for (index = 0; index < getGroupContainerWidget().getWidgetCount(); index++) {
262            if (CmsDomUtil.hasClass(HIDDEN_ELEMENT_CLASS, getGroupContainerWidget().getWidget(index).getElement())) {
263                break;
264            }
265        }
266        getGroupContainerWidget().insert(elementWidget, index);
267        elementWidget.getInheritanceInfo().setVisible(true);
268        elementWidget.removeStyleName(HIDDEN_ELEMENT_CLASS);
269        updateButtonVisibility(elementWidget);
270        getGroupContainerWidget().refreshHighlighting();
271        List<Element> elements = CmsDomUtil.getElementsByClass(
272            HIDDEN_ELEMENT_OVERLAY_CLASS,
273            Tag.div,
274            elementWidget.getElement());
275        for (Element element : elements) {
276            element.removeFromParent();
277        }
278        if (CmsDomUtil.getElementsByClass(
279            HIDDEN_ELEMENT_OVERLAY_CLASS,
280            Tag.div,
281            getGroupContainerWidget().getElement()).isEmpty()) {
282            // if no other hidden elements present disable toggle button
283            m_showElementsButton.disable(Messages.get().key(Messages.GUI_INHERITANCECONTAINER_NO_HIDDEN_ELEMENTS_0));
284        }
285        m_changedInheritanceInfo = true;
286    }
287
288    /**
289     * @see org.opencms.ade.containerpage.client.ui.groupeditor.A_CmsGroupEditor#addButtons()
290     */
291    @Override
292    protected void addButtons() {
293
294        addCancelButton();
295        addSaveButton();
296        m_showElementsButton = new CmsToggleButton();
297        m_showElementsButton.setText(Messages.get().key(Messages.GUI_INHERITANCECONTAINER_SHOW_HIDDEN_0));
298        m_showElementsButton.setDownFace(Messages.get().key(Messages.GUI_INHERITANCECONTAINER_HIDE_ELEMENTS_0), null);
299        m_showElementsButton.setUseMinWidth(true);
300        m_showElementsButton.setButtonStyle(ButtonStyle.TEXT, ButtonColor.RED);
301        m_showElementsButton.addClickHandler(new ClickHandler() {
302
303            public void onClick(ClickEvent event) {
304
305                toggleElementVisibility();
306            }
307        });
308        m_showElementsButton.disable(Messages.get().key(Messages.GUI_INHERITANCECONTAINER_NO_HIDDEN_ELEMENTS_0));
309        m_showElementsButton.getElement().getStyle().setFloat(Float.LEFT);
310        addButton(m_showElementsButton);
311        CmsPushButton breakUpButton = new CmsPushButton();
312        breakUpButton.setText(Messages.get().key(Messages.GUI_BUTTON_BREAK_UP_TEXT_0));
313        breakUpButton.setUseMinWidth(true);
314        breakUpButton.setButtonStyle(ButtonStyle.TEXT, ButtonColor.RED);
315        breakUpButton.addClickHandler(new ClickHandler() {
316
317            public void onClick(ClickEvent event) {
318
319                breakUpContainer();
320            }
321        });
322        breakUpButton.getElement().getStyle().setFloat(Float.LEFT);
323        breakUpButton.getElement().getStyle().setMarginLeft(0, Unit.PX);
324        addButton(breakUpButton);
325    }
326
327    /**
328     * @see org.opencms.ade.containerpage.client.ui.groupeditor.A_CmsGroupEditor#addInputFields()
329     */
330    @Override
331    protected void addInputFields() {
332
333        m_inputTitle = new CmsTextBox();
334        addInputField(Messages.get().key(Messages.GUI_GROUPCONTAINER_LABEL_TITLE_0), m_inputTitle);
335        m_inputDescription = new CmsTextBox();
336        addInputField(Messages.get().key(Messages.GUI_GROUPCONTAINER_LABEL_DESCRIPTION_0), m_inputDescription);
337    }
338
339    /**
340     * Breaks up the group container inserting all visible elements into the parent container instead.<p>
341     */
342    protected void breakUpContainer() {
343
344        List<String> clientIds = new ArrayList<String>();
345        for (Widget w : getGroupContainerWidget()) {
346            if (w instanceof CmsContainerPageElementPanel) {
347                CmsContainerPageElementPanel elementWidget = (CmsContainerPageElementPanel)w;
348                if ((elementWidget.getInheritanceInfo() == null) || elementWidget.getInheritanceInfo().isVisible()) {
349                    clientIds.add(elementWidget.getId());
350                }
351            }
352        }
353        int index = getIndexPosition();
354        for (String clientId : clientIds) {
355            try {
356                CmsContainerElementData elementData = getController().getCachedElement(clientId);
357                CmsContainerPageElementPanel containerElement = getController().getContainerpageUtil().createElement(
358                    elementData,
359                    getParentContainer(),
360                    false);
361                getParentContainer().insert(containerElement, index);
362                index++;
363            } catch (Exception e) {
364                CmsDebugLog.getInstance().printLine(e.getMessage());
365            }
366        }
367        getController().addToRecentList(getGroupContainerWidget().getId(), null);
368        getController().unlockResource(
369            new CmsUUID(CmsContainerpageController.getServerId(m_elementData.getClientId())));
370        closeDialog(true);
371        getController().setPageChanged();
372    }
373
374    /**
375     * @see org.opencms.ade.containerpage.client.ui.groupeditor.A_CmsGroupEditor#cancelEdit()
376     */
377    @Override
378    protected void cancelEdit() {
379
380        removeAllChildren();
381        for (CmsContainerPageElementPanel element : getBackUpElements()) {
382            getGroupContainerWidget().add(element);
383        }
384        if (getBackUpElements().size() == 0) {
385            getGroupContainerWidget().addStyleName(I_CmsLayoutBundle.INSTANCE.containerpageCss().emptyGroupContainer());
386        }
387        getController().unlockResource(
388            new CmsUUID(CmsContainerpageController.getServerId(m_elementData.getClientId())));
389        closeDialog(false);
390    }
391
392    /**
393     * @see org.opencms.ade.containerpage.client.ui.groupeditor.A_CmsGroupEditor#clearInstance()
394     */
395    @Override
396    protected void clearInstance() {
397
398        clear();
399    }
400
401    /**
402     * @see org.opencms.ade.containerpage.client.ui.groupeditor.A_CmsGroupEditor#closeDialog(boolean)
403     */
404    @Override
405    protected void closeDialog(boolean breakingUp) {
406
407        m_moveHandlerRegistration.removeHandler();
408        getGroupContainerWidget().removeStyleName(I_CmsLayoutBundle.INSTANCE.containerpageCss().hideElements());
409        super.closeDialog(breakingUp);
410    }
411
412    /**
413     * @see org.opencms.ade.containerpage.client.ui.groupeditor.A_CmsGroupEditor#saveEdit()
414     */
415    @Override
416    protected void saveEdit() {
417
418        List<CmsContainerElement> elements = new ArrayList<CmsContainerElement>();
419        boolean moved = m_moveHandler.isDropped();
420        m_changedInheritanceInfo |= moved;
421        m_changedInheritanceInfo |= m_editedSettings;
422        for (Widget widget : getGroupContainerWidget()) {
423            if (widget instanceof CmsContainerPageElementPanel) {
424                CmsContainerPageElementPanel elementWidget = (CmsContainerPageElementPanel)widget;
425                CmsInheritanceInfo inheritanceInfo = elementWidget.getInheritanceInfo();
426                if (inheritanceInfo == null) {
427                    inheritanceInfo = new CmsInheritanceInfo(null, true, true);
428                    m_changedInheritanceInfo = true;
429                }
430                CmsContainerElement element = getController().getCachedElement(elementWidget.getId()).copy();
431                element.setInheritanceInfo(inheritanceInfo);
432                elements.add(element);
433            }
434        }
435        CmsInheritanceContainer container = new CmsInheritanceContainer();
436        container.setElementsChanged(m_changedInheritanceInfo);
437        container.setElementsMoved(moved);
438        container.setNew(getGroupContainerWidget().isNew());
439        container.setClientId(getGroupContainerWidget().getId());
440        container.setTitle(m_inputTitle.getText());
441        container.setDescription(m_inputDescription.getText());
442        container.setName(m_elementData.getInheritanceName());
443        container.setElements(elements);
444        getController().saveInheritContainer(container, getGroupContainerWidget());
445        closeDialog(false);
446    }
447
448    /**
449     * Sets the loaded element data.<p>
450     *
451     * @param elementsData the elements data
452     */
453    protected void setInheritContainerData(Map<String, CmsContainerElementData> elementsData) {
454
455        m_elementData = elementsData.get(getGroupContainerWidget().getId());
456        if (m_elementData != null) {
457            m_inputDescription.setFormValueAsString(m_elementData.getDescription());
458            m_inputTitle.setFormValueAsString(m_elementData.getTitle());
459            removeAllChildren();
460            CmsContainerpageUtil util = getController().getContainerpageUtil();
461            for (CmsInheritanceInfo info : m_elementData.getInheritanceInfos()) {
462                if (info.isVisible()) {
463                    CmsContainerElementData element = getController().getCachedElement(info.getClientId());
464                    try {
465                        CmsContainerPageElementPanel elementWidget = util.createElement(
466                            element,
467                            getGroupContainerWidget(),
468                            false);
469                        elementWidget.setInheritanceInfo(info);
470                        setOptionBar(elementWidget);
471                        getGroupContainerWidget().add(elementWidget);
472                    } catch (Exception e) {
473                        CmsDebugLog.getInstance().printLine(e.getMessage());
474                    }
475                }
476            }
477            boolean hasInvisible = false;
478            for (CmsInheritanceInfo info : m_elementData.getInheritanceInfos()) {
479                if (!info.isVisible()) {
480                    CmsContainerElementData element = getController().getCachedElement(info.getClientId());
481                    try {
482                        CmsContainerPageElementPanel elementWidget = util.createElement(
483                            element,
484                            getGroupContainerWidget(),
485                            false);
486                        elementWidget.setInheritanceInfo(info);
487                        elementWidget.addStyleName(HIDDEN_ELEMENT_CLASS);
488                        setOptionBar(elementWidget);
489                        getGroupContainerWidget().add(elementWidget);
490                        Element elementOverlay = DOM.createDiv();
491                        elementOverlay.setClassName(HIDDEN_ELEMENT_OVERLAY_CLASS);
492                        elementWidget.getElement().appendChild(elementOverlay);
493                        hasInvisible = true;
494                    } catch (Exception e) {
495                        CmsDebugLog.getInstance().printLine(e.getMessage());
496                    }
497                }
498            }
499            if (hasInvisible) {
500                m_showElementsButton.enable();
501            }
502        }
503
504        getGroupContainerWidget().refreshHighlighting();
505        setSaveEnabled(true, null);
506    }
507
508    /**
509     * Toggles the visibility of hidden elements.<p>
510     */
511    protected void toggleElementVisibility() {
512
513        if (CmsDomUtil.hasClass(HIDE_ELEMENTS_CLASS, getGroupContainerWidget().getElement())) {
514            getGroupContainerWidget().removeStyleName(HIDE_ELEMENTS_CLASS);
515        } else {
516            getGroupContainerWidget().addStyleName(HIDE_ELEMENTS_CLASS);
517        }
518        getGroupContainerWidget().refreshHighlighting();
519    }
520
521    /**
522     * Creates an option bar for the given element.<p>
523     *
524     * @param elementWidget the element widget
525     *
526     * @return the option bar
527     */
528    private CmsElementOptionBar createOptionBar(CmsContainerPageElementPanel elementWidget) {
529
530        CmsElementOptionBar optionBar = new CmsElementOptionBar(elementWidget);
531        CmsPushButton button = new CmsRemoveOptionButton(elementWidget, this);
532        button.addClickHandler(m_optionClickHandler);
533        optionBar.add(button);
534
535        button = new CmsFavoritesOptionButton(elementWidget, this);
536        button.addClickHandler(m_optionClickHandler);
537        optionBar.add(button);
538
539        button = new CmsSettingsOptionButton(elementWidget, this);
540        button.addClickHandler(m_optionClickHandler);
541        optionBar.add(button);
542
543        button = new CmsInfoOptionButton(elementWidget, this);
544        button.addClickHandler(m_optionClickHandler);
545        optionBar.add(button);
546
547        button = new CmsAddOptionButton(elementWidget, this);
548        button.addClickHandler(m_optionClickHandler);
549        optionBar.add(button);
550
551        button = new CmsInheritedOptionButton(elementWidget, this);
552        optionBar.add(button);
553
554        button = new CmsMoveOptionButton(elementWidget, this);
555        // setting the drag and drop handler
556        button.addMouseDownHandler(getController().getDndHandler());
557        optionBar.add(button);
558
559        button = new CmsEditOptionButton(elementWidget, this);
560        button.addClickHandler(m_optionClickHandler);
561        optionBar.add(button);
562
563        return optionBar;
564    }
565
566    /**
567     * Updates the visibility of the option bar buttons of the given element.<p>
568     *
569     * @param elementWidget the element widget
570     */
571    private void updateButtonVisibility(CmsContainerPageElementPanel elementWidget) {
572
573        Iterator<Widget> it = elementWidget.getElementOptionBar().iterator();
574        while (it.hasNext()) {
575            Widget w = it.next();
576            if (w instanceof I_CmsGroupEditorOption) {
577                if (((I_CmsGroupEditorOption)w).checkVisibility()) {
578                    w.getElement().getStyle().clearDisplay();
579                } else {
580                    w.getElement().getStyle().setDisplay(Display.NONE);
581                }
582            }
583        }
584    }
585}