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;
029
030import org.opencms.ade.containerpage.client.CmsContainerpageController;
031import org.opencms.ade.containerpage.client.CmsContainerpageHandler;
032import org.opencms.ade.containerpage.shared.CmsContainerElement;
033import org.opencms.gwt.client.ui.I_CmsButton;
034import org.opencms.gwt.shared.CmsGwtConstants;
035
036import com.google.gwt.event.dom.client.ClickEvent;
037
038import elemental2.dom.Element;
039import elemental2.dom.NodeList;
040import jsinterop.base.Js;
041import jsinterop.base.JsPropertyMap;
042
043/**
044 * Button to open the dialog for adding list elements
045 */
046public class CmsToolbarListAddButton extends A_CmsToolbarOptionButton {
047
048    /**
049     * Constructor.<p>
050     *
051     * @param handler the container-page handler
052     */
053    public CmsToolbarListAddButton(CmsContainerpageHandler handler) {
054
055        super(I_CmsButton.ButtonData.ADD, handler);
056    }
057
058    /**
059     * @see org.opencms.ade.containerpage.client.ui.A_CmsToolbarOptionButton#isOptionAvailable(org.opencms.ade.containerpage.client.ui.CmsContainerPageElementPanel)
060     */
061    @Override
062    public boolean isOptionAvailable(CmsContainerPageElementPanel element) {
063
064        return !CmsContainerpageController.get().isEditingDisabled() && (getListAddInfo(element) != null);
065    }
066
067    /**
068     * @see org.opencms.ade.containerpage.client.ui.A_CmsToolbarOptionButton#onElementClick(com.google.gwt.event.dom.client.ClickEvent, org.opencms.ade.containerpage.client.ui.CmsContainerPageElementPanel)
069     */
070    @Override
071    public void onElementClick(ClickEvent event, CmsContainerPageElementPanel containerElement) {
072
073        String listAddInfo = getListAddInfo(containerElement);
074        m_handler.openListAddDialog(containerElement.getStructureId(), listAddInfo);
075    }
076
077    /**
078     * Validates that a list creation metadata element directly belongs to a given container element and not to some other element in a nested container.
079     *
080     * @param containerElement the container element widget
081     * @param dataElement the list data element
082     *
083     * @return true if the data element really belongs directly to the container element
084     */
085    private boolean checkIfListDataBelongsToContainerElement(
086        CmsContainerPageElementPanel containerElement,
087        Element dataElement) {
088
089        // note: this does not catch model groups, but we handle that case elsewhere
090
091        Element currentElement = dataElement;
092        String elementObjectId = null;
093        while (currentElement != null) {
094            if (currentElement.classList.contains(CmsContainerElement.CLASS_CONTAINER)) {
095                return false;
096            }
097            JsPropertyMap props = Js.cast(currentElement);
098            elementObjectId = (String)(props.get(CmsContainerPageElementPanel.PROP_ELEMENT_OBJECT_ID));
099            if (elementObjectId != null) {
100                break;
101            }
102            currentElement = currentElement.parentElement;
103        }
104        boolean result = containerElement.getObjectId().equals(elementObjectId);
105        return result;
106
107    }
108
109    /**
110     * Finds an element with the data-oc-listadd attribute in the container element and return the attribute's content,
111     * or null if no element with that attribute exists in the content element.
112     *
113     * @param containerElement the container element
114     * @return the information used by the dialog for adding new list elements
115     */
116    private String getListAddInfo(CmsContainerPageElementPanel containerElement) {
117
118        Element element = Js.cast(containerElement.getElement());
119        if (containerElement.isModelGroup()) {
120            // in principle, the list element adding mechanism should also work when used inside a model group,
121            // but this could cause ambiguities/conflicts (multiple lists in a model group or similar cases).
122            return null;
123        }
124        // first try to find a sub-element with the necessary metadata in the DOM
125        NodeList<Element> dataElements = element.querySelectorAll("[" + CmsGwtConstants.ATTR_DATA_LISTADD + "]");
126        for (int i = 0; i < dataElements.getLength(); i++) {
127            Element dataElement = dataElements.item(i);
128            // just having an element with the data is not enough, we have to check if it's really part of *this*
129            // container element, not just from an element of a nested container
130            if (checkIfListDataBelongsToContainerElement(containerElement, dataElement)) {
131                // we do not currently support more than one list-add configuration - just return the first one
132                return dataElement.getAttribute(CmsGwtConstants.ATTR_DATA_LISTADD);
133            }
134        }
135        return null;
136    }
137}