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.groupeditor; 029 030import org.opencms.ade.containerpage.client.CmsContainerpageController; 031import org.opencms.ade.containerpage.client.CmsContainerpageEvent; 032import org.opencms.ade.containerpage.client.CmsContainerpageEvent.EventType; 033import org.opencms.ade.containerpage.client.CmsContainerpageHandler; 034import org.opencms.ade.containerpage.client.Messages; 035import org.opencms.ade.containerpage.client.ui.CmsContainerPageContainer; 036import org.opencms.ade.containerpage.client.ui.CmsContainerPageElementPanel; 037import org.opencms.ade.containerpage.client.ui.CmsGroupContainerElementPanel; 038import org.opencms.ade.containerpage.client.ui.css.I_CmsLayoutBundle; 039import org.opencms.ade.containerpage.shared.CmsContainerElement; 040import org.opencms.ade.containerpage.shared.CmsContainerElementData; 041import org.opencms.gwt.client.ui.CmsPopup; 042import org.opencms.gwt.client.ui.CmsPushButton; 043import org.opencms.gwt.client.ui.I_CmsButton.ButtonColor; 044import org.opencms.gwt.client.ui.I_CmsButton.ButtonStyle; 045import org.opencms.gwt.client.ui.css.I_CmsToolbarButtonLayoutBundle; 046import org.opencms.gwt.client.ui.input.CmsLabel; 047import org.opencms.gwt.client.util.CmsDomUtil; 048import org.opencms.gwt.client.util.CmsPositionBean; 049import org.opencms.util.CmsStringUtil; 050 051import java.util.ArrayList; 052import java.util.HashSet; 053import java.util.Iterator; 054import java.util.List; 055import java.util.Map; 056import java.util.Set; 057 058import com.google.gwt.core.client.GWT; 059import com.google.gwt.dom.client.DivElement; 060import com.google.gwt.dom.client.Element; 061import com.google.gwt.dom.client.Style; 062import com.google.gwt.dom.client.Style.Display; 063import com.google.gwt.dom.client.Style.Position; 064import com.google.gwt.dom.client.Style.Unit; 065import com.google.gwt.event.dom.client.ClickEvent; 066import com.google.gwt.event.dom.client.ClickHandler; 067import com.google.gwt.uibinder.client.UiBinder; 068import com.google.gwt.uibinder.client.UiField; 069import com.google.gwt.user.client.Command; 070import com.google.gwt.user.client.Window; 071import com.google.gwt.user.client.ui.Composite; 072import com.google.gwt.user.client.ui.FlowPanel; 073import com.google.gwt.user.client.ui.HTMLPanel; 074import com.google.gwt.user.client.ui.RootPanel; 075import com.google.gwt.user.client.ui.Widget; 076 077/** 078 * Abstract group editor.<p> 079 * 080 * @since 8.5.0 081 */ 082public abstract class A_CmsGroupEditor extends Composite { 083 084 /** The ui-binder interface for this widget. */ 085 interface I_CmsGroupEditorUiBinder extends UiBinder<HTMLPanel, A_CmsGroupEditor> { 086 // GWT interface, nothing to do here 087 } 088 089 /** The dialog base height. The height without any content. */ 090 private static final int DIALOG_BASE_HEIGHT = 103; 091 092 /** The ui-binder for this widget. */ 093 private static I_CmsGroupEditorUiBinder uiBinder = GWT.create(I_CmsGroupEditorUiBinder.class); 094 095 /** The container marker div element. */ 096 @UiField 097 protected DivElement m_containerMarker; 098 099 /** The dialog element. */ 100 @UiField 101 protected FlowPanel m_dialogContent; 102 103 /** The container element data. */ 104 protected CmsContainerElementData m_elementData; 105 106 /** The overlay div element. */ 107 @UiField 108 protected DivElement m_overlayDiv; 109 110 /** List of elements when editing started, use to restore on cancel. */ 111 private List<CmsContainerPageElementPanel> m_backUpElements; 112 113 /** The dialog cancel button. */ 114 private CmsPushButton m_cancelButton; 115 116 /** The container-page controller. */ 117 private CmsContainerpageController m_controller; 118 119 /** The group-container place-holder. */ 120 private Element m_editingPlaceholder; 121 122 /** The editor popup dialog. */ 123 private CmsPopup m_editorDialog; 124 125 /** The editor HTML-id. */ 126 private String m_editorId; 127 128 /** The editor widget. */ 129 private HTMLPanel m_editorWidget; 130 131 /** The group-container. */ 132 private CmsGroupContainerElementPanel m_groupContainer; 133 134 /** The group container element position. */ 135 private CmsPositionBean m_groupContainerPosition; 136 137 /** The the container page handler. */ 138 private CmsContainerpageHandler m_handler; 139 140 /** The index position of the group-container inside it's parent. */ 141 private int m_indexPosition; 142 143 /** The parent container. */ 144 private CmsContainerPageContainer m_parentContainer; 145 146 /** The dialog save button. */ 147 private CmsPushButton m_saveButton; 148 149 /** 150 * Constructor.<p> 151 * 152 * @param groupContainer the group-container 153 * @param controller the container-page controller 154 * @param handler the container-page handler 155 */ 156 protected A_CmsGroupEditor( 157 CmsGroupContainerElementPanel groupContainer, 158 CmsContainerpageController controller, 159 CmsContainerpageHandler handler) { 160 161 m_controller = controller; 162 m_handler = handler; 163 m_editorWidget = uiBinder.createAndBindUi(this); 164 initWidget(m_editorWidget); 165 m_editorId = HTMLPanel.createUniqueId(); 166 m_editorWidget.getElement().setId(m_editorId); 167 m_groupContainer = groupContainer; 168 m_backUpElements = new ArrayList<CmsContainerPageElementPanel>(); 169 Iterator<Widget> it = m_groupContainer.iterator(); 170 while (it.hasNext()) { 171 Widget w = it.next(); 172 if (w instanceof CmsContainerPageElementPanel) { 173 m_backUpElements.add((CmsContainerPageElementPanel)w); 174 } 175 } 176 m_parentContainer = (CmsContainerPageContainer)m_groupContainer.getParentTarget(); 177 m_groupContainerPosition = CmsPositionBean.getBoundingClientRect(m_groupContainer.getElement()); 178 m_editingPlaceholder = createPlaceholder(m_groupContainer.getElement()); 179 m_groupContainer.setEditingPlaceholder(m_editingPlaceholder); 180 m_groupContainer.setEditingMarker(m_containerMarker); 181 m_indexPosition = m_parentContainer.getWidgetIndex(m_groupContainer); 182 // inserting placeholder element 183 m_parentContainer.getElement().insertBefore(m_editingPlaceholder, m_groupContainer.getElement()); 184 m_editorWidget.add(m_groupContainer, m_editorId); 185 Style style = m_groupContainer.getElement().getStyle(); 186 style.setPosition(Position.ABSOLUTE); 187 style.setLeft(m_groupContainerPosition.getLeft(), Unit.PX); 188 style.setTop(m_groupContainerPosition.getTop(), Unit.PX); 189 style.setWidth(m_groupContainerPosition.getWidth(), Unit.PX); 190 style.setZIndex(I_CmsLayoutBundle.INSTANCE.constants().css().zIndexGroupContainer()); 191 m_containerMarker.getStyle().setLeft(m_groupContainerPosition.getLeft() - 3, Unit.PX); 192 m_containerMarker.getStyle().setTop(m_groupContainerPosition.getTop() - 4, Unit.PX); 193 m_containerMarker.getStyle().setWidth(m_groupContainerPosition.getWidth() + 4, Unit.PX); 194 m_containerMarker.getStyle().setHeight(m_groupContainerPosition.getHeight() + 4, Unit.PX); 195 m_containerMarker.getStyle().setBackgroundColor( 196 CmsDomUtil.getEffectiveBackgroundColor(m_parentContainer.getElement())); 197 m_groupContainer.getElementOptionBar().setVisible(false); 198 m_groupContainer.getElementOptionBar().removeStyleName( 199 I_CmsToolbarButtonLayoutBundle.INSTANCE.toolbarButtonCss().cmsHovering()); 200 201 RootPanel.get().addStyleName(I_CmsLayoutBundle.INSTANCE.containerpageCss().groupcontainerEditing()); 202 addInputFields(); 203 m_editorDialog = new CmsPopup(); 204 addButtons(); 205 if (m_saveButton != null) { 206 m_saveButton.disable(Messages.get().key(Messages.GUI_GROUPCONTAINER_LOADING_DATA_0)); 207 } 208 m_editorDialog.setGlassEnabled(false); 209 m_editorDialog.setModal(false); 210 m_editorDialog.addDialogClose(new Command() { 211 212 /** 213 * @see com.google.gwt.user.client.Command#execute() 214 */ 215 public void execute() { 216 217 cancelEdit(); 218 } 219 }); 220 } 221 222 /** 223 * Returns the group container widget.<p> 224 * 225 * @return the group container widget 226 */ 227 public CmsGroupContainerElementPanel getGroupContainerWidget() { 228 229 return m_groupContainer; 230 } 231 232 /** 233 * Returns the the container page handler.<p> 234 * 235 * @return the the container page handler 236 */ 237 public CmsContainerpageHandler getHandler() { 238 239 return m_handler; 240 } 241 242 /** 243 * Hides the editor pop-up. Use during inline editing.<p> 244 */ 245 public void hidePopup() { 246 247 m_editorDialog.getElement().getStyle().setDisplay(Display.NONE); 248 } 249 250 /** 251 * Reinitializes the option bar buttons on the contained elements.<p> 252 */ 253 public abstract void reinitializeButtons(); 254 255 /** 256 * Shows the editor pop-up.<p> 257 */ 258 public void showPopup() { 259 260 m_editorDialog.getElement().getStyle().clearDisplay(); 261 } 262 263 /** 264 * Updates the backup elements.<p> 265 * 266 * @param updateElements the updated element data 267 */ 268 public void updateBackupElements(Map<String, CmsContainerElementData> updateElements) { 269 270 ArrayList<CmsContainerPageElementPanel> updatedList = new ArrayList<CmsContainerPageElementPanel>(); 271 String containerId = m_groupContainer.getContainerId(); 272 for (CmsContainerPageElementPanel element : m_backUpElements) { 273 if (updateElements.containsKey(element.getId()) 274 && CmsStringUtil.isNotEmptyOrWhitespaceOnly( 275 updateElements.get(element.getId()).getContents().get(containerId))) { 276 CmsContainerElementData elementData = updateElements.get(element.getId()); 277 try { 278 CmsContainerPageElementPanel replacer = m_controller.getContainerpageUtil().createElement( 279 elementData, 280 m_groupContainer, 281 false); 282 if (element.getInheritanceInfo() != null) { 283 // in case of inheritance container editing, keep the inheritance info 284 replacer.setInheritanceInfo(element.getInheritanceInfo()); 285 } 286 updatedList.add(replacer); 287 288 } catch (Exception e) { 289 // in this case keep the old version 290 updatedList.add(element); 291 } 292 } else { 293 updatedList.add(element); 294 } 295 } 296 m_backUpElements = updatedList; 297 } 298 299 /** 300 * Adds a button to the dialog.<p> 301 * 302 * @param button the button to add 303 */ 304 protected void addButton(Widget button) { 305 306 if (m_editorDialog != null) { 307 m_editorDialog.addButton(button); 308 } 309 } 310 311 /** 312 * Adds the buttons to the dialog.<p> 313 */ 314 protected abstract void addButtons(); 315 316 /** 317 * Adds a cancel button to the dialog.<p> 318 */ 319 protected void addCancelButton() { 320 321 if (m_editorDialog != null) { 322 m_cancelButton = new CmsPushButton(); 323 m_cancelButton.setText(Messages.get().key(Messages.GUI_BUTTON_CANCEL_TEXT_0)); 324 m_cancelButton.setUseMinWidth(true); 325 m_cancelButton.setButtonStyle(ButtonStyle.TEXT, ButtonColor.BLUE); 326 m_cancelButton.addClickHandler(new ClickHandler() { 327 328 public void onClick(ClickEvent event) { 329 330 cancelEdit(); 331 } 332 }); 333 m_editorDialog.addButton(m_cancelButton); 334 } 335 } 336 337 /** 338 * Adds an input field with the given label to the dialog.<p> 339 * 340 * @param label the label 341 * @param inputWidget the input widget 342 */ 343 protected void addInputField(String label, Widget inputWidget) { 344 345 FlowPanel row = new FlowPanel(); 346 row.setStyleName(I_CmsLayoutBundle.INSTANCE.groupcontainerCss().inputRow()); 347 CmsLabel labelWidget = new CmsLabel(label); 348 labelWidget.setStyleName(I_CmsLayoutBundle.INSTANCE.groupcontainerCss().inputLabel()); 349 row.add(labelWidget); 350 inputWidget.addStyleName(I_CmsLayoutBundle.INSTANCE.groupcontainerCss().inputBox()); 351 row.add(inputWidget); 352 m_dialogContent.add(row); 353 } 354 355 /** 356 * Adds the required input fields to the dialog.<p> 357 */ 358 protected abstract void addInputFields(); 359 360 /** 361 * Adds the save button to the dialog.<p> 362 */ 363 protected void addSaveButton() { 364 365 if (m_editorDialog != null) { 366 m_saveButton = new CmsPushButton(); 367 m_saveButton.setText(Messages.get().key(Messages.GUI_BUTTON_SAVE_TEXT_0)); 368 m_saveButton.setUseMinWidth(true); 369 m_saveButton.setButtonStyle(ButtonStyle.TEXT, ButtonColor.GREEN); 370 m_saveButton.addClickHandler(new ClickHandler() { 371 372 public void onClick(ClickEvent event) { 373 374 saveEdit(); 375 } 376 }); 377 m_editorDialog.addButton(m_saveButton); 378 } 379 } 380 381 /** 382 * On click function for cancel button.<p> 383 */ 384 protected abstract void cancelEdit(); 385 386 /** 387 * Clears the static instance reference.<p> 388 */ 389 protected abstract void clearInstance(); 390 391 /** 392 * Closes the dialog.<p> 393 * 394 * @param breakingUp <code>true</code> if the group container is to be removed 395 */ 396 protected void closeDialog(boolean breakingUp) { 397 398 m_controller.stopEditingGroupcontainer(); 399 m_editingPlaceholder.removeFromParent(); 400 m_editorDialog.hide(); 401 RootPanel.get().removeStyleName(I_CmsLayoutBundle.INSTANCE.containerpageCss().groupcontainerEditing()); 402 if (!breakingUp) { 403 m_groupContainer.clearEditingPlaceholder(); 404 Style style = m_groupContainer.getElement().getStyle(); 405 style.clearPosition(); 406 style.clearTop(); 407 style.clearLeft(); 408 style.clearZIndex(); 409 style.clearWidth(); 410 m_parentContainer.insert(m_groupContainer, m_indexPosition); 411 m_groupContainer.getElementOptionBar().setVisible(true); 412 if (!m_groupContainer.iterator().hasNext()) { 413 // group-container is empty, mark it 414 m_groupContainer.addStyleName(I_CmsLayoutBundle.INSTANCE.containerpageCss().emptyGroupContainer()); 415 } 416 } 417 clearInstance(); 418 removeFromParent(); 419 if (!m_controller.getData().isUseClassicEditor()) { 420 for (Widget element : m_groupContainer) { 421 if (element instanceof CmsContainerPageElementPanel) { 422 ((CmsContainerPageElementPanel)element).removeInlineEditor(); 423 } 424 } 425 } 426 m_controller.reinitializeButtons(); 427 m_controller.reInitInlineEditing(); 428 m_controller.fireEvent(new CmsContainerpageEvent(EventType.elementEdited)); 429 } 430 431 /** 432 * Creates a place-holder for the group-container.<p> 433 * 434 * @param element the element 435 * 436 * @return the place-holder widget 437 */ 438 protected Element createPlaceholder(Element element) { 439 440 Element result = CmsDomUtil.clone(element); 441 result.addClassName(I_CmsLayoutBundle.INSTANCE.containerpageCss().groupcontainerPlaceholder()); 442 result.getStyle().setBackgroundColor("transparent"); 443 return result; 444 } 445 446 /** 447 * Returns the list of back up elements.<p> 448 * 449 * @return the back up elements 450 */ 451 protected List<CmsContainerPageElementPanel> getBackUpElements() { 452 453 return m_backUpElements; 454 } 455 456 /** 457 * Returns the container page controller.<p> 458 * 459 * @return the container page controller 460 */ 461 protected CmsContainerpageController getController() { 462 463 return m_controller; 464 } 465 466 /** 467 * Returns the ids of the contained elements and group-container itself.<p> 468 * 469 * @return the element ids 470 */ 471 protected Set<String> getElementIds() { 472 473 Set<String> subItems = new HashSet<String>(); 474 Iterator<Widget> it = m_groupContainer.iterator(); 475 while (it.hasNext()) { 476 Widget w = it.next(); 477 if (w instanceof CmsContainerPageElementPanel) { 478 subItems.add(((CmsContainerPageElementPanel)w).getId()); 479 } 480 } 481 subItems.add(m_groupContainer.getId()); 482 return subItems; 483 } 484 485 /** 486 * Returns the element data of the contained elements.<p> 487 * 488 * @return the contained elements data 489 */ 490 protected List<CmsContainerElement> getElements() { 491 492 List<CmsContainerElement> subItems = new ArrayList<CmsContainerElement>(); 493 Iterator<Widget> it = m_groupContainer.iterator(); 494 while (it.hasNext()) { 495 Widget w = it.next(); 496 if (w instanceof CmsContainerPageElementPanel) { 497 CmsContainerPageElementPanel elementWidget = (CmsContainerPageElementPanel)w; 498 CmsContainerElement element = new CmsContainerElement(); 499 element.setClientId(elementWidget.getId()); 500 element.setResourceType(elementWidget.getNewType()); 501 element.setCreateNew(elementWidget.isCreateNew()); 502 element.setSitePath(elementWidget.getSitePath()); 503 element.setNewEditorDisabled(elementWidget.isNewEditorDisabled()); 504 subItems.add(element); 505 } 506 } 507 return subItems; 508 } 509 510 /** 511 * Returns the group container widget index position.<p> 512 * 513 * @return the index position 514 */ 515 protected int getIndexPosition() { 516 517 return m_indexPosition; 518 } 519 520 /** 521 * Returns the parent container widget.<p> 522 * 523 * @return the parent container widget 524 */ 525 protected CmsContainerPageContainer getParentContainer() { 526 527 return m_parentContainer; 528 } 529 530 /** 531 * Opens the group container edit dialog.<p> 532 * 533 * @param dialogTitle the dialog title 534 */ 535 protected void openDialog(String dialogTitle) { 536 537 m_editorDialog.setCaption(dialogTitle); 538 int contentHeight = m_dialogContent.getOffsetHeight(); 539 m_editorDialog.setMainContent(m_dialogContent); 540 // position dialog and show it 541 if (m_groupContainerPosition != null) { 542 int lefthandSpace = m_groupContainerPosition.getLeft() - Window.getScrollLeft(); 543 int righthandSpace = (Window.getClientWidth() + Window.getScrollLeft()) 544 - (m_groupContainerPosition.getLeft() + m_groupContainerPosition.getWidth()); 545 int requiredWidth = CmsPopup.DEFAULT_WIDTH + 30; 546 int left = m_groupContainerPosition.getLeft(); 547 if (requiredWidth > (righthandSpace + m_groupContainerPosition.getWidth())) { 548 left = (Window.getClientWidth() + Window.getScrollLeft()) - requiredWidth; 549 } 550 if (left < Window.getScrollLeft()) { 551 left = 0; 552 } 553 if (lefthandSpace > requiredWidth) { 554 // place left of the group container if there is enough space 555 m_editorDialog.setPopupPosition( 556 m_groupContainerPosition.getLeft() - requiredWidth, 557 m_groupContainerPosition.getTop() - 1); 558 } else if ((m_groupContainerPosition.getTop() - Window.getScrollTop()) > (contentHeight 559 + DIALOG_BASE_HEIGHT 560 + 50)) { 561 // else place above if there is enough space 562 563 m_editorDialog.setPopupPosition( 564 left, 565 m_groupContainerPosition.getTop() - (contentHeight + DIALOG_BASE_HEIGHT)); 566 } else if (righthandSpace > requiredWidth) { 567 // else on the right if there is enough space 568 m_editorDialog.setPopupPosition( 569 m_groupContainerPosition.getLeft() + m_groupContainerPosition.getWidth() + 20, 570 m_groupContainerPosition.getTop() - 1); 571 } else { 572 // last resort, place below 573 m_editorDialog.setPopupPosition( 574 left, 575 m_groupContainerPosition.getTop() + m_groupContainerPosition.getHeight() + 20); 576 } 577 m_editorDialog.show(); 578 } else { 579 // should never happen 580 m_editorDialog.center(); 581 } 582 if (!m_controller.getData().isUseClassicEditor()) { 583 for (Widget element : m_groupContainer) { 584 if (element instanceof CmsContainerPageElementPanel) { 585 ((CmsContainerPageElementPanel)element).initInlineEditor(m_controller); 586 } 587 } 588 } 589 } 590 591 /** 592 * Removes all child container elements.<p> 593 */ 594 protected void removeAllChildren() { 595 596 for (int i = getGroupContainerWidget().getWidgetCount() - 1; i >= 0; i--) { 597 Widget widget = getGroupContainerWidget().getWidget(i); 598 if (widget instanceof CmsContainerPageElementPanel) { 599 widget.removeFromParent(); 600 } 601 } 602 } 603 604 /** 605 * On click function for save button.<p> 606 */ 607 protected abstract void saveEdit(); 608 609 /** 610 * Enables or disables the save button.<p> 611 * 612 * @param enabled <code>true</code> to enable the save button 613 * @param disabledMessage the message to display when the button is disabled 614 */ 615 protected void setSaveEnabled(boolean enabled, String disabledMessage) { 616 617 if (m_saveButton != null) { 618 if (enabled) { 619 m_saveButton.enable(); 620 } else { 621 m_saveButton.disable(disabledMessage); 622 } 623 } 624 } 625}