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