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.acacia.client.CmsEditorBase; 031import org.opencms.acacia.client.I_CmsInlineFormParent; 032import org.opencms.ade.containerpage.client.CmsContainerpageController; 033import org.opencms.ade.containerpage.client.ui.css.I_CmsLayoutBundle; 034import org.opencms.ade.containerpage.shared.CmsElementLockInfo; 035import org.opencms.ade.containerpage.shared.CmsInheritanceInfo; 036import org.opencms.ade.contenteditor.client.CmsContentEditor; 037import org.opencms.gwt.client.I_CmsElementToolbarContext; 038import org.opencms.gwt.client.dnd.I_CmsDraggable; 039import org.opencms.gwt.client.dnd.I_CmsDropTarget; 040import org.opencms.gwt.client.ui.CmsHighlightingBorder; 041import org.opencms.gwt.client.ui.CmsListItemWidget; 042import org.opencms.gwt.client.ui.I_CmsButton; 043import org.opencms.gwt.client.util.CmsDebugLog; 044import org.opencms.gwt.client.util.CmsDomUtil; 045import org.opencms.gwt.client.util.CmsDomUtil.Tag; 046import org.opencms.gwt.client.util.CmsPositionBean; 047import org.opencms.gwt.shared.CmsGwtConstants; 048import org.opencms.gwt.shared.CmsListInfoBean; 049import org.opencms.util.CmsStringUtil; 050import org.opencms.util.CmsUUID; 051 052import java.util.Arrays; 053import java.util.HashMap; 054import java.util.Iterator; 055import java.util.List; 056import java.util.Map; 057import java.util.Map.Entry; 058 059import com.google.common.base.Optional; 060import com.google.common.collect.Lists; 061import com.google.gwt.core.client.JavaScriptObject; 062import com.google.gwt.core.client.Scheduler; 063import com.google.gwt.core.client.Scheduler.ScheduledCommand; 064import com.google.gwt.dom.client.Element; 065import com.google.gwt.dom.client.NodeList; 066import com.google.gwt.dom.client.Style; 067import com.google.gwt.dom.client.Style.Display; 068import com.google.gwt.dom.client.Style.Position; 069import com.google.gwt.dom.client.Style.Unit; 070import com.google.gwt.event.dom.client.ClickEvent; 071import com.google.gwt.event.dom.client.ClickHandler; 072import com.google.gwt.event.dom.client.HasClickHandlers; 073import com.google.gwt.event.shared.HandlerRegistration; 074import com.google.gwt.regexp.shared.MatchResult; 075import com.google.gwt.regexp.shared.RegExp; 076import com.google.gwt.user.client.DOM; 077import com.google.gwt.user.client.Event; 078import com.google.gwt.user.client.Event.NativePreviewEvent; 079import com.google.gwt.user.client.Event.NativePreviewHandler; 080import com.google.gwt.user.client.Timer; 081import com.google.gwt.user.client.ui.AbsolutePanel; 082import com.google.gwt.user.client.ui.IsWidget; 083import com.google.gwt.user.client.ui.RootPanel; 084 085/** 086 * Content element within a container-page.<p> 087 * 088 * @since 8.0.0 089 */ 090public class CmsContainerPageElementPanel extends AbsolutePanel 091implements I_CmsDraggable, HasClickHandlers, I_CmsInlineFormParent { 092 093 /** 094 * Parses CSS classess of the form 'oc-point-TY_LX', where X and Y are strings 095 * of decimal digits possibly preceded by a minus sign.<p> 096 * 097 * The numeric values of Y and X will be available after a successful parse using the 098 * methods getOffsetTop() and getOffsetLeft(). 099 * 100 * This is used to offer the formatter developer some control over the edit point 101 * positioning. 102 */ 103 public static class PointPositioningParser { 104 105 /** Regular expression used to match the special CSS classes. */ 106 private static final RegExp REGEX = RegExp.compile("^oc-point-T(-?[0-9]+)_L(-?[0-9]+)$"); 107 108 /** The left offset. */ 109 private int m_left; 110 111 /** The top offset. */ 112 private int m_top; 113 114 /** 115 * Gets the left offset after a CSS class has successfully been parsed.<p> 116 * 117 * @return the left offset 118 */ 119 int getOffsetLeft() { 120 121 return m_left; 122 } 123 124 /** 125 * Gets the top offset after a CSS class has successfully been parsed.<p> 126 * 127 * @return the top offset 128 */ 129 int getOffsetTop() { 130 131 return m_top; 132 } 133 134 /** 135 * Tries to parse a point positioning instruction from an element's class attribute 136 * and returns true when successful.<p> 137 * 138 * @param cssClass the value of a class attribute 139 * 140 * @return true if a positioning instruction was found 141 */ 142 boolean tryParse(String cssClass) { 143 144 m_left = 0; 145 m_top = 0; 146 if (cssClass == null) { 147 cssClass = ""; 148 } 149 for (String token : cssClass.trim().split(" +")) { 150 if (tryParseSingleClass(token)) { 151 return true; 152 } 153 } 154 return false; 155 } 156 157 /** 158 * Parses a single token from a class attribute.<p> 159 * 160 * @param token the token 161 * @return true if the token was a point positioning instruction 162 */ 163 private boolean tryParseSingleClass(String token) { 164 165 MatchResult result = REGEX.exec(token); 166 if (result != null) { 167 m_top = Integer.parseInt(result.getGroup(1)); 168 m_left = Integer.parseInt(result.getGroup(2)); 169 return true; 170 } else { 171 if (token.startsWith("oc-point")) { 172 CmsDebugLog.consoleLog("Malformed oc-point class: " + token); 173 } 174 return false; 175 } 176 } 177 178 } 179 180 /** The is model group property key. */ 181 public static final String PROP_IS_MODEL_GROUP = "is_model_group"; 182 183 /** The former copy model property. */ 184 public static final String PROP_WAS_MODEL_GROUP = "was_model_group"; 185 186 /** Highlighting border for this element. */ 187 protected CmsHighlightingBorder m_highlighting; 188 189 /** A flag which indicates whether the height has already been checked. */ 190 private boolean m_checkedHeight; 191 192 /** Flag indicating the the editables are currently being checked. */ 193 private boolean m_checkingEditables; 194 195 /** The elements client id. */ 196 private String m_clientId; 197 198 /** The 'create new' flag. */ 199 private boolean m_createNew; 200 201 /** 202 * Flag which indicates whether the new editor is disabled for this element.<p> 203 */ 204 private boolean m_disableNewEditor; 205 206 /** The direct edit bar instances. */ 207 private Map<Element, CmsListCollectorEditor> m_editables; 208 209 /** The editor click handler registration. */ 210 private HandlerRegistration m_editorClickHandlerRegistration; 211 212 /** The option bar, holding optional function buttons. */ 213 private CmsElementOptionBar m_elementOptionBar; 214 215 /** The element element view. */ 216 private CmsUUID m_elementView; 217 218 /** The overlay for expired elements. */ 219 private Element m_expiredOverlay; 220 221 /** Indicates an edit handler is configured for the element resource type. */ 222 private boolean m_hasEditHandler; 223 224 /** Indicates whether this element has settings to edit. */ 225 private boolean m_hasSettings; 226 227 /** The inheritance info for this element. */ 228 private CmsInheritanceInfo m_inheritanceInfo; 229 230 /** The model group id. */ 231 private CmsUUID m_modelGroupId; 232 233 /** The is new element type. */ 234 private String m_newType; 235 236 /** The registered node insert event handler. */ 237 private JavaScriptObject m_nodeInsertHandler; 238 239 /** The no edit reason, if empty editing is allowed. */ 240 private String m_noEditReason; 241 242 /** The parent drop target. */ 243 private I_CmsDropContainer m_parent; 244 245 /** Parser for point positioning isntructions. */ 246 private PointPositioningParser m_positioningInstructionParser = new PointPositioningParser(); 247 248 /** The drag and drop parent div. */ 249 private Element m_provisionalParent; 250 251 /** Flag indicating if the element resource is currently released and not expired. */ 252 private boolean m_releasedAndNotExpired; 253 254 /** The resource type. */ 255 private String m_resourceType; 256 257 /** The element resource site-path. */ 258 private String m_sitePath; 259 260 /** The sub title. */ 261 private String m_subTitle; 262 263 /** The resource title. */ 264 private String m_title; 265 266 /** 267 * Indicates if the current user has view permissions on the element resource. 268 * Without view permissions, the element can neither be edited, nor moved. 269 **/ 270 private boolean m_viewPermission; 271 272 /** The former copy model status. */ 273 private boolean m_wasModelGroup; 274 /** 275 * Indicates if the current user has write permissions on the element resource. 276 * Without write permissions, the element can not be edited. 277 **/ 278 private boolean m_writePermission; 279 280 /** The resource type icon CSS classes. */ 281 private String m_iconClasses; 282 283 /** The lock information. */ 284 private CmsElementLockInfo m_lockInfo; 285 286 /** 287 * Constructor.<p> 288 * 289 * @param element the DOM element 290 * @param parent the drag parent 291 * @param clientId the client id 292 * @param sitePath the element site-path 293 * @param noEditReason the no edit reason, if empty, editing is allowed 294 * @param lockInfo the lock information 295 * @param title the resource title 296 * @param subTitle the sub title 297 * @param resourceType the resource type 298 * @param hasSettings should be true if the element has settings which can be edited 299 * @param hasViewPermission indicates if the current user has view permissions on the element resource 300 * @param hasWritePermission indicates if the current user has write permissions on the element resource 301 * @param releasedAndNotExpired <code>true</code> if the element resource is currently released and not expired 302 * @param disableNewEditor flag to disable the new editor for this element 303 * @param hasEditHandler indicates an edit handler is configured for the element resource type 304 * @param modelGroupId the model group id 305 * @param wasModelGroup in case of a former copy model group 306 * @param elementView the element view of the element 307 * @param iconClasses the resource type icon CSS classes 308 */ 309 public CmsContainerPageElementPanel( 310 Element element, 311 I_CmsDropContainer parent, 312 String clientId, 313 String sitePath, 314 String noEditReason, 315 CmsElementLockInfo lockInfo, 316 String title, 317 String subTitle, 318 String resourceType, 319 boolean hasSettings, 320 boolean hasViewPermission, 321 boolean hasWritePermission, 322 boolean releasedAndNotExpired, 323 boolean disableNewEditor, 324 boolean hasEditHandler, 325 CmsUUID modelGroupId, 326 boolean wasModelGroup, 327 CmsUUID elementView, 328 String iconClasses) { 329 330 super(element); 331 m_clientId = clientId; 332 m_sitePath = sitePath; 333 m_title = title; 334 m_subTitle = subTitle; 335 m_resourceType = resourceType; 336 m_noEditReason = noEditReason; 337 m_lockInfo = lockInfo; 338 m_hasSettings = hasSettings; 339 m_parent = parent; 340 m_disableNewEditor = disableNewEditor; 341 m_modelGroupId = modelGroupId; 342 m_wasModelGroup = wasModelGroup; 343 m_hasEditHandler = hasEditHandler; 344 setViewPermission(hasViewPermission); 345 setWritePermission(hasWritePermission); 346 setReleasedAndNotExpired(releasedAndNotExpired); 347 m_elementView = elementView; 348 getElement().setPropertyBoolean(PROP_IS_MODEL_GROUP, modelGroupId != null); 349 getElement().setPropertyBoolean(PROP_WAS_MODEL_GROUP, wasModelGroup); 350 m_iconClasses = iconClasses; 351 } 352 353 /** 354 * Checks if the element is an overlay for a container page element.<p> 355 * 356 * @param element the element to check 357 * @return true if the element is an overlay 358 */ 359 public static boolean isOverlay(Element element) { 360 361 for (String overlayClass : Arrays.asList( 362 I_CmsLayoutBundle.INSTANCE.containerpageCss().expiredOverlay(), 363 I_CmsElementToolbarContext.ELEMENT_OPTION_BAR_CSS_CLASS)) { 364 if (element.hasClassName(overlayClass)) { 365 return true; 366 } 367 } 368 return false; 369 370 } 371 372 /** 373 * @see com.google.gwt.event.dom.client.HasClickHandlers#addClickHandler(com.google.gwt.event.dom.client.ClickHandler) 374 */ 375 public HandlerRegistration addClickHandler(ClickHandler handler) { 376 377 return addDomHandler(handler, ClickEvent.getType()); 378 } 379 380 /** 381 * @see org.opencms.acacia.client.I_CmsInlineFormParent#adoptWidget(com.google.gwt.user.client.ui.IsWidget) 382 */ 383 public void adoptWidget(IsWidget widget) { 384 385 getChildren().add(widget.asWidget()); 386 adopt(widget.asWidget()); 387 } 388 389 /** 390 * @see org.opencms.gwt.client.dnd.I_CmsDraggable#getCursorOffsetDelta() 391 */ 392 public Optional<int[]> getCursorOffsetDelta() { 393 394 return Optional.absent(); 395 } 396 397 /** 398 * @see org.opencms.gwt.client.dnd.I_CmsDraggable#getDragHelper(org.opencms.gwt.client.dnd.I_CmsDropTarget) 399 */ 400 public Element getDragHelper(I_CmsDropTarget target) { 401 402 CmsListInfoBean info = new CmsListInfoBean(m_title, m_subTitle, null); 403 info.setResourceType(m_resourceType); 404 info.setBigIconClasses(m_iconClasses); 405 CmsListItemWidget helperWidget = new CmsListItemWidget(info); 406 helperWidget.setWidth("600px"); 407 helperWidget.truncate("ggg", 550); 408 Element helper = helperWidget.getElement(); 409 Element button = DOM.createDiv(); 410 button.addClassName("opencms-icon"); 411 button.addClassName(I_CmsButton.MOVE_SMALL); 412 button.addClassName(I_CmsLayoutBundle.INSTANCE.dragdropCss().dragHandle()); 413 helper.appendChild(button); 414 helper.addClassName(org.opencms.gwt.client.ui.css.I_CmsLayoutBundle.INSTANCE.generalCss().shadow()); 415 Element parentElement = getElement().getParentElement(); 416 int elementTop = getElement().getAbsoluteTop(); 417 int parentTop = parentElement.getAbsoluteTop(); 418 m_provisionalParent = DOM.createElement(parentElement.getTagName()); 419 RootPanel.getBodyElement().appendChild(m_provisionalParent); 420 m_provisionalParent.addClassName( 421 org.opencms.gwt.client.ui.css.I_CmsLayoutBundle.INSTANCE.generalCss().clearStyles()); 422 m_provisionalParent.addClassName( 423 org.opencms.gwt.client.ui.css.I_CmsLayoutBundle.INSTANCE.generalCss().opencms()); 424 m_provisionalParent.getStyle().setWidth(parentElement.getOffsetWidth(), Unit.PX); 425 m_provisionalParent.appendChild(helper); 426 Style style = helper.getStyle(); 427 style.setWidth(helper.getOffsetWidth(), Unit.PX); 428 // the dragging class will set position absolute 429 helper.addClassName(org.opencms.gwt.client.ui.css.I_CmsLayoutBundle.INSTANCE.listItemWidgetCss().dragging()); 430 style.setTop(elementTop - parentTop, Unit.PX); 431 m_provisionalParent.getStyle().setPosition(Position.ABSOLUTE); 432 m_provisionalParent.getStyle().setTop(parentTop, Unit.PX); 433 m_provisionalParent.getStyle().setLeft(parentElement.getAbsoluteLeft(), Unit.PX); 434 m_provisionalParent.getStyle().setZIndex(I_CmsLayoutBundle.INSTANCE.constants().css().zIndexDND()); 435 436 return helper; 437 } 438 439 /** 440 * Returns the option bar of this element.<p> 441 * 442 * @return the option bar widget 443 */ 444 public CmsElementOptionBar getElementOptionBar() { 445 446 return m_elementOptionBar; 447 } 448 449 /** 450 * Returns the elements element view.<p> 451 * 452 * @return the element view 453 */ 454 public CmsUUID getElementView() { 455 456 return m_elementView; 457 } 458 459 /** 460 * @see org.opencms.gwt.client.dnd.I_CmsDraggable#getId() 461 */ 462 public String getId() { 463 464 return m_clientId; 465 } 466 467 /** 468 * Returns the inheritance info for this element.<p> 469 * 470 * @return the inheritance info for this element 471 */ 472 public CmsInheritanceInfo getInheritanceInfo() { 473 474 return m_inheritanceInfo; 475 } 476 477 /** 478 * Gets the lock information. 479 * 480 * @return the lock information 481 */ 482 public CmsElementLockInfo getLockInfo() { 483 484 return m_lockInfo != null ? m_lockInfo : new CmsElementLockInfo(null, false); 485 } 486 487 /** 488 * Returns the model group id.<p> 489 * 490 * @return the model group id 491 */ 492 public CmsUUID getModelGroupId() { 493 494 return m_modelGroupId; 495 } 496 497 /** 498 * Returns the new element type. 499 * 500 * @return the new element type 501 */ 502 public String getNewType() { 503 504 return m_newType; 505 } 506 507 /** 508 * Returns the no edit reason.<p> 509 * 510 * @return the no edit reason 511 */ 512 public String getNoEditReason() { 513 514 return m_noEditReason; 515 } 516 517 /** 518 * @see org.opencms.gwt.client.dnd.I_CmsDraggable#getParentTarget() 519 */ 520 public I_CmsDropContainer getParentTarget() { 521 522 return m_parent; 523 } 524 525 /** 526 * @see org.opencms.gwt.client.dnd.I_CmsDraggable#getPlaceholder(org.opencms.gwt.client.dnd.I_CmsDropTarget) 527 */ 528 public Element getPlaceholder(I_CmsDropTarget target) { 529 530 Element placeholder = CmsDomUtil.clone(getElement()); 531 placeholder.addClassName(I_CmsLayoutBundle.INSTANCE.dragdropCss().dragPlaceholder()); 532 return placeholder; 533 } 534 535 /** 536 * Returns if the element resource is currently released and not expired.<p> 537 * 538 * @return <code>true</code> if the element resource is currently released and not expired 539 */ 540 public boolean getReleasedAndNotExpired() { 541 542 return m_releasedAndNotExpired; 543 } 544 545 /** 546 * Returns the element resource type name.<p> 547 * 548 * @return the resource type name 549 */ 550 public String getResourceType() { 551 552 return m_resourceType; 553 } 554 555 /** 556 * Returns the site-path.<p> 557 * 558 * @return the site-path 559 */ 560 public String getSitePath() { 561 562 return m_sitePath; 563 } 564 565 /** 566 * Returns the structure id of the element.<p> 567 * 568 * @return the structure id of the element 569 */ 570 public CmsUUID getStructureId() { 571 572 if (m_clientId == null) { 573 return null; 574 } 575 return new CmsUUID(CmsContainerpageController.getServerId(m_clientId)); 576 } 577 578 /** 579 * Indicates an edit handler is configured for the element resource type.<p> 580 * 581 * @return indicates an edit handler is configured for the element resource type 582 */ 583 public boolean hasEditHandler() { 584 585 return m_hasEditHandler; 586 } 587 588 /** 589 * Returns whether this element has a model group parent.<p> 590 * 591 * @return <code>true</code> if this element has a model group parent 592 */ 593 public boolean hasModelGroupParent() { 594 595 boolean result = false; 596 Element parent = getElement().getParentElement(); 597 while (parent != null) { 598 if (parent.getPropertyBoolean(PROP_IS_MODEL_GROUP)) { 599 result = true; 600 break; 601 } 602 parent = parent.getParentElement(); 603 } 604 return result; 605 } 606 607 /** 608 * In case the inner HTML contains the reload marker.<p> 609 * 610 * @return <code>true</code> in case the inner HTML contains the reload marker 611 */ 612 public boolean hasReloadMarker() { 613 614 return getElement().getInnerHTML().contains(CmsGwtConstants.FORMATTER_RELOAD_MARKER); 615 } 616 617 /** 618 * Returns true if the element has settings to edit.<p> 619 * 620 * @return true if the element has settings to edit 621 */ 622 public boolean hasSettings() { 623 624 return m_hasSettings; 625 } 626 627 /** 628 * Returns if the current user has view permissions for the element resource.<p> 629 * 630 * @return <code>true</code> if the current user has view permissions for the element resource 631 */ 632 public boolean hasViewPermission() { 633 634 return m_viewPermission; 635 } 636 637 /** 638 * Returns if the user has write permission.<p> 639 * 640 * @return <code>true</code> if the user has write permission 641 */ 642 public boolean hasWritePermission() { 643 644 return m_writePermission; 645 } 646 647 /** 648 * Hides list collector direct edit buttons, if present.<p> 649 */ 650 public void hideEditableListButtons() { 651 652 if (m_editables != null) { 653 for (CmsListCollectorEditor editor : m_editables.values()) { 654 editor.getElement().getStyle().setDisplay(Display.NONE); 655 } 656 } 657 } 658 659 /** 660 * Puts a highlighting border around the element.<p> 661 */ 662 public void highlightElement() { 663 664 CmsPositionBean position = CmsPositionBean.getBoundingClientRect(getElement()); 665 if (m_highlighting == null) { 666 m_highlighting = new CmsHighlightingBorder( 667 position, 668 isNew() || (CmsContainerpageController.get().getData().isModelPage() && isCreateNew()) 669 ? CmsHighlightingBorder.BorderColor.blue 670 : CmsHighlightingBorder.BorderColor.red); 671 RootPanel.get().add(m_highlighting); 672 } else { 673 m_highlighting.setPosition(position); 674 } 675 } 676 677 /** 678 * Initializes the editor click handler.<p> 679 * 680 * @param controller the container page controller instance 681 */ 682 public void initInlineEditor(final CmsContainerpageController controller) { 683 684 if (CmsStringUtil.isEmptyOrWhitespaceOnly(m_noEditReason) 685 && !m_disableNewEditor 686 && CmsContentEditor.setEditable(getElement(), CmsContainerpageController.getServerId(m_clientId), true)) { 687 if (m_editorClickHandlerRegistration != null) { 688 m_editorClickHandlerRegistration.removeHandler(); 689 } 690 m_editorClickHandlerRegistration = Event.addNativePreviewHandler(new NativePreviewHandler() { 691 692 public void onPreviewNativeEvent(NativePreviewEvent event) { 693 694 if (event.getTypeInt() == Event.ONCLICK) { 695 // if another content is already being edited, don't start another editor 696 if (controller.isContentEditing()) { 697 return; 698 } 699 Element eventTarget = event.getNativeEvent().getEventTarget().cast(); 700 // check if the event target is a child 701 if (getElement().isOrHasChild(eventTarget)) { 702 Element target = event.getNativeEvent().getEventTarget().cast(); 703 // check if the target closest ancestor drag element is this element 704 Element parentContainerElement = CmsDomUtil.getAncestor( 705 target, 706 I_CmsLayoutBundle.INSTANCE.dragdropCss().dragElement()); 707 if (parentContainerElement == getElement()) { 708 while ((target != null) 709 && !target.getTagName().equalsIgnoreCase("a") 710 && (target != getElement())) { 711 if (CmsContentEditor.isEditable(target)) { 712 CmsEditorBase.markForInlineFocus(target); 713 controller.getHandler().openEditorForElement( 714 CmsContainerPageElementPanel.this, 715 true, 716 isNew()); 717 removeEditorHandler(); 718 event.cancel(); 719 break; 720 } else { 721 target = target.getParentElement(); 722 } 723 } 724 } 725 } 726 } 727 } 728 }); 729 } 730 } 731 732 /** 733 * Checks if this element has 'createNew' status, i.e. will be copied when using this page as a model for a new container page.<p> 734 * 735 * @return true if this element has createNew status 736 */ 737 public boolean isCreateNew() { 738 739 return m_createNew; 740 } 741 742 /** 743 * Returns whether the element is used as a model group.<p> 744 * 745 * @return <code>true</code> if the element is used as a model group 746 */ 747 public boolean isModelGroup() { 748 749 return m_modelGroupId != null; 750 } 751 752 /** 753 * Returns if this is e newly created element.<p> 754 * 755 * @return <code>true</code> if the element is new 756 */ 757 public boolean isNew() { 758 759 return m_newType != null; 760 } 761 762 /** 763 * Returns true if the new content editor is disabled for this element.<p> 764 * 765 * @return true if the new editor is disabled for this element 766 */ 767 public boolean isNewEditorDisabled() { 768 769 return m_disableNewEditor; 770 } 771 772 /** 773 * @see org.opencms.gwt.client.dnd.I_CmsDraggable#onDragCancel() 774 */ 775 public void onDragCancel() { 776 777 clearDrag(); 778 resetOptionbar(); 779 } 780 781 /** 782 * @see org.opencms.gwt.client.dnd.I_CmsDraggable#onDrop(org.opencms.gwt.client.dnd.I_CmsDropTarget) 783 */ 784 public void onDrop(I_CmsDropTarget target) { 785 786 clearDrag(); 787 } 788 789 /** 790 * @see org.opencms.gwt.client.dnd.I_CmsDraggable#onStartDrag(org.opencms.gwt.client.dnd.I_CmsDropTarget) 791 */ 792 public void onStartDrag(I_CmsDropTarget target) { 793 794 CmsDomUtil.addDisablingOverlay(getElement()); 795 getElement().getStyle().setOpacity(0.7); 796 removeHighlighting(); 797 } 798 799 /** 800 * @see com.google.gwt.user.client.ui.Widget#removeFromParent() 801 */ 802 @Override 803 public void removeFromParent() { 804 805 if (m_highlighting != null) { 806 m_highlighting.removeFromParent(); 807 m_highlighting = null; 808 } 809 super.removeFromParent(); 810 } 811 812 /** 813 * Removes the highlighting border.<p> 814 */ 815 public void removeHighlighting() { 816 817 if (m_highlighting != null) { 818 m_highlighting.removeFromParent(); 819 m_highlighting = null; 820 } 821 } 822 823 /** 824 * Removes the inline editor.<p> 825 */ 826 public void removeInlineEditor() { 827 828 CmsContentEditor.setEditable(getElement(), CmsContainerpageController.getServerId(m_clientId), false); 829 removeEditorHandler(); 830 } 831 832 /** 833 * @see org.opencms.acacia.client.I_CmsInlineFormParent#replaceHtml(java.lang.String) 834 */ 835 public void replaceHtml(String html) { 836 837 // detach all children first 838 while (getChildren().size() > 0) { 839 getChildren().get(getChildren().size() - 1).removeFromParent(); 840 } 841 Element tempDiv = DOM.createDiv(); 842 tempDiv.setInnerHTML(html); 843 getElement().setInnerHTML(tempDiv.getFirstChildElement().getInnerHTML()); 844 } 845 846 /** 847 * Sets the 'create new' status of the element.<p> 848 * 849 * @param createNew the new value for the 'create new' status 850 */ 851 public void setCreateNew(boolean createNew) { 852 853 m_createNew = createNew; 854 } 855 856 /** 857 * Sets the element option bar.<p> 858 * 859 * @param elementOptionBar the element option bar to set 860 */ 861 public void setElementOptionBar(CmsElementOptionBar elementOptionBar) { 862 863 if ((m_elementOptionBar != null) && (getWidgetIndex(m_elementOptionBar) >= 0)) { 864 m_elementOptionBar.removeFromParent(); 865 } 866 m_elementOptionBar = elementOptionBar; 867 if (m_elementOptionBar != null) { 868 getElement().addClassName(I_CmsLayoutBundle.INSTANCE.dragdropCss().dragElement()); 869 insert(m_elementOptionBar, 0); 870 updateOptionBarPosition(); 871 } else { 872 getElement().removeClassName(I_CmsLayoutBundle.INSTANCE.dragdropCss().dragElement()); 873 } 874 } 875 876 /** 877 * Sets the element id.<p> 878 * 879 * @param id the id 880 */ 881 public void setId(String id) { 882 883 m_clientId = id; 884 } 885 886 /** 887 * Sets the inheritance info for this element.<p> 888 * 889 * @param inheritanceInfo the inheritance info for this element to set 890 */ 891 public void setInheritanceInfo(CmsInheritanceInfo inheritanceInfo) { 892 893 m_inheritanceInfo = inheritanceInfo; 894 } 895 896 /** 897 * Sets the new-type of the element.<p> 898 * 899 * @param newType the new-type 900 */ 901 public void setNewType(String newType) { 902 903 m_newType = newType; 904 } 905 906 /** 907 * Sets the no edit reason.<p> 908 * 909 * @param noEditReason the no edit reason to set 910 */ 911 public void setNoEditReason(String noEditReason) { 912 913 m_noEditReason = noEditReason; 914 } 915 916 /** 917 * Sets if the element resource is currently released and not expired.<p> 918 * 919 * @param releasedAndNotExpired <code>true</code> if the element resource is currently released and not expired 920 */ 921 public void setReleasedAndNotExpired(boolean releasedAndNotExpired) { 922 923 m_releasedAndNotExpired = releasedAndNotExpired; 924 if (m_releasedAndNotExpired) { 925 removeStyleName(I_CmsLayoutBundle.INSTANCE.containerpageCss().expired()); 926 if (m_expiredOverlay != null) { 927 m_expiredOverlay.removeFromParent(); 928 m_expiredOverlay = null; 929 } 930 931 } else { 932 addStyleName(I_CmsLayoutBundle.INSTANCE.containerpageCss().expired()); 933 m_expiredOverlay = DOM.createDiv(); 934 m_expiredOverlay.setTitle("Expired resource"); 935 m_expiredOverlay.addClassName(I_CmsLayoutBundle.INSTANCE.containerpageCss().expiredOverlay()); 936 getElement().appendChild(m_expiredOverlay); 937 } 938 } 939 940 /** 941 * Sets the site path.<p> 942 * 943 * @param sitePath the site path to set 944 */ 945 public void setSitePath(String sitePath) { 946 947 m_sitePath = sitePath; 948 } 949 950 /** 951 * Sets if the current user has view permissions for the element resource.<p> 952 * 953 * @param viewPermission the view permission to set 954 */ 955 public void setViewPermission(boolean viewPermission) { 956 957 m_viewPermission = viewPermission; 958 } 959 960 /** 961 * Sets the user write permission.<p> 962 * 963 * @param writePermission the user write permission to set 964 */ 965 public void setWritePermission(boolean writePermission) { 966 967 m_writePermission = writePermission; 968 } 969 970 /** 971 * Shows list collector direct edit buttons (old direct edit style), if present.<p> 972 */ 973 public void showEditableListButtons() { 974 975 m_checkingEditables = true; 976 if (m_editables == null) { 977 m_editables = new HashMap<Element, CmsListCollectorEditor>(); 978 List<Element> editables = getEditableElements(); 979 if ((editables != null) && (editables.size() > 0)) { 980 for (Element editable : editables) { 981 addListCollectorEditorButtons(editable); 982 } 983 } 984 } else { 985 986 Iterator<Entry<Element, CmsListCollectorEditor>> it = m_editables.entrySet().iterator(); 987 while (it.hasNext()) { 988 Entry<Element, CmsListCollectorEditor> entry = it.next(); 989 CmsListCollectorEditor editor = entry.getValue(); 990 if (!editor.isValid()) { 991 editor.removeFromParent(); 992 it.remove(); 993 } else { 994 if (CmsDomUtil.hasDimension(editor.getElement().getParentElement())) { 995 editor.setParentHasDimensions(true); 996 editor.setPosition( 997 CmsDomUtil.getEditablePosition(entry.getValue().getMarkerTag()), 998 getElement()); 999 } else { 1000 editor.setParentHasDimensions(false); 1001 } 1002 } 1003 } 1004 List<Element> editables = getEditableElements(); 1005 if (editables.size() > m_editables.size()) { 1006 for (Element editable : editables) { 1007 if (!m_editables.containsKey(editable)) { 1008 addListCollectorEditorButtons(editable); 1009 } 1010 } 1011 } 1012 } 1013 1014 boolean editableContainer = true; 1015 if (m_parent instanceof CmsContainerPageContainer) { 1016 editableContainer = CmsContainerpageController.get().isContainerEditable(m_parent); 1017 } 1018 for (CmsListCollectorEditor editor : m_editables.values()) { 1019 editor.updateVisibility(editableContainer); 1020 } 1021 1022 m_checkingEditables = false; 1023 resetNodeInsertedHandler(); 1024 } 1025 1026 /** 1027 * Updates the option bar position.<p> 1028 */ 1029 public void updateOptionBarPosition() { 1030 1031 // only if attached to the DOM 1032 if ((m_elementOptionBar != null) && RootPanel.getBodyElement().isOrHasChild(getElement())) { 1033 int absoluteTop = getElement().getAbsoluteTop(); 1034 int absoluteRight = getElement().getAbsoluteRight(); 1035 CmsPositionBean dimensions = CmsPositionBean.getBoundingClientRect(getElement()); 1036 1037 int top = 0; 1038 int right = 0; 1039 int offsetLeft = 0; 1040 int offsetTop = 0; 1041 1042 final Style style = m_elementOptionBar.getElement().getStyle(); 1043 1044 if (m_positioningInstructionParser.tryParse(getElement().getClassName())) { 1045 offsetLeft = m_positioningInstructionParser.getOffsetLeft(); 1046 offsetTop = m_positioningInstructionParser.getOffsetTop(); 1047 } 1048 1049 if (Math.abs(absoluteTop - dimensions.getTop()) > 20) { 1050 absoluteTop = (dimensions.getTop() - absoluteTop) + 2; 1051 top = absoluteTop; 1052 } 1053 if (Math.abs(absoluteRight - dimensions.getLeft() - dimensions.getWidth()) > 20) { 1054 absoluteRight = (absoluteRight - dimensions.getLeft() - dimensions.getWidth()) + 2; 1055 right = absoluteRight; 1056 } 1057 1058 top += offsetTop; 1059 right -= offsetLeft; 1060 1061 if (top != 0) { 1062 style.setTop(top, Unit.PX); 1063 } else { 1064 style.clearTop(); 1065 } 1066 1067 if (right != 0) { 1068 style.setRight(right, Unit.PX); 1069 } else { 1070 style.clearRight(); 1071 } 1072 1073 if (isOptionbarIFrameCollision(absoluteTop, m_elementOptionBar.getCalculatedWidth())) { 1074 style.setPosition(Position.RELATIVE); 1075 int marginLeft = getElement().getClientWidth() - m_elementOptionBar.getCalculatedWidth(); 1076 if (marginLeft > 0) { 1077 style.setMarginLeft(marginLeft, Unit.PX); 1078 } 1079 } else { 1080 style.clearPosition(); 1081 style.clearMarginLeft(); 1082 } 1083 } 1084 } 1085 1086 /** 1087 * Checks for changes in the list collector direct edit content.<p> 1088 */ 1089 protected void checkForEditableChanges() { 1090 1091 if (!m_checkingEditables) { 1092 m_checkingEditables = true; 1093 Timer timer = new Timer() { 1094 1095 @Override 1096 public void run() { 1097 1098 showEditableListButtons(); 1099 } 1100 }; 1101 timer.schedule(500); 1102 } 1103 } 1104 1105 /** 1106 * Gets the editable list elements.<p> 1107 * 1108 * @return the editable list elements 1109 */ 1110 protected List<Element> getEditableElements() { 1111 1112 List<Element> elems = CmsDomUtil.getElementsByClass(CmsGwtConstants.CLASS_EDITABLE, Tag.div, getElement()); 1113 List<Element> result = Lists.newArrayList(); 1114 for (Element currentElem : elems) { 1115 // don't return elements which are contained in nested containers 1116 if (m_parent.getContainerId().equals(getParentContainerId(currentElem))) { 1117 result.add(currentElem); 1118 } 1119 } 1120 return result; 1121 } 1122 1123 /** 1124 * Returns if the list collector direct edit content has changed.<p> 1125 * 1126 * @return <code>true</code> if the list collector direct edit content has changed 1127 */ 1128 protected boolean hasChangedEditables() { 1129 1130 if (m_editables == null) { 1131 return true; 1132 } 1133 for (CmsListCollectorEditor editor : m_editables.values()) { 1134 if (!editor.isValid()) { 1135 return true; 1136 } 1137 } 1138 return getEditableElements().size() > m_editables.size(); 1139 } 1140 1141 /** 1142 * @see com.google.gwt.user.client.ui.Widget#onDetach() 1143 */ 1144 @Override 1145 protected void onDetach() { 1146 1147 super.onDetach(); 1148 removeEditorHandler(); 1149 } 1150 1151 /** 1152 * @see com.google.gwt.user.client.ui.Widget#onLoad() 1153 */ 1154 @Override 1155 protected void onLoad() { 1156 1157 if ((getParentTarget() instanceof CmsContainerPageContainer) 1158 && ((CmsContainerPageContainer)getParentTarget()).isEditable() 1159 && !hasCheckedHeight()) { 1160 Scheduler.get().scheduleDeferred(new ScheduledCommand() { 1161 1162 public void execute() { 1163 1164 CmsContainerPageElementPanel thisElement = CmsContainerPageElementPanel.this; 1165 if (!hasCheckedHeight() && CmsSmallElementsHandler.isSmall(thisElement)) { 1166 CmsContainerpageController.get().getSmallElementsHandler().prepareSmallElement(thisElement); 1167 } 1168 setCheckedHeight(true); 1169 } 1170 }); 1171 } 1172 resetOptionbar(); 1173 } 1174 1175 /** 1176 * Removes the inline editor handler.<p> 1177 */ 1178 protected void removeEditorHandler() { 1179 1180 if (m_editorClickHandlerRegistration != null) { 1181 m_editorClickHandlerRegistration.removeHandler(); 1182 m_editorClickHandlerRegistration = null; 1183 } 1184 } 1185 1186 /** 1187 * Returns if the minimum element height has been checked.<p> 1188 * 1189 * @return <code>true</code> if the minimum element height has been checked 1190 */ 1191 boolean hasCheckedHeight() { 1192 1193 return m_checkedHeight; 1194 } 1195 1196 /** 1197 * Sets the checked height flag.<p> 1198 * 1199 * @param checked the checked height flag 1200 */ 1201 void setCheckedHeight(boolean checked) { 1202 1203 m_checkedHeight = checked; 1204 } 1205 1206 /** 1207 * Adds the collector edit buttons.<p> 1208 * 1209 * @param editable the marker element for an editable list element 1210 */ 1211 private void addListCollectorEditorButtons(Element editable) { 1212 1213 CmsListCollectorEditor editor = new CmsListCollectorEditor(editable, m_clientId); 1214 add(editor, editable.getParentElement()); 1215 if (CmsDomUtil.hasDimension(editable.getParentElement())) { 1216 editor.setParentHasDimensions(true); 1217 editor.setPosition(CmsDomUtil.getEditablePosition(editable), getElement()); 1218 } else { 1219 editor.setParentHasDimensions(false); 1220 } 1221 m_editables.put(editable, editor); 1222 } 1223 1224 /** 1225 * Removes all styling done during drag and drop.<p> 1226 */ 1227 private void clearDrag() { 1228 1229 CmsDomUtil.removeDisablingOverlay(getElement()); 1230 m_elementOptionBar.getElement().removeClassName( 1231 org.opencms.gwt.client.ui.css.I_CmsLayoutBundle.INSTANCE.stateCss().cmsHovering()); 1232 getElement().getStyle().clearOpacity(); 1233 getElement().getStyle().clearDisplay(); 1234 updateOptionBarPosition(); 1235 if (m_provisionalParent != null) { 1236 m_provisionalParent.removeFromParent(); 1237 m_provisionalParent = null; 1238 } 1239 } 1240 1241 /** 1242 * Gets the container id of the most deeply nested container containing the given element, or null if no such container can be found.<p> 1243 * 1244 * @param elem the element 1245 * @return the container id of the deepest container containing the element 1246 */ 1247 private String getParentContainerId(Element elem) { 1248 1249 String attr = CmsContainerPageContainer.PROP_CONTAINER_MARKER; 1250 Element lastElem; 1251 do { 1252 String propValue = elem.getPropertyString(attr); 1253 if (!CmsStringUtil.isEmptyOrWhitespaceOnly(propValue)) { 1254 return propValue; 1255 } 1256 lastElem = elem; 1257 elem = elem.getParentElement(); 1258 } while ((elem != null) && (elem != lastElem)); 1259 return null; 1260 } 1261 1262 /** 1263 * Returns if the option bar position collides with any iframe child elements.<p> 1264 * 1265 * @param optionTop the option bar absolute top 1266 * @param optionWidth the option bar width 1267 * 1268 * @return <code>true</code> if there are iframe child elements located no less than 25px below the upper edge of the element 1269 */ 1270 private boolean isOptionbarIFrameCollision(int optionTop, int optionWidth) { 1271 1272 if (RootPanel.getBodyElement().isOrHasChild(getElement())) { 1273 NodeList<Element> frames = getElement().getElementsByTagName(CmsDomUtil.Tag.iframe.name()); 1274 for (int i = 0; i < frames.getLength(); i++) { 1275 int frameTop = frames.getItem(i).getAbsoluteTop(); 1276 int frameHeight = frames.getItem(i).getOffsetHeight(); 1277 int frameRight = frames.getItem(i).getAbsoluteRight(); 1278 if (((frameTop - optionTop) < 25) 1279 && (((frameTop + frameHeight) - optionTop) > 0) 1280 && ((frameRight - getElement().getAbsoluteRight()) < optionWidth)) { 1281 return true; 1282 } 1283 1284 } 1285 } 1286 return false; 1287 } 1288 1289 /** 1290 * Resets the node inserted handler.<p> 1291 */ 1292 private native void resetNodeInsertedHandler()/*-{ 1293 var $this = this; 1294 var element = $this.@org.opencms.ade.containerpage.client.ui.CmsContainerPageElementPanel::getElement()(); 1295 var handler = $this.@org.opencms.ade.containerpage.client.ui.CmsContainerPageElementPanel::m_nodeInsertHandler; 1296 if (handler == null) { 1297 handler = function(event) { 1298 $this.@org.opencms.ade.containerpage.client.ui.CmsContainerPageElementPanel::checkForEditableChanges()(); 1299 }; 1300 $this.@org.opencms.ade.containerpage.client.ui.CmsContainerPageElementPanel::m_nodeInsertHandler = handler; 1301 } else { 1302 if (element.removeEventLister) { 1303 element.removeEventListener("DOMNodeInserted", handler); 1304 } else if (element.detachEvent) { 1305 // IE specific 1306 element.detachEvent("onDOMNodeInserted", handler); 1307 } 1308 } 1309 if (element.addEventListener) { 1310 element.addEventListener("DOMNodeInserted", handler, false); 1311 } else if (element.attachEvent) { 1312 // IE specific 1313 element.attachEvent("onDOMNodeInserted", handler); 1314 } 1315 }-*/; 1316 1317 /** 1318 * This method removes the option-bar widget from DOM and re-attaches it at it's original position.<p> 1319 * Use to avoid mouse-over and mouse-down malfunction.<p> 1320 */ 1321 private void resetOptionbar() { 1322 1323 if (m_elementOptionBar != null) { 1324 if (getWidgetIndex(m_elementOptionBar) >= 0) { 1325 m_elementOptionBar.removeFromParent(); 1326 } 1327 updateOptionBarPosition(); 1328 insert(m_elementOptionBar, 0); 1329 } 1330 } 1331}