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