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.sitemap.client; 029 030import org.opencms.ade.sitemap.client.control.CmsSitemapChangeEvent; 031import org.opencms.ade.sitemap.client.control.CmsSitemapController; 032import org.opencms.ade.sitemap.client.control.CmsSitemapDNDController; 033import org.opencms.ade.sitemap.client.control.CmsSitemapLoadEvent; 034import org.opencms.ade.sitemap.client.control.I_CmsSitemapChangeHandler; 035import org.opencms.ade.sitemap.client.control.I_CmsSitemapLoadHandler; 036import org.opencms.ade.sitemap.client.hoverbar.A_CmsSitemapMenuEntry; 037import org.opencms.ade.sitemap.client.hoverbar.CmsChangeCategoryMenuEntry; 038import org.opencms.ade.sitemap.client.hoverbar.CmsCreateCategoryMenuEntry; 039import org.opencms.ade.sitemap.client.hoverbar.CmsCreateCategoryMenuEntry.CmsCategoryTitleAndName; 040import org.opencms.ade.sitemap.client.hoverbar.CmsDeleteCategoryMenuEntry; 041import org.opencms.ade.sitemap.client.hoverbar.CmsHoverbarCreateGalleryButton; 042import org.opencms.ade.sitemap.client.hoverbar.CmsHoverbarCreateModelPageButton; 043import org.opencms.ade.sitemap.client.hoverbar.CmsSitemapHoverbar; 044import org.opencms.ade.sitemap.client.hoverbar.I_CmsContextMenuItemProvider; 045import org.opencms.ade.sitemap.client.toolbar.CmsSitemapToolbar; 046import org.opencms.ade.sitemap.client.ui.CmsStatusIconUpdateHandler; 047import org.opencms.ade.sitemap.client.ui.css.I_CmsImageBundle; 048import org.opencms.ade.sitemap.client.ui.css.I_CmsSitemapLayoutBundle; 049import org.opencms.ade.sitemap.shared.CmsClientSitemapEntry; 050import org.opencms.ade.sitemap.shared.CmsDetailPageTable; 051import org.opencms.ade.sitemap.shared.CmsGalleryFolderEntry; 052import org.opencms.ade.sitemap.shared.CmsGalleryType; 053import org.opencms.ade.sitemap.shared.CmsModelInfo; 054import org.opencms.ade.sitemap.shared.CmsModelPageEntry; 055import org.opencms.ade.sitemap.shared.CmsSitemapCategoryData; 056import org.opencms.ade.sitemap.shared.CmsSitemapChange; 057import org.opencms.ade.sitemap.shared.CmsSitemapData; 058import org.opencms.ade.sitemap.shared.CmsSitemapData.EditorMode; 059import org.opencms.ade.sitemap.shared.CmsSitemapInfo; 060import org.opencms.file.CmsResource; 061import org.opencms.gwt.client.A_CmsEntryPoint; 062import org.opencms.gwt.client.CmsBroadcastTimer; 063import org.opencms.gwt.client.dnd.CmsDNDHandler; 064import org.opencms.gwt.client.rpc.CmsRpcAction; 065import org.opencms.gwt.client.ui.CmsErrorDialog; 066import org.opencms.gwt.client.ui.CmsInfoHeader; 067import org.opencms.gwt.client.ui.CmsList; 068import org.opencms.gwt.client.ui.CmsListItemWidget; 069import org.opencms.gwt.client.ui.CmsListItemWidget.Background; 070import org.opencms.gwt.client.ui.CmsNotification; 071import org.opencms.gwt.client.ui.CmsPushButton; 072import org.opencms.gwt.client.ui.I_CmsButton; 073import org.opencms.gwt.client.ui.I_CmsButton.ButtonStyle; 074import org.opencms.gwt.client.ui.I_CmsListItem; 075import org.opencms.gwt.client.ui.css.I_CmsLayoutBundle; 076import org.opencms.gwt.client.ui.tree.CmsLazyTree; 077import org.opencms.gwt.client.ui.tree.CmsLazyTreeItem; 078import org.opencms.gwt.client.ui.tree.CmsTree; 079import org.opencms.gwt.client.ui.tree.CmsTreeItem; 080import org.opencms.gwt.client.ui.tree.I_CmsLazyOpenHandler; 081import org.opencms.gwt.client.util.CmsDomUtil; 082import org.opencms.gwt.client.util.CmsJsUtil; 083import org.opencms.gwt.client.util.CmsScriptCallbackHelper; 084import org.opencms.gwt.client.util.CmsStyleVariable; 085import org.opencms.gwt.shared.CmsCategoryBean; 086import org.opencms.gwt.shared.CmsCategoryTreeEntry; 087import org.opencms.gwt.shared.CmsGwtConstants; 088import org.opencms.gwt.shared.CmsListInfoBean; 089import org.opencms.gwt.shared.property.CmsClientProperty; 090import org.opencms.gwt.shared.property.CmsPropertyModification; 091import org.opencms.util.CmsPair; 092import org.opencms.util.CmsStringUtil; 093import org.opencms.util.CmsUUID; 094 095import java.util.ArrayList; 096import java.util.Arrays; 097import java.util.Collection; 098import java.util.Collections; 099import java.util.Comparator; 100import java.util.HashMap; 101import java.util.HashSet; 102import java.util.List; 103import java.util.Map; 104import java.util.Set; 105 106import com.google.common.base.Function; 107import com.google.common.collect.Lists; 108import com.google.common.collect.Multimap; 109import com.google.gwt.core.client.JsArrayString; 110import com.google.gwt.core.client.Scheduler; 111import com.google.gwt.core.client.Scheduler.ScheduledCommand; 112import com.google.gwt.dom.client.Style.Display; 113import com.google.gwt.event.dom.client.ClickEvent; 114import com.google.gwt.event.dom.client.ClickHandler; 115import com.google.gwt.event.logical.shared.OpenEvent; 116import com.google.gwt.user.client.Window; 117import com.google.gwt.user.client.rpc.AsyncCallback; 118import com.google.gwt.user.client.ui.FlowPanel; 119import com.google.gwt.user.client.ui.Label; 120import com.google.gwt.user.client.ui.RootPanel; 121import com.google.gwt.user.client.ui.Widget; 122 123/** 124 * Sitemap editor.<p> 125 * 126 * @since 8.0.0 127 */ 128public final class CmsSitemapView extends A_CmsEntryPoint 129implements I_CmsSitemapChangeHandler, I_CmsSitemapLoadHandler { 130 131 /** 132 * The sitemap tree open handler.<p> 133 */ 134 protected class TreeOpenHandler implements I_CmsLazyOpenHandler<CmsSitemapTreeItem> { 135 136 /** Flag indicating the tree is initializing. */ 137 private boolean m_initializing; 138 139 /** 140 * @see org.opencms.gwt.client.ui.tree.I_CmsLazyOpenHandler#load(org.opencms.gwt.client.ui.tree.CmsLazyTreeItem, java.lang.Runnable) 141 */ 142 public void load(final CmsSitemapTreeItem target, Runnable loadCallback) { 143 144 // not used 145 } 146 147 /** 148 * @see org.opencms.gwt.client.ui.tree.I_CmsLazyOpenHandler#onOpen(com.google.gwt.event.logical.shared.OpenEvent) 149 */ 150 public void onOpen(OpenEvent<CmsSitemapTreeItem> event) { 151 152 CmsSitemapTreeItem target = event.getTarget(); 153 if ((target.getLoadState() == CmsLazyTreeItem.LoadState.UNLOADED)) { 154 target.onStartLoading(); 155 target.setOpen(false); 156 getController().getChildren(target.getEntryId(), true, null); 157 } else if (!m_initializing 158 && ((target.getChildren().getWidgetCount() > 0) 159 && (((CmsSitemapTreeItem)target.getChild( 160 0)).getLoadState() == CmsLazyTreeItem.LoadState.UNLOADED))) { 161 // load grand children in advance 162 getController().getChildren(target.getEntryId(), false, null); 163 } 164 } 165 166 /** 167 * Sets the initializing flag.<p> 168 * 169 * @param initializing the initializing flag 170 */ 171 protected void setInitializing(boolean initializing) { 172 173 m_initializing = initializing; 174 } 175 } 176 177 /** The download gallery type name. */ 178 public static final String DOWNLOAD_GALLERY_TYPE = "downloadgallery"; 179 180 /** The image gallery type name. */ 181 public static final String IMAGE_GALLERY_TYPE = "imagegallery"; 182 183 /** The singleton instance. */ 184 private static CmsSitemapView m_instance; 185 186 /** Text metrics key. */ 187 private static final String TM_SITEMAP = "Sitemap"; 188 189 /** The displayed sitemap tree. */ 190 protected CmsLazyTree<CmsSitemapTreeItem> m_tree; 191 192 /** The tree shown in category mode. */ 193 private CmsTree<CmsTreeItem> m_categoryTree; 194 195 /** The controller. */ 196 private CmsSitemapController m_controller; 197 198 /** The current sitemap editor mode. */ 199 private EditorMode m_editorMode; 200 201 /** The gallery tree widget. */ 202 private CmsTree<CmsGalleryTreeItem> m_galleryTree; 203 204 /** The gallery folder items by id. */ 205 private Map<CmsUUID, CmsGalleryTreeItem> m_galleryTreeItems; 206 207 /** The gallery type items by type name. */ 208 private Map<String, CmsGalleryTreeItem> m_galleryTypeItems; 209 210 /** The header. */ 211 private CmsInfoHeader m_header; 212 213 /** The header container. */ 214 private FlowPanel m_headerContainer; 215 216 /** Style variable which keeps track of whether we are in VFS mode or navigation mode. */ 217 private CmsStyleVariable m_inNavigationStyle; 218 219 /** The locale comparison view. */ 220 private FlowPanel m_localeComparison = new FlowPanel(); 221 222 /** The model group pages root entry. */ 223 private CmsModelPageTreeItem m_modelGroupRoot; 224 225 /** Model page data beans for the model page mode. */ 226 private Map<CmsUUID, CmsModelPageEntry> m_modelPageData = new HashMap<CmsUUID, CmsModelPageEntry>(); 227 228 /** Root tree item for the model page mode. */ 229 private CmsModelPageTreeItem m_modelPageRoot; 230 231 /** Tree for the model page editor mode. */ 232 private CmsTree<CmsModelPageTreeItem> m_modelPageTree; 233 234 /** The tree items for the model page mode, indexed by structure id. */ 235 private Map<CmsUUID, CmsModelPageTreeItem> m_modelPageTreeItems = new HashMap<CmsUUID, CmsModelPageTreeItem>(); 236 237 /** Flag to indicate that the tree needs to be refreshed. */ 238 private boolean m_needTreeRefresh; 239 240 /** Label to display to no galleries in this sub site message. */ 241 private Label m_noGalleriesLabel; 242 243 /** The tree open handler. */ 244 private TreeOpenHandler m_openHandler; 245 246 /** The page. */ 247 private FlowPanel m_page; 248 249 /** The parent model page root. */ 250 private CmsModelPageTreeItem m_parentModelPageRoot; 251 252 /** The parent model page entries. */ 253 private Map<CmsUUID, CmsModelPageTreeItem> m_parentModelPageTreeItems = new HashMap<CmsUUID, CmsModelPageTreeItem>(); 254 255 /** The sitemap tree root item. */ 256 private CmsSitemapTreeItem m_rootItem; 257 258 /** The sitemap toolbar. */ 259 private CmsSitemapToolbar m_toolbar; 260 261 /** The registered tree items. */ 262 private Map<CmsUUID, CmsSitemapTreeItem> m_treeItems; 263 264 /** 265 * Returns the instance.<p> 266 * 267 * @return the instance 268 */ 269 public static CmsSitemapView getInstance() { 270 271 return m_instance; 272 } 273 274 /** 275 * Creates a new tree item from the given sitemap entry.<p> 276 * 277 * @param entry the sitemap entry 278 * 279 * @return the new created (still orphan) tree item 280 */ 281 public CmsSitemapTreeItem create(CmsClientSitemapEntry entry) { 282 283 CmsSitemapTreeItem treeItem = new CmsSitemapTreeItem(entry); 284 CmsSitemapHoverbar.installOn(m_controller, treeItem, entry.getId()); 285 // highlight the open path 286 if (isLastPage(entry)) { 287 treeItem.setBackgroundColor(Background.YELLOW); 288 } 289 m_treeItems.put(entry.getId(), treeItem); 290 return treeItem; 291 } 292 293 /** 294 * Creates a sitemap tree item from a client sitemap entry.<p> 295 * 296 * @param entry the entry from which the sitemap tree item should be created 297 * 298 * @return the new sitemap tree item 299 */ 300 public CmsSitemapTreeItem createSitemapItem(CmsClientSitemapEntry entry) { 301 302 CmsSitemapTreeItem result = create(entry); 303 result.clearChildren(); 304 for (CmsClientSitemapEntry child : entry.getSubEntries()) { 305 CmsSitemapTreeItem childItem = createSitemapItem(child); 306 if (!entry.isSubSitemapType()) { 307 result.addChild(childItem); 308 } 309 } 310 if (entry.getChildrenLoadedInitially()) { 311 result.onFinishLoading(); 312 } 313 return result; 314 } 315 316 /** 317 * Displays the category data.<p> 318 * 319 * @param categoryData the category data 320 * @param openLocalCategories true if the local category tree should be opened 321 * @param openItemId the id of the item to open 322 */ 323 public void displayCategoryData( 324 CmsSitemapCategoryData categoryData, 325 final boolean openLocalCategories, 326 CmsUUID openItemId) { 327 328 final Set<CmsUUID> openIds = new HashSet<CmsUUID>(); 329 if (openLocalCategories) { 330 if (openItemId != null) { 331 openIds.add(openItemId); 332 } 333 for (Widget item : m_categoryTree) { 334 if (item instanceof CmsTreeItem) { 335 ((CmsTreeItem)item).visit(new Function<CmsTreeItem, Boolean>() { 336 337 public Boolean apply(CmsTreeItem input) { 338 339 if (input.isOpen() && (input instanceof CmsCategoryTreeItem)) { 340 openIds.add(((CmsCategoryTreeItem)input).getStructureId()); 341 } 342 return null; 343 } 344 }); 345 } 346 } 347 } 348 349 m_categoryTree.clear(); 350 351 Multimap<Boolean, CmsCategoryTreeEntry> entries = categoryData.getEntriesIndexedByLocality(); 352 String localLabel = Messages.get().key(Messages.GUI_CATEGORIES_LOCAL_0); 353 String nonlocalLabel = Messages.get().key(Messages.GUI_CATEGORIES_NONLOCAL_0); 354 355 String localSubtitle = Messages.get().key(Messages.GUI_CATEGORIES_LOCAL_SUBTITLE_0); 356 String nonlocalSubtitle = Messages.get().key(Messages.GUI_CATEGORIES_NONLOCAL_SUBTITLE_0); 357 358 CmsListInfoBean localRootInfo = new CmsListInfoBean(localLabel, localSubtitle, null); 359 localRootInfo.setResourceType("category"); 360 localRootInfo.setBigIconClasses(CmsCategoryBean.BIG_ICON_CLASSES); 361 final CmsTreeItem localRoot = new CmsTreeItem(true, new CmsListItemWidget(localRootInfo)); 362 363 CmsListInfoBean nonlocalRootInfo = new CmsListInfoBean(nonlocalLabel, nonlocalSubtitle, null); 364 nonlocalRootInfo.setResourceType("category"); 365 nonlocalRootInfo.setBigIconClasses(CmsCategoryBean.BIG_ICON_CLASSES); 366 final CmsTreeItem nonlocalRoot = new CmsTreeItem(true, new CmsListItemWidget(nonlocalRootInfo)); 367 368 m_categoryTree.add(nonlocalRoot); 369 m_categoryTree.add(localRoot); 370 371 for (Boolean isLocal : Arrays.asList(Boolean.FALSE, Boolean.TRUE)) { 372 for (CmsCategoryTreeEntry entry : entries.get(isLocal)) { 373 CmsTreeItem treeItem = createCategoryTreeItem(entry); 374 (isLocal.booleanValue() ? localRoot : nonlocalRoot).addChild(treeItem); 375 } 376 } 377 378 for (CmsTreeItem root : Arrays.asList(localRoot, nonlocalRoot)) { 379 final CmsTreeItem finalRoot = root; 380 381 root.visit(new Function<CmsTreeItem, Boolean>() { 382 383 @SuppressWarnings("synthetic-access") 384 public Boolean apply(CmsTreeItem input) { 385 386 CmsUUID id = null; 387 if (input == localRoot) { 388 id = CmsUUID.getNullUUID(); 389 } else if ((input instanceof CmsCategoryTreeItem) && (localRoot == finalRoot)) { 390 id = ((CmsCategoryTreeItem)input).getStructureId(); 391 392 } 393 394 if ((id != null) && m_controller.isEditable()) { 395 final CmsSitemapHoverbar hoverbar = CmsSitemapHoverbar.installOn( 396 m_controller, 397 input, 398 id, 399 false, 400 !(id.isNullUUID()), 401 new I_CmsContextMenuItemProvider() { 402 403 public List<A_CmsSitemapMenuEntry> createContextMenu(CmsSitemapHoverbar hoverbar2) { 404 405 List<A_CmsSitemapMenuEntry> result = Lists.newArrayList(); 406 407 result.add(new CmsChangeCategoryMenuEntry(hoverbar2)); 408 result.add(new CmsDeleteCategoryMenuEntry(hoverbar2)); 409 return result; 410 } 411 }); 412 if (input == localRoot) { 413 hoverbar.setAlwaysVisible(); 414 } 415 CmsPushButton newButton = new CmsPushButton(); 416 newButton.setTitle(Messages.get().key(Messages.GUI_SITEMAP_CONTEXT_MENU_CREATE_CATEGORY_0)); 417 newButton.setImageClass(I_CmsButton.ADD_SMALL); 418 newButton.setButtonStyle(ButtonStyle.FONT_ICON, null); 419 hoverbar.add(newButton); 420 newButton.addClickHandler(new ClickHandler() { 421 422 /** 423 * @see com.google.gwt.event.dom.client.ClickHandler#onClick(com.google.gwt.event.dom.client.ClickEvent) 424 */ 425 public void onClick(ClickEvent event) { 426 427 hoverbar.hide(); 428 final CmsSitemapController controller = hoverbar.getController(); 429 final CmsUUID hoverbarId = hoverbar.getId(); 430 CmsCreateCategoryMenuEntry.askForNewCategoryInfo( 431 hoverbarId, 432 new AsyncCallback<CmsCategoryTitleAndName>() { 433 434 public void onFailure(Throwable caught) { 435 436 // do nothing 437 } 438 439 public void onSuccess(CmsCategoryTitleAndName result) { 440 441 controller.createCategory(hoverbarId, result.getTitle(), result.getName()); 442 } 443 }); 444 } 445 }); 446 } 447 if (((finalRoot == localRoot) && openLocalCategories && openIds.contains(id)) 448 || (input == nonlocalRoot) 449 || (input == localRoot)) { 450 input.setOpen(true); 451 } 452 return null; 453 } 454 }); 455 } 456 } 457 458 /** 459 * Displays the gallery view.<p> 460 * 461 * @param galleries the gallery data 462 */ 463 public void displayGalleries(Map<CmsGalleryType, List<CmsGalleryFolderEntry>> galleries) { 464 465 m_galleryTree.clear(); 466 m_galleryTreeItems.clear(); 467 m_galleryTypeItems.clear(); 468 CmsUUID galleriesFolderId = null; 469 if (getRootItem().getChild(m_controller.getData().getDefaultGalleryFolder()) != null) { 470 galleriesFolderId = ((CmsSitemapTreeItem)getRootItem().getChild( 471 m_controller.getData().getDefaultGalleryFolder())).getEntryId(); 472 } 473 List<CmsGalleryType> types = new ArrayList<CmsGalleryType>(galleries.keySet()); 474 Collections.sort(types, new Comparator<CmsGalleryType>() { 475 476 public int compare(CmsGalleryType o1, CmsGalleryType o2) { 477 478 return o1.getTitle().compareTo(o2.getTitle()); 479 } 480 }); 481 m_toolbar.setGalleryTypes(types); 482 boolean hasGalleries = false; 483 for (CmsGalleryType type : types) { 484 CmsGalleryTreeItem typeItem = new CmsGalleryTreeItem(type); 485 if (m_controller.isEditable() && m_controller.getData().isGalleryManager()) { 486 CmsHoverbarCreateGalleryButton createButton = new CmsHoverbarCreateGalleryButton( 487 type.getTypeId(), 488 galleriesFolderId); 489 CmsSitemapHoverbar hoverbar = CmsSitemapHoverbar.installOn( 490 m_controller, 491 typeItem, 492 Collections.<Widget> singleton(createButton)); 493 createButton.setHoverbar(hoverbar); 494 hoverbar.setAlwaysVisible(); 495 } 496 m_galleryTypeItems.put(type.getResourceType(), typeItem); 497 addGalleryEntries(typeItem, galleries.get(type)); 498 typeItem.setOpen(true); 499 hasGalleries = true; 500 m_galleryTree.addItem(typeItem); 501 } 502 // position image and download galleries at the top 503 if (m_galleryTypeItems.containsKey(DOWNLOAD_GALLERY_TYPE)) { 504 m_galleryTree.insertItem(m_galleryTypeItems.get(DOWNLOAD_GALLERY_TYPE), 0); 505 } 506 if (m_galleryTypeItems.containsKey(IMAGE_GALLERY_TYPE)) { 507 m_galleryTree.insertItem(m_galleryTypeItems.get(IMAGE_GALLERY_TYPE), 0); 508 } 509 m_galleryTree.truncate(TM_SITEMAP, 920); 510 if (hasGalleries) { 511 m_noGalleriesLabel.getElement().getStyle().setDisplay(Display.NONE); 512 } else { 513 m_noGalleriesLabel.getElement().getStyle().clearDisplay(); 514 } 515 } 516 517 /** 518 * Displays the model page data in a tree.<p> 519 * 520 * @param modelPageData the model page data 521 */ 522 public void displayModelPages(CmsModelInfo modelPageData) { 523 524 m_modelPageTree.clear(); 525 m_modelPageTreeItems.clear(); 526 m_parentModelPageTreeItems.clear(); 527 528 if ((modelPageData.getParentModelPages() != null) && !modelPageData.getParentModelPages().isEmpty()) { 529 m_parentModelPageRoot = CmsModelPageTreeItem.createRootItem( 530 false, 531 Messages.get().key(Messages.GUI_PARENT_MODEL_PAGE_TREE_ROOT_TITLE_0), 532 ""); 533 534 for (CmsModelPageEntry parentModel : modelPageData.getParentModelPages()) { 535 if (parentModel.isDisabled()) { 536 continue; 537 } 538 CmsModelPageTreeItem treeItem = new CmsModelPageTreeItem(parentModel, false, true); 539 if (m_controller.isEditable()) { 540 CmsSitemapHoverbar.installOn( 541 m_controller, 542 treeItem, 543 parentModel.getStructureId(), 544 parentModel.getSitePath(), 545 parentModel.getSitePath() != null); 546 } 547 m_parentModelPageTreeItems.put(parentModel.getStructureId(), treeItem); 548 m_parentModelPageRoot.addChild(treeItem); 549 m_modelPageData.put(parentModel.getStructureId(), parentModel); 550 } 551 if (m_parentModelPageRoot.getChildren().getWidgetCount() > 0) { 552 m_modelPageTree.add(m_parentModelPageRoot); 553 } else { 554 m_parentModelPageRoot = null; 555 } 556 } else { 557 m_parentModelPageRoot = null; 558 } 559 m_modelPageRoot = CmsModelPageTreeItem.createRootItem( 560 false, 561 Messages.get().key(Messages.GUI_MODEL_PAGE_TREE_ROOT_TITLE_0), 562 Messages.get().key(Messages.GUI_MODEL_PAGE_TREE_ROOT_SUBTITLE_0)); 563 if (m_controller.isEditable()) { 564 CmsHoverbarCreateModelPageButton createButton = new CmsHoverbarCreateModelPageButton(false); 565 CmsSitemapHoverbar hoverbar = CmsSitemapHoverbar.installOn( 566 m_controller, 567 m_modelPageRoot, 568 Collections.<Widget> singleton(createButton)); 569 hoverbar.setAlwaysVisible(); 570 createButton.setHoverbar(hoverbar); 571 } 572 m_modelPageTree.add(m_modelPageRoot); 573 for (CmsModelPageEntry entry : modelPageData.getModelPages()) { 574 if (m_parentModelPageTreeItems.containsKey(entry.getStructureId())) { 575 CmsModelPageTreeItem item = m_parentModelPageTreeItems.get(entry.getStructureId()); 576 item.setDisabled(entry.isDisabled()); 577 } else { 578 m_modelPageData.put(entry.getStructureId(), entry); 579 CmsModelPageTreeItem treeItem = new CmsModelPageTreeItem(entry, false, false); 580 if (m_controller.isEditable()) { 581 CmsSitemapHoverbar.installOn( 582 m_controller, 583 treeItem, 584 entry.getStructureId(), 585 entry.getSitePath(), 586 entry.getSitePath() != null); 587 } 588 m_modelPageTreeItems.put(entry.getStructureId(), treeItem); 589 m_modelPageRoot.addChild(treeItem); 590 } 591 } 592 m_modelPageRoot.setOpen(true); 593 m_modelGroupRoot = CmsModelPageTreeItem.createRootItem( 594 true, 595 Messages.get().key(Messages.GUI_MODEL_GROUP_PAGE_TREE_ROOT_TITLE_0), 596 Messages.get().key(Messages.GUI_MODEL_GROUP_PAGE_TREE_ROOT_SUBTITLE_0)); 597 if (m_controller.isEditable()) { 598 CmsHoverbarCreateModelPageButton createModelGroupButton = new CmsHoverbarCreateModelPageButton(true); 599 CmsSitemapHoverbar modelGroupHoverbar = CmsSitemapHoverbar.installOn( 600 m_controller, 601 m_modelGroupRoot, 602 Collections.<Widget> singleton(createModelGroupButton)); 603 modelGroupHoverbar.setAlwaysVisible(); 604 createModelGroupButton.setHoverbar(modelGroupHoverbar); 605 } 606 m_modelPageTree.add(m_modelGroupRoot); 607 for (CmsModelPageEntry entry : modelPageData.getModelGroups()) { 608 CmsModelPageTreeItem treeItem = new CmsModelPageTreeItem(entry, true, false); 609 if (m_controller.isEditable()) { 610 CmsSitemapHoverbar.installOn( 611 m_controller, 612 treeItem, 613 entry.getStructureId(), 614 entry.getSitePath(), 615 entry.getSitePath() != null); 616 } 617 m_modelPageTreeItems.put(entry.getStructureId(), treeItem); 618 m_modelGroupRoot.addChild(treeItem); 619 } 620 m_modelGroupRoot.setOpen(true); 621 m_modelPageTree.truncate(TM_SITEMAP, 920); 622 } 623 624 /** 625 * Displays a newly created gallery folder.<p> 626 * 627 * @param galleryFolder the gallery folder 628 */ 629 public void displayNewGallery(CmsGalleryFolderEntry galleryFolder) { 630 631 String parent = CmsResource.getParentFolder(galleryFolder.getSitePath()); 632 CmsSitemapTreeItem parentItem = getTreeItem(parent); 633 if (parentItem != null) { 634 CmsUUID parentId = parentItem.getEntryId(); 635 m_controller.updateEntry(parentId); 636 } else { 637 m_controller.loadPath(parent); 638 } 639 CmsGalleryTreeItem typeItem = m_galleryTypeItems.get(galleryFolder.getResourceType()); 640 CmsGalleryTreeItem folderItem = createGalleryFolderItem(galleryFolder); 641 if ((parentItem != null) && m_galleryTreeItems.containsKey(parentItem.getEntryId())) { 642 CmsGalleryTreeItem galleryParent = m_galleryTreeItems.get(parentItem.getEntryId()); 643 galleryParent.addChild(folderItem); 644 galleryParent.setOpen(true); 645 } else { 646 typeItem.addChild(folderItem); 647 } 648 m_galleryTreeItems.put(galleryFolder.getStructureId(), folderItem); 649 typeItem.setOpen(true); 650 // in case the type item had been hidden 651 typeItem.getElement().getStyle().clearDisplay(); 652 m_noGalleriesLabel.getElement().getStyle().setDisplay(Display.NONE); 653 } 654 655 /** 656 * Adds a new model page to the model page view.<p> 657 * 658 * @param modelPageData the data for the new model page 659 * @param isModelGroup in case of a model group page 660 */ 661 public void displayNewModelPage(CmsModelPageEntry modelPageData, boolean isModelGroup) { 662 663 CmsModelPageTreeItem treeItem = new CmsModelPageTreeItem(modelPageData, isModelGroup, false); 664 CmsSitemapHoverbar.installOn( 665 m_controller, 666 treeItem, 667 modelPageData.getStructureId(), 668 modelPageData.getSitePath(), 669 modelPageData.getSitePath() != null); 670 m_modelPageTreeItems.put(modelPageData.getStructureId(), treeItem); 671 if (isModelGroup) { 672 m_modelGroupRoot.addChild(treeItem); 673 } else { 674 m_modelPageRoot.addChild(treeItem); 675 } 676 } 677 678 /** 679 * Ensures the given item is visible in the viewport.<p> 680 * 681 * @param item the item to see 682 */ 683 public void ensureVisible(CmsSitemapTreeItem item) { 684 685 // open the tree 686 CmsTreeItem ti = item.getParentItem(); 687 while (ti != null) { 688 ti.setOpen(true); 689 ti = ti.getParentItem(); 690 } 691 // scroll 692 CmsDomUtil.ensureVisible(RootPanel.getBodyElement(), item.getElement(), 200); 693 } 694 695 /** 696 * Returns the controller.<p> 697 * 698 * @return the controller 699 */ 700 public CmsSitemapController getController() { 701 702 return m_controller; 703 } 704 705 /** 706 * Returns the editor mode.<p> 707 * 708 * @return the editor mode 709 */ 710 public EditorMode getEditorMode() { 711 712 return m_editorMode; 713 } 714 715 /** 716 * Returns the icon class for the given entry depending on the editor mode.<p> 717 * 718 * @param entry the entry to get the icon for 719 * 720 * @return the icon CSS class 721 */ 722 public String getIconForEntry(CmsClientSitemapEntry entry) { 723 724 String result; 725 if (isNavigationMode()) { 726 if (m_controller.isDetailPage(entry.getId())) { 727 result = m_controller.getDetailPageInfo(entry.getId()).getIconClasses(); 728 } else { 729 result = entry.getNavModeIcon(); 730 } 731 } else { 732 result = entry.getVfsModeIcon(); 733 } 734 return result; 735 } 736 737 /** 738 * Gets the model page bean with the given structure id.<p> 739 * 740 * @param id a structure id 741 * 742 * @return the model page bean with the given id 743 */ 744 public CmsModelPageEntry getModelPageEntry(CmsUUID id) { 745 746 return m_modelPageData.get(id); 747 } 748 749 /** 750 * Gets the list of descendants of a path and splits it into two lists, one containing the sitemap entries whose children have 751 * already been loaded, and those whose children haven't been loaded.<p> 752 * 753 * @param path the path for which the open and closed descendants should be returned 754 * 755 * @return a pair whose first and second components are lists of open and closed descendant entries of the path, respectively 756 */ 757 public CmsPair<List<CmsClientSitemapEntry>, List<CmsClientSitemapEntry>> getOpenAndClosedDescendants(String path) { 758 759 List<CmsClientSitemapEntry> descendants = m_controller.getLoadedDescendants(path); 760 List<CmsClientSitemapEntry> openDescendants = new ArrayList<CmsClientSitemapEntry>(); 761 List<CmsClientSitemapEntry> closedDescendants = new ArrayList<CmsClientSitemapEntry>(); 762 for (CmsClientSitemapEntry entry : descendants) { 763 CmsSitemapTreeItem treeItem = getTreeItem(entry.getSitePath()); 764 List<CmsClientSitemapEntry> listToAddTo = treeItem.isLoaded() ? openDescendants : closedDescendants; 765 listToAddTo.add(entry); 766 } 767 return new CmsPair<List<CmsClientSitemapEntry>, List<CmsClientSitemapEntry>>( 768 openDescendants, 769 closedDescendants); 770 771 } 772 773 /** 774 * Gets the sitemap toolbar.<p> 775 * 776 * @return the sitemap toolbar 777 */ 778 public CmsSitemapToolbar getToolbar() { 779 780 return m_toolbar; 781 } 782 783 /** 784 * Returns the tree.<p> 785 * 786 * @return the tree 787 */ 788 public CmsLazyTree<CmsSitemapTreeItem> getTree() { 789 790 return m_tree; 791 } 792 793 /** 794 * Returns the tree entry with the given path.<p> 795 * 796 * @param entryId the id of the sitemap entry 797 * 798 * @return the tree entry with the given path, or <code>null</code> if not found 799 */ 800 public CmsSitemapTreeItem getTreeItem(CmsUUID entryId) { 801 802 return m_treeItems.get(entryId); 803 } 804 805 /** 806 * Returns the tree entry with the given path.<p> 807 * 808 * @param path the path to look for 809 * 810 * @return the tree entry with the given path, or <code>null</code> if not found 811 */ 812 public CmsSitemapTreeItem getTreeItem(String path) { 813 814 CmsSitemapData data = m_controller.getData(); 815 CmsClientSitemapEntry root = data.getRoot(); 816 String rootSitePath = root.getSitePath(); 817 String remainingPath = path.substring(rootSitePath.length()); 818 819 CmsSitemapTreeItem result = getRootItem(); 820 821 String[] names = CmsStringUtil.splitAsArray(remainingPath, "/"); 822 for (String name : names) { 823 if (CmsStringUtil.isEmptyOrWhitespaceOnly(name)) { 824 continue; 825 } 826 result = (CmsSitemapTreeItem)result.getChild(name); 827 if (result == null) { 828 return null; 829 } 830 } 831 return result; 832 833 } 834 835 /** 836 * Highlights the sitemap entry with the given path.<p> 837 * 838 * @param sitePath the sitemap path of the entry to highlight 839 */ 840 public void highlightPath(String sitePath) { 841 842 openItemsOnPath(sitePath); 843 CmsSitemapTreeItem item = getTreeItem(sitePath); 844 if (item != null) { 845 item.highlightTemporarily( 846 1500, 847 isLastPage(item.getSitemapEntry()) ? Background.YELLOW : Background.DEFAULT); 848 } 849 } 850 851 /** 852 * Returns the disabled state of the given model page entry.<p> 853 * 854 * @param id the entry id 855 * 856 * @return the disabled state 857 */ 858 public boolean isDisabledModelPageEntry(CmsUUID id) { 859 860 boolean result = false; 861 if (m_modelPageTreeItems.containsKey(id)) { 862 result = m_modelPageTreeItems.get(id).isDisabled(); 863 } else if (m_parentModelPageTreeItems.containsKey(id)) { 864 result = m_parentModelPageTreeItems.get(id).isDisabled(); 865 } 866 return result; 867 } 868 869 /** 870 * Returns if the current sitemap editor mode is galleries.<p> 871 * 872 * @return <code>true</code> if the current sitemap editor mode is galleries 873 */ 874 public boolean isGalleryMode() { 875 876 return EditorMode.galleries == m_editorMode; 877 } 878 879 /** 880 * Returns if the given entry is a model group page.<p> 881 * 882 * @param entryId the entry id 883 * 884 * @return <code>true</code> if the given entry is a model group page 885 */ 886 public boolean isModelGroupEntry(CmsUUID entryId) { 887 888 return m_modelPageTreeItems.containsKey(entryId) && m_modelPageTreeItems.get(entryId).isModelGroup(); 889 } 890 891 /** 892 * Returns if the given entry is a template model page.<p> 893 * 894 * @param entryId the entry id 895 * 896 * @return <code>true</code> if the given entry is a template model page 897 */ 898 public boolean isModelPageEntry(CmsUUID entryId) { 899 900 return m_modelPageData.containsKey(entryId); 901 } 902 903 /** 904 * Checks if the sitemap editor is in 'model page mode'.<p> 905 * 906 * @return true if we are in model page view 907 */ 908 public boolean isModelPageMode() { 909 910 return EditorMode.modelpages == m_editorMode; 911 } 912 913 /** 914 * Returns if the current sitemap editor mode is navigation.<p> 915 * 916 * @return <code>true</code> if the current sitemap editor mode is navigation 917 */ 918 public boolean isNavigationMode() { 919 920 return EditorMode.navigation == m_editorMode; 921 } 922 923 /** 924 * Returns if the given entry is a template model page inherited from the parent configuration.<p> 925 * 926 * @param entryId the entry id 927 * 928 * @return <code>true</code> if the given entry is a template model page 929 */ 930 public boolean isParentModelPageEntry(CmsUUID entryId) { 931 932 return m_parentModelPageTreeItems.containsKey(entryId); 933 } 934 935 /** 936 * Returns true if we are not currently displaying the navigation or VFS tree.<p> 937 * 938 * @return true if we are not currently in navigation or VFS mode 939 */ 940 public boolean isSpecialMode() { 941 942 return isGalleryMode() || (EditorMode.modelpages == m_editorMode); 943 } 944 945 /** 946 * Performs necessary async actions before actually setting the mode.<p> 947 * 948 * @param mode the mode 949 */ 950 public void onBeforeSetEditorMode(final EditorMode mode) { 951 952 EditorMode oldMode = m_editorMode; 953 if ((oldMode == EditorMode.categories) && ((mode == EditorMode.vfs) || (mode == EditorMode.navigation))) { 954 final CmsRpcAction<Void> action = new CmsRpcAction<Void>() { 955 956 @Override 957 public void execute() { 958 959 start(200, true); 960 getController().refreshRoot(this); 961 } 962 963 @Override 964 protected void onResponse(Void result) { 965 966 stop(false); 967 setEditorMode(mode); 968 } 969 }; 970 action.execute(); 971 } else { 972 setEditorMode(mode); 973 } 974 975 } 976 977 /** 978 * @see org.opencms.ade.sitemap.client.control.I_CmsSitemapChangeHandler#onChange(org.opencms.ade.sitemap.client.control.CmsSitemapChangeEvent) 979 */ 980 public void onChange(CmsSitemapChangeEvent changeEvent) { 981 982 CmsSitemapChange change = changeEvent.getChange(); 983 switch (change.getChangeType()) { 984 case undelete: 985 return; 986 case delete: 987 CmsSitemapTreeItem item = getTreeItem(change.getEntryId()); 988 item.getParentItem().removeChild(item); 989 break; 990 case create: 991 CmsClientSitemapEntry newEntry = m_controller.getEntryById(change.getEntryId()); 992 CmsSitemapTreeItem newItem = createSitemapItem(newEntry); 993 getTreeItem(change.getParentId()).insertChild(newItem, newEntry.getPosition()); 994 break; 995 case bumpDetailPage: 996 updateDetailPageView(m_controller.getEntryById(change.getEntryId())); 997 updateAll(m_controller.getEntryById(change.getEntryId())); 998 break; 999 case modify: 1000 if (change.hasChangedPosition() || change.hasNewParent()) { 1001 CmsClientSitemapEntry entry = m_controller.getEntryById(change.getEntryId()); 1002 CmsSitemapTreeItem moveEntry = getTreeItem(change.getEntryId()); 1003 CmsSitemapTreeItem sourceParent = (CmsSitemapTreeItem)moveEntry.getParentItem(); 1004 getTree().setAnimationEnabled(false); 1005 sourceParent.removeChild(moveEntry); 1006 CmsSitemapTreeItem destParent = change.hasNewParent() 1007 ? getTreeItem(change.getParentId()) 1008 : sourceParent; 1009 if (entry.getPosition() < destParent.getChildCount()) { 1010 destParent.insertChild(moveEntry, entry.getPosition()); 1011 } else { 1012 destParent.addChild(moveEntry); 1013 } 1014 updateAll(entry); 1015 ensureVisible(moveEntry); 1016 getTree().setAnimationEnabled(true); 1017 break; 1018 } 1019 //$FALL-THROUGH$ 1020 case remove: 1021 updateAll(m_controller.getEntryById(change.getEntryId())); 1022 break; 1023 default: 1024 } 1025 if (m_editorMode == EditorMode.galleries) { 1026 applyChangeToGalleryTree(changeEvent); 1027 } 1028 if (m_editorMode == EditorMode.modelpages) { 1029 applyChangeToModelPages(changeEvent); 1030 } 1031 } 1032 1033 /** 1034 * @see org.opencms.ade.sitemap.client.control.I_CmsSitemapLoadHandler#onLoad(org.opencms.ade.sitemap.client.control.CmsSitemapLoadEvent) 1035 */ 1036 public void onLoad(CmsSitemapLoadEvent event) { 1037 1038 CmsSitemapTreeItem target = getTreeItem(event.getEntry().getId()); 1039 if (target != null) { 1040 if (event.getEntry().isSubSitemapType()) { 1041 return; 1042 } 1043 target.getTree().setAnimationEnabled(false); 1044 target.clearChildren(); 1045 for (CmsClientSitemapEntry child : event.getEntry().getSubEntries()) { 1046 CmsSitemapTreeItem childItem = createSitemapItem(child); 1047 target.addChild(childItem); 1048 } 1049 target.onFinishLoading(); 1050 target.getTree().setAnimationEnabled(true); 1051 if (event.isSetOpen()) { 1052 target.setOpen(true); 1053 } 1054 } 1055 m_controller.recomputeProperties(); 1056 } 1057 1058 /** 1059 * @see com.google.gwt.core.client.EntryPoint#onModuleLoad() 1060 */ 1061 @Override 1062 public void onModuleLoad() { 1063 1064 super.onModuleLoad(); 1065 CmsBroadcastTimer.start(); 1066 m_instance = this; 1067 1068 initVaadin(); 1069 RootPanel rootPanel = RootPanel.get(); 1070 1071 m_editorMode = EditorMode.navigation; 1072 // init 1073 I_CmsSitemapLayoutBundle.INSTANCE.sitemapCss().ensureInjected(); 1074 I_CmsSitemapLayoutBundle.INSTANCE.clipboardCss().ensureInjected(); 1075 I_CmsSitemapLayoutBundle.INSTANCE.sitemapItemCss().ensureInjected(); 1076 I_CmsSitemapLayoutBundle.INSTANCE.propertiesCss().ensureInjected(); 1077 I_CmsImageBundle.INSTANCE.buttonCss().ensureInjected(); 1078 I_CmsLayoutBundle.INSTANCE.attributeEditorCss().ensureInjected(); 1079 1080 rootPanel.addStyleName(I_CmsSitemapLayoutBundle.INSTANCE.sitemapCss().root()); 1081 m_treeItems = new HashMap<CmsUUID, CmsSitemapTreeItem>(); 1082 // controller 1083 m_controller = new CmsSitemapController(); 1084 1085 if (m_controller.getData() == null) { 1086 CmsErrorDialog dialog = new CmsErrorDialog(Messages.get().key(Messages.GUI_ERROR_ON_SITEMAP_LOAD_0), null); 1087 dialog.center(); 1088 return; 1089 } 1090 m_controller.addChangeHandler(this); 1091 m_controller.addLoadHandler(this); 1092 1093 // toolbar 1094 m_toolbar = new CmsSitemapToolbar(m_controller); 1095 m_toolbar.setAppTitle(Messages.get().key(Messages.GUI_SITEMAP_0)); 1096 rootPanel.add(m_toolbar); 1097 CmsSitemapInfo info = m_controller.getData().getSitemapInfo(); 1098 // header 1099 CmsInfoHeader header = new CmsInfoHeader( 1100 info.getTitle(), 1101 info.getDescription(), 1102 info.getSiteHost(), 1103 info.getSiteLocale(), 1104 m_controller.getData().getRoot().getVfsModeIcon()); 1105 1106 FlowPanel headerContainer = new FlowPanel(); 1107 m_headerContainer = headerContainer; 1108 m_header = header; 1109 FlowPanel localeHeaderContainer = new FlowPanel(); 1110 headerContainer.add(m_header); 1111 headerContainer.add(localeHeaderContainer); 1112 localeHeaderContainer.getElement().setId(CmsGwtConstants.ID_LOCALE_HEADER_CONTAINER); 1113 headerContainer.addStyleName(I_CmsSitemapLayoutBundle.INSTANCE.sitemapCss().headerContainer()); 1114 1115 if (CmsStringUtil.isNotEmptyOrWhitespaceOnly(m_controller.getData().getParentSitemap())) { 1116 CmsPushButton goToParentButton = new CmsPushButton(); 1117 goToParentButton.setImageClass(I_CmsButton.EDIT_UP_SMALL); 1118 goToParentButton.setTitle(Messages.get().key(Messages.GUI_HOVERBAR_PARENT_0)); 1119 goToParentButton.setButtonStyle(ButtonStyle.FONT_ICON, null); 1120 goToParentButton.addClickHandler(new ClickHandler() { 1121 1122 /** 1123 * @see com.google.gwt.event.dom.client.ClickHandler#onClick(com.google.gwt.event.dom.client.ClickEvent) 1124 */ 1125 public void onClick(ClickEvent event) { 1126 1127 getController().gotoParentSitemap(); 1128 } 1129 }); 1130 header.addButtonTopRight(goToParentButton); 1131 } 1132 1133 rootPanel.add(headerContainer); 1134 final FlowPanel page = new FlowPanel(); 1135 m_page = page; 1136 page.setStyleName(I_CmsSitemapLayoutBundle.INSTANCE.sitemapCss().page()); 1137 page.addStyleName(I_CmsSitemapLayoutBundle.INSTANCE.generalCss().cornerAll()); 1138 rootPanel.add(page); 1139 // initial content 1140 final Label loadingLabel = new Label( 1141 org.opencms.gwt.client.Messages.get().key(org.opencms.gwt.client.Messages.GUI_LOADING_0)); 1142 page.add(loadingLabel); 1143 1144 // initialize the tree 1145 m_openHandler = new TreeOpenHandler(); 1146 m_tree = new CmsLazyTree<CmsSitemapTreeItem>(m_openHandler); 1147 1148 m_inNavigationStyle = new CmsStyleVariable(m_tree); 1149 m_inNavigationStyle.setValue(I_CmsSitemapLayoutBundle.INSTANCE.sitemapItemCss().navMode()); 1150 if (m_controller.isEditable()) { 1151 // enable drag'n drop 1152 CmsDNDHandler dndHandler = new CmsDNDHandler(new CmsSitemapDNDController(m_controller, m_toolbar)); 1153 dndHandler.addTarget(m_tree); 1154 m_tree.setDNDHandler(dndHandler); 1155 m_tree.setDropEnabled(true); 1156 m_tree.setDNDTakeAll(true); 1157 } 1158 m_tree.truncate(TM_SITEMAP, 920); 1159 m_tree.setAnimationEnabled(true); 1160 page.add(m_tree); 1161 1162 m_galleryTree = new CmsTree<CmsGalleryTreeItem>(); 1163 m_modelPageTree = new CmsTree<CmsModelPageTreeItem>(); 1164 m_galleryTree.addStyleName(I_CmsSitemapLayoutBundle.INSTANCE.sitemapItemCss().galleriesMode()); 1165 m_modelPageTree.addStyleName(I_CmsSitemapLayoutBundle.INSTANCE.sitemapItemCss().modelPageMode()); 1166 1167 m_categoryTree = new CmsTree<CmsTreeItem>(); 1168 1169 m_galleryTreeItems = new HashMap<CmsUUID, CmsGalleryTreeItem>(); 1170 m_galleryTypeItems = new HashMap<String, CmsGalleryTreeItem>(); 1171 page.add(m_galleryTree); 1172 page.add(m_modelPageTree); 1173 page.add(m_categoryTree); 1174 page.add(m_localeComparison); 1175 m_localeComparison.setVisible(false); 1176 m_localeComparison.getElement().setId(CmsGwtConstants.ID_LOCALE_COMPARISON); 1177 m_noGalleriesLabel = new Label(); 1178 m_noGalleriesLabel.setStyleName(I_CmsLayoutBundle.INSTANCE.generalCss().textMedium()); 1179 m_noGalleriesLabel.getElement().setInnerHTML(Messages.get().key(Messages.GUI_NO_GALLERIES_AVAILABLE_0)); 1180 m_noGalleriesLabel.getElement().getStyle().setDisplay(Display.NONE); 1181 page.add(m_noGalleriesLabel); 1182 1183 CmsClientSitemapEntry root = m_controller.getData().getRoot(); 1184 m_rootItem = createSitemapItem(root); 1185 m_rootItem.onFinishLoading(); 1186 m_rootItem.setOpen(true); 1187 m_tree.addItem(m_rootItem); 1188 // draw tree items 1189 Scheduler.get().scheduleDeferred(new ScheduledCommand() { 1190 1191 public void execute() { 1192 1193 initiateTreeItems(page, loadingLabel); 1194 } 1195 }); 1196 setEditorMode(m_controller.getData().getEditorMode()); 1197 CmsScriptCallbackHelper localeComparePropertyEditorCallback = new CmsScriptCallbackHelper() { 1198 1199 @Override 1200 public void run() { 1201 1202 JsArrayString args = m_arguments.cast(); 1203 String id = args.get(0); 1204 String rootId = args.get(1); 1205 CmsSitemapView.getInstance().getController().openPropertyDialogForVaadin( 1206 new CmsUUID(id), 1207 new CmsUUID(rootId)); 1208 } 1209 }; 1210 localeComparePropertyEditorCallback.installCallbackOnWindow(CmsGwtConstants.LOCALECOMPARE_EDIT_PROPERTIES); 1211 1212 } 1213 1214 /** 1215 * Removes deleted entry widget reference.<p> 1216 * 1217 * @param entry the entry being deleted 1218 */ 1219 public void removeDeleted(CmsClientSitemapEntry entry) { 1220 1221 for (CmsClientSitemapEntry child : entry.getSubEntries()) { 1222 removeDeleted(child); 1223 } 1224 m_treeItems.remove(entry.getId()); 1225 } 1226 1227 /** 1228 * Sets the editor mode.<p> 1229 * 1230 * @param editorMode the editor mode to set 1231 */ 1232 public void setEditorMode(EditorMode editorMode) { 1233 1234 if (editorMode != m_editorMode) { 1235 EditorMode oldEditorMode = m_editorMode; 1236 m_editorMode = editorMode; 1237 m_controller.setEditorModeInSession(m_editorMode); 1238 if (oldEditorMode == EditorMode.compareLocales) { 1239 // reload sitemap editor because we may need to move to a different site 1240 reloadForLocaleCompareRoot(m_editorMode); 1241 return; 1242 } 1243 switch (m_editorMode) { 1244 case galleries: 1245 m_tree.getElement().getStyle().setDisplay(Display.NONE); 1246 setGalleriesVisible(true); 1247 m_toolbar.setNewGalleryEnabled(false, ""); 1248 setCategoriesVisible(false); 1249 setModelPagesVisible(false); 1250 setHeaderVisible(true); 1251 m_localeComparison.setVisible(false); 1252 m_toolbar.setClipboardEnabled(false, Messages.get().key(Messages.GUI_TOOLBAR_CLIPBOARD_DISABLE_0)); 1253 getController().loadGalleries(); 1254 break; 1255 case navigation: 1256 m_tree.getElement().getStyle().clearDisplay(); 1257 setGalleriesVisible(false); 1258 setModelPagesVisible(false); 1259 setCategoriesVisible(false); 1260 setHeaderVisible(true); 1261 m_localeComparison.setVisible(false); 1262 m_toolbar.setNewEnabled(true, null); 1263 1264 m_inNavigationStyle.setValue(I_CmsSitemapLayoutBundle.INSTANCE.sitemapItemCss().navMode()); 1265 break; 1266 case vfs: 1267 m_tree.getElement().getStyle().clearDisplay(); 1268 setGalleriesVisible(false); 1269 setCategoriesVisible(false); 1270 setHeaderVisible(true); 1271 m_localeComparison.setVisible(false); 1272 m_toolbar.setNewEnabled(false, Messages.get().key(Messages.GUI_TOOLBAR_NEW_DISABLE_0)); 1273 m_toolbar.setClipboardEnabled(true, null); 1274 m_inNavigationStyle.setValue(I_CmsSitemapLayoutBundle.INSTANCE.sitemapItemCss().vfsMode()); 1275 setModelPagesVisible(false); 1276 break; 1277 case modelpages: 1278 m_tree.getElement().getStyle().setDisplay(Display.NONE); 1279 setGalleriesVisible(false); 1280 setCategoriesVisible(false); 1281 setModelPagesVisible(true); 1282 setHeaderVisible(true); 1283 m_localeComparison.setVisible(false); 1284 m_toolbar.setNewEnabled(false, Messages.get().key(Messages.GUI_TOOLBAR_NEW_DISABLE_0)); 1285 m_toolbar.setClipboardEnabled(false, Messages.get().key(Messages.GUI_TOOLBAR_CLIPBOARD_DISABLE_0)); 1286 getController().loadModelPages(); 1287 1288 break; 1289 case categories: 1290 m_tree.getElement().getStyle().setDisplay(Display.NONE); 1291 setGalleriesVisible(false); 1292 setModelPagesVisible(false); 1293 setCategoriesVisible(true); 1294 setHeaderVisible(true); 1295 m_localeComparison.setVisible(false); 1296 m_toolbar.setNewEnabled(false, Messages.get().key(Messages.GUI_TOOLBAR_NEW_DISABLE_0)); 1297 m_toolbar.setClipboardEnabled(false, Messages.get().key(Messages.GUI_TOOLBAR_CLIPBOARD_DISABLE_0)); 1298 getController().loadCategories(false, null); 1299 break; 1300 case compareLocales: 1301 m_tree.getElement().getStyle().setDisplay(Display.NONE); 1302 setGalleriesVisible(false); 1303 setModelPagesVisible(false); 1304 setCategoriesVisible(false); 1305 setHeaderVisible(false); 1306 m_toolbar.setNewEnabled(false, Messages.get().key(Messages.GUI_TOOLBAR_NEW_DISABLE_0)); 1307 m_toolbar.setClipboardEnabled(false, Messages.get().key(Messages.GUI_TOOLBAR_CLIPBOARD_DISABLE_0)); 1308 m_localeComparison.setVisible(true); 1309 m_controller.getData().getRoot().getId(); 1310 CmsJsUtil.callNamedFunctionWithString( 1311 CmsGwtConstants.CALLBACK_REFRESH_LOCALE_COMPARISON, 1312 "" + m_controller.getData().getRoot().getId()); 1313 break; 1314 1315 default: 1316 1317 } 1318 // check if the tree has been drawn yet 1319 getRootItem().updateEditorMode(); 1320 m_toolbar.setMode(editorMode); 1321 } 1322 } 1323 1324 /** 1325 * Sets the visibility of the normal siteamp header (the header can be hidden so that the Vaadin code can display its own header).<p> 1326 * 1327 * @param visible true if the normal header should be visible 1328 */ 1329 public void setHeaderVisible(boolean visible) { 1330 1331 String style = I_CmsSitemapLayoutBundle.INSTANCE.sitemapCss().headerContainerVaadinMode(); 1332 if (visible) { 1333 m_headerContainer.removeStyleName(style); 1334 } else { 1335 m_headerContainer.addStyleName(style); 1336 } 1337 } 1338 1339 /** 1340 * Updates the detail page view for a given changed entry.<p> 1341 * 1342 * @param entry the entry which was changed 1343 */ 1344 public void updateDetailPageView(CmsClientSitemapEntry entry) { 1345 1346 CmsDetailPageTable detailPageTable = m_controller.getDetailPageTable(); 1347 List<CmsUUID> idsToUpdate = new ArrayList<CmsUUID>(); 1348 if (m_controller.isDetailPage(entry)) { 1349 idsToUpdate.add(entry.getId()); 1350 idsToUpdate.addAll(detailPageTable.getAllIds()); 1351 } 1352 updateEntriesById(idsToUpdate); 1353 } 1354 1355 /** 1356 * Updates the entries whose id is in the given list of ids.<p> 1357 * 1358 * @param ids a list of sitemap entry ids 1359 */ 1360 public void updateEntriesById(Collection<CmsUUID> ids) { 1361 1362 Map<CmsUUID, CmsClientSitemapEntry> entries = m_controller.getEntriesById(ids); 1363 for (CmsClientSitemapEntry entry : entries.values()) { 1364 CmsSitemapTreeItem item = CmsSitemapTreeItem.getItemById(entry.getId()); 1365 item.updateEntry(entry); 1366 } 1367 } 1368 1369 /** 1370 * Updates the disabled state for the given model page.<p> 1371 * 1372 * @param entryId the model page id 1373 * @param disabled the disabled state 1374 */ 1375 public void updateModelPageDisabledState(CmsUUID entryId, boolean disabled) { 1376 1377 if (m_modelPageTreeItems.containsKey(entryId)) { 1378 m_modelPageTreeItems.get(entryId).setDisabled(disabled); 1379 } else if (m_parentModelPageTreeItems.containsKey(entryId)) { 1380 m_parentModelPageTreeItems.get(entryId).setDisabled(disabled); 1381 } 1382 } 1383 1384 /** 1385 * Makes sure corresponding sitemap entries are loaded when the gallery tree is opened.<p> 1386 * 1387 * @param parent the parent gallery tree item 1388 */ 1389 protected void ensureEntriesLoaded(CmsGalleryTreeItem parent) { 1390 1391 CmsList<? extends I_CmsListItem> children = parent.getChildren(); 1392 Set<String> parentPaths = new HashSet<String>(); 1393 for (Widget listItem : children) { 1394 CmsGalleryTreeItem treeItem = (CmsGalleryTreeItem)listItem; 1395 CmsUUID entryId = treeItem.getEntryId(); 1396 if (m_controller.getEntryById(entryId) == null) { 1397 parentPaths.add(CmsResource.getParentFolder(treeItem.getSitePath())); 1398 } 1399 } 1400 for (String parentPath : parentPaths) { 1401 m_controller.loadPath(parentPath); 1402 } 1403 } 1404 1405 /** 1406 * Gets the sitemap tree item widget which represents the root of the current sitemap.<p> 1407 * 1408 * @return the root sitemap tree item widget 1409 */ 1410 protected CmsSitemapTreeItem getRootItem() { 1411 1412 return m_rootItem; 1413 } 1414 1415 /** 1416 * Shows/hides the category view.<p> 1417 * 1418 * @param visible true if the categories should be made visible 1419 */ 1420 protected void setCategoriesVisible(boolean visible) { 1421 1422 setElementVisible(m_categoryTree, visible); 1423 1424 } 1425 1426 /** 1427 * Shows or hides the element for a widget.<p> 1428 * 1429 * @param widget the widget to show or hide 1430 * @param visible true if the widget should be shown 1431 */ 1432 protected void setElementVisible(Widget widget, boolean visible) { 1433 1434 if (visible) { 1435 widget.getElement().getStyle().clearDisplay(); 1436 } else { 1437 widget.getElement().getStyle().setDisplay(Display.NONE); 1438 } 1439 1440 } 1441 1442 /** 1443 * Shows or hides the tree for the gallery mode.<p> 1444 * 1445 * @param visible true if the tree should be shown 1446 */ 1447 protected void setGalleriesVisible(boolean visible) { 1448 1449 if (visible) { 1450 m_noGalleriesLabel.getElement().getStyle().clearDisplay(); 1451 m_galleryTree.getElement().getStyle().clearDisplay(); 1452 } else { 1453 m_galleryTree.getElement().getStyle().setDisplay(Display.NONE); 1454 m_noGalleriesLabel.getElement().getStyle().setDisplay(Display.NONE); 1455 1456 } 1457 } 1458 1459 /** 1460 * Shows or hides the tree for the model page mode.<p> 1461 * 1462 * @param visible true if the model pages should be shown 1463 */ 1464 protected void setModelPagesVisible(boolean visible) { 1465 1466 setElementVisible(m_modelPageTree, visible); 1467 } 1468 1469 /** 1470 * Recursively creates the widget tree for a given category bean.<p> 1471 * 1472 * @param entry the category bean 1473 * 1474 * @return the widget for that category bean, with widgets for its descendants attached 1475 */ 1476 CmsCategoryTreeItem createCategoryTreeItem(CmsCategoryTreeEntry entry) { 1477 1478 CmsCategoryTreeItem result = new CmsCategoryTreeItem(entry); 1479 for (CmsCategoryTreeEntry child : entry.getChildren()) { 1480 result.addChild(createCategoryTreeItem(child)); 1481 } 1482 return result; 1483 } 1484 1485 /** 1486 * Builds the tree items initially.<p> 1487 * 1488 * @param page the page 1489 * @param loadingLabel the loading label, will be removed when finished 1490 */ 1491 void initiateTreeItems(FlowPanel page, Label loadingLabel) { 1492 1493 CmsSitemapTreeItem rootItem = getRootItem(); 1494 m_controller.addPropertyUpdateHandler(new CmsStatusIconUpdateHandler()); 1495 m_controller.recomputeProperties(); 1496 rootItem.updateSitePath(); 1497 // check if editable 1498 if (!m_controller.isEditable()) { 1499 // notify user 1500 CmsNotification.get().sendSticky( 1501 CmsNotification.Type.WARNING, 1502 Messages.get().key(Messages.GUI_NO_EDIT_NOTIFICATION_1, m_controller.getData().getNoEditReason())); 1503 } 1504 String openPath = m_controller.getData().getOpenPath(); 1505 if (CmsStringUtil.isNotEmptyOrWhitespaceOnly(openPath)) { 1506 m_openHandler.setInitializing(true); 1507 openItemsOnPath(openPath); 1508 m_openHandler.setInitializing(false); 1509 } 1510 page.remove(loadingLabel); 1511 } 1512 1513 /** 1514 * Adds the gallery tree items to the parent.<p> 1515 * 1516 * @param parent the parent item 1517 * @param galleries the gallery folder entries 1518 */ 1519 private void addGalleryEntries(CmsGalleryTreeItem parent, List<CmsGalleryFolderEntry> galleries) { 1520 1521 for (CmsGalleryFolderEntry galleryFolder : galleries) { 1522 CmsGalleryTreeItem folderItem = createGalleryFolderItem(galleryFolder); 1523 parent.addChild(folderItem); 1524 m_galleryTreeItems.put(galleryFolder.getStructureId(), folderItem); 1525 addGalleryEntries(folderItem, galleryFolder.getSubGalleries()); 1526 } 1527 } 1528 1529 /** 1530 * Applies the given change to the gallery view.<p> 1531 * 1532 * @param changeEvent the change event 1533 */ 1534 private void applyChangeToGalleryTree(CmsSitemapChangeEvent changeEvent) { 1535 1536 CmsSitemapChange change = changeEvent.getChange(); 1537 switch (change.getChangeType()) { 1538 case delete: 1539 CmsGalleryTreeItem deleteItem = m_galleryTreeItems.get(change.getEntryId()); 1540 if (deleteItem != null) { 1541 deleteItem.removeFromParent(); 1542 } 1543 1544 break; 1545 1546 case undelete: 1547 case create: 1548 String typeName = m_controller.getGalleryType( 1549 Integer.valueOf(change.getNewResourceTypeId())).getResourceType(); 1550 if (typeName != null) { 1551 CmsGalleryFolderEntry galleryFolder = new CmsGalleryFolderEntry(); 1552 galleryFolder.setSitePath(change.getSitePath()); 1553 galleryFolder.setResourceType(typeName); 1554 galleryFolder.setStructureId(change.getEntryId()); 1555 galleryFolder.setOwnProperties(change.getOwnProperties()); 1556 galleryFolder.setIconClasses( 1557 m_controller.getGalleryType(Integer.valueOf(change.getNewResourceTypeId())).getBigIconClasses()); 1558 CmsGalleryTreeItem folderItem = new CmsGalleryTreeItem(galleryFolder); 1559 CmsSitemapHoverbar.installOn(m_controller, folderItem, galleryFolder.getStructureId()); 1560 m_galleryTypeItems.get(typeName).addChild(folderItem); 1561 m_galleryTreeItems.put(galleryFolder.getStructureId(), folderItem); 1562 } 1563 break; 1564 1565 case modify: 1566 CmsGalleryTreeItem changeItem = m_galleryTreeItems.get(change.getEntryId()); 1567 if (changeItem != null) { 1568 CmsListItemWidget widget = changeItem.getListItemWidget(); 1569 for (CmsPropertyModification mod : change.getPropertyChanges()) { 1570 if (mod.getName().equals(CmsClientProperty.PROPERTY_TITLE)) { 1571 widget.setTitleLabel(mod.getValue()); 1572 } 1573 } 1574 String oldPath = changeItem.getSitePath(); 1575 if ((change.getName() != null) && !oldPath.endsWith("/" + change.getName())) { 1576 String newPath = CmsResource.getParentFolder(oldPath) + change.getName() + "/"; 1577 changeItem.updateSitePath(newPath); 1578 } 1579 1580 } 1581 break; 1582 case bumpDetailPage: 1583 case clipboardOnly: 1584 case remove: 1585 default: 1586 // nothing to do 1587 } 1588 } 1589 1590 /** 1591 * Applies sitemap changes to the model page mode.<p> 1592 * 1593 * @param changeEvent the change event to apply to the model page mode 1594 */ 1595 private void applyChangeToModelPages(CmsSitemapChangeEvent changeEvent) { 1596 1597 CmsSitemapChange change = changeEvent.getChange(); 1598 switch (change.getChangeType()) { 1599 case delete: 1600 1601 CmsModelPageTreeItem deleteItem = m_modelPageTreeItems.get(change.getEntryId()); 1602 if (deleteItem != null) { 1603 deleteItem.removeFromParent(); 1604 } 1605 1606 break; 1607 1608 case undelete: 1609 case create: 1610 String typeName = m_controller.getGalleryType( 1611 Integer.valueOf(change.getNewResourceTypeId())).getResourceType(); 1612 if (typeName != null) { 1613 CmsModelPageEntry modelPage = new CmsModelPageEntry(); 1614 modelPage.setSitePath(change.getSitePath()); 1615 modelPage.setResourceType(typeName); 1616 modelPage.setStructureId(change.getEntryId()); 1617 modelPage.setOwnProperties(change.getOwnProperties()); 1618 CmsModelPageTreeItem folderItem = new CmsModelPageTreeItem(modelPage, false, false); 1619 CmsSitemapHoverbar.installOn(m_controller, folderItem, modelPage.getStructureId()); 1620 m_modelPageRoot.addChild(folderItem); 1621 m_modelPageTreeItems.put(modelPage.getStructureId(), folderItem); 1622 } 1623 break; 1624 1625 case modify: 1626 CmsModelPageTreeItem changeItem = m_modelPageTreeItems.get(change.getEntryId()); 1627 if (changeItem != null) { 1628 CmsListItemWidget widget = changeItem.getListItemWidget(); 1629 for (CmsPropertyModification mod : change.getPropertyChanges()) { 1630 if (mod.getName().equals(CmsClientProperty.PROPERTY_TITLE)) { 1631 widget.setTitleLabel(mod.getValue()); 1632 } 1633 } 1634 String oldPath = changeItem.getSitePath(); 1635 if ((change.getName() != null) && !oldPath.endsWith("/" + change.getName())) { 1636 String newPath = CmsResource.getParentFolder(oldPath) + change.getName() + "/"; 1637 changeItem.updateSitePath(newPath); 1638 } 1639 1640 } 1641 break; 1642 case bumpDetailPage: 1643 case clipboardOnly: 1644 case remove: 1645 default: 1646 // nothing to do 1647 } 1648 } 1649 1650 /** 1651 * Create a gallery folder tree item.<p> 1652 * 1653 * @param galleryFolder the gallery folder 1654 * 1655 * @return the tree item 1656 */ 1657 private CmsGalleryTreeItem createGalleryFolderItem(CmsGalleryFolderEntry galleryFolder) { 1658 1659 CmsGalleryTreeItem folderItem = new CmsGalleryTreeItem(galleryFolder); 1660 1661 CmsSitemapHoverbar.installOn( 1662 m_controller, 1663 folderItem, 1664 galleryFolder.getStructureId(), 1665 galleryFolder.getSitePath(), 1666 true); 1667 return folderItem; 1668 } 1669 1670 /** 1671 * Helper method to get all sitemap tree items from the root to a given path.<p> 1672 * 1673 * For example, if the root item has the site path '/root/', and the value of path is 1674 * '/root/a/b/', the sitemap tree items corresponding to '/root/', '/root/a/' and '/root/a/b' 1675 * will be returned (in that order).<p> 1676 * 1677 * @param path the path for which the sitemap tree items should be returned 1678 * 1679 * @return the sitemap tree items on the path 1680 */ 1681 private List<CmsSitemapTreeItem> getItemsOnPath(String path) { 1682 1683 List<CmsSitemapTreeItem> result = new ArrayList<CmsSitemapTreeItem>(); 1684 1685 CmsSitemapData data = m_controller.getData(); 1686 CmsClientSitemapEntry root = data.getRoot(); 1687 String rootSitePath = root.getSitePath(); 1688 String remainingPath = path.substring(rootSitePath.length()); 1689 1690 CmsSitemapTreeItem currentItem = getRootItem(); 1691 result.add(currentItem); 1692 1693 String[] names = CmsStringUtil.splitAsArray(remainingPath, "/"); 1694 for (String name : names) { 1695 if (currentItem == null) { 1696 break; 1697 } 1698 if (CmsStringUtil.isEmptyOrWhitespaceOnly(name)) { 1699 continue; 1700 } 1701 currentItem = (CmsSitemapTreeItem)currentItem.getChild(name); 1702 if (currentItem != null) { 1703 result.add(currentItem); 1704 } 1705 } 1706 return result; 1707 } 1708 1709 /** 1710 * Initializes the Vaadin part of the sitemap editor.<p> 1711 */ 1712 private native void initVaadin() /*-{ 1713 $wnd.initVaadin(); 1714 }-*/; 1715 1716 /** 1717 * Checks if the given entry represents the last opened page.<p> 1718 * 1719 * @param entry the entry to check 1720 * 1721 * @return <code>true</code> if the given entry is the last opened page 1722 */ 1723 private boolean isLastPage(CmsClientSitemapEntry entry) { 1724 1725 return ((entry.isInNavigation() && (entry.getId().toString().equals(m_controller.getData().getReturnCode()))) 1726 || ((entry.getDefaultFileId() != null) 1727 && entry.getDefaultFileId().toString().equals(m_controller.getData().getReturnCode()))); 1728 } 1729 1730 /** 1731 * Opens all sitemap tree items on a path, except the last one.<p> 1732 * 1733 * @param path the path for which all intermediate sitemap items should be opened 1734 */ 1735 private void openItemsOnPath(String path) { 1736 1737 List<CmsSitemapTreeItem> itemsOnPath = getItemsOnPath(path); 1738 for (CmsSitemapTreeItem item : itemsOnPath) { 1739 item.setOpen(true); 1740 } 1741 } 1742 1743 /** 1744 * Reloads the sitemap editor for the currently selected locale when leaving locale compare mode.<p> 1745 * 1746 * @param editorMode the new editor mode 1747 */ 1748 private void reloadForLocaleCompareRoot(final EditorMode editorMode) { 1749 1750 final String localeRootIdStr = CmsJsUtil.getAttributeString( 1751 CmsJsUtil.getWindow(), 1752 CmsGwtConstants.VAR_LOCALE_ROOT); 1753 CmsRpcAction<String> action = new CmsRpcAction<String>() { 1754 1755 @SuppressWarnings("synthetic-access") 1756 @Override 1757 public void execute() { 1758 1759 start(0, false); 1760 m_controller.getService().prepareReloadSitemap(new CmsUUID(localeRootIdStr), editorMode, this); 1761 } 1762 1763 @Override 1764 protected void onResponse(String result) { 1765 1766 stop(false); 1767 if (result != null) { 1768 Window.Location.assign(result); 1769 } else { 1770 Window.Location.reload(); 1771 } 1772 } 1773 }; 1774 action.execute(); 1775 1776 } 1777 1778 /** 1779 * Updates the entry and it's children's view.<p> 1780 * 1781 * @param entry the entry to update 1782 */ 1783 private void updateAll(CmsClientSitemapEntry entry) { 1784 1785 CmsSitemapTreeItem item = getTreeItem(entry.getId()); 1786 if (item != null) { 1787 item.updateEntry(entry); 1788 item.updateSitePath(entry.getSitePath()); 1789 for (CmsClientSitemapEntry child : entry.getSubEntries()) { 1790 updateAll(child); 1791 } 1792 } 1793 } 1794}