/*
* This library is part of OpenCms -
* the Open Source Content Management System
*
* Copyright (c) Alkacon Software GmbH (http://www.alkacon.com)
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* For further information about Alkacon Software, please see the
* company website: http://www.alkacon.com
*
* For further information about OpenCms, please see the
* project website: http://www.opencms.org
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
package org.opencms.ade.containerpage.client.ui;
import org.opencms.ade.containerpage.client.CmsContainerpageController;
import org.opencms.ade.containerpage.client.ui.css.I_CmsLayoutBundle;
import org.opencms.gwt.client.dnd.I_CmsDraggable;
import org.opencms.gwt.client.dnd.I_CmsDropTarget;
import org.opencms.gwt.client.ui.CmsHighlightingBorder;
import org.opencms.gwt.client.util.CmsDomUtil;
import org.opencms.gwt.client.util.CmsDomUtil.Tag;
import org.opencms.gwt.client.util.CmsPositionBean;
import org.opencms.util.CmsUUID;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import com.google.gwt.core.client.JavaScriptObject;
import com.google.gwt.dom.client.Element;
import com.google.gwt.dom.client.NodeList;
import com.google.gwt.dom.client.Style;
import com.google.gwt.dom.client.Style.Display;
import com.google.gwt.dom.client.Style.Position;
import com.google.gwt.dom.client.Style.Unit;
import com.google.gwt.user.client.DOM;
import com.google.gwt.user.client.Timer;
import com.google.gwt.user.client.ui.AbsolutePanel;
import com.google.gwt.user.client.ui.RootPanel;
/**
* Content element within a container-page.<p>
*
* @since 8.0.0
*/
public class CmsContainerPageElementPanel extends AbsolutePanel implements I_CmsDraggable {
/** The height necessary for a container page element. */
public static int NECESSARY_HEIGHT = 24;
/** Highlighting border for this element. */
protected CmsHighlightingBorder m_highlighting;
private boolean m_checkingEditables;
/** The elements client id. */
private String m_clientId;
/** The direct edit bar instances. */
private Map<Element, CmsListCollectorEditor> m_editables;
/** The option bar, holding optional function buttons. */
private CmsElementOptionBar m_elementOptionBar;
/** The overlay for expired elements. */
private Element m_expiredOverlay;
/** Indicates whether this element has settings to edit. */
private boolean m_hasSettings;
/** The is new element type. */
private String m_newType;
/** The registered node insert event handler. */
private JavaScriptObject m_nodeInsertHandler;
/** The no edit reason, if empty editing is allowed. */
private String m_noEditReason;
/** The parent drop target. */
private I_CmsDropContainer m_parent;
/** Flag indicating if the element resource is currently released and not expired. */
private boolean m_releasedAndNotExpired;
/** The element resource site-path. */
private String m_sitePath;
/**
* Indicates if the current user has view permissions on the element resource.
* Without view permissions, the element can neither be edited, nor moved.
**/
private boolean m_viewPermission;
/**
* Constructor.<p>
*
* @param element the DOM element
* @param parent the drag parent
* @param clientId the client id
* @param sitePath the element site-path
* @param noEditReason the no edit reason, if empty, editing is allowed
* @param hasSettings should be true if the element has settings which can be edited
* @param hasViewPermission indicates if the current user has view permissions on the element resource
* @param releasedAndNotExpired <code>true</code> if the element resource is currently released and not expired
*/
public CmsContainerPageElementPanel(
Element element,
I_CmsDropContainer parent,
String clientId,
String sitePath,
String noEditReason,
boolean hasSettings,
boolean hasViewPermission,
boolean releasedAndNotExpired) {
super((com.google.gwt.user.client.Element)element);
m_clientId = clientId;
m_sitePath = sitePath;
m_noEditReason = noEditReason;
m_hasSettings = hasSettings;
m_parent = parent;
setViewPermission(hasViewPermission);
setReleasedAndNotExpired(releasedAndNotExpired);
getElement().addClassName(I_CmsLayoutBundle.INSTANCE.dragdropCss().dragElement());
}
/**
* Make sure that the element has at least a certain minimum height so that option bars of subsequent elements
* in a container don't overlap.<p>
*/
public void applyMinHeight() {
if (isAttached()) {
if (getOffsetHeight() < NECESSARY_HEIGHT) {
getElement().getStyle().setProperty("minHeight", NECESSARY_HEIGHT + "px");
}
}
}
/**
* @see org.opencms.gwt.client.dnd.I_CmsDraggable#getDragHelper(org.opencms.gwt.client.dnd.I_CmsDropTarget)
*/
public Element getDragHelper(I_CmsDropTarget target) {
Element helper = CmsDomUtil.clone(getElement());
target.getElement().appendChild(helper);
// preparing helper styles
String width = CmsDomUtil.getCurrentStyle(helper, CmsDomUtil.Style.width);
Style style = helper.getStyle();
style.setPosition(Position.ABSOLUTE);
style.setMargin(0, Unit.PX);
style.setProperty(CmsDomUtil.Style.width.name(), width);
style.setZIndex(I_CmsLayoutBundle.INSTANCE.constants().css().zIndexDND());
helper.addClassName(I_CmsLayoutBundle.INSTANCE.dragdropCss().dragging());
helper.addClassName(org.opencms.gwt.client.ui.css.I_CmsLayoutBundle.INSTANCE.generalCss().shadow());
if (!CmsDomUtil.hasBackground(helper)) {
helper.addClassName(I_CmsLayoutBundle.INSTANCE.dragdropCss().dragElementBackground());
}
if (!CmsDomUtil.hasBorder(helper)) {
helper.addClassName(I_CmsLayoutBundle.INSTANCE.dragdropCss().dragElementBorder());
}
return helper;
}
/**
* Returns the option bar of this element.<p>
*
* @return the option bar widget
*/
public CmsElementOptionBar getElementOptionBar() {
return m_elementOptionBar;
}
/**
* @see org.opencms.gwt.client.dnd.I_CmsDraggable#getId()
*/
public String getId() {
return m_clientId;
}
/**
* Returns the new element type.
*
* @return the new element type
*/
public String getNewType() {
return m_newType;
}
/**
* Returns the no edit reason.<p>
*
* @return the no edit reason
*/
public String getNoEditReason() {
return m_noEditReason;
}
/**
* @see org.opencms.gwt.client.dnd.I_CmsDraggable#getParentTarget()
*/
public I_CmsDropContainer getParentTarget() {
return m_parent;
}
/**
* @see org.opencms.gwt.client.dnd.I_CmsDraggable#getPlaceholder(org.opencms.gwt.client.dnd.I_CmsDropTarget)
*/
public Element getPlaceholder(I_CmsDropTarget target) {
Element placeholder = CmsDomUtil.clone(getElement());
placeholder.addClassName(I_CmsLayoutBundle.INSTANCE.dragdropCss().dragPlaceholder());
return placeholder;
}
/**
* Returns if the element resource is currently released and not expired.<p>
*
* @return <code>true</code> if the element resource is currently released and not expired
*/
public boolean getReleasedAndNotExpired() {
return m_releasedAndNotExpired;
}
/**
* Returns the site-path.<p>
*
* @return the site-path
*/
public String getSitePath() {
return m_sitePath;
}
/**
* Returns the structure id of the element.<p>
*
* @return the structure id of the element
*/
public CmsUUID getStructureId() {
if (m_clientId == null) {
return null;
}
return new CmsUUID(CmsContainerpageController.getServerId(m_clientId));
}
/**
* Returns true if the element has settings to edit.<p>
*
* @return true if the element has settings to edit
*/
public boolean hasSettings() {
return m_hasSettings;
}
/**
* @see org.opencms.gwt.client.dnd.I_CmsDraggable#hasTag(java.lang.String)
*/
public boolean hasTag(String tag) {
return false;
}
/**
* Returns if the current user has view permissions for the element resource.<p>
*
* @return <code>true</code> if the current user has view permissions for the element resource
*/
public boolean hasViewPermission() {
return m_viewPermission;
}
/**
* Hides list collector direct edit buttons, if present.<p>
*/
public void hideEditableListButtons() {
if (m_editables != null) {
for (CmsListCollectorEditor editor : m_editables.values()) {
editor.getElement().getStyle().setDisplay(Display.NONE);
}
}
}
/**
* Puts a highlighting border around the element.<p>
*/
public void highlightElement() {
if (m_highlighting == null) {
m_highlighting = new CmsHighlightingBorder(CmsPositionBean.generatePositionInfo(this), isNew()
? CmsHighlightingBorder.BorderColor.blue
: CmsHighlightingBorder.BorderColor.red);
RootPanel.get().add(m_highlighting);
} else {
m_highlighting.setPosition(CmsPositionBean.generatePositionInfo(this));
}
}
/**
* Returns if this is e newly created element.<p>
*
* @return <code>true</code> if the element is new
*/
public boolean isNew() {
return m_newType != null;
}
/**
* @see org.opencms.gwt.client.dnd.I_CmsDraggable#onDragCancel()
*/
public void onDragCancel() {
clearDrag();
resetOptionbar();
}
/**
* @see org.opencms.gwt.client.dnd.I_CmsDraggable#onDrop(org.opencms.gwt.client.dnd.I_CmsDropTarget)
*/
public void onDrop(I_CmsDropTarget target) {
clearDrag();
}
/**
* @see org.opencms.gwt.client.dnd.I_CmsDraggable#onStartDrag(org.opencms.gwt.client.dnd.I_CmsDropTarget)
*/
public void onStartDrag(I_CmsDropTarget target) {
CmsDomUtil.addDisablingOverlay(getElement());
getElement().getStyle().setOpacity(0.5);
removeHighlighting();
}
/**
* @see com.google.gwt.user.client.ui.Widget#removeFromParent()
*/
@Override
public void removeFromParent() {
removeHighlighting();
super.removeFromParent();
}
/**
* Removes the highlighting border.<p>
*/
public void removeHighlighting() {
if (m_highlighting != null) {
m_highlighting.removeFromParent();
m_highlighting = null;
}
}
/**
* Sets the elementOptionBar.<p>
*
* @param elementOptionBar the elementOptionBar to set
*/
public void setElementOptionBar(CmsElementOptionBar elementOptionBar) {
if ((m_elementOptionBar != null) && (getWidgetIndex(m_elementOptionBar) >= 0)) {
m_elementOptionBar.removeFromParent();
}
m_elementOptionBar = elementOptionBar;
insert(m_elementOptionBar, 0);
if (isOptionbarIFrameCollision()) {
m_elementOptionBar.getElement().getStyle().setPosition(Position.RELATIVE);
int marginLeft = getElement().getOffsetWidth() - m_elementOptionBar.getCalculatedWidth();
m_elementOptionBar.getElement().getStyle().setMarginLeft(marginLeft, Unit.PX);
}
}
/**
* Sets the element id.<p>
*
* @param id the id
*/
public void setId(String id) {
m_clientId = id;
}
/**
* Sets the new-type of the element.<p>
*
* @param newType the new-type
*/
public void setNewType(String newType) {
m_newType = newType;
}
/**
* Sets the no edit reason.<p>
*
* @param noEditReason the no edit reason to set
*/
public void setNoEditReason(String noEditReason) {
m_noEditReason = noEditReason;
}
/**
* Sets if the element resource is currently released and not expired.<p>
*
* @param releasedAndNotExpired <code>true</code> if the element resource is currently released and not expired
*/
public void setReleasedAndNotExpired(boolean releasedAndNotExpired) {
m_releasedAndNotExpired = releasedAndNotExpired;
if (m_releasedAndNotExpired) {
getElement().removeClassName(I_CmsLayoutBundle.INSTANCE.containerpageCss().expired());
if (m_expiredOverlay != null) {
m_expiredOverlay.removeFromParent();
m_expiredOverlay = null;
}
} else {
getElement().addClassName(I_CmsLayoutBundle.INSTANCE.containerpageCss().expired());
m_expiredOverlay = DOM.createDiv();
m_expiredOverlay.setTitle("Expired resource");
m_expiredOverlay.addClassName(I_CmsLayoutBundle.INSTANCE.containerpageCss().expiredOverlay());
getElement().appendChild(m_expiredOverlay);
}
}
/**
* Sets the site path.<p>
*
* @param sitePath the site path to set
*/
public void setSitePath(String sitePath) {
m_sitePath = sitePath;
}
/**
* Sets if the current user has view permissions for the element resource.<p>
*
* @param viewPermission the view permission to set
*/
public void setViewPermission(boolean viewPermission) {
m_viewPermission = viewPermission;
}
/**
* Shows list collector direct edit buttons (old direct edit style), if present.<p>
*/
public void showEditableListButtons() {
m_checkingEditables = true;
if (m_editables == null) {
m_editables = new HashMap<Element, CmsListCollectorEditor>();
List<Element> editables = CmsDomUtil.getElementsByClass("cms-editable", Tag.div, getElement());
if ((editables != null) && (editables.size() > 0)) {
for (Element editable : editables) {
CmsListCollectorEditor editor = new CmsListCollectorEditor(editable, m_clientId);
add(editor, (com.google.gwt.user.client.Element)editable.getParentElement());
if (CmsDomUtil.hasDimension(editable.getParentElement())) {
editor.setPosition(CmsDomUtil.getEditablePosition(editable), getElement());
} else {
editor.getElement().getStyle().setDisplay(Display.NONE);
}
m_editables.put(editable, editor);
}
}
} else {
Iterator<Entry<Element, CmsListCollectorEditor>> it = m_editables.entrySet().iterator();
while (it.hasNext()) {
Entry<Element, CmsListCollectorEditor> entry = it.next();
if (!entry.getValue().isValid()) {
entry.getValue().removeFromParent();
it.remove();
} else if (CmsDomUtil.hasDimension(entry.getValue().getElement().getParentElement())) {
entry.getValue().getElement().getStyle().clearDisplay();
entry.getValue().setPosition(
CmsDomUtil.getEditablePosition(entry.getValue().getMarkerTag()),
getElement());
}
}
List<Element> editables = CmsDomUtil.getElementsByClass("cms-editable", Tag.div, getElement());
if (editables.size() > m_editables.size()) {
for (Element editable : editables) {
if (!m_editables.containsKey(editable)) {
CmsListCollectorEditor editor = new CmsListCollectorEditor(editable, m_clientId);
add(editor, (com.google.gwt.user.client.Element)editable.getParentElement());
if (CmsDomUtil.hasDimension(editable.getParentElement())) {
editor.setPosition(CmsDomUtil.getEditablePosition(editable), getElement());
} else {
editor.getElement().getStyle().setDisplay(Display.NONE);
}
m_editables.put(editable, editor);
}
}
}
}
m_checkingEditables = false;
resetNodeInsertedHandler();
}
/**
* Perform layout corrections.<p>
*
* This method will be called in regular intervals by the containerpage editor.<p>
*/
public void update() {
applyMinHeight();
}
/**
* Checks for changes in the list collector direct edit content.<p>
*/
protected void checkForEditableChanges() {
if (!m_checkingEditables) {
m_checkingEditables = true;
Timer timer = new Timer() {
@Override
public void run() {
showEditableListButtons();
}
};
timer.schedule(500);
}
}
/**
* Returns if the list collector direct edit content has changed.<p>
*
* @return <code>true</code> if the list collector direct edit content has changed
*/
protected boolean hasChangedEditables() {
if (m_editables == null) {
return true;
}
for (CmsListCollectorEditor editor : m_editables.values()) {
if (!editor.isValid()) {
return true;
}
}
return CmsDomUtil.getElementsByClass("cms-editable", Tag.div, getElement()).size() > m_editables.size();
}
/**
* @see com.google.gwt.user.client.ui.Widget#onAttach()
*/
@Override
protected void onAttach() {
super.onAttach();
resetOptionbar();
}
/**
* Removes all styling done during drag and drop.<p>
*/
private void clearDrag() {
CmsDomUtil.removeDisablingOverlay(getElement());
m_elementOptionBar.getElement().removeClassName(
org.opencms.gwt.client.ui.css.I_CmsLayoutBundle.INSTANCE.stateCss().cmsHovering());
// using own implementation as GWT won't do it properly on IE7-8
CmsDomUtil.clearOpacity(getElement());
getElement().getStyle().clearDisplay();
}
/**
* Returns if the option bar position collides with any iframe child elements.<p>
*
* @return <code>true</code> if there are iframe child elements located no less than 25px below the upper edge of the element
*/
private boolean isOptionbarIFrameCollision() {
if (RootPanel.getBodyElement().isOrHasChild(getElement())) {
int elementTop = getElement().getAbsoluteTop();
NodeList<Element> frames = getElement().getElementsByTagName(CmsDomUtil.Tag.iframe.name());
for (int i = 0; i < frames.getLength(); i++) {
if ((frames.getItem(i).getAbsoluteTop() - elementTop) < 25) {
return true;
}
}
}
return false;
}
private native void resetNodeInsertedHandler()/*-{
var $this = this;
var element = $this.@org.opencms.ade.containerpage.client.ui.CmsContainerPageElementPanel::getElement()();
var handler = $this.@org.opencms.ade.containerpage.client.ui.CmsContainerPageElementPanel::m_nodeInsertHandler;
if (handler == null) {
handler = function(event) {
$this.@org.opencms.ade.containerpage.client.ui.CmsContainerPageElementPanel::checkForEditableChanges()();
};
$this.@org.opencms.ade.containerpage.client.ui.CmsContainerPageElementPanel::m_nodeInsertHandler = handler;
} else {
if (element.removeEventLister) {
element.removeEventListener("DOMNodeInserted", handler);
} else if (element.detachEvent) {
// IE specific
element.detachEvent("onDOMNodeInserted", handler);
}
}
if (element.addEventListener) {
element.addEventListener("DOMNodeInserted", handler, false);
} else if (element.attachEvent) {
// IE specific
element.attachEvent("onDOMNodeInserted", handler);
}
}-*/;
/**
* This method removes the option-bar widget from DOM and re-attaches it at it's original position.<p>
* Use to avoid mouse-over and mouse-down malfunction.<p>
*/
private void resetOptionbar() {
if (m_elementOptionBar != null) {
if (getWidgetIndex(m_elementOptionBar) >= 0) {
m_elementOptionBar.removeFromParent();
}
if (isOptionbarIFrameCollision()) {
m_elementOptionBar.getElement().getStyle().setPosition(Position.RELATIVE);
int marginLeft = getElement().getClientWidth() - m_elementOptionBar.getCalculatedWidth();
if (marginLeft > 0) {
m_elementOptionBar.getElement().getStyle().setMarginLeft(marginLeft, Unit.PX);
}
} else {
m_elementOptionBar.getElement().getStyle().clearPosition();
m_elementOptionBar.getElement().getStyle().clearMarginLeft();
}
insert(m_elementOptionBar, 0);
}
}
}