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