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