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.ui.sitemap; 029 030import org.opencms.file.CmsObject; 031import org.opencms.file.CmsProject; 032import org.opencms.file.CmsProperty; 033import org.opencms.file.CmsPropertyDefinition; 034import org.opencms.file.CmsResource; 035import org.opencms.file.CmsResourceFilter; 036import org.opencms.file.types.CmsResourceTypeXmlContainerPage; 037import org.opencms.gwt.CmsCoreService; 038import org.opencms.i18n.CmsLocaleGroupService; 039import org.opencms.i18n.CmsLocaleManager; 040import org.opencms.lock.CmsLockActionRecord; 041import org.opencms.lock.CmsLockActionRecord.LockChange; 042import org.opencms.lock.CmsLockUtil; 043import org.opencms.main.CmsException; 044import org.opencms.main.CmsLog; 045import org.opencms.main.OpenCms; 046import org.opencms.site.CmsSite; 047import org.opencms.ui.A_CmsUI; 048import org.opencms.ui.CmsVaadinUtils; 049import org.opencms.ui.FontOpenCms; 050import org.opencms.ui.I_CmsDialogContext; 051import org.opencms.ui.Messages; 052import org.opencms.ui.actions.CmsResourceInfoAction; 053import org.opencms.ui.apps.CmsSitemapEditorConfiguration; 054import org.opencms.ui.components.CmsBasicDialog; 055import org.opencms.ui.components.CmsBasicDialog.DialogWidth; 056import org.opencms.ui.components.CmsErrorDialog; 057import org.opencms.ui.components.CmsResourceIcon; 058import org.opencms.ui.components.CmsResourceIcon.IconMode; 059import org.opencms.ui.components.CmsResourceInfo; 060import org.opencms.ui.components.OpenCmsTheme; 061import org.opencms.ui.contextmenu.CmsContextMenu; 062import org.opencms.ui.contextmenu.CmsMenuItemVisibilityMode; 063import org.opencms.ui.contextmenu.I_CmsSimpleContextMenuEntry; 064import org.opencms.util.CmsUUID; 065 066import java.util.Arrays; 067import java.util.Collection; 068import java.util.IdentityHashMap; 069import java.util.List; 070import java.util.Locale; 071 072import org.apache.commons.logging.Log; 073 074import com.google.common.base.Joiner; 075import com.google.common.base.Predicate; 076import com.google.common.collect.Lists; 077import com.vaadin.event.LayoutEvents.LayoutClickEvent; 078import com.vaadin.event.LayoutEvents.LayoutClickListener; 079import com.vaadin.server.FontAwesome; 080import com.vaadin.server.Resource; 081import com.vaadin.ui.AbstractComponent; 082import com.vaadin.ui.Button.ClickEvent; 083import com.vaadin.ui.Button.ClickListener; 084import com.vaadin.ui.Component; 085import com.vaadin.ui.ComponentContainer; 086import com.vaadin.ui.CssLayout; 087import com.vaadin.ui.MenuBar; 088import com.vaadin.ui.MenuBar.Command; 089import com.vaadin.ui.MenuBar.MenuItem; 090import com.vaadin.ui.Notification; 091import com.vaadin.ui.Notification.Type; 092import com.vaadin.ui.UI; 093import com.vaadin.ui.Window; 094 095/** 096 * Manages the sitemap tree in the 'locale comparison' view in the sitemap editor.<p> 097 */ 098public class CmsSitemapTreeController { 099 100 /** 101 * The context used for child dialogs.<p> 102 */ 103 public class DialogContext implements I_CmsDialogContext { 104 105 /** The tree node. */ 106 private CmsSitemapTreeNode m_node; 107 108 /** The resource. */ 109 private CmsResource m_resource; 110 111 /** 112 * Creates a new instance.<p> 113 * 114 * @param resource the resource 115 * @param node the tree node 116 */ 117 public DialogContext(CmsResource resource, CmsSitemapTreeNode node) { 118 119 m_resource = resource; 120 m_node = node; 121 } 122 123 /** 124 * Closes the dialog window.<p> 125 */ 126 public void closeWindow() { 127 128 if (m_window != null) { 129 m_window.close(); 130 m_window = null; 131 } 132 } 133 134 /** 135 * @see org.opencms.ui.I_CmsDialogContext#error(java.lang.Throwable) 136 */ 137 public void error(Throwable error) { 138 139 getTreeControllerLog().error(error.getLocalizedMessage(), error); 140 CmsErrorDialog.showErrorDialog(error); 141 } 142 143 /** 144 * @see org.opencms.ui.I_CmsDialogContext#finish(org.opencms.file.CmsProject, java.lang.String) 145 */ 146 public void finish(CmsProject project, String siteRoot) { 147 148 closeWindow(); 149 } 150 151 /** 152 * @see org.opencms.ui.I_CmsDialogContext#finish(java.util.Collection) 153 */ 154 @SuppressWarnings("synthetic-access") 155 public void finish(Collection<CmsUUID> result) { 156 157 closeWindow(); 158 if (result.isEmpty()) { 159 return; 160 } 161 if (m_node != null) { 162 if (m_node == m_currentRootNode) { 163 m_localeContext.refreshAll(); 164 } else { 165 updateNode(m_node); 166 } 167 } 168 } 169 170 /** 171 * @see org.opencms.ui.I_CmsDialogContext#focus(org.opencms.util.CmsUUID) 172 */ 173 public void focus(CmsUUID structureId) { 174 175 // not used 176 } 177 178 /** 179 * @see org.opencms.ui.I_CmsDialogContext#getAllStructureIdsInView() 180 */ 181 public List<CmsUUID> getAllStructureIdsInView() { 182 183 return null; 184 } 185 186 /** 187 * @see org.opencms.ui.I_CmsDialogContext#getAppId() 188 */ 189 public String getAppId() { 190 191 return CmsSitemapEditorConfiguration.APP_ID; 192 } 193 194 /** 195 * @see org.opencms.ui.I_CmsDialogContext#getCms() 196 */ 197 public CmsObject getCms() { 198 199 return A_CmsUI.getCmsObject(); 200 } 201 202 /** 203 * @see org.opencms.ui.I_CmsDialogContext#getContextType() 204 */ 205 public ContextType getContextType() { 206 207 return null; 208 } 209 210 /** 211 * @see org.opencms.ui.I_CmsDialogContext#getResources() 212 */ 213 public List<CmsResource> getResources() { 214 215 return Arrays.asList(m_resource); 216 } 217 218 /** 219 * @see org.opencms.ui.I_CmsDialogContext#navigateTo(java.lang.String) 220 */ 221 public void navigateTo(String appId) { 222 223 // not used 224 } 225 226 /** 227 * @see org.opencms.ui.I_CmsDialogContext#onViewChange() 228 */ 229 public void onViewChange() { 230 231 // do nothing 232 } 233 234 /** 235 * @see org.opencms.ui.I_CmsDialogContext#reload() 236 */ 237 public void reload() { 238 239 // do nothing 240 241 } 242 243 /** 244 * @see org.opencms.ui.I_CmsDialogContext#setWindow(com.vaadin.ui.Window) 245 */ 246 public void setWindow(Window window) { 247 248 m_window = window; 249 } 250 251 /** 252 * @see org.opencms.ui.I_CmsDialogContext#start(java.lang.String, com.vaadin.ui.Component) 253 */ 254 public void start(String title, Component dialog) { 255 256 start(title, dialog, DialogWidth.narrow); 257 } 258 259 /** 260 * @see org.opencms.ui.I_CmsDialogContext#start(java.lang.String, com.vaadin.ui.Component, org.opencms.ui.components.CmsBasicDialog.DialogWidth) 261 */ 262 public void start(String title, Component dialog, DialogWidth width) { 263 264 if (dialog != null) { 265 m_window = CmsBasicDialog.prepareWindow(width); 266 m_window.setCaption(title); 267 m_window.setContent(dialog); 268 UI.getCurrent().addWindow(m_window); 269 if (dialog instanceof CmsBasicDialog) { 270 ((CmsBasicDialog)dialog).initActionHandler(m_window); 271 } 272 } 273 } 274 275 /** 276 * @see org.opencms.ui.I_CmsDialogContext#updateUserInfo() 277 */ 278 public void updateUserInfo() { 279 280 // not supported 281 } 282 } 283 284 /** 285 * Copy menu entry. 286 */ 287 class EntryCopy implements I_CmsSimpleContextMenuEntry<MenuContext> { 288 289 /** 290 * @see org.opencms.ui.contextmenu.I_CmsSimpleContextMenuEntry#executeAction(java.lang.Object) 291 */ 292 public void executeAction(MenuContext context) { 293 294 try { 295 CmsResource resource = context.getTargetResource(); 296 DialogContext dialogContext = new DialogContext(resource, context.getNode()); 297 CmsCopyPageDialog dialog = new CmsCopyPageDialog(dialogContext); 298 String title = CmsVaadinUtils.getMessageText(Messages.GUI_COPYPAGE_DIALOG_TITLE_0); 299 dialogContext.start(title, dialog); 300 } catch (Exception e) { 301 LOG.error(e.getLocalizedMessage(), e); 302 CmsErrorDialog.showErrorDialog(e); 303 } 304 305 } 306 307 /** 308 * @see org.opencms.ui.contextmenu.I_CmsSimpleContextMenuEntry#getTitle(java.util.Locale) 309 */ 310 public String getTitle(Locale locale) { 311 312 return CmsVaadinUtils.getMessageText(Messages.GUI_LOCALECOMPARE_COPY_PAGE_0); 313 } 314 315 /** 316 * @see org.opencms.ui.contextmenu.I_CmsSimpleContextMenuEntry#getVisibility(java.lang.Object) 317 */ 318 public CmsMenuItemVisibilityMode getVisibility(MenuContext context) { 319 320 return visibleIfTrue( 321 context.isTargetingLinkedResource() 322 ? CmsResourceTypeXmlContainerPage.isContainerPage(context.getData().getLinkedResource()) 323 : context.getData().isCopyable()); 324 325 } 326 } 327 328 /** 329 * Menu entry for opening the explorer.<p> 330 */ 331 class EntryExplorer implements I_CmsSimpleContextMenuEntry<MenuContext> { 332 333 /** 334 * @see org.opencms.ui.contextmenu.I_CmsSimpleContextMenuEntry#executeAction(java.lang.Object) 335 */ 336 public void executeAction(MenuContext context) { 337 338 String link = CmsCoreService.getVaadinWorkplaceLink( 339 A_CmsUI.getCmsObject(), 340 context.getTargetResource().getStructureId()); 341 A_CmsUI.get().getPage().setLocation(link); 342 343 } 344 345 /** 346 * @see org.opencms.ui.contextmenu.I_CmsSimpleContextMenuEntry#getTitle(java.util.Locale) 347 */ 348 public String getTitle(Locale locale) { 349 350 return CmsVaadinUtils.getMessageText(Messages.GUI_LOCALECOMPARE_EXPLORER_0); 351 } 352 353 /** 354 * @see org.opencms.ui.contextmenu.I_CmsSimpleContextMenuEntry#getVisibility(java.lang.Object) 355 */ 356 public CmsMenuItemVisibilityMode getVisibility(MenuContext context) { 357 358 return visibleIfTrue(true); 359 } 360 361 } 362 363 /** 364 * Menu entry for opening the info dialog.<p> 365 */ 366 class EntryInfo implements I_CmsSimpleContextMenuEntry<MenuContext> { 367 368 /** 369 * @see org.opencms.ui.contextmenu.I_CmsSimpleContextMenuEntry#executeAction(java.lang.Object) 370 */ 371 public void executeAction(MenuContext context) { 372 373 CmsResourceInfoAction infoAction = new CmsResourceInfoAction(); 374 infoAction.executeAction(new DialogContext(context.getTargetResource(), context.getNode())); 375 } 376 377 /** 378 * @see org.opencms.ui.contextmenu.I_CmsSimpleContextMenuEntry#getTitle(java.util.Locale) 379 */ 380 public String getTitle(Locale locale) { 381 382 return CmsVaadinUtils.getMessageText(Messages.GUI_RESOURCE_INFO_0); 383 } 384 385 /** 386 * @see org.opencms.ui.contextmenu.I_CmsSimpleContextMenuEntry#getVisibility(java.lang.Object) 387 */ 388 public CmsMenuItemVisibilityMode getVisibility(MenuContext context) { 389 390 return visibleIfTrue(true); 391 } 392 393 } 394 395 /** 396 * Menu entry for opening the 'LInk locale' dialog.<p> 397 */ 398 class EntryLink implements I_CmsSimpleContextMenuEntry<MenuContext> { 399 400 /** 401 * @see org.opencms.ui.contextmenu.I_CmsSimpleContextMenuEntry#executeAction(java.lang.Object) 402 */ 403 @SuppressWarnings("synthetic-access") 404 public void executeAction(MenuContext data) { 405 406 try { 407 DialogContext dialogContext = new DialogContext( 408 A_CmsUI.getCmsObject().readResource( 409 data.getData().getClientEntry().getId(), 410 CmsResourceFilter.IGNORE_EXPIRATION), 411 data.getNode()); 412 CmsLocaleLinkTargetSelectionDialog dialog = new CmsLocaleLinkTargetSelectionDialog( 413 dialogContext, 414 m_localeContext); 415 dialogContext.start( 416 CmsVaadinUtils.getMessageText(Messages.GUI_LOCALECOMPARE_LINK_LOCALE_VARIANT_0), 417 dialog, 418 DialogWidth.narrow); 419 } catch (CmsException e) { 420 LOG.error(e.getLocalizedMessage(), e); 421 CmsErrorDialog.showErrorDialog(e); 422 } 423 } 424 425 /** 426 * @see org.opencms.ui.contextmenu.I_CmsSimpleContextMenuEntry#getTitle(java.util.Locale) 427 */ 428 public String getTitle(Locale locale) { 429 430 return CmsVaadinUtils.getMessageText(Messages.GUI_LOCALECOMPARE_LINK_LOCALE_VARIANT_0); 431 } 432 433 /** 434 * @see org.opencms.ui.contextmenu.I_CmsSimpleContextMenuEntry#getVisibility(java.lang.Object) 435 */ 436 @SuppressWarnings("synthetic-access") 437 public CmsMenuItemVisibilityMode getVisibility(MenuContext context) { 438 439 return activeIfTrue( 440 !context.getData().isLinked() 441 && !context.getData().isMarkedNoTranslation(m_localeContext.getComparisonLocale())); 442 } 443 } 444 445 /** 446 * Context menu entry for the 'Do not translate' mark.<p> 447 */ 448 class EntryMark implements I_CmsSimpleContextMenuEntry<MenuContext> { 449 450 /** 451 * @see org.opencms.ui.contextmenu.I_CmsSimpleContextMenuEntry#executeAction(java.lang.Object) 452 */ 453 @SuppressWarnings("synthetic-access") 454 public void executeAction(MenuContext context) { 455 456 CmsSitemapTreeNodeData entry = context.getData(); 457 CmsSitemapTreeNode node = context.getNode(); 458 459 CmsObject cms = A_CmsUI.getCmsObject(); 460 CmsLockActionRecord actionRecord = null; 461 CmsResource fileToModify2 = null; 462 try { 463 464 CmsResource primary = A_CmsUI.getCmsObject().readResource( 465 entry.getClientEntry().getId(), 466 CmsResourceFilter.IGNORE_EXPIRATION); 467 if (primary.isFolder()) { 468 CmsResource defaultFile = A_CmsUI.getCmsObject().readDefaultFile( 469 primary, 470 CmsResourceFilter.IGNORE_EXPIRATION); 471 if (defaultFile != null) { 472 primary = defaultFile; 473 } 474 } 475 final CmsResource primaryFinal = primary; 476 477 fileToModify2 = primaryFinal; 478 if (fileToModify2.isFolder()) { 479 try { 480 fileToModify2 = A_CmsUI.getCmsObject().readDefaultFile( 481 fileToModify2, 482 CmsResourceFilter.IGNORE_EXPIRATION); 483 } catch (CmsException e) { 484 LOG.error(e.getLocalizedMessage(), e); 485 } 486 } 487 488 actionRecord = CmsLockUtil.ensureLock(cms, fileToModify2); 489 m_localeContext.getComparisonLocale().toString(); 490 CmsProperty prop = cms.readPropertyObject( 491 fileToModify2, 492 CmsPropertyDefinition.PROPERTY_LOCALE_NOTRANSLATION, 493 false); 494 String propValue = prop.getValue(); 495 if (propValue == null) { 496 propValue = ""; // make getLocales not return null 497 } 498 List<Locale> currentLocales = CmsLocaleManager.getLocales(propValue); 499 if (!currentLocales.contains(m_localeContext.getComparisonLocale())) { 500 currentLocales.add(m_localeContext.getComparisonLocale()); 501 String newPropValue = Joiner.on(",").join(currentLocales); 502 CmsProperty newProp = new CmsProperty( 503 CmsPropertyDefinition.PROPERTY_LOCALE_NOTRANSLATION, 504 newPropValue, 505 null); 506 cms.writePropertyObjects(fileToModify2, Arrays.asList(newProp)); 507 DialogContext dialogContext = new DialogContext( 508 A_CmsUI.getCmsObject().readResource( 509 entry.getClientEntry().getId(), 510 CmsResourceFilter.IGNORE_EXPIRATION), 511 node); 512 dialogContext.finish(Arrays.asList(fileToModify2.getStructureId())); 513 514 } 515 516 } catch (CmsException e) { 517 LOG.error(e.getLocalizedMessage(), e); 518 CmsErrorDialog.showErrorDialog(e); 519 520 } finally { 521 if ((actionRecord != null) && (actionRecord.getChange() == LockChange.locked)) { 522 try { 523 cms.unlockResource(fileToModify2); 524 } catch (CmsException e) { 525 LOG.error(e.getLocalizedMessage(), e); 526 CmsErrorDialog.showErrorDialog(e); 527 } 528 } 529 } 530 531 } 532 533 /** 534 * @see org.opencms.ui.contextmenu.I_CmsSimpleContextMenuEntry#getTitle(java.util.Locale) 535 */ 536 public String getTitle(Locale locale) { 537 538 return CmsVaadinUtils.getMessageText(Messages.GUI_LOCALECOMPARE_ADD_DONT_TRANSLATE_0); 539 } 540 541 /** 542 * @see org.opencms.ui.contextmenu.I_CmsSimpleContextMenuEntry#getVisibility(java.lang.Object) 543 */ 544 @SuppressWarnings("synthetic-access") 545 public CmsMenuItemVisibilityMode getVisibility(MenuContext context) { 546 547 CmsSitemapTreeNodeData entry = context.getData(); 548 boolean result = context.isMainLocale() 549 && !entry.isMarkedNoTranslation(m_localeContext.getComparisonLocale()) 550 && !entry.isLinked(); 551 return visibleIfTrue(result); 552 553 } 554 555 } 556 557 /** 558 * 'Open page' menu entry.<p> 559 */ 560 class EntryOpen implements I_CmsSimpleContextMenuEntry<MenuContext> { 561 562 /** 563 * @see org.opencms.ui.contextmenu.I_CmsSimpleContextMenuEntry#executeAction(java.lang.Object) 564 */ 565 @SuppressWarnings("synthetic-access") 566 public void executeAction(MenuContext context) { 567 568 openTargetPage((CmsSitemapTreeNodeData)(context.getNode().getData()), context.isTargetingLinkedResource()); 569 570 } 571 572 /** 573 * @see org.opencms.ui.contextmenu.I_CmsSimpleContextMenuEntry#getTitle(java.util.Locale) 574 */ 575 public String getTitle(Locale locale) { 576 577 return CmsVaadinUtils.getMessageText(Messages.GUI_LOCALECOMPARE_OPEN_PAGE_0); 578 579 } 580 581 /** 582 * @see org.opencms.ui.contextmenu.I_CmsSimpleContextMenuEntry#getVisibility(java.lang.Object) 583 */ 584 public CmsMenuItemVisibilityMode getVisibility(MenuContext data) { 585 586 return visibleIfTrue(true); 587 } 588 589 } 590 591 /** 592 * 'Properties' menu entry.<p> 593 */ 594 class EntryProperties implements I_CmsSimpleContextMenuEntry<MenuContext> { 595 596 /** 597 * @see org.opencms.ui.contextmenu.I_CmsSimpleContextMenuEntry#executeAction(java.lang.Object) 598 */ 599 @SuppressWarnings("synthetic-access") 600 public void executeAction(MenuContext context) { 601 602 ((CmsSitemapUI)A_CmsUI.get()).getSitemapExtension().openPropertyDialog( 603 context.getTargetResource().getStructureId(), 604 m_root.getStructureId()); 605 } 606 607 /** 608 * @see org.opencms.ui.contextmenu.I_CmsSimpleContextMenuEntry#getTitle(java.util.Locale) 609 */ 610 public String getTitle(Locale locale) { 611 612 return CmsVaadinUtils.getMessageText(Messages.GUI_LOCALECOMPARE_PROPERTIES_0); 613 614 } 615 616 /** 617 * @see org.opencms.ui.contextmenu.I_CmsSimpleContextMenuEntry#getVisibility(java.lang.Object) 618 */ 619 public CmsMenuItemVisibilityMode getVisibility(MenuContext context) { 620 621 return visibleIfTrue(true); 622 } 623 624 } 625 626 /** 627 * 'Remove mark' menu entry.<p> 628 */ 629 class EntryRemoveMark implements I_CmsSimpleContextMenuEntry<MenuContext> { 630 631 /** 632 * @see org.opencms.ui.contextmenu.I_CmsSimpleContextMenuEntry#executeAction(java.lang.Object) 633 */ 634 @SuppressWarnings("synthetic-access") 635 public void executeAction(MenuContext context) { 636 637 CmsSitemapTreeNodeData entry = context.getData(); 638 CmsSitemapTreeNode node = context.getNode(); 639 640 CmsObject cms = A_CmsUI.getCmsObject(); 641 CmsLockActionRecord actionRecord = null; 642 CmsResource fileToModify2 = null; 643 try { 644 CmsResource primary = A_CmsUI.getCmsObject().readResource( 645 entry.getClientEntry().getId(), 646 CmsResourceFilter.IGNORE_EXPIRATION); 647 if (primary.isFolder()) { 648 CmsResource defaultFile = A_CmsUI.getCmsObject().readDefaultFile( 649 primary, 650 CmsResourceFilter.IGNORE_EXPIRATION); 651 if (defaultFile != null) { 652 primary = defaultFile; 653 } 654 } 655 final CmsResource primaryFinal = primary; 656 657 fileToModify2 = primaryFinal; 658 if (fileToModify2.isFolder()) { 659 try { 660 fileToModify2 = A_CmsUI.getCmsObject().readDefaultFile( 661 fileToModify2, 662 CmsResourceFilter.IGNORE_EXPIRATION); 663 } catch (CmsException e) { 664 LOG.error(e.getLocalizedMessage(), e); 665 } 666 } 667 668 actionRecord = CmsLockUtil.ensureLock(cms, fileToModify2); 669 m_localeContext.getComparisonLocale().toString(); 670 CmsProperty prop = cms.readPropertyObject( 671 fileToModify2, 672 CmsPropertyDefinition.PROPERTY_LOCALE_NOTRANSLATION, 673 false); 674 String propValue = prop.getValue(); 675 if (propValue == null) { 676 propValue = ""; // make getLocales not return null 677 } 678 List<Locale> currentLocales = CmsLocaleManager.getLocales(propValue); 679 if (currentLocales.contains(m_localeContext.getComparisonLocale())) { 680 currentLocales.remove(m_localeContext.getComparisonLocale()); 681 String newPropValue = Joiner.on(",").join(currentLocales); 682 CmsProperty newProp = new CmsProperty( 683 CmsPropertyDefinition.PROPERTY_LOCALE_NOTRANSLATION, 684 newPropValue, 685 null); 686 cms.writePropertyObjects(primaryFinal, Arrays.asList(newProp)); 687 DialogContext dialogContext = new DialogContext( 688 A_CmsUI.getCmsObject().readResource( 689 entry.getClientEntry().getId(), 690 CmsResourceFilter.IGNORE_EXPIRATION), 691 node); 692 dialogContext.finish(Arrays.asList(fileToModify2.getStructureId())); 693 } 694 695 } catch (CmsException e) { 696 LOG.error(e.getLocalizedMessage(), e); 697 CmsErrorDialog.showErrorDialog(e); 698 699 } finally { 700 if ((actionRecord != null) && (actionRecord.getChange() == LockChange.locked)) { 701 try { 702 cms.unlockResource(fileToModify2); 703 } catch (CmsException e) { 704 LOG.error(e.getLocalizedMessage(), e); 705 CmsErrorDialog.showErrorDialog(e); 706 } 707 } 708 } 709 710 } 711 712 /** 713 * @see org.opencms.ui.contextmenu.I_CmsSimpleContextMenuEntry#getTitle(java.util.Locale) 714 */ 715 public String getTitle(Locale locale) { 716 717 return CmsVaadinUtils.getMessageText(Messages.GUI_LOCALECOMPARE_REMOVE_DONT_TRANSLATE_0); 718 } 719 720 /** 721 * @see org.opencms.ui.contextmenu.I_CmsSimpleContextMenuEntry#getVisibility(java.lang.Object) 722 */ 723 @SuppressWarnings("synthetic-access") 724 public CmsMenuItemVisibilityMode getVisibility(MenuContext context) { 725 726 boolean result = context.isMainLocale() 727 && context.getData().isMarkedNoTranslation(m_localeContext.getComparisonLocale()); 728 return visibleIfTrue(result); 729 730 } 731 } 732 733 /** 734 * 'Unlink' menu entry.<p> 735 */ 736 class EntryUnlink implements I_CmsSimpleContextMenuEntry<MenuContext> { 737 738 /** 739 * @see org.opencms.ui.contextmenu.I_CmsSimpleContextMenuEntry#executeAction(java.lang.Object) 740 */ 741 @SuppressWarnings("synthetic-access") 742 public void executeAction(MenuContext context) { 743 744 try { 745 CmsResource secondary = context.getData().getLinkedResource(); 746 DialogContext dialogContext = new DialogContext( 747 A_CmsUI.getCmsObject().readResource( 748 context.getData().getClientEntry().getId(), 749 CmsResourceFilter.IGNORE_EXPIRATION), 750 context.getNode()); 751 CmsUnlinkDialog dialog = new CmsUnlinkDialog(dialogContext, secondary); 752 dialogContext.start( 753 CmsVaadinUtils.getMessageText(Messages.GUI_LOCALECOMPARE_UNLINK_LOCALE_VARIANT_0), 754 dialog, 755 DialogWidth.wide); 756 } catch (CmsException e) { 757 LOG.error(e.getLocalizedMessage(), e); 758 CmsErrorDialog.showErrorDialog(e); 759 } 760 } 761 762 /** 763 * @see org.opencms.ui.contextmenu.I_CmsSimpleContextMenuEntry#getTitle(java.util.Locale) 764 */ 765 public String getTitle(Locale locale) { 766 767 return CmsVaadinUtils.getMessageText(Messages.GUI_LOCALECOMPARE_UNLINK_LOCALE_VARIANT_0); 768 } 769 770 /** 771 * @see org.opencms.ui.contextmenu.I_CmsSimpleContextMenuEntry#getVisibility(java.lang.Object) 772 */ 773 @SuppressWarnings("synthetic-access") 774 public CmsMenuItemVisibilityMode getVisibility(final MenuContext context) { 775 776 if (!context.getData().isLinked()) { 777 return visibleIfTrue(false); 778 } 779 try { 780 CmsResource primary = A_CmsUI.getCmsObject().readResource( 781 context.getData().getClientEntry().getId(), 782 CmsResourceFilter.IGNORE_EXPIRATION); 783 if (primary.isFolder()) { 784 CmsResource defaultFile = A_CmsUI.getCmsObject().readDefaultFile( 785 primary, 786 CmsResourceFilter.IGNORE_EXPIRATION); 787 if (defaultFile != null) { 788 primary = defaultFile; 789 } 790 } 791 CmsLocaleGroupService groupService = A_CmsUI.getCmsObject().getLocaleGroupService(); 792 Locale mainLocale = groupService.getMainLocale(m_localeContext.getRoot().getRootPath()); 793 int mainLocaleCount = 0; 794 for (Locale testLocale : Arrays.asList( 795 m_localeContext.getRootLocale(), 796 m_localeContext.getComparisonLocale())) { 797 mainLocaleCount += mainLocale.equals(testLocale) ? 1 : 0; 798 } 799 return visibleIfTrue(mainLocaleCount == 1); 800 } catch (Exception e) { 801 return visibleIfTrue(false); 802 803 } 804 805 } 806 } 807 808 /** 809 * Context object for the context menu.<p> 810 */ 811 class MenuContext { 812 813 /** The tree node data. */ 814 private CmsSitemapTreeNodeData m_data; 815 816 /** The tree node widget. */ 817 private CmsSitemapTreeNode m_node; 818 819 /** True if menu entries should act on the linked resource. */ 820 private boolean m_isTargetingLinkedResource; 821 822 /** 823 * Creates a new instance.<p> 824 * 825 * @param data the sitemap tree data 826 * @param node the tree node widget 827 * @param isTargetingLinkedResource true if the menu entries should act on the linked resource 828 */ 829 public MenuContext(CmsSitemapTreeNodeData data, CmsSitemapTreeNode node, boolean isTargetingLinkedResource) { 830 831 m_node = node; 832 m_data = data; 833 m_isTargetingLinkedResource = isTargetingLinkedResource; 834 835 } 836 837 /** 838 * Gets the tree node data.<p> 839 * 840 * @return the tree node data 841 */ 842 public CmsSitemapTreeNodeData getData() { 843 844 return m_data; 845 } 846 847 /** 848 * Gets the tree node widget.<p> 849 * 850 * @return the tree node widget 851 */ 852 public CmsSitemapTreeNode getNode() { 853 854 return m_node; 855 } 856 857 /** 858 * Gets the resource which context menu entries should operate on. 859 * 860 * @return the resource which context menu entries should operate on 861 */ 862 public CmsResource getTargetResource() { 863 864 if (m_isTargetingLinkedResource) { 865 return m_data.getLinkedEntryResource(); 866 } else { 867 return m_data.getResource(); 868 } 869 } 870 871 /** 872 * Checks if the currently selected locale is the main locale.<p> 873 * 874 * @return true if we are in the main locale 875 */ 876 @SuppressWarnings("synthetic-access") 877 public boolean isMainLocale() { 878 879 return m_localeContext.getRootLocale().equals( 880 A_CmsUI.getCmsObject().getLocaleGroupService().getMainLocale(m_localeContext.getRoot().getRootPath())); 881 } 882 883 /** 884 * Returns true if the menu entries should act on the linked resource if possible. 885 * 886 * @return true if the menu entries should act on the linked resource 887 */ 888 public boolean isTargetingLinkedResource() { 889 890 return m_isTargetingLinkedResource; 891 } 892 893 } 894 895 /** Default width for linked items displayed on the right side of tree items. */ 896 public static final int RHS_WIDTH = 420; 897 898 /** The log isntance for this class. */ 899 private static final Log LOG = CmsLog.getLog(CmsSitemapTreeController.class); 900 901 /** The context menu. */ 902 CmsContextMenu m_menu = new CmsContextMenu(); 903 904 /** The currently opened window. */ 905 Window m_window; 906 907 /** Map of already loaded nodes. */ 908 private IdentityHashMap<CmsSitemapTreeNode, Void> m_alreadyLoaded = new IdentityHashMap<>(); 909 910 /** Current root node widget. */ 911 private CmsSitemapTreeNode m_currentRootNode; 912 913 /** The locale context. */ 914 private I_CmsLocaleCompareContext m_localeContext; 915 916 /** The resource corresponding to the tree's root. */ 917 private CmsResource m_root; 918 919 /** The tree data provider. */ 920 private CmsSitemapTreeDataProvider m_treeDataProvider; 921 922 /** 923 * Creates a new instance.<p> 924 * 925 * @param cms the CMS context 926 * @param root the tree's root resource 927 * @param context the locale comparison context 928 * @param parent the parent widget in which the tree will be rendered 929 */ 930 public CmsSitemapTreeController( 931 CmsObject cms, 932 CmsResource root, 933 I_CmsLocaleCompareContext context, 934 Component parent) { 935 936 m_treeDataProvider = new CmsSitemapTreeDataProvider(cms, root, context); 937 m_localeContext = context; 938 m_root = root; 939 m_menu.extend((AbstractComponent)parent); 940 941 } 942 943 /** 944 * Returns VISIBILITY_ACTIVE if the given parameter is true, and VISIBILITY_INACTIVE otherwise.<p> 945 * 946 * @param condition a boolean value 947 * @return the visibility based on the condition value 948 */ 949 public static CmsMenuItemVisibilityMode activeIfTrue(boolean condition) { 950 951 return condition ? CmsMenuItemVisibilityMode.VISIBILITY_ACTIVE : CmsMenuItemVisibilityMode.VISIBILITY_INACTIVE; 952 } 953 954 /** 955 * If the given resource is the default file of a sitmeap entry folder, then returns that 956 * folder, else the original file.<p> 957 * 958 * @param resource a resource 959 * @return the resource or its parent folder 960 */ 961 public static CmsResource readSitemapEntryFolderIfPossible(CmsResource resource) { 962 963 CmsObject cms = A_CmsUI.getCmsObject(); 964 try { 965 if (resource.isFolder()) { 966 return resource; 967 } 968 CmsResource parent = cms.readParentFolder(resource.getStructureId()); 969 CmsResource defaultFile = cms.readDefaultFile(parent, CmsResourceFilter.IGNORE_EXPIRATION); 970 if ((defaultFile != null) && defaultFile.equals(resource)) { 971 return parent; 972 } 973 return resource; 974 } catch (CmsException e) { 975 LOG.error(e.getLocalizedMessage(), e); 976 return resource; 977 } 978 } 979 980 /** 981 * Returns VISIBILITY_ACTIVE if the given parameter is true, and VISIBILITY_INVISIBLE otherwise.<p> 982 * 983 * @param condition a boolean value 984 * @return the visibility based on the condition value 985 */ 986 public static CmsMenuItemVisibilityMode visibleIfTrue(boolean condition) { 987 988 return condition ? CmsMenuItemVisibilityMode.VISIBILITY_ACTIVE : CmsMenuItemVisibilityMode.VISIBILITY_INVISIBLE; 989 } 990 991 /** 992 * Creates a sitemap tree node widget from a tree node bean.<p> 993 * 994 * @param entry the tree node bean 995 * @return the tree node widget 996 */ 997 public CmsSitemapTreeNode createNode(final CmsSitemapTreeNodeData entry) { 998 999 final CmsSitemapTreeNode node = new CmsSitemapTreeNode(); 1000 1001 node.addLayoutClickListener(new LayoutClickListener() { 1002 1003 private static final long serialVersionUID = 1L; 1004 1005 @SuppressWarnings("synthetic-access") 1006 public void layoutClick(LayoutClickEvent event) { 1007 1008 Component currentComponent = event.getClickedComponent(); 1009 if (currentComponent != null) { 1010 boolean linked = false; 1011 do { 1012 currentComponent = currentComponent.getParent(); 1013 if ((currentComponent != null) 1014 && "linked".equals(((AbstractComponent)currentComponent).getData())) { 1015 linked = true; 1016 } 1017 if (event.getClickedComponent() instanceof CmsResourceIcon) { 1018 if (currentComponent == node) { 1019 openTargetPage((CmsSitemapTreeNodeData)(node.getData()), linked); 1020 } else if (currentComponent instanceof CmsSitemapTreeNode) { 1021 break; 1022 } 1023 } 1024 } while (currentComponent != null); 1025 } 1026 1027 } 1028 1029 }); 1030 Resource icon = CmsResourceIcon.getSitemapResourceIcon( 1031 A_CmsUI.getCmsObject(), 1032 entry.getResource(), 1033 IconMode.localeCompare); 1034 CmsResourceInfo info = new CmsResourceInfo( 1035 entry.getClientEntry().getTitle(), 1036 entry.getClientEntry().getSitePath(), 1037 icon); 1038 info = CmsResourceInfo.createSitemapResourceInfo( 1039 entry.getResource(), 1040 OpenCms.getSiteManager().getSiteForRootPath(m_localeContext.getRoot().getRootPath())); 1041 info.getResourceIcon().addStyleName(OpenCmsTheme.POINTER); 1042 info.getResourceIcon().setDescription(CmsVaadinUtils.getMessageText(Messages.GUI_LOCALECOMPARE_OPEN_PAGE_0)); 1043 1044 if (entry.getClientEntry().isHiddenNavigationEntry()) { 1045 info.addStyleName(OpenCmsTheme.RESOURCE_INFO_WEAK); 1046 } 1047 1048 boolean noTranslation = false; 1049 noTranslation = entry.isMarkedNoTranslation(m_localeContext.getComparisonLocale()); 1050 CssLayout rightSide = new CssLayout(); 1051 info.setButtonWidget(rightSide); 1052 1053 final MenuBar menu = createMenu(entry, node, false); 1054 rightSide.addComponent(menu); 1055 if (entry.isLinked()) { 1056 CmsSite site = OpenCms.getSiteManager().getSiteForRootPath(m_localeContext.getRoot().getRootPath()); 1057 CmsResourceInfo linkedInfo = CmsResourceInfo.createSitemapResourceInfo( 1058 readSitemapEntryFolderIfPossible(entry.getLinkedResource()), 1059 site); 1060 linkedInfo.addStyleName(OpenCmsTheme.RESOURCE_INFO_DIRECTLINK); 1061 CssLayout linkedRightSide = new CssLayout(); 1062 linkedInfo.setButtonWidget(linkedRightSide); 1063 linkedRightSide.addComponent(createMenu(entry, node, true)); 1064 rightSide.addComponent(linkedInfo); 1065 linkedInfo.setWidth(RHS_WIDTH + "px"); 1066 node.setContent(info); 1067 linkedInfo.setData("linked"); // Data used by click handler to distinguish clicked resource icons 1068 linkedInfo.getResourceIcon().setDescription( 1069 CmsVaadinUtils.getMessageText(Messages.GUI_LOCALECOMPARE_OPEN_PAGE_0)); 1070 linkedInfo.getResourceIcon().addStyleName(OpenCmsTheme.POINTER); 1071 } else { 1072 if (noTranslation) { 1073 CmsResourceInfo noTranslationInfo = new CmsResourceInfo(); 1074 String topMessage = CmsVaadinUtils.getMessageText(Messages.GUI_LOCALECOMPARE_NO_TRANSLATION_TOP_0); 1075 String bottomMessage = CmsVaadinUtils.getMessageText( 1076 Messages.GUI_LOCALECOMPARE_NO_TRANSLATION_BOTTOM_0); 1077 noTranslationInfo.getTopLine().setValue(topMessage); 1078 noTranslationInfo.getBottomLine().setValue(bottomMessage); 1079 noTranslationInfo.getResourceIcon().setValue( 1080 "<span class=\"" 1081 + OpenCmsTheme.RESOURCE_ICON 1082 + " " 1083 + OpenCmsTheme.NO_TRANSLATION_ICON 1084 + "\">" 1085 + FontAwesome.BAN.getHtml() 1086 + "</span>"); 1087 noTranslationInfo.addStyleName(OpenCmsTheme.RESOURCE_INFO_DIRECTLINK); 1088 noTranslationInfo.setWidth(RHS_WIDTH + "px"); 1089 rightSide.addComponent(noTranslationInfo); 1090 } 1091 node.setContent(info); 1092 } 1093 1094 if (entry.hasNoChildren()) { 1095 node.setOpen(true); 1096 node.setOpenerVisible(false); 1097 } 1098 node.setData(entry); 1099 return node; 1100 1101 } 1102 1103 /** 1104 * Creates the root node of the tree.<p> 1105 * 1106 * @return the root node of the tree 1107 */ 1108 public CmsSitemapTreeNode createRootNode() { 1109 1110 m_currentRootNode = createNode(m_treeDataProvider.getRoot()); 1111 return m_currentRootNode; 1112 } 1113 1114 /** 1115 * Gets the resource corresponding to the tree's root.<p> 1116 * 1117 * @return the resource for the root node 1118 */ 1119 public CmsResource getRoot() { 1120 1121 return m_root; 1122 } 1123 1124 /** 1125 * Initializes the event handlers for a tree node widget.<p> 1126 * 1127 * @param node the node for which to initialize the event handlers 1128 */ 1129 public void initEventHandlers(final CmsSitemapTreeNode node) { 1130 1131 node.getOpener().addClickListener(new ClickListener() { 1132 1133 private static final long serialVersionUID = 1L; 1134 1135 public void buttonClick(ClickEvent event) { 1136 1137 CmsSitemapTreeController.this.onClickOpen(node); 1138 } 1139 }); 1140 } 1141 1142 /** 1143 * Called when the user clicks on the 'opener' icon of a sitemap tree entry.<p> 1144 * 1145 * @param node the sitemap node widget 1146 */ 1147 public void onClickOpen(CmsSitemapTreeNode node) { 1148 1149 if (node.isOpen()) { 1150 node.setOpen(false); 1151 } else { 1152 if (!m_alreadyLoaded.containsKey(node)) { 1153 Object nodeData = node.getData(); 1154 List<CmsSitemapTreeNodeData> children = m_treeDataProvider.getChildren( 1155 (CmsSitemapTreeNodeData)nodeData); 1156 m_alreadyLoaded.put(node, null); 1157 if (children.isEmpty()) { 1158 node.setOpenerVisible(false); 1159 } else { 1160 for (CmsSitemapTreeNodeData child : children) { 1161 CmsSitemapTreeNode childNode = createNode(child); 1162 childNode.setData(child); 1163 initEventHandlers(childNode); 1164 node.getChildren().addComponent(childNode); 1165 } 1166 } 1167 } 1168 node.setOpen(true); 1169 } 1170 } 1171 1172 /** 1173 * Updates a sitemap node widget after the resource it corresponds to has changed.<p> 1174 * 1175 * @param node the sitemap node 1176 */ 1177 public void updateNode(CmsSitemapTreeNode node) { 1178 1179 CmsSitemapTreeNodeData data = (CmsSitemapTreeNodeData)node.getData(); 1180 try { 1181 CmsSitemapTreeNodeData changedData = m_treeDataProvider.getData( 1182 A_CmsUI.getCmsObject().readResource( 1183 data.getClientEntry().getId(), 1184 CmsResourceFilter.IGNORE_EXPIRATION)); 1185 CmsSitemapTreeNode changedNode = createNode(changedData); 1186 initEventHandlers(changedNode); 1187 ComponentContainer parent = (ComponentContainer)(node.getParent()); 1188 parent.replaceComponent(node, changedNode); 1189 } catch (CmsException e) { 1190 LOG.error(e.getLocalizedMessage(), e); 1191 } 1192 } 1193 1194 /** 1195 * Updates the tree node for the resource with the given structure id, if it exists.<p> 1196 * 1197 * @param id the structure id of a resource 1198 */ 1199 public void updateNodeForId(final CmsUUID id) { 1200 1201 final List<CmsSitemapTreeNode> nodes = Lists.newArrayList(); 1202 CmsVaadinUtils.visitDescendants(m_currentRootNode, new Predicate<Component>() { 1203 1204 public boolean apply(Component input) { 1205 1206 if (input instanceof CmsSitemapTreeNode) { 1207 CmsSitemapTreeNode node = (CmsSitemapTreeNode)input; 1208 CmsSitemapTreeNodeData data = (CmsSitemapTreeNodeData)node.getData(); 1209 if (data.getResource().getStructureId().equals(id) 1210 || ((data.getLinkedEntryResource() != null) 1211 && data.getLinkedEntryResource().getStructureId().equals(id))) { 1212 nodes.add(node); 1213 return false; 1214 } 1215 } 1216 return true; 1217 } 1218 }); 1219 if (nodes.size() == 1) { 1220 updateNode(nodes.get(0)); 1221 } 1222 1223 } 1224 1225 /** 1226 * Gets the logger for the tree controller.<p> 1227 * 1228 * @return the logger 1229 */ 1230 Log getTreeControllerLog() { 1231 1232 return LOG; 1233 } 1234 1235 /** 1236 * Creates the context menu for either the main resource or the linked resource of a tree node. 1237 * 1238 * @param entry the tree node data 1239 * @param node the tree node widget 1240 * @param linked true if we want to create the menu for the linked resource 1241 * 1242 * @return the context menu 1243 */ 1244 private MenuBar createMenu(final CmsSitemapTreeNodeData entry, final CmsSitemapTreeNode node, boolean linked) { 1245 1246 final MenuBar menu = new MenuBar(); 1247 final MenuItem main = menu.addItem("", null); 1248 main.setIcon(FontOpenCms.CONTEXT_MENU); 1249 main.setCommand(new Command() { 1250 1251 /** Serial version id. */ 1252 private static final long serialVersionUID = 1L; 1253 1254 public void menuSelected(MenuItem selectedItem) { 1255 1256 List<I_CmsSimpleContextMenuEntry<MenuContext>> entries = Arrays.asList( 1257 1258 new EntryOpen(), 1259 new EntryExplorer(), 1260 new EntryProperties(), 1261 new EntryLink(), 1262 new EntryUnlink(), 1263 new EntryMark(), 1264 new EntryRemoveMark(), 1265 new EntryCopy(), 1266 new EntryInfo()); 1267 1268 MenuContext context = new MenuContext(entry, node, linked); 1269 m_menu.setEntries(entries, context); 1270 m_menu.open(menu); 1271 1272 } 1273 1274 }); 1275 menu.addStyleName("borderless o-toolbar-button o-resourceinfo-toolbar"); 1276 return menu; 1277 } 1278 1279 /** 1280 * Opens the page corresponding to a sitemap entry.<p> 1281 * 1282 * @param nodeData the node bean 1283 * @param second true if the user has clicked on the second resource box in a tree node 1284 */ 1285 private void openTargetPage(CmsSitemapTreeNodeData nodeData, boolean second) { 1286 1287 CmsUUID id = nodeData.getClientEntry().getId(); 1288 CmsUUID defaultFileId = nodeData.getClientEntry().getDefaultFileId(); 1289 CmsUUID targetId = defaultFileId; 1290 if (targetId == null) { 1291 targetId = id; 1292 } 1293 try { 1294 CmsResource resource = A_CmsUI.getCmsObject().readResource(targetId, CmsResourceFilter.IGNORE_EXPIRATION); 1295 String link = OpenCms.getLinkManager().substituteLink(A_CmsUI.getCmsObject(), resource); 1296 if (second) { 1297 resource = A_CmsUI.getCmsObject().readResource( 1298 nodeData.getLinkedResource().getStructureId(), 1299 CmsResourceFilter.IGNORE_EXPIRATION); 1300 link = OpenCms.getLinkManager().substituteLink(A_CmsUI.getCmsObject(), resource); 1301 } 1302 1303 String mySiteRoot = A_CmsUI.getCmsObject().getRequestContext().getSiteRoot(); 1304 final boolean sameSite = mySiteRoot.equals(OpenCms.getSiteManager().getSiteRoot(resource.getRootPath())); 1305 1306 if (sameSite) { 1307 A_CmsUI.get().getPage().setLocation(link); 1308 } else { 1309 String message = CmsVaadinUtils.getMessageText( 1310 Messages.GUI_LOCALECOMPARE_SHOW_WRONGSITE_1, 1311 resource.getRootPath()); 1312 1313 Notification.show(message, Type.ERROR_MESSAGE); 1314 } 1315 } catch (CmsException e) { 1316 LOG.error(e.getLocalizedMessage(), e); 1317 } 1318 1319 } 1320 1321}