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