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