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.CmsContainerPageContainer; 031import org.opencms.ade.containerpage.client.ui.CmsContainerPageElementPanel; 032import org.opencms.ade.containerpage.client.ui.CmsDroppedElementModeSelectionDialog; 033import org.opencms.ade.containerpage.client.ui.CmsElementOptionBar; 034import org.opencms.ade.containerpage.client.ui.CmsGroupContainerElementPanel; 035import org.opencms.ade.containerpage.client.ui.CmsToolbarAllGalleriesMenu; 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.CmsCntPageData.ElementReuseMode; 039import org.opencms.ade.containerpage.shared.CmsContainer; 040import org.opencms.ade.containerpage.shared.CmsContainerElement; 041import org.opencms.ade.containerpage.shared.CmsContainerElementData; 042import org.opencms.ade.contenteditor.shared.CmsEditorConstants; 043import org.opencms.ade.galleries.client.ui.CmsResultListItem; 044import org.opencms.gwt.client.dnd.CmsDNDHandler; 045import org.opencms.gwt.client.dnd.CmsDNDHandler.Orientation; 046import org.opencms.gwt.client.dnd.I_CmsDNDController; 047import org.opencms.gwt.client.dnd.I_CmsDraggable; 048import org.opencms.gwt.client.dnd.I_CmsDropTarget; 049import org.opencms.gwt.client.ui.CmsErrorDialog; 050import org.opencms.gwt.client.ui.CmsList; 051import org.opencms.gwt.client.ui.CmsListItem; 052import org.opencms.gwt.client.util.CmsDebugLog; 053import org.opencms.gwt.client.util.CmsDomUtil; 054import org.opencms.gwt.client.util.CmsPositionBean; 055import org.opencms.gwt.client.util.I_CmsSimpleCallback; 056import org.opencms.gwt.shared.CmsTemplateContextInfo; 057import org.opencms.util.CmsStringUtil; 058import org.opencms.util.CmsUUID; 059 060import java.util.ArrayList; 061import java.util.HashMap; 062import java.util.List; 063import java.util.Map; 064import java.util.Map.Entry; 065import java.util.Set; 066 067import com.google.common.base.Objects; 068import com.google.gwt.core.client.Scheduler; 069import com.google.gwt.core.client.Scheduler.ScheduledCommand; 070import com.google.gwt.dom.client.Document; 071import com.google.gwt.dom.client.Element; 072import com.google.gwt.dom.client.Style; 073import com.google.gwt.dom.client.Style.Display; 074import com.google.gwt.dom.client.Style.Position; 075import com.google.gwt.dom.client.Style.Unit; 076import com.google.gwt.user.client.DOM; 077import com.google.gwt.user.client.rpc.AsyncCallback; 078import com.google.gwt.user.client.ui.Widget; 079 080/** 081 * The container-page editor drag and drop controller.<p> 082 * 083 * @since 8.0.0 084 */ 085public class CmsContainerpageDNDController implements I_CmsDNDController { 086 087 /** The container highlighting offset. */ 088 public static final int HIGHLIGHTING_OFFSET = 4; 089 090 /** The minimum margin set to empty containers. */ 091 private static final int MINIMUM_CONTAINER_MARGIN = 10; 092 093 /** The container page controller. */ 094 protected CmsContainerpageController m_controller; 095 096 /** The id of the dragged element. */ 097 protected String m_draggableId; 098 099 /** The id of the container from which an element was dragged. */ 100 String m_originalContainerId; 101 102 /** The copy group id. */ 103 private String m_copyGroupId; 104 105 /** The current place holder index. */ 106 private int m_currentIndex = -1; 107 108 /** The current drop target. */ 109 private I_CmsDropTarget m_currentTarget; 110 111 /** Map of current drag info beans. */ 112 private Map<I_CmsDropTarget, Element> m_dragInfos; 113 114 /** The drag overlay. */ 115 private Element m_dragOverlay; 116 117 /** DND controller for images. We set this in onDragStart if we are dragging an image, and then delegate most of the other method calls to it if it is set.*/ 118 private CmsImageDndController m_imageDndController; 119 120 /** The ionitial drop target. */ 121 private I_CmsDropTarget m_initialDropTarget; 122 123 /** Creating new flag. */ 124 private boolean m_isNew; 125 126 /** The original position of the draggable. */ 127 private int m_originalIndex; 128 129 /** 130 * Constructor.<p> 131 * 132 * @param controller the container page controller 133 */ 134 public CmsContainerpageDNDController(CmsContainerpageController controller) { 135 136 m_controller = controller; 137 m_dragInfos = new HashMap<I_CmsDropTarget, Element>(); 138 } 139 140 /** 141 * @see org.opencms.gwt.client.dnd.I_CmsDNDController#onAnimationStart(org.opencms.gwt.client.dnd.I_CmsDraggable, org.opencms.gwt.client.dnd.I_CmsDropTarget, org.opencms.gwt.client.dnd.CmsDNDHandler) 142 */ 143 public void onAnimationStart(I_CmsDraggable draggable, I_CmsDropTarget target, CmsDNDHandler handler) { 144 145 // remove highlighting 146 for (I_CmsDropTarget dropTarget : m_dragInfos.keySet()) { 147 if (dropTarget instanceof I_CmsDropContainer) { 148 ((I_CmsDropContainer)dropTarget).removeHighlighting(); 149 } 150 } 151 } 152 153 /** 154 * @see org.opencms.gwt.client.dnd.I_CmsDNDController#onBeforeDrop(org.opencms.gwt.client.dnd.I_CmsDraggable, org.opencms.gwt.client.dnd.I_CmsDropTarget, org.opencms.gwt.client.dnd.CmsDNDHandler) 155 */ 156 public boolean onBeforeDrop(I_CmsDraggable draggable, I_CmsDropTarget target, CmsDNDHandler handler) { 157 158 return true; 159 } 160 161 /** 162 * @see org.opencms.gwt.client.dnd.I_CmsDNDController#onDragCancel(org.opencms.gwt.client.dnd.I_CmsDraggable, org.opencms.gwt.client.dnd.I_CmsDropTarget, org.opencms.gwt.client.dnd.CmsDNDHandler) 163 */ 164 public void onDragCancel(I_CmsDraggable draggable, I_CmsDropTarget target, CmsDNDHandler handler) { 165 166 if (m_imageDndController != null) { 167 m_imageDndController.onDragCancel(draggable, target, handler); 168 removeDragOverlay(); 169 m_imageDndController = null; 170 return; 171 } 172 stopDrag(handler); 173 } 174 175 /** 176 * @see org.opencms.gwt.client.dnd.I_CmsDNDController#onDragStart(org.opencms.gwt.client.dnd.I_CmsDraggable, org.opencms.gwt.client.dnd.I_CmsDropTarget, org.opencms.gwt.client.dnd.CmsDNDHandler) 177 */ 178 public boolean onDragStart(final I_CmsDraggable draggable, I_CmsDropTarget target, final CmsDNDHandler handler) { 179 180 installDragOverlay(); 181 m_currentTarget = null; 182 m_currentIndex = -1; 183 m_draggableId = draggable.getId(); 184 m_isNew = false; 185 m_originalIndex = -1; 186 m_initialDropTarget = target; 187 handler.setOrientation(Orientation.ALL); 188 m_controller.hideEditableListButtons(); 189 190 if (isImage(draggable)) { 191 if (m_controller.isGroupcontainerEditing()) { 192 // don't allow image DND while editing group containers 193 return false; 194 } 195 // We are going to delegate further method calls to this, and set it to null again if the image is dragged or the 196 // DND operation is cancelled 197 m_imageDndController = new CmsImageDndController(CmsContainerpageController.get()); 198 return m_imageDndController.onDragStart(draggable, target, handler); 199 200 } 201 202 if (!m_controller.isGroupcontainerEditing()) { 203 m_controller.lockContainerpage(new I_CmsSimpleCallback<Boolean>() { 204 205 public void execute(Boolean arg) { 206 207 if (!arg.booleanValue()) { 208 handler.cancel(); 209 } 210 } 211 }); 212 } 213 214 String containerId = null; 215 if (target instanceof CmsContainerPageContainer) { 216 containerId = ((CmsContainerPageContainer)target).getContainerId(); 217 } else { 218 // set marker id 219 containerId = CmsContainerElement.MENU_CONTAINER_ID; 220 } 221 m_originalContainerId = containerId; 222 223 if (target != null) { 224 handler.addTarget(target); 225 if (target instanceof I_CmsDropContainer) { 226 prepareTargetContainer((I_CmsDropContainer)target, draggable, handler.getPlaceholder()); 227 Element helper = handler.getDragHelper(); 228 handler.setCursorOffsetX(helper.getOffsetWidth() - 15); 229 handler.setCursorOffsetY(20); 230 } 231 } 232 233 m_dragInfos.put(target, handler.getPlaceholder()); 234 m_controller.getHandler().hideMenu(); 235 String clientId = draggable.getId(); 236 if (CmsStringUtil.isEmptyOrWhitespaceOnly(clientId)) { 237 CmsDebugLog.getInstance().printLine("draggable has no id, canceling drop"); 238 return false; 239 } 240 final I_CmsSimpleCallback<CmsContainerElementData> callback = new I_CmsSimpleCallback<CmsContainerElementData>() { 241 242 /** 243 * Execute on success.<p> 244 * 245 * @param arg the container element data 246 */ 247 public void execute(CmsContainerElementData arg) { 248 249 prepareHelperElements(arg, handler, draggable); 250 handler.updatePosition(); 251 } 252 }; 253 if (isNewId(clientId)) { 254 // for new content elements dragged from the gallery menu, the given id contains the resource type name 255 m_isNew = true; 256 m_controller.getNewElement(clientId, callback); 257 } else { 258 m_controller.getElementForDragAndDropFromContainer(clientId, m_originalContainerId, false, callback); 259 } 260 if (!(target instanceof CmsContainerPageContainer)) { 261 handler.setStartPosition(-1, 0); 262 } 263 return true; 264 265 } 266 267 /** 268 * @see org.opencms.gwt.client.dnd.I_CmsDNDController#onDrop(org.opencms.gwt.client.dnd.I_CmsDraggable, org.opencms.gwt.client.dnd.I_CmsDropTarget, org.opencms.gwt.client.dnd.CmsDNDHandler) 269 */ 270 public void onDrop(final I_CmsDraggable draggable, final I_CmsDropTarget target, CmsDNDHandler handler) { 271 272 if (m_imageDndController != null) { 273 m_imageDndController.onDrop(draggable, target, handler); 274 removeDragOverlay(); 275 m_imageDndController = null; 276 return; 277 } 278 if (target != m_initialDropTarget) { 279 if (target instanceof I_CmsDropContainer) { 280 final I_CmsDropContainer container = (I_CmsDropContainer)target; 281 final int index = container.getPlaceholderIndex(); 282 final String modelReplaceId = container instanceof CmsContainerPageContainer 283 ? ((CmsContainerPageContainer)container).getCopyModelReplaceId() 284 : null; 285 if (!m_isNew && (draggable instanceof CmsListItem)) { 286 final String copyGroupId = m_copyGroupId; 287 AsyncCallback<String> modeCallback = new AsyncCallback<String>() { 288 289 public void onFailure(Throwable caught) { 290 291 if (caught != null) { 292 CmsErrorDialog.handleException(caught); 293 } 294 } 295 296 public void onSuccess(String result) { 297 298 if (Objects.equal(result, CmsEditorConstants.MODE_COPY)) { 299 final CmsContainerpageController controller = CmsContainerpageController.get(); 300 if (copyGroupId == null) { 301 302 CmsContainerElementData data = controller.getCachedElement(m_draggableId); 303 final Map<String, String> settings = data.getSettings(); 304 305 controller.copyElement( 306 CmsContainerpageController.getServerId(m_draggableId), 307 new I_CmsSimpleCallback<CmsUUID>() { 308 309 public void execute(CmsUUID resultId) { 310 311 controller.getElementWithSettings( 312 "" + resultId, 313 settings, 314 new I_CmsSimpleCallback<CmsContainerElementData>() { 315 316 public void execute(CmsContainerElementData newData) { 317 318 insertDropElement( 319 newData, 320 container, 321 index, 322 draggable instanceof CmsContainerPageElementPanel 323 ? (CmsContainerPageElementPanel)draggable 324 : null, 325 modelReplaceId); 326 container.removePlaceholder(); 327 } 328 }); 329 } 330 }); 331 } else { 332 controller.getElementForDragAndDropFromContainer( 333 copyGroupId, 334 m_originalContainerId, 335 true, 336 new I_CmsSimpleCallback<CmsContainerElementData>() { 337 338 public void execute(CmsContainerElementData arg) { 339 340 insertDropElement( 341 arg, 342 container, 343 index, 344 draggable instanceof CmsContainerPageElementPanel 345 ? (CmsContainerPageElementPanel)draggable 346 : null, 347 modelReplaceId); 348 container.removePlaceholder(); 349 } 350 }); 351 } 352 353 } else if (Objects.equal(result, CmsEditorConstants.MODE_REUSE)) { 354 insertDropElement( 355 m_controller.getCachedElement(m_draggableId), 356 container, 357 index, 358 draggable instanceof CmsContainerPageElementPanel 359 ? (CmsContainerPageElementPanel)draggable 360 : null, 361 modelReplaceId); 362 container.removePlaceholder(); 363 } 364 } 365 }; 366 367 CmsUUID structureId = new CmsUUID(CmsContainerpageController.getServerId(m_draggableId)); 368 CmsContainerElementData cachedElementData = m_controller.getCachedElement(m_draggableId); 369 ElementReuseMode reuseMode = isCopyModel(draggable) 370 ? ElementReuseMode.copy 371 : (((cachedElementData != null) && !cachedElementData.isCopyInModels()) 372 ? ElementReuseMode.reuse 373 : CmsContainerpageController.get().getData().getElementReuseMode()); 374 if (handler.hasModifierCTRL()) { 375 reuseMode = ElementReuseMode.ask; 376 } 377 if (reuseMode != ElementReuseMode.reuse) { 378 379 if ((cachedElementData != null) 380 && (!cachedElementData.hasWritePermission() 381 || cachedElementData.isModelGroup() 382 || cachedElementData.isCopyDisabled() 383 || cachedElementData.isWasModelGroup())) { 384 // User is not allowed to create this element in current view, so reuse the element instead 385 reuseMode = ElementReuseMode.reuse; 386 } 387 } 388 switch (reuseMode) { 389 case ask: 390 // when dropping elements from the into the page, we ask the user if the dropped element should 391 // be used, or a copy of it. If the user wants a copy, we copy the corresponding resource and replace the element 392 // in the page 393 CmsDroppedElementModeSelectionDialog.showDialog(structureId, modeCallback); 394 break; 395 case copy: 396 modeCallback.onSuccess(CmsEditorConstants.MODE_COPY); 397 break; 398 case reuse: 399 default: 400 modeCallback.onSuccess(CmsEditorConstants.MODE_REUSE); 401 break; 402 } 403 } else { 404 insertDropElement( 405 m_controller.getCachedElement(m_draggableId), 406 container, 407 index, 408 draggable instanceof CmsContainerPageElementPanel 409 ? (CmsContainerPageElementPanel)draggable 410 : null, 411 null); 412 } 413 } else if (target instanceof CmsList<?>) { 414 m_controller.addToFavoriteList(m_draggableId); 415 } 416 } else if ((target instanceof I_CmsDropContainer) 417 && (draggable instanceof CmsContainerPageElementPanel) 418 && isChangedPosition(target)) { 419 CmsDomUtil.showOverlay(draggable.getElement(), false); 420 I_CmsDropContainer container = (I_CmsDropContainer)target; 421 int count = container.getWidgetCount(); 422 handler.getPlaceholder().getStyle().setDisplay(Display.NONE); 423 if (container.getPlaceholderIndex() >= count) { 424 container.add((CmsContainerPageElementPanel)draggable); 425 } else { 426 container.insert((CmsContainerPageElementPanel)draggable, container.getPlaceholderIndex()); 427 } 428 m_controller.addToRecentList(m_draggableId, null); 429 // changes are only relevant to the container page if not group-container editing 430 if (!m_controller.isGroupcontainerEditing()) { 431 m_controller.setPageChanged(); 432 } 433 } else if (draggable instanceof CmsContainerPageElementPanel) { 434 CmsDomUtil.showOverlay(draggable.getElement(), false); 435 // to reset mouse over state remove and attach the option bar 436 CmsContainerPageElementPanel containerElement = (CmsContainerPageElementPanel)draggable; 437 CmsElementOptionBar optionBar = containerElement.getElementOptionBar(); 438 optionBar.removeFromParent(); 439 containerElement.setElementOptionBar(optionBar); 440 } 441 stopDrag(handler); 442 } 443 444 /** 445 * @see org.opencms.gwt.client.dnd.I_CmsDNDController#onPositionedPlaceholder(org.opencms.gwt.client.dnd.I_CmsDraggable, org.opencms.gwt.client.dnd.I_CmsDropTarget, org.opencms.gwt.client.dnd.CmsDNDHandler) 446 */ 447 public void onPositionedPlaceholder(I_CmsDraggable draggable, I_CmsDropTarget target, CmsDNDHandler handler) { 448 449 if (m_imageDndController != null) { 450 m_imageDndController.onPositionedPlaceholder(draggable, target, handler); 451 return; 452 } 453 454 if (hasChangedPosition(target)) { 455 checkPlaceholderVisibility(target); 456 updateHighlighting(false); 457 } 458 } 459 460 /** 461 * @see org.opencms.gwt.client.dnd.I_CmsDNDController#onTargetEnter(org.opencms.gwt.client.dnd.I_CmsDraggable, org.opencms.gwt.client.dnd.I_CmsDropTarget, org.opencms.gwt.client.dnd.CmsDNDHandler) 462 */ 463 public boolean onTargetEnter(I_CmsDraggable draggable, I_CmsDropTarget target, CmsDNDHandler handler) { 464 465 if (m_imageDndController != null) { 466 return m_imageDndController.onTargetEnter(draggable, target, handler); 467 } 468 469 if (target instanceof CmsContainerPageContainer) { 470 CmsContainerPageContainer container = (CmsContainerPageContainer)target; 471 if (container.isShowingEmptyContainerElement()) { 472 CmsContainerPageContainer.newResizeHelper(container).initHeight(); 473 } 474 } 475 476 Element placeholder = m_dragInfos.get(target); 477 if (placeholder != null) { 478 handler.getPlaceholder().getStyle().setDisplay(Display.NONE); 479 handler.setPlaceholder(placeholder); 480 placeholder.getStyle().clearDisplay(); 481 if ((target != m_initialDropTarget) && (target instanceof I_CmsDropContainer)) { 482 ((I_CmsDropContainer)target).checkMaxElementsOnEnter(); 483 } 484 } 485 return true; 486 } 487 488 /** 489 * @see org.opencms.gwt.client.dnd.I_CmsDNDController#onTargetLeave(org.opencms.gwt.client.dnd.I_CmsDraggable, org.opencms.gwt.client.dnd.I_CmsDropTarget, org.opencms.gwt.client.dnd.CmsDNDHandler) 490 */ 491 public void onTargetLeave(I_CmsDraggable draggable, I_CmsDropTarget target, CmsDNDHandler handler) { 492 493 if (m_imageDndController != null) { 494 m_imageDndController.onTargetLeave(draggable, target, handler); 495 return; 496 } 497 CmsContainerPageContainer.clearResizeHelper(); 498 m_currentTarget = null; 499 m_currentIndex = -1; 500 Element placeholder = m_dragInfos.get(m_initialDropTarget); 501 if (placeholder != null) { 502 handler.getPlaceholder().getStyle().setDisplay(Display.NONE); 503 handler.setPlaceholder(placeholder); 504 placeholder.getStyle().setDisplay(Display.NONE); 505 if ((target != m_initialDropTarget) && (target instanceof I_CmsDropContainer)) { 506 ((I_CmsDropContainer)target).checkMaxElementsOnLeave(); 507 } 508 } 509 updateHighlighting(false); 510 } 511 512 /** 513 * Prepares all helper elements for the different drop targets.<p> 514 * 515 * @param elementData the element data 516 * @param handler the drag and drop handler 517 * @param draggable the draggable 518 */ 519 protected void prepareHelperElements( 520 CmsContainerElementData elementData, 521 CmsDNDHandler handler, 522 I_CmsDraggable draggable) { 523 524 if (elementData == null) { 525 CmsDebugLog.getInstance().printLine("elementData == null!"); 526 handler.cancel(); 527 return; 528 } 529 if (!handler.isDragging()) { 530 return; 531 } 532 if (elementData.isGroup()) { 533 m_copyGroupId = m_draggableId; 534 } else { 535 m_copyGroupId = null; 536 } 537 538 if ((elementData.getDndId() != null) && (m_controller.getCachedElement(elementData.getDndId()) != null)) { 539 m_draggableId = elementData.getDndId(); 540 elementData = m_controller.getCachedElement(m_draggableId); 541 } else { 542 m_draggableId = elementData.getClientId(); 543 } 544 if (m_controller.isGroupcontainerEditing()) { 545 CmsGroupContainerElementPanel groupContainer = m_controller.getGroupcontainer(); 546 if ((groupContainer != m_initialDropTarget) 547 && !(elementData.isGroupContainer() || elementData.isInheritContainer()) 548 && (elementData.getContents().get(groupContainer.getContainerId()) != null)) { 549 Element placeholder = null; 550 Set<String> cssResources = elementData.getCssResources(groupContainer.getContainerId()); 551 if ((cssResources != null) && !cssResources.isEmpty()) { 552 // the element requires certain CSS resources, check if present and include if necessary 553 for (String cssResourceLink : cssResources) { 554 CmsDomUtil.ensureStyleSheetIncluded(cssResourceLink); 555 } 556 } 557 try { 558 String htmlContent = elementData.getContents().get(groupContainer.getContainerId()); 559 placeholder = CmsDomUtil.createElement(htmlContent); 560 // ensure any embedded flash players are set opaque so UI elements may be placed above them 561 CmsDomUtil.fixFlashZindex(placeholder); 562 placeholder.addClassName(I_CmsLayoutBundle.INSTANCE.dragdropCss().dragPlaceholder()); 563 } catch (Exception e) { 564 CmsDebugLog.getInstance().printLine(e.getMessage()); 565 } 566 567 if (placeholder != null) { 568 prepareDragInfo(placeholder, groupContainer, handler); 569 groupContainer.highlightContainer(); 570 } 571 } 572 return; 573 } 574 if (!m_controller.isEditingDisabled()) { 575 for (CmsContainerPageContainer container : m_controller.getContainerTargets().values()) { 576 if (draggable.getElement().isOrHasChild(container.getElement())) { 577 // skip containers that are children of the draggable element 578 continue; 579 } 580 if ((container != m_initialDropTarget) 581 && !container.isDetailView() 582 && (m_controller.getData().isModelGroup() || !container.hasModelGroupParent()) 583 && (elementData.getContents().get(container.getContainerId()) != null)) { 584 585 Element placeholder = null; 586 if (elementData.isGroupContainer() || elementData.isInheritContainer()) { 587 placeholder = DOM.createDiv(); 588 String content = ""; 589 for (String groupId : elementData.getSubItems()) { 590 CmsContainerElementData subData = m_controller.getCachedElement(groupId); 591 if (subData != null) { 592 if (subData.isShowInContext( 593 CmsContainerpageController.get().getData().getTemplateContextInfo().getCurrentContext())) { 594 if ((subData.getContents().get(container.getContainerId()) != null)) { 595 content += subData.getContents().get(container.getContainerId()); 596 } 597 } else { 598 content += "<div class='" 599 + CmsTemplateContextInfo.DUMMY_ELEMENT_MARKER 600 + "' style='display: none !important;'></div>"; 601 } 602 } 603 } 604 if (CmsStringUtil.isNotEmptyOrWhitespaceOnly(content)) { 605 placeholder.setInnerHTML(content); 606 // ensure any embedded flash players are set opaque so UI elements may be placed above them 607 CmsDomUtil.fixFlashZindex(placeholder); 608 } else { 609 placeholder.addClassName( 610 I_CmsLayoutBundle.INSTANCE.containerpageCss().emptyGroupContainer()); 611 } 612 } else { 613 Set<String> cssResources = elementData.getCssResources(container.getContainerId()); 614 if ((cssResources != null) && !cssResources.isEmpty()) { 615 // the element requires certain CSS resources, check if present and include if necessary 616 for (String cssResourceLink : cssResources) { 617 CmsDomUtil.ensureStyleSheetIncluded(cssResourceLink); 618 } 619 } 620 try { 621 String htmlContent = elementData.getContents().get(container.getContainerId()); 622 placeholder = CmsDomUtil.createElement(htmlContent); 623 // ensure any embedded flash players are set opaque so UI elements may be placed above them 624 CmsDomUtil.fixFlashZindex(placeholder); 625 placeholder.addClassName(I_CmsLayoutBundle.INSTANCE.dragdropCss().dragPlaceholder()); 626 } catch (Exception e) { 627 CmsDebugLog.getInstance().printLine(e.getMessage()); 628 } 629 } 630 if (placeholder != null) { 631 prepareDragInfo(placeholder, container, handler); 632 } 633 } 634 } 635 initNestedContainers(); 636 // add highlighting after all drag targets have been initialized 637 updateHighlighting(true); 638 } 639 } 640 641 /** 642 * Inserts e new container element into the container once the drag and drop is finished.<p> 643 * 644 * @param elementData the element data to create the element from 645 * @param container the target container 646 * @param index the element index 647 * @param draggable the original drag element 648 * @param modelReplaceId the model replace id 649 */ 650 void insertDropElement( 651 CmsContainerElementData elementData, 652 I_CmsDropContainer container, 653 int index, 654 CmsContainerPageElementPanel draggable, 655 String modelReplaceId) { 656 657 try { 658 CmsContainerPageElementPanel containerElement = m_controller.getContainerpageUtil().createElement( 659 elementData, 660 container, 661 m_isNew); 662 if (m_isNew) { 663 containerElement.setNewType(CmsContainerpageController.getServerId(m_draggableId)); 664 } else { 665 m_controller.addToRecentList(elementData.getClientId(), null); 666 } 667 668 if (index >= container.getWidgetCount()) { 669 container.add(containerElement); 670 } else { 671 container.insert(containerElement, index); 672 } 673 if (draggable != null) { 674 draggable.removeFromParent(); 675 } 676 m_controller.initializeSubContainers(containerElement); 677 678 if (modelReplaceId != null) { 679 m_controller.executeCopyModelReplace( 680 modelReplaceId, 681 ((CmsContainerPageContainer)container).getFormerModelGroupParent(), 682 m_controller.getCachedElement(m_draggableId)); 683 } 684 685 if (!m_controller.isGroupcontainerEditing()) { 686 if (containerElement.hasReloadMarker()) { 687 m_controller.setPageChanged(new Runnable() { 688 689 public void run() { 690 691 CmsContainerpageController.get().reloadPage(); 692 } 693 }); 694 } else { 695 m_controller.setPageChanged(); 696 String clientId = elementData.getClientId(); 697 String containerId = container.getContainerId(); 698 Map<String, String> settings = elementData.getSettings(); 699 m_controller.updateServerElementFormatter(clientId, containerId, settings); 700 } 701 } 702 if (m_controller.isGroupcontainerEditing()) { 703 container.getElement().removeClassName( 704 I_CmsLayoutBundle.INSTANCE.containerpageCss().emptyGroupContainer()); 705 } 706 707 } catch (Exception e) { 708 CmsErrorDialog.handleException(e.getMessage(), e); 709 } 710 } 711 712 /** 713 * Checks and sets if the place holder should be visible.<p> 714 * 715 * @param target the target container 716 */ 717 private void checkPlaceholderVisibility(I_CmsDropTarget target) { 718 719 if (target instanceof I_CmsDropContainer) { 720 I_CmsDropContainer container = (I_CmsDropContainer)target; 721 container.setPlaceholderVisibility( 722 (container != m_initialDropTarget) 723 || ((m_currentIndex - m_originalIndex) > 1) 724 || ((m_originalIndex - m_currentIndex) > 0)); 725 } 726 } 727 728 /** 729 * Checks if the placeholder position has changed.<p> 730 * 731 * @param target the current drop target 732 * 733 * @return <code>true</code> if the placeholder position has changed 734 */ 735 private boolean hasChangedPosition(I_CmsDropTarget target) { 736 737 if ((m_currentTarget != target) || (m_currentIndex != target.getPlaceholderIndex())) { 738 m_currentTarget = target; 739 m_currentIndex = target.getPlaceholderIndex(); 740 return true; 741 } 742 return false; 743 } 744 745 /** 746 * Initializes the nested container infos.<p> 747 */ 748 private void initNestedContainers() { 749 750 for (CmsContainer container : m_controller.getContainers().values()) { 751 if (container.isSubContainer()) { 752 CmsContainerPageContainer containerWidget = m_controller.m_targetContainers.get(container.getName()); 753 // check if the sub container is a valid drop targets 754 if (m_dragInfos.keySet().contains(containerWidget)) { 755 CmsContainer parentContainer = m_controller.getContainers().get(container.getParentContainerName()); 756 // add the container to all it's ancestors as a dnd child 757 while (parentContainer != null) { 758 if (m_dragInfos.keySet().contains( 759 m_controller.m_targetContainers.get(parentContainer.getName()))) { 760 m_controller.m_targetContainers.get(parentContainer.getName()).addDndChild(containerWidget); 761 } 762 if (parentContainer.isSubContainer()) { 763 parentContainer = m_controller.getContainers().get( 764 parentContainer.getParentContainerName()); 765 } else { 766 parentContainer = null; 767 } 768 } 769 } 770 } 771 772 } 773 } 774 775 /** 776 * Installs the drag overlay to avoid any mouse over issues or similar.<p> 777 */ 778 private void installDragOverlay() { 779 780 if (m_dragOverlay != null) { 781 m_dragOverlay.removeFromParent(); 782 } 783 m_dragOverlay = DOM.createDiv(); 784 m_dragOverlay.addClassName(I_CmsLayoutBundle.INSTANCE.dragdropCss().dragOverlay()); 785 Document.get().getBody().appendChild(m_dragOverlay); 786 } 787 788 /** 789 * Checks whether the current placeholder position represents a change to the original draggable position within the tree.<p> 790 * 791 * @param target the current drop target 792 * 793 * @return <code>true</code> if the position changed 794 */ 795 private boolean isChangedPosition(I_CmsDropTarget target) { 796 797 // if the new index is not next to the old one, the position has changed 798 if ((target != m_initialDropTarget) 799 || !((target.getPlaceholderIndex() == (m_originalIndex + 1)) 800 || (target.getPlaceholderIndex() == m_originalIndex))) { 801 return true; 802 } 803 return false; 804 } 805 806 /** 807 * Checks if the draggable item is a copy model.<p> 808 * 809 * @param draggable the draggable item 810 * 811 * @return true if the item is a copy model 812 */ 813 private boolean isCopyModel(I_CmsDraggable draggable) { 814 815 if (!(draggable instanceof CmsResultListItem)) { 816 return false; 817 } 818 return ((CmsResultListItem)draggable).getResult().isCopyModel(); 819 } 820 821 /** 822 * Checks if the given draggable item is an image.<p> 823 * 824 * @param draggable the item to check 825 * 826 * @return true if the given item is an image 827 */ 828 private boolean isImage(I_CmsDraggable draggable) { 829 830 return (draggable instanceof CmsResultListItem) 831 && CmsToolbarAllGalleriesMenu.DND_MARKER.equals(((CmsResultListItem)draggable).getData()); 832 833 } 834 835 /** 836 * Checks if the given id is a new id.<p> 837 * 838 * @param id the id 839 * 840 * @return <code>true</code> if the id is a new id 841 */ 842 private boolean isNewId(String id) { 843 844 if (id.contains("#")) { 845 id = id.substring(0, id.indexOf("#")); 846 } 847 return !CmsUUID.isValidUUID(id); 848 } 849 850 /** 851 * Sets styles of helper elements, appends the to the drop target and puts them into a drag info bean.<p> 852 * 853 * @param placeholder the placeholder element 854 * @param target the drop target 855 * @param handler the drag and drop handler 856 */ 857 private void prepareDragInfo(Element placeholder, I_CmsDropContainer target, CmsDNDHandler handler) { 858 859 target.getElement().addClassName(I_CmsLayoutBundle.INSTANCE.dragdropCss().dragging()); 860 String positioning = CmsDomUtil.getCurrentStyle( 861 target.getElement(), 862 org.opencms.gwt.client.util.CmsDomUtil.Style.position); 863 // set target relative, if not absolute or fixed 864 if (!Position.ABSOLUTE.getCssName().equals(positioning) && !Position.FIXED.getCssName().equals(positioning)) { 865 target.getElement().getStyle().setPosition(Position.RELATIVE); 866 // check for empty containers that don't have a minimum top and bottom margin to avoid containers overlapping 867 if (target.getElement().getFirstChildElement() == null) { 868 if (CmsDomUtil.getCurrentStyleInt( 869 target.getElement(), 870 CmsDomUtil.Style.marginTop) < MINIMUM_CONTAINER_MARGIN) { 871 target.getElement().getStyle().setMarginTop(MINIMUM_CONTAINER_MARGIN, Unit.PX); 872 } 873 if (CmsDomUtil.getCurrentStyleInt( 874 target.getElement(), 875 CmsDomUtil.Style.marginBottom) < MINIMUM_CONTAINER_MARGIN) { 876 target.getElement().getStyle().setMarginBottom(MINIMUM_CONTAINER_MARGIN, Unit.PX); 877 } 878 } 879 } 880 m_dragInfos.put(target, placeholder); 881 handler.addTarget(target); 882 } 883 884 /** 885 * Prepares the target container.<p> 886 * 887 * @param targetContainer the container 888 * @param draggable the draggable 889 * @param placeholder the placeholder 890 */ 891 private void prepareTargetContainer( 892 I_CmsDropContainer targetContainer, 893 I_CmsDraggable draggable, 894 Element placeholder) { 895 896 String positioning = CmsDomUtil.getCurrentStyle( 897 targetContainer.getElement(), 898 org.opencms.gwt.client.util.CmsDomUtil.Style.position); 899 // set target relative, if not absolute or fixed 900 if (!Position.ABSOLUTE.getCssName().equals(positioning) && !Position.FIXED.getCssName().equals(positioning)) { 901 targetContainer.getElement().getStyle().setPosition(Position.RELATIVE); 902 } 903 m_originalIndex = targetContainer.getWidgetIndex((Widget)draggable); 904 targetContainer.getElement().insertBefore(placeholder, draggable.getElement()); 905 targetContainer.getElement().addClassName(I_CmsLayoutBundle.INSTANCE.dragdropCss().dragging()); 906 CmsDomUtil.showOverlay(draggable.getElement(), true); 907 targetContainer.highlightContainer(); 908 } 909 910 /** 911 * Removes the drag overlay.<p> 912 */ 913 private void removeDragOverlay() { 914 915 if (m_dragOverlay != null) { 916 m_dragOverlay.removeFromParent(); 917 m_dragOverlay = null; 918 } 919 } 920 921 /** 922 * Function which is called when the drag process is stopped, either by cancelling or dropping.<p> 923 * 924 * @param handler the drag and drop handler 925 */ 926 private void stopDrag(final CmsDNDHandler handler) { 927 928 removeDragOverlay(); 929 CmsContainerPageContainer.clearResizeHelper(); 930 for (I_CmsDropTarget target : m_dragInfos.keySet()) { 931 target.getElement().removeClassName(I_CmsLayoutBundle.INSTANCE.dragdropCss().dragging()); 932 target.getElement().removeClassName(I_CmsLayoutBundle.INSTANCE.dragdropCss().clearFix()); 933 Style targetStyle = target.getElement().getStyle(); 934 if (!(target instanceof CmsGroupContainerElementPanel)) { 935 targetStyle.clearPosition(); 936 } 937 targetStyle.clearMarginTop(); 938 targetStyle.clearMarginBottom(); 939 if (target instanceof I_CmsDropContainer) { 940 ((I_CmsDropContainer)target).removeHighlighting(); 941 } 942 } 943 handler.getDragHelper().removeFromParent(); 944 m_copyGroupId = null; 945 m_currentTarget = null; 946 m_currentIndex = -1; 947 m_isNew = false; 948 m_controller.getHandler().deactivateMenuButton(); 949 final List<I_CmsDropTarget> dragTargets = new ArrayList<I_CmsDropTarget>(m_dragInfos.keySet()); 950 m_dragInfos.clear(); 951 Scheduler.get().scheduleDeferred(new ScheduledCommand() { 952 953 /** 954 * @see com.google.gwt.user.client.Command#execute() 955 */ 956 public void execute() { 957 958 handler.clearTargets(); 959 m_controller.resetEditButtons(); 960 // in case of group editing, this will refresh the container position and size 961 for (I_CmsDropTarget target : dragTargets) { 962 if (target instanceof I_CmsDropContainer) { 963 ((I_CmsDropContainer)target).refreshHighlighting(); 964 // reset the nested container infos 965 ((I_CmsDropContainer)target).clearDnDChildren(); 966 } 967 } 968 } 969 }); 970 if (handler.getDraggable() instanceof CmsContainerPageElementPanel) { 971 ((CmsContainerPageElementPanel)(handler.getDraggable())).removeHighlighting(); 972 } 973 } 974 975 /** 976 * Updates the drag target highlighting.<p> 977 * 978 * @param initial <code>true</code> when initially highlighting the drop containers 979 */ 980 private void updateHighlighting(boolean initial) { 981 982 Map<I_CmsDropContainer, CmsPositionBean> containers = new HashMap<I_CmsDropContainer, CmsPositionBean>(); 983 for (I_CmsDropTarget target : m_dragInfos.keySet()) { 984 if ((target instanceof I_CmsDropContainer) 985 && !Display.NONE.getCssName().equalsIgnoreCase( 986 CmsDomUtil.getCurrentStyle(target.getElement(), CmsDomUtil.Style.display))) { 987 if (initial && (target != m_initialDropTarget)) { 988 ((I_CmsDropContainer)target).highlightContainer(); 989 } else { 990 ((I_CmsDropContainer)target).updatePositionInfo(); 991 } 992 containers.put((I_CmsDropContainer)target, ((I_CmsDropContainer)target).getPositionInfo()); 993 } 994 } 995 List<I_CmsDropContainer> containersToMatch = new ArrayList<I_CmsDropContainer>(containers.keySet()); 996 for (I_CmsDropContainer contA : containers.keySet()) { 997 containersToMatch.remove(contA); 998 for (I_CmsDropContainer contB : containersToMatch) { 999 CmsPositionBean posA = containers.get(contA); 1000 CmsPositionBean posB = containers.get(contB); 1001 if (CmsPositionBean.checkCollision(posA, posB, HIGHLIGHTING_OFFSET * 3)) { 1002 if (contA.hasDnDChildren() && contA.getDnDChildren().contains(contB)) { 1003 if (!posA.isInside(posB, HIGHLIGHTING_OFFSET)) { 1004 // the nested container is not completely inside the other 1005 // increase the size of the outer container 1006 posA.ensureSurrounds(posB, HIGHLIGHTING_OFFSET); 1007 } 1008 } else if (contB.hasDnDChildren() && contB.getDnDChildren().contains(contA)) { 1009 if (!posB.isInside(posA, HIGHLIGHTING_OFFSET)) { 1010 // the nested container is not completely inside the other 1011 // increase the size of the outer container 1012 posB.ensureSurrounds(posA, HIGHLIGHTING_OFFSET); 1013 } 1014 } else { 1015 CmsPositionBean.avoidCollision(posA, posB, HIGHLIGHTING_OFFSET * 3); 1016 } 1017 } 1018 } 1019 } 1020 1021 for (Entry<I_CmsDropContainer, CmsPositionBean> containerEntry : containers.entrySet()) { 1022 containerEntry.getKey().refreshHighlighting(containerEntry.getValue()); 1023 } 1024 } 1025}