001/* 002 * This library is part of OpenCms - 003 * the Open Source Content Management System 004 * 005 * Copyright (C) Alkacon Software (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.components; 029 030import static org.opencms.ui.components.CmsResourceTableProperty.PROPERTY_CACHE; 031import static org.opencms.ui.components.CmsResourceTableProperty.PROPERTY_COPYRIGHT; 032import static org.opencms.ui.components.CmsResourceTableProperty.PROPERTY_DATE_CREATED; 033import static org.opencms.ui.components.CmsResourceTableProperty.PROPERTY_DATE_EXPIRED; 034import static org.opencms.ui.components.CmsResourceTableProperty.PROPERTY_DATE_MODIFIED; 035import static org.opencms.ui.components.CmsResourceTableProperty.PROPERTY_DATE_RELEASED; 036import static org.opencms.ui.components.CmsResourceTableProperty.PROPERTY_INSIDE_PROJECT; 037import static org.opencms.ui.components.CmsResourceTableProperty.PROPERTY_INTERNAL_RESOURCE_TYPE; 038import static org.opencms.ui.components.CmsResourceTableProperty.PROPERTY_IN_NAVIGATION; 039import static org.opencms.ui.components.CmsResourceTableProperty.PROPERTY_IS_FOLDER; 040import static org.opencms.ui.components.CmsResourceTableProperty.PROPERTY_NAVIGATION_POSITION; 041import static org.opencms.ui.components.CmsResourceTableProperty.PROPERTY_NAVIGATION_TEXT; 042import static org.opencms.ui.components.CmsResourceTableProperty.PROPERTY_PERMISSIONS; 043import static org.opencms.ui.components.CmsResourceTableProperty.PROPERTY_PROJECT; 044import static org.opencms.ui.components.CmsResourceTableProperty.PROPERTY_RELEASED_NOT_EXPIRED; 045import static org.opencms.ui.components.CmsResourceTableProperty.PROPERTY_RESOURCE_NAME; 046import static org.opencms.ui.components.CmsResourceTableProperty.PROPERTY_RESOURCE_TYPE; 047import static org.opencms.ui.components.CmsResourceTableProperty.PROPERTY_SIZE; 048import static org.opencms.ui.components.CmsResourceTableProperty.PROPERTY_STATE; 049import static org.opencms.ui.components.CmsResourceTableProperty.PROPERTY_STATE_NAME; 050import static org.opencms.ui.components.CmsResourceTableProperty.PROPERTY_TITLE; 051import static org.opencms.ui.components.CmsResourceTableProperty.PROPERTY_TYPE_ICON; 052import static org.opencms.ui.components.CmsResourceTableProperty.PROPERTY_USER_CREATED; 053import static org.opencms.ui.components.CmsResourceTableProperty.PROPERTY_USER_LOCKED; 054import static org.opencms.ui.components.CmsResourceTableProperty.PROPERTY_USER_MODIFIED; 055 056import org.opencms.db.CmsResourceState; 057import org.opencms.file.CmsObject; 058import org.opencms.file.CmsResource; 059import org.opencms.file.CmsResourceFilter; 060import org.opencms.file.CmsVfsResourceNotFoundException; 061import org.opencms.main.CmsException; 062import org.opencms.main.CmsLog; 063import org.opencms.main.OpenCms; 064import org.opencms.ui.A_CmsUI; 065import org.opencms.ui.CmsVaadinUtils; 066import org.opencms.ui.I_CmsDialogContext; 067import org.opencms.ui.I_CmsEditPropertyContext; 068import org.opencms.ui.actions.I_CmsDefaultAction; 069import org.opencms.ui.apps.CmsFileExplorerSettings; 070import org.opencms.ui.apps.I_CmsContextProvider; 071import org.opencms.ui.contextmenu.CmsContextMenu; 072import org.opencms.ui.contextmenu.CmsResourceContextMenuBuilder; 073import org.opencms.ui.contextmenu.I_CmsContextMenuBuilder; 074import org.opencms.ui.util.I_CmsItemSorter; 075import org.opencms.util.CmsStringUtil; 076import org.opencms.util.CmsUUID; 077 078import java.util.ArrayList; 079import java.util.Collection; 080import java.util.Collections; 081import java.util.HashSet; 082import java.util.LinkedHashMap; 083import java.util.List; 084import java.util.Map; 085import java.util.Map.Entry; 086import java.util.Set; 087 088import org.apache.commons.logging.Log; 089 090import com.google.common.collect.Lists; 091import com.vaadin.event.FieldEvents.BlurEvent; 092import com.vaadin.event.FieldEvents.BlurListener; 093import com.vaadin.event.ShortcutAction.KeyCode; 094import com.vaadin.event.ShortcutListener; 095import com.vaadin.shared.MouseEventDetails.MouseButton; 096import com.vaadin.ui.Component; 097import com.vaadin.ui.themes.ValoTheme; 098import com.vaadin.v7.data.Container; 099import com.vaadin.v7.data.Container.Filter; 100import com.vaadin.v7.data.Item; 101import com.vaadin.v7.data.Property.ValueChangeEvent; 102import com.vaadin.v7.data.Property.ValueChangeListener; 103import com.vaadin.v7.data.util.DefaultItemSorter; 104import com.vaadin.v7.data.util.IndexedContainer; 105import com.vaadin.v7.data.util.filter.Or; 106import com.vaadin.v7.data.util.filter.SimpleStringFilter; 107import com.vaadin.v7.event.ItemClickEvent; 108import com.vaadin.v7.event.ItemClickEvent.ItemClickListener; 109import com.vaadin.v7.ui.AbstractTextField.TextChangeEventMode; 110import com.vaadin.v7.ui.DefaultFieldFactory; 111import com.vaadin.v7.ui.Field; 112import com.vaadin.v7.ui.Table; 113import com.vaadin.v7.ui.Table.TableDragMode; 114import com.vaadin.v7.ui.TextField; 115 116/** 117 * Table for displaying resources.<p> 118 */ 119public class CmsFileTable extends CmsResourceTable { 120 121 /** 122 * File edit handler.<p> 123 */ 124 public class FileEditHandler implements BlurListener { 125 126 /** The serial version id. */ 127 private static final long serialVersionUID = -2286815522247807054L; 128 129 /** 130 * @see com.vaadin.event.FieldEvents.BlurListener#blur(com.vaadin.event.FieldEvents.BlurEvent) 131 */ 132 public void blur(BlurEvent event) { 133 134 stopEdit(); 135 } 136 } 137 138 /** 139 * Field factory to enable inline editing of individual file properties.<p> 140 */ 141 public class FileFieldFactory extends DefaultFieldFactory { 142 143 /** The serial version id. */ 144 private static final long serialVersionUID = 3079590603587933576L; 145 146 /** 147 * @see com.vaadin.ui.DefaultFieldFactory#createField(com.vaadin.v7.data.Container, java.lang.Object, java.lang.Object, com.vaadin.ui.Component) 148 */ 149 @Override 150 public Field<?> createField(Container container, Object itemId, Object propertyId, Component uiContext) { 151 152 Field<?> result = null; 153 if (itemId.equals(getEditItemId().toString()) && isEditProperty((CmsResourceTableProperty)propertyId)) { 154 result = super.createField(container, itemId, propertyId, uiContext); 155 result.addStyleName(OpenCmsTheme.INLINE_TEXTFIELD); 156 result.addValidator(m_editHandler); 157 if (result instanceof TextField) { 158 ((TextField)result).setComponentError(null); 159 ((TextField)result).addShortcutListener(new ShortcutListener("Cancel edit", KeyCode.ESCAPE, null) { 160 161 private static final long serialVersionUID = 1L; 162 163 @Override 164 public void handleAction(Object sender, Object target) { 165 166 cancelEdit(); 167 } 168 }); 169 ((TextField)result).addShortcutListener(new ShortcutListener("Save", KeyCode.ENTER, null) { 170 171 private static final long serialVersionUID = 1L; 172 173 @Override 174 public void handleAction(Object sender, Object target) { 175 176 stopEdit(); 177 } 178 }); 179 ((TextField)result).addBlurListener(m_fileEditHandler); 180 ((TextField)result).setTextChangeEventMode(TextChangeEventMode.LAZY); 181 ((TextField)result).addTextChangeListener(m_editHandler); 182 } 183 result.focus(); 184 } 185 return result; 186 } 187 } 188 189 /** 190 * Extends the default sorting to differentiate between files and folder when sorting by name.<p> 191 * Also allows sorting by navPos property for the Resource icon column.<p> 192 */ 193 public static class FileSorter extends DefaultItemSorter implements I_CmsItemSorter { 194 195 /** The serial version id. */ 196 private static final long serialVersionUID = 1L; 197 198 /** 199 * @see org.opencms.ui.util.I_CmsItemSorter#getSortableContainerPropertyIds(com.vaadin.v7.data.Container) 200 */ 201 public Collection<?> getSortableContainerPropertyIds(Container container) { 202 203 Set<Object> result = new HashSet<Object>(); 204 for (Object propId : container.getContainerPropertyIds()) { 205 Class<?> propertyType = container.getType(propId); 206 if (Comparable.class.isAssignableFrom(propertyType) 207 || propertyType.isPrimitive() 208 || (propId.equals(CmsResourceTableProperty.PROPERTY_TYPE_ICON) 209 && container.getContainerPropertyIds().contains( 210 CmsResourceTableProperty.PROPERTY_NAVIGATION_POSITION))) { 211 result.add(propId); 212 } 213 } 214 return result; 215 } 216 217 /** 218 * @see com.vaadin.v7.data.util.DefaultItemSorter#compareProperty(java.lang.Object, boolean, com.vaadin.v7.data.Item, com.vaadin.v7.data.Item) 219 */ 220 @Override 221 protected int compareProperty(Object propertyId, boolean sortDirection, Item item1, Item item2) { 222 223 //@formatter:off 224 if (CmsResourceTableProperty.PROPERTY_RESOURCE_NAME.equals(propertyId)) { 225 Boolean isFolder1 = (Boolean)item1.getItemProperty( 226 CmsResourceTableProperty.PROPERTY_IS_FOLDER).getValue(); 227 Boolean isFolder2 = (Boolean)item2.getItemProperty( 228 CmsResourceTableProperty.PROPERTY_IS_FOLDER).getValue(); 229 if (!isFolder1.equals(isFolder2)) { 230 int result = isFolder1.booleanValue() ? -1 : 1; 231 if (!sortDirection) { 232 result = result * (-1); 233 } 234 return result; 235 } 236 } else if ((CmsResourceTableProperty.PROPERTY_TYPE_ICON.equals(propertyId) 237 || CmsResourceTableProperty.PROPERTY_NAVIGATION_TEXT.equals(propertyId)) 238 && (item1.getItemProperty(CmsResourceTableProperty.PROPERTY_NAVIGATION_POSITION) != null)) { 239 int result; 240 Float pos1 = (Float)item1.getItemProperty( 241 CmsResourceTableProperty.PROPERTY_NAVIGATION_POSITION).getValue(); 242 Float pos2 = (Float)item2.getItemProperty( 243 CmsResourceTableProperty.PROPERTY_NAVIGATION_POSITION).getValue(); 244 if (pos1 == null) { 245 result = pos2 == null 246 ? compareProperty(CmsResourceTableProperty.PROPERTY_RESOURCE_NAME, true, item1, item2) 247 : 1; 248 } else { 249 result = pos2 == null ? -1 : Float.compare(pos1.floatValue(), pos2.floatValue()); 250 } 251 if (!sortDirection) { 252 result = result * (-1); 253 } 254 return result; 255 } else if (((CmsResourceTableProperty)propertyId).getColumnType().equals(String.class)) { 256 String value1 = (String)item1.getItemProperty(propertyId).getValue(); 257 String value2 = (String)item2.getItemProperty(propertyId).getValue(); 258 // Java collators obtained by java.text.Collator.getInstance(...) ignore spaces, and we don't want to ignore them, so we use 259 // ICU collators instead 260 com.ibm.icu.text.Collator collator = com.ibm.icu.text.Collator.getInstance( 261 com.ibm.icu.util.ULocale.ROOT); 262 int result = collator.compare(value1, value2); 263 if (!sortDirection) { 264 result = -result; 265 } 266 return result; 267 } 268 return super.compareProperty(propertyId, sortDirection, item1, item2); 269 //@formatter:on 270 } 271 } 272 273 /** 274 * Handles folder selects in the file table.<p> 275 */ 276 public interface I_FolderSelectHandler { 277 278 /** 279 * Called when the folder name is left clicked.<p> 280 * 281 * @param folderId the selected folder id 282 */ 283 void onFolderSelect(CmsUUID folderId); 284 } 285 286 /** The default file table columns. */ 287 public static final Map<CmsResourceTableProperty, Integer> DEFAULT_TABLE_PROPERTIES; 288 289 /** The logger instance for this class. */ 290 static final Log LOG = CmsLog.getLog(CmsFileTable.class); 291 292 /** The serial version id. */ 293 private static final long serialVersionUID = 5460048685141699277L; 294 295 static { 296 Map<CmsResourceTableProperty, Integer> defaultProps = new LinkedHashMap<CmsResourceTableProperty, Integer>(); 297 defaultProps.put(PROPERTY_TYPE_ICON, Integer.valueOf(0)); 298 defaultProps.put(PROPERTY_PROJECT, Integer.valueOf(COLLAPSED)); 299 defaultProps.put(PROPERTY_RESOURCE_NAME, Integer.valueOf(0)); 300 defaultProps.put(PROPERTY_TITLE, Integer.valueOf(0)); 301 defaultProps.put(PROPERTY_NAVIGATION_TEXT, Integer.valueOf(COLLAPSED)); 302 defaultProps.put(PROPERTY_NAVIGATION_POSITION, Integer.valueOf(INVISIBLE)); 303 defaultProps.put(PROPERTY_IN_NAVIGATION, Integer.valueOf(INVISIBLE)); 304 defaultProps.put(PROPERTY_COPYRIGHT, Integer.valueOf(COLLAPSED)); 305 defaultProps.put(PROPERTY_CACHE, Integer.valueOf(COLLAPSED)); 306 defaultProps.put(PROPERTY_RESOURCE_TYPE, Integer.valueOf(0)); 307 defaultProps.put(PROPERTY_INTERNAL_RESOURCE_TYPE, Integer.valueOf(COLLAPSED)); 308 defaultProps.put(PROPERTY_SIZE, Integer.valueOf(0)); 309 defaultProps.put(PROPERTY_PERMISSIONS, Integer.valueOf(COLLAPSED)); 310 defaultProps.put(PROPERTY_DATE_MODIFIED, Integer.valueOf(0)); 311 defaultProps.put(PROPERTY_USER_MODIFIED, Integer.valueOf(COLLAPSED)); 312 defaultProps.put(PROPERTY_DATE_CREATED, Integer.valueOf(COLLAPSED)); 313 defaultProps.put(PROPERTY_USER_CREATED, Integer.valueOf(COLLAPSED)); 314 defaultProps.put(PROPERTY_DATE_RELEASED, Integer.valueOf(0)); 315 defaultProps.put(PROPERTY_DATE_EXPIRED, Integer.valueOf(0)); 316 defaultProps.put(PROPERTY_STATE_NAME, Integer.valueOf(0)); 317 defaultProps.put(PROPERTY_USER_LOCKED, Integer.valueOf(0)); 318 defaultProps.put(PROPERTY_IS_FOLDER, Integer.valueOf(INVISIBLE)); 319 defaultProps.put(PROPERTY_STATE, Integer.valueOf(INVISIBLE)); 320 defaultProps.put(PROPERTY_INSIDE_PROJECT, Integer.valueOf(INVISIBLE)); 321 defaultProps.put(PROPERTY_RELEASED_NOT_EXPIRED, Integer.valueOf(INVISIBLE)); 322 DEFAULT_TABLE_PROPERTIES = Collections.unmodifiableMap(defaultProps); 323 } 324 325 /** The selected resources. */ 326 protected List<CmsResource> m_currentResources = new ArrayList<CmsResource>(); 327 328 /** The default action column property. */ 329 CmsResourceTableProperty m_actionColumnProperty; 330 331 /** The additional cell style generators. */ 332 List<Table.CellStyleGenerator> m_additionalStyleGenerators; 333 334 /** The current file property edit handler. */ 335 I_CmsFilePropertyEditHandler m_editHandler; 336 337 /** File edit event handler. */ 338 FileEditHandler m_fileEditHandler = new FileEditHandler(); 339 340 /** The context menu. */ 341 CmsContextMenu m_menu; 342 343 /** The context menu builder. */ 344 I_CmsContextMenuBuilder m_menuBuilder; 345 346 /** The table drag mode, stored during item editing. */ 347 private TableDragMode m_beforEditDragMode; 348 349 /** The dialog context provider. */ 350 private I_CmsContextProvider m_contextProvider; 351 352 /** The edited item id. */ 353 private CmsUUID m_editItemId; 354 355 /** The edited property id. */ 356 private CmsResourceTableProperty m_editProperty; 357 358 /** Saved container filters. */ 359 private Collection<Filter> m_filters = Collections.emptyList(); 360 361 /** The folder select handler. */ 362 private I_FolderSelectHandler m_folderSelectHandler; 363 364 /** The original edit value. */ 365 private String m_originalEditValue; 366 367 /** 368 * Default constructor.<p> 369 * 370 * @param contextProvider the dialog context provider 371 */ 372 public CmsFileTable(I_CmsContextProvider contextProvider) { 373 374 this(contextProvider, DEFAULT_TABLE_PROPERTIES); 375 } 376 377 /** 378 * Default constructor.<p> 379 * 380 * @param contextProvider the dialog context provider 381 * @param tableColumns the table columns to show 382 */ 383 public CmsFileTable(I_CmsContextProvider contextProvider, Map<CmsResourceTableProperty, Integer> tableColumns) { 384 385 super(); 386 m_additionalStyleGenerators = new ArrayList<Table.CellStyleGenerator>(); 387 m_actionColumnProperty = PROPERTY_RESOURCE_NAME; 388 m_contextProvider = contextProvider; 389 m_container.setItemSorter(new FileSorter()); 390 m_fileTable.addStyleName(ValoTheme.TABLE_BORDERLESS); 391 m_fileTable.addStyleName(OpenCmsTheme.SIMPLE_DRAG); 392 m_fileTable.setSizeFull(); 393 m_fileTable.setColumnCollapsingAllowed(true); 394 m_fileTable.setSelectable(true); 395 m_fileTable.setMultiSelect(true); 396 397 m_fileTable.setTableFieldFactory(new FileFieldFactory()); 398 ColumnBuilder builder = new ColumnBuilder(); 399 for (Entry<CmsResourceTableProperty, Integer> entry : tableColumns.entrySet()) { 400 builder.column(entry.getKey(), entry.getValue().intValue()); 401 } 402 builder.buildColumns(); 403 404 m_fileTable.setSortContainerPropertyId(CmsResourceTableProperty.PROPERTY_RESOURCE_NAME); 405 m_menu = new CmsContextMenu(); 406 m_fileTable.addValueChangeListener(new ValueChangeListener() { 407 408 private static final long serialVersionUID = 1L; 409 410 public void valueChange(ValueChangeEvent event) { 411 412 @SuppressWarnings("unchecked") 413 Set<String> selectedIds = (Set<String>)event.getProperty().getValue(); 414 List<CmsResource> selectedResources = new ArrayList<CmsResource>(); 415 for (String id : selectedIds) { 416 try { 417 CmsResource resource = A_CmsUI.getCmsObject().readResource( 418 getUUIDFromItemID(id), 419 CmsResourceFilter.ALL); 420 selectedResources.add(resource); 421 } catch (CmsException e) { 422 LOG.error(e.getLocalizedMessage(), e); 423 } 424 425 } 426 m_currentResources = selectedResources; 427 428 rebuildMenu(); 429 } 430 }); 431 432 m_fileTable.addItemClickListener(new ItemClickListener() { 433 434 private static final long serialVersionUID = 1L; 435 436 public void itemClick(ItemClickEvent event) { 437 438 handleFileItemClick(event); 439 } 440 }); 441 442 m_fileTable.setCellStyleGenerator(new Table.CellStyleGenerator() { 443 444 private static final long serialVersionUID = 1L; 445 446 public String getStyle(Table source, Object itemId, Object propertyId) { 447 448 Item item = m_container.getItem(itemId); 449 String style = getStateStyle(item); 450 if (m_actionColumnProperty == propertyId) { 451 style += " " + OpenCmsTheme.HOVER_COLUMN; 452 } else if ((CmsResourceTableProperty.PROPERTY_NAVIGATION_TEXT == propertyId) 453 || (CmsResourceTableProperty.PROPERTY_TITLE == propertyId)) { 454 if ((item.getItemProperty(CmsResourceTableProperty.PROPERTY_IN_NAVIGATION) != null) 455 && ((Boolean)item.getItemProperty( 456 CmsResourceTableProperty.PROPERTY_IN_NAVIGATION).getValue()).booleanValue()) { 457 style += " " + OpenCmsTheme.IN_NAVIGATION; 458 } 459 } 460 for (Table.CellStyleGenerator generator : m_additionalStyleGenerators) { 461 String additional = generator.getStyle(source, itemId, propertyId); 462 if (CmsStringUtil.isNotEmptyOrWhitespaceOnly(additional)) { 463 style += " " + additional; 464 } 465 } 466 return style; 467 } 468 }); 469 470 m_menu.setAsTableContextMenu(m_fileTable); 471 } 472 473 /** 474 * Returns the resource state specific style name.<p> 475 * 476 * @param resourceItem the resource item 477 * 478 * @return the style name 479 */ 480 public static String getStateStyle(Item resourceItem) { 481 482 String result = ""; 483 if (resourceItem != null) { 484 if ((resourceItem.getItemProperty(PROPERTY_INSIDE_PROJECT) == null) 485 || ((Boolean)resourceItem.getItemProperty(PROPERTY_INSIDE_PROJECT).getValue()).booleanValue()) { 486 487 CmsResourceState state = (CmsResourceState)resourceItem.getItemProperty( 488 CmsResourceTableProperty.PROPERTY_STATE).getValue(); 489 result = getStateStyle(state); 490 } else { 491 result = OpenCmsTheme.PROJECT_OTHER; 492 } 493 if ((resourceItem.getItemProperty(PROPERTY_RELEASED_NOT_EXPIRED) != null) 494 && !((Boolean)resourceItem.getItemProperty(PROPERTY_RELEASED_NOT_EXPIRED).getValue()).booleanValue()) { 495 result += " " + OpenCmsTheme.EXPIRED; 496 } 497 if ((resourceItem.getItemProperty(CmsResourceTableProperty.PROPERTY_DISABLED) != null) 498 && ((Boolean)resourceItem.getItemProperty( 499 CmsResourceTableProperty.PROPERTY_DISABLED).getValue()).booleanValue()) { 500 result += " " + OpenCmsTheme.DISABLED; 501 } 502 } 503 return result; 504 } 505 506 /** 507 * Adds an additional cell style generator.<p> 508 * 509 * @param styleGenerator the cell style generator 510 */ 511 public void addAdditionalStyleGenerator(Table.CellStyleGenerator styleGenerator) { 512 513 m_additionalStyleGenerators.add(styleGenerator); 514 } 515 516 /** 517 * Applies settings generally used within workplace app file lists.<p> 518 */ 519 public void applyWorkplaceAppSettings() { 520 521 // add site path property to container 522 m_container.addContainerProperty( 523 CmsResourceTableProperty.PROPERTY_SITE_PATH, 524 CmsResourceTableProperty.PROPERTY_SITE_PATH.getColumnType(), 525 CmsResourceTableProperty.PROPERTY_SITE_PATH.getDefaultValue()); 526 527 // replace the resource name column with the path column 528 Object[] visibleCols = m_fileTable.getVisibleColumns(); 529 for (int i = 0; i < visibleCols.length; i++) { 530 if (CmsResourceTableProperty.PROPERTY_RESOURCE_NAME.equals(visibleCols[i])) { 531 visibleCols[i] = CmsResourceTableProperty.PROPERTY_SITE_PATH; 532 } 533 } 534 m_fileTable.setVisibleColumns(visibleCols); 535 m_fileTable.setColumnCollapsible(CmsResourceTableProperty.PROPERTY_SITE_PATH, false); 536 m_fileTable.setColumnHeader( 537 CmsResourceTableProperty.PROPERTY_SITE_PATH, 538 CmsVaadinUtils.getMessageText(CmsResourceTableProperty.PROPERTY_SITE_PATH.getHeaderKey())); 539 540 // update column visibility according to the latest file explorer settings 541 CmsFileExplorerSettings settings; 542 try { 543 settings = OpenCms.getWorkplaceAppManager().getAppSettings( 544 A_CmsUI.getCmsObject(), 545 CmsFileExplorerSettings.class); 546 547 setTableState(settings); 548 } catch (Exception e) { 549 LOG.error("Error while reading file explorer settings from user.", e); 550 } 551 m_fileTable.setSortContainerPropertyId(CmsResourceTableProperty.PROPERTY_SITE_PATH); 552 setActionColumnProperty(CmsResourceTableProperty.PROPERTY_SITE_PATH); 553 setMenuBuilder(new CmsResourceContextMenuBuilder()); 554 } 555 556 /** 557 * Clears all container filters. 558 */ 559 public void clearFilters() { 560 561 IndexedContainer container = (IndexedContainer)m_fileTable.getContainerDataSource(); 562 container.removeAllContainerFilters(); 563 } 564 565 /** 566 * Filters the displayed resources.<p> 567 * Only resources where either the resource name, the title or the nav-text contains the given substring are shown.<p> 568 * 569 * @param search the search term 570 */ 571 public void filterTable(String search) { 572 573 m_container.removeAllContainerFilters(); 574 if (CmsStringUtil.isNotEmptyOrWhitespaceOnly(search)) { 575 m_container.addContainerFilter( 576 new Or( 577 new SimpleStringFilter(CmsResourceTableProperty.PROPERTY_RESOURCE_NAME, search, true, false), 578 new SimpleStringFilter(CmsResourceTableProperty.PROPERTY_NAVIGATION_TEXT, search, true, false), 579 new SimpleStringFilter(CmsResourceTableProperty.PROPERTY_TITLE, search, true, false))); 580 } 581 if ((m_fileTable.getValue() != null) & !((Set<?>)m_fileTable.getValue()).isEmpty()) { 582 m_fileTable.setCurrentPageFirstItemId(((Set<?>)m_fileTable.getValue()).iterator().next()); 583 } 584 } 585 586 /** 587 * Returns the index of the first visible item.<p> 588 * 589 * @return the first visible item 590 */ 591 public int getFirstVisibleItemIndex() { 592 593 return m_fileTable.getCurrentPageFirstItemIndex(); 594 } 595 596 /** 597 * Gets the selected structure ids.<p> 598 * 599 * @return the set of selected structure ids 600 */ 601 @SuppressWarnings("unchecked") 602 public Collection<CmsUUID> getSelectedIds() { 603 604 return itemIdsToUUIDs((Collection<String>)m_fileTable.getValue()); 605 } 606 607 /** 608 * Gets the list of selected resources.<p> 609 * 610 * @return the list of selected resources 611 */ 612 public List<CmsResource> getSelectedResources() { 613 614 return m_currentResources; 615 } 616 617 /** 618 * Returns the current table state.<p> 619 * 620 * @return the table state 621 */ 622 public CmsFileExplorerSettings getTableSettings() { 623 624 CmsFileExplorerSettings fileTableState = new CmsFileExplorerSettings(); 625 626 fileTableState.setSortAscending(m_fileTable.isSortAscending()); 627 fileTableState.setSortColumnId((CmsResourceTableProperty)m_fileTable.getSortContainerPropertyId()); 628 List<CmsResourceTableProperty> collapsedCollumns = new ArrayList<CmsResourceTableProperty>(); 629 Object[] visibleCols = m_fileTable.getVisibleColumns(); 630 for (int i = 0; i < visibleCols.length; i++) { 631 if (m_fileTable.isColumnCollapsed(visibleCols[i])) { 632 collapsedCollumns.add((CmsResourceTableProperty)visibleCols[i]); 633 } 634 } 635 fileTableState.setCollapsedColumns(collapsedCollumns); 636 return fileTableState; 637 } 638 639 /** 640 * Handles the item selection.<p> 641 * 642 * @param itemId the selected item id 643 */ 644 public void handleSelection(String itemId) { 645 646 Collection<?> selection = (Collection<?>)m_fileTable.getValue(); 647 if (selection == null) { 648 m_fileTable.select(itemId); 649 } else if (!selection.contains(itemId)) { 650 m_fileTable.setValue(null); 651 m_fileTable.select(itemId); 652 } 653 } 654 655 /** 656 * Returns if a file property is being edited.<p> 657 * @return <code>true</code> if a file property is being edited 658 */ 659 public boolean isEditing() { 660 661 return m_editItemId != null; 662 } 663 664 /** 665 * Returns if the given property is being edited.<p> 666 * 667 * @param propertyId the property id 668 * 669 * @return <code>true</code> if the given property is being edited 670 */ 671 public boolean isEditProperty(CmsResourceTableProperty propertyId) { 672 673 return (m_editProperty != null) && m_editProperty.equals(propertyId); 674 } 675 676 /** 677 * Opens the context menu.<p> 678 * 679 * @param event the click event 680 */ 681 public void openContextMenu(ItemClickEvent event) { 682 683 m_menu.openForTable(event, m_fileTable); 684 } 685 686 /** 687 * Removes the given cell style generator.<p> 688 * 689 * @param styleGenerator the cell style generator to remove 690 */ 691 public void removeAdditionalStyleGenerator(Table.CellStyleGenerator styleGenerator) { 692 693 m_additionalStyleGenerators.remove(styleGenerator); 694 } 695 696 /** 697 * Restores container filters to the ones previously saved via saveFilters(). 698 */ 699 public void restoreFilters() { 700 701 IndexedContainer container = (IndexedContainer)m_fileTable.getContainerDataSource(); 702 container.removeAllContainerFilters(); 703 for (Filter filter : m_filters) { 704 container.addContainerFilter(filter); 705 } 706 } 707 708 /** 709 * Saves currently active filters.<p> 710 */ 711 public void saveFilters() { 712 713 IndexedContainer container = (IndexedContainer)m_fileTable.getContainerDataSource(); 714 m_filters = container.getContainerFilters(); 715 } 716 717 /** 718 * Sets the default action column property.<p> 719 * 720 * @param actionColumnProperty the default action column property 721 */ 722 public void setActionColumnProperty(CmsResourceTableProperty actionColumnProperty) { 723 724 m_actionColumnProperty = actionColumnProperty; 725 } 726 727 /** 728 * Sets the dialog context provider.<p> 729 * 730 * @param provider the dialog context provider 731 */ 732 public void setContextProvider(I_CmsContextProvider provider) { 733 734 m_contextProvider = provider; 735 } 736 737 /** 738 * Sets the first visible item index.<p> 739 * 740 * @param i the item index 741 */ 742 public void setFirstVisibleItemIndex(int i) { 743 744 m_fileTable.setCurrentPageFirstItemIndex(i); 745 } 746 747 /** 748 * Sets the folder select handler.<p> 749 * 750 * @param folderSelectHandler the folder select handler 751 */ 752 public void setFolderSelectHandler(I_FolderSelectHandler folderSelectHandler) { 753 754 m_folderSelectHandler = folderSelectHandler; 755 } 756 757 /** 758 * Sets the menu builder.<p> 759 * 760 * @param builder the menu builder 761 */ 762 public void setMenuBuilder(I_CmsContextMenuBuilder builder) { 763 764 m_menuBuilder = builder; 765 } 766 767 /** 768 * Sets the table state.<p> 769 * 770 * @param state the table state 771 */ 772 public void setTableState(CmsFileExplorerSettings state) { 773 774 if (state != null) { 775 m_fileTable.setSortContainerPropertyId(state.getSortColumnId()); 776 m_fileTable.setSortAscending(state.isSortAscending()); 777 Object[] visibleCols = m_fileTable.getVisibleColumns(); 778 for (int i = 0; i < visibleCols.length; i++) { 779 m_fileTable.setColumnCollapsed(visibleCols[i], state.getCollapsedColumns().contains(visibleCols[i])); 780 } 781 } 782 } 783 784 /** 785 * Starts inline editing of the given file property.<p> 786 * 787 * @param itemId the item resource structure id 788 * @param propertyId the property to edit 789 * @param editHandler the edit handler 790 */ 791 public void startEdit( 792 CmsUUID itemId, 793 CmsResourceTableProperty propertyId, 794 I_CmsFilePropertyEditHandler editHandler) { 795 796 m_editItemId = itemId; 797 m_editProperty = propertyId; 798 m_originalEditValue = (String)m_container.getItem(m_editItemId.toString()).getItemProperty( 799 m_editProperty).getValue(); 800 m_editHandler = editHandler; 801 802 // storing current drag mode and setting it to none to avoid text selection issues in IE11 803 m_beforEditDragMode = m_fileTable.getDragMode(); 804 m_fileTable.setDragMode(TableDragMode.NONE); 805 806 m_fileTable.setEditable(true); 807 } 808 809 /** 810 * Stops the current edit process to save the changed property value.<p> 811 */ 812 public void stopEdit() { 813 814 if (m_editHandler != null) { 815 String value = (String)m_container.getItem(m_editItemId.toString()).getItemProperty( 816 m_editProperty).getValue(); 817 if (!value.equals(m_originalEditValue)) { 818 m_editHandler.validate(value); 819 m_editHandler.save(value); 820 } else { 821 // call cancel to ensure unlock 822 m_editHandler.cancel(); 823 } 824 } 825 clearEdit(); 826 827 // restoring drag mode 828 m_fileTable.setDragMode(m_beforEditDragMode); 829 830 m_beforEditDragMode = null; 831 } 832 833 /** 834 * Updates all items with ids from the given list.<p> 835 * 836 * @param ids the resource structure ids to update 837 * @param remove true if the item should be removed only 838 */ 839 public void update(Collection<CmsUUID> ids, boolean remove) { 840 841 for (CmsUUID id : ids) { 842 updateItem(id, remove); 843 } 844 rebuildMenu(); 845 } 846 847 /** 848 * Updates the column widths.<p> 849 * 850 * The reason this is needed is that the Vaadin table does not support minimum widths for columns, 851 * so expanding columns get squished when most of the horizontal space is used by other columns. 852 * So we try to determine whether the expanded columns would have enough space, and if not, give them a 853 * fixed width. 854 * 855 * @param estimatedSpace the estimated horizontal space available for the table. 856 */ 857 public void updateColumnWidths(int estimatedSpace) { 858 859 Object[] cols = m_fileTable.getVisibleColumns(); 860 List<CmsResourceTableProperty> expandCols = Lists.newArrayList(); 861 int nonExpandWidth = 0; 862 int totalExpandMinWidth = 0; 863 for (Object colObj : cols) { 864 if (m_fileTable.isColumnCollapsed(colObj)) { 865 continue; 866 } 867 CmsResourceTableProperty prop = (CmsResourceTableProperty)colObj; 868 if (0 < m_fileTable.getColumnExpandRatio(prop)) { 869 expandCols.add(prop); 870 totalExpandMinWidth += getAlternativeWidthForExpandingColumns(prop); 871 } else { 872 nonExpandWidth += prop.getColumnWidth(); 873 } 874 } 875 if (estimatedSpace < (totalExpandMinWidth + nonExpandWidth)) { 876 for (CmsResourceTableProperty expandCol : expandCols) { 877 m_fileTable.setColumnWidth(expandCol, getAlternativeWidthForExpandingColumns(expandCol)); 878 } 879 } 880 } 881 882 /** 883 * Updates the file table sorting.<p> 884 */ 885 public void updateSorting() { 886 887 m_fileTable.sort(); 888 } 889 890 /** 891 * Cancels the current edit process.<p> 892 */ 893 void cancelEdit() { 894 895 if (m_editHandler != null) { 896 m_editHandler.cancel(); 897 } 898 clearEdit(); 899 } 900 901 /** 902 * Returns the dialog context provider.<p> 903 * 904 * @return the dialog context provider 905 */ 906 I_CmsContextProvider getContextProvider() { 907 908 return m_contextProvider; 909 } 910 911 /** 912 * Returns the edit item id.<p> 913 * 914 * @return the edit item id 915 */ 916 CmsUUID getEditItemId() { 917 918 return m_editItemId; 919 } 920 921 /** 922 * Returns the edit property id.<p> 923 * 924 * @return the edit property id 925 */ 926 CmsResourceTableProperty getEditProperty() { 927 928 return m_editProperty; 929 } 930 931 /** 932 * Handles the file table item click.<p> 933 * 934 * @param event the click event 935 */ 936 void handleFileItemClick(ItemClickEvent event) { 937 938 if (isEditing()) { 939 stopEdit(); 940 941 } else if (!event.isCtrlKey() && !event.isShiftKey()) { 942 // don't interfere with multi-selection using control key 943 String itemId = (String)event.getItemId(); 944 CmsUUID structureId = getUUIDFromItemID(itemId); 945 boolean openedFolder = false; 946 if (event.getButton().equals(MouseButton.RIGHT)) { 947 handleSelection(itemId); 948 openContextMenu(event); 949 } else { 950 if ((event.getPropertyId() == null) 951 || CmsResourceTableProperty.PROPERTY_TYPE_ICON.equals(event.getPropertyId())) { 952 handleSelection(itemId); 953 openContextMenu(event); 954 } else { 955 if (m_actionColumnProperty.equals(event.getPropertyId())) { 956 Boolean isFolder = (Boolean)event.getItem().getItemProperty( 957 CmsResourceTableProperty.PROPERTY_IS_FOLDER).getValue(); 958 if ((isFolder != null) && isFolder.booleanValue()) { 959 if (m_folderSelectHandler != null) { 960 m_folderSelectHandler.onFolderSelect(structureId); 961 } 962 openedFolder = true; 963 } else { 964 try { 965 CmsObject cms = A_CmsUI.getCmsObject(); 966 CmsResource res = cms.readResource(structureId, CmsResourceFilter.IGNORE_EXPIRATION); 967 m_currentResources = Collections.singletonList(res); 968 I_CmsDialogContext context = m_contextProvider.getDialogContext(); 969 I_CmsDefaultAction action = OpenCms.getWorkplaceAppManager().getDefaultAction( 970 context, 971 m_menuBuilder); 972 if (action != null) { 973 action.executeAction(context); 974 return; 975 } 976 } catch (CmsVfsResourceNotFoundException e) { 977 LOG.info(e.getLocalizedMessage(), e); 978 } catch (CmsException e) { 979 LOG.error(e.getLocalizedMessage(), e); 980 } 981 } 982 } else { 983 I_CmsDialogContext context = m_contextProvider.getDialogContext(); 984 if ((m_currentResources.size() == 1) 985 && m_currentResources.get(0).getStructureId().equals(structureId) 986 && (context instanceof I_CmsEditPropertyContext) 987 && ((I_CmsEditPropertyContext)context).isPropertyEditable(event.getPropertyId())) { 988 989 ((I_CmsEditPropertyContext)context).editProperty(event.getPropertyId()); 990 } 991 } 992 } 993 } 994 // update the item on click to show any available changes 995 if (!openedFolder) { 996 update(Collections.singletonList(structureId), false); 997 } 998 } 999 } 1000 1001 /** 1002 * Rebuilds the context menu.<p> 1003 */ 1004 void rebuildMenu() { 1005 1006 if (!getSelectedIds().isEmpty() && (m_menuBuilder != null)) { 1007 m_menu.removeAllItems(); 1008 m_menuBuilder.buildContextMenu(getContextProvider().getDialogContext(), m_menu); 1009 } 1010 } 1011 1012 /** 1013 * Clears the current edit process.<p> 1014 */ 1015 private void clearEdit() { 1016 1017 m_fileTable.setEditable(false); 1018 if (m_editItemId != null) { 1019 updateItem(m_editItemId, false); 1020 } 1021 m_editItemId = null; 1022 m_editProperty = null; 1023 m_editHandler = null; 1024 updateSorting(); 1025 } 1026 1027 /** 1028 * Gets alternative width for expanding table columns which is used when there is not enough space for 1029 * all visible columns.<p> 1030 * 1031 * @param prop the table property 1032 * @return the alternative column width 1033 */ 1034 private int getAlternativeWidthForExpandingColumns(CmsResourceTableProperty prop) { 1035 1036 if (prop.getId().equals(CmsResourceTableProperty.PROPERTY_RESOURCE_NAME.getId())) { 1037 return 200; 1038 } 1039 if (prop.getId().equals(CmsResourceTableProperty.PROPERTY_TITLE.getId())) { 1040 return 300; 1041 } 1042 if (prop.getId().equals(CmsResourceTableProperty.PROPERTY_NAVIGATION_TEXT.getId())) { 1043 return 200; 1044 } 1045 return 200; 1046 } 1047 1048 /** 1049 * Updates the given item in the file table.<p> 1050 * 1051 * @param itemId the item id 1052 * @param remove true if the item should be removed only 1053 */ 1054 private void updateItem(CmsUUID itemId, boolean remove) { 1055 1056 if (remove) { 1057 String idStr = itemId != null ? itemId.toString() : null; 1058 m_container.removeItem(idStr); 1059 return; 1060 } 1061 1062 CmsObject cms = A_CmsUI.getCmsObject(); 1063 try { 1064 CmsResource resource = cms.readResource(itemId, CmsResourceFilter.ALL); 1065 fillItem(cms, resource, OpenCms.getWorkplaceManager().getWorkplaceLocale(cms)); 1066 1067 } catch (CmsVfsResourceNotFoundException e) { 1068 if (null != itemId) { 1069 m_container.removeItem(itemId.toString()); 1070 } 1071 LOG.debug("Failed to update file table item, removing it from view.", e); 1072 } catch (CmsException e) { 1073 LOG.error(e.getLocalizedMessage(), e); 1074 } 1075 } 1076 1077}