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.fileselect; 029 030import org.opencms.file.CmsObject; 031import org.opencms.file.CmsResource; 032import org.opencms.file.CmsResourceFilter; 033import org.opencms.main.CmsException; 034import org.opencms.main.CmsLog; 035import org.opencms.main.OpenCms; 036import org.opencms.site.CmsSite; 037import org.opencms.ui.A_CmsUI; 038import org.opencms.ui.CmsVaadinUtils; 039import org.opencms.ui.components.CmsResourceTableProperty; 040import org.opencms.util.CmsStringUtil; 041import org.opencms.util.CmsUUID; 042import org.opencms.workplace.CmsWorkplace; 043 044import java.util.Arrays; 045import java.util.Collections; 046import java.util.List; 047 048import org.apache.commons.logging.Log; 049 050import com.google.common.collect.Lists; 051import com.vaadin.ui.Component; 052import com.vaadin.ui.CustomComponent; 053import com.vaadin.v7.data.Container; 054import com.vaadin.v7.data.Property.ValueChangeEvent; 055import com.vaadin.v7.data.Property.ValueChangeListener; 056import com.vaadin.v7.data.util.IndexedContainer; 057import com.vaadin.v7.shared.ui.combobox.FilteringMode; 058import com.vaadin.v7.ui.ComboBox; 059 060/** 061 * Dialog with a site selector and file tree which can be used to select resources.<p> 062 */ 063public class CmsResourceSelectDialog extends CustomComponent { 064 065 /** 066 * Class for site select options.<p> 067 */ 068 public static class Options { 069 070 /**Indexed container.*/ 071 private IndexedContainer m_siteSelectionContainer; 072 073 /** 074 * Returns the siteSelectionContainer.<p> 075 * 076 * @return the siteSelectionContainer 077 */ 078 public IndexedContainer getSiteSelectionContainer() { 079 080 return m_siteSelectionContainer; 081 } 082 083 /** 084 * Sets the siteSelectionContainer.<p> 085 * 086 * @param siteSelectionContainer the siteSelectionContainer to set 087 */ 088 public void setSiteSelectionContainer(IndexedContainer siteSelectionContainer) { 089 090 m_siteSelectionContainer = siteSelectionContainer; 091 } 092 093 } 094 095 /** 096 * Converts resource selection to path (string) selection - either as root paths or site paths.<p> 097 */ 098 class PathSelectionAdapter implements I_CmsSelectionHandler<CmsResource> { 099 100 /** The wrapped string selection handler. */ 101 private I_CmsSelectionHandler<String> m_pathHandler; 102 103 /** If true, pass site paths to the wrapped path handler, else root paths. */ 104 private boolean m_useSitePaths; 105 106 /** 107 * Creates a new instance.<p> 108 * 109 * @param pathHandler the selection handler to call 110 * @param useSitePaths true if we want changes as site paths 111 */ 112 public PathSelectionAdapter(I_CmsSelectionHandler<String> pathHandler, boolean useSitePaths) { 113 114 m_pathHandler = pathHandler; 115 m_useSitePaths = useSitePaths; 116 } 117 118 /** 119 * @see org.opencms.ui.components.fileselect.I_CmsSelectionHandler#onSelection(java.lang.Object) 120 */ 121 @SuppressWarnings("synthetic-access") 122 public void onSelection(CmsResource selected) { 123 124 String path = selected.getRootPath(); 125 if (m_useSitePaths) { 126 try { 127 CmsObject cms = OpenCms.initCmsObject(A_CmsUI.getCmsObject()); 128 cms.getRequestContext().setSiteRoot(m_siteRoot); 129 path = cms.getRequestContext().removeSiteRoot(path); 130 } catch (CmsException e) { 131 LOG.error(e.getLocalizedMessage(), e); 132 133 } 134 } 135 m_pathHandler.onSelection(path); 136 } 137 } 138 139 /** The property used for the site caption. */ 140 public static final String PROPERTY_SITE_CAPTION = "caption"; 141 142 /** Logger instance for this class. */ 143 private static final Log LOG = CmsLog.getLog(CmsResourceSelectDialog.class); 144 145 /** Serial version id. */ 146 private static final long serialVersionUID = 1L; 147 148 /** The CMS context. */ 149 protected CmsObject m_currentCms; 150 151 /** The resource filter. */ 152 protected CmsResourceFilter m_filter; 153 154 /** The resource initially displayed at the root of the tree. */ 155 protected CmsResource m_root; 156 157 /** The file tree (wrapped in an array, because Vaadin Declarative tries to bind it otherwise) .*/ 158 private CmsResourceTreeTable m_fileTree; 159 160 /** Boolean flag indicating whether the tree is currently filtered. */ 161 private boolean m_isSitemapView = true; 162 163 /** The site root. */ 164 private String m_siteRoot; 165 166 /** Contains the data for the tree. */ 167 private CmsResourceTreeContainer m_treeData; 168 169 /** 170 * Creates a new instance.<p> 171 * 172 * @param filter the resource filter to use 173 * 174 * @throws CmsException if something goes wrong 175 */ 176 public CmsResourceSelectDialog(CmsResourceFilter filter) 177 throws CmsException { 178 179 this(filter, A_CmsUI.getCmsObject()); 180 } 181 182 /** 183 * public constructor with given CmsObject.<p> 184 * 185 * @param filter filter the resource filter to use 186 * @param cms CmsObejct to use 187 * @throws CmsException if something goes wrong 188 */ 189 public CmsResourceSelectDialog(CmsResourceFilter filter, CmsObject cms) 190 throws CmsException { 191 192 this(filter, cms, new Options()); 193 } 194 195 /** 196 * public constructor.<p> 197 * 198 * @param filter resource filter 199 * @param cms CmsObject 200 * @param options options 201 * @throws CmsException exception 202 */ 203 public CmsResourceSelectDialog(CmsResourceFilter filter, CmsObject cms, Options options) 204 throws CmsException { 205 206 m_filter = filter; 207 setCompositionRoot(new CmsResourceSelectDialogContents()); 208 IndexedContainer container = options.getSiteSelectionContainer() != null 209 ? options.getSiteSelectionContainer() 210 : CmsVaadinUtils.getAvailableSitesContainer(cms, PROPERTY_SITE_CAPTION); 211 getSiteSelector().setContainerDataSource(container); 212 213 if (!cms.existsResource("/", CmsResourceFilter.IGNORE_EXPIRATION)) { 214 cms = OpenCms.initCmsObject(cms); 215 cms.getRequestContext().setSiteRoot("/system/"); 216 } 217 m_siteRoot = cms.getRequestContext().getSiteRoot(); 218 219 getSiteSelector().setValue( 220 CmsVaadinUtils.getPathItemId(getSiteSelector().getContainerDataSource(), m_siteRoot)); 221 getSiteSelector().setNullSelectionAllowed(false); 222 getSiteSelector().setItemCaptionPropertyId(PROPERTY_SITE_CAPTION); 223 getSiteSelector().setFilteringMode(FilteringMode.CONTAINS); 224 getSiteSelector().addValueChangeListener(new ValueChangeListener() { 225 226 /** Serial version id. */ 227 private static final long serialVersionUID = 1L; 228 229 public void valueChange(ValueChangeEvent event) { 230 231 String site = (String)(event.getProperty().getValue()); 232 onSiteChange(site); 233 } 234 }); 235 236 CmsResource root = cms.readResource("/"); 237 m_fileTree = createTree(cms, root); 238 m_fileTree.setColumnExpandRatio(CmsResourceTreeTable.CAPTION_FOLDERS, 5); 239 m_fileTree.setColumnExpandRatio(CmsResourceTableProperty.PROPERTY_NAVIGATION_TEXT, 1); 240 m_treeData = m_fileTree.getTreeContainer(); 241 updateRoot(cms, root); 242 243 getContents().getTreeContainer().addComponent(m_fileTree); 244 ((Component)m_fileTree).setSizeFull(); 245 updateView(); 246 } 247 248 /** 249 * Adds a resource selection handler.<p> 250 * 251 * @param handler the handler 252 */ 253 public void addSelectionHandler(I_CmsSelectionHandler<CmsResource> handler) { 254 255 m_fileTree.addResourceSelectionHandler(handler); 256 } 257 258 /** 259 * Disables the option to select resources from other sites.<p> 260 */ 261 public void disableSiteSwitch() { 262 263 getSiteSelector().setEnabled(false); 264 } 265 266 /** 267 * Opens the given path.<p> 268 * 269 * @param path the path to open 270 */ 271 public void openPath(String path) { 272 273 if (!CmsStringUtil.isPrefixPath(m_root.getRootPath(), path)) { 274 CmsSite site = OpenCms.getSiteManager().getSiteForRootPath(path); 275 if (site != null) { 276 // the given path is a root path switch to the determined site 277 getSiteSelector().setValue(site.getSiteRoot()); 278 path = m_currentCms.getRequestContext().removeSiteRoot(path); 279 } else if (OpenCms.getSiteManager().startsWithShared(path)) { 280 getSiteSelector().setValue(OpenCms.getSiteManager().getSharedFolder()); 281 } else if (path.startsWith(CmsWorkplace.VFS_PATH_SYSTEM)) { 282 Container container = getSiteSelector().getContainerDataSource(); 283 String newSiteRoot = null; 284 for (String possibleSiteRoot : Arrays.asList("", "/", "/system", "/system/")) { 285 if (container.containsId(possibleSiteRoot)) { 286 newSiteRoot = possibleSiteRoot; 287 break; 288 } 289 } 290 if (newSiteRoot == null) { 291 LOG.warn( 292 "Couldn't open path in site selector because neither root site nor system folder are in the site selector. path=" 293 + path); 294 return; 295 } 296 getSiteSelector().setValue(newSiteRoot); 297 } 298 } 299 if (!"/".equals(path)) { 300 List<CmsUUID> idsToOpen = Lists.newArrayList(); 301 try { 302 CmsResource currentFolder = m_currentCms.readResource(CmsResource.getParentFolder(path)); 303 if (!m_root.getStructureId().equals(currentFolder.getStructureId())) { 304 idsToOpen.add(currentFolder.getStructureId()); 305 CmsResource parentFolder = null; 306 307 do { 308 try { 309 parentFolder = m_currentCms.readParentFolder(currentFolder.getStructureId()); 310 idsToOpen.add(parentFolder.getStructureId()); 311 currentFolder = parentFolder; 312 } catch (CmsException | NullPointerException e) { 313 LOG.info(e.getLocalizedMessage(), e); 314 break; 315 } 316 } while (!parentFolder.getStructureId().equals(m_root.getStructureId())); 317 // we need to iterate from "top" to "bottom", so we reverse the list of folders 318 Collections.reverse(idsToOpen); 319 320 for (CmsUUID id : idsToOpen) { 321 m_fileTree.expandItem(id); 322 } 323 } 324 } catch (CmsException e) { 325 LOG.debug("Can not read parent folder of current path.", e); 326 } 327 } 328 } 329 330 /** 331 * Switches between the folders and sitemap view of the tree.<p> 332 * 333 * @param showSitemapView <code>true</code> to show the sitemap view 334 */ 335 public void showSitemapView(boolean showSitemapView) { 336 337 if (m_isSitemapView != showSitemapView) { 338 m_isSitemapView = showSitemapView; 339 updateView(); 340 } 341 } 342 343 /** 344 * Displays the start resource by opening all nodes in the tree leading to it.<p> 345 * 346 * @param startResource the resource which should be shown in the tree 347 */ 348 public void showStartResource(CmsResource startResource) { 349 350 openPath(startResource.getRootPath()); 351 } 352 353 /** 354 * Creates the resource tree for the given root.<p> 355 * 356 * @param cms the CMS context 357 * @param root the root resource 358 * @return the resource tree 359 */ 360 protected CmsResourceTreeTable createTree(CmsObject cms, CmsResource root) { 361 362 return new CmsResourceTreeTable(cms, root, m_filter); 363 } 364 365 /** 366 * Gets the content panel of this dialog.<p> 367 * 368 * @return content panel of this dialog 369 */ 370 protected CmsResourceSelectDialogContents getContents() { 371 372 return ((CmsResourceSelectDialogContents)getCompositionRoot()); 373 } 374 375 /** 376 * Gets the file tree.<p> 377 * 378 * @return the file tree 379 */ 380 protected CmsResourceTreeTable getFileTree() { 381 382 return m_fileTree; 383 } 384 385 /** 386 * Called when the user changes the site.<p> 387 * 388 * @param site the new site root 389 */ 390 protected void onSiteChange(String site) { 391 392 try { 393 m_treeData.removeAllItems(); 394 CmsObject rootCms = OpenCms.initCmsObject(A_CmsUI.getCmsObject()); 395 rootCms.getRequestContext().setSiteRoot(""); 396 CmsResource siteRootResource = rootCms.readResource(site); 397 398 m_treeData.initRoot(rootCms, siteRootResource); 399 m_fileTree.expandItem(siteRootResource.getStructureId()); 400 m_siteRoot = site; 401 updateRoot(rootCms, siteRootResource); 402 } catch (CmsException e) { 403 LOG.error(e.getLocalizedMessage(), e); 404 } 405 } 406 407 /** 408 * Updates the current site root resource.<p> 409 * 410 * @param rootCms the CMS context 411 * @param siteRootResource the resource corresponding to a site root 412 */ 413 protected void updateRoot(CmsObject rootCms, CmsResource siteRootResource) { 414 415 m_root = siteRootResource; 416 m_currentCms = rootCms; 417 updateView(); 418 } 419 420 /** 421 * Updates the filtering state.<p> 422 */ 423 protected void updateView() { 424 425 m_fileTree.showSitemapView(m_isSitemapView); 426 } 427 428 /** 429 * Gets the site selector.<p> 430 * 431 * @return the site selector 432 */ 433 private ComboBox getSiteSelector() { 434 435 return getContents().getSiteSelector(); 436 } 437 438}