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