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.control; 029 030import org.opencms.ade.detailpage.CmsDetailPageInfo; 031import org.opencms.ade.sitemap.client.CmsSitemapTreeItem; 032import org.opencms.ade.sitemap.client.CmsSitemapView; 033import org.opencms.ade.sitemap.client.Messages; 034import org.opencms.ade.sitemap.client.edit.CmsLocaleComparePropertyHandler; 035import org.opencms.ade.sitemap.client.edit.CmsNavModePropertyEditor; 036import org.opencms.ade.sitemap.shared.CmsClientSitemapEntry; 037import org.opencms.ade.sitemap.shared.CmsDetailPageTable; 038import org.opencms.ade.sitemap.shared.CmsGalleryFolderEntry; 039import org.opencms.ade.sitemap.shared.CmsGalleryType; 040import org.opencms.ade.sitemap.shared.CmsLocaleComparePropertyData; 041import org.opencms.ade.sitemap.shared.CmsModelInfo; 042import org.opencms.ade.sitemap.shared.CmsModelPageEntry; 043import org.opencms.ade.sitemap.shared.CmsNewResourceInfo; 044import org.opencms.ade.sitemap.shared.CmsSitemapCategoryData; 045import org.opencms.ade.sitemap.shared.CmsSitemapChange; 046import org.opencms.ade.sitemap.shared.CmsSitemapChange.ChangeType; 047import org.opencms.ade.sitemap.shared.CmsSitemapClipboardData; 048import org.opencms.ade.sitemap.shared.CmsSitemapData; 049import org.opencms.ade.sitemap.shared.CmsSitemapData.EditorMode; 050import org.opencms.ade.sitemap.shared.I_CmsSitemapController; 051import org.opencms.ade.sitemap.shared.rpc.I_CmsSitemapService; 052import org.opencms.ade.sitemap.shared.rpc.I_CmsSitemapServiceAsync; 053import org.opencms.file.CmsResource; 054import org.opencms.gwt.client.CmsCoreProvider; 055import org.opencms.gwt.client.property.A_CmsPropertyEditor; 056import org.opencms.gwt.client.property.CmsPropertySubmitHandler; 057import org.opencms.gwt.client.property.CmsReloadMode; 058import org.opencms.gwt.client.property.definition.CmsPropertyDefinitionButton; 059import org.opencms.gwt.client.rpc.CmsRpcAction; 060import org.opencms.gwt.client.rpc.CmsRpcPrefetcher; 061import org.opencms.gwt.client.ui.CmsDeleteWarningDialog; 062import org.opencms.gwt.client.ui.CmsErrorDialog; 063import org.opencms.gwt.client.ui.input.form.CmsDialogFormHandler; 064import org.opencms.gwt.client.ui.input.form.CmsFormDialog; 065import org.opencms.gwt.client.ui.input.form.I_CmsFormSubmitHandler; 066import org.opencms.gwt.client.ui.tree.CmsLazyTreeItem.LoadState; 067import org.opencms.gwt.client.util.CmsDebugLog; 068import org.opencms.gwt.client.util.I_CmsSimpleCallback; 069import org.opencms.gwt.shared.CmsCoreData; 070import org.opencms.gwt.shared.CmsListInfoBean; 071import org.opencms.gwt.shared.property.CmsClientProperty; 072import org.opencms.gwt.shared.property.CmsPropertyModification; 073import org.opencms.gwt.shared.rpc.I_CmsVfsServiceAsync; 074import org.opencms.util.CmsStringUtil; 075import org.opencms.util.CmsUUID; 076import org.opencms.xml.content.CmsXmlContentProperty; 077 078import java.util.ArrayList; 079import java.util.Collection; 080import java.util.Collections; 081import java.util.HashMap; 082import java.util.HashSet; 083import java.util.LinkedHashMap; 084import java.util.LinkedList; 085import java.util.List; 086import java.util.Map; 087import java.util.Set; 088 089import com.google.common.collect.Maps; 090import com.google.gwt.core.client.GWT; 091import com.google.gwt.dom.client.Style; 092import com.google.gwt.event.shared.HandlerRegistration; 093import com.google.gwt.event.shared.SimpleEventBus; 094import com.google.gwt.user.client.Command; 095import com.google.gwt.user.client.DOM; 096import com.google.gwt.user.client.Window; 097import com.google.gwt.user.client.rpc.AsyncCallback; 098import com.google.gwt.user.client.rpc.SerializationException; 099import com.google.gwt.user.client.rpc.ServiceDefTarget; 100 101/** 102 * Sitemap editor controller.<p> 103 * 104 * @since 8.0.0 105 */ 106public class CmsSitemapController implements I_CmsSitemapController { 107 108 /** The name to use for new entries. */ 109 public static final String NEW_ENTRY_NAME = "page"; 110 111 /** A map of *all* detail page info beans, indexed by page id. */ 112 protected Map<CmsUUID, CmsDetailPageInfo> m_allDetailPageInfos = new HashMap<CmsUUID, CmsDetailPageInfo>(); 113 114 /** The sitemap data. */ 115 protected CmsSitemapData m_data; 116 117 /** The detail page table. */ 118 protected CmsDetailPageTable m_detailPageTable; 119 120 /** The event bus. */ 121 protected SimpleEventBus m_eventBus; 122 123 /** The category data. */ 124 CmsSitemapCategoryData m_categoryData; 125 126 /** The entry data model. */ 127 private Map<CmsUUID, CmsClientSitemapEntry> m_entriesById; 128 129 /** The sitemap entries by path. */ 130 private Map<String, CmsClientSitemapEntry> m_entriesByPath; 131 132 /** The gallery type names by id. */ 133 private Map<Integer, CmsGalleryType> m_galleryTypes; 134 135 /** The set of names of hidden properties. */ 136 private Set<String> m_hiddenProperties; 137 138 /** The map of property maps by structure id. */ 139 private Map<CmsUUID, Map<String, CmsClientProperty>> m_propertyMaps = Maps.newHashMap(); 140 141 /** The list of property update handlers. */ 142 private List<I_CmsPropertyUpdateHandler> m_propertyUpdateHandlers = new ArrayList<I_CmsPropertyUpdateHandler>(); 143 144 /** The sitemap service instance. */ 145 private I_CmsSitemapServiceAsync m_service; 146 147 /** The vfs service. */ 148 private I_CmsVfsServiceAsync m_vfsService; 149 150 /** 151 * Constructor.<p> 152 */ 153 public CmsSitemapController() { 154 155 m_entriesById = new HashMap<CmsUUID, CmsClientSitemapEntry>(); 156 m_entriesByPath = new HashMap<String, CmsClientSitemapEntry>(); 157 m_galleryTypes = new HashMap<Integer, CmsGalleryType>(); 158 try { 159 m_data = (CmsSitemapData)CmsRpcPrefetcher.getSerializedObjectFromDictionary( 160 getService(), 161 CmsSitemapData.DICT_NAME); 162 } catch (SerializationException e) { 163 CmsErrorDialog.handleException( 164 new Exception( 165 "Deserialization of sitemap data failed. This may be caused by expired java-script resources, please clear your browser cache and try again.", 166 e)); 167 } 168 169 m_hiddenProperties = new HashSet<String>(); 170 if (m_data != null) { 171 m_detailPageTable = m_data.getDetailPageTable(); 172 m_data.getRoot().initializeAll(this); 173 m_eventBus = new SimpleEventBus(); 174 initDetailPageInfos(); 175 } 176 } 177 178 /** 179 * Opens the property editor for locale compare mode.<p> 180 * 181 * @param data the data used by the property editor 182 * @param ownId the id 183 * @param defaultFileId the default file id 184 * @param noEditReason the reason the properties can't be edited 185 */ 186 public static void editPropertiesForLocaleCompareMode( 187 final CmsLocaleComparePropertyData data, 188 final CmsUUID ownId, 189 final CmsUUID defaultFileId, 190 final String noEditReason) { 191 192 final CmsUUID infoId; 193 infoId = defaultFileId; 194 Set<CmsUUID> idsForPropertyConfig = new HashSet<CmsUUID>(); 195 idsForPropertyConfig.add(defaultFileId); 196 idsForPropertyConfig.add(ownId); 197 198 final List<CmsUUID> propertyConfigIds = new ArrayList<CmsUUID>(idsForPropertyConfig); 199 200 CmsRpcAction<CmsListInfoBean> action = new CmsRpcAction<CmsListInfoBean>() { 201 202 @Override 203 public void execute() { 204 205 start(0, true); 206 CmsCoreProvider.getVfsService().getPageInfo(infoId, this); 207 } 208 209 @Override 210 protected void onResponse(CmsListInfoBean infoResult) { 211 212 stop(false); 213 final CmsLocaleComparePropertyHandler handler = new CmsLocaleComparePropertyHandler(data); 214 handler.setPageInfo(infoResult); 215 216 CmsRpcAction<Map<CmsUUID, Map<String, CmsXmlContentProperty>>> propertyAction = new CmsRpcAction<Map<CmsUUID, Map<String, CmsXmlContentProperty>>>() { 217 218 @Override 219 public void execute() { 220 221 start(0, true); 222 CmsCoreProvider.getVfsService().getDefaultProperties(propertyConfigIds, this); 223 } 224 225 @Override 226 protected void onResponse(Map<CmsUUID, Map<String, CmsXmlContentProperty>> propertyResult) { 227 228 stop(false); 229 Map<String, CmsXmlContentProperty> propConfig = new LinkedHashMap<String, CmsXmlContentProperty>(); 230 for (Map<String, CmsXmlContentProperty> defaultProps : propertyResult.values()) { 231 propConfig.putAll(defaultProps); 232 } 233 propConfig.putAll(CmsSitemapView.getInstance().getController().getData().getProperties()); 234 A_CmsPropertyEditor editor = new CmsNavModePropertyEditor(propConfig, handler); 235 editor.setPropertyNames( 236 CmsSitemapView.getInstance().getController().getData().getAllPropertyNames()); 237 final CmsFormDialog dialog = new CmsFormDialog(handler.getDialogTitle(), editor.getForm()); 238 CmsPropertyDefinitionButton defButton = new CmsPropertyDefinitionButton() { 239 240 /** 241 * @see org.opencms.gwt.client.property.definition.CmsPropertyDefinitionButton#onBeforeEditPropertyDefinition() 242 */ 243 @Override 244 public void onBeforeEditPropertyDefinition() { 245 246 dialog.hide(); 247 } 248 249 }; 250 defButton.getElement().getStyle().setFloat(Style.Float.LEFT); 251 defButton.installOnDialog(dialog); 252 CmsDialogFormHandler formHandler = new CmsDialogFormHandler(); 253 formHandler.setDialog(dialog); 254 I_CmsFormSubmitHandler submitHandler = new CmsPropertySubmitHandler(handler); 255 formHandler.setSubmitHandler(submitHandler); 256 dialog.setFormHandler(formHandler); 257 editor.initializeWidgets(dialog); 258 dialog.centerHorizontally(50); 259 dialog.catchNotifications(); 260 if (noEditReason != null) { 261 editor.disableInput(noEditReason, false); 262 dialog.getOkButton().disable(noEditReason); 263 } 264 265 } 266 }; 267 propertyAction.execute(); 268 } 269 270 }; 271 action.execute(); 272 } 273 274 /** 275 * Helper method for looking up a value in a map which may be null.<p> 276 * 277 * @param <A> the key type 278 * @param <B> the value type 279 * @param map the map (which may be null) 280 * @param key the map key 281 * 282 * @return the value of the map at the given key, or null if the map is null 283 */ 284 public static <A, B> B safeLookup(Map<A, B> map, A key) { 285 286 if (map == null) { 287 return null; 288 } 289 return map.get(key); 290 } 291 292 /** 293 * Adds a new change event handler.<p> 294 * 295 * @param handler the handler to add 296 * 297 * @return the handler registration 298 */ 299 public HandlerRegistration addChangeHandler(I_CmsSitemapChangeHandler handler) { 300 301 return m_eventBus.addHandlerToSource(CmsSitemapChangeEvent.getType(), this, handler); 302 } 303 304 /** 305 * Adds a new detail page information bean.<p> 306 * 307 * @param info the detail page information bean to add 308 */ 309 public void addDetailPageInfo(CmsDetailPageInfo info) { 310 311 m_detailPageTable.add(info); 312 m_allDetailPageInfos.put(info.getId(), info); 313 } 314 315 /** 316 * Adds a new load event handler.<p> 317 * 318 * @param handler the handler to add 319 * 320 * @return the handler registration 321 */ 322 public HandlerRegistration addLoadHandler(I_CmsSitemapLoadHandler handler) { 323 324 return m_eventBus.addHandlerToSource(CmsSitemapLoadEvent.getType(), this, handler); 325 } 326 327 /** 328 * Adds a handler for property changes caused by user edits.<p> 329 * 330 * @param handler a new handler for property updates caused by the user 331 */ 332 public void addPropertyUpdateHandler(I_CmsPropertyUpdateHandler handler) { 333 334 m_propertyUpdateHandlers.add(handler); 335 336 } 337 338 /** 339 * Adds the entry to the navigation.<p> 340 * 341 * @param entry the entry 342 */ 343 public void addToNavigation(CmsClientSitemapEntry entry) { 344 345 entry.setInNavigation(true); 346 CmsSitemapChange change = new CmsSitemapChange(entry.getId(), entry.getSitePath(), ChangeType.modify); 347 CmsPropertyModification mod = new CmsPropertyModification( 348 entry.getId(), 349 CmsClientProperty.PROPERTY_NAVTEXT, 350 entry.getTitle(), 351 true); 352 change.setPropertyChanges(Collections.singletonList(mod)); 353 change.setPosition(entry.getPosition()); 354 commitChange(change, null); 355 } 356 357 /** 358 * Makes the given sitemap entry the default detail page for its detail page type.<p> 359 * 360 * @param entry an entry representing a detail page 361 */ 362 public void bump(CmsClientSitemapEntry entry) { 363 364 CmsDetailPageTable table = getDetailPageTable().copy(); 365 table.makeDefault(entry.getId()); 366 CmsSitemapChange change = new CmsSitemapChange(entry.getId(), entry.getSitePath(), ChangeType.bumpDetailPage); 367 change.setDetailPageInfos(table.toList()); 368 commitChange(change, null); 369 } 370 371 /** 372 * Changes the given category.<p> 373 * 374 * @param id the category id 375 * @param title the new category title 376 * @param name the new category name 377 */ 378 public void changeCategory(final CmsUUID id, final String title, final String name) { 379 380 CmsRpcAction<Void> action = new CmsRpcAction<Void>() { 381 382 @Override 383 public void execute() { 384 385 start(200, true); 386 getService().changeCategory(getEntryPoint(), id, title, name, this); 387 } 388 389 @Override 390 protected void onResponse(Void result) { 391 392 stop(false); 393 loadCategories(true, null); 394 } 395 396 }; 397 action.execute(); 398 } 399 400 /** 401 * Clears the deleted clip-board list and commits the change.<p> 402 */ 403 public void clearDeletedList() { 404 405 CmsSitemapClipboardData clipboardData = getData().getClipboardData().copy(); 406 clipboardData.getDeletions().clear(); 407 CmsSitemapChange change = new CmsSitemapChange(null, null, ChangeType.clipboardOnly); 408 change.setClipBoardData(clipboardData); 409 commitChange(change, null); 410 } 411 412 /** 413 * Clears the modified clip-board list and commits the change.<p> 414 */ 415 public void clearModifiedList() { 416 417 CmsSitemapClipboardData clipboardData = getData().getClipboardData().copy(); 418 clipboardData.getModifications().clear(); 419 CmsSitemapChange change = new CmsSitemapChange(null, null, ChangeType.clipboardOnly); 420 change.setClipBoardData(clipboardData); 421 commitChange(change, null); 422 } 423 424 /** 425 * Registers a new sitemap entry.<p> 426 * 427 * @param newEntry the new entry 428 * @param parentId the parent entry id 429 * @param resourceTypeId the resource type id 430 * @param copyResourceId the copy resource id 431 * @param parameter an additional parameter which may contain more information needed to create the new resource 432 * @param isNewSitemap a flag controlling whether a new sitemap should be created 433 */ 434 public void create( 435 CmsClientSitemapEntry newEntry, 436 CmsUUID parentId, 437 int resourceTypeId, 438 CmsUUID copyResourceId, 439 String parameter, 440 boolean isNewSitemap) { 441 442 assert (getEntry(newEntry.getSitePath()) == null); 443 444 CmsSitemapChange change = new CmsSitemapChange(null, newEntry.getSitePath(), ChangeType.create); 445 change.setDefaultFileId(newEntry.getDefaultFileId()); 446 change.setParentId(parentId); 447 change.setName(newEntry.getName()); 448 change.setPosition(newEntry.getPosition()); 449 change.setOwnInternalProperties(newEntry.getOwnProperties()); 450 change.setDefaultFileInternalProperties(newEntry.getDefaultFileProperties()); 451 change.setTitle(newEntry.getTitle()); 452 change.setCreateParameter(parameter); 453 change.setNewResourceTypeId(resourceTypeId); 454 if (isNewSitemap) { 455 change.setCreateSitemapFolderType(newEntry.getResourceTypeName()); 456 } 457 change.setNewCopyResourceId(copyResourceId); 458 if (isDetailPage(newEntry)) { 459 CmsDetailPageTable table = getDetailPageTable().copy(); 460 if (!table.contains(newEntry.getId())) { 461 CmsDetailPageInfo info = new CmsDetailPageInfo( 462 newEntry.getId(), 463 newEntry.getSitePath(), 464 newEntry.getDetailpageTypeName(), 465 /* qualifier = */null, 466 newEntry.getVfsModeIcon()); 467 table.add(info); 468 } 469 change.setDetailPageInfos(table.toList()); 470 } 471 CmsSitemapClipboardData data = getData().getClipboardData().copy(); 472 data.addModified(newEntry); 473 change.setClipBoardData(data); 474 commitChange(change, null); 475 } 476 477 /** 478 * Creates a new category.<p> 479 * 480 * @param id the parent category id, or the null uuid for a new top-level category 481 * @param title the title of the category 482 * @param name the name of the category 483 */ 484 public void createCategory(final CmsUUID id, final String title, final String name) { 485 486 CmsRpcAction<Void> action = new CmsRpcAction<Void>() { 487 488 @Override 489 public void execute() { 490 491 start(200, true); 492 getService().createCategory(getEntryPoint(), id, title, name, this); 493 } 494 495 @Override 496 protected void onResponse(Void result) { 497 498 stop(false); 499 loadCategories(true, id); 500 501 } 502 }; 503 action.execute(); 504 } 505 506 /** 507 * Creates a new gallery folder of the given type.<p> 508 * 509 * @param parentId the parent folder id 510 * @param galleryTypeId the folder type id 511 * @param title the folder title 512 */ 513 public void createNewGallery(final CmsUUID parentId, final int galleryTypeId, final String title) { 514 515 final String parentFolder = parentId != null 516 ? getEntryById(parentId).getSitePath() 517 : CmsStringUtil.joinPaths(m_data.getRoot().getSitePath(), m_data.getDefaultGalleryFolder()); 518 519 CmsRpcAction<CmsGalleryFolderEntry> action = new CmsRpcAction<CmsGalleryFolderEntry>() { 520 521 @Override 522 public void execute() { 523 524 getService().createNewGalleryFolder(parentFolder, title, galleryTypeId, this); 525 } 526 527 @Override 528 protected void onResponse(CmsGalleryFolderEntry result) { 529 530 CmsSitemapView.getInstance().displayNewGallery(result); 531 532 } 533 }; 534 action.execute(); 535 } 536 537 /** 538 * Creates a new model page.<p> 539 * 540 * @param title the title of the model page 541 * @param description the description of the model page 542 * @param copyId the structure id of the resource which should be used as a copy model for the new page 543 * @param isModelGroup in case of a model group page 544 */ 545 public void createNewModelPage( 546 final String title, 547 final String description, 548 final CmsUUID copyId, 549 final boolean isModelGroup) { 550 551 CmsRpcAction<CmsModelPageEntry> action = new CmsRpcAction<CmsModelPageEntry>() { 552 553 @Override 554 public void execute() { 555 556 start(200, true); 557 558 getService().createNewModelPage(getEntryPoint(), title, description, copyId, isModelGroup, this); 559 560 } 561 562 @Override 563 protected void onResponse(final CmsModelPageEntry result) { 564 565 stop(false); 566 if (isModelGroup) { 567 CmsSitemapView.getInstance().displayNewModelPage(result, true); 568 } else { 569 loadNewElementInfo(new AsyncCallback<Void>() { 570 571 public void onFailure(Throwable caught) { 572 573 // nothing to do 574 575 } 576 577 public void onSuccess(Void v) { 578 579 CmsSitemapView.getInstance().displayNewModelPage(result, false); 580 } 581 }); 582 } 583 584 } 585 586 }; 587 action.execute(); 588 } 589 590 /** 591 * Creates a sitemap folder.<p> 592 * 593 * @param newEntry the new entry 594 * @param parentId the entry parent id 595 * @param sitemapType the resource type for the subsitemap folder 596 */ 597 public void createSitemapSubEntry(final CmsClientSitemapEntry newEntry, CmsUUID parentId, String sitemapType) { 598 599 CmsUUID structureId = m_data.getDefaultNewElementInfo().getCopyResourceId(); 600 newEntry.setResourceTypeName(sitemapType); 601 create(newEntry, parentId, m_data.getDefaultNewElementInfo().getId(), structureId, null, true); 602 } 603 604 /** 605 * Creates a new sub-entry which is a subsitemap.<p> 606 * 607 * @param parent the parent entry 608 * @param sitemapFolderType the sitemap folder type 609 */ 610 public void createSitemapSubEntry(final CmsClientSitemapEntry parent, final String sitemapFolderType) { 611 612 CmsSitemapTreeItem item = CmsSitemapTreeItem.getItemById(parent.getId()); 613 AsyncCallback<CmsClientSitemapEntry> callback = new AsyncCallback<CmsClientSitemapEntry>() { 614 615 public void onFailure(Throwable caught) { 616 617 // do nothing 618 } 619 620 public void onSuccess(CmsClientSitemapEntry result) { 621 622 makeNewEntry(parent, new I_CmsSimpleCallback<CmsClientSitemapEntry>() { 623 624 public void execute(CmsClientSitemapEntry newEntry) { 625 626 newEntry.setResourceTypeName(sitemapFolderType); 627 createSitemapSubEntry(newEntry, parent.getId(), sitemapFolderType); 628 } 629 }); 630 } 631 }; 632 if (item.getLoadState().equals(LoadState.UNLOADED)) { 633 getChildren(parent.getId(), true, callback); 634 } else { 635 callback.onSuccess(parent); 636 } 637 } 638 639 /** 640 * Creates a new sub-entry of an existing sitemap entry.<p> 641 * 642 * @param parent the entry to which a new sub-entry should be added 643 */ 644 public void createSubEntry(final CmsClientSitemapEntry parent) { 645 646 createSubEntry(parent, null); 647 } 648 649 /** 650 * Creates a new sub-entry of an existing sitemap entry.<p> 651 * 652 * @param parent the entry to which a new sub-entry should be added 653 * @param structureId the structure id of the model page (if null, uses default model page) 654 */ 655 public void createSubEntry(final CmsClientSitemapEntry parent, final CmsUUID structureId) { 656 657 CmsSitemapTreeItem item = CmsSitemapTreeItem.getItemById(parent.getId()); 658 AsyncCallback<CmsClientSitemapEntry> callback = new AsyncCallback<CmsClientSitemapEntry>() { 659 660 public void onFailure(Throwable caught) { 661 662 // nothing to do 663 } 664 665 public void onSuccess(CmsClientSitemapEntry result) { 666 667 makeNewEntry(parent, new I_CmsSimpleCallback<CmsClientSitemapEntry>() { 668 669 public void execute(CmsClientSitemapEntry newEntry) { 670 671 createSubEntry(newEntry, parent.getId(), structureId); 672 } 673 }); 674 675 } 676 677 }; 678 679 if (item.getLoadState().equals(LoadState.UNLOADED)) { 680 getChildren(parent.getId(), true, callback); 681 } else { 682 callback.onSuccess(parent); 683 } 684 } 685 686 /** 687 * Registers a new sitemap entry.<p> 688 * 689 * @param newEntry the new entry 690 * @param parentId the parent entry id 691 * @param structureId the structure id of the model page (if null, uses default model page) 692 */ 693 public void createSubEntry(final CmsClientSitemapEntry newEntry, CmsUUID parentId, CmsUUID structureId) { 694 695 if (structureId == null) { 696 structureId = m_data.getDefaultNewElementInfo().getCopyResourceId(); 697 } 698 create(newEntry, parentId, m_data.getDefaultNewElementInfo().getId(), structureId, null, false); 699 } 700 701 /** 702 * Creates a sub-sitemap from the subtree of the current sitemap starting at the given entry.<p> 703 * 704 * @param entryId the id of the entry 705 */ 706 public void createSubSitemap(final CmsUUID entryId) { 707 708 CmsRpcAction<CmsSitemapChange> subSitemapAction = new CmsRpcAction<CmsSitemapChange>() { 709 710 /** 711 * @see org.opencms.gwt.client.rpc.CmsRpcAction#execute() 712 */ 713 @Override 714 public void execute() { 715 716 start(0, true); 717 getService().createSubSitemap(entryId, this); 718 } 719 720 /** 721 * @see org.opencms.gwt.client.rpc.CmsRpcAction#onResponse(java.lang.Object) 722 */ 723 @Override 724 protected void onResponse(CmsSitemapChange result) { 725 726 stop(false); 727 applyChange(result); 728 } 729 }; 730 subSitemapAction.execute(); 731 } 732 733 /** 734 * Deletes the given entry and all its descendants.<p> 735 * 736 * @param sitePath the site path of the entry to delete 737 */ 738 public void delete(String sitePath) { 739 740 CmsClientSitemapEntry entry = getEntry(sitePath); 741 CmsClientSitemapEntry parent = getEntry(CmsResource.getParentFolder(entry.getSitePath())); 742 CmsSitemapChange change = new CmsSitemapChange(entry.getId(), entry.getSitePath(), ChangeType.delete); 743 change.setParentId(parent.getId()); 744 change.setDefaultFileId(entry.getDefaultFileId()); 745 CmsSitemapClipboardData data = CmsSitemapView.getInstance().getController().getData().getClipboardData().copy(); 746 if (!entry.isNew()) { 747 data.addDeleted(entry); 748 removeDeletedFromModified(entry, data); 749 } 750 change.setClipBoardData(data); 751 CmsDetailPageTable detailPageTable = CmsSitemapView.getInstance().getController().getData().getDetailPageTable(); 752 CmsUUID id = entry.getId(); 753 if (detailPageTable.contains(id)) { 754 CmsDetailPageTable copyTable = detailPageTable.copy(); 755 copyTable.remove(id); 756 change.setDetailPageInfos(copyTable.toList()); 757 } 758 commitChange(change, null); 759 } 760 761 /** 762 * Deletes a category.<p> 763 * 764 * @param id the id of the category 765 */ 766 public void deleteCategory(final CmsUUID id) { 767 768 CmsDeleteWarningDialog deleteWarningDialog = new CmsDeleteWarningDialog(id) { 769 770 @Override 771 protected void onAfterDeletion() { 772 773 CmsSitemapView.getInstance().getController().loadCategories(true, null); 774 } 775 }; 776 deleteWarningDialog.loadAndShow(null); 777 } 778 779 /** 780 * Disables the given model page entry within the configuration.<p> 781 * 782 * @param id the entry id 783 * @param disable <code>true</code> to disable the entry 784 */ 785 public void disableModelPage(final CmsUUID id, final boolean disable) { 786 787 CmsRpcAction<Void> action = new CmsRpcAction<Void>() { 788 789 @Override 790 public void execute() { 791 792 start(200, true); 793 getService().disableModelPage(getEntryPoint(), id, disable, this); 794 795 } 796 797 @Override 798 protected void onResponse(Void result) { 799 800 stop(false); 801 CmsSitemapView.getInstance().updateModelPageDisabledState(id, disable); 802 } 803 }; 804 action.execute(); 805 806 } 807 808 /** 809 * Edits the given sitemap entry.<p> 810 * 811 * @param entry the sitemap entry to update 812 * @param propertyChanges the property changes 813 * @param reloadStatus a value indicating which entries need to be reloaded after the change 814 */ 815 public void edit( 816 CmsClientSitemapEntry entry, 817 List<CmsPropertyModification> propertyChanges, 818 final CmsReloadMode reloadStatus) { 819 820 CmsSitemapChange change = getChangeForEdit(entry, propertyChanges); 821 final String updateTarget = ((reloadStatus == CmsReloadMode.reloadParent)) 822 ? getParentEntry(entry).getSitePath() 823 : entry.getSitePath(); 824 Command callback = new Command() { 825 826 public void execute() { 827 828 if ((reloadStatus == CmsReloadMode.reloadParent) || (reloadStatus == CmsReloadMode.reloadEntry)) { 829 updateEntry(updateTarget); 830 } 831 } 832 }; 833 if (change != null) { 834 commitChange(change, callback); 835 } 836 } 837 838 /** 839 * Edits an entry and changes its URL name.<p> 840 * 841 * @param entry the entry which is being edited 842 * @param newUrlName the new URL name of the entry 843 * @param propertyChanges the property changes 844 * @param keepNewStatus <code>true</code> if the entry should keep it's new status 845 * @param reloadStatus a value indicating which entries need to be reloaded after the change 846 */ 847 public void editAndChangeName( 848 final CmsClientSitemapEntry entry, 849 String newUrlName, 850 List<CmsPropertyModification> propertyChanges, 851 final boolean keepNewStatus, 852 final CmsReloadMode reloadStatus) { 853 854 CmsSitemapChange change = getChangeForEdit(entry, propertyChanges); 855 change.setName(newUrlName); 856 final CmsUUID entryId = entry.getId(); 857 final boolean newStatus = keepNewStatus && entry.isNew(); 858 859 final CmsUUID updateTarget = ((reloadStatus == CmsReloadMode.reloadParent)) 860 ? getParentEntry(entry).getId() 861 : entryId; 862 Command callback = new Command() { 863 864 public void execute() { 865 866 if ((reloadStatus == CmsReloadMode.reloadParent) || (reloadStatus == CmsReloadMode.reloadEntry)) { 867 updateEntry(updateTarget); 868 } 869 getEntryById(entryId).setNew(newStatus); 870 } 871 872 }; 873 874 commitChange(change, callback); 875 } 876 877 /** 878 * Ensure the uniqueness of a given URL-name within the children of the given parent site-map entry.<p> 879 * 880 * @param parent the parent entry 881 * @param newName the proposed name 882 * @param callback the callback to execute 883 */ 884 public void ensureUniqueName(CmsClientSitemapEntry parent, String newName, I_CmsSimpleCallback<String> callback) { 885 886 ensureUniqueName(parent.getSitePath(), newName, callback); 887 } 888 889 /** 890 * Ensure the uniqueness of a given URL-name within the children of the given parent folder.<p> 891 * 892 * @param parentFolder the parent folder 893 * @param newName the proposed name 894 * @param callback the callback to execute 895 */ 896 public void ensureUniqueName( 897 final String parentFolder, 898 String newName, 899 final I_CmsSimpleCallback<String> callback) { 900 901 // using lower case folder names 902 final String lowerCaseName = newName.toLowerCase(); 903 CmsRpcAction<String> action = new CmsRpcAction<String>() { 904 905 /** 906 * @see org.opencms.gwt.client.rpc.CmsRpcAction#execute() 907 */ 908 @Override 909 public void execute() { 910 911 start(0, false); 912 CmsCoreProvider.getService().getUniqueFileName(parentFolder, lowerCaseName, this); 913 } 914 915 /** 916 * @see org.opencms.gwt.client.rpc.CmsRpcAction#onResponse(java.lang.Object) 917 */ 918 @Override 919 protected void onResponse(String result) { 920 921 stop(false); 922 callback.execute(result); 923 } 924 925 }; 926 action.execute(); 927 } 928 929 /** 930 * Applies the given property modification.<p> 931 * 932 * @param propMod the property modification to apply 933 */ 934 public void executePropertyModification(CmsPropertyModification propMod) { 935 936 CmsClientSitemapEntry entry = getEntryById(propMod.getId()); 937 if (entry != null) { 938 Map<String, CmsClientProperty> props = getPropertiesForId(propMod.getId()); 939 if (props != null) { 940 propMod.updatePropertyInMap(props); 941 entry.setOwnProperties(props); 942 } 943 } 944 } 945 946 /** 947 * Gets the category data.<p> 948 * 949 * @return the category data 950 */ 951 public CmsSitemapCategoryData getCategoryData() { 952 953 return m_categoryData; 954 } 955 956 /** 957 * Retrieves the child entries of the given node from the server.<p> 958 * 959 * @param entryId the entry id 960 * @param setOpen if the entry should be opened 961 * 962 * @param callback the callback to execute after the children have been loaded 963 */ 964 public void getChildren( 965 final CmsUUID entryId, 966 final boolean setOpen, 967 final AsyncCallback<CmsClientSitemapEntry> callback) { 968 969 getChildren(entryId, setOpen, false, callback); 970 971 } 972 973 /** 974 * Retrieves the child entries of the given node from the server.<p> 975 * 976 * @param entryId the entry id 977 * @param setOpen if the entry should be opened 978 * @param continueIfParentNotLoaded if false, and the entry identified by the id has not been loaded, stop; else store the entry in memory 979 * 980 * @param callback the callback to execute after the children have been loaded 981 */ 982 public void getChildren( 983 final CmsUUID entryId, 984 final boolean setOpen, 985 final boolean continueIfParentNotLoaded, 986 final AsyncCallback<CmsClientSitemapEntry> callback) { 987 988 CmsRpcAction<CmsClientSitemapEntry> getChildrenAction = new CmsRpcAction<CmsClientSitemapEntry>() { 989 990 /** 991 * @see org.opencms.gwt.client.rpc.CmsRpcAction#execute() 992 */ 993 @Override 994 public void execute() { 995 996 // Make the call to the sitemap service 997 start(500, false); 998 // loading grand children as well 999 getService().getChildren(getEntryPoint(), entryId, 2, this); 1000 } 1001 1002 /** 1003 * @see org.opencms.gwt.client.rpc.CmsRpcAction#onResponse(java.lang.Object) 1004 */ 1005 @Override 1006 public void onResponse(CmsClientSitemapEntry result) { 1007 1008 CmsClientSitemapEntry target = getEntryById(entryId); 1009 if ((target == null) && !continueIfParentNotLoaded) { 1010 // this might happen after an automated deletion 1011 stop(false); 1012 return; 1013 } 1014 CmsSitemapTreeItem item = null; 1015 if (target != null) { 1016 target.setSubEntries(result.getSubEntries(), CmsSitemapController.this); 1017 item = CmsSitemapTreeItem.getItemById(target.getId()); 1018 target.update(result); 1019 } else { 1020 target = result; 1021 } 1022 1023 target.initializeAll(CmsSitemapController.this); 1024 if (item != null) { 1025 1026 item.updateEntry(target); 1027 } 1028 m_eventBus.fireEventFromSource(new CmsSitemapLoadEvent(target, setOpen), CmsSitemapController.this); 1029 stop(false); 1030 if (callback != null) { 1031 callback.onSuccess(result); 1032 } 1033 } 1034 }; 1035 getChildrenAction.execute(); 1036 } 1037 1038 /** 1039 * Returns the sitemap data.<p> 1040 * 1041 * @return the sitemap data 1042 */ 1043 public CmsSitemapData getData() { 1044 1045 return m_data; 1046 } 1047 1048 /** 1049 * Returns the detail page info for a given entry id.<p> 1050 * 1051 * @param id a sitemap entry id 1052 * 1053 * @return the detail page info for that id 1054 */ 1055 public CmsDetailPageInfo getDetailPageInfo(CmsUUID id) { 1056 1057 return m_allDetailPageInfos.get(id); 1058 } 1059 1060 /** 1061 * Returns the detail page table.<p> 1062 * 1063 * @return the detail page table 1064 */ 1065 public CmsDetailPageTable getDetailPageTable() { 1066 1067 return m_detailPageTable; 1068 } 1069 1070 /** 1071 * Gets the effective value of a property value for a sitemap entry.<p> 1072 * 1073 * @param entry the sitemap entry 1074 * @param name the name of the property 1075 * 1076 * @return the effective value 1077 */ 1078 public String getEffectiveProperty(CmsClientSitemapEntry entry, String name) { 1079 1080 CmsClientProperty prop = getEffectivePropertyObject(entry, name); 1081 if (prop == null) { 1082 return null; 1083 } 1084 return prop.getEffectiveValue(); 1085 } 1086 1087 /** 1088 * Gets the value of a property which is effective at a given sitemap entry.<p> 1089 * 1090 * @param entry the sitemap entry 1091 * @param name the name of the property 1092 * @return the effective property value 1093 */ 1094 public CmsClientProperty getEffectivePropertyObject(CmsClientSitemapEntry entry, String name) { 1095 1096 Map<String, CmsClientProperty> dfProps = entry.getDefaultFileProperties(); 1097 CmsClientProperty result = safeLookup(dfProps, name); 1098 if (!CmsClientProperty.isPropertyEmpty(result)) { 1099 return result.withOrigin(entry.getSitePath()); 1100 } 1101 result = safeLookup(entry.getOwnProperties(), name); 1102 if (!CmsClientProperty.isPropertyEmpty(result)) { 1103 return result.withOrigin(entry.getSitePath()); 1104 } 1105 return getInheritedPropertyObject(entry, name); 1106 } 1107 1108 /** 1109 * Returns all entries with an id from a given list.<p> 1110 * 1111 * @param ids a list of sitemap entry ids 1112 * 1113 * @return all entries whose id is contained in the id list 1114 */ 1115 public Map<CmsUUID, CmsClientSitemapEntry> getEntriesById(Collection<CmsUUID> ids) { 1116 1117 // TODO: use some map of id -> entry instead 1118 List<CmsClientSitemapEntry> entriesToProcess = new ArrayList<CmsClientSitemapEntry>(); 1119 Map<CmsUUID, CmsClientSitemapEntry> result = new HashMap<CmsUUID, CmsClientSitemapEntry>(); 1120 1121 entriesToProcess.add(m_data.getRoot()); 1122 while (!entriesToProcess.isEmpty()) { 1123 CmsClientSitemapEntry entry = entriesToProcess.remove(entriesToProcess.size() - 1); 1124 if (ids.contains(entry.getId())) { 1125 result.put(entry.getId(), entry); 1126 if (result.size() == ids.size()) { 1127 return result; 1128 } 1129 } 1130 entriesToProcess.addAll(entry.getSubEntries()); 1131 } 1132 return result; 1133 } 1134 1135 /** 1136 * Returns the tree entry with the given path.<p> 1137 * 1138 * @param entryPath the path to look for 1139 * 1140 * @return the tree entry with the given path, or <code>null</code> if not found 1141 */ 1142 public CmsClientSitemapEntry getEntry(String entryPath) { 1143 1144 return m_entriesByPath.get(entryPath); 1145 } 1146 1147 /** 1148 * Finds an entry by id.<p> 1149 * 1150 * @param id the id of the entry to find 1151 * 1152 * @return the found entry, or null if the entry wasn't found 1153 */ 1154 public CmsClientSitemapEntry getEntryById(CmsUUID id) { 1155 1156 return m_entriesById.get(id); 1157 } 1158 1159 /** 1160 * Returns the gallery type with the given id.<p> 1161 * 1162 * @param typeId the type id 1163 * 1164 * @return the gallery type 1165 */ 1166 public CmsGalleryType getGalleryType(Integer typeId) { 1167 1168 return m_galleryTypes.get(typeId); 1169 } 1170 1171 /** 1172 * Gets the value for a property which a sitemap entry would inherit if it didn't have its own properties.<p> 1173 * 1174 * @param entry the sitemap entry 1175 * @param name the property name 1176 * @return the inherited property value 1177 */ 1178 public String getInheritedProperty(CmsClientSitemapEntry entry, String name) { 1179 1180 CmsClientProperty prop = getInheritedPropertyObject(entry, name); 1181 if (prop == null) { 1182 return null; 1183 } 1184 return prop.getEffectiveValue(); 1185 } 1186 1187 /** 1188 * Gets the property object which would be inherited by a sitemap entry.<p> 1189 * 1190 * @param entry the sitemap entry 1191 * @param name the name of the property 1192 * @return the property object which would be inherited 1193 */ 1194 public CmsClientProperty getInheritedPropertyObject(CmsClientSitemapEntry entry, String name) { 1195 1196 CmsClientSitemapEntry currentEntry = entry; 1197 while (currentEntry != null) { 1198 currentEntry = getParentEntry(currentEntry); 1199 if (currentEntry != null) { 1200 CmsClientProperty folderProp = currentEntry.getOwnProperties().get(name); 1201 if (!CmsClientProperty.isPropertyEmpty(folderProp)) { 1202 return folderProp.withOrigin(currentEntry.getSitePath()); 1203 } 1204 } 1205 } 1206 CmsClientProperty parentProp = getParentProperties().get(name); 1207 if (!CmsClientProperty.isPropertyEmpty(parentProp)) { 1208 String origin = parentProp.getOrigin(); 1209 String siteRoot = CmsCoreProvider.get().getSiteRoot(); 1210 if (origin.startsWith(siteRoot)) { 1211 origin = origin.substring(siteRoot.length()); 1212 } 1213 return parentProp.withOrigin(origin); 1214 } 1215 return null; 1216 1217 } 1218 1219 /** 1220 * Returns a list of all descendant sitemap entries of a given path which have already been loaded on the client.<p> 1221 * 1222 * @param path the path for which the descendants should be collected 1223 * 1224 * @return the list of descendant sitemap entries 1225 */ 1226 public List<CmsClientSitemapEntry> getLoadedDescendants(String path) { 1227 1228 LinkedList<CmsClientSitemapEntry> remainingEntries = new LinkedList<CmsClientSitemapEntry>(); 1229 List<CmsClientSitemapEntry> result = new ArrayList<CmsClientSitemapEntry>(); 1230 CmsClientSitemapEntry entry = getEntry(path); 1231 remainingEntries.add(entry); 1232 while (remainingEntries.size() > 0) { 1233 CmsClientSitemapEntry currentEntry = remainingEntries.removeFirst(); 1234 result.add(currentEntry); 1235 for (CmsClientSitemapEntry subEntry : currentEntry.getSubEntries()) { 1236 remainingEntries.add(subEntry); 1237 } 1238 } 1239 1240 return result; 1241 1242 } 1243 1244 /** 1245 * Returns the no edit reason or <code>null</code> if editing is allowed.<p> 1246 * 1247 * @param entry the entry to get the no edit reason for 1248 * 1249 * @return the no edit reason 1250 */ 1251 public String getNoEditReason(CmsClientSitemapEntry entry) { 1252 1253 return getNoEditReason(entry, true); 1254 } 1255 1256 /** 1257 * Returns the no edit reason or <code>null</code> if editing is allowed.<p> 1258 * 1259 * @param entry the entry to get the no edit reason for 1260 * @param checkChildLocks true if locks of children should be checked 1261 * 1262 * @return the no edit reason 1263 */ 1264 public String getNoEditReason(CmsClientSitemapEntry entry, boolean checkChildLocks) { 1265 1266 String reason = entry.getNoEditReason(); 1267 if (CmsStringUtil.isEmptyOrWhitespaceOnly(reason)) { 1268 reason = null; 1269 if ((entry.getLock() != null) 1270 && (entry.getLock().getLockOwner() != null) 1271 && !entry.getLock().isOwnedByUser()) { 1272 reason = Messages.get().key(Messages.GUI_DISABLED_LOCKED_BY_1, entry.getLock().getLockOwner()); 1273 1274 } 1275 if (checkChildLocks && entry.hasBlockingLockedChildren()) { 1276 reason = Messages.get().key(Messages.GUI_DISABLED_BLOCKING_LOCKED_CHILDREN_0); 1277 } 1278 } 1279 return reason; 1280 } 1281 1282 /** 1283 * Returns the parent entry of a sitemap entry, or null if it is the root entry.<p> 1284 * 1285 * @param entry a sitemap entry 1286 * 1287 * @return the parent entry or null 1288 */ 1289 public CmsClientSitemapEntry getParentEntry(CmsClientSitemapEntry entry) { 1290 1291 String path = entry.getSitePath(); 1292 String parentPath = CmsResource.getParentFolder(path); 1293 if (parentPath == null) { 1294 return null; 1295 } 1296 return getEntry(parentPath); 1297 } 1298 1299 /** 1300 * Gets the properties for a given structure id.<p> 1301 * 1302 * @param id the structure id of a sitemap entry 1303 * 1304 * @return the properties for that structure id 1305 */ 1306 public Map<String, CmsClientProperty> getPropertiesForId(CmsUUID id) { 1307 1308 return m_propertyMaps.get(id); 1309 } 1310 1311 /** 1312 * Returns the sitemap service instance.<p> 1313 * 1314 * @return the sitemap service instance 1315 */ 1316 public I_CmsSitemapServiceAsync getService() { 1317 1318 if (m_service == null) { 1319 m_service = GWT.create(I_CmsSitemapService.class); 1320 String serviceUrl = CmsCoreProvider.get().link("org.opencms.ade.sitemap.CmsVfsSitemapService.gwt"); 1321 ((ServiceDefTarget)m_service).setServiceEntryPoint(serviceUrl); 1322 } 1323 return m_service; 1324 } 1325 1326 /** 1327 * Leaves the current sitemap to open the parent sitemap.<p> 1328 */ 1329 public void gotoParentSitemap() { 1330 1331 openSiteMap(getData().getParentSitemap()); 1332 } 1333 1334 /** 1335 * Hides the entry within the site navigation.<p> 1336 * 1337 * Hidden entries will still be visible in the navigation mode of the sitemap editor. 1338 * They will also have a NavText and a NavPos. Only when using the NavBuilder get navigation for folder method, 1339 * they will not be included.<p> 1340 * 1341 * @param entryId the entry id 1342 */ 1343 public void hideInNavigation(CmsUUID entryId) { 1344 1345 CmsClientSitemapEntry entry = getEntryById(entryId); 1346 CmsSitemapChange change = getChangeForEdit( 1347 entry, 1348 Collections.singletonList( 1349 new CmsPropertyModification( 1350 entryId.toString() 1351 + "/" 1352 + CmsClientProperty.PROPERTY_NAVINFO 1353 + "/" 1354 + CmsClientProperty.PATH_STRUCTURE_VALUE, 1355 CmsClientSitemapEntry.HIDDEN_NAVIGATION_ENTRY))); 1356 commitChange(change, null); 1357 } 1358 1359 /** 1360 * Checks whether this entry belongs to a detail page.<p> 1361 * 1362 * @param entry the entry to check 1363 * 1364 * @return true if this entry belongs to a detail page 1365 */ 1366 public boolean isDetailPage(CmsClientSitemapEntry entry) { 1367 1368 return (entry.getDetailpageTypeName() != null) || isDetailPage(entry.getId()); 1369 } 1370 1371 /** 1372 * Returns true if the id is the id of a detail page.<p> 1373 * 1374 * @param id the sitemap entry id 1375 * @return true if the id is the id of a detail page entry 1376 */ 1377 public boolean isDetailPage(CmsUUID id) { 1378 1379 return m_allDetailPageInfos.containsKey(id); 1380 } 1381 1382 /** 1383 * Checks if the current sitemap is editable.<p> 1384 * 1385 * @return <code>true</code> if the current sitemap is editable 1386 */ 1387 public boolean isEditable() { 1388 1389 return CmsStringUtil.isEmptyOrWhitespaceOnly(m_data.getNoEditReason()); 1390 } 1391 1392 /** 1393 * Checks whether a string is the name of a hidden property.<p> 1394 * 1395 * A hidden property is a property which should not appear in the property editor 1396 * because it requires special treatment.<p> 1397 * 1398 * @param propertyName the property name which should be checked 1399 * 1400 * @return true if the argument is the name of a hidden property 1401 */ 1402 public boolean isHiddenProperty(String propertyName) { 1403 1404 if (propertyName.equals("secure") && !m_data.isSecure()) { 1405 // "secure" property should not be editable in a site for which no secure server is configured 1406 return true; 1407 } 1408 1409 return m_hiddenProperties.contains(propertyName); 1410 } 1411 1412 /** 1413 * Returns true if the locale comparison mode is enabled.<p> 1414 * 1415 * @return true if the locale comparison mode is enabled 1416 */ 1417 public boolean isLocaleComparisonEnabled() { 1418 1419 return m_data.isLocaleComparisonEnabled(); 1420 } 1421 1422 /** 1423 * Checks if the given site path is the sitemap root.<p> 1424 * 1425 * @param sitePath the site path to check 1426 * 1427 * @return <code>true</code> if the given site path is the sitemap root 1428 */ 1429 public boolean isRoot(String sitePath) { 1430 1431 return m_data.getRoot().getSitePath().equals(sitePath); 1432 } 1433 1434 /** 1435 * Ask to save the page before leaving, if necessary.<p> 1436 * 1437 * @param target the leaving target 1438 */ 1439 public void leaveEditor(String target) { 1440 1441 CmsUUID baseId = getData().getRoot().getId(); 1442 CmsRpcAction<String> action = new CmsRpcAction<String>() { 1443 1444 @Override 1445 public void execute() { 1446 1447 start(0, false); 1448 getService().getResourceLink(baseId, target, this); 1449 } 1450 1451 @Override 1452 protected void onResponse(String link) { 1453 1454 Window.Location.assign(link); 1455 } 1456 1457 }; 1458 action.execute(); 1459 } 1460 1461 /** 1462 * Loads and displays the category data.<p> 1463 * 1464 * @param openLocalCategories true if the local category tree should be opened 1465 * @param openItemId the id of the item to open 1466 */ 1467 public void loadCategories(final boolean openLocalCategories, final CmsUUID openItemId) { 1468 1469 CmsRpcAction<CmsSitemapCategoryData> action = new CmsRpcAction<CmsSitemapCategoryData>() { 1470 1471 @Override 1472 public void execute() { 1473 1474 start(200, false); 1475 getService().getCategoryData(getEntryPoint(), this); 1476 } 1477 1478 @Override 1479 protected void onResponse(CmsSitemapCategoryData result) { 1480 1481 stop(false); 1482 m_categoryData = result; 1483 CmsSitemapView.getInstance().displayCategoryData(result, openLocalCategories, openItemId); 1484 } 1485 }; 1486 action.execute(); 1487 } 1488 1489 /** 1490 * Loads all available galleries for the current sub site.<p> 1491 */ 1492 public void loadGalleries() { 1493 1494 CmsRpcAction<Map<CmsGalleryType, List<CmsGalleryFolderEntry>>> action = new CmsRpcAction<Map<CmsGalleryType, List<CmsGalleryFolderEntry>>>() { 1495 1496 @Override 1497 public void execute() { 1498 1499 start(500, false); 1500 getService().getGalleryData(m_data.getRoot().getSitePath(), this); 1501 } 1502 1503 @Override 1504 protected void onResponse(Map<CmsGalleryType, List<CmsGalleryFolderEntry>> result) { 1505 1506 storeGalleryTypes(result.keySet()); 1507 CmsSitemapView.getInstance().displayGalleries(result); 1508 stop(false); 1509 } 1510 }; 1511 action.execute(); 1512 } 1513 1514 /** 1515 * Loads the model pages.<p> 1516 */ 1517 public void loadModelPages() { 1518 1519 CmsRpcAction<CmsModelInfo> action = new CmsRpcAction<CmsModelInfo>() { 1520 1521 @Override 1522 public void execute() { 1523 1524 start(500, false); 1525 getService().getModelInfos(m_data.getRoot().getId(), this); 1526 1527 } 1528 1529 @Override 1530 protected void onResponse(CmsModelInfo result) { 1531 1532 stop(false); 1533 CmsSitemapView.getInstance().displayModelPages(result); 1534 } 1535 }; 1536 action.execute(); 1537 } 1538 1539 /** 1540 * Loads the new element info.<p> 1541 * 1542 * @param callback the callback to call when done 1543 */ 1544 public void loadNewElementInfo(final AsyncCallback<Void> callback) { 1545 1546 CmsRpcAction<List<CmsNewResourceInfo>> newResourceInfoAction = new CmsRpcAction<List<CmsNewResourceInfo>>() { 1547 1548 @Override 1549 public void execute() { 1550 1551 start(200, true); 1552 1553 getService().getNewElementInfo(m_data.getRoot().getSitePath(), this); 1554 } 1555 1556 @Override 1557 protected void onResponse(List<CmsNewResourceInfo> result) { 1558 1559 stop(false); 1560 1561 m_data.setNewElementInfos(result); 1562 if (callback != null) { 1563 callback.onSuccess(null); 1564 } 1565 } 1566 1567 }; 1568 newResourceInfoAction.execute(); 1569 } 1570 1571 /** 1572 * Loads all entries on the given path.<p> 1573 * 1574 * @param sitePath the site path 1575 */ 1576 public void loadPath(final String sitePath) { 1577 1578 loadPath(sitePath, null); 1579 } 1580 1581 /** 1582 * Loads the sitemap entry for the given site path.<p> 1583 * 1584 * @param sitePath the site path 1585 * @param callback the callback 1586 */ 1587 public void loadPath(final String sitePath, final AsyncCallback<CmsClientSitemapEntry> callback) { 1588 1589 loadPath(sitePath, false, callback); 1590 } 1591 1592 /** 1593 * Loads all entries on the given path.<p> 1594 * 1595 * @param sitePath the site path 1596 * @param continueIfNotLoaded parameter passed to getChildren 1597 * @param callback the callback to execute when done 1598 */ 1599 public void loadPath( 1600 final String sitePath, 1601 final boolean continueIfNotLoaded, 1602 final AsyncCallback<CmsClientSitemapEntry> callback) { 1603 1604 if (getEntry(sitePath) != null) { 1605 CmsClientSitemapEntry entry = getEntry(sitePath); 1606 getChildren(entry.getId(), CmsSitemapTreeItem.getItemById(entry.getId()).isOpen(), callback); 1607 } else { 1608 String parentPath = CmsResource.getParentFolder(sitePath); 1609 CmsUUID idToLoad = null; 1610 CmsClientSitemapEntry entry = getEntry(parentPath); 1611 while (entry == null) { 1612 parentPath = CmsResource.getParentFolder(parentPath); 1613 if (parentPath == null) { 1614 break; 1615 } else { 1616 entry = getEntry(parentPath); 1617 } 1618 } 1619 if (entry != null) { 1620 idToLoad = entry.getId(); 1621 } else { 1622 idToLoad = m_data.getSiteRootId(); 1623 } 1624 CmsSitemapTreeItem treeItem = CmsSitemapTreeItem.getItemById(idToLoad); 1625 boolean open = true; 1626 if (treeItem != null) { 1627 open = treeItem.isOpen(); 1628 } 1629 getChildren(idToLoad, open, continueIfNotLoaded, new AsyncCallback<CmsClientSitemapEntry>() { 1630 1631 public void onFailure(Throwable caught) { 1632 1633 // nothing to do 1634 } 1635 1636 public void onSuccess(CmsClientSitemapEntry result) { 1637 1638 // check if target entry is loaded 1639 CmsClientSitemapEntry target = getEntry(sitePath); 1640 if (target == null) { 1641 loadPath(sitePath, continueIfNotLoaded, callback); 1642 } else { 1643 if (callback != null) { 1644 callback.onSuccess(target); 1645 } 1646 } 1647 } 1648 }); 1649 } 1650 } 1651 1652 /** 1653 * Merges a subsitemap at the given id back into this sitemap.<p> 1654 * 1655 * @param entryId the id of the sub sitemap entry 1656 */ 1657 public void mergeSubSitemap(final CmsUUID entryId) { 1658 1659 CmsRpcAction<CmsSitemapChange> mergeAction = new CmsRpcAction<CmsSitemapChange>() { 1660 1661 /** 1662 * @see org.opencms.gwt.client.rpc.CmsRpcAction#execute() 1663 */ 1664 @Override 1665 public void execute() { 1666 1667 start(0, true); 1668 getService().mergeSubSitemap(getEntryPoint(), entryId, this); 1669 } 1670 1671 /** 1672 * @see org.opencms.gwt.client.rpc.CmsRpcAction#onResponse(java.lang.Object) 1673 */ 1674 @Override 1675 protected void onResponse(CmsSitemapChange result) { 1676 1677 stop(false); 1678 applyChange(result); 1679 1680 } 1681 }; 1682 mergeAction.execute(); 1683 1684 } 1685 1686 /** 1687 * Moves the given sitemap entry with all its descendants to the new position.<p> 1688 * 1689 * @param entry the sitemap entry to move 1690 * @param toPath the destination path 1691 * @param position the new position between its siblings 1692 */ 1693 public void move(CmsClientSitemapEntry entry, String toPath, int position) { 1694 1695 // check for valid data 1696 if (!isValidEntryAndPath(entry, toPath)) { 1697 // invalid data, do nothing 1698 CmsDebugLog.getInstance().printLine("invalid data, doing nothing"); 1699 return; 1700 } 1701 // check for relevance 1702 if (isChangedPosition(entry, toPath, position)) { 1703 // only register real changes 1704 CmsSitemapChange change = new CmsSitemapChange(entry.getId(), entry.getSitePath(), ChangeType.modify); 1705 change.setDefaultFileId(entry.getDefaultFileId()); 1706 if (!toPath.equals(entry.getSitePath())) { 1707 change.setParentId(getEntry(CmsResource.getParentFolder(toPath)).getId()); 1708 change.setName(CmsResource.getName(toPath)); 1709 } 1710 if (CmsSitemapView.getInstance().isNavigationMode()) { 1711 change.setPosition(position); 1712 } 1713 change.setLeafType(entry.isLeafType()); 1714 CmsSitemapClipboardData data = getData().getClipboardData().copy(); 1715 data.addModified(entry); 1716 change.setClipBoardData(data); 1717 commitChange(change, null); 1718 } 1719 } 1720 1721 /** 1722 * Opens the property dialog for the locale comparison mode.<p> 1723 * 1724 * @param structureId the structure id of the sitemap entry to edit 1725 * @param rootId the structure id of the resource comparing to the tree root in sitemap compare mode 1726 */ 1727 public void openPropertyDialogForVaadin(final CmsUUID structureId, final CmsUUID rootId) { 1728 1729 CmsRpcAction<CmsLocaleComparePropertyData> action = new CmsRpcAction<CmsLocaleComparePropertyData>() { 1730 1731 @SuppressWarnings("synthetic-access") 1732 @Override 1733 public void execute() { 1734 1735 start(0, false); 1736 m_service.loadPropertyDataForLocaleCompareView(structureId, rootId, this); 1737 } 1738 1739 @Override 1740 protected void onResponse(CmsLocaleComparePropertyData result) { 1741 1742 stop(false); 1743 CmsSitemapController.editPropertiesForLocaleCompareMode( 1744 result, 1745 structureId, 1746 result.getDefaultFileId(), 1747 null); 1748 } 1749 }; 1750 action.execute(); 1751 1752 } 1753 1754 /** 1755 * Opens the site-map specified.<p> 1756 * 1757 * @param sitePath the site path to the site-map folder 1758 */ 1759 public void openSiteMap(String sitePath) { 1760 1761 openSiteMap(sitePath, false); 1762 } 1763 1764 /** 1765 * Opens the site-map specified.<p> 1766 * 1767 * @param sitePath the site path to the site-map folder 1768 * @param siteChange in case the site was changed 1769 */ 1770 public void openSiteMap(String sitePath, boolean siteChange) { 1771 1772 String uri = CmsCoreProvider.get().link( 1773 CmsCoreProvider.get().getUri()) + "?" + CmsCoreData.PARAM_PATH + "=" + sitePath; 1774 if (!siteChange) { 1775 uri += "&" + CmsCoreData.PARAM_RETURNCODE + "=" + getData().getReturnCode(); 1776 } 1777 Window.Location.replace(uri); 1778 } 1779 1780 /** 1781 * Recomputes properties for all sitemap entries.<p> 1782 */ 1783 public void recomputeProperties() { 1784 1785 CmsClientSitemapEntry root = getData().getRoot(); 1786 recomputeProperties(root); 1787 } 1788 1789 /** 1790 * Refreshes the root entry.<p> 1791 * 1792 * @param callback the callback to call after the entry has been refreshed 1793 */ 1794 public void refreshRoot(AsyncCallback<Void> callback) { 1795 1796 updateEntry(getData().getRoot().getId(), callback); 1797 } 1798 1799 /** 1800 * @see org.opencms.ade.sitemap.shared.I_CmsSitemapController#registerEntry(org.opencms.ade.sitemap.shared.CmsClientSitemapEntry) 1801 */ 1802 public void registerEntry(CmsClientSitemapEntry entry) { 1803 1804 if (m_entriesById.containsKey(entry.getId())) { 1805 CmsClientSitemapEntry oldEntry = m_entriesById.get(entry.getId()); 1806 oldEntry.update(entry); 1807 if (oldEntry != m_entriesByPath.get(oldEntry.getSitePath())) { 1808 m_entriesByPath.put(oldEntry.getSitePath(), oldEntry); 1809 } 1810 } else { 1811 m_entriesById.put(entry.getId(), entry); 1812 m_entriesByPath.put(entry.getSitePath(), entry); 1813 } 1814 1815 } 1816 1817 /** 1818 * @see org.opencms.ade.sitemap.shared.I_CmsSitemapController#registerPathChange(org.opencms.ade.sitemap.shared.CmsClientSitemapEntry, java.lang.String) 1819 */ 1820 public void registerPathChange(CmsClientSitemapEntry entry, String oldPath) { 1821 1822 m_entriesById.put(entry.getId(), entry); 1823 m_entriesByPath.remove(oldPath); 1824 m_entriesByPath.put(entry.getSitePath(), entry); 1825 } 1826 1827 /** 1828 * Removes the entry with the given site-path from navigation.<p> 1829 * 1830 * @param entryId the entry id 1831 */ 1832 public void removeFromNavigation(CmsUUID entryId) { 1833 1834 CmsClientSitemapEntry entry = getEntryById(entryId); 1835 CmsClientSitemapEntry parent = getEntry(CmsResource.getParentFolder(entry.getSitePath())); 1836 CmsSitemapChange change = new CmsSitemapChange(entry.getId(), entry.getSitePath(), ChangeType.remove); 1837 change.setParentId(parent.getId()); 1838 change.setDefaultFileId(entry.getDefaultFileId()); 1839 CmsSitemapClipboardData data = CmsSitemapView.getInstance().getController().getData().getClipboardData().copy(); 1840 data.addModified(entry); 1841 change.setClipBoardData(data); 1842 //TODO: handle detail page delete 1843 1844 commitChange(change, null); 1845 } 1846 1847 /** 1848 * Removes a model page from the sitemap's configuration.<p> 1849 * 1850 * @param id the structure id of the model page 1851 * 1852 * @param asyncCallback the callback to call when done 1853 */ 1854 public void removeModelPage(final CmsUUID id, final AsyncCallback<Void> asyncCallback) { 1855 1856 CmsRpcAction<Void> action = new CmsRpcAction<Void>() { 1857 1858 @Override 1859 public void execute() { 1860 1861 start(200, true); 1862 getService().removeModelPage(getEntryPoint(), id, this); 1863 1864 } 1865 1866 @Override 1867 protected void onResponse(Void result) { 1868 1869 stop(false); 1870 loadNewElementInfo(null); 1871 asyncCallback.onSuccess(null); 1872 1873 } 1874 }; 1875 action.execute(); 1876 1877 } 1878 1879 /** 1880 * @see org.opencms.ade.sitemap.shared.I_CmsSitemapController#replaceProperties(org.opencms.util.CmsUUID, java.util.Map) 1881 */ 1882 public Map<String, CmsClientProperty> replaceProperties(CmsUUID id, Map<String, CmsClientProperty> properties) { 1883 1884 if ((id == null) || (properties == null)) { 1885 return null; 1886 } 1887 Map<String, CmsClientProperty> props = m_propertyMaps.get(id); 1888 if (props == null) { 1889 props = properties; 1890 m_propertyMaps.put(id, props); 1891 } else { 1892 props.clear(); 1893 props.putAll(properties); 1894 } 1895 return props; 1896 1897 } 1898 1899 /** 1900 * Sets the editor mode in the user session.<p> 1901 * 1902 * @param editorMode the editor mode 1903 */ 1904 public void setEditorModeInSession(final EditorMode editorMode) { 1905 1906 CmsRpcAction<Void> action = new CmsRpcAction<Void>() { 1907 1908 @Override 1909 public void execute() { 1910 1911 getService().setEditorMode(editorMode, this); 1912 } 1913 1914 @Override 1915 protected void onResponse(Void result) { 1916 1917 // nothing to do 1918 1919 } 1920 }; 1921 action.execute(); 1922 } 1923 1924 /** 1925 * Shows a formerly hidden entry in the navigation.<p> 1926 * 1927 * @see #hideInNavigation(CmsUUID) 1928 * 1929 * @param entryId the entry id 1930 */ 1931 public void showInNavigation(CmsUUID entryId) { 1932 1933 CmsClientSitemapEntry entry = getEntryById(entryId); 1934 CmsSitemapChange change = getChangeForEdit( 1935 entry, 1936 Collections.singletonList( 1937 new CmsPropertyModification( 1938 entryId.toString() 1939 + "/" 1940 + CmsClientProperty.PROPERTY_NAVINFO 1941 + "/" 1942 + CmsClientProperty.PATH_STRUCTURE_VALUE, 1943 ""))); 1944 commitChange(change, null); 1945 } 1946 1947 /** 1948 * Undeletes the resource with the given structure id.<p> 1949 * 1950 * @param entryId the entry id 1951 * @param sitePath the site-path 1952 */ 1953 public void undelete(CmsUUID entryId, String sitePath) { 1954 1955 CmsSitemapChange change = new CmsSitemapChange(entryId, sitePath, ChangeType.undelete); 1956 CmsSitemapClipboardData data = CmsSitemapView.getInstance().getController().getData().getClipboardData().copy(); 1957 data.getDeletions().remove(entryId); 1958 change.setClipBoardData(data); 1959 commitChange(change, null); 1960 } 1961 1962 /** 1963 * Updates the given entry.<p> 1964 * 1965 * @param entryId the entry id 1966 */ 1967 public void updateEntry(CmsUUID entryId) { 1968 1969 getChildren(entryId, CmsSitemapTreeItem.getItemById(entryId).isOpen(), null); 1970 } 1971 1972 /** 1973 * Updates the given entry.<p> 1974 * 1975 * @param entryId the entry id 1976 * @param callback the callback to call after the entry has been updated 1977 */ 1978 public void updateEntry(CmsUUID entryId, final AsyncCallback<Void> callback) { 1979 1980 getChildren( 1981 entryId, 1982 CmsSitemapTreeItem.getItemById(entryId).isOpen(), 1983 new AsyncCallback<CmsClientSitemapEntry>() { 1984 1985 public void onFailure(Throwable caught) { 1986 1987 // nothing to do 1988 1989 } 1990 1991 public void onSuccess(CmsClientSitemapEntry result) { 1992 1993 if (callback != null) { 1994 callback.onSuccess(null); 1995 } 1996 } 1997 }); 1998 } 1999 2000 /** 2001 * Updates the given entry.<p> 2002 * 2003 * @param sitePath the entry sitepath 2004 */ 2005 public void updateEntry(String sitePath) { 2006 2007 CmsClientSitemapEntry entry = getEntry(sitePath); 2008 if ((entry != null) && (CmsSitemapTreeItem.getItemById(entry.getId()) != null)) { 2009 getChildren(entry.getId(), CmsSitemapTreeItem.getItemById(entry.getId()).isOpen(), null); 2010 } 2011 } 2012 2013 /** 2014 * Updates the given entry only, not evaluating any child changes.<p> 2015 * 2016 * @param entryId the entry id 2017 */ 2018 public void updateSingleEntry(final CmsUUID entryId) { 2019 2020 CmsRpcAction<CmsClientSitemapEntry> getChildrenAction = new CmsRpcAction<CmsClientSitemapEntry>() { 2021 2022 /** 2023 * @see org.opencms.gwt.client.rpc.CmsRpcAction#execute() 2024 */ 2025 @Override 2026 public void execute() { 2027 2028 getService().getChildren(getEntryPoint(), entryId, 0, this); 2029 } 2030 2031 /** 2032 * @see org.opencms.gwt.client.rpc.CmsRpcAction#onResponse(java.lang.Object) 2033 */ 2034 @Override 2035 public void onResponse(CmsClientSitemapEntry result) { 2036 2037 CmsClientSitemapEntry target = getEntryById(entryId); 2038 if (target == null) { 2039 // this might happen after an automated deletion 2040 stop(false); 2041 return; 2042 } 2043 target.update(result); 2044 CmsSitemapTreeItem item = CmsSitemapTreeItem.getItemById(target.getId()); 2045 item.updateEntry(target); 2046 } 2047 }; 2048 getChildrenAction.execute(); 2049 } 2050 2051 /** 2052 * Fires a sitemap change event.<p> 2053 * 2054 * @param change the change event to fire 2055 */ 2056 protected void applyChange(CmsSitemapChange change) { 2057 2058 // update the clip board data 2059 if (change.getClipBoardData() != null) { 2060 getData().getClipboardData().setDeletions(change.getClipBoardData().getDeletions()); 2061 getData().getClipboardData().setModifications(change.getClipBoardData().getModifications()); 2062 } 2063 switch (change.getChangeType()) { 2064 case bumpDetailPage: 2065 CmsDetailPageTable detailPageTable = getData().getDetailPageTable(); 2066 if (detailPageTable.contains(change.getEntryId())) { 2067 detailPageTable.makeDefault(change.getEntryId()); 2068 } 2069 break; 2070 case clipboardOnly: 2071 // nothing to do 2072 break; 2073 case remove: 2074 CmsClientSitemapEntry entry = getEntryById(change.getEntryId()); 2075 entry.setInNavigation(false); 2076 CmsPropertyModification propMod = new CmsPropertyModification( 2077 entry.getId(), 2078 CmsClientProperty.PROPERTY_NAVTEXT, 2079 null, 2080 true); 2081 executePropertyModification(propMod); 2082 propMod = new CmsPropertyModification(entry.getId(), CmsClientProperty.PROPERTY_NAVTEXT, null, true); 2083 executePropertyModification(propMod); 2084 entry.normalizeProperties(); 2085 break; 2086 case undelete: 2087 updateEntry(change.getParentId()); 2088 break; 2089 case create: 2090 CmsClientSitemapEntry newEntry = change.getUpdatedEntry(); 2091 getEntryById(change.getParentId()).insertSubEntry(newEntry, change.getPosition(), this); 2092 newEntry.initializeAll(this); 2093 if (isDetailPage(newEntry)) { 2094 CmsDetailPageInfo info = getDetailPageInfo(newEntry.getId()); 2095 if (info == null) { 2096 info = new CmsDetailPageInfo( 2097 newEntry.getId(), 2098 newEntry.getSitePath(), 2099 newEntry.getDetailpageTypeName(), 2100 /* qualifier = */null, 2101 newEntry.getVfsModeIcon()); 2102 } 2103 addDetailPageInfo(info); 2104 } 2105 break; 2106 case delete: 2107 removeEntry(change.getEntryId(), change.getParentId()); 2108 break; 2109 case modify: 2110 if (change.hasNewParent() || change.hasChangedPosition()) { 2111 CmsClientSitemapEntry moved = getEntryById(change.getEntryId()); 2112 String oldSitepath = moved.getSitePath(); 2113 CmsClientSitemapEntry sourceParent = getEntry(CmsResource.getParentFolder(oldSitepath)); 2114 sourceParent.removeSubEntry(moved.getId()); 2115 CmsClientSitemapEntry destParent = change.hasNewParent() 2116 ? getEntryById(change.getParentId()) 2117 : sourceParent; 2118 if (change.getPosition() < destParent.getSubEntries().size()) { 2119 destParent.insertSubEntry(moved, change.getPosition(), this); 2120 } else { 2121 // inserting as last entry of the parent list 2122 destParent.addSubEntry(moved, this); 2123 } 2124 if (change.hasNewParent()) { 2125 cleanupOldPaths(oldSitepath, destParent.getSitePath()); 2126 } 2127 } 2128 if (change.hasChangedName()) { 2129 CmsClientSitemapEntry changed = getEntryById(change.getEntryId()); 2130 String oldSitepath = changed.getSitePath(); 2131 String parentPath = CmsResource.getParentFolder(oldSitepath); 2132 String newSitepath = CmsStringUtil.joinPaths(parentPath, change.getName()); 2133 changed.updateSitePath(newSitepath, this); 2134 cleanupOldPaths(oldSitepath, newSitepath); 2135 } 2136 if (change.hasChangedProperties()) { 2137 for (CmsPropertyModification modification : change.getPropertyChanges()) { 2138 executePropertyModification(modification); 2139 } 2140 getEntryById(change.getEntryId()).normalizeProperties(); 2141 } 2142 if (change.getUpdatedEntry() != null) { 2143 CmsClientSitemapEntry oldEntry = getEntryById(change.getEntryId()); 2144 CmsClientSitemapEntry parent = getEntry(CmsResource.getParentFolder(oldEntry.getSitePath())); 2145 removeEntry(change.getEntryId(), parent.getId()); 2146 parent.insertSubEntry(change.getUpdatedEntry(), oldEntry.getPosition(), this); 2147 change.getUpdatedEntry().initializeAll(this); 2148 } 2149 break; 2150 default: 2151 } 2152 if (change.getChangeType() != ChangeType.delete) { 2153 recomputeProperties(); 2154 } 2155 m_eventBus.fireEventFromSource(new CmsSitemapChangeEvent(change), this); 2156 } 2157 2158 /** 2159 * Adds a change to the queue.<p> 2160 * 2161 * @param change the change to commit 2162 * @param callback the callback to execute after the change has been applied 2163 */ 2164 protected void commitChange(final CmsSitemapChange change, final Command callback) { 2165 2166 if (change != null) { 2167 // save the sitemap 2168 CmsRpcAction<CmsSitemapChange> saveAction = new CmsRpcAction<CmsSitemapChange>() { 2169 2170 /** 2171 * @see org.opencms.gwt.client.rpc.CmsRpcAction#execute() 2172 */ 2173 @Override 2174 public void execute() { 2175 2176 start(0, true); 2177 getService().save(getEntryPoint(), change, this); 2178 2179 } 2180 2181 @Override 2182 public void onFailure(Throwable t) { 2183 // An error after a drag/drop operation can cause click events to get sent to the sitemap entry's move handle 2184 // instead of the error dialog's buttons. We use releaseCapture() to fix this. 2185 DOM.releaseCapture(DOM.getCaptureElement()); 2186 super.onFailure(t); 2187 } 2188 2189 /** 2190 * @see org.opencms.gwt.client.rpc.CmsRpcAction#onResponse(java.lang.Object) 2191 */ 2192 @Override 2193 public void onResponse(CmsSitemapChange result) { 2194 2195 stop(false); 2196 2197 applyChange(result); 2198 if (callback != null) { 2199 callback.execute(); 2200 } 2201 } 2202 }; 2203 saveAction.execute(); 2204 } 2205 } 2206 2207 /** 2208 * Creates a change object for an edit operation.<p> 2209 * 2210 * @param entry the edited sitemap entry 2211 * @param propertyChanges the list of property changes 2212 * 2213 * @return the change object 2214 */ 2215 protected CmsSitemapChange getChangeForEdit( 2216 CmsClientSitemapEntry entry, 2217 List<CmsPropertyModification> propertyChanges) { 2218 2219 CmsSitemapChange change = new CmsSitemapChange(entry.getId(), entry.getSitePath(), ChangeType.modify); 2220 change.setDefaultFileId(entry.getDefaultFileId()); 2221 change.setLeafType(entry.isLeafType()); 2222 List<CmsPropertyModification> propertyChangeData = new ArrayList<CmsPropertyModification>(); 2223 for (CmsPropertyModification propChange : propertyChanges) { 2224 propertyChangeData.add(propChange); 2225 } 2226 change.setPropertyChanges(propertyChangeData); 2227 2228 CmsSitemapClipboardData data = getData().getClipboardData().copy(); 2229 data.addModified(entry); 2230 change.setClipBoardData(data); 2231 return change; 2232 } 2233 2234 /** 2235 * Returns the URI of the current sitemap.<p> 2236 * 2237 * @return the URI of the current sitemap 2238 */ 2239 protected String getEntryPoint() { 2240 2241 return m_data.getRoot().getSitePath(); 2242 2243 } 2244 2245 /** 2246 * Helper method for getting the full path of a sitemap entry whose URL name is being edited.<p> 2247 * 2248 * @param entry the sitemap entry 2249 * @param newUrlName the new url name of the sitemap entry 2250 * 2251 * @return the new full site path of the sitemap entry 2252 */ 2253 protected String getPath(CmsClientSitemapEntry entry, String newUrlName) { 2254 2255 if (newUrlName.equals("")) { 2256 return entry.getSitePath(); 2257 } 2258 return CmsResource.getParentFolder(entry.getSitePath()) + newUrlName + "/"; 2259 } 2260 2261 /** 2262 * Returns the sitemap service instance.<p> 2263 * 2264 * @return the sitemap service instance 2265 */ 2266 protected I_CmsVfsServiceAsync getVfsService() { 2267 2268 if (m_vfsService == null) { 2269 m_vfsService = CmsCoreProvider.getVfsService(); 2270 } 2271 return m_vfsService; 2272 } 2273 2274 /** 2275 * Initializes the detail page information.<p> 2276 */ 2277 protected void initDetailPageInfos() { 2278 2279 for (CmsUUID id : m_data.getDetailPageTable().getAllIds()) { 2280 CmsDetailPageInfo info = m_data.getDetailPageTable().get(id); 2281 m_allDetailPageInfos.put(id, info); 2282 } 2283 } 2284 2285 /** 2286 * Creates a new client sitemap entry bean to use for the RPC call which actually creates the entry on the server side.<p> 2287 * 2288 * @param parent the parent entry 2289 * @param callback the callback to execute 2290 */ 2291 protected void makeNewEntry( 2292 final CmsClientSitemapEntry parent, 2293 final I_CmsSimpleCallback<CmsClientSitemapEntry> callback) { 2294 2295 ensureUniqueName(parent, NEW_ENTRY_NAME, new I_CmsSimpleCallback<String>() { 2296 2297 public void execute(String urlName) { 2298 2299 CmsClientSitemapEntry newEntry = new CmsClientSitemapEntry(); 2300 //newEntry.setTitle(urlName); 2301 newEntry.setName(urlName); 2302 String sitePath = parent.getSitePath() + urlName + "/"; 2303 newEntry.setSitePath(sitePath); 2304 newEntry.setVfsPath(null); 2305 newEntry.setPosition(0); 2306 newEntry.setNew(true); 2307 newEntry.setInNavigation(true); 2308 newEntry.setResourceTypeName("folder"); 2309 newEntry.getOwnProperties().put( 2310 CmsClientProperty.PROPERTY_TITLE, 2311 new CmsClientProperty(CmsClientProperty.PROPERTY_TITLE, NEW_ENTRY_NAME, NEW_ENTRY_NAME)); 2312 callback.execute(newEntry); 2313 } 2314 }); 2315 2316 } 2317 2318 /** 2319 * Recomputes the properties for a client sitemap entry.<p> 2320 * 2321 * @param entry the entry for whose descendants the properties should be recomputed 2322 */ 2323 protected void recomputeProperties(CmsClientSitemapEntry entry) { 2324 2325 for (I_CmsPropertyUpdateHandler handler : m_propertyUpdateHandlers) { 2326 handler.handlePropertyUpdate(entry); 2327 } 2328 for (CmsClientSitemapEntry child : entry.getSubEntries()) { 2329 recomputeProperties(child); 2330 } 2331 } 2332 2333 /** 2334 * Store the gallery type information.<p> 2335 * 2336 * @param galleryTypes the gallery types 2337 */ 2338 void storeGalleryTypes(Collection<CmsGalleryType> galleryTypes) { 2339 2340 m_galleryTypes.clear(); 2341 for (CmsGalleryType type : galleryTypes) { 2342 m_galleryTypes.put(Integer.valueOf(type.getTypeId()), type); 2343 } 2344 } 2345 2346 /** 2347 * Cleans up wrong path references.<p> 2348 * 2349 * @param oldSitepath the old sitepath 2350 * @param newSitepath the new sitepath 2351 */ 2352 private void cleanupOldPaths(String oldSitepath, String newSitepath) { 2353 2354 // use a separate list to avoid concurrent changes 2355 List<CmsClientSitemapEntry> entries = new ArrayList<CmsClientSitemapEntry>(m_entriesById.values()); 2356 for (CmsClientSitemapEntry entry : entries) { 2357 if (entry.getSitePath().startsWith(oldSitepath)) { 2358 String currentPath = entry.getSitePath(); 2359 String partAfterOldSitePath = currentPath.substring(oldSitepath.length()); 2360 2361 String updatedSitePath = "".equals(partAfterOldSitePath) ? newSitepath : CmsStringUtil.joinPaths( 2362 newSitepath, 2363 partAfterOldSitePath); 2364 entry.updateSitePath(updatedSitePath, this); 2365 } 2366 } 2367 } 2368 2369 /** 2370 * Returns the properties above the sitemap root.<p> 2371 * 2372 * @return the map of properties of the root's parent 2373 */ 2374 private Map<String, CmsClientProperty> getParentProperties() { 2375 2376 return m_data.getParentProperties(); 2377 } 2378 2379 /** 2380 * Checks if the given path and position indicate a changed position for the entry.<p> 2381 * 2382 * @param entry the sitemap entry to move 2383 * @param toPath the destination path 2384 * @param position the new position between its siblings 2385 * 2386 * @return <code>true</code> if this is a position change 2387 */ 2388 private boolean isChangedPosition(CmsClientSitemapEntry entry, String toPath, int position) { 2389 2390 return (!entry.getSitePath().equals(toPath) || (entry.getPosition() != position)); 2391 } 2392 2393 /** 2394 * Validates the entry and the given path.<p> 2395 * 2396 * @param entry the entry 2397 * @param toPath the path 2398 * 2399 * @return <code>true</code> if entry and path are valid 2400 */ 2401 private boolean isValidEntryAndPath(CmsClientSitemapEntry entry, String toPath) { 2402 2403 return ((toPath != null) 2404 && (CmsResource.getParentFolder(toPath) != null) 2405 && (entry != null) 2406 && (getEntry(CmsResource.getParentFolder(toPath)) != null) 2407 && (getEntry(entry.getSitePath()) != null)); 2408 } 2409 2410 /** 2411 * Removes all children of the given entry recursively from the data model.<p> 2412 * 2413 * @param entry the entry 2414 */ 2415 private void removeAllChildren(CmsClientSitemapEntry entry) { 2416 2417 for (CmsClientSitemapEntry child : entry.getSubEntries()) { 2418 removeAllChildren(child); 2419 m_entriesById.remove(child.getId()); 2420 m_entriesByPath.remove(child.getSitePath()); 2421 // apply to detailpage table 2422 CmsDetailPageTable detailPageTable = getData().getDetailPageTable(); 2423 if (detailPageTable.contains(child.getId())) { 2424 detailPageTable.remove(child.getId()); 2425 } 2426 } 2427 entry.getSubEntries().clear(); 2428 } 2429 2430 /** 2431 * Removes the given entry and it's descendants from the modified list.<p> 2432 * 2433 * @param entry the entry 2434 * @param data the clip board data 2435 */ 2436 private void removeDeletedFromModified(CmsClientSitemapEntry entry, CmsSitemapClipboardData data) { 2437 2438 data.removeModified(entry); 2439 for (CmsClientSitemapEntry child : entry.getSubEntries()) { 2440 removeDeletedFromModified(child, data); 2441 } 2442 } 2443 2444 /** 2445 * Removes the given entry from the data model.<p> 2446 * 2447 * @param entryId the id of the entry to remove 2448 * @param parentId the parent entry id 2449 */ 2450 private void removeEntry(CmsUUID entryId, CmsUUID parentId) { 2451 2452 CmsClientSitemapEntry entry = getEntryById(entryId); 2453 CmsClientSitemapEntry deleteParent = getEntryById(parentId); 2454 removeAllChildren(entry); 2455 deleteParent.removeSubEntry(entry.getPosition()); 2456 m_entriesById.remove(entryId); 2457 m_entriesByPath.remove(entry.getSitePath()); 2458 // apply to detailpage table 2459 CmsDetailPageTable detailPageTable = getData().getDetailPageTable(); 2460 if (detailPageTable.contains(entryId)) { 2461 detailPageTable.remove(entryId); 2462 } 2463 } 2464}