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; 029 030import org.opencms.ade.containerpage.client.ui.A_CmsToolbarOptionButton; 031import org.opencms.ade.containerpage.client.ui.CmsContainerPageContainer; 032import org.opencms.ade.containerpage.client.ui.CmsContainerPageElementPanel; 033import org.opencms.ade.containerpage.client.ui.CmsElementOptionBar; 034import org.opencms.ade.containerpage.client.ui.CmsGroupContainerElementPanel; 035import org.opencms.ade.containerpage.client.ui.CmsMenuListItem; 036import org.opencms.ade.containerpage.client.ui.I_CmsDropContainer; 037import org.opencms.ade.containerpage.client.ui.css.I_CmsLayoutBundle; 038import org.opencms.ade.containerpage.shared.CmsContainer; 039import org.opencms.ade.containerpage.shared.CmsContainerElement; 040import org.opencms.ade.containerpage.shared.CmsContainerElementData; 041import org.opencms.ade.contenteditor.client.CmsContentEditor; 042import org.opencms.gwt.client.CmsCoreProvider; 043import org.opencms.gwt.client.CmsEditableData; 044import org.opencms.gwt.client.ui.CmsErrorDialog; 045import org.opencms.gwt.client.ui.CmsPushButton; 046import org.opencms.gwt.client.ui.contextmenu.CmsContextMenuButton; 047import org.opencms.gwt.client.ui.contextmenu.CmsContextMenuHandler; 048import org.opencms.gwt.client.util.CmsDebugLog; 049import org.opencms.gwt.client.util.CmsDomUtil; 050import org.opencms.gwt.shared.CmsCoreData.AdeContext; 051import org.opencms.gwt.shared.CmsGwtConstants; 052import org.opencms.gwt.shared.CmsTemplateContextInfo; 053import org.opencms.util.CmsStringUtil; 054import org.opencms.util.CmsUUID; 055 056import java.util.ArrayList; 057import java.util.HashMap; 058import java.util.Iterator; 059import java.util.List; 060import java.util.Map; 061 062import com.google.common.collect.Lists; 063import com.google.gwt.dom.client.Element; 064import com.google.gwt.dom.client.Node; 065import com.google.gwt.dom.client.Style; 066import com.google.gwt.event.dom.client.ClickEvent; 067import com.google.gwt.event.dom.client.ClickHandler; 068import com.google.gwt.user.client.DOM; 069import com.google.gwt.user.client.Window; 070 071/** 072 * Utility class for the container-page editor.<p> 073 * 074 * @since 8.0.0 075 */ 076public class CmsContainerpageUtil { 077 078 /** The container page controller. */ 079 private CmsContainerpageController m_controller; 080 081 /** List of buttons of the tool-bar. */ 082 private A_CmsToolbarOptionButton[] m_optionButtons; 083 084 /** 085 * Constructor.<p> 086 * 087 * @param controller the container page controller 088 * @param optionButtons the tool-bar option buttons 089 */ 090 public CmsContainerpageUtil(CmsContainerpageController controller, A_CmsToolbarOptionButton... optionButtons) { 091 092 m_controller = controller; 093 m_optionButtons = optionButtons; 094 } 095 096 /** 097 * Adds an option bar to the given drag element.<p> 098 * 099 * @param element the element 100 */ 101 public void addOptionBar(CmsContainerPageElementPanel element) { 102 103 // the view permission is required for any actions regarding this element 104 if (element.hasViewPermission()) { 105 CmsElementOptionBar optionBar = CmsElementOptionBar.createOptionBarForElement( 106 element, 107 m_controller.getDndHandler(), 108 m_optionButtons); 109 element.setElementOptionBar(optionBar); 110 } 111 } 112 113 /** 114 * Transforms all contained elements into {@link CmsContainerPageElementPanel}.<p> 115 * 116 * @param container the container 117 */ 118 public void consumeContainerElements(I_CmsDropContainer container) { 119 120 boolean containsElements = false; 121 // the drag element widgets are created from the existing DOM elements, 122 Element child = container.getElement().getFirstChildElement(); 123 List<CmsContainerPageElementPanel> children = Lists.newArrayList(); 124 while (child != null) { 125 boolean isContainerElement = CmsDomUtil.hasClass( 126 CmsContainerElement.CLASS_CONTAINER_ELEMENT_START_MARKER, 127 child); 128 boolean isGroupcontainerElement = CmsDomUtil.hasClass( 129 CmsContainerElement.CLASS_GROUP_CONTAINER_ELEMENT_MARKER, 130 child); 131 if (isContainerElement || isGroupcontainerElement) { 132 containsElements = true; 133 String serializedData = child.getAttribute(CmsGwtConstants.ATTR_DATA_ELEMENT); 134 child.removeAttribute(CmsGwtConstants.ATTR_DATA_ELEMENT); 135 CmsContainerElement elementData = null; 136 try { 137 elementData = m_controller.getSerializedElement(serializedData); 138 } catch (Exception e) { 139 CmsErrorDialog.handleException( 140 new Exception( 141 "Deserialization of element data failed. This may be caused by expired java-script resources, please clear your browser cache and try again.", 142 e)); 143 } 144 if (isContainerElement) { 145 146 // searching for content element root 147 Element elementRoot = (Element)child.getNextSibling(); 148 while ((elementRoot != null) && (elementRoot.getNodeType() != Node.ELEMENT_NODE)) { 149 Element temp = elementRoot; 150 elementRoot = (Element)elementRoot.getNextSibling(); 151 temp.removeFromParent(); 152 } 153 if (elementRoot == null) { 154 child.removeFromParent(); 155 child = null; 156 continue; 157 } 158 if (CmsDomUtil.hasClass(CmsContainerElement.CLASS_CONTAINER_ELEMENT_START_MARKER, elementRoot)) { 159 // broken element, already at next start marker 160 if (elementData != null) { 161 alertParsingError(elementData.getSitePath()); 162 } 163 child.removeFromParent(); 164 child = elementRoot; 165 continue; 166 } 167 if (CmsDomUtil.hasClass(CmsContainerElement.CLASS_CONTAINER_ELEMENT_END_MARKER, elementRoot)) { 168 // broken element, no content element root 169 if (elementData != null) { 170 alertParsingError(elementData.getSitePath()); 171 } 172 child.removeFromParent(); 173 child = elementRoot.getNextSiblingElement(); 174 elementRoot.removeFromParent(); 175 continue; 176 } else { 177 // looking for the next marker that wraps the current element 178 Element endMarker = (Element)elementRoot.getNextSibling(); 179 // only if the end marker node is not null and has neither the end-marker class or start-marker class 180 // remove the current node and check the next sibling 181 while (!((endMarker == null) 182 || ((endMarker.getNodeType() == Node.ELEMENT_NODE) 183 && (CmsDomUtil.hasClass( 184 CmsContainerElement.CLASS_CONTAINER_ELEMENT_END_MARKER, 185 endMarker) 186 || CmsDomUtil.hasClass( 187 CmsContainerElement.CLASS_CONTAINER_ELEMENT_START_MARKER, 188 endMarker))))) { 189 Element temp = endMarker; 190 endMarker = (Element)endMarker.getNextSibling(); 191 temp.removeFromParent(); 192 } 193 if (endMarker == null) { 194 if (elementData != null) { 195 alertParsingError(elementData.getSitePath()); 196 } 197 // broken element, end marker missing 198 elementRoot.removeFromParent(); 199 child.removeFromParent(); 200 child = null; 201 continue; 202 } 203 if (CmsDomUtil.hasClass(CmsContainerElement.CLASS_CONTAINER_ELEMENT_START_MARKER, endMarker)) { 204 if (elementData != null) { 205 alertParsingError(elementData.getSitePath()); 206 } 207 // broken element, end marker missing 208 elementRoot.removeFromParent(); 209 child.removeFromParent(); 210 child = endMarker; 211 } 212 if (elementData == null) { 213 // deserialization failed, remove whole element 214 child.removeFromParent(); 215 elementRoot.removeFromParent(); 216 child = endMarker.getNextSiblingElement(); 217 endMarker.removeFromParent(); 218 continue; 219 } 220 CmsDomUtil.removeScriptTags(elementRoot); 221 CmsContainerPageElementPanel containerElement = createElement( 222 elementRoot, 223 container, 224 elementData); 225 children.add(containerElement); 226 if (elementData.isNew()) { 227 containerElement.setNewType(elementData.getResourceType()); 228 } 229 container.adoptElement(containerElement); 230 child.removeFromParent(); 231 child = endMarker.getNextSiblingElement(); 232 endMarker.removeFromParent(); 233 } 234 } else if (isGroupcontainerElement && (container instanceof CmsContainerPageContainer)) { 235 if (elementData == null) { 236 Element sibling = child.getNextSiblingElement(); 237 container.getElement().removeChild(child); 238 child = sibling; 239 continue; 240 } 241 CmsDomUtil.removeScriptTags(child); 242 CmsGroupContainerElementPanel groupContainer = createGroupcontainer(child, container, elementData); 243 groupContainer.setContainerId(container.getContainerId()); 244 container.adoptElement(groupContainer); 245 consumeContainerElements(groupContainer); 246 if (groupContainer.getWidgetCount() == 0) { 247 groupContainer.addStyleName( 248 I_CmsLayoutBundle.INSTANCE.containerpageCss().emptyGroupContainer()); 249 } 250 children.add(groupContainer); 251 // important: adding the option-bar only after the group-containers have been consumed 252 if (container.isEditable() 253 && (!m_controller.isDetailPage() || container.isDetailView() || container.isDetailOnly())) { 254 //only allow editing if either element of detail only container or not in detail view 255 if (m_controller.requiresOptionBar(groupContainer, container)) { 256 addOptionBar(groupContainer); 257 } 258 } 259 child = child.getNextSiblingElement(); 260 } 261 } else { 262 Element sibling = child.getNextSiblingElement(); 263 if (!containsElements && (sibling == null) && (container instanceof CmsContainerPageContainer)) { 264 // this element is no container element and is the container only child, assume it is an empty container marker 265 ((CmsContainerPageContainer)container).setEmptyContainerElement(child); 266 } else { 267 // e.g. option bar 268 if (!CmsContainerPageElementPanel.isOverlay(child)) { 269 container.getElement().removeChild(child); 270 } 271 } 272 child = sibling; 273 continue; 274 } 275 } 276 container.onConsumeChildren(children); 277 } 278 279 /** 280 * The method will create {@link CmsContainerPageContainer} object for all given containers 281 * by converting the associated DOM elements. The contained elements will be transformed into {@link CmsContainerPageElementPanel}.<p> 282 * 283 * @param containers the container data 284 * @param context the parent element to the containers 285 * 286 * @return the drag target containers 287 */ 288 public Map<String, CmsContainerPageContainer> consumeContainers( 289 Map<String, CmsContainer> containers, 290 Element context) { 291 292 Map<String, CmsContainerPageContainer> result = new HashMap<String, CmsContainerPageContainer>(); 293 List<Element> containerElements = CmsDomUtil.getElementsByClass(CmsContainerElement.CLASS_CONTAINER, context); 294 for (Element containerElement : containerElements) { 295 String data = containerElement.getAttribute(CmsGwtConstants.ATTR_DATA_CONTAINER); 296 try { 297 CmsContainer container = m_controller.getSerializedContainer(data); 298 containers.put(container.getName(), container); 299 try { 300 CmsContainerPageContainer dragContainer = new CmsContainerPageContainer( 301 container, 302 containerElement); 303 consumeContainerElements(dragContainer); 304 result.put(container.getName(), dragContainer); 305 } catch (Exception e) { 306 CmsErrorDialog.handleException( 307 "Error parsing container " 308 + container.getName() 309 + ". Please check if your HTML is well formed.", 310 e); 311 } 312 } catch (Exception e) { 313 CmsErrorDialog.handleException( 314 new Exception( 315 "Deserialization of container data failed. This may be caused by expired java-script resources, please clear your browser cache and try again.", 316 e)); 317 } 318 containerElement.removeAttribute(CmsGwtConstants.ATTR_DATA_CONTAINER); 319 } 320 return result; 321 } 322 323 /** 324 * Creates an drag container element.<p> 325 * 326 * @param containerElement the container element data 327 * @param container the container parent 328 * @param isNew in case of a newly created element 329 * 330 * @return the draggable element 331 * 332 * @throws Exception if something goes wrong 333 */ 334 public CmsContainerPageElementPanel createElement( 335 CmsContainerElementData containerElement, 336 I_CmsDropContainer container, 337 boolean isNew) 338 throws Exception { 339 340 if (containerElement.isGroupContainer() || containerElement.isInheritContainer()) { 341 List<CmsContainerElementData> subElements = new ArrayList<CmsContainerElementData>(); 342 for (String subId : containerElement.getSubItems()) { 343 CmsContainerElementData element = m_controller.getCachedElement(subId); 344 if (element != null) { 345 subElements.add(element); 346 } else { 347 CmsDebugLog.getInstance().printLine("Cached element not found"); 348 } 349 } 350 return createGroupcontainerElement(containerElement, subElements, container); 351 } 352 Element element = CmsDomUtil.createElement(containerElement.getContents().get(container.getContainerId())); 353 // ensure any embedded flash players are set opaque so UI elements may be placed above them 354 CmsDomUtil.fixFlashZindex(element); 355 if (isNew) { 356 CmsContentEditor.replaceResourceIds( 357 element, 358 CmsUUID.getNullUUID().toString(), 359 CmsContainerpageController.getServerId(containerElement.getClientId())); 360 } 361 362 CmsContainerPageElementPanel result = createElement(element, container, containerElement); 363 if (!CmsContainerpageController.get().shouldShowInContext(containerElement)) { 364 result.getElement().getStyle().setDisplay(Style.Display.NONE); 365 result.addStyleName(CmsTemplateContextInfo.DUMMY_ELEMENT_MARKER); 366 } 367 return result; 368 } 369 370 /** 371 * Creates a drag container element for group-container elements.<p> 372 * 373 * @param containerElement the container element data 374 * @param subElements the sub-elements 375 * @param container the drag parent 376 * 377 * @return the draggable element 378 * 379 * @throws Exception if something goes wrong 380 */ 381 public CmsContainerPageElementPanel createGroupcontainerElement( 382 CmsContainerElementData containerElement, 383 List<CmsContainerElementData> subElements, 384 I_CmsDropContainer container) 385 throws Exception { 386 387 Element element = DOM.createDiv(); 388 element.addClassName(CmsContainerElement.CLASS_GROUP_CONTAINER_ELEMENT_MARKER); 389 CmsGroupContainerElementPanel groupContainer = createGroupcontainer(element, container, containerElement); 390 groupContainer.setContainerId(container.getContainerId()); 391 //adding sub-elements 392 Iterator<CmsContainerElementData> it = subElements.iterator(); 393 while (it.hasNext()) { 394 CmsContainerElementData subElement = it.next(); 395 if (subElement.getContents().containsKey(container.getContainerId())) { 396 CmsContainerPageElementPanel subDragElement = createElement(subElement, groupContainer, false); 397 groupContainer.add(subDragElement); 398 } 399 } 400 if (subElements.size() == 0) { 401 groupContainer.addStyleName(I_CmsLayoutBundle.INSTANCE.containerpageCss().emptyGroupContainer()); 402 } 403 addOptionBar(groupContainer); 404 return groupContainer; 405 } 406 407 /** 408 * Creates a list item.<p> 409 * 410 * @param containerElement the element data 411 * 412 * @return the list item widget 413 */ 414 public CmsMenuListItem createListItem(final CmsContainerElementData containerElement) { 415 416 CmsMenuListItem listItem = new CmsMenuListItem(containerElement); 417 if (!containerElement.isGroupContainer() 418 && !containerElement.isInheritContainer() 419 && CmsStringUtil.isEmptyOrWhitespaceOnly(containerElement.getNoEditReason())) { 420 listItem.enableEdit(new ClickHandler() { 421 422 public void onClick(ClickEvent event) { 423 424 CmsEditableData editableData = new CmsEditableData(); 425 editableData.setElementLanguage(CmsCoreProvider.get().getLocale()); 426 editableData.setStructureId( 427 new CmsUUID(CmsContainerpageController.getServerId(containerElement.getClientId()))); 428 editableData.setSitePath(containerElement.getSitePath()); 429 getController().getContentEditorHandler().openDialog(editableData, false, null, null, null); 430 ((CmsPushButton)event.getSource()).clearHoverState(); 431 } 432 }); 433 } else { 434 if (CmsStringUtil.isNotEmptyOrWhitespaceOnly(containerElement.getNoEditReason())) { 435 listItem.disableEdit(containerElement.getNoEditReason(), true); 436 } else { 437 listItem.disableEdit(Messages.get().key(Messages.GUI_CLIPBOARD_ITEM_CAN_NOT_BE_EDITED_0), false); 438 } 439 } 440 String clientId = containerElement.getClientId(); 441 String serverId = CmsContainerpageController.getServerId(clientId); 442 if (CmsUUID.isValidUUID(serverId)) { 443 CmsContextMenuButton button = new CmsContextMenuButton(new CmsUUID(serverId), new CmsContextMenuHandler() { 444 445 @Override 446 public void refreshResource(CmsUUID structureId) { 447 448 Window.Location.reload(); 449 } 450 }, AdeContext.gallery); 451 listItem.getListItemWidget().addButtonToFront(button); 452 } 453 if (!containerElement.isAddDisabled()) { 454 listItem.initMoveHandle(m_controller.getDndHandler(), true); 455 } 456 return listItem; 457 } 458 459 /** 460 * Returns the container page controller.<p> 461 * 462 * @return the container page controller 463 */ 464 protected CmsContainerpageController getController() { 465 466 return m_controller; 467 } 468 469 /** 470 * Displays the element parsing error dialog.<p> 471 * 472 * @param sitePath the element site path 473 */ 474 private void alertParsingError(String sitePath) { 475 476 new CmsErrorDialog( 477 "Error parsing element " 478 + sitePath 479 + ". Please check if the HTML generated by the element formatter is well formed.", 480 null).center(); 481 } 482 483 /** 484 * Creates an drag container element.<p> 485 * 486 * @param element the DOM element 487 * @param dragParent the drag parent 488 * @param elementData the element data 489 * 490 * @return the draggable element 491 */ 492 private CmsContainerPageElementPanel createElement( 493 Element element, 494 I_CmsDropContainer dragParent, 495 CmsContainerElement elementData) { 496 497 CmsContainerPageElementPanel dragElement = new CmsContainerPageElementPanel( 498 element, 499 dragParent, 500 elementData.getClientId(), 501 elementData.getSitePath(), 502 elementData.getNoEditReason(), 503 elementData.getLockInfo(), 504 elementData.getTitle(), 505 elementData.getSubTitle(), 506 elementData.getResourceType(), 507 elementData.hasSettings(dragParent.getContainerId()), 508 elementData.hasViewPermission(), 509 elementData.hasWritePermission(), 510 elementData.isReleasedAndNotExpired(), 511 elementData.isNewEditorDisabled(), 512 elementData.hasEditHandler(), 513 elementData.getModelGroupId(), 514 elementData.isWasModelGroup(), 515 elementData.getElementView(), 516 elementData.getBigIconClasses(), 517 elementData.isReused()); 518 dragElement.setCreateNew(elementData.isCreateNew()); 519 if (m_controller.requiresOptionBar(dragElement, dragParent)) { 520 addOptionBar(dragElement); 521 } 522 if (m_controller.isInlineEditable(dragElement, dragParent)) { 523 dragElement.initInlineEditor(m_controller); 524 } 525 return dragElement; 526 } 527 528 /** 529 * Creates a drag container element. This will not add an option-bar!<p> 530 * 531 * @param element the DOM element 532 * @param dragParent the drag parent 533 * @param elementData the element data 534 * 535 * @return the draggable element 536 */ 537 private CmsGroupContainerElementPanel createGroupcontainer( 538 Element element, 539 I_CmsDropContainer dragParent, 540 CmsContainerElement elementData) { 541 542 CmsGroupContainerElementPanel groupContainer = new CmsGroupContainerElementPanel( 543 element, 544 dragParent, 545 elementData.getClientId(), 546 elementData.getSitePath(), 547 elementData.getResourceType(), 548 elementData.getNoEditReason(), 549 elementData.getTitle(), 550 elementData.getSubTitle(), 551 elementData.hasSettings(dragParent.getContainerId()), 552 elementData.hasViewPermission(), 553 elementData.hasWritePermission(), 554 elementData.isReleasedAndNotExpired(), 555 elementData.getElementView(), 556 elementData.getBigIconClasses()); 557 return groupContainer; 558 } 559}