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