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.CmsContainerpageEvent.EventType; 031import org.opencms.ade.containerpage.client.ui.CmsConfirmRemoveDialog; 032import org.opencms.ade.containerpage.client.ui.CmsContainerPageContainer; 033import org.opencms.ade.containerpage.client.ui.CmsContainerPageElementPanel; 034import org.opencms.ade.containerpage.client.ui.CmsGroupContainerElementPanel; 035import org.opencms.ade.containerpage.client.ui.CmsRemovedElementDeletionDialog; 036import org.opencms.ade.containerpage.client.ui.CmsReuseInfoDialog; 037import org.opencms.ade.containerpage.client.ui.CmsSmallElementsHandler; 038import org.opencms.ade.containerpage.client.ui.I_CmsDropContainer; 039import org.opencms.ade.containerpage.client.ui.css.I_CmsLayoutBundle; 040import org.opencms.ade.containerpage.client.ui.groupeditor.A_CmsGroupEditor; 041import org.opencms.ade.containerpage.client.ui.groupeditor.CmsGroupContainerEditor; 042import org.opencms.ade.containerpage.client.ui.groupeditor.CmsInheritanceContainerEditor; 043import org.opencms.ade.containerpage.shared.CmsCntPageData; 044import org.opencms.ade.containerpage.shared.CmsCntPageData.ElementDeleteMode; 045import org.opencms.ade.containerpage.shared.CmsContainer; 046import org.opencms.ade.containerpage.shared.CmsContainerElement; 047import org.opencms.ade.containerpage.shared.CmsContainerElementData; 048import org.opencms.ade.containerpage.shared.CmsContainerPageGalleryData; 049import org.opencms.ade.containerpage.shared.CmsContainerPageRpcContext; 050import org.opencms.ade.containerpage.shared.CmsCreateElementData; 051import org.opencms.ade.containerpage.shared.CmsDialogOptionsAndInfo; 052import org.opencms.ade.containerpage.shared.CmsElementSettingsConfig; 053import org.opencms.ade.containerpage.shared.CmsElementViewInfo; 054import org.opencms.ade.containerpage.shared.CmsGroupContainer; 055import org.opencms.ade.containerpage.shared.CmsGroupContainerSaveResult; 056import org.opencms.ade.containerpage.shared.CmsInheritanceContainer; 057import org.opencms.ade.containerpage.shared.CmsRemovedElementStatus; 058import org.opencms.ade.containerpage.shared.CmsReuseInfo; 059import org.opencms.ade.containerpage.shared.rpc.I_CmsContainerpageService; 060import org.opencms.ade.containerpage.shared.rpc.I_CmsContainerpageServiceAsync; 061import org.opencms.ade.contenteditor.client.CmsContentEditor; 062import org.opencms.gwt.client.CmsCoreProvider; 063import org.opencms.gwt.client.I_CmsElementToolbarContext; 064import org.opencms.gwt.client.dnd.CmsCompositeDNDController; 065import org.opencms.gwt.client.dnd.CmsDNDHandler; 066import org.opencms.gwt.client.dnd.I_CmsDNDController; 067import org.opencms.gwt.client.rpc.CmsRpcAction; 068import org.opencms.gwt.client.rpc.CmsRpcPrefetcher; 069import org.opencms.gwt.client.ui.CmsErrorDialog; 070import org.opencms.gwt.client.ui.CmsNotification; 071import org.opencms.gwt.client.ui.CmsNotification.Type; 072import org.opencms.gwt.client.ui.contextmenu.CmsEmbeddedAction; 073import org.opencms.gwt.client.util.CmsDebugLog; 074import org.opencms.gwt.client.util.CmsDomUtil; 075import org.opencms.gwt.client.util.I_CmsSimpleCallback; 076import org.opencms.gwt.shared.CmsContextMenuEntryBean; 077import org.opencms.gwt.shared.CmsCoreData.AdeContext; 078import org.opencms.gwt.shared.CmsGalleryContainerInfo; 079import org.opencms.gwt.shared.CmsGwtConstants; 080import org.opencms.gwt.shared.CmsGwtLog; 081import org.opencms.gwt.shared.CmsListInfoBean; 082import org.opencms.gwt.shared.CmsTemplateContextInfo; 083import org.opencms.gwt.shared.I_CmsAutoBeanFactory; 084import org.opencms.gwt.shared.I_CmsUnlockData; 085import org.opencms.gwt.shared.rpc.I_CmsCoreServiceAsync; 086import org.opencms.util.CmsDefaultSet; 087import org.opencms.util.CmsStringUtil; 088import org.opencms.util.CmsUUID; 089 090import java.util.ArrayList; 091import java.util.Collection; 092import java.util.HashMap; 093import java.util.HashSet; 094import java.util.Iterator; 095import java.util.List; 096import java.util.Map; 097import java.util.Map.Entry; 098import java.util.Set; 099 100import com.google.common.base.Optional; 101import com.google.common.collect.Lists; 102import com.google.gwt.core.client.GWT; 103import com.google.gwt.dom.client.AnchorElement; 104import com.google.gwt.dom.client.Element; 105import com.google.gwt.dom.client.EventTarget; 106import com.google.gwt.event.dom.client.KeyCodes; 107import com.google.gwt.event.logical.shared.ResizeEvent; 108import com.google.gwt.event.logical.shared.ResizeHandler; 109import com.google.gwt.event.logical.shared.ValueChangeEvent; 110import com.google.gwt.event.logical.shared.ValueChangeHandler; 111import com.google.gwt.user.client.Command; 112import com.google.gwt.user.client.Event; 113import com.google.gwt.user.client.Event.NativePreviewEvent; 114import com.google.gwt.user.client.Event.NativePreviewHandler; 115import com.google.gwt.user.client.History; 116import com.google.gwt.user.client.Timer; 117import com.google.gwt.user.client.Window; 118import com.google.gwt.user.client.rpc.AsyncCallback; 119import com.google.gwt.user.client.rpc.SerializationException; 120import com.google.gwt.user.client.rpc.ServiceDefTarget; 121import com.google.gwt.user.client.ui.RootPanel; 122import com.google.gwt.user.client.ui.Widget; 123import com.google.web.bindery.autobean.shared.AutoBean; 124import com.google.web.bindery.autobean.shared.AutoBeanCodex; 125 126import elemental2.core.JsObject; 127import elemental2.dom.CustomEvent; 128import elemental2.dom.CustomEventInit; 129import elemental2.dom.DomGlobal; 130import elemental2.webstorage.WebStorageWindow; 131import jsinterop.annotations.JsPackage; 132import jsinterop.annotations.JsProperty; 133import jsinterop.annotations.JsType; 134import jsinterop.base.Js; 135import jsinterop.base.JsPropertyMap; 136 137/** 138 * Data provider for the container-page editor. All data concerning the container-page is requested and maintained by this provider.<p> 139 * 140 * @since 8.0.0 141 */ 142public final class CmsContainerpageController { 143 144 /** 145 * Enum which is used to control how elements are removed from the page.<p> 146 */ 147 public enum ElementRemoveMode { 148 /** Reference checks are performed and the user is asked for confirmation whether they really want to remove the element before the page is saved. */ 149 confirmRemove, 150 151 /** Reference checks are only performed after the page or group has been saved. */ 152 saveAndCheckReferences, 153 154 /** Element is just removed, no checks are performed. */ 155 silent; 156 } 157 158 /** 159 * Wrapper for the event details object for custom page editor events. 160 */ 161 @JsType(isNative = true, namespace = JsPackage.GLOBAL, name = "?") 162 public interface I_EventDetails { 163 164 /** 165 * Gets the HTML element for the container itself. 166 * 167 * @return the HTML element for the container 168 */ 169 @JsProperty 170 elemental2.dom.Element getContainer(); 171 172 /** 173 * Gets the HTML element for the page element. 174 * 175 * @return the HTML element for the page element 176 */ 177 @JsProperty 178 elemental2.dom.Element getElement(); 179 180 /** 181 * The 'placeholder' state of the element. 182 * 183 * @return the 'placedholder' state 184 */ 185 @JsProperty 186 boolean getIsPlaceholder(); 187 188 /** 189 * Gets the replaced element. 190 * 191 * @return the replaced element 192 */ 193 @JsProperty 194 elemental2.dom.Element getReplacedElement(); 195 196 /** 197 * Gets the type. 198 * 199 * @return the type 200 */ 201 @JsProperty 202 String getType(); 203 204 /** 205 * Sets the HTML element for the container. 206 * 207 * @param container the HTML element for the container 208 */ 209 @JsProperty 210 void setContainer(elemental2.dom.Element container); 211 212 /** 213 * Sets the HTML element for the page element. 214 * @param element the HTML element for the page element 215 */ 216 @JsProperty 217 void setElement(elemental2.dom.Element element); 218 219 /** 220 * Sets the 'placeholder' (i.e. 'new') state 221 * 222 * @param isPlaceholder true if the element was new 223 */ 224 @JsProperty 225 void setIsPlaceholder(boolean isPlaceholder); 226 227 /** 228 * Sets the replaced element. 229 * 230 * @param element the replaced element 231 */ 232 @JsProperty 233 void setReplacedElement(elemental2.dom.Element element); 234 235 /** 236 * Sets the type. 237 * 238 * @param type the type 239 */ 240 @JsProperty 241 void setType(String type); 242 243 } 244 245 /** 246 * Visitor interface used to process the current container content on the page.<p> 247 */ 248 public static interface I_PageContentVisitor { 249 250 /** 251 * This method is called before a container is processed.<p> 252 * 253 * If the method returns false, the container will be skipped.<p> 254 * 255 * @param name the container name 256 * @param container the container data object 257 * 258 * @return true if the container should be processed, true if it should be skipped 259 */ 260 boolean beginContainer(String name, CmsContainer container); 261 262 /** 263 * This method is called after all elements of a container have been processed.<p> 264 */ 265 void endContainer(); 266 267 /** 268 * This method is called for each element of a container.<p> 269 * 270 * @param element the container element 271 */ 272 void handleElement(CmsContainerPageElementPanel element); 273 } 274 275 /** 276 * Interface for classes that should be notified when container page elements are reloaded. 277 */ 278 public interface I_ReloadHandler { 279 280 /** 281 * Called after all other onReload calls for the current operation. 282 * */ 283 void finish(); 284 285 /** 286 * Is called when an element has been replaced on the page. 287 * 288 * @param oldElement the old element 289 * @param newElement the replacement element that is currently on the page 290 */ 291 void onReload(CmsContainerPageElementPanel oldElement, CmsContainerPageElementPanel newElement); 292 } 293 294 /** 295 * This visitor implementation checks whether there are other elements in the current page 296 * which correspond to the same VFS resource as a given container element. 297 */ 298 public static class ReferenceCheckVisitor implements I_PageContentVisitor { 299 300 /** The element for which we want to check whether there are other references to the same resource. */ 301 private CmsContainerPageElementPanel m_elementPanel; 302 303 /** True if other references have been found. */ 304 private boolean m_hasReferences; 305 306 /** The structure id of the element. */ 307 private String m_structureId; 308 309 /** 310 * Creates a new instance.<p> 311 * 312 * @param elementPanel the element for which we want to check if there are other references 313 */ 314 public ReferenceCheckVisitor(CmsContainerPageElementPanel elementPanel) { 315 316 m_elementPanel = elementPanel; 317 m_structureId = getServerId(elementPanel.getId()); 318 } 319 320 /** 321 * @see org.opencms.ade.containerpage.client.CmsContainerpageController.I_PageContentVisitor#beginContainer(java.lang.String, org.opencms.ade.containerpage.shared.CmsContainer) 322 */ 323 public boolean beginContainer(String name, CmsContainer container) { 324 325 return !container.isDetailView(); 326 } 327 328 /** 329 * @see org.opencms.ade.containerpage.client.CmsContainerpageController.I_PageContentVisitor#endContainer() 330 */ 331 public void endContainer() { 332 333 // do nothing 334 } 335 336 /** 337 * @see org.opencms.ade.containerpage.client.CmsContainerpageController.I_PageContentVisitor#handleElement(org.opencms.ade.containerpage.client.ui.CmsContainerPageElementPanel) 338 */ 339 public void handleElement(CmsContainerPageElementPanel element) { 340 341 if (element != m_elementPanel) { 342 String id = getServerId(element.getId()); 343 if (m_structureId.equals(id)) { 344 m_hasReferences = true; 345 } 346 } 347 } 348 349 /** 350 * Checks if other references have been found.<p> 351 * 352 * @return true if other references have been found 353 */ 354 public boolean hasReferences() { 355 356 return m_hasReferences; 357 } 358 359 } 360 361 /** 362 * Visitor implementation which is used to gather the container contents for saving.<p> 363 */ 364 protected class PageStateVisitor implements I_PageContentVisitor { 365 366 /** The current container name. */ 367 protected String m_containerName; 368 369 /** The contaienr which is currently being processed. */ 370 protected CmsContainer m_currentContainer; 371 372 /** The list of collected containers. */ 373 protected List<CmsContainer> m_resultContainers = new ArrayList<CmsContainer>(); 374 375 /** The list of elements of the currently processed container which have already been processed. */ 376 List<CmsContainerElement> m_currentElements; 377 378 /** 379 * @see org.opencms.ade.containerpage.client.CmsContainerpageController.I_PageContentVisitor#beginContainer(java.lang.String, org.opencms.ade.containerpage.shared.CmsContainer) 380 */ 381 public boolean beginContainer(String name, CmsContainer container) { 382 383 m_currentContainer = container; 384 m_containerName = name; 385 m_currentElements = new ArrayList<CmsContainerElement>(); 386 return true; 387 } 388 389 /** 390 * @see org.opencms.ade.containerpage.client.CmsContainerpageController.I_PageContentVisitor#endContainer() 391 */ 392 public void endContainer() { 393 394 CmsContainer container = new CmsContainer( 395 m_containerName, 396 m_currentContainer.getType(), 397 null, 398 m_currentContainer.getWidth(), 399 m_currentContainer.getMaxElements(), 400 m_currentContainer.isDetailViewContainer(), 401 m_currentContainer.isDetailView(), 402 true, 403 m_currentElements, 404 m_currentContainer.getParentContainerName(), 405 m_currentContainer.getParentInstanceId(), 406 m_currentContainer.getSettingPresets()); 407 container.setDetailOnly(m_currentContainer.isDetailOnly()); 408 container.setRootContainer(isRootContainer(m_currentContainer)); 409 m_resultContainers.add(container); 410 } 411 412 /** 413 * Gets the list of collected containers.<p> 414 * 415 * @return the list of containers 416 */ 417 public List<CmsContainer> getContainers() { 418 419 return m_resultContainers; 420 } 421 422 /** 423 * @see org.opencms.ade.containerpage.client.CmsContainerpageController.I_PageContentVisitor#handleElement(org.opencms.ade.containerpage.client.ui.CmsContainerPageElementPanel) 424 */ 425 public void handleElement(CmsContainerPageElementPanel elementWidget) { 426 427 CmsContainerElement element = new CmsContainerElement(); 428 element.setClientId(elementWidget.getId()); 429 element.setResourceType(elementWidget.getNewType()); 430 element.setCreateNew(elementWidget.isCreateNew()); 431 element.setModelGroupId(elementWidget.getModelGroupId()); 432 element.setSitePath(elementWidget.getSitePath()); 433 element.setNewEditorDisabled(elementWidget.isNewEditorDisabled()); 434 m_currentElements.add(element); 435 } 436 437 } 438 439 /** 440 * Visitor implementation which is used to gather the container contents for saving.<p> 441 */ 442 protected class SaveDataVisitor implements I_PageContentVisitor { 443 444 /** The current container name. */ 445 protected String m_containerName; 446 447 /** The contaienr which is currently being processed. */ 448 protected CmsContainer m_currentContainer; 449 450 /** The list of collected containers. */ 451 protected List<CmsContainer> m_resultContainers = new ArrayList<CmsContainer>(); 452 453 /** The list of elements of the currently processed container which have already been processed. */ 454 List<CmsContainerElement> m_currentElements; 455 456 /** 457 * @see org.opencms.ade.containerpage.client.CmsContainerpageController.I_PageContentVisitor#beginContainer(java.lang.String, org.opencms.ade.containerpage.shared.CmsContainer) 458 */ 459 public boolean beginContainer(String name, CmsContainer container) { 460 461 if (container.isDetailView() || ((getData().getDetailId() != null) && !container.isDetailOnly())) { 462 m_currentContainer = null; 463 return false; 464 465 } else { 466 m_currentContainer = container; 467 m_containerName = name; 468 m_currentElements = new ArrayList<CmsContainerElement>(); 469 return true; 470 } 471 } 472 473 /** 474 * @see org.opencms.ade.containerpage.client.CmsContainerpageController.I_PageContentVisitor#endContainer() 475 */ 476 public void endContainer() { 477 478 CmsContainer container = new CmsContainer( 479 m_containerName, 480 m_currentContainer.getType(), 481 null, 482 m_currentContainer.getWidth(), 483 m_currentContainer.getMaxElements(), 484 m_currentContainer.isDetailViewContainer(), 485 m_currentContainer.isDetailView(), 486 true, 487 m_currentElements, 488 m_currentContainer.getParentContainerName(), 489 m_currentContainer.getParentInstanceId(), 490 m_currentContainer.getSettingPresets()); 491 492 container.setRootContainer(isRootContainer(m_currentContainer)); 493 m_resultContainers.add(container); 494 } 495 496 /** 497 * Gets the list of collected containers.<p> 498 * 499 * @return the list of containers 500 */ 501 public List<CmsContainer> getContainers() { 502 503 return m_resultContainers; 504 } 505 506 /** 507 * @see org.opencms.ade.containerpage.client.CmsContainerpageController.I_PageContentVisitor#handleElement(org.opencms.ade.containerpage.client.ui.CmsContainerPageElementPanel) 508 */ 509 public void handleElement(CmsContainerPageElementPanel elementWidget) { 510 511 CmsContainerElement element = new CmsContainerElement(); 512 element.setClientId(elementWidget.getId()); 513 element.setResourceType(elementWidget.getNewType()); 514 element.setCreateNew(elementWidget.isCreateNew()); 515 element.setModelGroupId(elementWidget.getModelGroupId()); 516 element.setSitePath(elementWidget.getSitePath()); 517 element.setNewEditorDisabled(elementWidget.isNewEditorDisabled()); 518 m_currentElements.add(element); 519 } 520 521 } 522 523 /** 524 * A type which indicates the locking status of the currently edited container page.<p> 525 */ 526 enum LockStatus { 527 /** Locking the resource failed. */ 528 failed, 529 530 /** The resource could be successfully locked. */ 531 locked, 532 533 /** Locking the resource has not been tried. */ 534 unknown 535 } 536 537 /** 538 * A RPC action implementation used to request the data for container-page elements.<p> 539 */ 540 private class MultiElementAction extends CmsRpcAction<Map<String, CmsContainerElementData>> { 541 542 /** Call-back executed on response. */ 543 private I_CmsSimpleCallback<Map<String, CmsContainerElementData>> m_callback; 544 545 /** The requested client id's. */ 546 private Set<String> m_clientIds; 547 548 /** 549 " * Constructor.<p> 550 * 551 * @param clientIds the client id's 552 * @param callback the call-back 553 */ 554 public MultiElementAction( 555 Set<String> clientIds, 556 I_CmsSimpleCallback<Map<String, CmsContainerElementData>> callback) { 557 558 super(); 559 m_clientIds = clientIds; 560 m_callback = callback; 561 } 562 563 /** 564 * @see org.opencms.gwt.client.rpc.CmsRpcAction#execute() 565 */ 566 @Override 567 public void execute() { 568 569 Map<String, CmsContainerElementData> result = new HashMap<String, CmsContainerElementData>(); 570 List<String> neededIds = new ArrayList<String>(); 571 Iterator<String> it = m_clientIds.iterator(); 572 while (it.hasNext()) { 573 String clientId = it.next(); 574 if (m_elements.containsKey(clientId)) { 575 result.put(clientId, m_elements.get(clientId)); 576 } else { 577 neededIds.add(clientId); 578 } 579 } 580 if (neededIds.size() == 0) { 581 m_callback.execute(result); 582 } else { 583 getContainerpageService().getElementsData( 584 getData().getRpcContext(), 585 getData().getDetailId(), 586 getRequestParams(), 587 m_clientIds, 588 getPageState(), 589 false, 590 null, 591 getLocale(), 592 this); 593 } 594 595 } 596 597 /** 598 * @see org.opencms.gwt.client.rpc.CmsRpcAction#onResponse(java.lang.Object) 599 */ 600 @Override 601 protected void onResponse(Map<String, CmsContainerElementData> result) { 602 603 if (result != null) { 604 addElements(result); 605 Map<String, CmsContainerElementData> elements = new HashMap<String, CmsContainerElementData>(); 606 Iterator<String> it = m_clientIds.iterator(); 607 while (it.hasNext()) { 608 String clientId = it.next(); 609 elements.put(clientId, m_elements.get(clientId)); 610 } 611 m_callback.execute(elements); 612 } 613 } 614 615 } 616 617 /** 618 * A RPC action implementation used to reload the data for a container-page element.<p> 619 */ 620 private class ReloadElementAction extends CmsRpcAction<Map<String, CmsContainerElementData>> { 621 622 /** The requested client id's. */ 623 private Set<String> m_clientIds; 624 625 /** The callback to execute after the reload. */ 626 private I_ReloadHandler m_reloadHandler; 627 628 /** 629 * Constructor.<p> 630 * 631 * @param clientIds the client id's to reload 632 * @param reloadHandler the callback to call after the elements have been replaced 633 */ 634 public ReloadElementAction(Set<String> clientIds, I_ReloadHandler reloadHandler) { 635 636 super(); 637 m_clientIds = clientIds; 638 m_reloadHandler = reloadHandler; 639 } 640 641 /** 642 * @see org.opencms.gwt.client.rpc.CmsRpcAction#execute() 643 */ 644 @Override 645 public void execute() { 646 647 getContainerpageService().getElementsData( 648 getData().getRpcContext(), 649 getData().getDetailId(), 650 getRequestParams(), 651 m_clientIds, 652 getPageState(), 653 false, 654 null, 655 getLocale(), 656 this); 657 } 658 659 /** 660 * @see org.opencms.gwt.client.rpc.CmsRpcAction#onResponse(java.lang.Object) 661 */ 662 @Override 663 protected void onResponse(Map<String, CmsContainerElementData> result) { 664 665 if (result == null) { 666 return; 667 } 668 addElements(result); 669 Iterator<org.opencms.ade.containerpage.client.ui.CmsContainerPageElementPanel> it = getAllDragElements().iterator(); 670 boolean reloadMarkerFound = false; 671 while (it.hasNext()) { 672 org.opencms.ade.containerpage.client.ui.CmsContainerPageElementPanel containerElement = it.next(); 673 if (!m_clientIds.contains(containerElement.getId())) { 674 continue; 675 } 676 try { 677 CmsContainerPageElementPanel replacer = replaceContainerElement( 678 containerElement, 679 result.get(containerElement.getId())); 680 if (replacer.getElement().getInnerHTML().contains(CmsGwtConstants.FORMATTER_RELOAD_MARKER)) { 681 reloadMarkerFound = true; 682 } 683 m_reloadHandler.onReload(containerElement, replacer); 684 } catch (Exception e) { 685 CmsDebugLog.getInstance().printLine("trying to replace"); 686 CmsDebugLog.getInstance().printLine(e.getLocalizedMessage()); 687 } 688 689 } 690 if (isGroupcontainerEditing()) { 691 getGroupEditor().updateBackupElements(result); 692 getGroupcontainer().refreshHighlighting(); 693 } else { 694 if (reloadMarkerFound) { 695 CmsContainerpageController.get().reloadPage(); 696 } else { 697 long loadTime = result.values().iterator().next().getLoadTime(); 698 setLoadTime(Long.valueOf(loadTime)); 699 } 700 } 701 m_handler.updateClipboard(result); 702 resetEditButtons(); 703 CmsContainerpageController.get().fireEvent(new CmsContainerpageEvent(EventType.elementEdited)); 704 if (m_reloadHandler != null) { 705 m_reloadHandler.finish(); 706 } 707 } 708 } 709 710 /** 711 * A RPC action implementation used to request the data for a single container-page element.<p> 712 */ 713 private class SingleElementAction extends CmsRpcAction<Map<String, CmsContainerElementData>> { 714 715 /** Always copy createNew elements in case reading data for a clipboard element used as a copy group. */ 716 private boolean m_alwaysCopy; 717 718 /** Call-back executed on response. */ 719 private I_CmsSimpleCallback<CmsContainerElementData> m_callback; 720 /** The requested client id. */ 721 private String m_clientId; 722 723 /** If this action was triggered by drag and drop from a container, this should contain the id of the origin container. */ 724 private String m_dndContainer; 725 726 /** 727 * Constructor.<p> 728 * 729 * @param clientId the client id 730 * @param callback the call-back 731 * @param alwaysCopy <code>true</code> in case reading data for a clipboard element used as a copy group 732 */ 733 public SingleElementAction( 734 String clientId, 735 boolean alwaysCopy, 736 I_CmsSimpleCallback<CmsContainerElementData> callback) { 737 738 super(); 739 m_clientId = clientId; 740 m_callback = callback; 741 m_alwaysCopy = alwaysCopy; 742 } 743 744 /** 745 * @see org.opencms.gwt.client.rpc.CmsRpcAction#execute() 746 */ 747 @Override 748 public void execute() { 749 750 List<String> clientIds = new ArrayList<String>(); 751 clientIds.add(m_clientId); 752 getContainerpageService().getElementsData( 753 getData().getRpcContext(), 754 getData().getDetailId(), 755 getRequestParams(), 756 clientIds, 757 getPageState(), 758 m_alwaysCopy, 759 m_dndContainer, 760 getLocale(), 761 this); 762 } 763 764 /** 765 * Sets the origin container for the drag and drop case.<p> 766 * 767 * @param containerId the origin container name 768 */ 769 public void setDndContainer(String containerId) { 770 771 m_dndContainer = containerId; 772 } 773 774 /** 775 * @see org.opencms.gwt.client.rpc.CmsRpcAction#onResponse(java.lang.Object) 776 */ 777 @Override 778 protected void onResponse(Map<String, CmsContainerElementData> result) { 779 780 if (result != null) { 781 addElements(result); 782 long loadTime = result.get(m_clientId).getLoadTime(); 783 setLoadTime(Long.valueOf(loadTime)); 784 m_callback.execute(result.get(m_clientId)); 785 } 786 } 787 } 788 789 /** The client side id/setting-hash seperator. */ 790 public static final String CLIENT_ID_SEPERATOR = "#"; 791 792 /** Custom event name. */ 793 public static final String EVENT_DRAG = "ocDrag"; 794 795 /** Custom event name. */ 796 public static final String EVENT_DRAG_FINISHED = "ocDragFinished"; 797 798 /** Custom event name. */ 799 public static final String EVENT_DRAG_STARTED = "ocDragStarted"; 800 801 /** Custom event name. */ 802 public static final String EVENT_ELEMENT_ADDED = "ocElementAdded"; 803 804 /** Custom event name. */ 805 public static final String EVENT_ELEMENT_DELETED = "ocElementDeleted"; 806 807 /** Custom event name. */ 808 public static final String EVENT_ELEMENT_EDITED = "ocElementEdited"; 809 810 /** Custom event name. */ 811 public static final String EVENT_ELEMENT_EDITED_CONTENT = "ocElementEditedContent"; 812 813 /** Custom event name. */ 814 public static final String EVENT_ELEMENT_EDITED_SETTINGS = "ocElementEditedSettings"; 815 816 /** Custom event name. */ 817 public static final String EVENT_ELEMENT_MODIFIED = "ocElementModified"; 818 819 /** Custom event name. */ 820 public static final String EVENT_ELEMENT_MOVED = "ocElementMoved"; 821 822 /** CSS class on the body to signal whether we are currently editing a group container. */ 823 public static final String OC_EDITING_GROUPCONTAINER = "oc-editing-groupcontainer"; 824 825 /** Parameter name. */ 826 public static final String PARAM_REMOVEMODE = "removemode"; 827 828 /** Instance of the data provider. */ 829 private static CmsContainerpageController INSTANCE; 830 831 /** The container element data. All requested elements will be cached here.*/ 832 protected Map<String, CmsContainerElementData> m_elements; 833 834 /** The new element data by resource type name. */ 835 protected Map<String, CmsContainerElementData> m_newElements; 836 837 /** The gallery data update timer. */ 838 Timer m_galleryUpdateTimer; 839 840 /** The currently editing group-container editor. */ 841 A_CmsGroupEditor m_groupEditor; 842 843 /** The container-page handler. */ 844 CmsContainerpageHandler m_handler; 845 846 /** The drag targets within this page. */ 847 Map<String, org.opencms.ade.containerpage.client.ui.CmsContainerPageContainer> m_targetContainers; 848 849 /** The UUIDs for which reuse status has already been checked. */ 850 private Set<CmsUUID> m_checkedReuse = new HashSet<>(); 851 852 /** The container page drag and drop controller. */ 853 private I_CmsDNDController m_cntDndController; 854 855 /** The container-page RPC service. */ 856 private I_CmsContainerpageServiceAsync m_containerpageService; 857 858 /** The container-page util instance. */ 859 private CmsContainerpageUtil m_containerpageUtil; 860 861 /** The container data. */ 862 private Map<String, CmsContainer> m_containers; 863 864 /** The XML content editor handler. */ 865 private CmsContentEditorHandler m_contentEditorHandler; 866 867 /** The core RPC service instance. */ 868 private I_CmsCoreServiceAsync m_coreSvc; 869 870 /** The current edit container level. */ 871 private int m_currentEditLevel = -1; 872 873 /** The prefetched data. */ 874 private CmsCntPageData m_data; 875 876 /** The DND controller. */ 877 private CmsCompositeDNDController m_dndController; 878 879 /** The drag and drop handler. */ 880 private CmsDNDHandler m_dndHandler; 881 882 /** Edit button position timer. */ 883 private Timer m_editButtonsPositionTimer; 884 885 /** The current element view. */ 886 private CmsElementViewInfo m_elementView; 887 888 /** Flag indicating that a content element is being edited. */ 889 private boolean m_isContentEditing; 890 891 /** The container page load time. */ 892 private long m_loadTime; 893 894 /** The lock error message. */ 895 private String m_lockErrorMessage; 896 897 /** The current lock status for the page. */ 898 private LockStatus m_lockStatus = LockStatus.unknown; 899 900 /** The max container level. */ 901 private int m_maxContainerLevel; 902 903 /** The model group base element id. */ 904 private String m_modelGroupElementId; 905 906 /** The browser location at the time the containerpage controller was initialized. */ 907 private String m_originalUrl; 908 909 /** Flag if the container-page has changed. */ 910 private boolean m_pageChanged; 911 912 /** The publish lock checker. */ 913 private CmsPublishLockChecker m_publishLockChecker = new CmsPublishLockChecker(this); 914 915 /** Timer to handle window resize. */ 916 private Timer m_resizeTimer; 917 918 /** Handler for small elements. */ 919 private CmsSmallElementsHandler m_smallElementsHandler; 920 921 /** Alternative preview handler for events. */ 922 private NativePreviewHandler m_previewHandler; 923 924 /** 925 * Constructor.<p> 926 */ 927 public CmsContainerpageController() { 928 929 m_originalUrl = Window.Location.getHref(); 930 INSTANCE = this; 931 try { 932 m_data = (CmsCntPageData)CmsRpcPrefetcher.getSerializedObjectFromDictionary( 933 getContainerpageService(), 934 CmsCntPageData.DICT_NAME); 935 m_elementView = m_data.getElementView(); 936 m_modelGroupElementId = m_data.getModelGroupElementId(); 937 m_loadTime = m_data.getLoadTime(); 938 try { 939 WebStorageWindow window = WebStorageWindow.of(DomGlobal.window); 940 for (Map.Entry<String, String> entry : m_data.getSessionStorageData().entrySet()) { 941 window.sessionStorage.setItem(entry.getKey(), entry.getValue()); 942 } 943 } catch (Exception e) { 944 CmsGwtLog.log("can't use webstorage API"); 945 } 946 } catch (SerializationException e) { 947 CmsErrorDialog.handleException( 948 new Exception( 949 "Deserialization of page data failed. This may be caused by expired java-script resources, please clear your browser cache and try again.", 950 e)); 951 } 952 m_smallElementsHandler = new CmsSmallElementsHandler(getContainerpageService()); 953 if (m_data != null) { 954 m_smallElementsHandler.setEditSmallElements(m_data.isEditSmallElementsInitially(), false); 955 956 m_data.setRpcContext( 957 new CmsContainerPageRpcContext( 958 CmsCoreProvider.get().getStructureId(), 959 m_data.getTemplateContextInfo().getCurrentContext())); 960 } 961 962 } 963 964 /** 965 * Returns the data provider instance.<p> 966 * 967 * @return the data provider 968 */ 969 public static CmsContainerpageController get() { 970 971 if (INSTANCE == null) { 972 CmsDebugLog.getInstance().printLine("WARNING: The data provider has not been initialized!"); 973 return null; 974 } 975 return INSTANCE; 976 } 977 978 /** 979 * Returns the current URI.<p> 980 * 981 * @return the current URI 982 */ 983 public static String getCurrentUri() { 984 985 return CmsCoreProvider.get().getUri(); 986 987 } 988 989 /** 990 * Returns the server id for a given client element id.<p> 991 * 992 * @param clientId the client id including an optional element settings hash 993 * 994 * @return the server id 995 */ 996 public static String getServerId(String clientId) { 997 998 String serverId = clientId; 999 if (clientId.contains(CLIENT_ID_SEPERATOR)) { 1000 serverId = clientId.substring(0, clientId.lastIndexOf(CLIENT_ID_SEPERATOR)); 1001 } 1002 return serverId; 1003 } 1004 1005 /** 1006 * Checks whether element removal should be confirmed.<p> 1007 * 1008 * @return true if element removal should be confirmed 1009 */ 1010 public static boolean isConfirmRemove() { 1011 1012 Map<String, String> params = CmsCoreProvider.get().getAdeParameters(); 1013 String removeMode = params.get(PARAM_REMOVEMODE); 1014 return (removeMode == null) || removeMode.equals("confirm"); 1015 } 1016 1017 /** 1018 * Adds a handler for container page events.<p> 1019 * 1020 * @param handler the handler to add 1021 */ 1022 public void addContainerpageEventHandler(I_CmsContainerpageEventHandler handler) { 1023 1024 CmsCoreProvider.get().getEventBus().addHandler(CmsContainerpageEvent.TYPE, handler); 1025 } 1026 1027 /** 1028 * Adds an element specified by it's id to the favorite list.<p> 1029 * 1030 * @param clientId the element id 1031 */ 1032 public void addToFavoriteList(final String clientId) { 1033 1034 CmsRpcAction<Void> action = new CmsRpcAction<Void>() { 1035 1036 /** 1037 * @see org.opencms.gwt.client.rpc.CmsRpcAction#execute() 1038 */ 1039 @Override 1040 public void execute() { 1041 1042 getContainerpageService().addToFavoriteList(getData().getRpcContext(), clientId, this); 1043 } 1044 1045 /** 1046 * @see org.opencms.gwt.client.rpc.CmsRpcAction#onResponse(java.lang.Object) 1047 */ 1048 @Override 1049 protected void onResponse(Void result) { 1050 1051 CmsNotification.get().send( 1052 Type.NORMAL, 1053 Messages.get().key(Messages.GUI_NOTIFICATION_ADD_TO_FAVORITES_0)); 1054 } 1055 }; 1056 action.execute(); 1057 } 1058 1059 /** 1060 * Adds an element specified by it's id to the recent list.<p> 1061 * 1062 * @param clientId the element id 1063 * @param nextAction the action to execute after the element has been added 1064 */ 1065 public void addToRecentList(final String clientId, final Runnable nextAction) { 1066 1067 CmsRpcAction<Void> action = new CmsRpcAction<Void>() { 1068 1069 /** 1070 * @see org.opencms.gwt.client.rpc.CmsRpcAction#execute() 1071 */ 1072 @Override 1073 public void execute() { 1074 1075 getContainerpageService().addToRecentList(getData().getRpcContext(), clientId, this); 1076 } 1077 1078 /** 1079 * @see org.opencms.gwt.client.rpc.CmsRpcAction#onResponse(java.lang.Object) 1080 */ 1081 @Override 1082 protected void onResponse(Void result) { 1083 1084 if (nextAction != null) { 1085 nextAction.run(); 1086 } 1087 } 1088 }; 1089 action.execute(); 1090 } 1091 1092 /** 1093 * Checks whether GWT widgets are available for all fields of a content.<p> 1094 * 1095 * @param structureId the structure id of the content 1096 * @param resultCallback the callback for the result 1097 */ 1098 public void checkNewWidgetsAvailable(final CmsUUID structureId, final AsyncCallback<Boolean> resultCallback) { 1099 1100 CmsRpcAction<Boolean> action = new CmsRpcAction<Boolean>() { 1101 1102 @Override 1103 public void execute() { 1104 1105 start(200, false); 1106 getContainerpageService().checkNewWidgetsAvailable(structureId, this); 1107 } 1108 1109 @Override 1110 protected void onResponse(Boolean result) { 1111 1112 stop(false); 1113 resultCallback.onSuccess(result); 1114 } 1115 1116 // empty 1117 }; 1118 action.execute(); 1119 1120 } 1121 1122 /** 1123 * Checks reuse status of a container element, then shows a warning dialog if it is reused, and finally executes an action. 1124 * 1125 * @param elementWidget the container element for which to check the reuse status 1126 * @param action the action to execute after the reuse warning dialog is closed 1127 */ 1128 public void checkReuse(CmsContainerPageElementPanel elementWidget, Runnable action) { 1129 1130 Runnable wrappedAction = () -> { 1131 m_checkedReuse.add(elementWidget.getStructureId()); 1132 action.run(); 1133 }; 1134 if (elementWidget.isReused() 1135 && CmsCoreProvider.get().isWarnWhenEditingReusedElement() 1136 && !m_checkedReuse.contains(elementWidget.getStructureId())) { 1137 CmsUUID id = elementWidget.getStructureId(); 1138 CmsUUID detailId = CmsContainerpageController.get().getData().getDetailId(); 1139 CmsUUID pageId = CmsCoreProvider.get().getStructureId(); 1140 CmsRpcAction<CmsReuseInfo> rpc = new CmsRpcAction<CmsReuseInfo>() { 1141 1142 @Override 1143 public void execute() { 1144 1145 start(0, true); 1146 getContainerpageService().getReuseInfo(pageId, detailId, id, this); 1147 } 1148 1149 @Override 1150 protected void onResponse(CmsReuseInfo result) { 1151 1152 stop(false); 1153 if (result.getCount() == 0) { 1154 wrappedAction.run(); 1155 } else { 1156 CmsReuseInfoDialog dialog = new CmsReuseInfoDialog(result, status -> { 1157 if (status.booleanValue()) { 1158 wrappedAction.run(); 1159 } 1160 }); 1161 dialog.show(); 1162 // without delayed center(), positioning is inconsistent 1163 Timer timer = new Timer() { 1164 1165 @Override 1166 public void run() { 1167 1168 dialog.center(); 1169 } 1170 }; 1171 timer.schedule(100); 1172 } 1173 } 1174 }; 1175 rpc.execute(); 1176 } else { 1177 action.run(); 1178 } 1179 } 1180 1181 /** 1182 * Checks for container elements that are no longer present within the DOM.<p> 1183 */ 1184 public void cleanUpContainers() { 1185 1186 List<String> removed = new ArrayList<String>(); 1187 for (Entry<String, CmsContainerPageContainer> entry : m_targetContainers.entrySet()) { 1188 if (!RootPanel.getBodyElement().isOrHasChild(entry.getValue().getElement())) { 1189 removed.add(entry.getKey()); 1190 } 1191 } 1192 for (String containerId : removed) { 1193 m_targetContainers.remove(containerId); 1194 m_containers.remove(containerId); 1195 } 1196 if (removed.size() > 0) { 1197 scheduleGalleryUpdate(); 1198 } 1199 } 1200 1201 /** 1202 * Copies an element and asynchronously returns the structure id of the copy.<p> 1203 * 1204 * @param id the element id 1205 * @param callback the callback for the result 1206 */ 1207 public void copyElement(final String id, final I_CmsSimpleCallback<CmsUUID> callback) { 1208 1209 CmsRpcAction<CmsUUID> action = new CmsRpcAction<CmsUUID>() { 1210 1211 @Override 1212 public void execute() { 1213 1214 start(200, false); 1215 getContainerpageService().copyElement( 1216 CmsCoreProvider.get().getStructureId(), 1217 new CmsUUID(id), 1218 getData().getLocale(), 1219 this); 1220 } 1221 1222 @Override 1223 protected void onResponse(CmsUUID result) { 1224 1225 stop(false); 1226 callback.execute(result); 1227 } 1228 1229 }; 1230 action.execute(); 1231 } 1232 1233 /** 1234 * Creates a new resource for crag container elements with the status new and opens the content editor.<p> 1235 * 1236 * @param element the container element 1237 * @param inline <code>true</code> to open the inline editor for the given element if available 1238 */ 1239 public void createAndEditNewElement( 1240 final org.opencms.ade.containerpage.client.ui.CmsContainerPageElementPanel element, 1241 final boolean inline) { 1242 1243 if (!element.isNew()) { 1244 return; 1245 } 1246 1247 final CmsContainer container = m_containers.get(element.getParentTarget().getContainerId()); 1248 1249 m_handler.showPageOverlay(); 1250 CmsRpcAction<CmsCreateElementData> action = new CmsRpcAction<CmsCreateElementData>() { 1251 1252 /** 1253 * @see org.opencms.gwt.client.rpc.CmsRpcAction#execute() 1254 */ 1255 @Override 1256 public void execute() { 1257 1258 getContainerpageService().checkCreateNewElement( 1259 CmsCoreProvider.get().getStructureId(), 1260 getData().getDetailId(), 1261 element.getId(), 1262 element.getNewType(), 1263 container, 1264 getLocale(), 1265 this); 1266 1267 } 1268 1269 /** 1270 * @see org.opencms.gwt.client.rpc.CmsRpcAction#onResponse(java.lang.Object) 1271 */ 1272 @Override 1273 protected void onResponse(CmsCreateElementData result) { 1274 1275 if (result.needsModelSelection()) { 1276 getHandler().openModelResourceSelect(element, result.getModelResources()); 1277 } else { 1278 openEditorForNewElement(element, result.getCreatedElement(), inline); 1279 } 1280 } 1281 }; 1282 action.execute(); 1283 } 1284 1285 /** 1286 * Creates a new resource for drag container elements with the status new and opens the content editor.<p> 1287 * 1288 * @param element the container element 1289 * @param modelResourceStructureId the model resource structure id 1290 */ 1291 public void createAndEditNewElement( 1292 final org.opencms.ade.containerpage.client.ui.CmsContainerPageElementPanel element, 1293 final CmsUUID modelResourceStructureId) { 1294 1295 CmsRpcAction<CmsContainerElement> action = new CmsRpcAction<CmsContainerElement>() { 1296 1297 @Override 1298 public void execute() { 1299 1300 getContainerpageService().createNewElement( 1301 CmsCoreProvider.get().getStructureId(), 1302 getData().getDetailId(), 1303 element.getId(), 1304 element.getNewType(), 1305 modelResourceStructureId, 1306 getLocale(), 1307 this); 1308 1309 } 1310 1311 @Override 1312 protected void onResponse(CmsContainerElement result) { 1313 1314 openEditorForNewElement(element, result, false); 1315 1316 } 1317 }; 1318 action.execute(); 1319 } 1320 1321 /** 1322 * Creates the details object for a custom page editor event based on the given container page element. 1323 * 1324 * @param widget a container page element 1325 * @return the event details object 1326 */ 1327 public I_EventDetails createEventDetails(CmsContainerPageElementPanel widget) { 1328 1329 final Element parentContainerElement = CmsDomUtil.getAncestor( 1330 widget.getElement(), 1331 CmsContainerElement.CLASS_CONTAINER); 1332 1333 I_EventDetails details = Js.cast(new JsObject()); 1334 details.setIsPlaceholder(widget.isNew()); 1335 details.setElement(Js.cast(widget.getElement())); 1336 details.setContainer(Js.cast(parentContainerElement)); 1337 return details; 1338 } 1339 1340 /** 1341 * Creates a new element.<p> 1342 * 1343 * @param element the widget belonging to the element which is currently in memory only 1344 * @param callback the callback to call with the result 1345 */ 1346 public void createNewElement( 1347 final CmsContainerPageElementPanel element, 1348 final AsyncCallback<CmsContainerElement> callback) { 1349 1350 CmsRpcAction<CmsContainerElement> action = new CmsRpcAction<CmsContainerElement>() { 1351 1352 @Override 1353 public void execute() { 1354 1355 getContainerpageService().createNewElement( 1356 CmsCoreProvider.get().getStructureId(), 1357 getData().getDetailId(), 1358 element.getId(), 1359 element.getNewType(), 1360 null, 1361 getLocale(), 1362 this); 1363 1364 } 1365 1366 @Override 1367 protected void onResponse(CmsContainerElement result) { 1368 1369 callback.onSuccess(result); 1370 } 1371 }; 1372 action.execute(); 1373 } 1374 1375 /** 1376 * Deletes an element from the VFS, removes it from all containers and the client side cache.<p> 1377 * 1378 * @param elementId the element to delete 1379 * @param relatedElementId related element to reload after the element has been deleted 1380 */ 1381 public void deleteElement(String elementId, final String relatedElementId) { 1382 1383 elementId = getServerId(elementId); 1384 removeContainerElements(elementId); 1385 addToRecentList(elementId, null); 1386 reloadElements(new String[] {relatedElementId}, () -> {/*do nothing*/}); 1387 } 1388 1389 /** 1390 * Disables the inline editing for all content elements but the given one.<p> 1391 * 1392 * @param notThisOne the content element not to disable 1393 */ 1394 public void disableInlineEditing(CmsContainerPageElementPanel notThisOne) { 1395 1396 removeEditButtonsPositionTimer(); 1397 if (isGroupcontainerEditing()) { 1398 for (Widget element : m_groupEditor.getGroupContainerWidget()) { 1399 if ((element instanceof CmsContainerPageElementPanel) && (element != notThisOne)) { 1400 ((CmsContainerPageElementPanel)element).removeInlineEditor(); 1401 } 1402 } 1403 } else { 1404 for (org.opencms.ade.containerpage.client.ui.CmsContainerPageContainer container : m_targetContainers.values()) { 1405 for (Widget element : container) { 1406 if ((element instanceof CmsContainerPageElementPanel) && (element != notThisOne)) { 1407 ((CmsContainerPageElementPanel)element).removeInlineEditor(); 1408 } 1409 } 1410 } 1411 } 1412 } 1413 1414 /** 1415 * Enables the favorites editing drag and drop controller.<p> 1416 * 1417 * @param enable if <code>true</code> favorites editing will enabled, otherwise disabled 1418 * @param dndController the favorites editing drag and drop controller 1419 */ 1420 public void enableFavoriteEditing(boolean enable, I_CmsDNDController dndController) { 1421 1422 if (m_dndHandler.isDragging()) { 1423 // never switch drag and drop controllers while dragging 1424 return; 1425 } 1426 if (enable) { 1427 m_dndHandler.setController(dndController); 1428 } else { 1429 m_dndHandler.setController(m_cntDndController); 1430 } 1431 1432 } 1433 1434 /** 1435 * Replaces all element instances of the original element with the new element within the former copy model.<p> 1436 * 1437 * @param originalElementId the original element id 1438 * @param modelGroupParent the model group parent element 1439 * @param elementData the replace element data 1440 */ 1441 public void executeCopyModelReplace( 1442 String originalElementId, 1443 Element modelGroupParent, 1444 CmsContainerElementData elementData) { 1445 1446 String serverId = getServerId(originalElementId); 1447 for (CmsContainerPageContainer cont : m_targetContainers.values()) { 1448 if (modelGroupParent.isOrHasChild(cont.getElement())) { 1449 // look for instances of the original element 1450 for (Widget child : cont) { 1451 if ((child instanceof CmsContainerPageElementPanel) 1452 && ((CmsContainerPageElementPanel)child).getId().startsWith(serverId)) { 1453 CmsContainerPageElementPanel replacer = null; 1454 String elementContent = elementData.getContents().get(cont.getContainerId()); 1455 if ((elementContent != null) && (elementContent.trim().length() > 0)) { 1456 try { 1457 replacer = getContainerpageUtil().createElement(elementData, cont, false); 1458 cont.insert(replacer, cont.getWidgetIndex(child)); 1459 child.removeFromParent(); 1460 initializeSubContainers(replacer); 1461 } catch (Exception e) { 1462 //ignore 1463 } 1464 } 1465 } 1466 } 1467 } 1468 } 1469 } 1470 1471 /** 1472 * Fires an event on the core event bus.<p> 1473 * 1474 * @param event the event to fire 1475 */ 1476 public void fireEvent(CmsContainerpageEvent event) { 1477 1478 CmsCoreProvider.get().getEventBus().fireEvent(event); 1479 1480 } 1481 1482 /** 1483 * Returns all drag elements of the page.<p> 1484 * 1485 * @return the drag elements 1486 */ 1487 public List<org.opencms.ade.containerpage.client.ui.CmsContainerPageElementPanel> getAllDragElements() { 1488 1489 List<org.opencms.ade.containerpage.client.ui.CmsContainerPageElementPanel> result = new ArrayList<org.opencms.ade.containerpage.client.ui.CmsContainerPageElementPanel>(); 1490 Iterator<org.opencms.ade.containerpage.client.ui.CmsContainerPageContainer> it = m_targetContainers.values().iterator(); 1491 while (it.hasNext()) { 1492 result.addAll(it.next().getAllDragElements()); 1493 } 1494 if (isGroupcontainerEditing()) { 1495 Iterator<Widget> itSub = m_groupEditor.getGroupContainerWidget().iterator(); 1496 while (itSub.hasNext()) { 1497 Widget w = itSub.next(); 1498 if (w instanceof org.opencms.ade.containerpage.client.ui.CmsContainerPageElementPanel) { 1499 result.add((org.opencms.ade.containerpage.client.ui.CmsContainerPageElementPanel)w); 1500 } 1501 } 1502 } 1503 return result; 1504 } 1505 1506 /** 1507 * Returns the data for the requested element, or <code>null</code> if the element has not been cached yet.<p> 1508 * 1509 * @param clientId the element id 1510 * 1511 * @return the element data 1512 */ 1513 public CmsContainerElementData getCachedElement(String clientId) { 1514 1515 if (m_elements.containsKey(clientId)) { 1516 return m_elements.get(clientId); 1517 } 1518 return null; 1519 } 1520 1521 /** 1522 * Returns the data for the requested element, or <code>null</code> if the element has not been cached yet.<p> 1523 * 1524 * @param resourceTypeName the element resource type 1525 * 1526 * @return the element data 1527 */ 1528 public CmsContainerElementData getCachedNewElement(String resourceTypeName) { 1529 1530 if (m_newElements.containsKey(resourceTypeName)) { 1531 return m_newElements.get(resourceTypeName); 1532 } 1533 return null; 1534 } 1535 1536 /** 1537 * Returns the container data of container with the given name. 1538 * 1539 * @param containerName the container name 1540 * 1541 * @return the container data 1542 */ 1543 public CmsContainer getContainer(String containerName) { 1544 1545 return m_containers.get(containerName); 1546 } 1547 1548 /** 1549 * Gets the container element widget to which the given element belongs, or Optional.absent if none could be found.<p> 1550 * 1551 * @param element the element for which the container element widget should be found 1552 * 1553 * @return the container element widget, or Optional.absent if none can be found 1554 */ 1555 public Optional<CmsContainerPageElementPanel> getContainerElementWidgetForElement(Element element) { 1556 1557 final Element parentContainerElement = CmsDomUtil.getAncestor( 1558 element, 1559 I_CmsLayoutBundle.INSTANCE.dragdropCss().dragElement()); 1560 if (parentContainerElement == null) { 1561 return Optional.absent(); 1562 } 1563 final List<CmsContainerPageElementPanel> result = Lists.newArrayList(); 1564 processPageContent(new I_PageContentVisitor() { 1565 1566 public boolean beginContainer(String name, CmsContainer container) { 1567 1568 // we don't need to look into the container if we have already found our container element 1569 return result.isEmpty(); 1570 } 1571 1572 public void endContainer() { 1573 1574 // do nothing 1575 } 1576 1577 public void handleElement(CmsContainerPageElementPanel current) { 1578 1579 if ((current.getElement() == parentContainerElement) && result.isEmpty()) { 1580 result.add(current); 1581 } 1582 } 1583 }); 1584 if (result.isEmpty()) { 1585 return Optional.absent(); 1586 } else { 1587 return Optional.fromNullable(result.get(0)); 1588 } 1589 } 1590 1591 /** 1592 * Gets the container info to send to the gallery service. 1593 * 1594 * @return the container info to send to the gallery service 1595 */ 1596 public CmsGalleryContainerInfo getContainerInfoForGalleries() { 1597 1598 if (m_targetContainers != null) { 1599 HashSet<CmsGalleryContainerInfo.Item> items = new HashSet<>(); 1600 for (CmsContainerPageContainer cont : m_targetContainers.values()) { 1601 items.add(new CmsGalleryContainerInfo.Item(cont.getContainerType(), cont.getConfiguredWidth())); 1602 } 1603 return new CmsGalleryContainerInfo(items); 1604 } 1605 return null; 1606 } 1607 1608 /** 1609 * Returns the container-page RPC service.<p> 1610 * 1611 * @return the container-page service 1612 */ 1613 public I_CmsContainerpageServiceAsync getContainerpageService() { 1614 1615 if (m_containerpageService == null) { 1616 m_containerpageService = GWT.create(I_CmsContainerpageService.class); 1617 String serviceUrl = CmsCoreProvider.get().link("org.opencms.ade.containerpage.CmsContainerpageService.gwt"); 1618 ((ServiceDefTarget)m_containerpageService).setServiceEntryPoint(serviceUrl); 1619 } 1620 return m_containerpageService; 1621 } 1622 1623 /** 1624 * Returns the {@link org.opencms.ade.containerpage.client.CmsContainerpageUtil}.<p> 1625 * 1626 * @return the containerpage-util 1627 */ 1628 public CmsContainerpageUtil getContainerpageUtil() { 1629 1630 return m_containerpageUtil; 1631 } 1632 1633 /** 1634 * Returns the containers.<p> 1635 * 1636 * @return the containers 1637 */ 1638 public Map<String, CmsContainer> getContainers() { 1639 1640 return m_containers; 1641 } 1642 1643 /** 1644 * Returns the container drag target by name (HTML id attribute).<p> 1645 * 1646 * @param containerName the container name 1647 * @return the drag target 1648 */ 1649 public org.opencms.ade.containerpage.client.ui.CmsContainerPageContainer getContainerTarget(String containerName) { 1650 1651 return m_targetContainers.get(containerName); 1652 } 1653 1654 /** 1655 * Returns a map of the container drag targets.<p> 1656 * 1657 * @return the drag targets 1658 */ 1659 public Map<String, org.opencms.ade.containerpage.client.ui.CmsContainerPageContainer> getContainerTargets() { 1660 1661 Map<String, org.opencms.ade.containerpage.client.ui.CmsContainerPageContainer> result = new HashMap<String, org.opencms.ade.containerpage.client.ui.CmsContainerPageContainer>(); 1662 for (Entry<String, org.opencms.ade.containerpage.client.ui.CmsContainerPageContainer> entry : m_targetContainers.entrySet()) { 1663 if (entry.getValue().isEditable() 1664 && (!isDetailPage() || (entry.getValue().isDetailOnly() || entry.getValue().isDetailView()))) { 1665 result.put(entry.getKey(), entry.getValue()); 1666 } 1667 } 1668 return result; 1669 } 1670 1671 /** 1672 * Returns the type of container with the given name.<p> 1673 * 1674 * @param containerName the container name 1675 * 1676 * @return the container type 1677 */ 1678 public String getContainerType(String containerName) { 1679 1680 return getContainer(containerName).getType(); 1681 } 1682 1683 /** 1684 * Returns the XML content editor handler.<p> 1685 * 1686 * @return the XML content editor handler 1687 */ 1688 public CmsContentEditorHandler getContentEditorHandler() { 1689 1690 return m_contentEditorHandler; 1691 } 1692 1693 /** 1694 * Returns the prefetched data.<p> 1695 * 1696 * @return the prefetched data 1697 */ 1698 public CmsCntPageData getData() { 1699 1700 return m_data; 1701 } 1702 1703 /** 1704 * Returns the delete options for the given content element.<p> 1705 * 1706 * @param clientId the content id 1707 * @param callback the callback to execute 1708 */ 1709 public void getDeleteOptions(final String clientId, final I_CmsSimpleCallback<CmsDialogOptionsAndInfo> callback) { 1710 1711 CmsRpcAction<CmsDialogOptionsAndInfo> action = new CmsRpcAction<CmsDialogOptionsAndInfo>() { 1712 1713 @Override 1714 public void execute() { 1715 1716 getContainerpageService().getDeleteOptions( 1717 clientId, 1718 getData().getRpcContext().getPageStructureId(), 1719 getData().getRequestParams(), 1720 this); 1721 } 1722 1723 @Override 1724 protected void onResponse(CmsDialogOptionsAndInfo result) { 1725 1726 callback.execute(result); 1727 } 1728 }; 1729 action.execute(); 1730 } 1731 1732 /** 1733 * Gets the DND controller.<p> 1734 * 1735 * @return the DND controller 1736 */ 1737 public CmsCompositeDNDController getDndController() { 1738 1739 return m_dndController; 1740 } 1741 1742 /** 1743 * Returns the drag and drop handler.<p> 1744 * 1745 * @return the drag and drop handler 1746 */ 1747 public CmsDNDHandler getDndHandler() { 1748 1749 return m_dndHandler; 1750 } 1751 1752 /** 1753 * Returns the edit options for the given content element.<p> 1754 * 1755 * @param clientId the content id 1756 * @param isListElement in case a list element, not a container element is about to be edited 1757 * @param callback the callback to execute 1758 */ 1759 public void getEditOptions( 1760 final String clientId, 1761 final boolean isListElement, 1762 final I_CmsSimpleCallback<CmsDialogOptionsAndInfo> callback) { 1763 1764 CmsRpcAction<CmsDialogOptionsAndInfo> action = new CmsRpcAction<CmsDialogOptionsAndInfo>() { 1765 1766 @Override 1767 public void execute() { 1768 1769 getContainerpageService().getEditOptions( 1770 clientId, 1771 getData().getRpcContext().getPageStructureId(), 1772 getData().getRequestParams(), 1773 isListElement, 1774 this); 1775 } 1776 1777 @Override 1778 protected void onResponse(CmsDialogOptionsAndInfo result) { 1779 1780 callback.execute(result); 1781 } 1782 }; 1783 action.execute(); 1784 } 1785 1786 /** 1787 * Requests the data for a container element specified by the client id for drag and drop from a container. The data will be provided to the given call-back function.<p> 1788 * 1789 * @param clientId the element id 1790 * @param containerId the id of the container from which the element is being dragged 1791 * @param alwaysCopy <code>true</code> in case reading data for a clipboard element used as a copy group 1792 * @param callback the call-back to execute with the requested data 1793 */ 1794 public void getElementForDragAndDropFromContainer( 1795 final String clientId, 1796 final String containerId, 1797 boolean alwaysCopy, 1798 final I_CmsSimpleCallback<CmsContainerElementData> callback) { 1799 1800 SingleElementAction action = new SingleElementAction(clientId, alwaysCopy, callback); 1801 action.setDndContainer(containerId); 1802 action.execute(); 1803 } 1804 1805 /** 1806 * Requests the data for container elements specified by the client id. The data will be provided to the given call-back function.<p> 1807 * 1808 * @param clientIds the element id's 1809 * @param callback the call-back to execute with the requested data 1810 */ 1811 public void getElements(Set<String> clientIds, I_CmsSimpleCallback<Map<String, CmsContainerElementData>> callback) { 1812 1813 MultiElementAction action = new MultiElementAction(clientIds, callback); 1814 action.execute(); 1815 } 1816 1817 /** 1818 * Requests the element settings config data for a container element specified by the client id. The data will be provided to the given call-back function.<p> 1819 * 1820 * @param clientId the element id 1821 * @param containerId the parent container id 1822 * @param callback the call-back to execute with the requested data 1823 */ 1824 public void getElementSettingsConfig( 1825 final String clientId, 1826 final String containerId, 1827 final I_CmsSimpleCallback<CmsElementSettingsConfig> callback) { 1828 1829 CmsRpcAction<CmsElementSettingsConfig> action = new CmsRpcAction<CmsElementSettingsConfig>() { 1830 1831 /** 1832 * @see org.opencms.gwt.client.rpc.CmsRpcAction#execute() 1833 */ 1834 @Override 1835 public void execute() { 1836 1837 start(100, true); 1838 getContainerpageService().getElementSettingsConfig( 1839 getData().getRpcContext(), 1840 clientId, 1841 containerId, 1842 getPageState(), 1843 getLocale(), 1844 this); 1845 1846 } 1847 1848 /** 1849 * @see org.opencms.gwt.client.rpc.CmsRpcAction#onResponse(java.lang.Object) 1850 */ 1851 @Override 1852 protected void onResponse(CmsElementSettingsConfig result) { 1853 1854 if (result != null) { 1855 callback.execute(result); 1856 } 1857 stop(false); 1858 } 1859 }; 1860 action.execute(); 1861 } 1862 1863 /** 1864 * Returns the current element view.<p> 1865 * 1866 * @return the current element view 1867 */ 1868 public CmsElementViewInfo getElementView() { 1869 1870 return m_elementView; 1871 } 1872 1873 /** 1874 * Retrieves a container element with a given set of settings.<p> 1875 * 1876 * @param clientId the id of the container element 1877 * @param settings the set of settings 1878 * 1879 * @param callback the callback which should be executed when the element has been loaded 1880 */ 1881 public void getElementWithSettings( 1882 final String clientId, 1883 final Map<String, String> settings, 1884 final I_CmsSimpleCallback<CmsContainerElementData> callback) { 1885 1886 CmsRpcAction<CmsContainerElementData> action = new CmsRpcAction<CmsContainerElementData>() { 1887 1888 /** 1889 * @see org.opencms.gwt.client.rpc.CmsRpcAction#execute() 1890 */ 1891 @Override 1892 public void execute() { 1893 1894 start(200, false); 1895 getContainerpageService().getElementWithSettings( 1896 getData().getRpcContext(), 1897 getData().getDetailId(), 1898 getRequestParams(), 1899 clientId, 1900 settings, 1901 getPageState(), 1902 getLocale(), 1903 this); 1904 1905 } 1906 1907 /** 1908 * @see org.opencms.gwt.client.rpc.CmsRpcAction#onResponse(java.lang.Object) 1909 */ 1910 @Override 1911 protected void onResponse(CmsContainerElementData result) { 1912 1913 stop(false); 1914 if (result != null) { 1915 // cache the loaded element 1916 m_elements.put(result.getClientId(), result); 1917 } 1918 callback.execute(result); 1919 } 1920 1921 }; 1922 action.execute(); 1923 1924 } 1925 1926 /** 1927 * Returns the group-container element being edited.<p> 1928 * 1929 * @return the group-container 1930 */ 1931 public CmsGroupContainerElementPanel getGroupcontainer() { 1932 1933 return m_groupEditor.getGroupContainerWidget(); 1934 } 1935 1936 /** 1937 * Returns the id of the currently edited group-container.<p> 1938 * 1939 * @return the group-container id, or <code>null</code> if no editing is taking place 1940 */ 1941 public String getGroupcontainerId() { 1942 1943 if (m_groupEditor != null) { 1944 return m_groupEditor.getGroupContainerWidget().getContainerId(); 1945 } 1946 return null; 1947 } 1948 1949 /** 1950 * Returns the container-page handler.<p> 1951 * 1952 * @return the container-page handler 1953 */ 1954 public CmsContainerpageHandler getHandler() { 1955 1956 return m_handler; 1957 } 1958 1959 /** 1960 * Returns the time off page load.<p> 1961 * 1962 * @return the time stamp 1963 */ 1964 public long getLoadTime() { 1965 1966 return m_loadTime; 1967 } 1968 1969 /** 1970 * Gets the lock error message.<p> 1971 * 1972 * @return the lock error message 1973 */ 1974 public String getLockErrorMessage() { 1975 1976 return m_lockErrorMessage; 1977 } 1978 1979 /** 1980 * Returns the model group base element id.<p> 1981 * 1982 * @return the model group base element id 1983 */ 1984 public String getModelGroupElementId() { 1985 1986 return m_modelGroupElementId; 1987 } 1988 1989 /** 1990 * Collects all container elements which are model groups.<p> 1991 * 1992 * @return the list of model group container elements 1993 */ 1994 public List<CmsContainerPageElementPanel> getModelGroups() { 1995 1996 final List<CmsContainerPageElementPanel> result = Lists.newArrayList(); 1997 1998 processPageContent(new I_PageContentVisitor() { 1999 2000 public boolean beginContainer(String name, CmsContainer container) { 2001 2002 return true; 2003 } 2004 2005 public void endContainer() { 2006 2007 // do nothing 2008 } 2009 2010 public void handleElement(CmsContainerPageElementPanel element) { 2011 2012 if (element.isModelGroup()) { 2013 result.add(element); 2014 } 2015 } 2016 }); 2017 return result; 2018 } 2019 2020 /** 2021 * Returns the element data for a resource type representing a new element.<p> 2022 * 2023 * @param resourceType the resource type name 2024 * @param callback the callback to execute with the new element data 2025 */ 2026 public void getNewElement(final String resourceType, final I_CmsSimpleCallback<CmsContainerElementData> callback) { 2027 2028 CmsRpcAction<CmsContainerElementData> action = new CmsRpcAction<CmsContainerElementData>() { 2029 2030 @Override 2031 public void execute() { 2032 2033 getContainerpageService().getNewElementData( 2034 getData().getRpcContext(), 2035 getData().getDetailId(), 2036 getRequestParams(), 2037 resourceType, 2038 getPageState(), 2039 getLocale(), 2040 this); 2041 } 2042 2043 @Override 2044 protected void onResponse(CmsContainerElementData result) { 2045 2046 m_elements.put(result.getClientId(), result); 2047 callback.execute(result); 2048 } 2049 }; 2050 action.execute(); 2051 } 2052 2053 /** 2054 * Fetches the options for creating a new element from the edit handler.<p> 2055 * 2056 * @param clientId the client id of the element 2057 * @param callback the callback which is called with the result 2058 */ 2059 public void getNewOptions(final String clientId, final I_CmsSimpleCallback<CmsDialogOptionsAndInfo> callback) { 2060 2061 CmsRpcAction<CmsDialogOptionsAndInfo> action = new CmsRpcAction<CmsDialogOptionsAndInfo>() { 2062 2063 @Override 2064 public void execute() { 2065 2066 getContainerpageService().getNewOptions( 2067 clientId, 2068 getData().getRpcContext().getPageStructureId(), 2069 getData().getRequestParams(), 2070 this); 2071 } 2072 2073 @Override 2074 protected void onResponse(CmsDialogOptionsAndInfo result) { 2075 2076 callback.execute(result); 2077 } 2078 }; 2079 2080 action.execute(); 2081 } 2082 2083 /** 2084 * Produces the "return code", which is needed to return to the current page from the sitemap.<p> 2085 * 2086 * @return the return code 2087 */ 2088 public String getReturnCode() { 2089 2090 CmsUUID ownId = CmsCoreProvider.get().getStructureId(); 2091 CmsUUID detailId = m_data.getDetailId(); 2092 if (detailId != null) { 2093 return "" + ownId + ":" + detailId; 2094 } else { 2095 return "" + ownId; 2096 } 2097 } 2098 2099 /** 2100 * Returns the deserialized element data.<p> 2101 * 2102 * @param data the data to deserialize 2103 * 2104 * @return the container element 2105 * @throws SerializationException if deserialization fails 2106 */ 2107 public CmsContainer getSerializedContainer(String data) throws SerializationException { 2108 2109 return (CmsContainer)CmsRpcPrefetcher.getSerializedObjectFromString(getContainerpageService(), data); 2110 } 2111 2112 /** 2113 * Returns the deserialized element data.<p> 2114 * 2115 * @param data the data to deserialize 2116 * 2117 * @return the container element 2118 * @throws SerializationException if deserialization fails 2119 */ 2120 public CmsContainerElement getSerializedElement(String data) throws SerializationException { 2121 2122 return (CmsContainerElement)CmsRpcPrefetcher.getSerializedObjectFromString(getContainerpageService(), data); 2123 } 2124 2125 /** 2126 * Gets the handler for small elements.<p> 2127 * 2128 * @return the small elements handler 2129 */ 2130 public CmsSmallElementsHandler getSmallElementsHandler() { 2131 2132 return m_smallElementsHandler; 2133 } 2134 2135 /** 2136 * Gets the view with the given id.<p> 2137 * 2138 * @param value the view id as a string 2139 * 2140 * @return the view with the given id, or null if no such view is available 2141 */ 2142 public CmsElementViewInfo getView(String value) { 2143 2144 for (CmsElementViewInfo info : m_data.getElementViews()) { 2145 if (info.getElementViewId().toString().equals(value)) { 2146 return info; 2147 } 2148 } 2149 return null; 2150 } 2151 2152 /** 2153 * Handler that gets called when the template context setting of an element was changed by the user.<p> 2154 * 2155 * @param element the element whose template context setting was changed 2156 * 2157 * @param newValue the new value of the setting 2158 */ 2159 public void handleChangeTemplateContext(final CmsContainerPageElementPanel element, final String newValue) { 2160 2161 if (CmsStringUtil.isEmptyOrWhitespaceOnly(newValue) || CmsTemplateContextInfo.EMPTY_VALUE.equals(newValue)) { 2162 if (CmsInheritanceContainerEditor.getInstance() != null) { 2163 CmsInheritanceContainerEditor.getInstance().removeElement(element); 2164 } else { 2165 removeElement(element, ElementRemoveMode.silent); 2166 } 2167 } 2168 } 2169 2170 /** 2171 * Asks the user for confirmation before removing a container page element.<p> 2172 * 2173 * @param element the element for which the user should confirm the removal 2174 */ 2175 public void handleConfirmRemove(final CmsContainerPageElementPanel element) { 2176 2177 if (element.isNew()) { 2178 I_EventDetails details = createEventDetails(element); 2179 element.removeFromParent(); 2180 cleanUpContainers(); 2181 setPageChanged(); 2182 sendElementDeleted(details); 2183 return; 2184 } 2185 checkElementReferences(element, new AsyncCallback<CmsRemovedElementStatus>() { 2186 2187 public void onFailure(Throwable caught) { 2188 2189 // ignore, will never be executed 2190 2191 } 2192 2193 public void onSuccess(CmsRemovedElementStatus status) { 2194 2195 boolean showDeleteCheckbox = status.isDeletionCandidate(); 2196 ElementDeleteMode deleteMode = status.getElementDeleteMode(); 2197 if (deleteMode == null) { 2198 deleteMode = getData().getDeleteMode(); 2199 } 2200 CmsConfirmRemoveDialog removeDialog = new CmsConfirmRemoveDialog( 2201 status.getElementInfo(), 2202 showDeleteCheckbox, 2203 deleteMode, 2204 new AsyncCallback<Boolean>() { 2205 2206 public void onFailure(Throwable caught) { 2207 2208 element.removeHighlighting(); 2209 } 2210 2211 public void onSuccess(Boolean shouldDeleteResource) { 2212 2213 Runnable[] nextActions = new Runnable[] {}; 2214 2215 if (shouldDeleteResource.booleanValue()) { 2216 final CmsRpcAction<Void> deleteAction = new CmsRpcAction<Void>() { 2217 2218 @Override 2219 public void execute() { 2220 2221 start(200, true); 2222 2223 CmsUUID id = new CmsUUID(getServerId(element.getId())); 2224 CmsCoreProvider.getVfsService().deleteResource(id, this); 2225 } 2226 2227 @Override 2228 public void onResponse(Void result) { 2229 2230 stop(true); 2231 } 2232 }; 2233 nextActions = new Runnable[] {new Runnable() { 2234 2235 public void run() { 2236 2237 deleteAction.execute(); 2238 } 2239 }}; 2240 } 2241 I_CmsDropContainer container = element.getParentTarget(); 2242 I_EventDetails details = createEventDetails(element); 2243 element.removeFromParent(); 2244 if (container instanceof CmsContainerPageContainer) { 2245 ((CmsContainerPageContainer)container).checkEmptyContainers(); 2246 } 2247 cleanUpContainers(); 2248 sendElementDeleted(details); 2249 setPageChanged(nextActions); 2250 } 2251 }); 2252 removeDialog.center(); 2253 } 2254 2255 }); 2256 } 2257 2258 /** 2259 * Calls the edit handler to handle the delete action.<p> 2260 * 2261 * @param clientId the content client id 2262 * @param deleteOption the selected delete option 2263 * @param callback the callback to execute after the delete 2264 */ 2265 public void handleDelete( 2266 final String clientId, 2267 final String deleteOption, 2268 final I_CmsSimpleCallback<Void> callback) { 2269 2270 CmsRpcAction<Void> action = new CmsRpcAction<Void>() { 2271 2272 @Override 2273 public void execute() { 2274 2275 getContainerpageService().handleDelete( 2276 clientId, 2277 deleteOption, 2278 getData().getRpcContext().getPageStructureId(), 2279 getData().getRequestParams(), 2280 this); 2281 } 2282 2283 @Override 2284 protected void onResponse(Void result) { 2285 2286 if (callback != null) { 2287 callback.execute(result); 2288 } 2289 } 2290 }; 2291 action.execute(); 2292 } 2293 2294 /** 2295 * Returns if the selection button is active.<p> 2296 * 2297 * @return <code>true</code> if the selection button is active 2298 */ 2299 public boolean hasActiveSelection() { 2300 2301 return m_handler.hasActiveSelection(); 2302 } 2303 2304 /** 2305 * Returns if the page has changed.<p> 2306 * 2307 * @return <code>true</code> if the page has changed 2308 */ 2309 public boolean hasPageChanged() { 2310 2311 return m_pageChanged; 2312 } 2313 2314 /** 2315 * Hides list collector direct edit buttons, if present.<p> 2316 */ 2317 public void hideEditableListButtons() { 2318 2319 removeEditButtonsPositionTimer(); 2320 for (org.opencms.ade.containerpage.client.ui.CmsContainerPageContainer container : m_targetContainers.values()) { 2321 container.hideEditableListButtons(); 2322 } 2323 } 2324 2325 /** 2326 * Initializes the controller.<p> 2327 * 2328 * @param handler the container-page handler 2329 * @param dndHandler the drag and drop handler 2330 * @param contentEditorHandler the XML content editor handler 2331 * @param containerpageUtil the container-page utility 2332 */ 2333 public void init( 2334 CmsContainerpageHandler handler, 2335 CmsDNDHandler dndHandler, 2336 CmsContentEditorHandler contentEditorHandler, 2337 CmsContainerpageUtil containerpageUtil) { 2338 2339 if (getData().getDetailId() != null) { 2340 CmsEmbeddedAction.PARAMS.put(CmsGwtConstants.PARAM_ADE_DETAIL_ID, "" + getData().getDetailId()); 2341 } 2342 2343 Window.addResizeHandler(new ResizeHandler() { 2344 2345 public void onResize(ResizeEvent event) { 2346 2347 CmsContainerpageController.this.onResize(); 2348 } 2349 }); 2350 m_containerpageUtil = containerpageUtil; 2351 m_handler = handler; 2352 m_contentEditorHandler = contentEditorHandler; 2353 m_dndHandler = dndHandler; 2354 m_cntDndController = m_dndHandler.getController(); 2355 2356 m_elements = new HashMap<String, CmsContainerElementData>(); 2357 m_newElements = new HashMap<String, CmsContainerElementData>(); 2358 m_containers = new HashMap<String, CmsContainer>(); 2359 if (m_data == null) { 2360 m_handler.m_editor.disableEditing(Messages.get().key(Messages.ERR_READING_CONTAINER_PAGE_DATA_0)); 2361 CmsErrorDialog dialog = new CmsErrorDialog( 2362 Messages.get().key(Messages.ERR_READING_CONTAINER_PAGE_DATA_0), 2363 null); 2364 dialog.center(); 2365 return; 2366 } 2367 // ensure any embedded flash players are set opaque so UI elements may be placed above them 2368 CmsDomUtil.fixFlashZindex(RootPanel.getBodyElement()); 2369 m_targetContainers = m_containerpageUtil.consumeContainers(m_containers, RootPanel.getBodyElement()); 2370 updateContainerLevelInfo(); 2371 resetEditButtons(); 2372 Event.addNativePreviewHandler(new NativePreviewHandler() { 2373 2374 public void onPreviewNativeEvent(NativePreviewEvent event) { 2375 2376 previewNativeEvent(event); 2377 } 2378 }); 2379 if (CmsStringUtil.isNotEmptyOrWhitespaceOnly(m_data.getNoEditReason())) { 2380 m_handler.m_editor.disableEditing(m_data.getNoEditReason()); 2381 } else { 2382 checkLockInfo(); 2383 } 2384 2385 // initialize the browser history handler 2386 History.addValueChangeHandler(new ValueChangeHandler<String>() { 2387 2388 public void onValueChange(ValueChangeEvent<String> event) { 2389 2390 String historyToken = event.getValue(); 2391 if (CmsStringUtil.isNotEmptyOrWhitespaceOnly(historyToken)) { 2392 getContentEditorHandler().openEditorForHistory(historyToken); 2393 } else { 2394 getContentEditorHandler().closeContentEditor(); 2395 } 2396 } 2397 }); 2398 AsyncCallback<Void> doNothing = new AsyncCallback<Void>() { 2399 2400 public void onFailure(Throwable caught) { 2401 2402 // nothing to do 2403 } 2404 2405 public void onSuccess(Void result) { 2406 2407 // nothing to do 2408 } 2409 }; 2410 getContainerpageService().setLastPage(CmsCoreProvider.get().getStructureId(), m_data.getDetailId(), doNothing); 2411 2412 // check if there is already a history item available 2413 String historyToken = History.getToken(); 2414 if (CmsStringUtil.isNotEmptyOrWhitespaceOnly(historyToken)) { 2415 m_contentEditorHandler.openEditorForHistory(historyToken); 2416 } 2417 2418 updateGalleryData(false, null); 2419 addContainerpageEventHandler(event -> { 2420 updateDetailPreviewStyles(); 2421 }); 2422 updateDetailPreviewStyles(); 2423 updateButtonsForCurrentView(); 2424 startPublishLockCheck(); 2425 } 2426 2427 /** 2428 * Checks for element sub containers.<p> 2429 * 2430 * @param containerElement the container element 2431 */ 2432 public void initializeSubContainers(CmsContainerPageElementPanel containerElement) { 2433 2434 int containerCount = m_targetContainers.size(); 2435 m_targetContainers.putAll(m_containerpageUtil.consumeContainers(m_containers, containerElement.getElement())); 2436 updateContainerLevelInfo(); 2437 if (m_targetContainers.size() > containerCount) { 2438 // in case new containers have been added, the gallery data needs to be updated 2439 scheduleGalleryUpdate(); 2440 } 2441 } 2442 2443 /** 2444 * Returns if the given container is editable.<p> 2445 * 2446 * @param dragParent the parent container 2447 * 2448 * @return <code>true</code> if the given container is editable 2449 */ 2450 public boolean isContainerEditable(I_CmsDropContainer dragParent) { 2451 2452 boolean isSubElement = dragParent instanceof CmsGroupContainerElementPanel; 2453 boolean isContainerEditable = dragParent.isEditable() 2454 && (isSubElement || !isDetailPage() || dragParent.isDetailView() || dragParent.isDetailOnly()); 2455 return isContainerEditable; 2456 } 2457 2458 /** 2459 * Returns the flag indicating that a content element is being edited.<p> 2460 * 2461 * @return the flag indicating that a content element is being edited 2462 */ 2463 public boolean isContentEditing() { 2464 2465 return m_isContentEditing; 2466 } 2467 2468 /** 2469 * Returns if this page displays a detail view.<p> 2470 * 2471 * @return <code>true</code> if this page displays a detail view 2472 */ 2473 public boolean isDetailPage() { 2474 2475 return m_data.getDetailId() != null; 2476 } 2477 2478 /** 2479 * Checks if the page editing features should be disabled.<p> 2480 * 2481 * @return true if the page editing features should be disabled 2482 */ 2483 public boolean isEditingDisabled() { 2484 2485 return (m_data == null) 2486 || CmsStringUtil.isNotEmptyOrWhitespaceOnly(m_data.getNoEditReason()) 2487 || (m_lockStatus == LockStatus.failed); 2488 } 2489 2490 /** 2491 * Returns if a group-container is currently being edited.<p> 2492 * 2493 * @return <code>true</code> if a group-container is being edited 2494 */ 2495 public boolean isGroupcontainerEditing() { 2496 2497 return m_groupEditor != null; 2498 } 2499 2500 /** 2501 * Checks whether the given element should be inline editable.<p> 2502 * 2503 * @param element the element 2504 * @param dragParent the element parent 2505 * 2506 * @return <code>true</code> if the element should be inline editable 2507 */ 2508 public boolean isInlineEditable(CmsContainerPageElementPanel element, I_CmsDropContainer dragParent) { 2509 2510 CmsUUID elemView = element.getElementView(); 2511 return !getData().isUseClassicEditor() 2512 && CmsStringUtil.isEmptyOrWhitespaceOnly(element.getNoEditReason()) 2513 && hasActiveSelection() 2514 && matchRootView(elemView) 2515 && isContainerEditable(dragParent) 2516 && matchesCurrentEditLevel(dragParent) 2517 && (getData().isModelGroup() || !element.hasModelGroupParent()) 2518 && (!(dragParent instanceof CmsGroupContainerElementPanel) || isGroupcontainerEditing()); 2519 } 2520 2521 /** 2522 * Method to leave the page without saving.<p> 2523 * 2524 * @param targetUri the new URI to call 2525 */ 2526 public void leaveUnsaved(String targetUri) { 2527 2528 setPageChanged(false, true); 2529 Window.Location.assign(targetUri); 2530 } 2531 2532 /** 2533 * Loads the context menu entries.<p> 2534 * 2535 * @param structureId the structure id of the resource to get the context menu entries for 2536 * @param context the ade context (sitemap or containerpae) 2537 */ 2538 public void loadContextMenu(final CmsUUID structureId, final AdeContext context) { 2539 2540 /** The RPC menu action for the container page dialog. */ 2541 CmsRpcAction<List<CmsContextMenuEntryBean>> menuAction = new CmsRpcAction<List<CmsContextMenuEntryBean>>() { 2542 2543 /** 2544 * @see org.opencms.gwt.client.rpc.CmsRpcAction#execute() 2545 */ 2546 @Override 2547 public void execute() { 2548 2549 Map<String, String> params = new HashMap<>(); 2550 if (getData().getDetailId() != null) { 2551 params.put(CmsGwtConstants.PARAM_ADE_DETAIL_ID, "" + getData().getDetailId()); 2552 } 2553 getCoreService().getContextMenuEntries(structureId, context, params, this); 2554 } 2555 2556 /** 2557 * @see org.opencms.gwt.client.rpc.CmsRpcAction#onResponse(java.lang.Object) 2558 */ 2559 @Override 2560 public void onResponse(List<CmsContextMenuEntryBean> menuBeans) { 2561 2562 m_handler.insertContextMenu(menuBeans, structureId); 2563 } 2564 }; 2565 menuAction.execute(); 2566 2567 } 2568 2569 /** 2570 * Loads the favorite list and adds the elements to the favorite list widget of the tool-bar menu.<p> 2571 * 2572 * @param callback the call-back to execute with the result data 2573 */ 2574 public void loadFavorites(final I_CmsSimpleCallback<List<CmsContainerElementData>> callback) { 2575 2576 CmsRpcAction<List<CmsContainerElementData>> action = new CmsRpcAction<List<CmsContainerElementData>>() { 2577 2578 /** 2579 * @see org.opencms.gwt.client.rpc.CmsRpcAction#execute() 2580 */ 2581 @Override 2582 public void execute() { 2583 2584 start(200, true); 2585 getContainerpageService().getFavoriteList( 2586 CmsCoreProvider.get().getStructureId(), 2587 getData().getDetailId(), 2588 getPageState(), 2589 getLocale(), 2590 this); 2591 } 2592 2593 /** 2594 * @see org.opencms.gwt.client.rpc.CmsRpcAction#onResponse(java.lang.Object) 2595 */ 2596 @Override 2597 protected void onResponse(List<CmsContainerElementData> result) { 2598 2599 stop(false); 2600 addElements(result); 2601 callback.execute(result); 2602 } 2603 }; 2604 action.execute(); 2605 } 2606 2607 /** 2608 * Loads the recent list and adds the elements to the recent list widget of the tool-bar menu.<p> 2609 * 2610 * @param callback the call-back to execute with the result data 2611 */ 2612 public void loadRecent(final I_CmsSimpleCallback<List<CmsContainerElementData>> callback) { 2613 2614 CmsRpcAction<List<CmsContainerElementData>> action = new CmsRpcAction<List<CmsContainerElementData>>() { 2615 2616 /** 2617 * @see org.opencms.gwt.client.rpc.CmsRpcAction#execute() 2618 */ 2619 @Override 2620 public void execute() { 2621 2622 start(200, true); 2623 getContainerpageService().getRecentList( 2624 CmsCoreProvider.get().getStructureId(), 2625 getData().getDetailId(), 2626 getPageState(), 2627 getLocale(), 2628 this); 2629 } 2630 2631 /** 2632 * @see org.opencms.gwt.client.rpc.CmsRpcAction#onResponse(java.lang.Object) 2633 */ 2634 @Override 2635 protected void onResponse(List<CmsContainerElementData> result) { 2636 2637 stop(false); 2638 addElements(result); 2639 callback.execute(result); 2640 } 2641 }; 2642 action.execute(); 2643 } 2644 2645 /** 2646 * Locks the container-page.<p> 2647 * 2648 * @param callback the callback to execute 2649 */ 2650 public void lockContainerpage(final I_CmsSimpleCallback<Boolean> callback) { 2651 2652 if (m_lockStatus == LockStatus.locked) { 2653 callback.execute(Boolean.TRUE); 2654 } else if (m_lockStatus == LockStatus.failed) { 2655 callback.execute(Boolean.FALSE); 2656 } else { 2657 I_CmsSimpleCallback<String> call = new I_CmsSimpleCallback<String>() { 2658 2659 public void execute(String lockError) { 2660 2661 if (lockError == null) { 2662 onLockSuccess(); 2663 callback.execute(Boolean.TRUE); 2664 } else { 2665 onLockFail(lockError); 2666 callback.execute(Boolean.FALSE); 2667 } 2668 } 2669 }; 2670 2671 if (getData().getDetailContainerPage() != null) { 2672 CmsCoreProvider.get().lockOrReturnError(getData().getDetailContainerPage(), getLoadTime(), call); 2673 } else { 2674 CmsCoreProvider.get().lockOrReturnError(CmsCoreProvider.get().getStructureId(), getLoadTime(), call); 2675 } 2676 } 2677 } 2678 2679 /** 2680 * Returns true if the view with the given view id and the current view have the same root view.<p> 2681 * 2682 * @param viewIdFromElement the id of a view 2683 * @return true if the root view of the id matches the root view of the current view 2684 */ 2685 public boolean matchRootView(CmsUUID viewIdFromElement) { 2686 2687 if (viewIdFromElement == null) { 2688 viewIdFromElement = CmsUUID.getNullUUID(); 2689 } 2690 CmsElementViewInfo viewFromElement = getView(viewIdFromElement.toString()); 2691 return (viewFromElement != null) && viewFromElement.getRootViewId().equals(m_elementView.getRootViewId()); 2692 } 2693 2694 /** 2695 * This method should be called when locking the page has failed.<p> 2696 * 2697 * @param lockError the locking information 2698 */ 2699 public void onLockFail(String lockError) { 2700 2701 m_lockStatus = LockStatus.failed; 2702 m_handler.onLockFail(lockError); 2703 } 2704 2705 /** 2706 * This method should be called when locking the page has succeeded.<p> 2707 * 2708 */ 2709 public void onLockSuccess() { 2710 2711 assert m_lockStatus == LockStatus.unknown; 2712 m_lockStatus = LockStatus.locked; 2713 } 2714 2715 /** 2716 * Handler which is executed when the window closes.<p> 2717 */ 2718 public void onWindowClose() { 2719 2720 // causes synchronous RPC call 2721 unlockContainerpage(); 2722 } 2723 2724 /** 2725 * Calls the edit handler to prepare the given content element for editing.<p> 2726 * 2727 * @param clientId the element id 2728 * @param editOption the selected edit option 2729 * @param callback the callback to execute 2730 */ 2731 public void prepareForEdit( 2732 final String clientId, 2733 final String editOption, 2734 final I_CmsSimpleCallback<CmsUUID> callback) { 2735 2736 CmsRpcAction<CmsUUID> action = new CmsRpcAction<CmsUUID>() { 2737 2738 @Override 2739 public void execute() { 2740 2741 getContainerpageService().prepareForEdit( 2742 clientId, 2743 editOption, 2744 getData().getRpcContext().getPageStructureId(), 2745 getData().getRequestParams(), 2746 this); 2747 } 2748 2749 @Override 2750 protected void onResponse(CmsUUID result) { 2751 2752 callback.execute(result); 2753 } 2754 }; 2755 action.execute(); 2756 } 2757 2758 /** 2759 * Reinitializes the buttons in the container element menus.<p> 2760 */ 2761 public void reinitializeButtons() { 2762 2763 if (isGroupcontainerEditing()) { 2764 m_groupEditor.reinitializeButtons(); 2765 } else { 2766 List<CmsContainerPageElementPanel> elemWidgets = getAllContainerPageElements(true); 2767 2768 for (CmsContainerPageElementPanel elemWidget : elemWidgets) { 2769 if (requiresOptionBar(elemWidget, elemWidget.getParentTarget())) { 2770 getContainerpageUtil().addOptionBar(elemWidget); 2771 } else { 2772 // otherwise remove any present option bar 2773 elemWidget.setElementOptionBar(null); 2774 } 2775 elemWidget.showEditableListButtons(); 2776 } 2777 } 2778 } 2779 2780 /** 2781 * Re-initializes the inline editing.<p> 2782 */ 2783 public void reInitInlineEditing() { 2784 2785 removeEditButtonsPositionTimer(); 2786 if ((m_targetContainers == null) || getData().isUseClassicEditor()) { 2787 // if the target containers are not initialized yet or classic editor is set, don't do anything 2788 return; 2789 } 2790 if (isGroupcontainerEditing()) { 2791 for (Widget element : m_groupEditor.getGroupContainerWidget()) { 2792 if (((element instanceof CmsContainerPageElementPanel) 2793 && isInlineEditable( 2794 (CmsContainerPageElementPanel)element, 2795 m_groupEditor.getGroupContainerWidget()))) { 2796 ((CmsContainerPageElementPanel)element).initInlineEditor(this); 2797 } 2798 } 2799 } else { 2800 for (CmsContainerPageContainer container : m_targetContainers.values()) { 2801 // first remove inline editors 2802 for (Widget element : container) { 2803 if ((element instanceof CmsContainerPageElementPanel)) { 2804 ((CmsContainerPageElementPanel)element).removeInlineEditor(); 2805 } 2806 } 2807 2808 // add inline editors only on suitable elements 2809 if (isContainerEditable(container) && matchesCurrentEditLevel(container)) { 2810 for (Widget element : container) { 2811 if ((element instanceof CmsContainerPageElementPanel) 2812 && isInlineEditable((CmsContainerPageElementPanel)element, container)) { 2813 ((CmsContainerPageElementPanel)element).initInlineEditor(this); 2814 } 2815 } 2816 } 2817 } 2818 } 2819 } 2820 2821 /** 2822 * Reloads the content for the given elements and related elements. 2823 * 2824 * @param ids the element ids 2825 * @param callback the callback to execute after the reload 2826 */ 2827 public void reloadElements(Collection<String> ids, I_ReloadHandler callback) { 2828 2829 Set<String> related = new HashSet<String>(); 2830 for (String id : ids) { 2831 related.addAll(getRelatedElementIds(id)); 2832 } 2833 if (!related.isEmpty()) { 2834 ReloadElementAction action = new ReloadElementAction(related, callback); 2835 action.execute(); 2836 } 2837 } 2838 2839 /** 2840 * Reloads the content for the given elements and related elements. 2841 * 2842 * @param ids the element ids 2843 * @param callback the callback to execute after the reload 2844 */ 2845 public void reloadElements(Collection<String> ids, Runnable callback) { 2846 2847 reloadElements(ids, new I_ReloadHandler() { 2848 2849 public void finish() { 2850 2851 if (callback != null) { 2852 callback.run(); 2853 } 2854 } 2855 2856 public void onReload(CmsContainerPageElementPanel a, CmsContainerPageElementPanel b) { 2857 2858 // ignore 2859 } 2860 }); 2861 2862 } 2863 2864 /** 2865 * Reloads the content for the given element and all related elements.<p> 2866 * 2867 * Call this if the element content has changed.<p> 2868 * 2869 * @param ids the element ids 2870 * @param callback the callback to execute after the reload 2871 */ 2872 public void reloadElements(String[] ids, I_ReloadHandler callback) { 2873 2874 Set<String> related = new HashSet<String>(); 2875 for (int i = 0; i < ids.length; i++) { 2876 related.addAll(getRelatedElementIds(ids[i])); 2877 } 2878 if (!related.isEmpty()) { 2879 ReloadElementAction action = new ReloadElementAction(related, callback); 2880 action.execute(); 2881 } else { 2882 callback.finish(); 2883 } 2884 } 2885 2886 /** 2887 * Reloads the content for the given element and all related elements.<p> 2888 * 2889 * Call this if the element content has changed.<p> 2890 * 2891 * @param ids the element ids 2892 * @param callback the callback to execute after the reload 2893 */ 2894 public void reloadElements(String[] ids, Runnable callback) { 2895 2896 reloadElements(ids, new I_ReloadHandler() { 2897 2898 @Override 2899 public void finish() { 2900 2901 if (callback != null) { 2902 callback.run(); 2903 } 2904 } 2905 2906 @Override 2907 public void onReload(CmsContainerPageElementPanel oldElement, CmsContainerPageElementPanel newElement) { 2908 2909 } 2910 }); 2911 2912 } 2913 2914 /** 2915 * Reloads a container page element with a new set of settings.<p> 2916 * 2917 * @param elementWidget the widget of the container page element which should be reloaded 2918 * @param clientId the id of the container page element which should be reloaded 2919 * @param settings the new set of settings 2920 * @param reloadHandler the handler to call with the reloaded element 2921 */ 2922 public void reloadElementWithSettings( 2923 final org.opencms.ade.containerpage.client.ui.CmsContainerPageElementPanel elementWidget, 2924 final String clientId, 2925 final Map<String, String> settings, 2926 final I_ReloadHandler reloadHandler) { 2927 2928 final I_CmsSimpleCallback<CmsContainerElementData> callback = new I_CmsSimpleCallback<CmsContainerElementData>() { 2929 2930 public void execute(CmsContainerElementData newElement) { 2931 2932 try { 2933 final CmsContainerPageElementPanel replacement = replaceContainerElement(elementWidget, newElement); 2934 resetEditButtons(); 2935 addToRecentList(newElement.getClientId(), null); 2936 reloadHandler.onReload(elementWidget, replacement); 2937 reloadHandler.finish(); 2938 } catch (Exception e) { 2939 // should never happen 2940 CmsDebugLog.getInstance().printLine(e.getLocalizedMessage()); 2941 } 2942 } 2943 }; 2944 2945 if (!isGroupcontainerEditing()) { 2946 2947 lockContainerpage(new I_CmsSimpleCallback<Boolean>() { 2948 2949 public void execute(Boolean arg) { 2950 2951 if (arg.booleanValue()) { 2952 CmsRpcAction<CmsContainerElementData> action = new CmsRpcAction<CmsContainerElementData>() { 2953 2954 @Override 2955 public void execute() { 2956 2957 start(500, true); 2958 getContainerpageService().saveElementSettings( 2959 getData().getRpcContext(), 2960 getData().getDetailId(), 2961 getRequestParams(), 2962 clientId, 2963 settings, 2964 getPageState(), 2965 getLocale(), 2966 this); 2967 } 2968 2969 @Override 2970 protected void onResponse(CmsContainerElementData result) { 2971 2972 stop(false); 2973 CmsContainerpageController.get().fireEvent( 2974 new CmsContainerpageEvent(EventType.pageSaved)); 2975 setPageChanged(false, false); 2976 if (result != null) { 2977 // cache the loaded element 2978 m_elements.put(result.getClientId(), result); 2979 setLoadTime(Long.valueOf(result.getLoadTime())); 2980 } 2981 callback.execute(result); 2982 } 2983 }; 2984 action.execute(); 2985 } 2986 } 2987 }); 2988 2989 } else { 2990 getElementWithSettings(clientId, settings, callback); 2991 } 2992 } 2993 2994 /** 2995 * Reloads the page.<p> 2996 */ 2997 public void reloadPage() { 2998 2999 Timer timer = new Timer() { 3000 3001 @Override 3002 public void run() { 3003 3004 Window.Location.reload(); 3005 } 3006 }; 3007 3008 timer.schedule(150); 3009 3010 } 3011 3012 /** 3013 * Removes the given container element from its parent container.<p> 3014 * 3015 * @param dragElement the element to remove 3016 */ 3017 public void removeElement(org.opencms.ade.containerpage.client.ui.CmsContainerPageElementPanel dragElement) { 3018 3019 ElementRemoveMode removeMode = isConfirmRemove() 3020 ? ElementRemoveMode.confirmRemove 3021 : ElementRemoveMode.saveAndCheckReferences; 3022 removeElement(dragElement, removeMode); 3023 } 3024 3025 /** 3026 * Removes the given container element from its parent container.<p> 3027 * 3028 * @param dragElement the element to remove 3029 * @param removeMode the remove mode 3030 */ 3031 public void removeElement( 3032 org.opencms.ade.containerpage.client.ui.CmsContainerPageElementPanel dragElement, 3033 ElementRemoveMode removeMode) { 3034 3035 if (isGroupcontainerEditing()) { 3036 dragElement.removeFromParent(); 3037 if (!getGroupcontainer().iterator().hasNext()) { 3038 // group-container is empty, mark it 3039 getGroupcontainer().addStyleName(I_CmsLayoutBundle.INSTANCE.containerpageCss().emptyGroupContainer()); 3040 } 3041 getGroupcontainer().refreshHighlighting(); 3042 } else { 3043 final String id = dragElement.getId(); 3044 if (id != null) { 3045 addToRecentList(id, null); 3046 } 3047 3048 I_CmsDropContainer container = dragElement.getParentTarget(); 3049 switch (removeMode) { 3050 case saveAndCheckReferences: 3051 I_EventDetails details = createEventDetails(dragElement); 3052 dragElement.removeFromParent(); 3053 if (container instanceof CmsContainerPageContainer) { 3054 ((CmsContainerPageContainer)container).checkEmptyContainers(); 3055 } 3056 cleanUpContainers(); 3057 Runnable checkReferencesAction = new Runnable() { 3058 3059 public void run() { 3060 3061 checkReferencesToRemovedElement(id); 3062 } 3063 }; 3064 sendElementDeleted(details); 3065 setPageChanged(checkReferencesAction); 3066 break; 3067 case confirmRemove: 3068 handleConfirmRemove(dragElement); 3069 break; 3070 case silent: 3071 default: 3072 I_EventDetails details2 = createEventDetails(dragElement); 3073 dragElement.removeFromParent(); 3074 if (container instanceof CmsContainerPageContainer) { 3075 ((CmsContainerPageContainer)container).checkEmptyContainers(); 3076 } 3077 cleanUpContainers(); 3078 sendElementDeleted(details2); 3079 setPageChanged(); 3080 break; 3081 } 3082 } 3083 } 3084 3085 /** 3086 * Replaces the given drag-element with the given container element.<p> 3087 * 3088 * @param containerElement the container element to replace 3089 * @param elementData the new element data 3090 * 3091 * @return the container element which replaced the old one 3092 * 3093 * @throws Exception if something goes wrong 3094 */ 3095 public CmsContainerPageElementPanel replaceContainerElement( 3096 CmsContainerPageElementPanel containerElement, 3097 CmsContainerElementData elementData) 3098 throws Exception { 3099 3100 I_CmsDropContainer parentContainer = containerElement.getParentTarget(); 3101 String containerId = parentContainer.getContainerId(); 3102 CmsContainerPageElementPanel replacer = null; 3103 String elementContent = elementData.getContents().get(containerId); 3104 if ((elementContent != null) && (elementContent.trim().length() > 0)) { 3105 replacer = getContainerpageUtil().createElement(elementData, parentContainer, false); 3106 3107 if (containerElement.isNew()) { 3108 // if replacing element data has the same structure id, keep the 'new' state by setting the new type property 3109 // this should only be the case when editing settings of a new element that has not been created in the VFS yet 3110 String id = getServerId(containerElement.getId()); 3111 if (elementData.getClientId().startsWith(id)) { 3112 replacer.setNewType(containerElement.getNewType()); 3113 } 3114 } 3115 replacer.setCreateNew(containerElement.isCreateNew()); 3116 // replacer.setModelGroup(containerElement.isModelGroup()); 3117 if (isGroupcontainerEditing() && (containerElement.getInheritanceInfo() != null)) { 3118 // in case of inheritance container editing, keep the inheritance info 3119 replacer.setInheritanceInfo(containerElement.getInheritanceInfo()); 3120 // set the proper element options 3121 CmsInheritanceContainerEditor.getInstance().setOptionBar(replacer); 3122 } 3123 parentContainer.insert(replacer, parentContainer.getWidgetIndex(containerElement)); 3124 containerElement.removeFromParent(); 3125 initializeSubContainers(replacer); 3126 } 3127 cleanUpContainers(); 3128 return replacer; 3129 } 3130 3131 /** 3132 * Replaces the given element with another content while keeping it's settings.<p> 3133 * 3134 * @param elementWidget the element to replace 3135 * @param elementId the id of the replacing content 3136 * @param callback the callback to execute after the element is replaced 3137 */ 3138 public void replaceElement( 3139 final CmsContainerPageElementPanel elementWidget, 3140 final String elementId, 3141 I_ReloadHandler handler) { 3142 3143 final CmsRpcAction<CmsContainerElementData> action = new CmsRpcAction<CmsContainerElementData>() { 3144 3145 @Override 3146 public void execute() { 3147 3148 start(500, true); 3149 getContainerpageService().replaceElement( 3150 getData().getRpcContext(), 3151 getData().getDetailId(), 3152 getRequestParams(), 3153 elementWidget.getId(), 3154 elementId, 3155 getPageState(), 3156 getLocale(), 3157 this); 3158 } 3159 3160 @Override 3161 protected void onResponse(CmsContainerElementData result) { 3162 3163 stop(false); 3164 3165 if (result != null) { 3166 // cache the loaded element 3167 m_elements.put(result.getClientId(), result); 3168 try { 3169 CmsContainerPageElementPanel replacer = replaceContainerElement(elementWidget, result); 3170 resetEditButtons(); 3171 addToRecentList(result.getClientId(), null); 3172 setPageChanged(new Runnable() { 3173 3174 public void run() { 3175 3176 if (handler != null) { 3177 handler.onReload(elementWidget, replacer); 3178 handler.finish(); 3179 } 3180 } 3181 }); 3182 } catch (Exception e) { 3183 // should never happen 3184 CmsDebugLog.getInstance().printLine(e.getLocalizedMessage()); 3185 } 3186 } 3187 } 3188 }; 3189 3190 if (!isGroupcontainerEditing()) { 3191 3192 lockContainerpage(new I_CmsSimpleCallback<Boolean>() { 3193 3194 public void execute(Boolean arg) { 3195 3196 if (arg.booleanValue()) { 3197 action.execute(); 3198 } 3199 } 3200 }); 3201 3202 } else { 3203 action.execute(); 3204 } 3205 } 3206 3207 /** 3208 * Replaces the given element with another content while keeping it's settings.<p> 3209 * 3210 * @param elementWidget the element to replace 3211 * @param elementId the id of the replacing content 3212 * @param callback the callback to execute after the element is replaced 3213 */ 3214 public void replaceElement( 3215 final CmsContainerPageElementPanel elementWidget, 3216 final String elementId, 3217 Runnable callback) { 3218 3219 replaceElement(elementWidget, elementId, new I_ReloadHandler() { 3220 3221 @Override 3222 public void finish() { 3223 3224 if (callback != null) { 3225 callback.run(); 3226 } 3227 } 3228 3229 @Override 3230 public void onReload(CmsContainerPageElementPanel oldElement, CmsContainerPageElementPanel newElement) { 3231 3232 // do nothing 3233 } 3234 }); 3235 3236 } 3237 3238 /** 3239 * Checks whether the given element should display the option bar.<p> 3240 * 3241 * @param element the element 3242 * @param dragParent the element parent 3243 * 3244 * @return <code>true</code> if the given element should display the option bar 3245 */ 3246 public boolean requiresOptionBar(CmsContainerPageElementPanel element, I_CmsDropContainer dragParent) { 3247 3248 return element.hasViewPermission() 3249 && (!element.hasModelGroupParent() || getData().isModelGroup()) 3250 && (matchRootView(element.getElementView()) 3251 || isGroupcontainerEditing() 3252 || shouldShowModelgroupOptionBar(element)) 3253 && isContainerEditable(dragParent) 3254 && matchesCurrentEditLevel(dragParent); 3255 } 3256 3257 /** 3258 * Resets all edit buttons an there positions.<p> 3259 */ 3260 public void resetEditButtons() { 3261 3262 removeEditButtonsPositionTimer(); 3263 m_editButtonsPositionTimer = new Timer() { 3264 3265 /** Timer run counter. */ 3266 private int m_timerRuns; 3267 3268 @Override 3269 public void run() { 3270 3271 for (org.opencms.ade.containerpage.client.ui.CmsContainerPageContainer container : m_targetContainers.values()) { 3272 container.showEditableListButtons(); 3273 container.updateOptionBars(); 3274 } 3275 if (m_timerRuns > 3) { 3276 cancel(); 3277 } 3278 m_timerRuns++; 3279 } 3280 }; 3281 m_editButtonsPositionTimer.scheduleRepeating(100); 3282 } 3283 3284 /** 3285 * Resets the container-page.<p> 3286 */ 3287 public void resetPage() { 3288 3289 setPageChanged(false, true); 3290 Window.Location.reload(); 3291 } 3292 3293 /** 3294 * Method to save and leave the page.<p> 3295 * 3296 * @param leaveCommand the command to execute to leave the page 3297 */ 3298 public void saveAndLeave(final Command leaveCommand) { 3299 3300 if (hasPageChanged()) { 3301 CmsRpcAction<Long> action = new CmsRpcAction<Long>() { 3302 3303 /** 3304 * @see org.opencms.gwt.client.rpc.CmsRpcAction#execute() 3305 */ 3306 @Override 3307 public void execute() { 3308 3309 if (getData().getDetailContainerPage() != null) { 3310 getContainerpageService().saveDetailContainers( 3311 getData().getDetailId(), 3312 getData().getDetailContainerPage(), 3313 getPageContent(), 3314 this); 3315 } else { 3316 getContainerpageService().saveContainerpage( 3317 CmsCoreProvider.get().getStructureId(), 3318 getPageContent(), 3319 this); 3320 } 3321 } 3322 3323 /** 3324 * @see org.opencms.gwt.client.rpc.CmsRpcAction#onResponse(java.lang.Object) 3325 */ 3326 @Override 3327 protected void onResponse(Long result) { 3328 3329 setLoadTime(result); 3330 CmsNotification.get().send(Type.NORMAL, Messages.get().key(Messages.GUI_NOTIFICATION_PAGE_SAVED_0)); 3331 CmsContainerpageController.get().fireEvent(new CmsContainerpageEvent(EventType.pageSaved)); 3332 setPageChanged(false, true); 3333 leaveCommand.execute(); 3334 } 3335 }; 3336 action.execute(); 3337 } 3338 } 3339 3340 /** 3341 * Method to save and leave the page.<p> 3342 * 3343 * @param targetUri the new URI to call 3344 */ 3345 public void saveAndLeave(final String targetUri) { 3346 3347 if (hasPageChanged()) { 3348 CmsRpcAction<Long> action = new CmsRpcAction<Long>() { 3349 3350 /** 3351 * @see org.opencms.gwt.client.rpc.CmsRpcAction#execute() 3352 */ 3353 @Override 3354 public void execute() { 3355 3356 if (getData().getDetailContainerPage() != null) { 3357 getContainerpageService().saveDetailContainers( 3358 getData().getDetailId(), 3359 getData().getDetailContainerPage(), 3360 getPageContent(), 3361 this); 3362 } else { 3363 getContainerpageService().saveContainerpage( 3364 CmsCoreProvider.get().getStructureId(), 3365 getPageContent(), 3366 this); 3367 } 3368 } 3369 3370 /** 3371 * @see org.opencms.gwt.client.rpc.CmsRpcAction#onResponse(java.lang.Object) 3372 */ 3373 @Override 3374 protected void onResponse(Long result) { 3375 3376 setLoadTime(result); 3377 CmsNotification.get().send(Type.NORMAL, Messages.get().key(Messages.GUI_NOTIFICATION_PAGE_SAVED_0)); 3378 CmsContainerpageController.get().fireEvent(new CmsContainerpageEvent(EventType.pageSaved)); 3379 setPageChanged(false, true); 3380 Window.Location.assign(targetUri); 3381 } 3382 }; 3383 action.execute(); 3384 } 3385 } 3386 3387 /** 3388 * Saves the clipboard tab index selected by the user.<p> 3389 * 3390 * @param tabIndex the tab index 3391 */ 3392 public void saveClipboardTab(final int tabIndex) { 3393 3394 CmsRpcAction<Void> action = new CmsRpcAction<Void>() { 3395 3396 @Override 3397 public void execute() { 3398 3399 start(1, false); 3400 getContainerpageService().saveClipboardTab(tabIndex, this); 3401 } 3402 3403 @Override 3404 protected void onResponse(Void result) { 3405 3406 stop(false); 3407 } 3408 }; 3409 action.execute(); 3410 } 3411 3412 /** 3413 * Saves the current state of the container-page.<p> 3414 * 3415 * @param afterSaveActions the actions to execute after saving 3416 */ 3417 public void saveContainerpage(final Runnable... afterSaveActions) { 3418 3419 if (hasPageChanged()) { 3420 final CmsRpcAction<Long> action = new CmsRpcAction<Long>() { 3421 3422 /** 3423 * @see org.opencms.gwt.client.rpc.CmsRpcAction#execute() 3424 */ 3425 @Override 3426 public void execute() { 3427 3428 start(500, true); 3429 if (getData().getDetailContainerPage() != null) { 3430 getContainerpageService().saveDetailContainers( 3431 getData().getDetailId(), 3432 getData().getDetailContainerPage(), 3433 getPageContent(), 3434 this); 3435 } else { 3436 getContainerpageService().saveContainerpage( 3437 CmsCoreProvider.get().getStructureId(), 3438 getPageContent(), 3439 this); 3440 } 3441 } 3442 3443 /** 3444 * @see org.opencms.gwt.client.rpc.CmsRpcAction#onResponse(java.lang.Object) 3445 */ 3446 @Override 3447 protected void onResponse(Long result) { 3448 3449 setLoadTime(result); 3450 stop(false); 3451 setPageChanged(false, false); 3452 CmsContainerpageController.get().fireEvent(new CmsContainerpageEvent(EventType.pageSaved)); 3453 for (Runnable afterSaveAction : afterSaveActions) { 3454 afterSaveAction.run(); 3455 } 3456 } 3457 }; 3458 if (getData().getDetailContainerPage() != null) { 3459 action.execute(); 3460 } else { 3461 lockContainerpage(new I_CmsSimpleCallback<Boolean>() { 3462 3463 public void execute(Boolean arg) { 3464 3465 if (arg.booleanValue()) { 3466 action.execute(); 3467 } 3468 } 3469 }); 3470 } 3471 } 3472 } 3473 3474 /** 3475 * Saves the favorite list.<p> 3476 * 3477 * @param clientIds the client id's of the list's elements 3478 */ 3479 public void saveFavoriteList(final List<String> clientIds) { 3480 3481 CmsRpcAction<Void> action = new CmsRpcAction<Void>() { 3482 3483 /** 3484 * @see org.opencms.gwt.client.rpc.CmsRpcAction#execute() 3485 */ 3486 @Override 3487 public void execute() { 3488 3489 getContainerpageService().saveFavoriteList(clientIds, CmsCoreProvider.get().getUri(), this); 3490 } 3491 3492 /** 3493 * @see org.opencms.gwt.client.rpc.CmsRpcAction#onResponse(java.lang.Object) 3494 */ 3495 @Override 3496 protected void onResponse(Void result) { 3497 3498 CmsNotification.get().send( 3499 Type.NORMAL, 3500 Messages.get().key(Messages.GUI_NOTIFICATION_FAVORITES_SAVED_0)); 3501 } 3502 }; 3503 action.execute(); 3504 } 3505 3506 /** 3507 * Saves the group-container.<p> 3508 * 3509 * @param groupContainer the group-container data to save 3510 * @param groupContainerElement the group-container widget 3511 */ 3512 public void saveGroupcontainer( 3513 final CmsGroupContainer groupContainer, 3514 final CmsGroupContainerElementPanel groupContainerElement) { 3515 3516 if (getGroupcontainer() != null) { 3517 CmsRpcAction<CmsGroupContainerSaveResult> action = new CmsRpcAction<CmsGroupContainerSaveResult>() { 3518 3519 /** 3520 * @see org.opencms.gwt.client.rpc.CmsRpcAction#execute() 3521 */ 3522 @Override 3523 public void execute() { 3524 3525 start(0, true); 3526 getContainerpageService().saveGroupContainer( 3527 getData().getRpcContext(), 3528 getData().getDetailId(), 3529 getRequestParams(), 3530 groupContainer, 3531 getPageState(), 3532 getLocale(), 3533 this); 3534 } 3535 3536 /** 3537 * @see org.opencms.gwt.client.rpc.CmsRpcAction#onResponse(java.lang.Object) 3538 */ 3539 @Override 3540 protected void onResponse(CmsGroupContainerSaveResult saveResult) { 3541 3542 stop(false); 3543 Map<String, CmsContainerElementData> elementData = saveResult.getElementData(); 3544 m_elements.putAll(elementData); 3545 try { 3546 replaceContainerElement(groupContainerElement, elementData.get(groupContainerElement.getId())); 3547 } catch (Exception e) { 3548 CmsDebugLog.getInstance().printLine("Error replacing group container element"); 3549 } 3550 addToRecentList(groupContainerElement.getId(), null); 3551 CmsNotification.get().send( 3552 Type.NORMAL, 3553 Messages.get().key(Messages.GUI_NOTIFICATION_GROUP_CONTAINER_SAVED_0)); 3554 List<CmsRemovedElementStatus> removedElements = saveResult.getRemovedElements(); 3555 for (CmsRemovedElementStatus removedElement : removedElements) { 3556 askWhetherRemovedElementShouldBeDeleted(removedElement); 3557 } 3558 3559 } 3560 }; 3561 action.execute(); 3562 3563 } 3564 } 3565 3566 /** 3567 * Saves the inheritance container.<p> 3568 * 3569 * @param inheritanceContainer the inheritance container data to save 3570 * @param groupContainerElement the group container widget 3571 */ 3572 public void saveInheritContainer( 3573 final CmsInheritanceContainer inheritanceContainer, 3574 final CmsGroupContainerElementPanel groupContainerElement) { 3575 3576 if (getGroupcontainer() != null) { 3577 CmsRpcAction<Map<String, CmsContainerElementData>> action = new CmsRpcAction<Map<String, CmsContainerElementData>>() { 3578 3579 /** 3580 * @see org.opencms.gwt.client.rpc.CmsRpcAction#execute() 3581 */ 3582 @Override 3583 public void execute() { 3584 3585 start(0, true); 3586 getContainerpageService().saveInheritanceContainer( 3587 CmsCoreProvider.get().getStructureId(), 3588 getData().getDetailId(), 3589 inheritanceContainer, 3590 getPageState(), 3591 getLocale(), 3592 this); 3593 } 3594 3595 /** 3596 * @see org.opencms.gwt.client.rpc.CmsRpcAction#onResponse(java.lang.Object) 3597 */ 3598 @Override 3599 protected void onResponse(Map<String, CmsContainerElementData> result) { 3600 3601 stop(false); 3602 m_elements.putAll(result); 3603 try { 3604 replaceContainerElement(groupContainerElement, result.get(groupContainerElement.getId())); 3605 } catch (Exception e) { 3606 CmsDebugLog.getInstance().printLine("Error replacing group container element"); 3607 } 3608 addToRecentList(groupContainerElement.getId(), null); 3609 CmsNotification.get().send( 3610 Type.NORMAL, 3611 Messages.get().key(Messages.GUI_NOTIFICATION_INHERITANCE_CONTAINER_SAVED_0)); 3612 3613 } 3614 }; 3615 action.execute(); 3616 3617 } 3618 } 3619 3620 /** 3621 * Sends the ocDragFinished event. 3622 * 3623 * @param dragElement the drag element 3624 * @param target the target element 3625 * @param isNew true if the dragged element is new 3626 */ 3627 public void sendDragFinished(Element dragElement, Element target, boolean isNew) { 3628 3629 I_EventDetails details = Js.cast(new JsObject()); 3630 if (dragElement != null) { 3631 details.setElement(Js.cast(dragElement)); 3632 } 3633 if (target != null) { 3634 final Element parentContainerElement = CmsDomUtil.getAncestor(target, CmsContainerElement.CLASS_CONTAINER); 3635 details.setContainer(Js.cast(parentContainerElement)); 3636 } 3637 if (isNew) { 3638 details.setIsPlaceholder(isNew); 3639 } 3640 sendEvent(EVENT_DRAG_FINISHED, details); 3641 I_EventDetails details2 = copyEventDetails(details); 3642 details2.setType("finish"); 3643 sendEvent(EVENT_DRAG, details2); 3644 } 3645 3646 /** 3647 * Sends the ocDragStarted event. 3648 * 3649 * @param isNew true if the user started dragging a new element 3650 */ 3651 public void sendDragStarted(boolean isNew) { 3652 3653 I_EventDetails details = Js.cast(new JsObject()); 3654 details.setIsPlaceholder(isNew); 3655 sendEvent(EVENT_DRAG_STARTED, details); 3656 I_EventDetails details2 = copyEventDetails(details); 3657 details2.setType("start"); 3658 sendEvent(EVENT_DRAG, details2); 3659 } 3660 3661 /** 3662 * Sends the custom 'ocElementAdded' event for added container elements. 3663 * 3664 * @param widget the container element 3665 */ 3666 public void sendElementAdded(CmsContainerPageElementPanel widget) { 3667 3668 if (widget != null) { 3669 I_EventDetails details = createEventDetails(widget); 3670 sendEvent(EVENT_ELEMENT_ADDED, details); 3671 sendEvent(EVENT_ELEMENT_MODIFIED, addTypeToDetails(details, "add")); 3672 } 3673 } 3674 3675 /** 3676 * Sends the custom 'ocElementDeleted' event for a deleted container element. 3677 * 3678 * @param details the event details 3679 */ 3680 public void sendElementDeleted(I_EventDetails details) { 3681 3682 sendEvent(EVENT_ELEMENT_DELETED, details); 3683 sendEvent(EVENT_ELEMENT_MODIFIED, addTypeToDetails(details, "delete")); 3684 } 3685 3686 /** 3687 * Sends the custom 'ocElementEditedContent' event for an edited container element. 3688 * 3689 * @param widget the container element 3690 * @param replacedElement the element that was formerly on the page before the user edited it 3691 * @param wasNew true if the element was a new element before editing 3692 */ 3693 public void sendElementEditedContent( 3694 CmsContainerPageElementPanel widget, 3695 elemental2.dom.Element replacedElement, 3696 boolean wasNew) { 3697 3698 if (widget != null) { 3699 I_EventDetails details = createEventDetails(widget); 3700 details.setReplacedElement(replacedElement); 3701 details.setIsPlaceholder(wasNew); 3702 sendEvent(EVENT_ELEMENT_EDITED_CONTENT, details); 3703 sendEvent(EVENT_ELEMENT_EDITED, details); 3704 sendEvent(EVENT_ELEMENT_MODIFIED, addTypeToDetails(details, "editContent")); 3705 } 3706 } 3707 3708 /** 3709 * Sends the custom 'ocElementEditedSettings' event for an edited container element. 3710 * 3711 * @param widget the container element 3712 * @param replacedWidget the former container element that has now been replaced with 'widget' 3713 */ 3714 public void sendElementEditedSettings( 3715 CmsContainerPageElementPanel widget, 3716 CmsContainerPageElementPanel replacedWidget) { 3717 3718 if (widget != null) { 3719 I_EventDetails details = createEventDetails(widget); 3720 details.setReplacedElement(Js.cast(replacedWidget.getElement())); 3721 sendEvent(EVENT_ELEMENT_EDITED_SETTINGS, details); 3722 sendEvent(EVENT_ELEMENT_EDITED, details); 3723 sendEvent(EVENT_ELEMENT_MODIFIED, addTypeToDetails(details, "editSettings")); 3724 } 3725 } 3726 3727 /** 3728 * Sends the custom 'ocElementMoved' event for a moved container element. 3729 * 3730 * @param widget the container element 3731 */ 3732 public void sendElementMoved(CmsContainerPageElementPanel widget) { 3733 3734 if (widget != null) { 3735 I_EventDetails details = createEventDetails(widget); 3736 sendEvent(EVENT_ELEMENT_MOVED, details); 3737 sendEvent(EVENT_ELEMENT_MODIFIED, addTypeToDetails(details, "move")); 3738 } 3739 } 3740 3741 /** 3742 * Sets the flag indicating that a content element is being edited.<p> 3743 * 3744 * @param isContentEditing the flag indicating that a content element is being edited 3745 */ 3746 public void setContentEditing(boolean isContentEditing) { 3747 3748 if (m_groupEditor != null) { 3749 if (isContentEditing) { 3750 m_groupEditor.hidePopup(); 3751 } else { 3752 m_groupEditor.showPopup(); 3753 } 3754 } 3755 m_isContentEditing = isContentEditing; 3756 } 3757 3758 /** 3759 * Sets the DND controller.<p> 3760 * 3761 * @param dnd the new DND controller 3762 */ 3763 public void setDndController(CmsCompositeDNDController dnd) { 3764 3765 m_dndController = dnd; 3766 } 3767 3768 /** 3769 * Sets the element view.<p> 3770 * 3771 * @param viewInfo the element view 3772 * @param nextAction the action to execute after setting the view 3773 */ 3774 public void setElementView(CmsElementViewInfo viewInfo, Runnable nextAction) { 3775 3776 if (viewInfo != null) { 3777 m_elementView = viewInfo; 3778 3779 CmsRpcAction<Void> action = new CmsRpcAction<Void>() { 3780 3781 @SuppressWarnings("synthetic-access") 3782 @Override 3783 public void execute() { 3784 3785 getContainerpageService().setElementView(m_elementView.getElementViewId(), this); 3786 } 3787 3788 @Override 3789 protected void onResponse(Void result) { 3790 3791 // nothing to do 3792 } 3793 }; 3794 action.execute(); 3795 3796 m_currentEditLevel = -1; 3797 reinitializeButtons(); 3798 updateButtonsForCurrentView(); 3799 reInitInlineEditing(); 3800 updateGalleryData(true, nextAction); 3801 } 3802 } 3803 3804 /** 3805 * Sets the model group base element id.<p> 3806 * 3807 * @param modelGroupElementId the model group base element id 3808 */ 3809 public void setModelGroupElementId(String modelGroupElementId) { 3810 3811 m_modelGroupElementId = modelGroupElementId; 3812 } 3813 3814 /** 3815 * Marks the page as changed.<p> 3816 * 3817 * @param nextActions the actions to perform after the page has been marked as changed 3818 */ 3819 public void setPageChanged(Runnable... nextActions) { 3820 3821 if (!isGroupcontainerEditing()) { 3822 // the container page will be saved immediately 3823 m_pageChanged = true; 3824 saveContainerpage(nextActions); 3825 } 3826 } 3827 3828 /** 3829 * Sets an alternative event preview handler. 3830 * 3831 * @param handler the alternative preview handler to use 3832 */ 3833 public void setPreviewHandler(NativePreviewHandler handler) { 3834 m_previewHandler = handler; 3835 } 3836 3837 /** 3838 * Method to determine whether a container element should be shown in the current template context.<p> 3839 * 3840 * @param elementData the element data 3841 * 3842 * @return true if the element should be shown 3843 */ 3844 public boolean shouldShowInContext(CmsContainerElementData elementData) { 3845 3846 CmsTemplateContextInfo contextInfo = getData().getTemplateContextInfo(); 3847 if (contextInfo.getCurrentContext() == null) { 3848 return true; 3849 } 3850 CmsDefaultSet<String> allowedContexts = contextInfo.getAllowedContexts().get(elementData.getResourceType()); 3851 if ((allowedContexts != null) && !allowedContexts.contains(contextInfo.getCurrentContext())) { 3852 return false; 3853 } 3854 3855 String settingValue = elementData.getSettings().get(CmsTemplateContextInfo.SETTING); 3856 return (settingValue == null) || settingValue.contains(contextInfo.getCurrentContext()); 3857 } 3858 3859 /** 3860 * Tells the controller that group-container editing has started.<p> 3861 * 3862 * @param groupContainer the group container 3863 * @param isElementGroup <code>true</code> if the group container is an element group and not an inheritance group 3864 */ 3865 public void startEditingGroupcontainer( 3866 final CmsGroupContainerElementPanel groupContainer, 3867 final boolean isElementGroup) { 3868 3869 removeEditButtonsPositionTimer(); 3870 I_CmsSimpleCallback<Boolean> callback = new I_CmsSimpleCallback<Boolean>() { 3871 3872 public void execute(Boolean arg) { 3873 3874 if (arg.booleanValue()) { 3875 if (isElementGroup) { 3876 m_groupEditor = CmsGroupContainerEditor.openGroupcontainerEditor( 3877 groupContainer, 3878 CmsContainerpageController.this, 3879 m_handler); 3880 } else { 3881 m_groupEditor = CmsInheritanceContainerEditor.openInheritanceContainerEditor( 3882 groupContainer, 3883 CmsContainerpageController.this, 3884 m_handler); 3885 } 3886 } else { 3887 CmsNotification.get().send( 3888 Type.WARNING, 3889 Messages.get().key(Messages.GUI_NOTIFICATION_UNABLE_TO_LOCK_0)); 3890 } 3891 } 3892 }; 3893 RootPanel.get().addStyleName(OC_EDITING_GROUPCONTAINER); 3894 if ((m_groupEditor == null) && (groupContainer.isNew())) { 3895 callback.execute(Boolean.TRUE); 3896 } else { 3897 lockContainerpage(callback); 3898 } 3899 3900 } 3901 3902 /** 3903 * Starts the publish lock check. 3904 */ 3905 public void startPublishLockCheck() { 3906 3907 Set<CmsUUID> elementIds = new HashSet<>(); 3908 processPageContent(new I_PageContentVisitor() { 3909 3910 public boolean beginContainer(String name, CmsContainer container) { 3911 3912 return true; 3913 } 3914 3915 public void endContainer() { 3916 3917 // do nothing 3918 } 3919 3920 public void handleElement(CmsContainerPageElementPanel element) { 3921 3922 if (element.hasWritePermission() && element.getLockInfo().isPublishLock()) { 3923 CmsUUID structureId = element.getStructureId(); 3924 if (structureId != null) { 3925 elementIds.add(structureId); 3926 } 3927 } 3928 } 3929 }); 3930 3931 m_publishLockChecker.addIdsToCheck(elementIds); 3932 } 3933 3934 /** 3935 * Tells the controller that group-container editing has stopped.<p> 3936 */ 3937 public void stopEditingGroupcontainer() { 3938 3939 m_groupEditor = null; 3940 RootPanel.get().removeStyleName(OC_EDITING_GROUPCONTAINER); 3941 } 3942 3943 /** 3944 * Unlocks the given resource.<p> 3945 * 3946 * @param structureId the structure id of the resource to unlock 3947 * 3948 * @return <code>true</code> if the resource was unlocked successfully 3949 */ 3950 public boolean unlockResource(CmsUUID structureId) { 3951 3952 return CmsCoreProvider.get().unlock(structureId); 3953 } 3954 3955 /** 3956 * Updates he 3957 */ 3958 public void updateButtonsForCurrentView() { 3959 3960 String nonDefaultViewClass = I_CmsLayoutBundle.INSTANCE.containerpageCss().nonDefaultView(); 3961 CmsUUID viewId = getElementView().getRootViewId(); 3962 if (viewId.isNullUUID()) { 3963 RootPanel.get().removeStyleName(nonDefaultViewClass); 3964 } else { 3965 RootPanel.get().addStyleName(nonDefaultViewClass); 3966 } 3967 } 3968 3969 /** 3970 * Adds the given element data to the element cache.<p> 3971 * 3972 * @param elements the element data 3973 */ 3974 protected void addElements(List<CmsContainerElementData> elements) { 3975 3976 for (CmsContainerElementData element : elements) { 3977 m_elements.put(element.getClientId(), element); 3978 } 3979 } 3980 3981 /** 3982 * Adds the given element data to the element cache.<p> 3983 * 3984 * @param elements the element data 3985 */ 3986 protected void addElements(Map<String, CmsContainerElementData> elements) { 3987 3988 for (CmsContainerElementData element : elements.values()) { 3989 m_elements.put(element.getClientId(), element); 3990 } 3991 } 3992 3993 /** 3994 * Asks the user whether an element which has been removed should be deleted.<p> 3995 * 3996 * @param status the status of the removed element 3997 */ 3998 protected void askWhetherRemovedElementShouldBeDeleted(final CmsRemovedElementStatus status) { 3999 4000 CmsRemovedElementDeletionDialog dialog = new CmsRemovedElementDeletionDialog(status); 4001 dialog.center(); 4002 } 4003 4004 /** 4005 * Checks that a removed can be possibly deleted and if so, asks the user if it should be deleted.<p> 4006 * 4007 * @param id the client id of the element 4008 */ 4009 protected void checkReferencesToRemovedElement(final String id) { 4010 4011 if (id != null) { 4012 //NOTE: We only use an RPC call here to check for references on the server side. If, at a later point, we decide 4013 //to add a save button again, this will have to be changed, because then we have to consider client-side state. 4014 CmsRpcAction<CmsRemovedElementStatus> getStatusAction = new CmsRpcAction<CmsRemovedElementStatus>() { 4015 4016 @Override 4017 public void execute() { 4018 4019 start(200, true); 4020 getContainerpageService().getRemovedElementStatus(id, null, this); 4021 } 4022 4023 @Override 4024 public void onResponse(final CmsRemovedElementStatus status) { 4025 4026 stop(false); 4027 if (status.isDeletionCandidate()) { 4028 askWhetherRemovedElementShouldBeDeleted(status); 4029 4030 } 4031 } 4032 4033 }; 4034 getStatusAction.execute(); 4035 4036 } 4037 } 4038 4039 /** 4040 * Disables option and toolbar buttons.<p> 4041 */ 4042 protected void deactivateOnClosing() { 4043 4044 removeEditButtonsPositionTimer(); 4045 m_handler.deactivateCurrentButton(); 4046 m_handler.disableToolbarButtons(); 4047 } 4048 4049 /** 4050 * Helper method to get all current container page elements.<p> 4051 * 4052 * @param includeGroupContents true if the contents of group containers should also be included 4053 * 4054 * @return the list of current container page elements 4055 */ 4056 protected List<CmsContainerPageElementPanel> getAllContainerPageElements(boolean includeGroupContents) { 4057 4058 List<CmsContainerPageElementPanel> elemWidgets = new ArrayList<CmsContainerPageElementPanel>(); 4059 for (Entry<String, org.opencms.ade.containerpage.client.ui.CmsContainerPageContainer> entry : CmsContainerpageController.get().getContainerTargets().entrySet()) { 4060 Iterator<Widget> elIt = entry.getValue().iterator(); 4061 while (elIt.hasNext()) { 4062 try { 4063 org.opencms.ade.containerpage.client.ui.CmsContainerPageElementPanel elementWidget = (org.opencms.ade.containerpage.client.ui.CmsContainerPageElementPanel)elIt.next(); 4064 elemWidgets.add(elementWidget); 4065 if (includeGroupContents && (elementWidget instanceof CmsGroupContainerElementPanel)) { 4066 List<CmsContainerPageElementPanel> groupChildren = ((CmsGroupContainerElementPanel)elementWidget).getGroupChildren(); 4067 elemWidgets.addAll(groupChildren); 4068 } 4069 } catch (ClassCastException e) { 4070 // no proper container element, skip it (this should never happen!) 4071 CmsDebugLog.getInstance().printLine( 4072 "WARNING: there is an inappropriate element within a container"); 4073 } 4074 } 4075 } 4076 return elemWidgets; 4077 } 4078 4079 /** 4080 * Returns the core RPC service.<p> 4081 * 4082 * @return the core service 4083 */ 4084 protected I_CmsCoreServiceAsync getCoreService() { 4085 4086 if (m_coreSvc == null) { 4087 m_coreSvc = CmsCoreProvider.getService(); 4088 } 4089 return m_coreSvc; 4090 } 4091 4092 /** 4093 * Returns the currently active group editor.<p> 4094 * 4095 * @return the currently active group editor 4096 */ 4097 protected A_CmsGroupEditor getGroupEditor() { 4098 4099 return m_groupEditor; 4100 } 4101 4102 /** 4103 * Returns the content locale.<p> 4104 * 4105 * @return the content locale 4106 */ 4107 protected String getLocale() { 4108 4109 return m_data.getLocale(); 4110 } 4111 4112 /** 4113 * Gets the page content for purposes of saving.<p> 4114 * 4115 * @return the page content 4116 */ 4117 protected List<CmsContainer> getPageContent() { 4118 4119 SaveDataVisitor visitor = new SaveDataVisitor(); 4120 processPageContent(visitor); 4121 return visitor.getContainers(); 4122 4123 } 4124 4125 /** 4126 * Returns the containers of the page in their current state.<p> 4127 * 4128 * @return the containers of the page 4129 */ 4130 protected List<CmsContainer> getPageState() { 4131 4132 PageStateVisitor visitor = new PageStateVisitor(); 4133 processPageContent(visitor); 4134 return visitor.getContainers(); 4135 } 4136 4137 /** 4138 * Returns the request parameters of the displayed container-page.<p> 4139 * 4140 * @return the request parameters 4141 */ 4142 protected String getRequestParams() { 4143 4144 return m_data.getRequestParams(); 4145 } 4146 4147 /** 4148 * Checks if any of the containers are nested containers.<p> 4149 * 4150 * @return true if there are nested containers 4151 */ 4152 protected boolean hasNestedContainers() { 4153 4154 boolean hasNestedContainers = false; 4155 for (CmsContainer container : m_containers.values()) { 4156 if (container.getParentContainerName() != null) { 4157 hasNestedContainers = true; 4158 break; 4159 } 4160 } 4161 return hasNestedContainers; 4162 } 4163 4164 /** 4165 * Returns whether the given container is considered a root container.<p> 4166 * 4167 * @param container the container to check 4168 * 4169 * @return <code>true</code> if the given container is a root container 4170 */ 4171 protected boolean isRootContainer(CmsContainer container) { 4172 4173 boolean isRoot = false; 4174 if (!container.isSubContainer()) { 4175 isRoot = true; 4176 } else if (container.isDetailOnly()) { 4177 CmsContainer parent = getContainer(container.getParentContainerName()); 4178 isRoot = (parent != null) && !parent.isDetailOnly(); 4179 } 4180 return isRoot; 4181 } 4182 4183 /** 4184 * Opens the editor for the newly created element.<p> 4185 * 4186 * @param element the container element 4187 * @param newElementData the new element data 4188 * @param inline <code>true</code> to open the inline editor for the given element if available 4189 */ 4190 protected void openEditorForNewElement( 4191 org.opencms.ade.containerpage.client.ui.CmsContainerPageElementPanel element, 4192 CmsContainerElement newElementData, 4193 boolean inline) { 4194 4195 String oldId = element.getNewType(); 4196 element.setNewType(null); 4197 if (inline) { 4198 String newId = getServerId(newElementData.getClientId()); 4199 CmsContentEditor.replaceResourceIds(element.getElement(), oldId, newId); 4200 } 4201 element.setId(newElementData.getClientId()); 4202 element.setSitePath(newElementData.getSitePath()); 4203 if (!isGroupcontainerEditing()) { 4204 setPageChanged(); 4205 } 4206 getHandler().hidePageOverlay(); 4207 getHandler().openEditorForElement(element, inline, true); 4208 } 4209 4210 /** 4211 * Previews events. Shows the leaving page dialog, if the page has changed and an anchor has been clicked.<p> 4212 * Also triggers an element view change on 'Ctrl+E'.<p> 4213 * 4214 * @param event the native event 4215 */ 4216 protected void previewNativeEvent(NativePreviewEvent event) { 4217 if (m_previewHandler != null) { 4218 m_previewHandler.onPreviewNativeEvent(event); 4219 return; 4220 } 4221 4222 Event nativeEvent = Event.as(event.getNativeEvent()); 4223 4224 if ((nativeEvent.getTypeInt() == Event.ONCLICK) && hasPageChanged()) { 4225 EventTarget target = nativeEvent.getEventTarget(); 4226 if (!Element.is(target)) { 4227 return; 4228 } 4229 Element element = Element.as(target); 4230 element = CmsDomUtil.getAncestor(element, CmsDomUtil.Tag.a); 4231 if (element == null) { 4232 return; 4233 } 4234 AnchorElement anc = AnchorElement.as(element); 4235 final String uri = anc.getHref(); 4236 4237 // avoid to abort events for date-picker widgets 4238 if (CmsStringUtil.isEmptyOrWhitespaceOnly(uri) 4239 || (CmsDomUtil.getAncestor(element, "x-date-picker") != null)) { 4240 return; 4241 } 4242 nativeEvent.preventDefault(); 4243 nativeEvent.stopPropagation(); 4244 m_handler.leavePage(uri); 4245 } 4246 if (event.getTypeInt() == Event.ONKEYDOWN) { 4247 int keyCode = nativeEvent.getKeyCode(); 4248 if ((keyCode == KeyCodes.KEY_F5) && hasPageChanged()) { 4249 // user pressed F5 4250 nativeEvent.preventDefault(); 4251 nativeEvent.stopPropagation(); 4252 m_handler.leavePage(Window.Location.getHref()); 4253 } 4254 if (nativeEvent.getCtrlKey() || nativeEvent.getMetaKey()) { 4255 // look for short cuts 4256 if (keyCode == KeyCodes.KEY_E) { 4257 if (nativeEvent.getShiftKey()) { 4258 circleContainerEditLayers(); 4259 } else { 4260 openNextElementView(); 4261 } 4262 nativeEvent.preventDefault(); 4263 nativeEvent.stopPropagation(); 4264 } 4265 } 4266 } 4267 } 4268 4269 /** 4270 * Iterates over all the container contents and calls a visitor object with the visited containers/elements as parameters. 4271 * 4272 * @param visitor the visitor which the container elements should be passed to 4273 */ 4274 protected void processPageContent(I_PageContentVisitor visitor) { 4275 4276 for (Entry<String, org.opencms.ade.containerpage.client.ui.CmsContainerPageContainer> entry : m_targetContainers.entrySet()) { 4277 4278 CmsContainer cnt = m_containers.get(entry.getKey()); 4279 if (visitor.beginContainer(entry.getKey(), cnt)) { 4280 Iterator<Widget> elIt = entry.getValue().iterator(); 4281 while (elIt.hasNext()) { 4282 try { 4283 CmsContainerPageElementPanel elementWidget = (CmsContainerPageElementPanel)elIt.next(); 4284 visitor.handleElement(elementWidget); 4285 } catch (ClassCastException e) { 4286 // no proper container element, skip it (this should never happen!) 4287 CmsDebugLog.getInstance().printLine( 4288 "WARNING: there is an inappropriate element within a container"); 4289 } 4290 } 4291 visitor.endContainer(); 4292 } 4293 } 4294 } 4295 4296 /** 4297 * Removes all container elements with the given id from all containers and the client side cache.<p> 4298 * 4299 * @param resourceId the resource id 4300 */ 4301 protected void removeContainerElements(String resourceId) { 4302 4303 boolean changed = false; 4304 Iterator<org.opencms.ade.containerpage.client.ui.CmsContainerPageElementPanel> it = getAllDragElements().iterator(); 4305 while (it.hasNext()) { 4306 org.opencms.ade.containerpage.client.ui.CmsContainerPageElementPanel containerElement = it.next(); 4307 if (resourceId.startsWith(containerElement.getId())) { 4308 containerElement.removeFromParent(); 4309 changed = true; 4310 } 4311 } 4312 for (String elementId : m_elements.keySet()) { 4313 if (elementId.startsWith(resourceId)) { 4314 m_elements.remove(elementId); 4315 } 4316 } 4317 if (changed) { 4318 setPageChanged(); 4319 } 4320 } 4321 4322 /** 4323 * Schedules an update of the gallery data according to the current element view and the editable containers.<p> 4324 */ 4325 protected void scheduleGalleryUpdate() { 4326 4327 // only if not already scheduled 4328 if (m_galleryUpdateTimer == null) { 4329 m_galleryUpdateTimer = new Timer() { 4330 4331 @Override 4332 public void run() { 4333 4334 m_galleryUpdateTimer = null; 4335 updateGalleryData(false, null); 4336 } 4337 }; 4338 m_galleryUpdateTimer.schedule(50); 4339 } 4340 } 4341 4342 /** 4343 * Sets the page changed flag and initializes the window closing handler if necessary.<p> 4344 * 4345 * @param changed if <code>true</code> the page has changed 4346 * @param unlock if <code>true</code> the page will be unlocked for unchanged pages 4347 */ 4348 protected void setPageChanged(boolean changed, boolean unlock) { 4349 4350 if (changed) { 4351 if (!m_pageChanged) { 4352 m_pageChanged = changed; 4353 lockContainerpage(new I_CmsSimpleCallback<Boolean>() { 4354 4355 public void execute(Boolean arg) { 4356 4357 // nothing to do 4358 } 4359 }); 4360 } 4361 } else { 4362 m_pageChanged = changed; 4363 if (unlock) { 4364 unlockContainerpage(); 4365 } 4366 } 4367 } 4368 4369 /** 4370 * Asynchronously unlocks the container page. 4371 */ 4372 protected void unlockContainerpage() { 4373 4374 I_CmsAutoBeanFactory factory = CmsCoreProvider.AUTO_BEAN_FACTORY; 4375 AutoBean<I_CmsUnlockData> unlockParams = factory.unlockData(); 4376 unlockParams.as().setPageId("" + CmsCoreProvider.get().getStructureId()); 4377 if (getData().getDetailId() != null) { 4378 unlockParams.as().setDetailId("" + getData().getDetailId()); 4379 } 4380 unlockParams.as().setLocale(CmsCoreProvider.get().getLocale()); 4381 String url = CmsCoreProvider.get().link("/handleBuiltinService" + CmsGwtConstants.HANDLER_UNLOCK_PAGE); 4382 sendBeacon(url, AutoBeanCodex.encode(unlockParams).getPayload()); 4383 } 4384 4385 /** 4386 * Returns the pages of editable containers.<p> 4387 * 4388 * @return the containers 4389 */ 4390 List<CmsContainer> getEditableContainers() { 4391 4392 List<CmsContainer> containers = new ArrayList<CmsContainer>(); 4393 for (CmsContainer container : m_containers.values()) { 4394 if ((m_targetContainers.get(container.getName()) != null) 4395 && isContainerEditable(m_targetContainers.get(container.getName()))) { 4396 containers.add(container); 4397 } 4398 } 4399 return containers; 4400 } 4401 4402 /** 4403 * Handles a window resize to reset highlighting and the edit button positions.<p> 4404 */ 4405 void handleResize() { 4406 4407 m_resizeTimer = null; 4408 resetEditButtons(); 4409 } 4410 4411 /** 4412 * Call on window resize.<p> 4413 */ 4414 void onResize() { 4415 4416 if (!isGroupcontainerEditing() && (m_resizeTimer == null)) { 4417 m_resizeTimer = new Timer() { 4418 4419 @Override 4420 public void run() { 4421 4422 handleResize(); 4423 } 4424 }; 4425 m_resizeTimer.schedule(300); 4426 } 4427 } 4428 4429 /** 4430 * Sets the load time.<p> 4431 * 4432 * @param time the time to set 4433 */ 4434 void setLoadTime(Long time) { 4435 4436 if (time != null) { 4437 m_loadTime = time.longValue(); 4438 } 4439 } 4440 4441 /** 4442 * Updates the gallery data according to the current element view and the editable containers.<p> 4443 * This method should only be called from the gallery update timer to avoid unnecessary requests.<p> 4444 * 4445 * @param viewChanged <code>true</code> in case the element view changed 4446 * @param nextAction the action to execute after updating the gallery data 4447 */ 4448 void updateGalleryData(final boolean viewChanged, final Runnable nextAction) { 4449 4450 CmsRpcAction<CmsContainerPageGalleryData> dataAction = new CmsRpcAction<CmsContainerPageGalleryData>() { 4451 4452 @Override 4453 public void execute() { 4454 4455 getContainerpageService().getGalleryDataForPage( 4456 getEditableContainers(), 4457 getElementView().getElementViewId(), 4458 CmsCoreProvider.get().getUri(), 4459 getData().getDetailId(), 4460 getData().getLocale(), 4461 CmsContainerpageController.get().getData().getTemplateContextInfo(), 4462 this); 4463 } 4464 4465 @Override 4466 protected void onResponse(CmsContainerPageGalleryData result) { 4467 4468 m_handler.m_editor.getAdd().updateGalleryData(result, viewChanged); 4469 if (nextAction != null) { 4470 nextAction.run(); 4471 } 4472 } 4473 }; 4474 dataAction.execute(); 4475 } 4476 4477 /** 4478 * Creates a copy of the event details and adds a 'type' field with the specified value to the result. 4479 * 4480 * @param details the details to copy 4481 * @param type the type to add 4482 * @return the copy with the added type 4483 */ 4484 private I_EventDetails addTypeToDetails(I_EventDetails details, String type) { 4485 4486 I_EventDetails result = copyEventDetails(details); 4487 result.setType(type); 4488 return result; 4489 } 4490 4491 /** 4492 * Checks whether there are other references to a given container page element.<p> 4493 * 4494 * @param element the element to check 4495 * @param callback the callback which will be called with the result of the check (true if there are other references) 4496 */ 4497 private void checkElementReferences( 4498 final CmsContainerPageElementPanel element, 4499 final AsyncCallback<CmsRemovedElementStatus> callback) { 4500 4501 ReferenceCheckVisitor visitor = new ReferenceCheckVisitor(element); 4502 processPageContent(visitor); 4503 if (visitor.hasReferences()) { 4504 // Don't need to ask the server because we already know we have other references in the same page 4505 CmsRpcAction<CmsListInfoBean> infoAction = new CmsRpcAction<CmsListInfoBean>() { 4506 4507 @Override 4508 public void execute() { 4509 4510 start(200, true); 4511 CmsCoreProvider.getVfsService().getPageInfo(new CmsUUID(getServerId(element.getId())), this); 4512 } 4513 4514 @Override 4515 protected void onResponse(CmsListInfoBean result) { 4516 4517 stop(false); 4518 callback.onSuccess(new CmsRemovedElementStatus(null, result, false, null)); 4519 } 4520 }; 4521 infoAction.execute(); 4522 } else { 4523 CmsRpcAction<CmsRemovedElementStatus> getStatusAction = new CmsRpcAction<CmsRemovedElementStatus>() { 4524 4525 @Override 4526 public void execute() { 4527 4528 start(200, true); 4529 getContainerpageService().getRemovedElementStatus( 4530 element.getId(), 4531 CmsCoreProvider.get().getStructureId(), 4532 this); 4533 } 4534 4535 @Override 4536 public void onResponse(final CmsRemovedElementStatus status) { 4537 4538 stop(false); 4539 callback.onSuccess(status); 4540 } 4541 4542 }; 4543 getStatusAction.execute(); 4544 4545 } 4546 } 4547 4548 /** 4549 * Checks if the page was locked by another user at load time.<p> 4550 */ 4551 private void checkLockInfo() { 4552 4553 if (CmsStringUtil.isNotEmptyOrWhitespaceOnly(getData().getLockInfo())) { 4554 CmsNotification.get().send(Type.ERROR, getData().getLockInfo()); 4555 m_lockStatus = LockStatus.failed; 4556 m_handler.m_editor.disableEditing(getData().getLockInfo()); 4557 } 4558 } 4559 4560 /** 4561 * Selects the next container edit level.<p> 4562 */ 4563 private void circleContainerEditLayers() { 4564 4565 if (m_isContentEditing || isGroupcontainerEditing() || (m_maxContainerLevel == 0)) { 4566 return; 4567 } 4568 boolean hasEditables = false; 4569 int previousLevel = m_currentEditLevel; 4570 String message = ""; 4571 while (!hasEditables) { 4572 if (m_currentEditLevel == m_maxContainerLevel) { 4573 m_currentEditLevel = -1; 4574 message = Messages.get().key(Messages.GUI_SWITCH_EDIT_LEVEL_ALL_1, m_elementView.getTitle()); 4575 } else { 4576 m_currentEditLevel++; 4577 message = Messages.get().key(Messages.GUI_SWITCH_EDIT_LEVEL_1, Integer.valueOf(m_currentEditLevel)); 4578 } 4579 reinitializeButtons(); 4580 hasEditables = !CmsDomUtil.getElementsByClass( 4581 I_CmsElementToolbarContext.ELEMENT_OPTION_BAR_CSS_CLASS).isEmpty(); 4582 } 4583 if (previousLevel != m_currentEditLevel) { 4584 CmsNotification.get().send(Type.NORMAL, message); 4585 } 4586 } 4587 4588 /** 4589 * Copies an event details object. 4590 * 4591 * @param original the original event details 4592 * @return the copied object 4593 */ 4594 private I_EventDetails copyEventDetails(I_EventDetails original) { 4595 4596 JsPropertyMap<Object> originalMap = Js.cast(original); 4597 JsPropertyMap<Object> result = Js.cast(new JsObject()); 4598 originalMap.forEach(key -> { 4599 result.set(key, originalMap.get(key)); 4600 }); 4601 return Js.cast(result); 4602 } 4603 4604 /** 4605 * Returns all element id's related to the given one.<p> 4606 * 4607 * @param id the element id 4608 * @return the related id's 4609 */ 4610 private Set<String> getRelatedElementIds(String id) { 4611 4612 Set<String> result = new HashSet<String>(); 4613 if (id != null) { 4614 result.add(id); 4615 String serverId = getServerId(id); 4616 4617 Iterator<String> it = m_elements.keySet().iterator(); 4618 while (it.hasNext()) { 4619 String elId = it.next(); 4620 if (elId.startsWith(serverId)) { 4621 result.add(elId); 4622 } 4623 } 4624 4625 Iterator<org.opencms.ade.containerpage.client.ui.CmsContainerPageElementPanel> itEl = getAllDragElements().iterator(); 4626 while (itEl.hasNext()) { 4627 org.opencms.ade.containerpage.client.ui.CmsContainerPageElementPanel element = itEl.next(); 4628 if (element.getId().startsWith(serverId)) { 4629 result.add(element.getId()); 4630 } 4631 } 4632 } 4633 return result; 4634 } 4635 4636 /** 4637 * Checks whether the given container matches the current edit level.<p> 4638 * 4639 * @param container the container to check 4640 * 4641 * @return <code>true</code> if the given container matches the current edit level 4642 */ 4643 private boolean matchesCurrentEditLevel(I_CmsDropContainer container) { 4644 4645 boolean result = !(container instanceof CmsContainerPageContainer) 4646 || (m_currentEditLevel == -1) 4647 || (m_currentEditLevel == ((CmsContainerPageContainer)container).getContainerLevel()); 4648 return result; 4649 } 4650 4651 /** 4652 * Opens the next available root element view.<p> 4653 */ 4654 private void openNextElementView() { 4655 4656 List<CmsElementViewInfo> views = getData().getElementViews(); 4657 if (views.size() > 1) { 4658 CmsUUID current = m_elementView.getRootViewId(); 4659 4660 // look for the current view index 4661 int currentIndex = -1; 4662 for (int i = 0; i < views.size(); i++) { 4663 CmsElementViewInfo view = views.get(i); 4664 if (view.isRoot() && current.equals(view.getElementViewId())) { 4665 currentIndex = i; 4666 break; 4667 } 4668 } 4669 if (currentIndex != -1) { 4670 CmsElementViewInfo target = null; 4671 // look for the next root view 4672 for (int i = currentIndex + 1; i < views.size(); i++) { 4673 CmsElementViewInfo view = views.get(i); 4674 if (view.isRoot()) { 4675 target = view; 4676 break; 4677 } 4678 } 4679 if (target == null) { 4680 // start at the beginning 4681 for (int i = 0; i < currentIndex; i++) { 4682 CmsElementViewInfo view = views.get(i); 4683 if (view.isRoot()) { 4684 target = view; 4685 break; 4686 } 4687 } 4688 } 4689 if (target != null) { 4690 final String viewName = target.getTitle(); 4691 Runnable action = new Runnable() { 4692 4693 public void run() { 4694 4695 CmsNotification.get().send( 4696 Type.NORMAL, 4697 Messages.get().key(Messages.GUI_SWITCH_ELEMENT_VIEW_NOTIFICATION_1, viewName)); 4698 } 4699 }; 4700 setElementView(target, action); 4701 } 4702 } 4703 } 4704 } 4705 4706 /** 4707 * Removes the edit buttons position timer.<p> 4708 */ 4709 private void removeEditButtonsPositionTimer() { 4710 4711 if (m_editButtonsPositionTimer != null) { 4712 m_editButtonsPositionTimer.cancel(); 4713 m_editButtonsPositionTimer = null; 4714 } 4715 } 4716 4717 /** 4718 * Calls the browser's sendBeacon function. 4719 * 4720 * @param url the URL to send the data to 4721 * @param data the data to send 4722 */ 4723 private native void sendBeacon(String url, String data) /*-{ 4724 $wnd.navigator.sendBeacon(url, data); 4725 }-*/; 4726 4727 /** 4728 * Creates a custom event and dispatches it to the document. 4729 * 4730 * @param type the event type 4731 * @param details the event details 4732 */ 4733 private void sendEvent(String type, I_EventDetails details) { 4734 4735 CustomEventInit<I_EventDetails> init = Js.cast(CustomEventInit.create()); 4736 init.setBubbles(false); 4737 init.setCancelable(false); 4738 init.setDetail(details); 4739 CustomEvent<I_EventDetails> event1 = new elemental2.dom.CustomEvent<I_EventDetails>(type, init); 4740 CustomEvent<I_EventDetails> event = event1; 4741 // fire event in a timer so the page editor is not blocked by the event handler reacting to it 4742 Timer timer = new Timer() { 4743 4744 @Override 4745 public void run() { 4746 4747 DomGlobal.document.documentElement.dispatchEvent(event); 4748 } 4749 }; 4750 timer.schedule(0); 4751 } 4752 4753 /** 4754 * Checks whether given element is a model group and it's option bar edit points should be visible.<p> 4755 * 4756 * @param element the element to check 4757 * 4758 * @return <code>true</code> in case the current page is not a model group page, 4759 * the given element is a model group and it is inside a view visible to the current user 4760 */ 4761 private boolean shouldShowModelgroupOptionBar(CmsContainerPageElementPanel element) { 4762 4763 if (!getData().isModelGroup() && element.isModelGroup()) { 4764 for (CmsElementViewInfo info : getData().getElementViews()) { 4765 if (info.getElementViewId().equals(element.getElementView())) { 4766 return true; 4767 } 4768 } 4769 } 4770 4771 return false; 4772 } 4773 4774 /** 4775 * Updates the container level info on the present containers.<p> 4776 */ 4777 private void updateContainerLevelInfo() { 4778 4779 Map<String, CmsContainerPageContainer> containers = new HashMap<String, CmsContainerPageContainer>(); 4780 List<CmsContainerPageContainer> temp = new ArrayList<CmsContainerPageContainer>(m_targetContainers.values()); 4781 m_maxContainerLevel = 0; 4782 boolean progress = true; 4783 while (!temp.isEmpty() && progress) { 4784 int size = containers.size(); 4785 Iterator<CmsContainerPageContainer> it = temp.iterator(); 4786 while (it.hasNext()) { 4787 CmsContainerPageContainer container = it.next(); 4788 int level = -1; 4789 if (CmsStringUtil.isEmptyOrWhitespaceOnly(container.getParentContainerId())) { 4790 level = 0; 4791 } else if (containers.containsKey(container.getParentContainerId())) { 4792 level = containers.get(container.getParentContainerId()).getContainerLevel() + 1; 4793 } 4794 if (level > -1) { 4795 container.setContainerLevel(level); 4796 containers.put(container.getContainerId(), container); 4797 it.remove(); 4798 if (level > m_maxContainerLevel) { 4799 m_maxContainerLevel = level; 4800 } 4801 } 4802 } 4803 progress = containers.size() > size; 4804 } 4805 } 4806 4807 /** 4808 * Sets the oc-detail-preview class on first container elements of an appropriate type in detail containers, 4809 * if we are currently not showing a detail content. 4810 */ 4811 private void updateDetailPreviewStyles() { 4812 4813 Set<String> detailTypes = getData().getDetailTypes(); 4814 if ((getData().getDetailId() != null) || detailTypes.isEmpty()) { 4815 return; 4816 } 4817 for (Element elem : CmsDomUtil.getElementsByClass(CmsGwtConstants.CLASS_DETAIL_PREVIEW)) { 4818 elem.removeClassName(CmsGwtConstants.CLASS_DETAIL_PREVIEW); 4819 } 4820 boolean defaultDetailPage = detailTypes.contains(CmsGwtConstants.DEFAULT_DETAILPAGE_TYPE); 4821 4822 processPageContent(new I_PageContentVisitor() { 4823 4824 boolean m_isdetail = false; 4825 4826 public boolean beginContainer(String name, CmsContainer container) { 4827 4828 m_isdetail = container.isDetailViewContainer(); 4829 return true; 4830 } 4831 4832 public void endContainer() { 4833 4834 // do nothing 4835 } 4836 4837 public void handleElement(CmsContainerPageElementPanel element) { 4838 4839 if (m_isdetail && (defaultDetailPage || detailTypes.contains(element.getResourceType()))) { 4840 element.addStyleName(CmsGwtConstants.CLASS_DETAIL_PREVIEW); 4841 } 4842 } 4843 }); 4844 4845 } 4846}