001/* 002 * This library is part of OpenCms - 003 * the Open Source Content Management System 004 * 005 * Copyright (c) Alkacon Software GmbH & Co. KG (https://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: https://www.alkacon.com 019 * 020 * For further information about OpenCms, please see the 021 * project website: https://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 org.opencms.ade.configuration.CmsADEConfigData; 031import org.opencms.ade.detailpage.CmsDetailPageInfo; 032import org.opencms.db.CmsModificationContext; 033import org.opencms.db.CmsResourceState; 034import org.opencms.file.CmsObject; 035import org.opencms.file.CmsResource; 036import org.opencms.file.CmsResourceFilter; 037import org.opencms.file.types.CmsResourceTypeFolderExtended; 038import org.opencms.file.types.CmsResourceTypeFolderSubSitemap; 039import org.opencms.file.types.CmsResourceTypeXmlContainerPage; 040import org.opencms.file.types.I_CmsResourceType; 041import org.opencms.jsp.CmsJspNavBuilder; 042import org.opencms.main.CmsException; 043import org.opencms.main.CmsLog; 044import org.opencms.main.OpenCms; 045import org.opencms.security.CmsSecurityException; 046import org.opencms.ui.CmsCssIcon; 047import org.opencms.ui.CmsVaadinUtils; 048import org.opencms.util.CmsStringUtil; 049import org.opencms.workplace.CmsWorkplace; 050import org.opencms.workplace.explorer.CmsExplorerTypeSettings; 051import org.opencms.workplace.explorer.CmsResourceUtil; 052import org.opencms.workplace.list.Messages; 053import org.opencms.xml.containerpage.CmsXmlDynamicFunctionHandler; 054 055import java.util.List; 056 057import org.apache.commons.logging.Log; 058 059import com.google.common.collect.Lists; 060import com.vaadin.server.ExternalResource; 061import com.vaadin.server.FontIcon; 062import com.vaadin.server.Resource; 063import com.vaadin.v7.shared.ui.label.ContentMode; 064import com.vaadin.v7.ui.Label; 065 066/** 067 * Displays the resource icon and state and lock info.<p> 068 * Important: To avoid issues with click event propagation within tables, we are required to extent the Label component. 069 */ 070public class CmsResourceIcon extends Label { 071 072 /** Enum used to control icon display style. */ 073 public enum IconMode { 074 /** locale compare mode. */ 075 localeCompare, 076 077 /** sitemap selection mode. */ 078 sitemapSelect; 079 } 080 081 /** The changed icon class. */ 082 public static final String ICON_CLASS_CHANGED = "oc-icon-16-overlay-changed"; 083 084 public static final String ICON_CLASS_ONLINE_FOLDER = "oc-icon-16-online-folder"; 085 086 /** The other user lock icon class. */ 087 public static final String ICON_CLASS_LOCK_OTHER = "oc-icon-16-lock-other"; 088 089 /** The own user lock icon class. */ 090 public static final String ICON_CLASS_LOCK_OWN = "oc-icon-16-lock-own"; 091 092 /** The publish lock icon class. */ 093 public static final String ICON_CLASS_LOCK_PUBLISH = "oc-icon-16-lock-publish"; 094 095 /** The shared lock icon class. */ 096 public static final String ICON_CLASS_LOCK_SHARED = "oc-icon-16-lock-shared"; 097 098 /** The sibling icon class. */ 099 public static final String ICON_CLASS_SIBLING = "oc-icon-16-overlay-sibling"; 100 101 /** The log object for this class. */ 102 private static final Log LOG = CmsLog.getLog(CmsResourceIcon.class); 103 104 /** The serial version id. */ 105 private static final long serialVersionUID = 5031544534869165777L; 106 107 /** 108 * Constuctor.<p> 109 * To be used in declarative layouts. Make sure to call initContent later on.<p> 110 */ 111 public CmsResourceIcon() { 112 113 setPrimaryStyleName(OpenCmsTheme.RESOURCE_ICON); 114 setContentMode(ContentMode.HTML); 115 } 116 117 /** 118 * Constructor.<p> 119 * 120 * @param resUtil the resource util 121 * @param state the resource state 122 * @param showLocks <code>true</code> to show the resource locks 123 */ 124 public CmsResourceIcon(CmsResourceUtil resUtil, CmsResourceState state, boolean showLocks) { 125 126 this(); 127 initContent(resUtil, state, showLocks, true); 128 } 129 130 /** 131 * Returns the default file type or detail type for the given resource.<p> 132 * 133 * @param cms the cms context 134 * @param resource the container page resource 135 * 136 * @return the detail content type 137 */ 138 public static String getDefaultFileOrDetailType(CmsObject cms, CmsResource resource) { 139 140 String type = null; 141 142 if (resource.isFolder() 143 && !(OpenCms.getResourceManager().getResourceType(resource) instanceof CmsResourceTypeFolderExtended) 144 && !CmsJspNavBuilder.isNavLevelFolder(cms, resource)) { 145 try { 146 CmsResource defaultFile = cms.readDefaultFile(resource, CmsResourceFilter.ONLY_VISIBLE_NO_DELETED); 147 if (defaultFile != null) { 148 type = getDetailType(cms, defaultFile, resource); 149 if (type == null) { 150 type = OpenCms.getResourceManager().getResourceType(defaultFile).getTypeName(); 151 } 152 153 } 154 } catch (CmsSecurityException e) { 155 // ignore 156 } 157 } else if (CmsResourceTypeXmlContainerPage.isContainerPage(resource)) { 158 type = getDetailType(cms, resource, null); 159 160 } 161 return type; 162 163 } 164 165 /** 166 * Returns the detail content type for container pages that may be detail pages.<p> 167 * 168 * @param cms the cms context 169 * @param detailPage the container page resource 170 * @param parentFolder the parent folder or <code>null</code> 171 * 172 * @return the detail content type 173 */ 174 public static String getDetailType(CmsObject cms, CmsResource detailPage, CmsResource parentFolder) { 175 176 String type = null; 177 try { 178 if (OpenCms.getADEManager().isDetailPage(cms, detailPage)) { 179 List<CmsDetailPageInfo> detailPages = OpenCms.getADEManager().getRawDetailPages(cms); 180 if (parentFolder == null) { 181 parentFolder = cms.readParentFolder(detailPage.getStructureId()); 182 } 183 for (CmsDetailPageInfo info : detailPages) { 184 if (info.getId().equals(detailPage.getStructureId()) 185 || info.getId().equals(parentFolder.getStructureId())) { 186 type = info.getType(); 187 if (type.startsWith(CmsDetailPageInfo.FUNCTION_PREFIX)) { 188 type = CmsXmlDynamicFunctionHandler.TYPE_FUNCTION; 189 } 190 break; 191 } 192 } 193 } 194 } catch (CmsException e) { 195 // parent folder can't be read, ignore 196 } 197 return type; 198 } 199 200 /** 201 * Returns the icon HTML.<p> 202 * 203 * @param resUtil the resource util for the resource 204 * @param state the resource state 205 * @param showLocks <code>true</code> to show lock state overlay 206 * 207 * @return the icon HTML 208 */ 209 public static String getIconHTML(CmsResourceUtil resUtil, CmsResourceState state, boolean showLocks) { 210 211 return "<span class=\"" 212 + OpenCmsTheme.RESOURCE_ICON 213 + "\">" 214 + getIconInnerHTML(resUtil, state, showLocks, true) 215 + "</span>"; 216 } 217 218 /** 219 * Gets the resource icon for a resource for use in a CmsResourceInfo widget when used in a sitemap context.<p> 220 * 221 * @param cms the CMS context 222 * @param resource a resource 223 * @param iconMode the icon mode 224 * @return the path for the resource icon 225 */ 226 public static Resource getSitemapResourceIcon(CmsObject cms, CmsResource resource, IconMode iconMode) { 227 228 CmsResource defaultFile = null; 229 List<CmsResource> resourcesForType = Lists.newArrayList(); 230 resourcesForType.add(resource); 231 boolean skipDefaultFile = (iconMode == IconMode.sitemapSelect) 232 && OpenCms.getResourceManager().matchResourceType( 233 CmsResourceTypeFolderSubSitemap.TYPE_SUBSITEMAP, 234 resource.getTypeId()); 235 if (resource.isFolder() && !skipDefaultFile) { 236 237 try { 238 defaultFile = cms.readDefaultFile(resource, CmsResourceFilter.IGNORE_EXPIRATION); 239 if (defaultFile != null) { 240 resourcesForType.add(0, defaultFile); 241 } 242 } catch (Exception e) { 243 // Shouldn't normally happen - readDefaultFile returns null instead of throwing an exception when it doesn't find a default file 244 } 245 } 246 if (CmsJspNavBuilder.isNavLevelFolder(cms, resource)) { 247 return new CmsCssIcon(CmsExplorerTypeSettings.ICON_STYLE_NAV_LEVEL_BIG); 248 } 249 CmsResource maybePage = resourcesForType.get(0); 250 if (CmsResourceTypeXmlContainerPage.isContainerPage(maybePage)) { 251 CmsADEConfigData config = OpenCms.getADEManager().lookupConfiguration(cms, maybePage.getRootPath()); 252 for (CmsDetailPageInfo realInfo : config.getAllDetailPages(true)) { 253 if (realInfo.getUri().equals(maybePage.getRootPath()) 254 || realInfo.getUri().equals(CmsResource.getParentFolder(maybePage.getRootPath()))) { 255 CmsExplorerTypeSettings settings = OpenCms.getWorkplaceManager().getExplorerTypeSetting( 256 realInfo.getIconType()); 257 if (settings != null) { 258 return CmsResourceUtil.getBigIconResource(settings, resource.getName()); 259 } 260 } 261 } 262 } 263 264 Resource result = null; 265 for (CmsResource res : resourcesForType) { 266 I_CmsResourceType type = OpenCms.getResourceManager().getResourceType(res); 267 CmsExplorerTypeSettings settings = OpenCms.getWorkplaceManager().getExplorerTypeSetting(type.getTypeName()); 268 if (settings != null) { 269 result = CmsResourceUtil.getBigIconResource(settings, res.getName()); 270 break; 271 } 272 } 273 return result; 274 } 275 276 /** 277 * Returns the tree caption HTML including the resource icon.<p> 278 * 279 * @param resourceName the resource name to display 280 * @param resUtil the resource util for the resource 281 * @param state the resource state 282 * @param showLocks <code>true</code> to show lock state overlay 283 * 284 * @return the icon HTML 285 */ 286 public static String getTreeCaptionHTML( 287 String resourceName, 288 CmsResourceUtil resUtil, 289 CmsResourceState state, 290 boolean showLocks) { 291 292 return CmsResourceIcon.getIconHTML(resUtil, null, false) 293 + "<span class=\"o-tree-caption\">" 294 + resourceName 295 + "</span>"; 296 } 297 298 /** 299 * Returns the icon inner HTML.<p> 300 * 301 * @param resUtil the resource util for the resource 302 * @param state the resource state 303 * @param showLocks <code>true</code> to show lock state overlay 304 * @param showDetailIcon <code>true</code> to show the detail icon overlay 305 * 306 * @return the icon inner HTML 307 */ 308 private static String getIconInnerHTML( 309 CmsResourceUtil resUtil, 310 CmsResourceState state, 311 boolean showLocks, 312 boolean showDetailIcon) { 313 314 Resource iconResource = resUtil.getBigIconResource(); 315 316 return getIconInnerHTML(resUtil, iconResource, state, showLocks, showDetailIcon); 317 } 318 319 /** 320 * Returns the icon inner HTML.<p> 321 * 322 * @param resUtil the resource util for the resource 323 * @param iconResource the icon path 324 * @param state the resource state 325 * @param showLocks <code>true</code> to show lock state overlay 326 * @param showDetailIcon <code>true</code> to show the detail icon overlay 327 * 328 * @return the icon inner HTML 329 */ 330 private static String getIconInnerHTML( 331 CmsResourceUtil resUtil, 332 Resource iconResource, 333 CmsResourceState state, 334 boolean showLocks, 335 boolean showDetailIcon) { 336 337 String content; 338 if (iconResource instanceof FontIcon) { 339 content = ((FontIcon)iconResource).getHtml(); 340 } else if (iconResource instanceof ExternalResource) { 341 content = "<img src=\"" + ((ExternalResource)iconResource).getURL() + "\" />"; 342 } else { 343 content = ""; 344 } 345 346 boolean isNavLevel = false; 347 348 if (resUtil != null) { 349 if (showDetailIcon && !isNavLevel) { 350 351 if (resUtil.getResource().isFolder()) { 352 String detailType = getDefaultFileOrDetailType(resUtil.getCms(), resUtil.getResource()); 353 if (detailType != null) { 354 String smallIconUri = getSmallTypeIconHTML(detailType, false); 355 if (smallIconUri != null) { 356 content += smallIconUri; 357 } 358 } 359 } else if (CmsResourceTypeXmlContainerPage.isContainerPage(resUtil.getResource())) { 360 String detailType = getDefaultFileOrDetailType(resUtil.getCms(), resUtil.getResource()); 361 if (detailType != null) { 362 String smallIconUri = getSmallTypeIconHTML(detailType, true); 363 if (smallIconUri != null) { 364 content += smallIconUri; 365 } 366 } 367 368 } 369 } 370 if (showLocks) { 371 String lockIcon; 372 String message = null; 373 if (resUtil.getLock().getSystemLock().isPublish()) { 374 lockIcon = OpenCmsTheme.LOCK_PUBLISH + " " + ICON_CLASS_LOCK_PUBLISH; 375 message = CmsVaadinUtils.getMessageText( 376 org.opencms.workplace.explorer.Messages.GUI_PUBLISH_TOOLTIP_0); 377 } else { 378 switch (resUtil.getLockState()) { 379 case 1: 380 lockIcon = OpenCmsTheme.LOCK_OTHER + " " + ICON_CLASS_LOCK_OTHER; 381 break; 382 383 case 2: 384 lockIcon = OpenCmsTheme.LOCK_SHARED + " " + ICON_CLASS_LOCK_SHARED; 385 break; 386 case 3: 387 lockIcon = OpenCmsTheme.LOCK_USER + " " + ICON_CLASS_LOCK_OWN; 388 break; 389 default: 390 lockIcon = null; 391 } 392 if (lockIcon != null) { 393 message = CmsVaadinUtils.getMessageText( 394 Messages.GUI_EXPLORER_LIST_ACTION_LOCK_NAME_2, 395 resUtil.getLockedByName(), 396 resUtil.getLockedInProjectName()); 397 } 398 } 399 if (lockIcon != null) { 400 content += getOverlaySpan(lockIcon, message); 401 } 402 } 403 } 404 boolean isOnlineFolder = false; 405 406 if (resUtil != null) { 407 CmsResource resource = resUtil.getResource(); 408 if (CmsModificationContext.getOnlineFolderOptions().getPaths().stream().anyMatch( 409 onlineFolder -> CmsStringUtil.isPrefixPath(onlineFolder, resource.getRootPath()))) { 410 content += getOverlaySpan(ICON_CLASS_ONLINE_FOLDER + " " + OpenCmsTheme.ONLINE_FOLDER, null); 411 isOnlineFolder = true; 412 } 413 } 414 415 if ((state != null) && !isOnlineFolder) { 416 String title = resUtil != null 417 ? CmsVaadinUtils.getMessageText(org.opencms.workplace.commons.Messages.GUI_LABEL_USER_LAST_MODIFIED_0) 418 + " " 419 + resUtil.getUserLastModified() 420 : null; 421 if (state.isChanged() || state.isDeleted()) { 422 content += getOverlaySpan(OpenCmsTheme.STATE_CHANGED + " " + ICON_CLASS_CHANGED, title); 423 } else if (state.isNew()) { 424 content += getOverlaySpan(OpenCmsTheme.STATE_NEW + " " + ICON_CLASS_CHANGED, title); 425 } 426 } 427 if ((resUtil != null) && (resUtil.getLinkType() == 1)) { 428 content += getOverlaySpan(OpenCmsTheme.SIBLING + " " + ICON_CLASS_SIBLING, null); 429 } 430 return content; 431 } 432 433 /** 434 * Generates an overlay icon span.<p> 435 * 436 * @param title the span title 437 * @param cssClass the CSS class 438 * 439 * @return the span element string 440 */ 441 private static String getOverlaySpan(String cssClass, String title) { 442 443 StringBuffer result = new StringBuffer(); 444 result.append("<span class=\"").append(cssClass).append("\""); 445 if (title != null) { 446 result.append(" title=\"").append(title).append("\""); 447 } 448 result.append("></span>"); 449 return result.toString(); 450 } 451 452 /** 453 * Returns the URI of the small resource type icon for the given type.<p> 454 * 455 * @param type the resource type name 456 * @param isPageOverlay <code>true</code> in case this is a page overlay and not a folder overlay 457 * 458 * @return the icon URI 459 */ 460 private static String getSmallTypeIconHTML(String type, boolean isPageOverlay) { 461 462 CmsExplorerTypeSettings typeSettings = OpenCms.getWorkplaceManager().getExplorerTypeSetting(type); 463 if ((typeSettings == null) && LOG.isWarnEnabled()) { 464 LOG.warn("Could not read explorer type settings for " + type); 465 } 466 String result = null; 467 String overlayClass = isPageOverlay ? "o-page-icon-overlay" : "o-icon-overlay"; 468 if (typeSettings != null) { 469 if (typeSettings.getSmallIconStyle() != null) { 470 result = "<span class=\"v-icon " 471 + overlayClass 472 + " " 473 + typeSettings.getSmallIconStyle() 474 + "\"> </span>"; 475 } else if (typeSettings.getIcon() != null) { 476 result = "<img src=\"" 477 + CmsWorkplace.getResourceUri(CmsWorkplace.RES_PATH_FILETYPES + typeSettings.getIcon()) 478 + "\" class=\"" 479 + overlayClass 480 + "\" />"; 481 } else { 482 result = "<span class=\"v-icon " 483 + overlayClass 484 + " " 485 + CmsExplorerTypeSettings.ICON_STYLE_DEFAULT_SMALL 486 + "\"> </span>"; 487 } 488 } 489 return result; 490 } 491 492 /** 493 * Initializes the content.<p> 494 * 495 * @param resUtil the resource util 496 * @param state the resource state 497 * @param showLocks <code>true</code> to show the resource locks 498 * @param showDetailIcon <code>true</code> to show the detail icon overlay 499 */ 500 public void initContent( 501 CmsResourceUtil resUtil, 502 CmsResourceState state, 503 boolean showLocks, 504 boolean showDetailIcon) { 505 506 setValue(getIconInnerHTML(resUtil, state, showLocks, showDetailIcon)); 507 } 508 509 /** 510 * Initializes the content.<p> 511 * 512 * @param resUtil the resource util 513 * @param iconResource the icon path 514 * @param state the resource state 515 * @param showLocks <code>true</code> to show the resource locks 516 * @param showDetailIcon <code>true</code> to show the detail icon overlay 517 */ 518 public void initContent( 519 CmsResourceUtil resUtil, 520 Resource iconResource, 521 CmsResourceState state, 522 boolean showLocks, 523 boolean showDetailIcon) { 524 525 setValue(getIconInnerHTML(resUtil, iconResource, state, showLocks, showDetailIcon)); 526 } 527}