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; 029 030import org.opencms.ade.galleries.CmsSiteSelectorOptionBuilder; 031import org.opencms.ade.galleries.shared.CmsSiteSelectorOption; 032import org.opencms.db.CmsUserSettings; 033import org.opencms.file.CmsGroup; 034import org.opencms.file.CmsObject; 035import org.opencms.file.CmsProject; 036import org.opencms.file.CmsUser; 037import org.opencms.file.types.A_CmsResourceTypeFolderBase; 038import org.opencms.file.types.CmsResourceTypeXmlContent; 039import org.opencms.file.types.I_CmsResourceType; 040import org.opencms.i18n.CmsEncoder; 041import org.opencms.i18n.CmsMessages; 042import org.opencms.i18n.I_CmsMessageBundle; 043import org.opencms.main.CmsException; 044import org.opencms.main.CmsLog; 045import org.opencms.main.OpenCms; 046import org.opencms.security.CmsOrganizationalUnit; 047import org.opencms.security.CmsRole; 048import org.opencms.security.I_CmsPrincipal; 049import org.opencms.ui.apps.CmsAppWorkplaceUi; 050import org.opencms.ui.apps.Messages; 051import org.opencms.ui.apps.user.CmsOUHandler; 052import org.opencms.ui.components.OpenCmsTheme; 053import org.opencms.ui.contextmenu.CmsContextMenu; 054import org.opencms.ui.contextmenu.I_CmsSimpleContextMenuEntry; 055import org.opencms.util.CmsFileUtil; 056import org.opencms.util.CmsMacroResolver; 057import org.opencms.util.CmsStringUtil; 058import org.opencms.util.CmsUUID; 059import org.opencms.workplace.CmsWorkplace; 060import org.opencms.workplace.CmsWorkplaceMessages; 061import org.opencms.workplace.explorer.CmsExplorerTypeSettings; 062import org.opencms.workplace.explorer.CmsResourceUtil; 063 064import java.io.ByteArrayInputStream; 065import java.io.IOException; 066import java.io.InputStream; 067import java.io.UnsupportedEncodingException; 068import java.util.ArrayList; 069import java.util.Arrays; 070import java.util.Collection; 071import java.util.Collections; 072import java.util.HashSet; 073import java.util.Iterator; 074import java.util.LinkedHashMap; 075import java.util.List; 076import java.util.Locale; 077import java.util.Map; 078import java.util.Map.Entry; 079 080import javax.servlet.http.HttpServletRequest; 081 082import org.apache.commons.lang3.ClassUtils; 083import org.apache.commons.logging.Log; 084 085import com.google.common.base.Function; 086import com.google.common.base.Predicate; 087import com.google.common.collect.Lists; 088import com.vaadin.server.ErrorMessage; 089import com.vaadin.server.ExternalResource; 090import com.vaadin.server.FontIcon; 091import com.vaadin.server.Resource; 092import com.vaadin.server.VaadinService; 093import com.vaadin.shared.MouseEventDetails.MouseButton; 094import com.vaadin.shared.Version; 095import com.vaadin.ui.AbstractComponent; 096import com.vaadin.ui.Alignment; 097import com.vaadin.ui.Button; 098import com.vaadin.ui.Button.ClickEvent; 099import com.vaadin.ui.Button.ClickListener; 100import com.vaadin.ui.Component; 101import com.vaadin.ui.ComponentContainer; 102import com.vaadin.ui.HasComponents; 103import com.vaadin.ui.JavaScript; 104import com.vaadin.ui.Panel; 105import com.vaadin.ui.SingleComponentContainer; 106import com.vaadin.ui.TextField; 107import com.vaadin.ui.UI; 108import com.vaadin.ui.Window; 109import com.vaadin.ui.declarative.Design; 110import com.vaadin.ui.themes.ValoTheme; 111import com.vaadin.v7.data.Container; 112import com.vaadin.v7.data.Container.Filter; 113import com.vaadin.v7.data.Item; 114import com.vaadin.v7.data.util.IndexedContainer; 115import com.vaadin.v7.event.ItemClickEvent; 116import com.vaadin.v7.shared.ui.combobox.FilteringMode; 117import com.vaadin.v7.ui.AbstractField; 118import com.vaadin.v7.ui.ComboBox; 119import com.vaadin.v7.ui.Label; 120import com.vaadin.v7.ui.OptionGroup; 121import com.vaadin.v7.ui.Table; 122import com.vaadin.v7.ui.VerticalLayout; 123 124/** 125 * Vaadin utility functions.<p> 126 * 127 */ 128@SuppressWarnings("deprecation") 129public final class CmsVaadinUtils { 130 131 /** 132 * Helper class for building option groups.<p> 133 */ 134 public static class OptionGroupBuilder { 135 136 /** The option group being built. */ 137 private OptionGroup m_optionGroup = new OptionGroup(); 138 139 /** 140 * Adds an option.<p> 141 * 142 * @param key the option key 143 * @param text the option text 144 * 145 * @return this instance 146 */ 147 public OptionGroupBuilder add(String key, String text) { 148 149 m_optionGroup.addItem(key); 150 m_optionGroup.setItemCaption(key, text); 151 return this; 152 } 153 154 /** 155 * Returns the option group.<p> 156 * 157 * @return the option group 158 */ 159 public OptionGroup build() { 160 161 return m_optionGroup; 162 } 163 164 /** 165 * Adds horizontal style to option group.<p> 166 * 167 * @return this instance 168 */ 169 public OptionGroupBuilder horizontal() { 170 171 m_optionGroup.addStyleName(ValoTheme.OPTIONGROUP_HORIZONTAL); 172 return this; 173 } 174 } 175 176 /** Container property ids. */ 177 public static enum PropertyId { 178 /** The caption id. */ 179 caption, 180 /** The icon id. */ 181 icon, 182 /** The is folder id. */ 183 isFolder, 184 /** The is XML content id. */ 185 isXmlContent 186 } 187 188 /** Container filter for the resource type container to show not folder types only. */ 189 public static final Filter FILTER_NO_FOLDERS = new Filter() { 190 191 private static final long serialVersionUID = 1L; 192 193 public boolean appliesToProperty(Object propertyId) { 194 195 return PropertyId.isFolder.equals(propertyId); 196 } 197 198 public boolean passesFilter(Object itemId, Item item) throws UnsupportedOperationException { 199 200 return !((Boolean)item.getItemProperty(PropertyId.isFolder).getValue()).booleanValue(); 201 } 202 }; 203 /** Container filter for the resource type container to show XML content types only. */ 204 public static final Filter FILTER_XML_CONTENTS = new Filter() { 205 206 private static final long serialVersionUID = 1L; 207 208 public boolean appliesToProperty(Object propertyId) { 209 210 return PropertyId.isXmlContent.equals(propertyId); 211 } 212 213 public boolean passesFilter(Object itemId, Item item) throws UnsupportedOperationException { 214 215 return ((Boolean)item.getItemProperty(PropertyId.isXmlContent).getValue()).booleanValue(); 216 } 217 }; 218 219 /** The combo box label item property id. */ 220 public static final String PROPERTY_LABEL = "label"; 221 222 /** The combo box value item property id. */ 223 public static final String PROPERTY_VALUE = "value"; 224 225 /** The Vaadin bootstrap script, with some macros to be dynamically replaced later. */ 226 protected static final String BOOTSTRAP_SCRIPT = "vaadin.initApplication(\"%(elementId)\", {\n" 227 + " \"browserDetailsUrl\": \"%(vaadinServlet)\",\n" 228 + " \"serviceUrl\": \"%(vaadinServlet)\",\n" 229 + " \"widgetset\": \"org.opencms.ui.WidgetSet\",\n" 230 + " \"theme\": \"opencms\",\n" 231 + " \"versionInfo\": {\"vaadinVersion\": \"%(vaadinVersion)\"},\n" 232 + " \"vaadinDir\": \"%(vaadinDir)\",\n" 233 + " \"heartbeatInterval\": 30,\n" 234 + " \"debug\": false,\n" 235 + " \"standalone\": false,\n" 236 + " \"authErrMsg\": {\n" 237 + " \"message\": \"Take note of any unsaved data, \"+\n" 238 + " \"and <u>click here<\\/u> to continue.\",\n" 239 + " \"caption\": \"Authentication problem\"\n" 240 + " },\n" 241 + " \"comErrMsg\": {\n" 242 + " \"message\": \"Take note of any unsaved data, \"+\n" 243 + " \"and <u>click here<\\/u> to continue.\",\n" 244 + " \"caption\": \"Communication problem\"\n" 245 + " },\n" 246 + " \"sessExpMsg\": {\n" 247 + " \"message\": \"Take note of any unsaved data, \"+\n" 248 + " \"and <u>click here<\\/u> to continue.\",\n" 249 + " \"caption\": \"Session Expired\"\n" 250 + " }\n" 251 + " });"; 252 253 /** The logger of this class. */ 254 private static final Log LOG = CmsLog.getLog(CmsVaadinUtils.class); 255 256 /** 257 * Hidden default constructor for utility class.<p> 258 */ 259 private CmsVaadinUtils() { 260 261 } 262 263 /** 264 * Builds a container for use in combo boxes from a map of key/value pairs, where the keys are options and the values are captions.<p> 265 * 266 * @param captionProperty the property name to use for captions 267 * @param map the map 268 * @return the new container 269 */ 270 public static IndexedContainer buildContainerFromMap(String captionProperty, Map<String, String> map) { 271 272 IndexedContainer container = new IndexedContainer(); 273 for (Map.Entry<String, String> entry : map.entrySet()) { 274 container.addItem(entry.getKey()).getItemProperty(captionProperty).setValue(entry.getValue()); 275 } 276 return container; 277 } 278 279 /** 280 * Centers the parent window of given component.<p> 281 * 282 * @param component Component as child of window 283 */ 284 public static void centerWindow(Component component) { 285 286 Window window = getWindow(component); 287 if (window != null) { 288 window.center(); 289 } 290 } 291 292 /** 293 * Closes the window containing the given component. 294 * 295 * @param component a component 296 */ 297 public static void closeWindow(Component component) { 298 299 Window window = getWindow(component); 300 if (window != null) { 301 window.close(); 302 } 303 } 304 305 /** 306 * Creates a click listener which calls a Runnable when activated.<p> 307 * 308 * @param action the Runnable to execute on a click 309 * 310 * @return the click listener 311 */ 312 public static Button.ClickListener createClickListener(final Runnable action) { 313 314 return new Button.ClickListener() { 315 316 /** Serial version id. */ 317 private static final long serialVersionUID = 1L; 318 319 public void buttonClick(ClickEvent event) { 320 321 action.run(); 322 } 323 }; 324 } 325 326 /** 327 * Simple context menu handler for multi-select tables. 328 * 329 * @param table the table 330 * @param menu the table's context menu 331 * @param event the click event 332 * @param entries the context menu entries 333 */ 334 @SuppressWarnings("unchecked") 335 public static <T> void defaultHandleContextMenuForMultiselect( 336 Table table, 337 CmsContextMenu menu, 338 ItemClickEvent event, 339 List<I_CmsSimpleContextMenuEntry<Collection<T>>> entries) { 340 341 if (!event.isCtrlKey() && !event.isShiftKey()) { 342 if (event.getButton().equals(MouseButton.RIGHT)) { 343 Collection<T> oldValue = ((Collection<T>)table.getValue()); 344 if (oldValue.isEmpty() || !oldValue.contains(event.getItemId())) { 345 table.setValue(new HashSet<Object>(Arrays.asList(event.getItemId()))); 346 } 347 Collection<T> selection = (Collection<T>)table.getValue(); 348 menu.setEntries(entries, selection); 349 menu.openForTable(event, table); 350 } 351 } 352 353 } 354 355 /** 356 * Reads the content of an input stream into a string (using UTF-8 encoding), performs a function on the string, and returns the result 357 * again as an input stream.<p> 358 * 359 * @param stream the stream producing the input data 360 * @param transformation the function to apply to the input 361 * 362 * @return the stream producing the transformed input data 363 */ 364 public static InputStream filterUtf8ResourceStream(InputStream stream, Function<String, String> transformation) { 365 366 try { 367 byte[] streamData = CmsFileUtil.readFully(stream); 368 String dataAsString = new String(streamData, "UTF-8"); 369 byte[] transformedData = transformation.apply(dataAsString).getBytes("UTF-8"); 370 return new ByteArrayInputStream(transformedData); 371 } catch (UnsupportedEncodingException e) { 372 LOG.error(e.getLocalizedMessage(), e); 373 return null; 374 } catch (IOException e) { 375 LOG.error(e.getLocalizedMessage(), e); 376 throw new RuntimeException(e); 377 } 378 } 379 380 /** 381 * Get all groups with blacklist.<p> 382 * 383 * @param cms CmsObject 384 * @param ouFqn ou name 385 * @param propCaption property 386 * @param propIcon property for icon 387 * @param propOu organizational unit 388 * @param blackList blacklist 389 * @param iconProvider the icon provider 390 * @return indexed container 391 */ 392 public static IndexedContainer getAvailableGroupsContainerWithout( 393 CmsObject cms, 394 String ouFqn, 395 String propCaption, 396 String propIcon, 397 String propOu, 398 List<CmsGroup> blackList, 399 java.util.function.Function<CmsGroup, CmsCssIcon> iconProvider) { 400 401 if (blackList == null) { 402 blackList = new ArrayList<CmsGroup>(); 403 } 404 IndexedContainer res = new IndexedContainer(); 405 res.addContainerProperty(propCaption, String.class, ""); 406 res.addContainerProperty(propOu, String.class, ""); 407 if (propIcon != null) { 408 res.addContainerProperty(propIcon, CmsCssIcon.class, null); 409 } 410 try { 411 for (CmsGroup group : OpenCms.getRoleManager().getManageableGroups(cms, ouFqn, true)) { 412 if (!blackList.contains(group)) { 413 Item item = res.addItem(group); 414 if (item == null) { 415 continue; 416 } 417 if (iconProvider != null) { 418 item.getItemProperty(propIcon).setValue(iconProvider.apply(group)); 419 } 420 item.getItemProperty(propCaption).setValue(group.getSimpleName()); 421 item.getItemProperty(propOu).setValue(group.getOuFqn()); 422 } 423 } 424 425 } catch (CmsException e) { 426 LOG.error("Unable to read groups", e); 427 } 428 return res; 429 } 430 431 /** 432 * Returns the available projects.<p> 433 * 434 * @param cms the CMS context 435 * 436 * @return the available projects 437 */ 438 public static List<CmsProject> getAvailableProjects(CmsObject cms) { 439 440 // get all project information 441 List<CmsProject> allProjects; 442 try { 443 String ouFqn = ""; 444 CmsUserSettings settings = new CmsUserSettings(cms); 445 if (!settings.getListAllProjects()) { 446 ouFqn = cms.getRequestContext().getCurrentUser().getOuFqn(); 447 } 448 allProjects = new ArrayList<CmsProject>( 449 OpenCms.getOrgUnitManager().getAllAccessibleProjects(cms, ouFqn, settings.getListAllProjects())); 450 Iterator<CmsProject> itProjects = allProjects.iterator(); 451 while (itProjects.hasNext()) { 452 CmsProject prj = itProjects.next(); 453 if (prj.isHiddenFromSelector()) { 454 itProjects.remove(); 455 } 456 } 457 } catch (CmsException e) { 458 // should usually never happen 459 LOG.error(e.getLocalizedMessage(), e); 460 allProjects = Collections.emptyList(); 461 } 462 return allProjects; 463 } 464 465 /** 466 * Builds an IndexedContainer containing the sites selectable by the current user.<p> 467 * 468 * @param cms the CMS context 469 * @param captionPropertyName the name of the property used to store captions 470 * 471 * @return the container with the available sites 472 */ 473 public static IndexedContainer getAvailableSitesContainer(CmsObject cms, String captionPropertyName) { 474 475 IndexedContainer availableSites = new IndexedContainer(); 476 availableSites.addContainerProperty(captionPropertyName, String.class, null); 477 for (Map.Entry<String, String> entry : getAvailableSitesMap(cms).entrySet()) { 478 Item siteItem = availableSites.addItem(entry.getKey()); 479 siteItem.getItemProperty(captionPropertyName).setValue(entry.getValue()); 480 } 481 return availableSites; 482 } 483 484 /** 485 * Gets available sites as a LinkedHashMap, with site roots as keys and site labels as values. 486 * 487 * @param cms the current CMS context 488 * @return the map of available sites 489 */ 490 public static LinkedHashMap<String, String> getAvailableSitesMap(CmsObject cms) { 491 492 CmsSiteSelectorOptionBuilder optBuilder = new CmsSiteSelectorOptionBuilder(cms); 493 optBuilder.addNormalSites(true, (new CmsUserSettings(cms)).getStartFolder()); 494 optBuilder.addSharedSite(); 495 LinkedHashMap<String, String> result = new LinkedHashMap<String, String>(); 496 for (CmsSiteSelectorOption option : optBuilder.getOptions()) { 497 result.put(option.getSiteRoot(), option.getMessage()); 498 } 499 String currentSiteRoot = cms.getRequestContext().getSiteRoot(); 500 if (!result.containsKey(currentSiteRoot)) { 501 result.put(currentSiteRoot, currentSiteRoot); 502 } 503 return result; 504 505 } 506 507 /** 508 * Returns the Javascript code to use for initializing a Vaadin UI.<p> 509 * 510 * @param cms the CMS context 511 * @param elementId the id of the DOM element in which to initialize the UI 512 * @param servicePath the UI servlet path 513 * @return the Javascript code to initialize Vaadin 514 * 515 * @throws Exception if something goes wrong 516 */ 517 public static String getBootstrapScript(CmsObject cms, String elementId, String servicePath) throws Exception { 518 519 String script = BOOTSTRAP_SCRIPT; 520 CmsMacroResolver resolver = new CmsMacroResolver(); 521 String context = OpenCms.getSystemInfo().getContextPath(); 522 String vaadinDir = CmsStringUtil.joinPaths(context, "VAADIN/"); 523 String vaadinVersion = Version.getFullVersion(); 524 String vaadinServlet = CmsStringUtil.joinPaths(context, servicePath); 525 String vaadinBootstrap = CmsStringUtil.joinPaths(context, "VAADIN/vaadinBootstrap.js"); 526 resolver.addMacro("vaadinDir", vaadinDir); 527 resolver.addMacro("vaadinVersion", vaadinVersion); 528 resolver.addMacro("elementId", elementId); 529 resolver.addMacro("vaadinServlet", vaadinServlet); 530 resolver.addMacro("vaadinBootstrap", vaadinBootstrap); 531 script = resolver.resolveMacros(script); 532 return script; 533 534 } 535 536 /** 537 * Returns the path to the design template file of the given component.<p> 538 * 539 * @param component the component 540 * 541 * @return the path 542 */ 543 public static String getDefaultDesignPath(Component component) { 544 545 String className = component.getClass().getName(); 546 String designPath = className.replace(".", "/") + ".html"; 547 return designPath; 548 } 549 550 /** 551 * Gets container with alls groups of a certain user. 552 * 553 * @param cms cmsobject 554 * @param user to find groups for 555 * @param caption caption property 556 * @param iconProp property 557 * @param ou ou 558 * @param propStatus status property 559 * @param iconProvider the icon provider 560 * @return Indexed Container 561 */ 562 public static IndexedContainer getGroupsOfUser( 563 CmsObject cms, 564 CmsUser user, 565 String caption, 566 String iconProp, 567 String ou, 568 String propStatus, 569 Function<CmsGroup, CmsCssIcon> iconProvider) { 570 571 IndexedContainer container = new IndexedContainer(); 572 container.addContainerProperty(caption, String.class, ""); 573 container.addContainerProperty(ou, String.class, ""); 574 container.addContainerProperty(propStatus, Boolean.class, new Boolean(true)); 575 if (iconProvider != null) { 576 container.addContainerProperty(iconProp, CmsCssIcon.class, null); 577 } 578 try { 579 for (CmsGroup group : cms.getGroupsOfUser(user.getName(), true)) { 580 Item item = container.addItem(group); 581 item.getItemProperty(caption).setValue(group.getSimpleName()); 582 item.getItemProperty(ou).setValue(group.getOuFqn()); 583 if (iconProvider != null) { 584 item.getItemProperty(iconProp).setValue(iconProvider.apply(group)); 585 } 586 } 587 } catch (CmsException e) { 588 LOG.error("Unable to read groups from user", e); 589 } 590 return container; 591 } 592 593 /** 594 * Creates a layout with info panel.<p> 595 * 596 * @param messageString Message to be displayed 597 * @return layout 598 */ 599 public static VerticalLayout getInfoLayout(String messageString) { 600 601 VerticalLayout ret = new VerticalLayout(); 602 ret.setMargin(true); 603 ret.addStyleName("o-center"); 604 ret.setWidth("100%"); 605 VerticalLayout inner = new VerticalLayout(); 606 inner.addStyleName("o-workplace-maxwidth"); 607 Panel panel = new Panel(); 608 panel.setWidth("100%"); 609 610 Label label = new Label(CmsVaadinUtils.getMessageText(messageString)); 611 label.addStyleName("o-report"); 612 panel.setContent(label); 613 614 inner.addComponent(panel); 615 ret.addComponent(inner); 616 return ret; 617 } 618 619 /** 620 * Get container with languages.<p> 621 * 622 * @param captionPropertyName name 623 * @return indexed container 624 */ 625 public static IndexedContainer getLanguageContainer(String captionPropertyName) { 626 627 IndexedContainer result = new IndexedContainer(); 628 result.addContainerProperty(captionPropertyName, String.class, ""); 629 630 Iterator<Locale> itLocales = OpenCms.getLocaleManager().getAvailableLocales().iterator(); 631 while (itLocales.hasNext()) { 632 Locale locale = itLocales.next(); 633 Item item = result.addItem(locale); 634 item.getItemProperty(captionPropertyName).setValue(locale.getDisplayName(A_CmsUI.get().getLocale())); 635 } 636 637 return result; 638 639 } 640 641 /** 642 * Gets the message for the current locale and the given key and arguments.<p> 643 * 644 * @param messages the messages instance 645 * @param key the message key 646 * @param args the message arguments 647 * 648 * @return the message text for the current locale 649 */ 650 public static String getMessageText(I_CmsMessageBundle messages, String key, Object... args) { 651 652 return messages.getBundle(A_CmsUI.get().getLocale()).key(key, args); 653 } 654 655 /** 656 * Gets the workplace message for the current locale and the given key and arguments.<p> 657 * 658 * @param key the message key 659 * @param args the message arguments 660 * 661 * @return the message text for the current locale 662 */ 663 public static String getMessageText(String key, Object... args) { 664 665 return getWpMessagesForCurrentLocale().key(key, args); 666 } 667 668 /** 669 * Creates the ComboBox for OU selection.<p> 670 * @param cms CmsObject 671 * @param baseOu OU 672 * @param log Logger object 673 * 674 * @return ComboBox 675 */ 676 public static ComboBox getOUComboBox(CmsObject cms, String baseOu, Log log) { 677 678 return getOUComboBox(cms, baseOu, log, true); 679 } 680 681 /** 682 * Creates the ComboBox for OU selection.<p> 683 * @param cms CmsObject 684 * @param baseOu OU 685 * @param log Logger object 686 * @param includeWebOU include webou? 687 * 688 * @return ComboBox 689 */ 690 public static ComboBox getOUComboBox(CmsObject cms, String baseOu, Log log, boolean includeWebOU) { 691 692 ComboBox combo = null; 693 try { 694 IndexedContainer container = new IndexedContainer(); 695 container.addContainerProperty("desc", String.class, ""); 696 for (String ou : CmsOUHandler.getManagableOUs(cms)) { 697 if (includeWebOU | !OpenCms.getOrgUnitManager().readOrganizationalUnit(cms, ou).hasFlagWebuser()) { 698 Item item = container.addItem(ou); 699 if (ou == "") { 700 CmsOrganizationalUnit root = OpenCms.getOrgUnitManager().readOrganizationalUnit(cms, ""); 701 item.getItemProperty("desc").setValue(root.getDisplayName(A_CmsUI.get().getLocale())); 702 } else { 703 item.getItemProperty("desc").setValue( 704 OpenCms.getOrgUnitManager().readOrganizationalUnit(cms, ou).getDisplayName( 705 A_CmsUI.get().getLocale())); 706 } 707 } 708 } 709 combo = new ComboBox(null, container); 710 combo.setTextInputAllowed(true); 711 combo.setNullSelectionAllowed(false); 712 combo.setWidth("379px"); 713 combo.setInputPrompt( 714 Messages.get().getBundle(UI.getCurrent().getLocale()).key(Messages.GUI_EXPLORER_CLICK_TO_EDIT_0)); 715 combo.setItemCaptionPropertyId("desc"); 716 717 combo.setFilteringMode(FilteringMode.CONTAINS); 718 719 combo.select(baseOu); 720 721 } catch (CmsException e) { 722 if (log != null) { 723 log.error("Unable to read OU", e); 724 } 725 } 726 return combo; 727 } 728 729 /** 730 * Gives item id from path.<p> 731 * 732 * @param cnt to be used 733 * @param path to obtain item id from 734 * @return item id 735 */ 736 public static String getPathItemId(Container cnt, String path) { 737 738 for (String id : Arrays.asList(path, CmsFileUtil.toggleTrailingSeparator(path))) { 739 if (cnt.containsId(id)) { 740 return id; 741 } 742 } 743 return null; 744 } 745 746 /** 747 * Get container for principal. 748 * 749 * @param cms cmsobject 750 * @param list of principals 751 * @param captionID caption id 752 * @param descID description id 753 * @param iconID icon id 754 * @param ouID ou id 755 * @param icon icon 756 * @param iconList iconlist 757 * @return indexedcontainer 758 */ 759 public static IndexedContainer getPrincipalContainer( 760 CmsObject cms, 761 List<? extends I_CmsPrincipal> list, 762 String captionID, 763 String descID, 764 String iconID, 765 String ouID, 766 String icon, 767 List<FontIcon> iconList) { 768 769 IndexedContainer res = new IndexedContainer(); 770 771 res.addContainerProperty(captionID, String.class, ""); 772 res.addContainerProperty(ouID, String.class, ""); 773 res.addContainerProperty(iconID, FontIcon.class, new CmsCssIcon(icon)); 774 if (descID != null) { 775 res.addContainerProperty(descID, String.class, ""); 776 } 777 778 for (I_CmsPrincipal group : list) { 779 780 Item item = res.addItem(group); 781 item.getItemProperty(captionID).setValue(group.getSimpleName()); 782 item.getItemProperty(ouID).setValue(group.getOuFqn()); 783 if (descID != null) { 784 item.getItemProperty(descID).setValue(group.getDescription(A_CmsUI.get().getLocale())); 785 } 786 } 787 788 for (int i = 0; i < iconList.size(); i++) { 789 res.getItem(res.getIdByIndex(i)).getItemProperty(iconID).setValue(iconList.get(i)); 790 } 791 792 return res; 793 } 794 795 /** 796 * Returns the selectable projects container.<p> 797 * 798 * @param cms the CMS context 799 * @param captionPropertyName the name of the property used to store captions 800 * 801 * @return the projects container 802 */ 803 public static IndexedContainer getProjectsContainer(CmsObject cms, String captionPropertyName) { 804 805 IndexedContainer result = new IndexedContainer(); 806 result.addContainerProperty(captionPropertyName, String.class, null); 807 for (Map.Entry<CmsUUID, String> entry : getProjectsMap(cms).entrySet()) { 808 Item projectItem = result.addItem(entry.getKey()); 809 projectItem.getItemProperty(captionPropertyName).setValue(entry.getValue()); 810 } 811 return result; 812 } 813 814 /** 815 * Gets the available projects for the current user as a map, wth project ids as keys and project names as values. 816 * 817 * @param cms the current CMS context 818 * @return the map of projects 819 */ 820 public static LinkedHashMap<CmsUUID, String> getProjectsMap(CmsObject cms) { 821 822 Locale locale = A_CmsUI.get().getLocale(); 823 List<CmsProject> projects = getAvailableProjects(cms); 824 boolean isSingleOu = isSingleOu(projects); 825 LinkedHashMap<CmsUUID, String> result = new LinkedHashMap<>(); 826 for (CmsProject project : projects) { 827 String projectName = project.getSimpleName(); 828 if (!isSingleOu && !project.isOnlineProject()) { 829 try { 830 projectName = projectName 831 + " - " 832 + OpenCms.getOrgUnitManager().readOrganizationalUnit(cms, project.getOuFqn()).getDisplayName( 833 locale); 834 } catch (CmsException e) { 835 LOG.debug("Error reading project OU.", e); 836 projectName = projectName + " - " + project.getOuFqn(); 837 } 838 } 839 result.put(project.getUuid(), projectName); 840 } 841 return result; 842 843 } 844 845 /** 846 * Gets the current Vaadin request, cast to a HttpServletRequest.<p> 847 * 848 * @return the current request 849 */ 850 public static HttpServletRequest getRequest() { 851 852 return (HttpServletRequest)VaadinService.getCurrentRequest(); 853 } 854 855 /** 856 * Gets list of resource types.<p> 857 * 858 * @return List 859 */ 860 public static List<I_CmsResourceType> getResourceTypes() { 861 862 List<I_CmsResourceType> res = new ArrayList<I_CmsResourceType>(); 863 for (I_CmsResourceType type : OpenCms.getResourceManager().getResourceTypes()) { 864 CmsExplorerTypeSettings typeSetting = OpenCms.getWorkplaceManager().getExplorerTypeSetting( 865 type.getTypeName()); 866 if (typeSetting != null) { 867 res.add(type); 868 } 869 } 870 return res; 871 } 872 873 /** 874 * Returns the available resource types container.<p> 875 * 876 * @return the resource types container 877 */ 878 public static IndexedContainer getResourceTypesContainer() { 879 880 IndexedContainer types = new IndexedContainer(); 881 types.addContainerProperty(PropertyId.caption, String.class, null); 882 types.addContainerProperty(PropertyId.icon, Resource.class, null); 883 types.addContainerProperty(PropertyId.isFolder, Boolean.class, null); 884 types.addContainerProperty(PropertyId.isXmlContent, Boolean.class, null); 885 for (I_CmsResourceType type : getResourceTypes()) { 886 CmsExplorerTypeSettings typeSetting = OpenCms.getWorkplaceManager().getExplorerTypeSetting( 887 type.getTypeName()); 888 Item typeItem = types.addItem(type); 889 typeItem.getItemProperty(PropertyId.caption).setValue(CmsVaadinUtils.getMessageText(typeSetting.getKey())); 890 typeItem.getItemProperty(PropertyId.icon).setValue(CmsResourceUtil.getSmallIconResource(typeSetting, null)); 891 typeItem.getItemProperty(PropertyId.isXmlContent).setValue( 892 Boolean.valueOf(type instanceof CmsResourceTypeXmlContent)); 893 typeItem.getItemProperty(PropertyId.isFolder).setValue( 894 Boolean.valueOf(type instanceof A_CmsResourceTypeFolderBase)); 895 } 896 897 return types; 898 } 899 900 /** 901 * Returns the roles available for a given user.<p> 902 * 903 * @param cms CmsObject 904 * @param user to get available roles for 905 * @param captionPropertyName name of caption property 906 * @return indexed container 907 */ 908 public static IndexedContainer getRoleContainerForUser(CmsObject cms, CmsUser user, String captionPropertyName) { 909 910 IndexedContainer result = new IndexedContainer(); 911 result.addContainerProperty(captionPropertyName, String.class, ""); 912 try { 913 List<CmsRole> roles = OpenCms.getRoleManager().getRoles(cms, user.getOuFqn(), false); 914 CmsRole.applySystemRoleOrder(roles); 915 for (CmsRole role : roles) { 916 Item item = result.addItem(role); 917 item.getItemProperty(captionPropertyName).setValue(role.getDisplayName(cms, A_CmsUI.get().getLocale())); 918 } 919 } catch (CmsException e) { 920 LOG.error("Unabel to read roles for user", e); 921 } 922 return result; 923 } 924 925 /** 926 * Gets the window which contains a given component.<p> 927 * 928 * @param component the component 929 * @return the window containing the component, or null if no component is found 930 */ 931 public static Window getWindow(Component component) { 932 933 if (component == null) { 934 return null; 935 } else if (component instanceof Window) { 936 return (Window)component; 937 } else { 938 return getWindow(component.getParent()); 939 } 940 941 } 942 943 /** 944 * Gets the link to the (new) workplace.<p> 945 * 946 * @return the link to the workplace 947 */ 948 public static String getWorkplaceLink() { 949 950 return OpenCms.getSystemInfo().getWorkplaceContext(); 951 } 952 953 /** 954 * Returns the workplace link for the given app.<p> 955 * 956 * @param appId the app id 957 * 958 * @return the workplace link 959 */ 960 public static String getWorkplaceLink(String appId) { 961 962 return getWorkplaceLink() + CmsAppWorkplaceUi.WORKPLACE_APP_ID_SEPARATOR + appId; 963 } 964 965 /** 966 * Returns the workplace link to the given app with the given state.<p> 967 * 968 * @param appId the app id 969 * @param appState the app state 970 * 971 * @return the workplace link 972 */ 973 public static String getWorkplaceLink(String appId, String appState) { 974 975 return getWorkplaceLink(appId) + CmsAppWorkplaceUi.WORKPLACE_STATE_SEPARATOR + appState; 976 } 977 978 /** 979 * Returns the workplace link to the given app with the given state including the given request parameters.<p> 980 * 981 * @param appId the app id 982 * @param appState the app state 983 * @param requestParameters the request parameters 984 * 985 * @return the workplace link 986 */ 987 public static String getWorkplaceLink(String appId, String appState, Map<String, String[]> requestParameters) { 988 989 String result = getWorkplaceLink(); 990 if ((requestParameters != null) && !requestParameters.isEmpty()) { 991 boolean first = true; 992 for (Entry<String, String[]> param : requestParameters.entrySet()) { 993 for (String value : param.getValue()) { 994 if (first) { 995 result += "?"; 996 } else { 997 result += "&"; 998 } 999 result += param.getKey() + "=" + value; 1000 first = false; 1001 } 1002 } 1003 } 1004 1005 if (CmsStringUtil.isNotEmptyOrWhitespaceOnly(appId)) { 1006 result += CmsAppWorkplaceUi.WORKPLACE_APP_ID_SEPARATOR + appId; 1007 } 1008 if (CmsStringUtil.isNotEmptyOrWhitespaceOnly(appState)) { 1009 result += CmsAppWorkplaceUi.WORKPLACE_STATE_SEPARATOR + appState; 1010 } 1011 return result; 1012 } 1013 1014 /** 1015 * Gets external resource from workplace resource folder.<p> 1016 * 1017 * @param subPath path relative to workplace resource folder 1018 * 1019 * @return the external resource 1020 */ 1021 public static ExternalResource getWorkplaceResource(String subPath) { 1022 1023 return new ExternalResource(CmsWorkplace.getResourceUri(subPath)); 1024 1025 } 1026 1027 /** 1028 * Gets the workplace messages for the current locale.<p> 1029 * 1030 * @return the workplace messages 1031 */ 1032 public static CmsMessages getWpMessagesForCurrentLocale() { 1033 1034 return OpenCms.getWorkplaceManager().getMessages(A_CmsUI.get().getLocale()); 1035 } 1036 1037 /** 1038 * Checks if path is itemid in container.<p> 1039 * 1040 * @param cnt to be checked 1041 * @param path as itemid 1042 * @return true id path is itemid in container 1043 */ 1044 public static boolean hasPathAsItemId(Container cnt, String path) { 1045 1046 return cnt.containsId(path) || cnt.containsId(CmsFileUtil.toggleTrailingSeparator(path)); 1047 } 1048 1049 /** 1050 * Checks if a button is pressed.<p> 1051 * 1052 * @param button the button 1053 * 1054 * @return true if the button is pressed 1055 */ 1056 public static boolean isButtonPressed(Button button) { 1057 1058 if (button == null) { 1059 return false; 1060 } 1061 List<String> styles = Arrays.asList(button.getStyleName().split(" ")); 1062 1063 return styles.contains(OpenCmsTheme.BUTTON_PRESSED); 1064 } 1065 1066 /** 1067 * Uses the currently set locale to resolve localization macros in the input string using workplace message bundles.<p> 1068 * 1069 * @param baseString the string to localize 1070 * 1071 * @return the localized string 1072 */ 1073 public static String localizeString(String baseString) { 1074 1075 if (baseString == null) { 1076 return null; 1077 } 1078 CmsWorkplaceMessages wpMessages = OpenCms.getWorkplaceManager().getMessages(A_CmsUI.get().getLocale()); 1079 CmsMacroResolver resolver = new CmsMacroResolver(); 1080 resolver.setMessages(wpMessages); 1081 String result = resolver.resolveMacros(baseString); 1082 return result; 1083 } 1084 1085 /** 1086 * Message accessior function.<p> 1087 * 1088 * @return the message for Cancel buttons 1089 */ 1090 public static String messageCancel() { 1091 1092 return getMessageText(org.opencms.workplace.Messages.GUI_DIALOG_BUTTON_CANCEL_0); 1093 } 1094 1095 /** 1096 * Message accessior function.<p> 1097 * 1098 * @return the message for Cancel buttons 1099 */ 1100 public static String messageClose() { 1101 1102 return getMessageText(org.opencms.workplace.Messages.GUI_DIALOG_BUTTON_CLOSE_0); 1103 } 1104 1105 /** 1106 * Message accessor function.<p> 1107 * 1108 * @return the message for OK buttons 1109 */ 1110 public static String messageOk() { 1111 1112 return getMessageText(org.opencms.workplace.Messages.GUI_DIALOG_BUTTON_OK_0); 1113 } 1114 1115 /** 1116 * Generates the options items for the combo box using the map entry keys as values and the values as labels.<p> 1117 * 1118 * @param box the combo box to prepare 1119 * @param options the box options 1120 */ 1121 public static void prepareComboBox(ComboBox box, Map<?, String> options) { 1122 1123 IndexedContainer container = new IndexedContainer(); 1124 container.addContainerProperty(PROPERTY_VALUE, Object.class, null); 1125 container.addContainerProperty(PROPERTY_LABEL, String.class, ""); 1126 for (Entry<?, String> entry : options.entrySet()) { 1127 Item item = container.addItem(entry.getKey()); 1128 item.getItemProperty(PROPERTY_VALUE).setValue(entry.getKey()); 1129 item.getItemProperty(PROPERTY_LABEL).setValue(entry.getValue()); 1130 } 1131 box.setContainerDataSource(container); 1132 box.setItemCaptionPropertyId(PROPERTY_LABEL); 1133 } 1134 1135 /** 1136 * Reads the declarative design for a component and localizes it using a messages object.<p> 1137 * 1138 * The design will need to be located in the same directory as the component's class and have '.html' as a file extension. 1139 * 1140 * @param component the component for which to read the design 1141 * @param messages the message bundle to use for localization 1142 * @param macros the macros to use on the HTML template 1143 */ 1144 @SuppressWarnings("resource") 1145 public static void readAndLocalizeDesign(Component component, CmsMessages messages, Map<String, String> macros) { 1146 1147 Class<?> componentClass = component.getClass(); 1148 List<Class<?>> classes = Lists.newArrayList(); 1149 classes.add(componentClass); 1150 classes.addAll(ClassUtils.getAllSuperclasses(componentClass)); 1151 InputStream designStream = null; 1152 for (Class<?> cls : classes) { 1153 if (cls.getName().startsWith("com.vaadin")) { 1154 break; 1155 } 1156 String filename = cls.getSimpleName() + ".html"; 1157 designStream = cls.getResourceAsStream(filename); 1158 if (designStream != null) { 1159 break; 1160 } 1161 1162 } 1163 if (designStream == null) { 1164 throw new IllegalArgumentException("Design not found for : " + component.getClass()); 1165 } 1166 readAndLocalizeDesign(component, designStream, messages, macros); 1167 } 1168 1169 /** 1170 * Reads a layout from a resource, applies basic i18n macro substitution on the contained text, and returns a stream of the transformed 1171 * data.<p> 1172 * 1173 * @param layoutClass the class relative to which the layout resource will be looked up 1174 * @param relativeName the file name of the layout file 1175 * 1176 * @return an input stream which produces the transformed layout resource html 1177 */ 1178 public static InputStream readCustomLayout(Class<? extends Component> layoutClass, String relativeName) { 1179 1180 CmsMacroResolver resolver = new CmsMacroResolver() { 1181 1182 @Override 1183 public String getMacroValue(String macro) { 1184 1185 return CmsEncoder.escapeXml(super.getMacroValue(macro)); 1186 } 1187 }; 1188 resolver.setMessages(CmsVaadinUtils.getWpMessagesForCurrentLocale()); 1189 InputStream layoutStream = CmsVaadinUtils.filterUtf8ResourceStream( 1190 layoutClass.getResourceAsStream(relativeName), 1191 resolver.toFunction()); 1192 return layoutStream; 1193 } 1194 1195 /** 1196 * Replaces component with new component.<p> 1197 * 1198 * @param component to be replaced 1199 * @param replacement new component 1200 */ 1201 public static void replaceComponent(Component component, Component replacement) { 1202 1203 if (!component.isAttached()) { 1204 throw new IllegalArgumentException("Component must be attached"); 1205 } 1206 HasComponents parent = component.getParent(); 1207 if (parent instanceof ComponentContainer) { 1208 ((ComponentContainer)parent).replaceComponent(component, replacement); 1209 } else if (parent instanceof SingleComponentContainer) { 1210 ((SingleComponentContainer)parent).setContent(replacement); 1211 } else { 1212 throw new IllegalArgumentException("Illegal class for parent: " + parent.getClass()); 1213 } 1214 } 1215 1216 /** 1217 * Configures a text field to look like a filter box for a table. 1218 * 1219 * @param searchBox the text field to configure 1220 */ 1221 public static void setFilterBoxStyle(TextField searchBox) { 1222 1223 searchBox.setIcon(FontOpenCms.FILTER); 1224 1225 searchBox.setPlaceholder( 1226 org.opencms.ui.apps.Messages.get().getBundle(UI.getCurrent().getLocale()).key( 1227 org.opencms.ui.apps.Messages.GUI_EXPLORER_FILTER_0)); 1228 searchBox.addStyleName(ValoTheme.TEXTFIELD_INLINE_ICON); 1229 } 1230 1231 /** 1232 * Sets the value of a text field which may be set to read-only mode.<p> 1233 * 1234 * When setting a Vaadin field to read-only, you also can't set its value programmatically anymore. 1235 * So we need to temporarily disable read-only mode, set the value, and then switch back to read-only mode. 1236 * 1237 * @param field the field 1238 * @param value the value to set 1239 */ 1240 public static <T> void setReadonlyValue(AbstractField<T> field, T value) { 1241 1242 boolean readonly = field.isReadOnly(); 1243 try { 1244 field.setReadOnly(false); 1245 field.setValue(value); 1246 } finally { 1247 field.setReadOnly(readonly); 1248 } 1249 } 1250 1251 /** 1252 * Shows an alert box to the user with the given information, which will perform the given action after the user clicks on OK.<p> 1253 * 1254 * @param title the title 1255 * @param message the message 1256 * 1257 * @param callback the callback to execute after clicking OK 1258 */ 1259 public static void showAlert(String title, String message, final Runnable callback) { 1260 1261 final Window window = new Window(); 1262 window.setModal(true); 1263 Panel panel = new Panel(); 1264 panel.setCaption(title); 1265 panel.setWidth("500px"); 1266 VerticalLayout layout = new VerticalLayout(); 1267 layout.setMargin(true); 1268 panel.setContent(layout); 1269 layout.addComponent(new Label(message)); 1270 Button okButton = new Button(); 1271 okButton.addClickListener(new ClickListener() { 1272 1273 /** The serial version id. */ 1274 private static final long serialVersionUID = 1L; 1275 1276 public void buttonClick(ClickEvent event) { 1277 1278 window.close(); 1279 if (callback != null) { 1280 callback.run(); 1281 } 1282 } 1283 }); 1284 layout.addComponent(okButton); 1285 layout.setComponentAlignment(okButton, Alignment.BOTTOM_RIGHT); 1286 okButton.setCaption( 1287 org.opencms.workplace.Messages.get().getBundle(A_CmsUI.get().getLocale()).key( 1288 org.opencms.workplace.Messages.GUI_DIALOG_BUTTON_OK_0)); 1289 window.setContent(panel); 1290 window.setClosable(false); 1291 window.setResizable(false); 1292 A_CmsUI.get().addWindow(window); 1293 1294 } 1295 1296 /** 1297 * Creates a new option group builder.<p> 1298 * 1299 * @return a new option group builder 1300 */ 1301 public static OptionGroupBuilder startOptionGroup() { 1302 1303 return new OptionGroupBuilder(); 1304 } 1305 1306 /** 1307 * Sets style of a toggle button depending on its current state.<p> 1308 * 1309 * @param button the button to update 1310 */ 1311 public static void toggleButton(Button button) { 1312 1313 if (isButtonPressed(button)) { 1314 button.removeStyleName(OpenCmsTheme.BUTTON_PRESSED); 1315 } else { 1316 button.addStyleName(OpenCmsTheme.BUTTON_PRESSED); 1317 } 1318 } 1319 1320 /** 1321 * Updates the component error of a component, but only if it differs from the currently set 1322 * error.<p> 1323 * 1324 * @param component the component 1325 * @param error the error 1326 * 1327 * @return true if the error was changed 1328 */ 1329 public static boolean updateComponentError(AbstractComponent component, ErrorMessage error) { 1330 1331 if (component.getComponentError() != error) { 1332 component.setComponentError(error); 1333 return true; 1334 } 1335 return false; 1336 } 1337 1338 /** 1339 * Visits all descendants of a given component (including the component itself) and applies a predicate 1340 * to each.<p> 1341 * 1342 * If the predicate returns false for a component, no further descendants will be processed.<p> 1343 * 1344 * @param component the component 1345 * @param handler the predicate 1346 */ 1347 public static void visitDescendants(Component component, Predicate<Component> handler) { 1348 1349 List<Component> stack = Lists.newArrayList(); 1350 stack.add(component); 1351 while (!stack.isEmpty()) { 1352 Component currentComponent = stack.get(stack.size() - 1); 1353 stack.remove(stack.size() - 1); 1354 if (!handler.apply(currentComponent)) { 1355 return; 1356 } 1357 if (currentComponent instanceof HasComponents) { 1358 List<Component> children = Lists.newArrayList((HasComponents)currentComponent); 1359 Collections.reverse(children); 1360 stack.addAll(children); 1361 } 1362 } 1363 } 1364 1365 /** 1366 * Waggle the component.<p> 1367 * 1368 * @param component to be waggled 1369 */ 1370 public static void waggleMeOnce(Component component) { 1371 1372 //TODO Until now, the component gets a waggler class which can not be removed again here.. 1373 component.addStyleName("waggler"); 1374 //Add JavaScript code, which adds the waggle class and removes it after a short time. 1375 JavaScript.getCurrent().execute( 1376 "waggler=document.querySelectorAll(\".waggler\")[0];" 1377 + "waggler.className=waggler.className + \" waggle\";" 1378 + "setTimeout(function () {\n" 1379 + "waggler.className=waggler.className.replace(/\\bwaggle\\b/g, \"\");" 1380 + " }, 1500);"); 1381 } 1382 1383 /** 1384 * Reads the given design and resolves the given macros and localizations.<p> 1385 1386 * @param component the component whose design to read 1387 * @param designStream stream to read the design from 1388 * @param messages the message bundle to use for localization in the design (may be null) 1389 * @param macros other macros to substitute in the macro design (may be null) 1390 */ 1391 protected static void readAndLocalizeDesign( 1392 Component component, 1393 InputStream designStream, 1394 CmsMessages messages, 1395 Map<String, String> macros) { 1396 1397 try { 1398 byte[] designBytes = CmsFileUtil.readFully(designStream, true); 1399 final String encoding = "UTF-8"; 1400 String design = new String(designBytes, encoding); 1401 CmsMacroResolver resolver = new CmsMacroResolver() { 1402 1403 @Override 1404 public String getMacroValue(String macro) { 1405 1406 String result = super.getMacroValue(macro); 1407 // The macro may contain quotes or angle brackets, so we need to escape the values for insertion into the design file 1408 return CmsEncoder.escapeXml(result); 1409 1410 } 1411 }; 1412 1413 if (macros != null) { 1414 for (Map.Entry<String, String> entry : macros.entrySet()) { 1415 resolver.addMacro(entry.getKey(), entry.getValue()); 1416 } 1417 } 1418 if (messages != null) { 1419 resolver.setMessages(messages); 1420 } 1421 String resolvedDesign = resolver.resolveMacros(design); 1422 Design.read(new ByteArrayInputStream(resolvedDesign.getBytes(encoding)), component); 1423 } catch (IOException e) { 1424 throw new RuntimeException("Could not read design", e); 1425 } finally { 1426 try { 1427 designStream.close(); 1428 } catch (IOException e) { 1429 LOG.warn(e.getLocalizedMessage(), e); 1430 } 1431 } 1432 } 1433 1434 /** 1435 * Returns whether only a single OU is visible to the current user.<p> 1436 * 1437 * @param projects the selectable projects 1438 * 1439 * @return <code>true</code> if only a single OU is visible to the current user 1440 */ 1441 private static boolean isSingleOu(List<CmsProject> projects) { 1442 1443 String ouFqn = null; 1444 for (CmsProject project : projects) { 1445 if (project.isOnlineProject()) { 1446 // skip the online project 1447 continue; 1448 } 1449 if (ouFqn == null) { 1450 // set the first ou 1451 ouFqn = project.getOuFqn(); 1452 } else if (!ouFqn.equals(project.getOuFqn())) { 1453 // break if one different ou is found 1454 return false; 1455 } 1456 } 1457 return true; 1458 } 1459 1460}