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