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.ui.apps; 029 030import org.opencms.db.CmsUserSettings; 031import org.opencms.file.CmsObject; 032import org.opencms.file.CmsProject; 033import org.opencms.file.CmsResource; 034import org.opencms.file.CmsResourceFilter; 035import org.opencms.file.CmsVfsResourceNotFoundException; 036import org.opencms.main.CmsException; 037import org.opencms.main.CmsLog; 038import org.opencms.main.OpenCms; 039import org.opencms.security.CmsPermissionSet; 040import org.opencms.site.CmsSiteManagerImpl; 041import org.opencms.ui.A_CmsUI; 042import org.opencms.ui.CmsVaadinUtils; 043import org.opencms.ui.FontOpenCms; 044import org.opencms.ui.I_CmsDialogContext; 045import org.opencms.ui.I_CmsDialogContext.ContextType; 046import org.opencms.ui.I_CmsUpdateListener; 047import org.opencms.ui.actions.CmsCopyDialogAction; 048import org.opencms.ui.actions.CmsPropertiesDialogAction; 049import org.opencms.ui.actions.I_CmsWorkplaceAction; 050import org.opencms.ui.components.A_CmsFocusShortcutListener; 051import org.opencms.ui.components.CmsErrorDialog; 052import org.opencms.ui.components.CmsExtendedSiteSelector; 053import org.opencms.ui.components.CmsFileTable; 054import org.opencms.ui.components.CmsResourceIcon; 055import org.opencms.ui.components.CmsResourceTableProperty; 056import org.opencms.ui.components.CmsToolBar; 057import org.opencms.ui.components.CmsUploadButton; 058import org.opencms.ui.components.CmsUploadButton.I_UploadListener; 059import org.opencms.ui.components.I_CmsWindowCloseListener; 060import org.opencms.ui.components.OpenCmsTheme; 061import org.opencms.ui.components.extensions.CmsUploadAreaExtension; 062import org.opencms.ui.contextmenu.CmsResourceContextMenuBuilder; 063import org.opencms.ui.dialogs.CmsCopyMoveDialog; 064import org.opencms.ui.dialogs.CmsDeleteDialog; 065import org.opencms.ui.dialogs.CmsNewDialog; 066import org.opencms.util.CmsFileUtil; 067import org.opencms.util.CmsStringUtil; 068import org.opencms.util.CmsUUID; 069import org.opencms.workplace.CmsWorkplace; 070import org.opencms.workplace.explorer.CmsResourceUtil; 071 072import java.io.IOException; 073import java.nio.charset.StandardCharsets; 074import java.util.ArrayList; 075import java.util.Arrays; 076import java.util.Collection; 077import java.util.Collections; 078import java.util.HashMap; 079import java.util.HashSet; 080import java.util.List; 081import java.util.Map; 082import java.util.Set; 083 084import org.apache.commons.lang.RandomStringUtils; 085import org.apache.commons.logging.Log; 086 087import com.google.common.collect.Lists; 088import com.google.common.collect.Sets; 089import com.vaadin.event.Action; 090import com.vaadin.event.FieldEvents.BlurEvent; 091import com.vaadin.event.FieldEvents.FocusEvent; 092import com.vaadin.event.ShortcutAction; 093import com.vaadin.event.ShortcutAction.KeyCode; 094import com.vaadin.event.dd.DragAndDropEvent; 095import com.vaadin.event.dd.DropHandler; 096import com.vaadin.event.dd.acceptcriteria.AcceptCriterion; 097import com.vaadin.event.dd.acceptcriteria.ServerSideCriterion; 098import com.vaadin.navigator.ViewChangeListener; 099import com.vaadin.server.Page; 100import com.vaadin.server.Sizeable.Unit; 101import com.vaadin.ui.Button; 102import com.vaadin.ui.Button.ClickEvent; 103import com.vaadin.ui.Button.ClickListener; 104import com.vaadin.ui.CssLayout; 105import com.vaadin.ui.HorizontalSplitPanel; 106import com.vaadin.ui.JavaScript; 107import com.vaadin.ui.Notification; 108import com.vaadin.ui.UI; 109import com.vaadin.ui.themes.ValoTheme; 110import com.vaadin.v7.data.Container; 111import com.vaadin.v7.data.Item; 112import com.vaadin.v7.data.Property.ValueChangeEvent; 113import com.vaadin.v7.data.Property.ValueChangeListener; 114import com.vaadin.v7.data.util.BeanItemContainer; 115import com.vaadin.v7.data.util.HierarchicalContainer; 116import com.vaadin.v7.event.FieldEvents.TextChangeEvent; 117import com.vaadin.v7.event.FieldEvents.TextChangeListener; 118import com.vaadin.v7.event.ItemClickEvent; 119import com.vaadin.v7.event.ItemClickEvent.ItemClickListener; 120import com.vaadin.v7.shared.ui.combobox.FilteringMode; 121import com.vaadin.v7.ui.AbstractSelect.AbstractSelectTargetDetails; 122import com.vaadin.v7.ui.ComboBox; 123import com.vaadin.v7.ui.HorizontalLayout; 124import com.vaadin.v7.ui.Table; 125import com.vaadin.v7.ui.Table.TableDragMode; 126import com.vaadin.v7.ui.TextField; 127import com.vaadin.v7.ui.Tree; 128import com.vaadin.v7.ui.Tree.CollapseEvent; 129import com.vaadin.v7.ui.Tree.CollapseListener; 130import com.vaadin.v7.ui.Tree.ExpandEvent; 131import com.vaadin.v7.ui.Tree.ExpandListener; 132import com.vaadin.v7.ui.Tree.ItemStyleGenerator; 133import com.vaadin.v7.ui.Tree.TreeDragMode; 134 135/** 136 * The file explorer app.<p> 137 */ 138@SuppressWarnings("deprecation") 139public class CmsFileExplorer 140implements I_CmsWorkplaceApp, I_CmsCachableApp, ViewChangeListener, I_CmsWindowCloseListener, I_CmsHasShortcutActions, 141I_CmsContextProvider, CmsFileTable.I_FolderSelectHandler { 142 143 /** The drop handler for copy/move operations. */ 144 public class ExplorerDropHandler implements DropHandler { 145 146 /** The serial version id. */ 147 private static final long serialVersionUID = 5392136127699472654L; 148 149 /** The copy move action. */ 150 transient final I_CmsWorkplaceAction m_copyMoveAction = new CmsCopyDialogAction(); 151 152 /** 153 * @see com.vaadin.event.dd.DropHandler#drop(com.vaadin.event.dd.DragAndDropEvent) 154 */ 155 public void drop(DragAndDropEvent dragEvent) { 156 157 try { 158 CmsExplorerDialogContext context = getContext(dragEvent); 159 if (m_copyMoveAction.isActive(context)) { 160 CmsCopyMoveDialog dialog = new CmsCopyMoveDialog( 161 context, 162 CmsCopyMoveDialog.DialogMode.copy_and_move); 163 dialog.setTargetFolder(getTargetId(dragEvent)); 164 context.start( 165 CmsVaadinUtils.getMessageText(org.opencms.ui.Messages.GUI_DIALOGTITLE_COPYMOVE_0), 166 dialog); 167 } 168 } catch (Exception e) { 169 LOG.error("Moving resource failed", e); 170 } 171 172 } 173 174 /** 175 * @see com.vaadin.event.dd.DropHandler#getAcceptCriterion() 176 */ 177 public AcceptCriterion getAcceptCriterion() { 178 179 return new ServerSideCriterion() { 180 181 private static final long serialVersionUID = 1L; 182 183 public boolean accept(DragAndDropEvent dragEvent) { 184 185 try { 186 if (!m_copyMoveAction.isActive(getContext(dragEvent))) { 187 return false; 188 } 189 } catch (CmsException e) { 190 LOG.error("Drag an drop evaluation failed", e); 191 return false; 192 } 193 CmsUUID targetId = getTargetId(dragEvent); 194 return mayDrop(targetId); 195 } 196 }; 197 } 198 199 /** 200 * Returns the drag target id.<p> 201 * 202 * @param dragEvent the drag event 203 * 204 * @return the drag target id 205 */ 206 protected CmsUUID getTargetId(DragAndDropEvent dragEvent) { 207 208 CmsUUID targetId = null; 209 if (dragEvent.getTargetDetails() instanceof AbstractSelectTargetDetails) { 210 AbstractSelectTargetDetails target = (AbstractSelectTargetDetails)dragEvent.getTargetDetails(); 211 Object itemOverId = target.getItemIdOver(); 212 if (itemOverId instanceof CmsUUID) { 213 targetId = (CmsUUID)itemOverId; 214 } else if (itemOverId instanceof String) { 215 targetId = m_fileTable.getUUIDFromItemID((String)itemOverId); 216 } 217 } 218 try { 219 CmsObject cms = A_CmsUI.getCmsObject(); 220 CmsResource target = cms.readResource(targetId); 221 if (target.isFile()) { 222 targetId = null; 223 } 224 } catch (CmsException e) { 225 targetId = null; 226 LOG.debug("Checking drop target failed, use current folder.", e); 227 } 228 229 if (targetId == null) { 230 targetId = getCurrentFolder(); 231 } 232 return targetId; 233 } 234 235 /** 236 * Evaluates if a drop on the given target is allowed.<p> 237 * 238 * @param targetId the target id 239 * 240 * @return <code>true</code> if the resources may be dropped to the given target 241 */ 242 protected boolean mayDrop(CmsUUID targetId) { 243 244 boolean result = false; 245 try { 246 CmsObject cms = A_CmsUI.getCmsObject(); 247 CmsResource target = cms.readResource(targetId); 248 result = cms.hasPermissions( 249 target, 250 CmsPermissionSet.ACCESS_WRITE, 251 false, 252 CmsResourceFilter.ONLY_VISIBLE_NO_DELETED); 253 } catch (Exception e) { 254 LOG.debug("Checking folder write permissions failed", e); 255 } 256 return result; 257 } 258 259 /** 260 * Returns the dialog context to use.<p> 261 * 262 * @param dragEvent the drag event 263 * 264 * @return the dialog context 265 * 266 * @throws CmsException if reading the drag resource fails 267 */ 268 CmsExplorerDialogContext getContext(DragAndDropEvent dragEvent) throws CmsException { 269 270 List<CmsResource> resources; 271 if ((dragEvent.getTransferable().getSourceComponent() instanceof Table) 272 && !m_fileTable.getSelectedResources().isEmpty()) { 273 resources = m_fileTable.getSelectedResources(); 274 } else { 275 CmsObject cms = A_CmsUI.getCmsObject(); 276 CmsUUID sourceId = m_fileTable.getUUIDFromItemID((String)dragEvent.getTransferable().getData("itemId")); 277 CmsResource source = cms.readResource(sourceId, CmsResourceFilter.IGNORE_EXPIRATION); 278 resources = Collections.singletonList(source); 279 } 280 CmsExplorerDialogContext context = new CmsExplorerDialogContext( 281 ContextType.fileTable, 282 m_fileTable, 283 CmsFileExplorer.this, 284 resources); 285 return context; 286 } 287 } 288 289 /** 290 * File tree expand listener.<p> 291 */ 292 public class TreeExpandListener implements ExpandListener { 293 294 /** The serial version id. */ 295 private static final long serialVersionUID = 1L; 296 /** 297 * The path fragment being opened. 298 * Will override folder visibility in case the the target path is visible to the user. 299 **/ 300 private String m_openPathFragment; 301 302 /** 303 * @see com.vaadin.ui.Tree.ExpandListener#nodeExpand(com.vaadin.ui.Tree.ExpandEvent) 304 */ 305 public void nodeExpand(ExpandEvent event) { 306 307 selectTreeItem((CmsUUID)event.getItemId()); 308 readTreeLevel((CmsUUID)event.getItemId(), m_openPathFragment); 309 } 310 311 /** 312 * Sets the open path fragment.<p> 313 * 314 * @param openPathFragment the open path fragment 315 */ 316 public void setOpenPathFragment(String openPathFragment) { 317 318 m_openPathFragment = openPathFragment; 319 } 320 } 321 322 /** Bean representing the file explorer navigation substate. */ 323 static class StateBean { 324 325 /** Current folder. */ 326 private String m_folder; 327 328 /** Project id. */ 329 private String m_projectId; 330 331 /**selected resource.*/ 332 private String m_selectedResource; 333 334 /** The site root. */ 335 private String m_siteRoot; 336 337 /** 338 * Creates a new state bean.<p> 339 * 340 * @param siteRoot the site root 341 * @param folder the folder 342 * @param projectId the project id 343 */ 344 public StateBean(String siteRoot, String folder, String projectId) { 345 346 m_siteRoot = siteRoot; 347 m_folder = folder; 348 m_projectId = projectId; 349 if ("".equals(m_siteRoot)) { 350 m_siteRoot = "/"; 351 } 352 } 353 354 /** 355 * Parses the state bean from a string.<p> 356 * 357 * @param state the state string 358 * @return the state bean 359 */ 360 public static StateBean parse(String state) { 361 362 List<String> fields = CmsStringUtil.splitAsList(state, A_CmsWorkplaceApp.PARAM_SEPARATOR); 363 if (fields.size() >= 3) { 364 String projectId = fields.get(0); 365 String siteRoot = fields.get(1); 366 String folder = fields.get(2); 367 StateBean ret = new StateBean(siteRoot, folder, projectId); 368 if (fields.size() == 4) { 369 ret.setSelectedResource(fields.get(3)); 370 } 371 return ret; 372 } else { 373 return new StateBean(null, null, null); 374 } 375 } 376 377 /** 378 * Converts state bean to a string.<p> 379 * 380 * @return the string format of the state 381 */ 382 public String asString() { 383 384 String result = m_projectId 385 + A_CmsWorkplaceApp.PARAM_SEPARATOR 386 + m_siteRoot 387 + A_CmsWorkplaceApp.PARAM_SEPARATOR 388 + m_folder 389 + A_CmsWorkplaceApp.PARAM_SEPARATOR; 390 return result; 391 } 392 393 /** 394 * Returns the folderId.<p> 395 * 396 * @return the folderId 397 */ 398 public String getFolder() { 399 400 return m_folder; 401 } 402 403 /** 404 * Returns the projectId.<p> 405 * 406 * @return the projectId 407 */ 408 public String getProjectId() { 409 410 return m_projectId; 411 } 412 413 /** 414 * Returns the resource to select, empty if no one was set.<p> 415 * 416 * @return UUID as string 417 */ 418 public String getSelectedResource() { 419 420 return m_selectedResource == null ? "" : m_selectedResource; 421 } 422 423 /** 424 * Returns the siteRoot.<p> 425 * 426 * @return the siteRoot 427 */ 428 public String getSiteRoot() { 429 430 return m_siteRoot; 431 } 432 433 /** 434 * Sets a resource to be selected.<p> 435 * 436 * @param resource to get selected 437 */ 438 public void setSelectedResource(String resource) { 439 440 m_selectedResource = resource; 441 } 442 } 443 444 /** The file explorer attribute key. */ 445 public static final String ATTR_KEY = "CmsFileExplorer"; 446 447 /** The in line editable resource properties. */ 448 public static final Collection<CmsResourceTableProperty> INLINE_EDIT_PROPERTIES = Arrays.asList( 449 CmsResourceTableProperty.PROPERTY_RESOURCE_NAME, 450 CmsResourceTableProperty.PROPERTY_TITLE, 451 CmsResourceTableProperty.PROPERTY_NAVIGATION_TEXT, 452 CmsResourceTableProperty.PROPERTY_COPYRIGHT, 453 CmsResourceTableProperty.PROPERTY_CACHE); 454 455 /** The initial split position between folder tree and file table. */ 456 public static final int LAYOUT_SPLIT_POSITION = 399; 457 458 /** The opened paths session attribute name. */ 459 public static final String OPENED_PATHS = "explorer-opened-paths"; 460 461 /** Site selector caption property. */ 462 public static final String SITE_CAPTION = "site_caption"; 463 464 /** Site selector site root property. */ 465 public static final String SITE_ROOT = "site_root"; 466 467 /** Threshold for updating the complete folder after file changes. */ 468 public static final int UPDATE_FOLDER_THRESHOLD = 200; 469 470 /** Logger instance for this class. */ 471 static final Log LOG = CmsLog.getLog(CmsFileExplorer.class); 472 473 /** The delete shortcut. */ 474 private static final Action ACTION_DELETE = new ShortcutAction("Del", ShortcutAction.KeyCode.DELETE, null); 475 476 /** The open parent folder shortcut. */ 477 private static final Action ACTION_FOLDER_UP = new ShortcutAction( 478 "Alt+ArrowUp", 479 ShortcutAction.KeyCode.ARROW_UP, 480 new int[] {ShortcutAction.ModifierKey.ALT}); 481 482 /** The edit properties shortcut. */ 483 private static final Action ACTION_PROPERTIES = new ShortcutAction( 484 "Alt+Enter", 485 ShortcutAction.KeyCode.ENTER, 486 new int[] {ShortcutAction.ModifierKey.ALT}); 487 488 /** The rename shortcut. */ 489 private static final Action ACTION_RENAME = new ShortcutAction("F2", ShortcutAction.KeyCode.F2, null); 490 491 /** The select all shortcut. */ 492 private static final Action ACTION_SELECT_ALL = new ShortcutAction( 493 "Ctrl+A", 494 ShortcutAction.KeyCode.A, 495 new int[] {ShortcutAction.ModifierKey.CTRL}); 496 497 /** The select all shortcut, (using Apple CMD as modifier). */ 498 private static final Action ACTION_SELECT_ALL_CMD = new ShortcutAction( 499 "CMD+A", 500 ShortcutAction.KeyCode.A, 501 new int[] {ShortcutAction.ModifierKey.META}); 502 503 /** The switch online shortcut. */ 504 private static final Action ACTION_SWITCH_ONLINE = new ShortcutAction( 505 "Ctrl+O", 506 ShortcutAction.KeyCode.O, 507 new int[] {ShortcutAction.ModifierKey.CTRL}); 508 509 /** The switch online shortcut, (using Apple CMD as modifier). */ 510 private static final Action ACTION_SWITCH_ONLINE_CMD = new ShortcutAction( 511 "CMD+O", 512 ShortcutAction.KeyCode.O, 513 new int[] {ShortcutAction.ModifierKey.META}); 514 515 /** The files and folder resource filter. */ 516 private static final CmsResourceFilter FILES_N_FOLDERS = CmsResourceFilter.ONLY_VISIBLE; 517 518 /** The folders resource filter. */ 519 private static final CmsResourceFilter FOLDERS = CmsResourceFilter.ONLY_VISIBLE_NO_DELETED.addRequireFolder(); 520 521 /** The serial version id. */ 522 private static final long serialVersionUID = 1L; 523 524 /** The UI context. */ 525 protected I_CmsAppUIContext m_appContext; 526 527 /** Saved explorer state used by dialogs after they have finished. */ 528 protected String m_savedExplorerState = ""; 529 530 /** The table containing the contents of the current folder. */ 531 CmsFileTable m_fileTable; 532 533 /** The info path. */ 534 com.vaadin.ui.TextField m_infoPath; 535 536 /** The explorer shortcuts. */ 537 Map<Action, Runnable> m_shortcutActions; 538 539 /** The bread crumb click listener. */ 540 private ClickListener m_crumbListener; 541 542 /** The path bread crumb container. */ 543 private CssLayout m_crumbs; 544 545 /** The currently viewed folder. */ 546 private CmsUUID m_currentFolder; 547 548 /** The current app state. */ 549 private String m_currentState; 550 551 /** The tree expand listener. */ 552 private TreeExpandListener m_expandListener; 553 554 /** The folder tree. */ 555 private Tree m_fileTree; 556 557 /** The first visible file table item index. */ 558 private int m_firstVisibleTableItemIndex; 559 560 /** The last context menu resources. */ 561 private List<CmsResource> m_lastDialogContextResources; 562 563 /** The quick launch location cache. */ 564 private CmsQuickLaunchLocationCache m_locationCache; 565 566 /** The new button. */ 567 private Button m_newButton; 568 569 /** The publish button. */ 570 private Button m_publishButton; 571 572 /** The search field. */ 573 private TextField m_searchField; 574 575 /** the currently selected file tree folder, may be null. */ 576 private CmsUUID m_selectTreeFolder; 577 578 /** The site selector. */ 579 private ComboBox m_siteSelector; 580 581 /** Button for uploading to folders with special upload actions. */ 582 private Button m_specialUploadButton; 583 584 /** The folder tree data container. */ 585 private HierarchicalContainer m_treeContainer; 586 587 /** Upload action for the current folder. */ 588 private String m_uploadAction; 589 590 /** The upload drop area extension. */ 591 private CmsUploadAreaExtension m_uploadArea; 592 593 /** The upload button. */ 594 private CmsUploadButton m_uploadButton; 595 596 /** The current upload folder. */ 597 private CmsResource m_uploadFolder; 598 599 /** 600 * Constructor.<p> 601 */ 602 public CmsFileExplorer() { 603 604 m_shortcutActions = new HashMap<Action, Runnable>(); 605 m_shortcutActions.put(ACTION_DELETE, new Runnable() { 606 607 public void run() { 608 609 if (!m_fileTable.getSelectedIds().isEmpty()) { 610 I_CmsDialogContext context1 = getDialogContext(); 611 context1.start("Delete", new CmsDeleteDialog(context1)); 612 } 613 } 614 }); 615 616 m_shortcutActions.put(ACTION_FOLDER_UP, new Runnable() { 617 618 public void run() { 619 620 showParentFolder(); 621 } 622 }); 623 624 m_shortcutActions.put(ACTION_PROPERTIES, new Runnable() { 625 626 public void run() { 627 628 I_CmsWorkplaceAction propAction = new CmsPropertiesDialogAction(); 629 I_CmsDialogContext context = getDialogContext(); 630 if (propAction.getVisibility(context).isActive()) { 631 propAction.executeAction(context); 632 } 633 } 634 }); 635 636 m_shortcutActions.put(ACTION_RENAME, new Runnable() { 637 638 public void run() { 639 640 CmsExplorerDialogContext context = getDialogContext(); 641 if (context.isPropertyEditable(CmsResourceTableProperty.PROPERTY_RESOURCE_NAME)) { 642 context.editProperty(CmsResourceTableProperty.PROPERTY_RESOURCE_NAME); 643 } 644 } 645 }); 646 Runnable selectAll = new Runnable() { 647 648 public void run() { 649 650 m_fileTable.selectAll(); 651 } 652 }; 653 m_shortcutActions.put(ACTION_SELECT_ALL, selectAll); 654 m_shortcutActions.put(ACTION_SELECT_ALL_CMD, selectAll); 655 656 Runnable switchOnline = new Runnable() { 657 658 public void run() { 659 660 toggleOnlineOffline(); 661 } 662 }; 663 m_shortcutActions.put(ACTION_SWITCH_ONLINE, switchOnline); 664 m_shortcutActions.put(ACTION_SWITCH_ONLINE_CMD, switchOnline); 665 666 m_fileTable = new CmsFileTable(this); 667 m_fileTable.setSizeFull(); 668 m_fileTable.setMenuBuilder(new CmsResourceContextMenuBuilder()); 669 m_fileTable.setFolderSelectHandler(this); 670 m_uploadArea = new CmsUploadAreaExtension(m_fileTable); 671 m_uploadArea.addUploadListener(new I_UploadListener() { 672 673 public void onUploadFinished(List<String> uploadedFiles) { 674 675 updateAll(true); 676 } 677 }); 678 m_treeContainer = new HierarchicalContainer(); 679 addTreeContainerProperties( 680 CmsResourceTableProperty.PROPERTY_RESOURCE_NAME, 681 CmsResourceTableProperty.PROPERTY_STATE, 682 CmsResourceTableProperty.PROPERTY_TREE_CAPTION, 683 CmsResourceTableProperty.PROPERTY_INSIDE_PROJECT, 684 CmsResourceTableProperty.PROPERTY_RELEASED_NOT_EXPIRED, 685 CmsResourceTableProperty.PROPERTY_DISABLED); 686 m_fileTree = new Tree(); 687 m_fileTree.addStyleName(OpenCmsTheme.SIMPLE_DRAG); 688 m_fileTree.addStyleName(OpenCmsTheme.FULL_WIDTH_PADDING); 689 m_fileTree.setWidth("100%"); 690 m_fileTree.setContainerDataSource(m_treeContainer); 691 // m_fileTree.setItemIconPropertyId(CmsResourceTableProperty.PROPERTY_TYPE_ICON_RESOURCE); 692 m_fileTree.setItemCaptionPropertyId(CmsResourceTableProperty.PROPERTY_TREE_CAPTION); 693 // m_fileTree.setCaptionAsHtml(true); 694 m_fileTree.setHtmlContentAllowed(true); 695 m_expandListener = new TreeExpandListener(); 696 m_fileTree.addExpandListener(m_expandListener); 697 m_fileTree.addCollapseListener(new CollapseListener() { 698 699 private static final long serialVersionUID = 1L; 700 701 public void nodeCollapse(CollapseEvent event) { 702 703 selectTreeItem((CmsUUID)event.getItemId()); 704 clearTreeLevel((CmsUUID)event.getItemId()); 705 } 706 }); 707 708 m_fileTree.addItemClickListener(new ItemClickListener() { 709 710 private static final long serialVersionUID = 1L; 711 712 public void itemClick(ItemClickEvent event) { 713 714 handleFileTreeClick(event); 715 } 716 }); 717 718 m_fileTree.setItemStyleGenerator(new ItemStyleGenerator() { 719 720 private static final long serialVersionUID = 1L; 721 722 public String getStyle(Tree source, Object itemId) { 723 724 return CmsFileTable.getStateStyle(source.getContainerDataSource().getItem(itemId)); 725 } 726 }); 727 m_fileTree.addValueChangeListener(new ValueChangeListener() { 728 729 private static final long serialVersionUID = 1L; 730 731 public void valueChange(ValueChangeEvent event) { 732 733 handleFileTreeValueChange(); 734 } 735 736 }); 737 738 m_fileTree.setNullSelectionAllowed(false); 739 740 // init drag and drop 741 ExplorerDropHandler handler = new ExplorerDropHandler(); 742 m_fileTable.setDropHandler(handler); 743 m_fileTable.setDragMode(TableDragMode.MULTIROW); 744 m_fileTree.setDropHandler(handler); 745 m_fileTree.setDragMode(TreeDragMode.NONE); 746 747 m_siteSelector = createSiteSelect(A_CmsUI.getCmsObject()); 748 m_infoPath = new com.vaadin.ui.TextField(); 749 A_CmsFocusShortcutListener shortcutListener = new A_CmsFocusShortcutListener("Open path", KeyCode.ENTER, null) { 750 751 private static final long serialVersionUID = 1L; 752 753 @Override 754 public void blur(BlurEvent event) { 755 756 super.blur(event); 757 showCrumbs(true); 758 } 759 760 @Override 761 public void focus(FocusEvent event) { 762 763 super.focus(event); 764 showCrumbs(false); 765 } 766 767 @Override 768 public void handleAction(Object sender, Object target) { 769 770 openPath(m_infoPath.getValue()); 771 } 772 }; 773 shortcutListener.installOn(m_infoPath); 774 775 m_crumbs = new CssLayout(); 776 m_crumbs.setPrimaryStyleName(OpenCmsTheme.CRUMBS); 777 m_crumbListener = new ClickListener() { 778 779 private static final long serialVersionUID = 1L; 780 781 public void buttonClick(ClickEvent event) { 782 783 openPath((String)event.getButton().getData()); 784 } 785 }; 786 787 m_searchField = new TextField(); 788 m_searchField.setIcon(FontOpenCms.FILTER); 789 m_searchField.setInputPrompt( 790 Messages.get().getBundle(UI.getCurrent().getLocale()).key(Messages.GUI_EXPLORER_FILTER_0)); 791 m_searchField.addStyleName(ValoTheme.TEXTFIELD_INLINE_ICON); 792 m_searchField.addTextChangeListener(new TextChangeListener() { 793 794 private static final long serialVersionUID = 1L; 795 796 public void textChange(TextChangeEvent event) { 797 798 filterTable(event.getText()); 799 800 } 801 }); 802 803 m_locationCache = CmsQuickLaunchLocationCache.getLocationCache(CmsAppWorkplaceUi.get().getHttpSession()); 804 String startSite = CmsWorkplace.getStartSiteRoot( 805 A_CmsUI.getCmsObject(), 806 CmsAppWorkplaceUi.get().getWorkplaceSettings()); 807 // remove trailing slashes 808 while (startSite.endsWith("/")) { 809 startSite = startSite.substring(0, startSite.length() - 1); 810 } 811 if (m_locationCache.getFileExplorerLocation(startSite) == null) { 812 // add the configured start folder for the start site 813 String startFolder = CmsAppWorkplaceUi.get().getWorkplaceSettings().getUserSettings().getStartFolder(); 814 m_locationCache.setFileExplorerLocation(startSite, startFolder); 815 } 816 } 817 818 /** 819 * @see com.vaadin.navigator.ViewChangeListener#afterViewChange(com.vaadin.navigator.ViewChangeListener.ViewChangeEvent) 820 */ 821 public void afterViewChange(ViewChangeEvent event) { 822 823 m_fileTable.setFirstVisibleItemIndex(m_firstVisibleTableItemIndex); 824 } 825 826 /** 827 * @see com.vaadin.navigator.ViewChangeListener#beforeViewChange(com.vaadin.navigator.ViewChangeListener.ViewChangeEvent) 828 */ 829 public boolean beforeViewChange(ViewChangeEvent event) { 830 831 m_firstVisibleTableItemIndex = m_fileTable.getFirstVisibleItemIndex(); 832 833 OpenCms.getWorkplaceAppManager().storeAppSettings( 834 A_CmsUI.getCmsObject(), 835 CmsFileExplorerSettings.class, 836 m_fileTable.getTableSettings()); 837 return true; 838 } 839 840 /** 841 * Changes to the given site and path.<p> 842 * 843 * @param siteRoot the site root 844 * @param path the path inside the site 845 */ 846 public void changeSite(String siteRoot, String path) { 847 848 changeSite(siteRoot, path, false); 849 850 } 851 852 /** 853 * Switches to the requested site.<p> 854 * 855 * @param siteRoot the site root 856 * @param path the folder path to open 857 * @param force force the path change, even if we are currently in the same site 858 */ 859 public void changeSite(String siteRoot, String path, boolean force) { 860 861 CmsObject cms = A_CmsUI.getCmsObject(); 862 String currentSiteRoot = cms.getRequestContext().getSiteRoot(); 863 if (force || !currentSiteRoot.equals(siteRoot) || (path != null)) { 864 CmsAppWorkplaceUi.get().changeSite(siteRoot); 865 if (path == null) { 866 path = m_locationCache.getFileExplorerLocation(siteRoot); 867 if (CmsStringUtil.isEmptyOrWhitespaceOnly(siteRoot) 868 && ((path == null) || !path.startsWith("/system"))) { 869 // switching to the root site and previous root site folder is not below /system 870 // -> stay in the current folder 871 path = m_locationCache.getFileExplorerLocation(currentSiteRoot); 872 if (path != null) { 873 path = CmsStringUtil.joinPaths(currentSiteRoot, path); 874 } 875 } 876 } 877 openPath(path, true); 878 Container container = m_siteSelector.getContainerDataSource(); 879 CmsExtendedSiteSelector.SiteSelectorOption optionToSelect = null; 880 for (Object id : container.getItemIds()) { 881 CmsExtendedSiteSelector.SiteSelectorOption option = (CmsExtendedSiteSelector.SiteSelectorOption)id; 882 if ((option != null) && CmsStringUtil.comparePaths(option.getSite(), siteRoot)) { 883 optionToSelect = option; 884 break; 885 } 886 } 887 m_siteSelector.select(optionToSelect); 888 } 889 } 890 891 /** 892 * Clears the file table selection. 893 */ 894 public void clearSelection() { 895 896 m_fileTable.clearSelection(); 897 } 898 899 /** 900 * Gets all ids of resources in current folder.<p> 901 * 902 * @return the 903 */ 904 public List<CmsUUID> getAllIds() { 905 906 return m_fileTable.getAllIds(); 907 } 908 909 /** 910 * Returns the current folder id.<p> 911 * 912 * @return the current folder structure id 913 */ 914 public CmsUUID getCurrentFolder() { 915 916 return m_currentFolder; 917 } 918 919 /** 920 * @see org.opencms.ui.apps.I_CmsContextProvider#getDialogContext() 921 */ 922 public CmsExplorerDialogContext getDialogContext() { 923 924 List<CmsResource> resources = m_fileTable.getSelectedResources(); 925 m_lastDialogContextResources = resources; 926 CmsExplorerDialogContext context = new CmsExplorerDialogContext( 927 ContextType.fileTable, 928 m_fileTable, 929 this, 930 resources); 931 context.setEditableProperties(INLINE_EDIT_PROPERTIES); 932 return context; 933 } 934 935 /** 936 * @see org.opencms.ui.apps.I_CmsHasShortcutActions#getShortcutActions() 937 */ 938 public Map<Action, Runnable> getShortcutActions() { 939 940 return m_shortcutActions; 941 } 942 943 /** 944 * @see org.opencms.ui.apps.I_CmsWorkplaceApp#initUI(org.opencms.ui.apps.I_CmsAppUIContext) 945 */ 946 public void initUI(I_CmsAppUIContext context) { 947 948 m_appContext = context; 949 m_appContext.setAttribute(ATTR_KEY, this); 950 m_appContext.setMenuDialogContext( 951 new CmsExplorerDialogContext(ContextType.appToolbar, m_fileTable, this, null)); 952 HorizontalSplitPanel sp = new HorizontalSplitPanel(); 953 sp.setSizeFull(); 954 sp.setFirstComponent(m_fileTree); 955 CmsFileExplorerSettings settings; 956 try { 957 settings = OpenCms.getWorkplaceAppManager().getAppSettings( 958 A_CmsUI.getCmsObject(), 959 CmsFileExplorerSettings.class); 960 961 m_fileTable.setTableState(settings); 962 } catch (Exception e) { 963 LOG.error("Error while reading file explorer settings from user.", e); 964 } 965 sp.setSecondComponent(m_fileTable); 966 967 sp.setSplitPosition(LAYOUT_SPLIT_POSITION, Unit.PIXELS); 968 969 context.setAppContent(sp); 970 context.showInfoArea(true); 971 HorizontalLayout inf = new HorizontalLayout(); 972 inf.setSizeFull(); 973 inf.setSpacing(true); 974 inf.setMargin(true); 975 m_siteSelector.setWidth("379px"); 976 inf.addComponent(m_siteSelector); 977 CssLayout crumbWrapper = new CssLayout(); 978 crumbWrapper.setSizeFull(); 979 crumbWrapper.setPrimaryStyleName(OpenCmsTheme.CRUMB_WRAPPER); 980 crumbWrapper.addComponent(m_crumbs); 981 982 m_infoPath.setWidth("100%"); 983 crumbWrapper.addComponent(m_infoPath); 984 inf.addComponent(crumbWrapper); 985 inf.setExpandRatio(crumbWrapper, 1); 986 987 m_searchField.setWidth("200px"); 988 inf.addComponent(m_searchField); 989 context.setAppInfo(inf); 990 991 initToolbarButtons(context); 992 try { 993 JavaScript.getCurrent().execute( 994 new String( 995 CmsFileUtil.readFully(getClass().getResourceAsStream("update-crumb-wrapper-parent.js")), 996 StandardCharsets.UTF_8)); 997 } catch (IOException e) {} 998 m_fileTable.updateColumnWidths(A_CmsUI.get().getPage().getBrowserWindowWidth() - LAYOUT_SPLIT_POSITION); 999 } 1000 1001 /** 1002 * @see org.opencms.ui.apps.I_CmsCachableApp#isCachable() 1003 */ 1004 public boolean isCachable() { 1005 1006 return true; 1007 } 1008 1009 /** 1010 * @see org.opencms.ui.components.CmsFileTable.I_FolderSelectHandler#onFolderSelect(org.opencms.util.CmsUUID) 1011 */ 1012 public void onFolderSelect(CmsUUID itemId) { 1013 1014 expandCurrentFolder(); 1015 if (m_fileTree.getItem(itemId) != null) { 1016 m_fileTree.select(itemId); 1017 } 1018 try { 1019 readFolder(itemId); 1020 } catch (CmsException e) { 1021 CmsErrorDialog.showErrorDialog( 1022 CmsVaadinUtils.getMessageText(Messages.ERR_EXPLORER_CAN_NOT_READ_RESOURCE_1, itemId), 1023 e); 1024 LOG.error(e.getLocalizedMessage(), e); 1025 } 1026 } 1027 1028 /** 1029 * @see org.opencms.ui.apps.I_CmsCachableApp#onRestoreFromCache() 1030 */ 1031 public void onRestoreFromCache() { 1032 1033 if (m_lastDialogContextResources != null) { 1034 List<CmsUUID> updateIds = Lists.newArrayList(); 1035 for (CmsResource resource : m_lastDialogContextResources) { 1036 updateIds.add(resource.getStructureId()); 1037 } 1038 update(updateIds); 1039 } 1040 1041 } 1042 1043 /** 1044 * Call if site and or project have been changed.<p> 1045 * 1046 * @param project the project 1047 * @param siteRoot the site root 1048 */ 1049 public void onSiteOrProjectChange(CmsProject project, String siteRoot) { 1050 1051 if ((siteRoot != null) && !siteRoot.equals(getSiteRootFromState())) { 1052 changeSite(siteRoot, null, true); 1053 } else if ((project != null) && !project.getUuid().equals(getProjectIdFromState())) { 1054 openPath(getPathFromState(), true); 1055 } 1056 m_appContext.updateOnChange(); 1057 setToolbarButtonsEnabled(!CmsAppWorkplaceUi.isOnlineProject()); 1058 } 1059 1060 /** 1061 * @see org.opencms.ui.apps.I_CmsWorkplaceApp#onStateChange(java.lang.String) 1062 */ 1063 public void onStateChange(String state) { 1064 1065 state = normalizeState(state); 1066 if ((m_currentState == null) || !m_currentState.equals(state)) { 1067 m_currentState = state; 1068 CmsObject cms = A_CmsUI.getCmsObject(); 1069 String siteRoot = getSiteRootFromState(); 1070 String path = getPathFromState(); 1071 1072 CmsUUID projectId = getProjectIdFromState(); 1073 if ((projectId != null) && !cms.getRequestContext().getCurrentProject().getUuid().equals(projectId)) { 1074 try { 1075 CmsProject project = cms.readProject(projectId); 1076 cms.getRequestContext().setCurrentProject(project); 1077 CmsAppWorkplaceUi.get().getWorkplaceSettings().setProject(project.getUuid()); 1078 } catch (CmsException e) { 1079 LOG.warn("Error reading project from history state", e); 1080 } 1081 changeSite(siteRoot, path, true); 1082 } else if ((siteRoot != null) 1083 && !CmsStringUtil.comparePaths(siteRoot, cms.getRequestContext().getSiteRoot())) { 1084 String saveState = m_currentState; 1085 changeSite(siteRoot, path); 1086 if (!getSelectionFromState(saveState).isEmpty()) { 1087 m_fileTable.setValue(Collections.singleton(getSelectionFromState(saveState))); 1088 } 1089 } else { 1090 String saveState = m_currentState; 1091 openPath(path, true); 1092 if (!getSelectionFromState(saveState).isEmpty()) { 1093 m_fileTable.setValue(Collections.singleton(getSelectionFromState(saveState))); 1094 } 1095 } 1096 } 1097 } 1098 1099 /** 1100 * @see org.opencms.ui.components.I_CmsWindowCloseListener#onWindowClose() 1101 */ 1102 public void onWindowClose() { 1103 1104 OpenCms.getWorkplaceAppManager().storeAppSettings( 1105 A_CmsUI.getCmsObject(), 1106 CmsFileExplorerSettings.class, 1107 m_fileTable.getTableSettings()); 1108 } 1109 1110 /** 1111 * Fills the file table with the resources from the given path.<p> 1112 * 1113 * @param sitePath a folder site path 1114 */ 1115 public void populateFileTable(String sitePath) { 1116 1117 CmsObject cms = A_CmsUI.getCmsObject(); 1118 m_searchField.clear(); 1119 m_firstVisibleTableItemIndex = 0; 1120 try { 1121 List<CmsResource> folderResources = cms.readResources(sitePath, FILES_N_FOLDERS, false); 1122 m_fileTable.fillTable(cms, folderResources); 1123 } catch (CmsException e) { 1124 CmsErrorDialog.showErrorDialog(e); 1125 LOG.error(e.getLocalizedMessage(), e); 1126 } 1127 } 1128 1129 /** 1130 * Updates the table entries with the given ids.<p> 1131 * 1132 * @param ids the ids of the table entries to update 1133 */ 1134 public void update(Collection<CmsUUID> ids) { 1135 1136 try { 1137 CmsResource currentFolderRes = A_CmsUI.getCmsObject().readResource(m_currentFolder, CmsResourceFilter.ALL); 1138 boolean updateFolder = m_fileTable.getItemCount() < UPDATE_FOLDER_THRESHOLD; 1139 Set<CmsUUID> removeIds = new HashSet<CmsUUID>(); 1140 for (CmsUUID id : ids) { 1141 boolean remove = false; 1142 try { 1143 CmsResource resource = A_CmsUI.getCmsObject().readResource(id, CmsResourceFilter.ALL); 1144 1145 remove = !CmsResource.getParentFolder(resource.getRootPath()).equals( 1146 currentFolderRes.getRootPath()); 1147 1148 } catch (CmsVfsResourceNotFoundException e) { 1149 remove = true; 1150 LOG.debug("Could not read update resource " + id, e); 1151 } 1152 if (remove) { 1153 removeIds.add(id); 1154 } 1155 1156 } 1157 for (CmsUUID id : ids) { 1158 updateTree(id); 1159 } 1160 if (updateFolder) { 1161 updateCurrentFolder(removeIds); 1162 } else { 1163 m_fileTable.update(removeIds, true); 1164 HashSet<CmsUUID> updateIds = new HashSet<CmsUUID>(ids); 1165 ids.removeAll(removeIds); 1166 m_fileTable.update(updateIds, false); 1167 } 1168 m_fileTable.updateSorting(); 1169 m_fileTable.clearSelection(); 1170 } catch (CmsException e) { 1171 CmsErrorDialog.showErrorDialog(e); 1172 } 1173 if (!CmsStringUtil.isEmptyOrWhitespaceOnly(m_searchField.getValue())) { 1174 filterTable(m_searchField.getValue()); 1175 } 1176 } 1177 1178 /** 1179 * Updates display for all contents of the current folder.<p> 1180 * 1181 * @param clearFilter <code>true</code> to clear the search filter 1182 */ 1183 public void updateAll(boolean clearFilter) { 1184 1185 try { 1186 readFolder(m_currentFolder, clearFilter); 1187 Set<Object> ids = Sets.newHashSet(); 1188 ids.addAll(m_fileTree.getItemIds()); 1189 for (Object id : ids) { 1190 updateTree((CmsUUID)id); 1191 } 1192 } catch (CmsException e) { 1193 CmsErrorDialog.showErrorDialog( 1194 CmsVaadinUtils.getMessageText(Messages.ERR_EXPLORER_CAN_NOT_READ_RESOURCE_1, m_currentFolder), 1195 e); 1196 LOG.error(e.getLocalizedMessage(), e); 1197 } 1198 } 1199 1200 /** 1201 * Updates the give tree item.<p> 1202 * 1203 * @param cms the cms context 1204 * @param id the item id 1205 */ 1206 public void updateResourceInTree(CmsObject cms, CmsUUID id) { 1207 1208 try { 1209 CmsResource resource = cms.readResource(id, CmsResourceFilter.IGNORE_EXPIRATION); 1210 if (resource.isFile()) { 1211 return; 1212 } 1213 // ensuring to only add items belonging to the current site 1214 if (!resource.getRootPath().startsWith(cms.getRequestContext().getSiteRoot())) { 1215 m_treeContainer.removeItemRecursively(id); 1216 return; 1217 } 1218 1219 CmsResource parent = cms.readParentFolder(id); 1220 CmsUUID parentId = null; 1221 if (parent != null) { 1222 parentId = parent.getStructureId(); 1223 } else { 1224 LOG.debug("Parent for resource '" + resource.getRootPath() + "' is null"); 1225 } 1226 Item resourceItem = m_treeContainer.getItem(id); 1227 if (resourceItem != null) { 1228 // use the root path as name in case of the root item 1229 String resName = parentId == null ? resource.getRootPath() : resource.getName(); 1230 resourceItem.getItemProperty(CmsResourceTableProperty.PROPERTY_RESOURCE_NAME).setValue(resName); 1231 CmsResourceUtil resUtil = new CmsResourceUtil(cms, resource); 1232 String iconHTML = CmsResourceIcon.getTreeCaptionHTML(resName, resUtil, null, false); 1233 resourceItem.getItemProperty(CmsResourceTableProperty.PROPERTY_TREE_CAPTION).setValue(iconHTML); 1234 resourceItem.getItemProperty(CmsResourceTableProperty.PROPERTY_STATE).setValue(resource.getState()); 1235 if (parentId != null) { 1236 m_treeContainer.setParent(resource.getStructureId(), parentId); 1237 } 1238 } else { 1239 addTreeItem(cms, resource, parentId, false, m_treeContainer); 1240 } 1241 m_fileTree.markAsDirty(); 1242 } catch (CmsVfsResourceNotFoundException e) { 1243 m_treeContainer.removeItemRecursively(id); 1244 LOG.debug(e.getLocalizedMessage(), e); 1245 } catch (CmsException e) { 1246 CmsErrorDialog.showErrorDialog(e); 1247 LOG.error(e.getLocalizedMessage(), e); 1248 } 1249 } 1250 1251 /** 1252 * Updates the tree items with the given ids.<p> 1253 * 1254 * @param id the 1255 */ 1256 public void updateTree(CmsUUID id) { 1257 1258 CmsObject cms = A_CmsUI.getCmsObject(); 1259 updateResourceInTree(cms, id); 1260 1261 } 1262 1263 /** 1264 * Clears the given tree level.<p> 1265 * 1266 * @param parentId the parent id 1267 */ 1268 protected void clearTreeLevel(CmsUUID parentId) { 1269 1270 // create a new list to avoid concurrent modifications 1271 Collection<?> children = m_treeContainer.getChildren(parentId); 1272 // may be null when monkey clicking 1273 if (children != null) { 1274 List<Object> childIds = new ArrayList<Object>(m_treeContainer.getChildren(parentId)); 1275 for (Object childId : childIds) { 1276 m_treeContainer.removeItemRecursively(childId); 1277 } 1278 } 1279 } 1280 1281 /** 1282 * Reads the given folder.<p> 1283 * 1284 * @param folderId the folder id 1285 * 1286 * @throws CmsException in case reading the folder fails 1287 */ 1288 protected void readFolder(CmsUUID folderId) throws CmsException { 1289 1290 readFolder(folderId, true); 1291 } 1292 1293 /** 1294 * Reads the given folder.<p> 1295 * 1296 * @param folderId the folder id 1297 * @param clearFilter <code>true</code> to clear the search filter 1298 * 1299 * @throws CmsException in case reading the folder fails 1300 */ 1301 protected void readFolder(CmsUUID folderId, boolean clearFilter) throws CmsException { 1302 1303 CmsObject cms = A_CmsUI.getCmsObject(); 1304 if (clearFilter) { 1305 m_searchField.clear(); 1306 } 1307 CmsResource folder = cms.readResource(folderId, FOLDERS); 1308 m_currentFolder = folderId; 1309 String folderPath = cms.getSitePath(folder); 1310 if (OpenCms.getSiteManager().isSharedFolder(cms.getRequestContext().getSiteRoot())) { 1311 folderPath = folderPath.substring(cms.getRequestContext().getSiteRoot().length()); 1312 } 1313 setPathInfo(folderPath); 1314 List<CmsResource> childResources = cms.readResources(folder, FILES_N_FOLDERS, false); 1315 m_fileTable.fillTable(cms, childResources, clearFilter); 1316 boolean hasFolderChild = false; 1317 for (CmsResource child : childResources) { 1318 if (child.isFolder()) { 1319 hasFolderChild = true; 1320 break; 1321 } 1322 } 1323 m_treeContainer.setChildrenAllowed(folderId, hasFolderChild); 1324 String sitePath = folder.getRootPath().equals(cms.getRequestContext().getSiteRoot() + "/") ? "" : folderPath; 1325 String state = new StateBean( 1326 cms.getRequestContext().getSiteRoot(), 1327 sitePath, 1328 cms.getRequestContext().getCurrentProject().getUuid().toString()).asString(); 1329 if (LOG.isDebugEnabled()) { 1330 String p = RandomStringUtils.randomAlphanumeric(5) + " readFolder "; 1331 LOG.debug(p + "siteRoot = " + cms.getRequestContext().getSiteRoot()); 1332 LOG.debug(p + "folder = " + folder.getRootPath()); 1333 LOG.debug(p + "folderPath = " + folderPath); 1334 LOG.debug(p + "sitePath = " + sitePath); 1335 LOG.debug(p + "state = " + state); 1336 LOG.debug(p + "m_currentState = " + m_currentState); 1337 LOG.debug(p + "m_currentState.asString = " + StateBean.parse(m_currentState).asString()); 1338 } 1339 if ((m_currentState == null) || !(state).equals(StateBean.parse(m_currentState).asString())) { 1340 m_currentState = state; 1341 CmsAppWorkplaceUi.get().changeCurrentAppState(m_currentState); 1342 } 1343 m_locationCache.setFileExplorerLocation(cms.getRequestContext().getSiteRoot(), sitePath); 1344 updateUploadButton(folder); 1345 if (!m_fileTree.isExpanded(folderId)) { 1346 expandCurrentFolder(); 1347 } 1348 if (!m_fileTree.isSelected(folderId)) { 1349 m_fileTree.select(folderId); 1350 } 1351 } 1352 1353 /** 1354 * Updates the current folder and removes the given resource items.<p> 1355 * 1356 * @param removeIds the resource item ids to remove 1357 */ 1358 protected void updateCurrentFolder(Collection<CmsUUID> removeIds) { 1359 1360 m_fileTable.update(removeIds, true); 1361 CmsObject cms = A_CmsUI.getCmsObject(); 1362 try { 1363 // current folder may be filtered, so we clear the filters and restore them later 1364 // to make updates work for filtered out resources 1365 m_fileTable.saveFilters(); 1366 m_fileTable.clearFilters(); 1367 CmsResource folder = cms.readResource(m_currentFolder, FOLDERS); 1368 List<CmsResource> childResources = cms.readResources(cms.getSitePath(folder), FILES_N_FOLDERS, false); 1369 Set<CmsUUID> ids = new HashSet<CmsUUID>(); 1370 for (CmsResource child : childResources) { 1371 ids.add(child.getStructureId()); 1372 } 1373 m_fileTable.update(ids, false); 1374 } catch (CmsException e) { 1375 CmsErrorDialog.showErrorDialog(e); 1376 LOG.error(e.getLocalizedMessage(), e); 1377 } finally { 1378 m_fileTable.restoreFilters(); 1379 } 1380 } 1381 1382 /** 1383 * Expands the currently viewed folder in the tree.<p> 1384 */ 1385 void expandCurrentFolder() { 1386 1387 if (m_currentFolder != null) { 1388 m_treeContainer.setChildrenAllowed(m_currentFolder, true); 1389 m_fileTree.expandItem(m_currentFolder); 1390 } 1391 } 1392 1393 /** 1394 * Filters the file table.<p> 1395 * 1396 * @param search the search term 1397 */ 1398 void filterTable(String search) { 1399 1400 m_fileTable.filterTable(search); 1401 } 1402 1403 /** 1404 * Handles the file tree click events.<p> 1405 * 1406 * @param event the event 1407 */ 1408 void handleFileTreeClick(ItemClickEvent event) { 1409 1410 Item resourceItem = m_treeContainer.getItem(event.getItemId()); 1411 if ((resourceItem.getItemProperty(CmsResourceTableProperty.PROPERTY_DISABLED).getValue() == null) 1412 || !((Boolean)resourceItem.getItemProperty( 1413 CmsResourceTableProperty.PROPERTY_DISABLED).getValue()).booleanValue()) { 1414 // don't handle disabled item clicks 1415 try { 1416 readFolder((CmsUUID)event.getItemId()); 1417 } catch (CmsException e) { 1418 CmsErrorDialog.showErrorDialog( 1419 CmsVaadinUtils.getMessageText(Messages.ERR_EXPLORER_CAN_NOT_READ_RESOURCE_1, event.getItemId()), 1420 e); 1421 LOG.error(e.getLocalizedMessage(), e); 1422 } 1423 } 1424 } 1425 1426 /** 1427 * Handles the file tree value changes.<p> 1428 */ 1429 void handleFileTreeValueChange() { 1430 1431 Item resourceItem = m_treeContainer.getItem(m_fileTree.getValue()); 1432 if (resourceItem != null) { 1433 if ((resourceItem.getItemProperty(CmsResourceTableProperty.PROPERTY_DISABLED).getValue() != null) 1434 && ((Boolean)resourceItem.getItemProperty( 1435 CmsResourceTableProperty.PROPERTY_DISABLED).getValue()).booleanValue()) { 1436 // in case the folder is disabled due to missing visibility, reset the value to the previous one 1437 m_fileTree.setValue(m_selectTreeFolder); 1438 1439 } else { 1440 m_selectTreeFolder = (CmsUUID)m_fileTree.getValue(); 1441 } 1442 } 1443 } 1444 1445 /** 1446 * Opens the new resource dialog.<p> 1447 */ 1448 void onClickNew() { 1449 1450 try { 1451 CmsObject cms = A_CmsUI.getCmsObject(); 1452 1453 CmsResource folderRes = cms.readResource(m_currentFolder, CmsResourceFilter.IGNORE_EXPIRATION); 1454 I_CmsDialogContext newDialogContext = getDialogContext(); 1455 1456 CmsNewDialog newDialog = new CmsNewDialog(folderRes, newDialogContext); 1457 newDialogContext.start( 1458 CmsVaadinUtils.getMessageText(org.opencms.ui.Messages.GUI_NEWRESOURCEDIALOG_TITLE_0), 1459 newDialog); 1460 1461 } catch (Exception e) { 1462 LOG.error(e.getLocalizedMessage(), e); 1463 } 1464 } 1465 1466 /** 1467 * Opens the given site path.<p> 1468 * 1469 * @param path the path 1470 */ 1471 void openPath(String path) { 1472 1473 openPath(path, false); 1474 } 1475 1476 /** 1477 * Opens the given site path.<p> 1478 * 1479 * @param path the path 1480 * @param initTree <code>true</code> in case the tree needs to be initialized 1481 */ 1482 void openPath(String path, boolean initTree) { 1483 1484 CmsObject cms = A_CmsUI.getCmsObject(); 1485 if (path == null) { 1486 String siteRoot = cms.getRequestContext().getSiteRoot(); 1487 path = m_locationCache.getFileExplorerLocation(siteRoot); 1488 if (path == null) { 1489 path = ""; 1490 } else if (OpenCms.getSiteManager().startsWithShared(path)) { 1491 path = path.substring(siteRoot.length()); 1492 } 1493 } 1494 1495 CmsSiteManagerImpl siteManager = OpenCms.getSiteManager(); 1496 if (siteManager.isSharedFolder(cms.getRequestContext().getSiteRoot()) && siteManager.startsWithShared(path)) { 1497 // siteManager.getSharedFolder() has a trailing slash - we want to cut off the shared folder, but keep a leading slash in path 1498 path = path.substring(siteManager.getSharedFolder().length() - 1); 1499 if ("".equals(path)) { 1500 path = "/"; 1501 } 1502 } 1503 1504 boolean existsPath = CmsStringUtil.isNotEmptyOrWhitespaceOnly(path) 1505 && cms.existsResource(path, FILES_N_FOLDERS); 1506 1507 if (path.startsWith("/")) { 1508 // remove a leading slash to avoid an empty first path fragment 1509 path = path.substring(1); 1510 } 1511 1512 String[] pathItems = path.split("/"); 1513 1514 if (initTree) { 1515 try { 1516 initFolderTree(existsPath); 1517 } catch (CmsException e) { 1518 CmsErrorDialog.showErrorDialog( 1519 CmsVaadinUtils.getMessageText(Messages.ERR_EXPLORER_CAN_NOT_OPEN_PATH_1, path), 1520 e); 1521 LOG.error(e.getLocalizedMessage(), e); 1522 return; 1523 } 1524 } 1525 1526 Collection<?> rootItems = m_treeContainer.rootItemIds(); 1527 if (rootItems.size() != 1) { 1528 throw new RuntimeException("Illeagal state, folder tree has " + rootItems.size() + " children"); 1529 } 1530 1531 CmsUUID folderId = (CmsUUID)rootItems.iterator().next(); 1532 if (initTree) { 1533 if (existsPath) { 1534 m_expandListener.setOpenPathFragment(pathItems[0]); 1535 } 1536 m_fileTree.expandItem(folderId); 1537 m_expandListener.setOpenPathFragment(null); 1538 } 1539 Collection<?> children = m_treeContainer.getChildren(folderId); 1540 for (int i = 0; i < pathItems.length; i++) { 1541 if (CmsStringUtil.isEmptyOrWhitespaceOnly(pathItems[i])) { 1542 continue; 1543 } 1544 CmsUUID level = null; 1545 if (children != null) { 1546 for (Object id : children) { 1547 if (m_treeContainer.getItem(id).getItemProperty( 1548 CmsResourceTableProperty.PROPERTY_RESOURCE_NAME).getValue().equals(pathItems[i])) { 1549 level = (CmsUUID)id; 1550 m_expandListener.setOpenPathFragment( 1551 existsPath && (i < (pathItems.length - 1)) ? pathItems[i + 1] : null); 1552 m_fileTree.expandItem(level); 1553 m_expandListener.setOpenPathFragment(null); 1554 break; 1555 } 1556 } 1557 } 1558 if ((level == null) || level.equals(folderId)) { 1559 break; 1560 } 1561 folderId = level; 1562 children = m_treeContainer.getChildren(folderId); 1563 } 1564 try { 1565 readFolder(folderId); 1566 } catch (CmsException e) { 1567 CmsErrorDialog.showErrorDialog( 1568 CmsVaadinUtils.getMessageText(Messages.ERR_EXPLORER_CAN_NOT_OPEN_PATH_1, path), 1569 e); 1570 LOG.error(e.getLocalizedMessage(), e); 1571 return; 1572 } 1573 } 1574 1575 /** 1576 * Reads the given tree level.<p> 1577 * 1578 * @param parentId the parent id 1579 * @param openPathFragment the open path fragment 1580 */ 1581 void readTreeLevel(CmsUUID parentId, String openPathFragment) { 1582 1583 CmsObject cms = A_CmsUI.getCmsObject(); 1584 boolean openedFragment = false; 1585 try { 1586 CmsResource parent = cms.readResource(parentId, CmsResourceFilter.IGNORE_EXPIRATION); 1587 String folderPath = cms.getSitePath(parent); 1588 List<CmsResource> folderResources = cms.readResources(folderPath, FOLDERS, false); 1589 1590 // sets the parent to leaf mode, in case no child folders are present 1591 m_treeContainer.setChildrenAllowed(parentId, !folderResources.isEmpty()); 1592 1593 for (CmsResource resource : folderResources) { 1594 addTreeItem(cms, resource, parentId, false, m_treeContainer); 1595 openedFragment = openedFragment || resource.getName().equals(openPathFragment); 1596 } 1597 if (!openedFragment && (openPathFragment != null)) { 1598 CmsResource resource = cms.readResource( 1599 CmsStringUtil.joinPaths(folderPath, openPathFragment), 1600 CmsResourceFilter.IGNORE_EXPIRATION); 1601 addTreeItem(cms, resource, parentId, true, m_treeContainer); 1602 } 1603 1604 m_fileTree.markAsDirtyRecursive(); 1605 } catch (CmsException e) { 1606 CmsErrorDialog.showErrorDialog(e); 1607 LOG.error(e.getLocalizedMessage(), e); 1608 } 1609 } 1610 1611 /** 1612 * Selects the item with the given id.<p> 1613 * 1614 * @param itemId the item id 1615 */ 1616 void selectTreeItem(CmsUUID itemId) { 1617 1618 m_fileTree.select(itemId); 1619 } 1620 1621 /** 1622 * Shows and hides the path bread crumb.<p> 1623 * 1624 * @param show <code>true</code> to show the bread crumb 1625 */ 1626 void showCrumbs(boolean show) { 1627 1628 if (show) { 1629 m_crumbs.removeStyleName(OpenCmsTheme.HIDDEN); 1630 CmsAppWorkplaceUi.get().enableGlobalShortcuts(); 1631 } else { 1632 m_crumbs.addStyleName(OpenCmsTheme.HIDDEN); 1633 CmsAppWorkplaceUi.get().disableGlobalShortcuts(); 1634 } 1635 } 1636 1637 /** 1638 * Shows the parent folder, if available.<p> 1639 */ 1640 void showParentFolder() { 1641 1642 CmsUUID parentId = (CmsUUID)m_treeContainer.getParent(m_currentFolder); 1643 if (parentId != null) { 1644 Item resourceItem = m_treeContainer.getItem(parentId); 1645 if ((resourceItem.getItemProperty(CmsResourceTableProperty.PROPERTY_DISABLED).getValue() == null) 1646 || !((Boolean)resourceItem.getItemProperty( 1647 CmsResourceTableProperty.PROPERTY_DISABLED).getValue()).booleanValue()) { 1648 // don't open disabled parent folders 1649 try { 1650 readFolder(parentId); 1651 m_fileTree.select(parentId); 1652 } catch (CmsException e) { 1653 CmsErrorDialog.showErrorDialog( 1654 CmsVaadinUtils.getMessageText(Messages.ERR_EXPLORER_CAN_NOT_READ_RESOURCE_1, parentId), 1655 e); 1656 LOG.error(e.getLocalizedMessage(), e); 1657 } 1658 } 1659 } 1660 } 1661 1662 /** 1663 * Toggles between the online and the offline project.<p> 1664 */ 1665 void toggleOnlineOffline() { 1666 1667 CmsObject cms = A_CmsUI.getCmsObject(); 1668 CmsProject targetProject = null; 1669 if (cms.getRequestContext().getCurrentProject().isOnlineProject()) { 1670 targetProject = A_CmsUI.get().getLastOfflineProject(); 1671 if (targetProject == null) { 1672 CmsUserSettings userSettings = new CmsUserSettings(cms); 1673 try { 1674 CmsProject project = cms.readProject(userSettings.getStartProject()); 1675 if (!project.isOnlineProject() 1676 && OpenCms.getOrgUnitManager().getAllAccessibleProjects( 1677 cms, 1678 project.getOuFqn(), 1679 false).contains(project)) { 1680 targetProject = project; 1681 } 1682 } catch (CmsException e) { 1683 LOG.debug("Error reading user start project.", e); 1684 } 1685 if (targetProject == null) { 1686 List<CmsProject> availableProjects = CmsVaadinUtils.getAvailableProjects(cms); 1687 for (CmsProject project : availableProjects) { 1688 if (!project.isOnlineProject()) { 1689 targetProject = project; 1690 break; 1691 } 1692 } 1693 } 1694 } 1695 } else { 1696 try { 1697 targetProject = cms.readProject(CmsProject.ONLINE_PROJECT_ID); 1698 } catch (CmsException e) { 1699 LOG.debug("Error reading online project.", e); 1700 } 1701 } 1702 if (targetProject != null) { 1703 A_CmsUI.get().changeProject(targetProject); 1704 onSiteOrProjectChange(targetProject, null); 1705 Notification.show( 1706 CmsVaadinUtils.getMessageText(Messages.GUI_SWITCHED_TO_PROJECT_1, targetProject.getName())); 1707 } 1708 } 1709 1710 /** 1711 * Adds the given properties to the tree container.<p> 1712 * 1713 * @param properties the properties tom add 1714 */ 1715 private void addTreeContainerProperties(CmsResourceTableProperty... properties) { 1716 1717 for (CmsResourceTableProperty property : properties) { 1718 m_treeContainer.addContainerProperty(property, property.getColumnType(), property.getDefaultValue()); 1719 } 1720 } 1721 1722 /** 1723 * Adds an item to the folder tree.<p> 1724 * 1725 * @param cms the cms context 1726 * @param resource the folder resource 1727 * @param parentId the parent folder id 1728 * @param disabled in case the item is disabled, used for non visible parent folders 1729 * @param container the data container 1730 */ 1731 private void addTreeItem( 1732 CmsObject cms, 1733 CmsResource resource, 1734 CmsUUID parentId, 1735 boolean disabled, 1736 HierarchicalContainer container) { 1737 1738 CmsResourceUtil resUtil = new CmsResourceUtil(cms, resource); 1739 Item resourceItem = container.getItem(resource.getStructureId()); 1740 if (resourceItem == null) { 1741 resourceItem = container.addItem(resource.getStructureId()); 1742 } 1743 1744 // use the root path as name in case of the root item 1745 String resName = parentId == null ? resource.getRootPath() : resource.getName(); 1746 resourceItem.getItemProperty(CmsResourceTableProperty.PROPERTY_RESOURCE_NAME).setValue(resName); 1747 resourceItem.getItemProperty(CmsResourceTableProperty.PROPERTY_STATE).setValue(resource.getState()); 1748 1749 resourceItem.getItemProperty(CmsResourceTableProperty.PROPERTY_INSIDE_PROJECT).setValue( 1750 Boolean.valueOf(resUtil.isInsideProject())); 1751 resourceItem.getItemProperty(CmsResourceTableProperty.PROPERTY_RELEASED_NOT_EXPIRED).setValue( 1752 Boolean.valueOf(resUtil.isReleasedAndNotExpired())); 1753 String iconHTML = CmsResourceIcon.getTreeCaptionHTML(resName, resUtil, null, false); 1754 1755 resourceItem.getItemProperty(CmsResourceTableProperty.PROPERTY_TREE_CAPTION).setValue(iconHTML); 1756 if (disabled) { 1757 resourceItem.getItemProperty(CmsResourceTableProperty.PROPERTY_DISABLED).setValue(Boolean.TRUE); 1758 } 1759 if (parentId != null) { 1760 container.setChildrenAllowed(parentId, true); 1761 container.setParent(resource.getStructureId(), parentId); 1762 } 1763 } 1764 1765 /** 1766 * Creates the site selector combo box.<p> 1767 * 1768 * @param cms the current cms context 1769 * 1770 * @return the combo box 1771 */ 1772 private ComboBox createSiteSelect(CmsObject cms) { 1773 1774 final List<CmsExtendedSiteSelector.SiteSelectorOption> sites = CmsExtendedSiteSelector.getExplorerSiteSelectorOptions( 1775 cms, 1776 true); 1777 final BeanItemContainer<CmsExtendedSiteSelector.SiteSelectorOption> container = new BeanItemContainer<>( 1778 CmsExtendedSiteSelector.SiteSelectorOption.class); 1779 for (CmsExtendedSiteSelector.SiteSelectorOption option : sites) { 1780 container.addItem(option); 1781 } 1782 ComboBox combo = new ComboBox(null, container); 1783 combo.setTextInputAllowed(true); 1784 combo.setNullSelectionAllowed(false); 1785 combo.setWidth("200px"); 1786 combo.setInputPrompt( 1787 Messages.get().getBundle(UI.getCurrent().getLocale()).key(Messages.GUI_EXPLORER_CLICK_TO_EDIT_0)); 1788 combo.setItemCaptionPropertyId("label"); 1789 combo.select(new CmsExtendedSiteSelector.SiteSelectorOption(cms.getRequestContext().getSiteRoot(), null, null)); 1790 combo.setFilteringMode(FilteringMode.CONTAINS); 1791 combo.addValueChangeListener(new ValueChangeListener() { 1792 1793 private static final long serialVersionUID = 1L; 1794 1795 /** Flag used to temporarily disable the standard listener behavior, used when we change the combo box's value programatically. */ 1796 private boolean m_disabled; 1797 1798 public void valueChange(ValueChangeEvent event) { 1799 1800 if (m_disabled) { 1801 return; 1802 } 1803 CmsExtendedSiteSelector.SiteSelectorOption option = (CmsExtendedSiteSelector.SiteSelectorOption)(event.getProperty().getValue()); 1804 if (container.containsId(option)) { 1805 changeSite(option.getSite(), option.getPath()); 1806 m_appContext.updateOnChange(); 1807 container.removeAllContainerFilters(); 1808 if (option.getPath() != null) { 1809 try { 1810 m_disabled = true; 1811 combo.select(new CmsExtendedSiteSelector.SiteSelectorOption(option.getSite(), null, null)); 1812 } finally { 1813 m_disabled = false; 1814 } 1815 } 1816 } 1817 } 1818 }); 1819 if (Page.getCurrent().getBrowserWindowHeight() > 650) { 1820 combo.setPageLength(20); 1821 } 1822 return combo; 1823 } 1824 1825 /** 1826 * Returns the site path from the current state.<p> 1827 * 1828 * @return the site path 1829 */ 1830 private String getPathFromState() { 1831 1832 return StateBean.parse(m_currentState).getFolder(); 1833 } 1834 1835 /** 1836 * Returns the project id from the current state.<p> 1837 * 1838 * @return the project id 1839 */ 1840 private CmsUUID getProjectIdFromState() { 1841 1842 String projectIdStr = StateBean.parse(m_currentState).getProjectId(); 1843 if (CmsUUID.isValidUUID(projectIdStr)) { 1844 return new CmsUUID(projectIdStr); 1845 1846 } 1847 return null; 1848 1849 } 1850 1851 /** 1852 * Returns selected resource UUID from state (empty if not set).<p> 1853 * 1854 * @param state current state 1855 * @return uuid as string 1856 */ 1857 private String getSelectionFromState(String state) { 1858 1859 return StateBean.parse(state).getSelectedResource(); 1860 } 1861 1862 /** 1863 * Returns the site root from the current state.<p> 1864 * 1865 * @return the site root 1866 */ 1867 private String getSiteRootFromState() { 1868 1869 String siteRoot = StateBean.parse(m_currentState).getSiteRoot(); 1870 return siteRoot; 1871 } 1872 1873 /** 1874 * Populates the folder tree.<p> 1875 * 1876 * @param showInvisible to show non visible root folder as disabled 1877 * 1878 * @throws CmsException in case reading the root folder fails 1879 */ 1880 private void initFolderTree(boolean showInvisible) throws CmsException { 1881 1882 CmsObject cms = A_CmsUI.getCmsObject(); 1883 m_treeContainer.removeAllItems(); 1884 try { 1885 CmsResource siteRoot = cms.readResource("/", FOLDERS); 1886 addTreeItem(cms, siteRoot, null, false, m_treeContainer); 1887 } catch (CmsException e) { 1888 if (showInvisible) { 1889 CmsResource siteRoot = cms.readResource("/", CmsResourceFilter.IGNORE_EXPIRATION); 1890 addTreeItem(cms, siteRoot, null, true, m_treeContainer); 1891 } else { 1892 throw e; 1893 } 1894 } 1895 1896 } 1897 1898 /** 1899 * Initializes the app specific toolbar buttons.<p> 1900 * 1901 * @param context the UI context 1902 */ 1903 private void initToolbarButtons(I_CmsAppUIContext context) { 1904 1905 m_publishButton = context.addPublishButton(new I_CmsUpdateListener<String>() { 1906 1907 public void onUpdate(List<String> updatedItems) { 1908 1909 updateAll(false); 1910 } 1911 1912 }); 1913 1914 m_newButton = CmsToolBar.createButton( 1915 FontOpenCms.WAND, 1916 CmsVaadinUtils.getMessageText(Messages.GUI_NEW_RESOURCE_TITLE_0)); 1917 if (CmsAppWorkplaceUi.isOnlineProject()) { 1918 m_newButton.setEnabled(false); 1919 m_newButton.setDescription(CmsVaadinUtils.getMessageText(Messages.GUI_TOOLBAR_NOT_AVAILABLE_ONLINE_0)); 1920 } 1921 m_newButton.addClickListener(new ClickListener() { 1922 1923 private static final long serialVersionUID = 1L; 1924 1925 public void buttonClick(ClickEvent event) { 1926 1927 onClickNew(); 1928 } 1929 1930 }); 1931 1932 context.addToolbarButton(m_newButton); 1933 1934 m_uploadButton = new CmsUploadButton(FontOpenCms.UPLOAD, "/"); 1935 m_specialUploadButton = new Button(FontOpenCms.UPLOAD); 1936 m_uploadButton.addStyleName(ValoTheme.BUTTON_BORDERLESS); 1937 m_uploadButton.addStyleName(OpenCmsTheme.TOOLBAR_BUTTON); 1938 if (CmsAppWorkplaceUi.isOnlineProject()) { 1939 m_uploadButton.setEnabled(false); 1940 m_specialUploadButton.setEnabled(false); 1941 m_uploadButton.setDescription(CmsVaadinUtils.getMessageText(Messages.GUI_TOOLBAR_NOT_AVAILABLE_ONLINE_0)); 1942 m_specialUploadButton.setDescription( 1943 CmsVaadinUtils.getMessageText(Messages.GUI_TOOLBAR_NOT_AVAILABLE_ONLINE_0)); 1944 } else { 1945 m_uploadButton.setDescription(CmsVaadinUtils.getMessageText(Messages.GUI_UPLOAD_BUTTON_TITLE_0)); 1946 m_specialUploadButton.setDescription(CmsVaadinUtils.getMessageText(Messages.GUI_UPLOAD_BUTTON_TITLE_0)); 1947 } 1948 m_uploadButton.addUploadListener(new I_UploadListener() { 1949 1950 public void onUploadFinished(List<String> uploadedFiles) { 1951 1952 updateAll(true); 1953 } 1954 }); 1955 m_specialUploadButton.addStyleName(ValoTheme.BUTTON_BORDERLESS); 1956 m_specialUploadButton.addStyleName(OpenCmsTheme.TOOLBAR_BUTTON); 1957 m_specialUploadButton.addClickListener(event -> { 1958 try { 1959 @SuppressWarnings("unchecked") 1960 Class<I_CmsWorkplaceAction> actionClass = (Class<I_CmsWorkplaceAction>)getClass().getClassLoader().loadClass( 1961 m_uploadAction); 1962 I_CmsWorkplaceAction action = actionClass.newInstance(); 1963 List<CmsResource> resources = new ArrayList<>(); 1964 resources.add(m_uploadFolder); 1965 CmsExplorerDialogContext uploadContext = new CmsExplorerDialogContext( 1966 ContextType.fileTable, 1967 m_fileTable, 1968 this, 1969 resources); 1970 action.executeAction(uploadContext); 1971 } catch (Exception e) { 1972 LOG.error(e.getLocalizedMessage(), e); 1973 CmsErrorDialog.showErrorDialog(e); 1974 } 1975 1976 }); 1977 1978 context.addToolbarButton(m_uploadButton); 1979 context.addToolbarButton(m_specialUploadButton); 1980 } 1981 1982 /** 1983 * Normalizes the state string. Ensuring it starts with the right number of slashes resolving an issue with the vaadin state handling.<p> 1984 * 1985 * @param state the state string 1986 * 1987 * @return the normalized state string 1988 */ 1989 private String normalizeState(String state) { 1990 1991 String result = ""; 1992 if (state.contains(A_CmsWorkplaceApp.PARAM_SEPARATOR)) { 1993 result = state; 1994 while (result.startsWith(CmsAppWorkplaceUi.WORKPLACE_STATE_SEPARATOR)) { 1995 result = result.substring(1); 1996 } 1997 } 1998 return result; 1999 } 2000 2001 /** 2002 * Sets the current path info.<p> 2003 * 2004 * @param path the path 2005 */ 2006 private void setPathInfo(String path) { 2007 2008 m_infoPath.setValue(path); 2009 2010 // generate the path bread crumb 2011 m_crumbs.removeAllComponents(); 2012 int i = path.indexOf("/"); 2013 String openPath = ""; 2014 while (i >= 0) { 2015 String fragment = path.substring(0, i + 1); 2016 openPath += fragment; 2017 path = path.substring(i + 1); 2018 i = path.indexOf("/"); 2019 Button crumb = new Button(fragment, m_crumbListener); 2020 crumb.setData(openPath); 2021 crumb.addStyleName(ValoTheme.BUTTON_LINK); 2022 m_crumbs.addComponent(crumb); 2023 } 2024 } 2025 2026 /** 2027 * Sets the toolbar buttons enabled.<p> 2028 * 2029 * @param enabled the enabled flag 2030 */ 2031 private void setToolbarButtonsEnabled(boolean enabled) { 2032 2033 m_publishButton.setEnabled(enabled); 2034 m_newButton.setEnabled(enabled); 2035 m_uploadButton.setEnabled(enabled); 2036 m_specialUploadButton.setEnabled(enabled); 2037 if (enabled) { 2038 m_publishButton.setDescription(CmsVaadinUtils.getMessageText(Messages.GUI_PUBLISH_BUTTON_TITLE_0)); 2039 m_newButton.setDescription(CmsVaadinUtils.getMessageText(Messages.GUI_NEW_RESOURCE_TITLE_0)); 2040 m_uploadButton.setDescription(CmsVaadinUtils.getMessageText(Messages.GUI_UPLOAD_BUTTON_TITLE_0)); 2041 m_specialUploadButton.setDescription(CmsVaadinUtils.getMessageText(Messages.GUI_UPLOAD_BUTTON_TITLE_0)); 2042 } else { 2043 m_publishButton.setDescription(CmsVaadinUtils.getMessageText(Messages.GUI_TOOLBAR_NOT_AVAILABLE_ONLINE_0)); 2044 m_newButton.setDescription(CmsVaadinUtils.getMessageText(Messages.GUI_TOOLBAR_NOT_AVAILABLE_ONLINE_0)); 2045 m_uploadButton.setDescription(CmsVaadinUtils.getMessageText(Messages.GUI_TOOLBAR_NOT_AVAILABLE_ONLINE_0)); 2046 m_specialUploadButton.setDescription( 2047 CmsVaadinUtils.getMessageText(Messages.GUI_TOOLBAR_NOT_AVAILABLE_ONLINE_0)); 2048 } 2049 } 2050 2051 /** 2052 * Updates the upload button state based on the current folder. 2053 * 2054 * @param folder the current folder 2055 */ 2056 private void updateUploadButton(CmsResource folder) { 2057 2058 String uploadAction = null; 2059 m_uploadFolder = folder; 2060 try { 2061 uploadAction = OpenCms.getResourceManager().getResourceType(folder).getConfiguration().get( 2062 "gallery.upload.action"); 2063 } catch (Exception e) { 2064 // Resource type not found? 2065 LOG.error(e.getLocalizedMessage(), e); 2066 } 2067 m_uploadAction = uploadAction; 2068 if (uploadAction != null) { 2069 m_uploadButton.setVisible(false); 2070 m_specialUploadButton.setVisible(true); 2071 m_uploadArea.setTargetFolder(null); 2072 } else { 2073 boolean enabled = OpenCms.getWorkplaceManager().getUploadRestriction().getUploadRestrictionInfo( 2074 A_CmsUI.getCmsObject()).isUploadEnabled(folder.getRootPath()); 2075 m_uploadButton.setVisible(enabled); 2076 m_specialUploadButton.setVisible(false); 2077 m_uploadButton.setTargetFolder(folder.getRootPath()); 2078 m_uploadArea.setTargetFolder(folder.getRootPath()); 2079 } 2080 } 2081}