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.components; 029 030import static org.opencms.ui.components.CmsResourceTableProperty.PROPERTY_CACHE; 031import static org.opencms.ui.components.CmsResourceTableProperty.PROPERTY_CATEGORIES; 032import static org.opencms.ui.components.CmsResourceTableProperty.PROPERTY_COPYRIGHT; 033import static org.opencms.ui.components.CmsResourceTableProperty.PROPERTY_DATE_CREATED; 034import static org.opencms.ui.components.CmsResourceTableProperty.PROPERTY_DATE_EXPIRED; 035import static org.opencms.ui.components.CmsResourceTableProperty.PROPERTY_DATE_MODIFIED; 036import static org.opencms.ui.components.CmsResourceTableProperty.PROPERTY_DATE_RELEASED; 037import static org.opencms.ui.components.CmsResourceTableProperty.PROPERTY_INSIDE_PROJECT; 038import static org.opencms.ui.components.CmsResourceTableProperty.PROPERTY_INTERNAL_RESOURCE_TYPE; 039import static org.opencms.ui.components.CmsResourceTableProperty.PROPERTY_IN_NAVIGATION; 040import static org.opencms.ui.components.CmsResourceTableProperty.PROPERTY_IS_FOLDER; 041import static org.opencms.ui.components.CmsResourceTableProperty.PROPERTY_NAVIGATION_POSITION; 042import static org.opencms.ui.components.CmsResourceTableProperty.PROPERTY_NAVIGATION_TEXT; 043import static org.opencms.ui.components.CmsResourceTableProperty.PROPERTY_PERMISSIONS; 044import static org.opencms.ui.components.CmsResourceTableProperty.PROPERTY_PROJECT; 045import static org.opencms.ui.components.CmsResourceTableProperty.PROPERTY_RELEASED_NOT_EXPIRED; 046import static org.opencms.ui.components.CmsResourceTableProperty.PROPERTY_RESOURCE_NAME; 047import static org.opencms.ui.components.CmsResourceTableProperty.PROPERTY_RESOURCE_TYPE; 048import static org.opencms.ui.components.CmsResourceTableProperty.PROPERTY_SITE_PATH; 049import static org.opencms.ui.components.CmsResourceTableProperty.PROPERTY_SIZE; 050import static org.opencms.ui.components.CmsResourceTableProperty.PROPERTY_STATE; 051import static org.opencms.ui.components.CmsResourceTableProperty.PROPERTY_STATE_NAME; 052import static org.opencms.ui.components.CmsResourceTableProperty.PROPERTY_TITLE; 053import static org.opencms.ui.components.CmsResourceTableProperty.PROPERTY_TYPE_ICON; 054import static org.opencms.ui.components.CmsResourceTableProperty.PROPERTY_USER_CREATED; 055import static org.opencms.ui.components.CmsResourceTableProperty.PROPERTY_USER_LOCKED; 056import static org.opencms.ui.components.CmsResourceTableProperty.PROPERTY_USER_MODIFIED; 057 058import org.opencms.ade.sitemap.shared.CmsClientSitemapEntry; 059import org.opencms.db.CmsResourceState; 060import org.opencms.file.CmsObject; 061import org.opencms.file.CmsProperty; 062import org.opencms.file.CmsPropertyDefinition; 063import org.opencms.file.CmsResource; 064import org.opencms.file.types.I_CmsResourceType; 065import org.opencms.i18n.CmsEncoder; 066import org.opencms.main.CmsException; 067import org.opencms.main.CmsLog; 068import org.opencms.main.OpenCms; 069import org.opencms.relations.CmsCategory; 070import org.opencms.relations.CmsCategoryService; 071import org.opencms.ui.A_CmsUI; 072import org.opencms.ui.CmsCssIcon; 073import org.opencms.ui.CmsVaadinUtils; 074import org.opencms.ui.util.I_CmsItemSorter; 075import org.opencms.util.CmsColorContrastCalculator; 076import org.opencms.util.CmsPath; 077import org.opencms.util.CmsStringUtil; 078import org.opencms.util.CmsUUID; 079import org.opencms.workplace.CmsWorkplaceMessages; 080import org.opencms.workplace.explorer.CmsResourceUtil; 081 082import java.util.ArrayList; 083import java.util.Arrays; 084import java.util.Collection; 085import java.util.Collections; 086import java.util.Comparator; 087import java.util.HashMap; 088import java.util.LinkedHashSet; 089import java.util.List; 090import java.util.Locale; 091import java.util.Map; 092import java.util.Set; 093import java.util.stream.Collectors; 094 095import org.apache.commons.lang3.ClassUtils; 096import org.apache.commons.logging.Log; 097 098import com.google.common.base.Joiner; 099import com.google.common.collect.ComparisonChain; 100import com.google.common.collect.Lists; 101import com.google.common.collect.Sets; 102import com.vaadin.event.dd.DropHandler; 103import com.vaadin.ui.Component; 104import com.vaadin.ui.Composite; 105import com.vaadin.ui.CustomComponent; 106import com.vaadin.v7.data.Item; 107import com.vaadin.v7.data.Property; 108import com.vaadin.v7.data.util.IndexedContainer; 109import com.vaadin.v7.data.util.converter.Converter; 110import com.vaadin.v7.shared.ui.label.ContentMode; 111import com.vaadin.v7.ui.AbstractSelect.ItemDescriptionGenerator; 112import com.vaadin.v7.ui.Label; 113import com.vaadin.v7.ui.Table; 114import com.vaadin.v7.ui.Table.RowHeaderMode; 115import com.vaadin.v7.ui.Table.TableDragMode; 116 117/** 118 * Generic table for displaying lists of resources.<p> 119 */ 120@SuppressWarnings("deprecation") 121public class CmsResourceTable extends CustomComponent { 122 123 /** 124 * Comparator used for sorting the categories column. 125 */ 126 public static class CategoryComparator implements Comparator<String> { 127 128 /** The collator used. */ 129 private final com.ibm.icu.text.Collator m_collator = com.ibm.icu.text.Collator.getInstance( 130 com.ibm.icu.util.ULocale.ROOT); 131 132 /** 133 * @see java.util.Comparator#compare(java.lang.Object, java.lang.Object) 134 */ 135 @Override 136 public int compare(String c1, String c2) { 137 138 // We want empty strings to come last, but otherwise just use case insensitive string order 139 if ("".equals(c1) && "".equals(c2)) { 140 return 0; 141 } 142 if ("".equals(c1)) { 143 // "" "foo" 144 return 1; 145 } 146 if ("".equals(c2)) { 147 // "foo" "" 148 return -1; 149 } 150 return m_collator.compare(c1, c2); 151 } 152 153 } 154 155 /** 156 * Widget for displaying a resource's categories in a table column. 157 * 158 * <p>For user experience reasons, this widget only loads the category data when needed, which is either when it is attached, 159 * or when other operations (sorting, filtering) need it. 160 */ 161 public static class CategoryLabel extends Composite implements Comparable<CategoryLabel> { 162 163 /** 164 * Holds the data to display for a single category. 165 */ 166 class CategoryItem { 167 168 /** The background color. */ 169 private String m_background; 170 171 /** The title to display. */ 172 private String m_title; 173 174 /** The category path. */ 175 private String m_categoryPath; 176 177 /** 178 * Creates a new instance. 179 * 180 * @param title the title 181 * @param background the background color 182 */ 183 public CategoryItem(String categoryPath, String title, String background) { 184 185 super(); 186 m_categoryPath = categoryPath; 187 m_title = title; 188 m_background = background; 189 } 190 191 /** 192 * Gets the background color. 193 * 194 * @return the background color 195 */ 196 public String getBackground() { 197 198 return m_background; 199 } 200 201 public String getCategoryPath() { 202 203 return m_categoryPath; 204 } 205 206 /** 207 * Gets the title. 208 * 209 * @return the title 210 */ 211 public String getTitle() { 212 213 return m_title; 214 } 215 } 216 217 /** Serial version id. */ 218 private static final long serialVersionUID = 1L; 219 220 /** True if the widget has been initialized. */ 221 private boolean m_initialized; 222 223 /** The label used to display the categories. */ 224 private Label m_label = new Label(); 225 226 /** The locale. */ 227 private Locale m_locale; 228 229 /** The resource utility wrapper. */ 230 private CmsResourceUtil m_resUtil; 231 232 /** The categories value as a string, for tooltips, sorting and filtering. */ 233 private String m_value = ""; 234 235 /** 236 * Creates a new instance. 237 * 238 * @param resUtil the resource utility wrapper 239 * @param locale the workplace locale 240 */ 241 public CategoryLabel(CmsResourceUtil resUtil, Locale locale) { 242 243 m_locale = locale; 244 m_resUtil = resUtil; 245 setCompositionRoot(m_label); 246 addStyleName("o-category-label"); 247 } 248 249 /** 250 * @see com.vaadin.ui.AbstractComponent#attach() 251 */ 252 @Override 253 public void attach() { 254 255 // Attach is only called when the user scrolls near the row in which this widget is located. 256 init(); 257 super.attach(); 258 } 259 260 /** 261 * @see java.lang.Comparable#compareTo(java.lang.Object) 262 */ 263 @Override 264 public int compareTo(CategoryLabel o) { 265 266 // getValue() calls init() 267 return CATEGORY_COMPARATOR.compare(getValue(), o.getValue()); 268 269 } 270 271 /** 272 * Gets the categories as a string (for tooltips, sorting and filtering). 273 * 274 * @return the category values 275 */ 276 public String getValue() { 277 278 init(); 279 return m_value; 280 } 281 282 /** 283 * Needed for filtering. 284 * 285 * @see java.lang.Object#toString() 286 */ 287 @Override 288 public String toString() { 289 290 // getValue() calls init 291 return getValue(); 292 } 293 294 /** 295 * Initializes the category data and the actual widget, unless it has already been initialized. 296 */ 297 protected synchronized void init() { 298 299 if (!m_initialized) { 300 // We definitely don't want to repeatedly try to initialize the widget, since performance is the whole point. 301 // Even failure should count as being initialized. So we might as well set m_initialized right here, at the start. 302 m_initialized = true; 303 try { 304 CmsObject cms = m_resUtil.getCms(); 305 CmsCategoryService catService = CmsCategoryService.getInstance(); 306 List<CmsCategory> categories = catService.readResourceCategories(cms, m_resUtil.getResource()); 307 categories = catService.localizeCategories(cms, categories, m_locale); 308 309 Map<CmsPath, CmsCategory> categoriesByPath = categories.stream().collect( 310 Collectors.toMap(cat -> new CmsPath(cat.getPath()), cat -> cat, (a, b) -> b)); 311 Set<CmsPath> parents = categories.stream().map( 312 cat -> CmsResource.getParentFolder(cat.getPath())).filter(path -> path != null).map( 313 path -> new CmsPath(path)).collect(Collectors.toSet()); 314 315 boolean removeParents = OpenCms.getWorkplaceManager().isExplorerCategoriesLeavesOnly(); 316 boolean fullPath = OpenCms.getWorkplaceManager().isExplorerCategoriesWithPath(); 317 List<CmsCategory> categoriesToDisplay = new ArrayList<>(categories); 318 if (removeParents) { 319 categoriesToDisplay.removeIf(cat -> parents.contains(new CmsPath(cat.getPath()))); 320 } 321 322 List<CategoryItem> items = categoriesToDisplay.stream().map( 323 cat -> new CategoryItem( 324 cat.getPath(), 325 fullPath ? getCompositeCategoryTitle(categoriesByPath, cat) : cat.getTitle(), 326 cat.getBackground())).collect(Collectors.toList()); 327 Comparator<CategoryItem> comparator = ( 328 a, 329 b) -> ComparisonChain.start().compare(a.getCategoryPath(), b.getCategoryPath()).result(); 330 Collections.sort(items, comparator); 331 // Comma-separated list of titles, for tooltip, sorting and filtering 332 m_value = items.stream().map(item -> item.getTitle()).collect(Collectors.joining(", ")); 333 m_label.setDescription(m_value); 334 335 // Assemble HTML based on the titles and fill the widget with it. 336 String html = items.stream().flatMap(item -> { 337 String colorStyle = ""; 338 String bg = item.getBackground(); 339 if (!CmsStringUtil.isEmptyOrWhitespaceOnly(bg)) { 340 bg = bg.trim(); 341 String fgSuffix = ""; 342 try { 343 fgSuffix = " color: " + m_contrastCalculator.getForeground(bg) + " !important;"; 344 } catch (Exception e) { 345 LOG.error(e.getLocalizedMessage(), e); 346 } 347 colorStyle = " style='background-color: " + bg + " !important; " + fgSuffix + "' "; 348 } 349 return Arrays.asList( 350 "<div class='o-category-label-category' ", 351 colorStyle, 352 ">", 353 CmsEncoder.escapeXml(item.getTitle()), 354 "</div>").stream(); 355 }).collect(Collectors.joining("")); 356 357 m_label.setContentMode(ContentMode.HTML); 358 m_label.setValue(html); 359 360 } catch (Exception e) { 361 LOG.error(e.getLocalizedMessage(), e); 362 } 363 } 364 } 365 } 366 367 /** 368 * Helper class for easily configuring a set of columns to display, together with their visibility / collapsed status.<p> 369 */ 370 public class ColumnBuilder { 371 372 /** The column entries configured so far. */ 373 private List<ColumnEntry> m_columnEntries = Lists.newArrayList(); 374 375 /** 376 * Sets up the table and its container using the columns configured so far.<p> 377 */ 378 public void buildColumns() { 379 380 Set<CmsResourceTableProperty> visible = new LinkedHashSet<CmsResourceTableProperty>(); 381 Set<CmsResourceTableProperty> collapsed = new LinkedHashSet<CmsResourceTableProperty>(); 382 for (ColumnEntry entry : m_columnEntries) { 383 CmsResourceTableProperty prop = entry.getColumn(); 384 m_container.addContainerProperty(prop, prop.getColumnType(), prop.getDefaultValue()); 385 if (entry.isCollapsed()) { 386 collapsed.add(entry.getColumn()); 387 } 388 if (entry.isVisible()) { 389 visible.add(entry.getColumn()); 390 } 391 } 392 m_fileTable.setVisibleColumns(visible.toArray(new Object[0])); 393 Object[] collapsedColumnsArray = collapsed.toArray(new Object[0]); 394 setCollapsedColumns(collapsedColumnsArray); 395 for (CmsResourceTableProperty visibleProp : visible) { 396 String headerKey = visibleProp.getHeaderKey(); 397 if (!CmsStringUtil.isEmptyOrWhitespaceOnly(headerKey)) { 398 m_fileTable.setColumnHeader(visibleProp, CmsVaadinUtils.getMessageText(headerKey)); 399 } else { 400 m_fileTable.setColumnHeader(visibleProp, ""); 401 } 402 m_fileTable.setColumnCollapsible(visibleProp, visibleProp.isCollapsible()); 403 if (visibleProp.getColumnWidth() > 0) { 404 m_fileTable.setColumnWidth(visibleProp, visibleProp.getColumnWidth()); 405 } 406 if (visibleProp.getExpandRatio() > 0) { 407 m_fileTable.setColumnExpandRatio(visibleProp, visibleProp.getExpandRatio()); 408 } 409 if (visibleProp.getConverter() != null) { 410 m_fileTable.setConverter(visibleProp, visibleProp.getConverter()); 411 } 412 } 413 } 414 415 /** 416 * Adds a new column.<p> 417 * 418 * @param prop the column 419 * 420 * @return this object 421 */ 422 public ColumnBuilder column(CmsResourceTableProperty prop) { 423 424 column(prop, 0); 425 return this; 426 } 427 428 /** 429 * Adds a new column.<p< 430 * 431 * @param prop the column 432 * @param flags the flags for the column 433 * 434 * @return this object 435 */ 436 public ColumnBuilder column(CmsResourceTableProperty prop, int flags) { 437 438 ColumnEntry entry = new ColumnEntry(); 439 entry.setColumn(prop); 440 entry.setFlags(flags); 441 m_columnEntries.add(entry); 442 return this; 443 } 444 } 445 446 /** 447 * Contains the data for the given column, along with some flags to control visibility/collapsed status.<p> 448 * 449 */ 450 public static class ColumnEntry { 451 452 /** The column. */ 453 private CmsResourceTableProperty m_column; 454 455 /** The flags. */ 456 private int m_flags; 457 458 /** 459 * Returns the column.<p> 460 * 461 * @return the column 462 */ 463 public CmsResourceTableProperty getColumn() { 464 465 return m_column; 466 } 467 468 /** 469 * Returns the collapsed.<p> 470 * 471 * @return the collapsed 472 */ 473 public boolean isCollapsed() { 474 475 return (m_flags & COLLAPSED) != 0; 476 } 477 478 /** 479 * Returns the visible.<p> 480 * 481 * @return the visible 482 */ 483 public boolean isVisible() { 484 485 return 0 == (m_flags & INVISIBLE); 486 } 487 488 /** 489 * Sets the column.<p> 490 * 491 * @param column the column to set 492 */ 493 public void setColumn(CmsResourceTableProperty column) { 494 495 m_column = column; 496 } 497 498 /** 499 * Sets the flags.<p> 500 * 501 * @param flags the flags to set 502 */ 503 public void setFlags(int flags) { 504 505 m_flags = flags; 506 } 507 508 @Override 509 public String toString() { 510 511 return "ColumnEntry[" + getColumn().getId() + "," + m_flags + "]"; 512 } 513 514 } 515 516 /** 517 * Interfaces for getting notified of column visibility/sort setting changes. 518 */ 519 public interface ColumnSettingChangeHandler { 520 521 /** 522 * Called when column visibility or sorting is changed by the user. 523 */ 524 void onColumnSettingsChanged(); 525 } 526 527 /** 528 * Default description generator for table entries. 529 */ 530 public static class DefaultItemDescriptionGenerator implements ItemDescriptionGenerator { 531 532 /** Serial version id.*/ 533 private static final long serialVersionUID = 1L; 534 535 /** 536 * @see com.vaadin.v7.ui.AbstractSelect.ItemDescriptionGenerator#generateDescription(com.vaadin.ui.Component, java.lang.Object, java.lang.Object) 537 */ 538 @SuppressWarnings("synthetic-access") 539 public String generateDescription(Component source, Object itemId, Object propertyId) { 540 541 Table table = (Table)source; 542 try { 543 if ((propertyId != null) && (itemId != null)) { 544 Property prop = table.getContainerDataSource().getItem(itemId).getItemProperty(propertyId); 545 Converter<String, Object> converter = table.getConverter(propertyId); 546 if (CmsResourceTableProperty.PROPERTY_RESOURCE_NAME == propertyId) { 547 // when working with the explorer, tool tips constantly showing up when 548 // hovering over the file name accidentally seems more annoying than useful 549 return null; 550 } else if ((converter != null) && String.class.equals(converter.getPresentationType())) { 551 return converter.convertToPresentation( 552 prop.getValue(), 553 String.class, 554 A_CmsUI.get().getLocale()); 555 } else if (String.class.equals(prop.getType()) || ClassUtils.isPrimitiveOrWrapper(prop.getType())) { 556 Object value = prop.getValue(); 557 if (value != null) { 558 return CmsEncoder.escapeXml("" + value); 559 } 560 } 561 } 562 } catch (Exception e) { 563 LOG.warn(e.getLocalizedMessage(), e); 564 } 565 return null; 566 } 567 } 568 569 /** 570 * Provides item property values for additional table columns.<p> 571 */ 572 public static interface I_ResourcePropertyProvider { 573 574 /** 575 * Adds the property values to the given item.<p> 576 * 577 * @param resourceItem the resource item 578 * @param cms the cms context 579 * @param resource the resource 580 * @param locale the workplace locale 581 */ 582 void addItemProperties(Item resourceItem, CmsObject cms, CmsResource resource, Locale locale); 583 } 584 585 /** 586 * Extending the indexed container to make the number of un-filtered items available.<p> 587 */ 588 protected static class ItemContainer extends IndexedContainer { 589 590 /** The serial version id. */ 591 private static final long serialVersionUID = -2033722658471550506L; 592 593 /** 594 * @see com.vaadin.v7.data.util.IndexedContainer#getSortableContainerPropertyIds() 595 */ 596 @Override 597 public Collection<?> getSortableContainerPropertyIds() { 598 599 if (getItemSorter() instanceof I_CmsItemSorter) { 600 return ((I_CmsItemSorter)getItemSorter()).getSortableContainerPropertyIds(this); 601 } else { 602 return super.getSortableContainerPropertyIds(); 603 } 604 } 605 606 /** 607 * Returns the number of items in the container, not considering any filters.<p> 608 * 609 * @return the number of items 610 */ 611 protected int getItemCount() { 612 613 return getAllItemIds().size(); 614 } 615 } 616 617 /** Flag to mark columns as initially collapsed.*/ 618 public static final int COLLAPSED = 1; 619 620 /** Flag to mark columns as invisible. */ 621 public static final int INVISIBLE = 2; 622 623 /** Static instance of the comparator used for categories. */ 624 private static final CategoryComparator CATEGORY_COMPARATOR = new CategoryComparator(); 625 626 /** The logger instance for this class. */ 627 private static final Log LOG = CmsLog.getLog(CmsResourceTable.class); 628 629 /** Used for calculating foreground colors for categories. */ 630 private static final CmsColorContrastCalculator m_contrastCalculator = new CmsColorContrastCalculator(); 631 632 /** Serial version id. */ 633 private static final long serialVersionUID = 1L; 634 635 /** The resource data container. */ 636 protected ItemContainer m_container = new ItemContainer(); 637 638 /** The table used to display the resource data. */ 639 protected Table m_fileTable = new Table() { 640 641 /** If greater than 0, we are in a changeVariables call - which means that column changes probably are the direct result of user interaction with the table rather than automatic/programmatic changes. */ 642 private long m_changingVariables; 643 644 /** 645 * @see com.vaadin.v7.ui.Table#changeVariables(java.lang.Object, java.util.Map) 646 */ 647 public void changeVariables(Object source, java.util.Map<String, Object> variables) { 648 649 m_changingVariables += 1; 650 try { 651 super.changeVariables(source, variables); 652 } finally { 653 m_changingVariables -= 1; 654 } 655 } 656 657 /** 658 * @see com.vaadin.v7.ui.Table#setColumnCollapsed(java.lang.Object, boolean) 659 */ 660 public void setColumnCollapsed(Object propertyId, boolean collapsed) throws IllegalStateException { 661 662 super.setColumnCollapsed(propertyId, collapsed); 663 if (m_changingVariables > 0) { 664 if (m_columnSettingChangeHandler != null) { 665 m_columnSettingChangeHandler.onColumnSettingsChanged(); 666 } 667 } 668 669 }; 670 671 /** 672 * @see com.vaadin.v7.ui.Table#sort(java.lang.Object[], boolean[]) 673 */ 674 public void sort(Object[] propertyId, boolean[] ascending) throws UnsupportedOperationException { 675 676 super.sort(propertyId, ascending); 677 if (m_changingVariables > 0) { 678 if (m_columnSettingChangeHandler != null) { 679 m_columnSettingChangeHandler.onColumnSettingsChanged(); 680 } 681 } 682 } 683 }; 684 685 /** Property provider for additional columns. */ 686 protected List<I_ResourcePropertyProvider> m_propertyProviders; 687 688 /** Handles column setting changes. */ 689 private ColumnSettingChangeHandler m_columnSettingChangeHandler; 690 691 /** 692 * Creates a new instance.<p> 693 * 694 * This constructor does *not* set up the columns of the table; use the ColumnBuilder inner class for this. 695 */ 696 public CmsResourceTable() { 697 698 m_propertyProviders = new ArrayList<I_ResourcePropertyProvider>(); 699 m_fileTable.setContainerDataSource(m_container); 700 setCompositionRoot(m_fileTable); 701 m_fileTable.setRowHeaderMode(RowHeaderMode.HIDDEN); 702 m_fileTable.setItemDescriptionGenerator(new DefaultItemDescriptionGenerator()); 703 } 704 705 /** 706 * Static helper method to initialize the 'standard' properties of a data item from a given resource.<p> 707 * @param resourceItem the resource item to fill 708 * @param cms the CMS context 709 * @param resource the resource 710 * @param locale the locale 711 */ 712 public static void fillItemDefault(Item resourceItem, CmsObject cms, CmsResource resource, Locale locale) { 713 714 if (resource == null) { 715 LOG.error("Error rendering item for 'null' resource"); 716 return; 717 } 718 719 if (resourceItem == null) { 720 LOG.error("Error rendering 'null' item for resource " + resource.getRootPath()); 721 return; 722 } 723 if (cms == null) { 724 cms = A_CmsUI.getCmsObject(); 725 LOG.warn("CmsObject was 'null', using thread local CmsObject"); 726 } 727 CmsResourceUtil resUtil = new CmsResourceUtil(cms, resource); 728 Map<String, CmsProperty> resourceProps = null; 729 try { 730 List<CmsProperty> props = cms.readPropertyObjects(resource, false); 731 resourceProps = new HashMap<String, CmsProperty>(); 732 for (CmsProperty prop : props) { 733 resourceProps.put(prop.getName(), prop); 734 } 735 } catch (CmsException e1) { 736 LOG.debug("Unable to read properties for resource '" + resource.getRootPath() + "'.", e1); 737 } 738 I_CmsResourceType type = OpenCms.getResourceManager().getResourceType(resource); 739 if (resourceItem.getItemProperty(PROPERTY_TYPE_ICON) != null) { 740 resourceItem.getItemProperty(PROPERTY_TYPE_ICON).setValue( 741 new CmsResourceIcon(resUtil, resource.getState(), true)); 742 } 743 744 if (resourceItem.getItemProperty(PROPERTY_PROJECT) != null) { 745 Label projectFlag = null; 746 switch (resUtil.getProjectState().getMode()) { 747 case 1: 748 projectFlag = new Label( 749 new CmsCssIcon(OpenCmsTheme.ICON_PROJECT_CURRENT).getHtml(resUtil.getLockedInProjectName()), 750 ContentMode.HTML); 751 break; 752 case 2: 753 projectFlag = new Label( 754 new CmsCssIcon(OpenCmsTheme.ICON_PROJECT_OTHER).getHtml(resUtil.getLockedInProjectName()), 755 ContentMode.HTML); 756 break; 757 case 5: 758 projectFlag = new Label( 759 new CmsCssIcon(OpenCmsTheme.ICON_PUBLISH).getHtml(resUtil.getLockedInProjectName()), 760 ContentMode.HTML); 761 break; 762 default: 763 } 764 resourceItem.getItemProperty(PROPERTY_PROJECT).setValue(projectFlag); 765 } 766 767 if (resourceItem.getItemProperty(PROPERTY_INSIDE_PROJECT) != null) { 768 resourceItem.getItemProperty(PROPERTY_INSIDE_PROJECT).setValue(Boolean.valueOf(resUtil.isInsideProject())); 769 } 770 771 if (resourceItem.getItemProperty(PROPERTY_RELEASED_NOT_EXPIRED) != null) { 772 resourceItem.getItemProperty(PROPERTY_RELEASED_NOT_EXPIRED).setValue( 773 Boolean.valueOf(resUtil.isReleasedAndNotExpired())); 774 } 775 776 if (resourceItem.getItemProperty(PROPERTY_RESOURCE_NAME) != null) { 777 resourceItem.getItemProperty(PROPERTY_RESOURCE_NAME).setValue(resource.getName()); 778 } 779 780 if (resourceItem.getItemProperty(PROPERTY_SITE_PATH) != null) { 781 resourceItem.getItemProperty(PROPERTY_SITE_PATH).setValue(cms.getSitePath(resource)); 782 } 783 784 if ((resourceItem.getItemProperty(PROPERTY_TITLE) != null) && (resourceProps != null)) { 785 resourceItem.getItemProperty(PROPERTY_TITLE).setValue( 786 resourceProps.containsKey(CmsPropertyDefinition.PROPERTY_TITLE) 787 ? resourceProps.get(CmsPropertyDefinition.PROPERTY_TITLE).getValue() 788 : ""); 789 } 790 boolean inNavigation = false; 791 if ((resourceItem.getItemProperty(PROPERTY_NAVIGATION_TEXT) != null) && (resourceProps != null)) { 792 resourceItem.getItemProperty(PROPERTY_NAVIGATION_TEXT).setValue( 793 resourceProps.containsKey(CmsPropertyDefinition.PROPERTY_NAVTEXT) 794 ? resourceProps.get(CmsPropertyDefinition.PROPERTY_NAVTEXT).getValue() 795 : ""); 796 inNavigation = resourceProps.containsKey(CmsPropertyDefinition.PROPERTY_NAVTEXT); 797 } 798 799 if ((resourceItem.getItemProperty(PROPERTY_NAVIGATION_POSITION) != null) && (resourceProps != null)) { 800 try { 801 Float navPos = resourceProps.containsKey(CmsPropertyDefinition.PROPERTY_NAVPOS) 802 ? Float.valueOf(resourceProps.get(CmsPropertyDefinition.PROPERTY_NAVPOS).getValue()) 803 : (inNavigation ? Float.valueOf(Float.MAX_VALUE) : null); 804 resourceItem.getItemProperty(PROPERTY_NAVIGATION_POSITION).setValue(navPos); 805 inNavigation = navPos != null; 806 } catch (Exception e) { 807 LOG.debug("Error evaluating navPos property", e); 808 } 809 } 810 811 if (resourceItem.getItemProperty(PROPERTY_IN_NAVIGATION) != null) { 812 if (inNavigation 813 && (resourceProps != null) 814 && resourceProps.containsKey(CmsPropertyDefinition.PROPERTY_NAVINFO) 815 && CmsClientSitemapEntry.HIDDEN_NAVIGATION_ENTRY.equals( 816 resourceProps.get(CmsPropertyDefinition.PROPERTY_NAVINFO).getValue())) { 817 inNavigation = false; 818 } 819 resourceItem.getItemProperty(PROPERTY_IN_NAVIGATION).setValue(Boolean.valueOf(inNavigation)); 820 } 821 822 if ((resourceItem.getItemProperty(PROPERTY_COPYRIGHT) != null) && (resourceProps != null)) { 823 resourceItem.getItemProperty(PROPERTY_COPYRIGHT).setValue( 824 resourceProps.containsKey(CmsPropertyDefinition.PROPERTY_COPYRIGHT) 825 ? resourceProps.get(CmsPropertyDefinition.PROPERTY_COPYRIGHT).getValue() 826 : ""); 827 } 828 829 if ((resourceItem.getItemProperty(PROPERTY_CACHE) != null) && (resourceProps != null)) { 830 resourceItem.getItemProperty(PROPERTY_CACHE).setValue( 831 resourceProps.containsKey(CmsPropertyDefinition.PROPERTY_CACHE) 832 ? resourceProps.get(CmsPropertyDefinition.PROPERTY_CACHE).getValue() 833 : ""); 834 } 835 836 if (resourceItem.getItemProperty(PROPERTY_RESOURCE_TYPE) != null) { 837 resourceItem.getItemProperty(PROPERTY_RESOURCE_TYPE).setValue( 838 CmsWorkplaceMessages.getResourceTypeName(locale, type.getTypeName())); 839 } 840 841 if (resourceItem.getItemProperty(PROPERTY_INTERNAL_RESOURCE_TYPE) != null) { 842 resourceItem.getItemProperty(PROPERTY_INTERNAL_RESOURCE_TYPE).setValue(type.getTypeName()); 843 } 844 845 if (resourceItem.getItemProperty(PROPERTY_IS_FOLDER) != null) { 846 resourceItem.getItemProperty(PROPERTY_IS_FOLDER).setValue(Boolean.valueOf(resource.isFolder())); 847 } 848 849 if (resourceItem.getItemProperty(PROPERTY_SIZE) != null) { 850 if (resource.isFile()) { 851 resourceItem.getItemProperty(PROPERTY_SIZE).setValue(Integer.valueOf(resource.getLength())); 852 } 853 } 854 855 if (resourceItem.getItemProperty(PROPERTY_PERMISSIONS) != null) { 856 resourceItem.getItemProperty(PROPERTY_PERMISSIONS).setValue(resUtil.getPermissionString()); 857 } 858 859 if (resourceItem.getItemProperty(PROPERTY_DATE_MODIFIED) != null) { 860 resourceItem.getItemProperty(PROPERTY_DATE_MODIFIED).setValue(Long.valueOf(resource.getDateLastModified())); 861 } 862 863 if (resourceItem.getItemProperty(PROPERTY_USER_MODIFIED) != null) { 864 resourceItem.getItemProperty(PROPERTY_USER_MODIFIED).setValue(resUtil.getUserLastModified()); 865 } 866 867 if (resourceItem.getItemProperty(PROPERTY_DATE_CREATED) != null) { 868 resourceItem.getItemProperty(PROPERTY_DATE_CREATED).setValue(Long.valueOf(resource.getDateCreated())); 869 } 870 871 if (resourceItem.getItemProperty(PROPERTY_USER_CREATED) != null) { 872 resourceItem.getItemProperty(PROPERTY_USER_CREATED).setValue(resUtil.getUserCreated()); 873 } 874 875 if (resourceItem.getItemProperty(PROPERTY_DATE_RELEASED) != null) { 876 long release = resource.getDateReleased(); 877 if (release != CmsResource.DATE_RELEASED_DEFAULT) { 878 resourceItem.getItemProperty(PROPERTY_DATE_RELEASED).setValue(Long.valueOf(release)); 879 } else { 880 resourceItem.getItemProperty(PROPERTY_DATE_RELEASED).setValue(null); 881 } 882 } 883 884 if (resourceItem.getItemProperty(PROPERTY_DATE_EXPIRED) != null) { 885 long expire = resource.getDateExpired(); 886 if (expire != CmsResource.DATE_EXPIRED_DEFAULT) { 887 resourceItem.getItemProperty(PROPERTY_DATE_EXPIRED).setValue(Long.valueOf(expire)); 888 } else { 889 resourceItem.getItemProperty(PROPERTY_DATE_EXPIRED).setValue(null); 890 } 891 } 892 893 if (resourceItem.getItemProperty(PROPERTY_STATE_NAME) != null) { 894 resourceItem.getItemProperty(PROPERTY_STATE_NAME).setValue(resUtil.getStateName()); 895 } 896 897 if (resourceItem.getItemProperty(PROPERTY_STATE) != null) { 898 resourceItem.getItemProperty(PROPERTY_STATE).setValue(resource.getState()); 899 } 900 901 if (resourceItem.getItemProperty(PROPERTY_USER_LOCKED) != null) { 902 resourceItem.getItemProperty(PROPERTY_USER_LOCKED).setValue(resUtil.getLockedByName()); 903 } 904 905 if (resourceItem.getItemProperty(PROPERTY_CATEGORIES) != null) { 906 CategoryLabel l = new CategoryLabel(resUtil, locale); 907 resourceItem.getItemProperty(PROPERTY_CATEGORIES).setValue(l); 908 } 909 } 910 911 /** 912 * Gets the CSS style name for the given resource state.<p> 913 * 914 * @param state the resource state 915 * @return the CSS style name 916 */ 917 public static String getStateStyle(CmsResourceState state) { 918 919 String stateStyle = ""; 920 if (state != null) { 921 if (state.isDeleted()) { 922 stateStyle = OpenCmsTheme.STATE_DELETED; 923 } else if (state.isNew()) { 924 stateStyle = OpenCmsTheme.STATE_NEW; 925 } else if (state.isChanged()) { 926 stateStyle = OpenCmsTheme.STATE_CHANGED; 927 } 928 } 929 return stateStyle; 930 } 931 932 /** 933 * Assembles the full title of a category from the title of its parents. 934 * 935 * @param categories the map of applicable categories by path 936 * @param category the category for which to build the title 937 * 938 * @return the combined title 939 */ 940 private static String getCompositeCategoryTitle(Map<CmsPath, CmsCategory> categories, CmsCategory category) { 941 942 ArrayList<String> components = new ArrayList<>(); 943 CmsCategory currentCategory = category; 944 while (currentCategory != null) { 945 components.add(currentCategory.getTitle()); 946 CmsPath parentPath = new CmsPath(CmsResource.getParentFolder(currentCategory.getPath())); 947 currentCategory = categories.get(parentPath); 948 } 949 // The while loop iterated "up" the category tree, we want the category titles in "down" direction 950 Collections.reverse(components); 951 return Joiner.on(" / ").join(components); 952 } 953 954 /** 955 * Adds a property provider.<p> 956 * 957 * @param provider the property provider 958 */ 959 public void addPropertyProvider(I_ResourcePropertyProvider provider) { 960 961 m_propertyProviders.add(provider); 962 } 963 964 /** 965 * Clears the value selection.<p> 966 */ 967 public void clearSelection() { 968 969 m_fileTable.setValue(Collections.emptySet()); 970 } 971 972 /** 973 * Fills the resource table.<p> 974 * 975 * @param cms the current CMS context 976 * @param resources the resources which should be displayed in the table 977 */ 978 public void fillTable(CmsObject cms, List<CmsResource> resources) { 979 980 fillTable(cms, resources, true); 981 } 982 983 /** 984 * Fills the resource table.<p> 985 * 986 * @param cms the current CMS context 987 * @param resources the resources which should be displayed in the table 988 * @param clearFilter <code>true</code> to clear the search filter 989 */ 990 public void fillTable(CmsObject cms, List<CmsResource> resources, boolean clearFilter) { 991 992 fillTable(cms, resources, clearFilter, true); 993 } 994 995 /** 996 * Fills the resource table.<p> 997 * 998 * @param cms the current CMS context 999 * @param resources the resources which should be displayed in the table 1000 * @param clearFilter <code>true</code> to clear the search filter 1001 * @param sort <code>true</code> to sort the table entries 1002 */ 1003 public void fillTable(CmsObject cms, List<CmsResource> resources, boolean clearFilter, boolean sort) { 1004 1005 fillTable(cms, resources, clearFilter, true, false); 1006 } 1007 1008 /** 1009 * Fills the resource table.<p> 1010 * 1011 * @param cms the current CMS context 1012 * @param resources the resources which should be displayed in the table 1013 * @param clearFilter <code>true</code> to clear the search filter 1014 * @param sort <code>true</code> to sort the table entries 1015 * @param distinctResources whether to only show distinct resources 1016 */ 1017 public void fillTable( 1018 CmsObject cms, 1019 List<CmsResource> resources, 1020 boolean clearFilter, 1021 boolean sort, 1022 boolean distinctResources) { 1023 1024 Locale wpLocale = OpenCms.getWorkplaceManager().getWorkplaceLocale(cms); 1025 m_container.removeAllItems(); 1026 if (clearFilter) { 1027 m_container.removeAllContainerFilters(); 1028 } 1029 if (distinctResources) { 1030 Map<String, String> ids = new HashMap<String, String>(); 1031 for (CmsResource resource : resources) { 1032 String id = resource.getStructureId().getStringValue(); 1033 if (!ids.containsKey(id)) { 1034 fillItem(cms, resource, wpLocale); 1035 ids.put(id, ""); 1036 } 1037 } 1038 } else { 1039 for (CmsResource resource : resources) { 1040 fillItem(cms, resource, wpLocale); 1041 } 1042 } 1043 if (sort) { 1044 m_fileTable.sort(); 1045 } 1046 clearSelection(); 1047 } 1048 1049 /** 1050 * Gets structure ids of resources for current folder in current sort order.<p> 1051 * 1052 * @return the structure ids of the current folder contents 1053 */ 1054 @SuppressWarnings("unchecked") 1055 public List<CmsUUID> getAllIds() { 1056 1057 return itemIdsToUUIDs((List<String>)m_fileTable.getContainerDataSource().getItemIds()); 1058 } 1059 1060 /** 1061 * Returns the number of currently visible items.<p> 1062 * 1063 * @return the number of currentliy visible items 1064 */ 1065 public int getItemCount() { 1066 1067 return m_container.getItemCount(); 1068 } 1069 1070 /** 1071 * Returns the structure id to the given string item id.<p> 1072 * 1073 * @param itemId the item id 1074 * 1075 * @return the structure id 1076 */ 1077 public CmsUUID getUUIDFromItemID(String itemId) { 1078 1079 return new CmsUUID(itemId); 1080 } 1081 1082 /** 1083 * Returns if the column with the given property id is visible and not collapsed.<p> 1084 * 1085 * @param propertyId the property id 1086 * 1087 * @return <code>true</code> if the column is visible 1088 */ 1089 public boolean isColumnVisible(CmsResourceTableProperty propertyId) { 1090 1091 return Arrays.asList(m_fileTable.getVisibleColumns()).contains(propertyId) 1092 && !m_fileTable.isColumnCollapsed(propertyId); 1093 } 1094 1095 /** 1096 * Removes a property provider.<p> 1097 * 1098 * @param provider the provider to remove 1099 */ 1100 public void removePropertyProvider(I_ResourcePropertyProvider provider) { 1101 1102 m_propertyProviders.remove(provider); 1103 } 1104 1105 /** 1106 * Selects all resources.<p> 1107 */ 1108 public void selectAll() { 1109 1110 m_fileTable.setValue(m_fileTable.getItemIds()); 1111 } 1112 1113 /** 1114 * Sets the list of collapsed columns.<p> 1115 * 1116 * @param collapsedColumns the list of collapsed columns 1117 */ 1118 public void setCollapsedColumns(Object... collapsedColumns) { 1119 1120 Set<Object> collapsedSet = Sets.newHashSet(); 1121 for (Object collapsed : collapsedColumns) { 1122 collapsedSet.add(collapsed); 1123 } 1124 for (Object key : m_fileTable.getVisibleColumns()) { 1125 boolean isCollapsed = collapsedSet.contains(key); 1126 internalSetColumnCollapsed(key, isCollapsed); 1127 } 1128 } 1129 1130 /** 1131 * Sets the column setting change handler. 1132 * @param columnSettingChangeHandler the handler instance 1133 */ 1134 public void setColumnSettingChangeHandler(ColumnSettingChangeHandler columnSettingChangeHandler) { 1135 1136 m_columnSettingChangeHandler = columnSettingChangeHandler; 1137 } 1138 1139 /** 1140 * Sets the table drag mode.<p> 1141 * 1142 * @param dragMode the drag mode 1143 */ 1144 public void setDragMode(TableDragMode dragMode) { 1145 1146 m_fileTable.setDragMode(dragMode); 1147 } 1148 1149 /** 1150 * Sets the table drop handler.<p> 1151 * 1152 * @param handler the drop handler 1153 */ 1154 public void setDropHandler(DropHandler handler) { 1155 1156 m_fileTable.setDropHandler(handler); 1157 } 1158 1159 /** 1160 * Selects an given object in table.<p> 1161 * 1162 * @param o object to be selected. 1163 */ 1164 public void setValue(Set<String> o) { 1165 1166 m_fileTable.setValue(o); 1167 } 1168 1169 /** 1170 * Fills the file item data.<p> 1171 * 1172 * @param cms the cms context 1173 * @param resource the resource 1174 * @param locale the workplace locale 1175 */ 1176 protected void fillItem(CmsObject cms, CmsResource resource, Locale locale) { 1177 1178 Item resourceItem = m_container.getItem(resource.getStructureId().toString()); 1179 if (resourceItem == null) { 1180 resourceItem = m_container.addItem(resource.getStructureId().toString()); 1181 } 1182 fillItemDefault(resourceItem, cms, resource, locale); 1183 for (I_ResourcePropertyProvider provider : m_propertyProviders) { 1184 provider.addItemProperties(resourceItem, cms, resource, locale); 1185 } 1186 } 1187 1188 protected void internalSetColumnCollapsed(Object key, boolean collapsed) { 1189 1190 m_fileTable.setColumnCollapsed(key, collapsed); 1191 } 1192 1193 /** 1194 * Transforms the given item ids into UUIDs.<p> 1195 * 1196 * @param itemIds the item ids 1197 * 1198 * @return the UUIDs 1199 */ 1200 protected List<CmsUUID> itemIdsToUUIDs(Collection<String> itemIds) { 1201 1202 List<CmsUUID> ids = new ArrayList<CmsUUID>(); 1203 for (String itemId : itemIds) { 1204 if (itemId != null) { 1205 ids.add(getUUIDFromItemID(itemId)); 1206 } 1207 } 1208 return ids; 1209 } 1210}