content: /*
   * This library is part of OpenCms -
   * the Open Source Content Management System
   *
   * Copyright (c) Alkacon Software GmbH & Co. KG (https://www.alkacon.com)
   *
   * This library is free software; you can redistribute it and/or
   * modify it under the terms of the GNU Lesser General Public
   * License as published by the Free Software Foundation; either
   * version 2.1 of the License, or (at your option) any later version.
   *
   * This library is distributed in the hope that it will be useful,
   * but WITHOUT ANY WARRANTY; without even the implied warranty of
   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
   * Lesser General Public License for more details.
   *
   * For further information about Alkacon Software, please see the
   * company website: https://www.alkacon.com
   *
   * For further information about OpenCms, please see the
   * project website: https://www.opencms.org
   *
   * You should have received a copy of the GNU Lesser General Public
   * License along with this library; if not, write to the Free Software
   * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
   */
  
  package org.opencms.ade.galleries;
  
  import org.opencms.ade.configuration.CmsADEConfigData;
  import org.opencms.ade.configuration.formatters.CmsFormatterConfigurationCacheState;
  import org.opencms.ade.galleries.CmsGalleryFilteredNavTreeBuilder.NavigationNode;
  import org.opencms.ade.galleries.preview.I_CmsPreviewProvider;
  import org.opencms.ade.galleries.shared.CmsGalleryActionInfo;
  import org.opencms.ade.galleries.shared.CmsGalleryConfiguration;
  import org.opencms.ade.galleries.shared.CmsGalleryDataBean;
  import org.opencms.ade.galleries.shared.CmsGalleryFolderBean;
  import org.opencms.ade.galleries.shared.CmsGalleryGroup;
  import org.opencms.ade.galleries.shared.CmsGallerySearchBean;
  import org.opencms.ade.galleries.shared.CmsGallerySearchScope;
  import org.opencms.ade.galleries.shared.CmsGalleryTabConfiguration;
  import org.opencms.ade.galleries.shared.CmsResourceTypeBean;
  import org.opencms.ade.galleries.shared.CmsResourceTypeBean.TypeVisibility;
  import org.opencms.ade.galleries.shared.CmsResultItemBean;
  import org.opencms.ade.galleries.shared.CmsSitemapEntryBean;
  import org.opencms.ade.galleries.shared.CmsVfsEntryBean;
  import org.opencms.ade.galleries.shared.I_CmsGalleryProviderConstants;
  import org.opencms.ade.galleries.shared.I_CmsGalleryProviderConstants.GalleryMode;
  import org.opencms.ade.galleries.shared.I_CmsGalleryProviderConstants.GalleryTabId;
  import org.opencms.ade.galleries.shared.I_CmsGalleryProviderConstants.SortParams;
  import org.opencms.ade.galleries.shared.rpc.I_CmsGalleryService;
  import org.opencms.file.CmsObject;
  import org.opencms.file.CmsProperty;
  import org.opencms.file.CmsPropertyDefinition;
  import org.opencms.file.CmsResource;
  import org.opencms.file.CmsResourceFilter;
  import org.opencms.file.CmsUser;
  import org.opencms.file.CmsVfsResourceNotFoundException;
  import org.opencms.file.types.CmsResourceTypeBinary;
  import org.opencms.file.types.CmsResourceTypeFunctionConfig;
  import org.opencms.file.types.CmsResourceTypeImage;
  import org.opencms.file.types.CmsResourceTypePointer;
  import org.opencms.file.types.CmsResourceTypeXmlContent;
  import org.opencms.file.types.CmsResourceTypeXmlPage;
  import org.opencms.file.types.I_CmsResourceType;
  import org.opencms.flex.CmsFlexController;
  import org.opencms.gwt.CmsCoreService;
  import org.opencms.gwt.CmsGwtService;
  import org.opencms.gwt.CmsIconUtil;
  import org.opencms.gwt.CmsRpcException;
  import org.opencms.gwt.CmsVfsService;
  import org.opencms.gwt.shared.CmsGalleryContainerInfo;
  import org.opencms.gwt.shared.CmsListInfoBean;
  import org.opencms.gwt.shared.CmsTemplateContextInfo;
  import org.opencms.i18n.CmsLocaleManager;
  import org.opencms.i18n.CmsMessages;
  import org.opencms.json.JSONException;
  import org.opencms.json.JSONObject;
  import org.opencms.jsp.CmsJspNavBuilder;
  import org.opencms.jsp.CmsJspNavBuilder.Visibility;
  import org.opencms.jsp.CmsJspNavElement;
  import org.opencms.loader.CmsLoaderException;
  import org.opencms.loader.CmsResourceManager;
  import org.opencms.loader.I_CmsTemplateContextProvider;
  import org.opencms.main.CmsException;
  import org.opencms.main.CmsLog;
  import org.opencms.main.CmsPermalinkResourceHandler;
  import org.opencms.main.CmsStaticResourceHandler;
  import org.opencms.main.OpenCms;
  import org.opencms.search.fields.CmsSearchFieldMapping;
  import org.opencms.search.galleries.CmsGallerySearch;
  import org.opencms.search.galleries.CmsGallerySearchParameters;
  import org.opencms.search.galleries.CmsGallerySearchResult;
  import org.opencms.search.galleries.CmsGallerySearchResultList;
  import org.opencms.security.CmsPermissionSet;
  import org.opencms.security.CmsPermissionViolationException;
  import org.opencms.security.CmsRole;
  import org.opencms.site.CmsSite;
  import org.opencms.staticexport.CmsLinkManager;
  import org.opencms.ui.components.CmsResourceIcon;
  import org.opencms.util.CmsDateUtil;
  import org.opencms.util.CmsPath;
  import org.opencms.util.CmsRequestUtil;
  import org.opencms.util.CmsStringUtil;
  import org.opencms.util.CmsUUID;
  import org.opencms.util.CmsUriSplitter;
  import org.opencms.workplace.CmsWorkplace;
  import org.opencms.workplace.CmsWorkplaceMessages;
  import org.opencms.workplace.CmsWorkplaceSettings;
  import org.opencms.workplace.explorer.CmsResourceUtil;
  import org.opencms.xml.containerpage.CmsADESessionCache;
  import org.opencms.xml.containerpage.CmsFormatterConfiguration;
  import org.opencms.xml.containerpage.CmsFunctionFormatterBean;
  import org.opencms.xml.containerpage.CmsXmlDynamicFunctionHandler;
  import org.opencms.xml.containerpage.I_CmsFormatterBean;
  
  import java.text.Collator;
  import java.text.DateFormat;
  import java.text.ParseException;
  import java.util.ArrayList;
  import java.util.Arrays;
  import java.util.Collection;
  import java.util.Collections;
  import java.util.Comparator;
  import java.util.Date;
  import java.util.HashMap;
  import java.util.HashSet;
  import java.util.Iterator;
  import java.util.List;
  import java.util.Locale;
  import java.util.Map;
  import java.util.Map.Entry;
  import java.util.Set;
  import java.util.TreeMap;
  import java.util.TreeSet;
  import java.util.function.Predicate;
  
  import javax.servlet.http.HttpServletRequest;
  import javax.servlet.http.HttpSession;
  
  import org.apache.commons.logging.Log;
  
  import com.google.common.collect.ArrayListMultimap;
  import com.google.common.collect.ComparisonChain;
  import com.google.common.collect.Lists;
  import com.google.common.collect.Maps;
  import com.google.common.collect.Multimap;
  import com.google.common.collect.Sets;
  
  /**
   * Handles all RPC services related to the gallery dialog.<p>
   *
   * @since 8.0.0
   *
   * @see org.opencms.ade.galleries.CmsGalleryService
   * @see org.opencms.ade.galleries.shared.rpc.I_CmsGalleryService
   * @see org.opencms.ade.galleries.shared.rpc.I_CmsGalleryServiceAsync
   */
  public class CmsGalleryService extends CmsGwtService implements I_CmsGalleryService {
  
      /**
       * Gallery info object.<p>
       */
      protected class CmsGalleryTypeInfo {
  
          /** The content types using this gallery. */
          private List<I_CmsResourceType> m_contentTypes;
  
          /** The gallery folder resources. */
          private List<CmsResource> m_galleries;
  
          /** The resource type of this gallery. */
          private I_CmsResourceType m_resourceType;
  
          /**
           * Constructor.<p>
           *
           * @param resourceType the resource type of the gallery
           * @param contentType the resource type of the gallery content
           * @param galleries the gallery resources
           */
          protected CmsGalleryTypeInfo(
              I_CmsResourceType resourceType,
              I_CmsResourceType contentType,
              List<CmsResource> galleries) {
  
              m_resourceType = resourceType;
              m_contentTypes = new ArrayList<I_CmsResourceType>();
              m_contentTypes.add(contentType);
              m_galleries = galleries;
          }
  
          /**
           * Adds a type to the list of content types.<p>
           *
           * @param type the type to add
           */
          protected void addContentType(I_CmsResourceType type) {
  
              m_contentTypes.add(type);
          }
  
          /**
           * Returns the contentTypes.<p>
           *
           * @return the contentTypes
           */
          protected List<I_CmsResourceType> getContentTypes() {
  
              return m_contentTypes;
          }
  
          /**
           * Returns the gallery folder resources.<p>
           *
           * @return the resources
           */
          protected List<CmsResource> getGalleries() {
  
              return m_galleries;
          }
  
          /**
           * Returns the resourceType.<p>
           *
           * @return the resourceType
           */
          protected I_CmsResourceType getResourceType() {
  
              return m_resourceType;
          }
  
          /**
           * Sets the contentTypes.<p>
           *
           * @param contentTypes the contentTypes to set
           */
          protected void setContentTypes(List<I_CmsResourceType> contentTypes) {
  
              m_contentTypes = contentTypes;
          }
  
          /**
           * Sets the galleries.<p>
           *
           * @param galleries the gallery resource list to set
           */
          protected void setGalleries(List<CmsResource> galleries) {
  
              m_galleries = galleries;
          }
  
          /**
           * Sets the resourceType.<p>
           *
           * @param resourceType the resourceType to set
           */
          protected void setResourceType(I_CmsResourceType resourceType) {
  
              m_resourceType = resourceType;
          }
      }
  
      /** Bean used to store a single type together with a flag indicating its visibility. */
      class TypeWithVisibility {
  
          /** True if the type should only be shown in the full list. */
          private boolean m_onlyShowInFullList;
  
          /** The resource type. */
          private I_CmsResourceType m_type;
  
          /**
           * Creates a new instance.<p>
           *
           * @param type the resource type
           * @param onlyShowInFullList the flag to control the visibility
           */
          public TypeWithVisibility(I_CmsResourceType type, boolean onlyShowInFullList) {
  
              m_type = type;
              m_onlyShowInFullList = onlyShowInFullList;
          }
  
          /**
           * Returns the type.<p>
           *
           * @return the type
           */
          public I_CmsResourceType getType() {
  
              return m_type;
          }
  
          /**
           * Returns the onlyShowInFullList.<p>
           *
           * @return the onlyShowInFullList
           */
          public boolean isOnlyShowInFullList() {
  
              return m_onlyShowInFullList;
          }
  
      }
  
      /** Key for additional info gallery folder filter. */
      public static final String FOLDER_FILTER_ADD_INFO_KEY = "gallery_folder_filter";
  
      /** Limit to the number results loaded on initial search. */
      public static final int INITIAL_SEARCH_MAX_RESULTS = 200;
  
      /** The key used for storing the last used gallery in adeview mode. */
      public static final String KEY_LAST_USED_GALLERY_ADEVIEW = "__adeView";
  
      /** Name for the 'galleryShowInvalidDefault' preference. */
      public static final String PREF_GALLERY_SHOW_INVALID_DEFAULT = "galleryShowInvalidDefault";
  
      /** Storage key for the last gallery sort order used in the content editor and ADE image gallery button. */
      public static final String RESULT_ORDER_KEY_EDITOR = "editor";
  
      /** Storage key for the last gallery sort order in other contexts. */
      public static final String RESULT_ORDER_KEY_OTHER = "other";
  
      /** Storage key for the last gallery sort order used in the 'magic wand'/new element dialog. */
      public static final String RESULT_ORDER_KEY_PAGE = "page";
  
      /** Key for additional info gallery result view type. */
      public static final String RESULT_VIEW_TYPE_ADD_INFO_KEY = "gallery_result_view_type";
  
      /** The dynamic function resource type names. */
      private static final Set<String> FUNCTION_TYPES = new HashSet<>(
          Arrays.asList(
              new String[] {CmsXmlDynamicFunctionHandler.TYPE_FUNCTION, CmsResourceTypeFunctionConfig.TYPE_NAME}));
  
      /** The logger instance for this class. */
      private static final Log LOG = CmsLog.getLog(CmsGalleryService.class);
  
      /** Serialization uid. */
      private static final long serialVersionUID = 1673026761080584889L;
  
      /** Compares the VFS entry beans. */
      private static final Comparator<CmsVfsEntryBean> VFS_COMPARATOR = new Comparator<CmsVfsEntryBean>() {
  
          public int compare(CmsVfsEntryBean o1, CmsVfsEntryBean o2) {
  
              String o1Path = o1.getRootPath();
              String o2Path = o2.getRootPath();
              if (o1Path.endsWith("/")) {
                  o1Path = o1Path.substring(0, o1Path.length() - 1);
              }
              if (o2Path.endsWith("/")) {
                  o2Path = o2Path.substring(0, o2Path.length() - 1);
              }
              return o1Path.compareTo(o2Path);
          }
      };
  
      /** The instance of the resource manager. */
      CmsResourceManager m_resourceManager;
  
      /** The workplace settings of the current user. */
      private CmsWorkplaceSettings m_workplaceSettings;
  
      /** The workplace locale from the current user's settings. */
      private Locale m_wpLocale;
  
      /**
       * Converts the gallery mode to the key used for storing the last used search order
       *
       * @param mode the gallery mode
       * @return the key to use for storing the last used search order
       */
      public static String convertModeToResultOrderKey(GalleryMode mode) {
  
          String key = null;
          switch (mode) {
              case ade:
                  key = RESULT_ORDER_KEY_PAGE;
                  break;
              case adeView:
              case view:
                  key = RESULT_ORDER_KEY_OTHER;
                  break;
              default:
                  return null;
  
          }
          return key;
  
      }
  
      /**
       * Generates the pre-loaded contents for the VFS tab of the gallery dialog.<p>
       *
       * @param cms the current CMS context
       * @param vfsState the saved VFS tree state (may be null)
       * @param folders the saved search folders (may be null)
       *
       * @return the root tree entry for the VFS tab
       */
      public static CmsVfsEntryBean generateVfsPreloadData(
          final CmsObject cms,
          final CmsTreeOpenState vfsState,
          final Set<String> folders) {
  
          CmsVfsEntryBean vfsPreloadData = null;
  
          A_CmsTreeTabDataPreloader<CmsVfsEntryBean> vfsloader = new A_CmsTreeTabDataPreloader<CmsVfsEntryBean>() {
  
              @SuppressWarnings("synthetic-access")
              @Override
              protected CmsVfsEntryBean createEntry(CmsObject innerCms, CmsResource resource) throws CmsException {
  
                  String title = innerCms.readPropertyObject(
                      resource,
                      CmsPropertyDefinition.PROPERTY_TITLE,
                      false).getValue();
                  boolean isEditable = false;
                  try {
                      isEditable = innerCms.hasPermissions(
                          resource,
                          CmsPermissionSet.ACCESS_WRITE,
                          false,
                          CmsResourceFilter.ALL);
                  } catch (CmsException e) {
                      LOG.info(e.getLocalizedMessage(), e);
                  }
  
                  return internalCreateVfsEntryBean(innerCms, resource, title, true, isEditable, null, false);
              }
  
          };
          Set<CmsResource> treeOpenResources = Sets.newHashSet();
          if (vfsState != null) {
  
              for (CmsUUID structureId : vfsState.getOpenItems()) {
                  try {
                      treeOpenResources.add(cms.readResource(structureId, CmsResourceFilter.ONLY_VISIBLE_NO_DELETED));
                  } catch (CmsException e) {
                      LOG.warn(e.getLocalizedMessage(), e);
                  }
              }
          }
          CmsObject rootCms = null;
          Set<CmsResource> folderSetResources = Sets.newHashSet();
          try {
              rootCms = OpenCms.initCmsObject(cms);
              rootCms.getRequestContext().setSiteRoot("");
              if (!((folders == null) || folders.isEmpty())) {
                  for (String folder : folders) {
                      try {
                          folderSetResources.add(rootCms.readResource(folder, CmsResourceFilter.ONLY_VISIBLE_NO_DELETED));
                      } catch (CmsException e) {
                          LOG.warn(e.getLocalizedMessage(), e);
                      }
                  }
              }
          } catch (CmsException e1) {
              LOG.error(e1.getLocalizedMessage(), e1);
          }
          try {
              vfsPreloadData = vfsloader.preloadData(cms, treeOpenResources, folderSetResources);
          } catch (CmsException e) {
              LOG.error(e.getLocalizedMessage(), e);
          }
  
          return vfsPreloadData;
  
      }
  
      /**
       * Returns the initial gallery settings.<p>
       *
       * @param request the current request
       * @param config the gallery configuration
       *
       * @return the initial gallery settings
       *
       * @throws CmsRpcException if something goes wrong
       */
      public static CmsGalleryDataBean getInitialSettings(HttpServletRequest request, CmsGalleryConfiguration config)
      throws CmsRpcException {
  
          CmsGalleryService srv = new CmsGalleryService();
          srv.setCms(CmsFlexController.getCmsObject(request));
          srv.setRequest(request);
          CmsGalleryDataBean result = null;
          try {
              result = srv.getInitialSettings(config);
          } finally {
              srv.clearThreadStorage();
          }
          return result;
      }
  
      /**
       * Returns the initial search data.<p>
       *
       * @param request the current request
       * @param config the gallery configuration
       *
       * @return the search data
       */
      public static CmsGallerySearchBean getSearch(HttpServletRequest request, CmsGalleryDataBean config) {
  
          CmsGalleryService srv = new CmsGalleryService();
          srv.setCms(CmsFlexController.getCmsObject(request));
          srv.setRequest(request);
          CmsGallerySearchBean result = null;
          try {
              result = srv.getSearch(config);
          } finally {
              srv.clearThreadStorage();
          }
          return result;
      }
  
      /**
       * Gets the attribute name for a tree open state.<p>
       *
       * @param treeName the tree name
       * @param treeToken the tree token
       *
       * @return the attribute name for the tree
       */
      public static String getTreeOpenStateAttributeName(String treeName, String treeToken) {
  
          return "tree_" + treeName + "_" + treeToken;
      }
  
      /**
       * Convenience method for reading the saved VFS tree state from the session.<p>
       *
       * @param request the current request
       * @param treeToken the tree token (may be null)
       *
       * @return the saved tree open state (may be null)
       */
      public static CmsTreeOpenState getVfsTreeState(HttpServletRequest request, String treeToken) {
  
          return (CmsTreeOpenState)request.getSession().getAttribute(
              getTreeOpenStateAttributeName(I_CmsGalleryProviderConstants.TREE_VFS, treeToken));
  
      }
  
      /**
       * Creates the VFS entry bean for a resource.<p>
       *
       * @param cms the CMS context to use
       * @param resource the resource for which to create the VFS entry bean
       * @param title the title
       * @param isRoot true if this is a root entry
       * @param isEditable true if this entry is editable
       * @param children the children of the entry
       * @param isMatch true if the VFS entry bean is a match for the quick search
       *
       * @return the created VFS entry bean
       * @throws CmsException if something goes wrong
       */
      public static CmsVfsEntryBean internalCreateVfsEntryBean(
          CmsObject cms,
          CmsResource resource,
          String title,
          boolean isRoot,
          boolean isEditable,
          List<CmsVfsEntryBean> children,
          boolean isMatch)
      throws CmsException {
  
          String rootPath = resource.getRootPath();
          CmsUUID structureId = resource.getStructureId();
          CmsVfsEntryBean result = new CmsVfsEntryBean(
              rootPath,
              structureId,
              title,
              CmsIconUtil.getIconClasses(CmsIconUtil.getDisplayType(cms, resource), resource.getName(), true),
              isRoot,
              isEditable,
              children,
              isMatch);
          String siteRoot = null;
          if (resource.isFolder()) {
              cms = OpenCms.initCmsObject(cms);
              cms.getRequestContext().setSiteRoot("");
              List<CmsResource> realChildren = cms.getResourcesInFolder(
                  rootPath,
                  CmsResourceFilter.ONLY_VISIBLE_NO_DELETED);
              List<CmsResource> effectiveChildren = new ArrayList<CmsResource>();
              for (CmsResource realChild : realChildren) {
                  if (realChild.isFolder()) {
                      effectiveChildren.add(realChild);
                  }
              }
              if (effectiveChildren.isEmpty()) {
                  result.setChildren(new ArrayList<CmsVfsEntryBean>());
              }
          }
  
          if (OpenCms.getSiteManager().startsWithShared(rootPath)) {
              siteRoot = OpenCms.getSiteManager().getSharedFolder();
          } else {
              String tempSiteRoot = OpenCms.getSiteManager().getSiteRoot(rootPath);
              if (tempSiteRoot != null) {
                  siteRoot = tempSiteRoot;
              } else {
                  siteRoot = "";
              }
          }
          result.setSiteRoot(siteRoot);
          return result;
      }
  
      /**
       * Helper function for getting the IDs of functions which do not fit into a given set of container type/width combinations.
       *
       * @param cms the current CMS context
       * @param config the sitemap configuration
       * @param containerInfo the information about available container type/width combinations
       *
       * @return the set of functions which should be excluded
       */
      private static Set<CmsUUID> getExcludedFunctionsForContainerInfo(
          CmsObject cms,
          CmsADEConfigData config,
          CmsGalleryContainerInfo containerInfo) {
  
          CmsFormatterConfigurationCacheState formatterCache = config.getCachedFormatters();
          // start with all functions, then go through each item in the container info and remove formatters if they match that item
          // -> we end up with the functions that don't match anything
          Map<CmsUUID, I_CmsFormatterBean> remainingUnmatchedFunctions = new HashMap<>(
              Maps.filterValues(
                  formatterCache.getFormatters(),
                  formatter -> formatter instanceof CmsFunctionFormatterBean));
          for (CmsGalleryContainerInfo.Item item : containerInfo.getItems()) {
              Iterator<Map.Entry<CmsUUID, I_CmsFormatterBean>> iterator = remainingUnmatchedFunctions.entrySet().iterator();
              while (iterator.hasNext()) {
                  Map.Entry<CmsUUID, I_CmsFormatterBean> entry = iterator.next();
                  I_CmsFormatterBean formatter = entry.getValue();
                  if (CmsFormatterConfiguration.matchFormatter(formatter, item.getType(), item.getWidth())) {
                      iterator.remove();
                  }
              }
          }
          return new TreeSet<>(remainingUnmatchedFunctions.keySet());
      }
  
      /**
       * Returns the resource types beans.<p>
       *
       * @param resourceTypes the resource types
       * @param creatableTypes the creatable types
       * @param deactivatedTypes the deactivated types
       * @param typesForTypeTab the types which should be shown in the types tab according to the gallery configuration
       *
       * @return the resource types
       */
      public List<CmsResourceTypeBean> buildTypesList(
          List<I_CmsResourceType> resourceTypes,
          Set<String> creatableTypes,
          Set<String> deactivatedTypes,
          final List<String> typesForTypeTab) {
  
          List<CmsResourceTypeBean> result = buildTypesList(resourceTypes, creatableTypes);
  
          for (CmsResourceTypeBean typeBean : result) {
              if ((typesForTypeTab != null) && (typesForTypeTab.size() > 0)) {
                  if (!typesForTypeTab.contains(typeBean.getType())) {
                      if (typeBean.getVisibility() != TypeVisibility.hidden) {
                          typeBean.setVisibility(TypeVisibility.showOptional);
                      }
                  }
              }
              typeBean.setDeactivated(deactivatedTypes.contains(typeBean.getType()));
          }
          if ((typesForTypeTab != null) && (typesForTypeTab.size() > 0)) {
              Collections.sort(result, new Comparator<CmsResourceTypeBean>() {
  
                  public int compare(CmsResourceTypeBean first, CmsResourceTypeBean second) {
  
                      return ComparisonChain.start().compare(searchTypeRank(first), searchTypeRank(second)).compare(
                          first.getTitle(),
                          second.getTitle(),
                          Collator.getInstance(getWorkplaceLocale())).compare(first.getType(), second.getType()).result();
                  }
  
                  int searchTypeRank(CmsResourceTypeBean type) {
  
                      int index = typesForTypeTab.indexOf(type.getType());
                      if (index == -1) {
                          return Integer.MAX_VALUE;
                      } else {
                          return index;
                      }
                  }
              });
  
          }
          return result;
      }
  
      /**
       * @see org.opencms.ade.galleries.shared.rpc.I_CmsGalleryService#deleteResource(java.lang.String)
       */
      public void deleteResource(String resourcePath) throws CmsRpcException {
  
          try {
              ensureLock(resourcePath);
              getCmsObject().deleteResource(resourcePath, CmsResource.DELETE_PRESERVE_SIBLINGS);
          } catch (Exception e) {
              error(e);
          }
      }
  
      /**
       * @see org.opencms.ade.galleries.shared.rpc.I_CmsGalleryService#getAdeViewModeConfiguration()
       */
      public CmsGalleryConfiguration getAdeViewModeConfiguration() {
  
          CmsGalleryConfiguration result = new CmsGalleryConfiguration();
          List<String> typeNames = new ArrayList<String>();
  
          for (I_CmsResourceType type : OpenCms.getResourceManager().getResourceTypes()) {
              Class<?> typeClass = type.getClass();
              if (CmsResourceTypeXmlContent.class.isAssignableFrom(typeClass)
                  || CmsResourceTypeXmlPage.class.isAssignableFrom(typeClass)) {
                  continue;
              }
              if (type.getGalleryTypes().size() > 0) {
                  typeNames.add(type.getTypeName());
              }
          }
          result.setSearchTypes(typeNames);
          result.setResourceTypes(typeNames);
          result.setGalleryMode(GalleryMode.adeView);
          result.setGalleryStoragePrefix("" + GalleryMode.adeView);
          result.setTabConfiguration(CmsGalleryTabConfiguration.resolve("selectDoc"));
          return result;
      }
  
      /**
       * @see org.opencms.ade.galleries.shared.rpc.I_CmsGalleryService#getGalleries(java.lang.String, java.util.List)
       */
      public List<CmsGalleryFolderBean> getGalleries(String context, List<String> resourceTypes) {
  
          return buildGalleriesList(context, readGalleryInfosByTypeNames(resourceTypes));
      }
  
      /**
       * @see org.opencms.ade.galleries.shared.rpc.I_CmsGalleryService#getGalleryActionInfo(java.lang.String)
       */
      public CmsGalleryActionInfo getGalleryActionInfo(String sitePath) throws CmsRpcException {
  
          try {
              CmsObject cms = getCmsObject();
              try {
                  CmsResource folderRes = cms.readResource(sitePath, CmsResourceFilter.IGNORE_EXPIRATION);
                  I_CmsResourceType type = OpenCms.getResourceManager().getResourceType(folderRes);
                  String action = type.getConfiguration().get("gallery.upload.action");
                  return new CmsGalleryActionInfo(folderRes.getStructureId(), action);
              } catch (CmsVfsResourceNotFoundException e) {
                  LOG.info(e.getLocalizedMessage(), e);
                  return null;
              }
          } catch (Throwable e) {
              error(e);
              return null;
          }
  
      }
  
      /**
       * @see org.opencms.ade.galleries.shared.rpc.I_CmsGalleryService#getInfoForResource(java.lang.String, java.lang.String)
       */
      public CmsResultItemBean getInfoForResource(String linkPath, String locale) throws CmsRpcException {
  
          CmsResultItemBean result = null;
          CmsObject cms = getCmsObject();
          CmsMessages messageBundle = Messages.get().getBundle(getWorkplaceLocale());
          try {
              if (new CmsUriSplitter(linkPath).getProtocol() != null) {
                  result = new CmsResultItemBean();
                  result.setTitle(messageBundle.key(Messages.GUI_EXTERNAL_LINK_0));
                  result.setSubTitle("");
                  result.setType(CmsResourceTypePointer.getStaticTypeName());
                  result.setBigIconClasses(
                      CmsIconUtil.getIconClasses(CmsResourceTypePointer.getStaticTypeName(), null, false));
              } else if (CmsStaticResourceHandler.isStaticResourceUri(linkPath)) {
                  result = new CmsResultItemBean();
                  result.setTitle(messageBundle.key(Messages.GUI_STATIC_RESOURCE_0));
                  result.setSubTitle(CmsStaticResourceHandler.removeStaticResourcePrefix(linkPath));
                  result.setType(CmsResourceTypeBinary.getStaticTypeName());
                  result.setBigIconClasses(
                      CmsIconUtil.getIconClasses(CmsResourceTypeBinary.getStaticTypeName(), null, false));
              } else {
                  boolean notFound = false;
                  String path = linkPath;
                  String siteRoot = OpenCms.getSiteManager().getSiteRoot(linkPath);
                  String oldSite = cms.getRequestContext().getSiteRoot();
                  try {
                      if (siteRoot != null) {
                          // only switch the site if needed
                          cms.getRequestContext().setSiteRoot(siteRoot);
                          // remove the site root, because the link manager call will append it anyway
                          path = cms.getRequestContext().removeSiteRoot(linkPath);
                      }
                      // remove parameters, if not the link manager call might fail
                      int pos = path.indexOf(CmsRequestUtil.URL_DELIMITER);
                      int anchorPos = path.indexOf('#');
                      if ((pos == -1) || ((anchorPos > -1) && (pos > anchorPos))) {
                          pos = anchorPos;
                      }
                      if (pos > -1) {
                          path = path.substring(0, pos);
                      }
                      // get the root path
                      path = OpenCms.getLinkManager().getRootPath(cms, path);
  
                  } catch (Exception e) {
                      notFound = true;
                  } finally {
                      if (siteRoot != null) {
                          cms.getRequestContext().setSiteRoot(oldSite);
                      }
                  }
                  notFound = notFound || (path == null);
                  boolean isInTimeRange = true;
                  if (!notFound) {
                      CmsObject rootCms = OpenCms.initCmsObject(cms);
                      rootCms.getRequestContext().setSiteRoot("");
                      try {
                          CmsResource selectedResource = rootCms.readResource(path, CmsResourceFilter.IGNORE_EXPIRATION);
                          long currentTime = System.currentTimeMillis();
                          isInTimeRange = selectedResource.isReleasedAndNotExpired(currentTime);
                          if (selectedResource.isFolder()) {
                              result = new CmsResultItemBean();
  
                              CmsJspNavElement folderNav = new CmsJspNavBuilder(rootCms).getNavigationForResource(
                                  selectedResource.getRootPath(),
                                  CmsResourceFilter.IGNORE_EXPIRATION);
                              CmsResource defaultFileResource = null;
                              if (folderNav.isInNavigation() && !folderNav.isNavigationLevel()) {
                                  try {
                                      defaultFileResource = rootCms.readDefaultFile(
                                          selectedResource,
                                          CmsResourceFilter.ONLY_VISIBLE);
                                  } catch (Exception e) {
                                      log(e.getMessage(), e);
                                  }
                              }
                              result.setBigIconClasses(
                                  CmsIconUtil.getIconClasses(
                                      CmsIconUtil.getDisplayType(rootCms, selectedResource),
                                      path,
                                      false));
                              CmsResource resourceForType = defaultFileResource != null
                              ? defaultFileResource
                              : selectedResource;
                              result.setType(OpenCms.getResourceManager().getResourceType(resourceForType).getTypeName());
                              if (defaultFileResource != null) {
                                  result.setSmallIconClasses(
                                      CmsIconUtil.getIconClasses(
                                          CmsIconUtil.getDisplayType(rootCms, defaultFileResource),
                                          defaultFileResource.getName(),
                                          true));
                              }
                              String title = folderNav.getProperty(CmsPropertyDefinition.PROPERTY_NAVTEXT);
                              if (CmsStringUtil.isEmptyOrWhitespaceOnly(title)) {
                                  title = folderNav.getTitle();
                              } else if (CmsStringUtil.isEmptyOrWhitespaceOnly(title)) {
                                  title = CmsResource.getName(path);
                                  if (title.contains("/")) {
                                      title = title.substring(0, title.indexOf("/"));
                                  }
                              }
                              result.setTitle(title);
                              try {
                                  String userName = cms.readUser(selectedResource.getUserLastModified()).getFullName();
                                  result.setUserLastModified(userName);
                              } catch (CmsException e) {
                                  log(e.getMessage(), e);
                              }
                              Date date = new Date(selectedResource.getDateLastModified());
                              String formattedDate = CmsDateUtil.getDateTime(
                                  date,
                                  DateFormat.MEDIUM,
                                  getWorkplaceLocale());
                              result.setDateLastModified(formattedDate);
  
                          } else {
                              CmsGallerySearchResult resultItem = null;
                              try {
                                  resultItem = CmsGallerySearch.searchByPath(
                                      cms,
                                      path,
                                      CmsLocaleManager.getLocale(locale));
                              } catch (CmsVfsResourceNotFoundException ex) {
                                  // ignore
                              }
                              notFound = resultItem == null;
                              if (!notFound) {
                                  result = buildSingleSearchResultItem(getCmsObject(), resultItem, null, res -> false);
                              }
                          }
                      } catch (CmsException ex) {
                          notFound = true;
                      }
                  }
                  if (notFound) {
                      result = new CmsResultItemBean();
                      result.setTitle(messageBundle.key(Messages.GUI_RESOURCE_NOT_FOUND_0));
                      result.setSubTitle("");
                      result.setType(CmsIconUtil.TYPE_RESOURCE_NOT_FOUND);
                      result.setBigIconClasses(
                          CmsIconUtil.getIconClasses(CmsIconUtil.TYPE_RESOURCE_NOT_FOUND, null, false));
                  } else if (!isInTimeRange && (result != null)) {
                      result.setType(CmsIconUtil.TYPE_RESOURCE_NOT_FOUND);
                      result.setTitle(messageBundle.key(Messages.GUI_RESOURCE_OUT_OF_TIME_RANGE_1, result.getTitle()));
                      result.setBigIconClasses(
                          CmsIconUtil.getIconClasses(CmsIconUtil.TYPE_RESOURCE_NOT_FOUND, null, false));
                  }
              }
          } catch (Throwable t) {
              error(t);
          }
          return result;
      }
  
      /**
       * @see org.opencms.ade.galleries.shared.rpc.I_CmsGalleryService#getInitialSettings(org.opencms.ade.galleries.shared.CmsGalleryConfiguration)
       */
      public CmsGalleryDataBean getInitialSettings(CmsGalleryConfiguration conf) throws CmsRpcException {
  
          try {
              return getInitialSettingsInternal(conf);
          } catch (Throwable t) {
              error(t);
              return null; // will never be reached
          }
      }
  
      /**
       * Returns the initial gallery data for the container page editor.<p>
       *
       * @param types the available resource types
       * @param uri the page URI
       * @param locale the content locale
       *
       * @return the gallery data
       */
      public CmsGalleryDataBean getInitialSettingsForContainerPage(
          List<CmsResourceTypeBean> types,
          String uri,
          String locale) {
  
          CmsGalleryDataBean data = null;
          try {
              data = new CmsGalleryDataBean();
              boolean galleryShowInvalidDefault = Boolean.parseBoolean(
                  getWorkplaceSettings().getUserSettings().getAdditionalPreference(
                      PREF_GALLERY_SHOW_INVALID_DEFAULT,
                      true));
              data.setIncludeExpiredDefault(galleryShowInvalidDefault);
              data.setResultViewType(readResultViewType());
              data.setMode(GalleryMode.ade);
              data.setGalleryStoragePrefix("");
              data.setLocales(buildLocalesMap());
              data.setLocale(locale);
  
              data.setVfsRootFolders(getRootEntries());
  
              data.setScope(getWorkplaceSettings().getLastSearchScope());
              data.setSortOrder(
                  getWorkplaceSettings().getLastGalleryResultOrder(CmsGalleryService.RESULT_ORDER_KEY_PAGE));
  
              data.setTabIds(GalleryMode.ade.getTabs());
              data.setReferenceSitePath(uri);
              data.setTypes(types);
              Map<String, CmsGalleryTypeInfo> adeGalleryTypeInfos = readGalleryInfosByTypeBeans(types);
              data.setGalleries(buildGalleriesList(uri, adeGalleryTypeInfos));
              data.setStartTab(GalleryTabId.cms_tab_types);
              Set<String> folderFilter = readFolderFilters();
              data.setStartFolderFilter(folderFilter);
              if ((folderFilter != null) && !folderFilter.isEmpty()) {
                  try {
                      data.setVfsPreloadData(generateVfsPreloadData(getCmsObject(), null, folderFilter));
                  } catch (Exception e) {
                      LOG.error(e.getLocalizedMessage(), e);
                  }
              }
              CmsSiteSelectorOptionBuilder optionBuilder = new CmsSiteSelectorOptionBuilder(getCmsObject());
              optionBuilder.addNormalSites(true, getWorkplaceSettings().getUserSettings().getStartFolder());
              optionBuilder.addSharedSite();
              data.setVfsSiteSelectorOptions(optionBuilder.getOptions());
  
              CmsSiteSelectorOptionBuilder sitemapOptionBuilder = new CmsSiteSelectorOptionBuilder(getCmsObject());
              sitemapOptionBuilder.addNormalSites(false, null);
              if (data.getReferenceSitePath() != null) {
                  sitemapOptionBuilder.addCurrentSubsite(getCmsObject().addSiteRoot(data.getReferenceSitePath()));
              }
              data.setSitemapSiteSelectorOptions(sitemapOptionBuilder.getOptions());
              data.setDefaultScope(OpenCms.getWorkplaceManager().getGalleryDefaultScope());
              CmsCoreService service = new CmsCoreService();
              service.setCms(getCmsObject());
              data.setCategories(service.getCategoriesForSitePath(data.getReferenceSitePath()));
          } catch (Exception e) {
              LOG.error(e.getLocalizedMessage(), e);
          }
          return data;
      }
  
      /**
       * @see org.opencms.ade.galleries.shared.rpc.I_CmsGalleryService#getSearch(org.opencms.ade.galleries.shared.CmsGalleryDataBean)
       */
      public CmsGallerySearchBean getSearch(CmsGalleryDataBean data) {
  
          CmsGallerySearchBean result = null;
          // search within all available types
          List<String> types = getTypeNames(data);
          switch (data.getMode()) {
              case editor:
              case view:
              case adeView:
              case widget:
                  String currentelement = data.getCurrentElement();
                  try {
                      CmsSitemapEntryBean sitemapPreloadData = null;
                      CmsVfsEntryBean vfsPreloadData = null;
                      boolean disablePreview = false;
                      GalleryTabId startTab = null;
                      if (CmsStringUtil.isNotEmptyOrWhitespaceOnly(currentelement)) {
                          log("looking up:" + currentelement);
                          // removing the servlet context if present
                          currentelement = CmsLinkManager.removeOpenCmsContext(currentelement);
                          // get search results given resource path
                          result = findResourceInGallery(currentelement, data);
                          if (result != null) {
                              sitemapPreloadData = result.getSitemapPreloadData();
                              vfsPreloadData = result.getVfsPreloadData();
                          }
                          if ((sitemapPreloadData != null)
                              && data.getTabConfiguration().getTabs().contains(GalleryTabId.cms_tab_sitemap)) {
                              startTab = GalleryTabId.cms_tab_sitemap;
                              disablePreview = true;
                          }
                      } else {
                          CmsTreeOpenState sitemapState = getSitemapTreeState(data.getTreeToken());
                          if (sitemapState != null) {
                              A_CmsTreeTabDataPreloader<CmsSitemapEntryBean> sitemaploader = new A_CmsTreeTabDataPreloader<CmsSitemapEntryBean>() {
  
                                  @Override
                                  protected CmsSitemapEntryBean createEntry(CmsObject cms, CmsResource resource)
                                  throws CmsException {
  
                                      return internalCreateSitemapEntryBean(cms, resource);
                                  }
  
                                  /**
                                   * @see org.opencms.ade.galleries.A_CmsTreeTabDataPreloader#getChildren(org.opencms.file.CmsResource)
                                   */
                                  @Override
                                  protected List<CmsResource> getChildren(CmsResource resource) throws CmsException {
  
                                      return getSitemapSubEntryResources(resource.getRootPath());
                                  }
  
                              };
                              // in the case
                              sitemapPreloadData = sitemaploader.preloadData(
                                  getCmsObject(),
                                  Sets.newHashSet(
                                      readAll(sitemapState.getOpenItems(), CmsResourceFilter.ONLY_VISIBLE_NO_DELETED)),
                                  null);
                          }
                      }
                      if ((result == null) || (result.getResults() == null) || result.getResults().isEmpty()) {
                          result = new CmsGallerySearchBean();
                          result.setOriginalGalleryData(data);
                          result.setGalleryMode(data.getMode());
                          result.setGalleryStoragePrefix(data.getGalleryStoragePrefix());
                          result.setIgnoreSearchExclude(true);
                          String gallery = data.getStartGallery();
                          if (CmsStringUtil.isNotEmptyOrWhitespaceOnly(gallery)) {
                              List<String> galleries = new ArrayList<String>();
                              galleries.add(gallery);
                              result.setGalleries(galleries);
                          }
                          if (CmsStringUtil.isNotEmptyOrWhitespaceOnly(data.getStartFolder())) {
                              Set<String> folders = new HashSet<String>();
                              folders.add(data.getStartFolder());
                              result.setFolders(folders);
                          } else if (data.getStartFolderFilter() != null) {
                              result.setFolders(data.getStartFolderFilter());
                          }
                          result.setTypes(types);
                          result.setLocale(data.getLocale());
                          CmsGallerySearchScope scope = data.getScope();
                          if (scope == null) {
                              scope = OpenCms.getWorkplaceManager().getGalleryDefaultScope();
                          }
                          result.setSortOrder(data.getSortOrder().name());
                          result.setScope(scope);
                          result.setIncludeExpired(data.getIncludeExpiredDefault());
                          result = search(result);
                      }
                      result.setSitemapPreloadData(sitemapPreloadData);
                      result.setVfsPreloadData(vfsPreloadData);
                      result.setInitialTabId(startTab);
                      result.setDisablePreview(disablePreview);
                      if (types.size() > 1) {
                          // only remove types parameter if there is more than one type available
                          result.setTypes(null);
                      }
                  } catch (CmsException e) {
                      logError(e);
                      result = null;
                  }
                  break;
              case ade:
              default:
                  break;
          }
          return result;
      }
  
      /**
       * @see org.opencms.ade.galleries.shared.rpc.I_CmsGalleryService#getSearch(CmsGallerySearchBean)
       */
      public CmsGallerySearchBean getSearch(CmsGallerySearchBean searchObj) throws CmsRpcException {
  
          CmsGallerySearchBean gSearchObj = null;
  
          try {
              gSearchObj = search(searchObj);
              getWorkplaceSettings().setLastSearchScope(searchObj.getScope());
              String resultOrderKey = convertModeToResultOrderKey(searchObj.getGalleryMode());
              getWorkplaceSettings().setLastGalleryResultOrder(
                  resultOrderKey,
                  SortParams.valueOf(searchObj.getSortOrder()));
              setLastOpenedGallery(searchObj);
          } catch (Throwable e) {
              error(e);
          }
  
          return gSearchObj;
      }
  
      /**
       * @see org.opencms.ade.galleries.shared.rpc.I_CmsGalleryService#getSubEntries(java.lang.String, boolean, java.lang.String)
       */
      public List<CmsSitemapEntryBean> getSubEntries(String rootPath, boolean isRoot, String filter)
      throws CmsRpcException {
  
          try {
              return getSubEntriesInternal(rootPath, isRoot, filter);
          } catch (Throwable e) {
              error(e);
          }
          return null;
      }
  
      /**
       * @see org.opencms.ade.galleries.shared.rpc.I_CmsGalleryService#getSubFolders(java.lang.String)
       */
      public List<CmsVfsEntryBean> getSubFolders(String rootPath) throws CmsRpcException {
  
          try {
              CmsObject cms = OpenCms.initCmsObject(getCmsObject());
              cms.getRequestContext().setSiteRoot("");
              List<CmsVfsEntryBean> result = new ArrayList<CmsVfsEntryBean>();
              if (cms.existsResource(rootPath, CmsResourceFilter.ONLY_VISIBLE_NO_DELETED)) {
                  CmsResource resource = cms.readResource(rootPath, CmsResourceFilter.ONLY_VISIBLE_NO_DELETED);
                  List<CmsResource> resources = cms.getSubFolders(
                      resource.getRootPath(),
                      CmsResourceFilter.ONLY_VISIBLE_NO_DELETED);
                  for (CmsResource res : resources) {
                      String title = cms.readPropertyObject(res, CmsPropertyDefinition.PROPERTY_TITLE, false).getValue();
                      result.add(
                          internalCreateVfsEntryBean(
                              getCmsObject(),
                              res,
                              title,
                              false,
                              isEditable(cms, res),
                              null,
                              false));
                  }
              }
              Collections.<CmsVfsEntryBean> sort(result, VFS_COMPARATOR);
              return result;
          } catch (Throwable e) {
              error(e);
          }
          return null;
      }
  
      /**
       * @see org.opencms.ade.galleries.shared.rpc.I_CmsGalleryService#loadVfsEntryBean(java.lang.String, java.lang.String)
       */
      public CmsVfsEntryBean loadVfsEntryBean(String path, String filter) throws CmsRpcException {
  
          try {
              if (CmsStringUtil.isEmpty(filter)) {
  
                  CmsObject cms = OpenCms.initCmsObject(getCmsObject());
                  cms.getRequestContext().setSiteRoot("");
                  if (!cms.existsResource(path, CmsResourceFilter.ONLY_VISIBLE_NO_DELETED)) {
                      String startFolder = CmsStringUtil.joinPaths(
                          path,
                          getWorkplaceSettings().getUserSettings().getStartFolder());
                      if (cms.existsResource(startFolder, CmsResourceFilter.ONLY_VISIBLE_NO_DELETED)) {
                          path = startFolder;
                      }
                  }
                  CmsResource optionRes = cms.readResource(path);
                  String title = cms.readPropertyObject(
                      optionRes,
                      CmsPropertyDefinition.PROPERTY_TITLE,
                      false).getValue();
                  if (CmsStringUtil.isEmpty(title)) {
                      title = path;
                  }
                  CmsVfsEntryBean entryBean = internalCreateVfsEntryBean(
                      getCmsObject(),
                      optionRes,
                      title,
                      true,
                      isEditable(cms, optionRes),
                      null,
                      false);
                  return entryBean;
              } else {
                  filter = filter.toLowerCase();
                  CmsObject cms = OpenCms.initCmsObject(getCmsObject());
                  cms.getRequestContext().setSiteRoot("");
                  if (!cms.existsResource(path, CmsResourceFilter.ONLY_VISIBLE_NO_DELETED)) {
                      String startFolder = CmsStringUtil.joinPaths(
                          path,
                          getWorkplaceSettings().getUserSettings().getStartFolder());
                      if (cms.existsResource(startFolder, CmsResourceFilter.ONLY_VISIBLE_NO_DELETED)) {
                          path = startFolder;
                      }
                  }
                  CmsResource optionRes = cms.readResource(path);
                  List<CmsResource> folders = cms.readResources(
                      optionRes.getRootPath(),
                      CmsResourceFilter.ONLY_VISIBLE_NO_DELETED.addRequireFolder());
                  folders.add(optionRes);
                  Set<CmsResource> folderSet = Sets.newHashSet(folders);
                  List<CmsResource> titleResources = cms.readResourcesWithProperty(
                      path,
                      CmsPropertyDefinition.PROPERTY_TITLE);
                  titleResources.retainAll(folderSet);
                  Set<CmsResource> filterMatches = Sets.newHashSet();
                  for (CmsResource folder : folderSet) {
                      if (folder.getName().toLowerCase().contains(filter)) {
                          filterMatches.add(folder);
                          titleResources.remove(folder); // we don't need to check the title if the name already matched
                      }
                  }
                  for (CmsResource titleRes : titleResources) {
                      CmsProperty prop = cms.readPropertyObject(titleRes, CmsPropertyDefinition.PROPERTY_TITLE, false);
                      String title = prop.getValue();
                      if ((title != null) && title.toLowerCase().contains(filter)) {
                          filterMatches.add(titleRes);
                      }
                  }
                  Set<String> filterMatchAncestorPaths = Sets.newHashSet();
                  if (filterMatches.size() > 0) {
                      for (CmsResource filterMatch : filterMatches) {
                          String currentPath = filterMatch.getRootPath();
                          while (currentPath != null) {
                              filterMatchAncestorPaths.add(currentPath);
                              currentPath = CmsResource.getParentFolder(currentPath);
                          }
                      }
                      Set<String> allPaths = Sets.newHashSet();
                      Set<String> parentPaths = Sets.newHashSet();
                      for (CmsResource folder : folderSet) {
                          allPaths.add(folder.getRootPath());
                          String parent = CmsResource.getParentFolder(folder.getRootPath());
                          if (parent != null) {
                              parentPaths.add(parent);
                          }
                      }
                      parentPaths.retainAll(allPaths);
  
                      Set<CmsResource> filterMatchAncestors = Sets.newHashSet();
                      for (CmsResource folderRes : folderSet) {
                          if (filterMatchAncestorPaths.contains(folderRes.getRootPath())) {
                              filterMatchAncestors.add(folderRes);
                          }
                      }
                      Map<String, CmsResource> resourcesByPath = Maps.newHashMap();
                      for (CmsResource treeRes : filterMatchAncestors) {
                          resourcesByPath.put(treeRes.getRootPath(), treeRes);
                      }
                      Multimap<CmsResource, CmsResource> childMap = ArrayListMultimap.create();
                      for (CmsResource res : filterMatchAncestors) {
                          CmsResource parent = resourcesByPath.get(CmsResource.getParentFolder(res.getRootPath()));
                          if (parent != null) {
                              childMap.put(parent, res);
                          }
                      }
                      return buildVfsEntryBeanForQuickSearch(optionRes, childMap, filterMatches, parentPaths, true);
                  } else {
                      return null;
                  }
              }
          } catch (Throwable e) {
              error(e);
              return null;
          }
      }
  
      /**
       * @see org.opencms.ade.galleries.shared.rpc.I_CmsGalleryService#saveResultViewType(java.lang.String)
       */
      public void saveResultViewType(String resultViewType) {
  
          CmsUser user = getCmsObject().getRequestContext().getCurrentUser();
          user.setAdditionalInfo(RESULT_VIEW_TYPE_ADD_INFO_KEY, resultViewType);
          try {
              getCmsObject().writeUser(user);
          } catch (CmsException e) {
              LOG.error(e.getLocalizedMessage(), e);
          }
      }
  
      /**
       * @see org.opencms.ade.galleries.shared.rpc.I_CmsGalleryService#saveTreeOpenState(java.lang.String, java.lang.String, java.lang.String, java.util.Set)
       */
      public void saveTreeOpenState(String treeName, String treeToken, String siteRoot, Set<CmsUUID> openItems)
      throws CmsRpcException {
  
          try {
              HttpServletRequest request = getRequest();
              HttpSession session = request.getSession();
              String attributeName = getTreeOpenStateAttributeName(treeName, treeToken);
              if (openItems.isEmpty()) {
                  CmsObject cms = OpenCms.initCmsObject(getCmsObject());
                  cms.getRequestContext().setSiteRoot("");
                  CmsResource resource = cms.readResource(siteRoot);
                  openItems = Sets.newHashSet(resource.getStructureId());
              }
              CmsTreeOpenState treeState = new CmsTreeOpenState(treeName, siteRoot, openItems);
              session.setAttribute(attributeName, treeState);
          } catch (Throwable e) {
              error(e);
          }
      }
  
      /**
       * @see org.opencms.ade.galleries.shared.rpc.I_CmsGalleryService#updateIndex()
       */
      public void updateIndex() throws CmsRpcException {
  
          try {
              OpenCms.getSearchManager().updateOfflineIndexes();
          } catch (Throwable e) {
              error(e);
          }
      }
  
      /**
       * Gets an initialized CmsObject to be used for the actual search for a given search bean.<p>
       *
       * @param searchObj the search object
       * @return the initialized CmsObject
       *
       * @throws CmsException if something goes wrong
       */
      protected CmsObject getSearchCms(CmsGallerySearchBean searchObj) throws CmsException {
  
          CmsObject searchCms = getCmsObject();
          if (searchObj.isIncludeExpired()) {
              searchCms = OpenCms.initCmsObject(getCmsObject());
              searchCms.getRequestContext().setRequestTime(CmsResource.DATE_RELEASED_EXPIRED_IGNORE);
          }
          return searchCms;
      }
  
      /**
       * Gets the sitemap sub entries for a given path as resources.<p>
       *
       * @param rootPath the root path
       * @return the sitemap sub entry resources
       * @throws CmsException if something goes wrong
       */
      protected List<CmsResource> getSitemapSubEntryResources(String rootPath) throws CmsException {
  
          CmsObject rootCms = OpenCms.initCmsObject(getCmsObject());
          List<CmsResource> result = new ArrayList<CmsResource>();
          rootCms.getRequestContext().setSiteRoot("");
          CmsJspNavBuilder navBuilder = new CmsJspNavBuilder(rootCms);
          for (CmsJspNavElement navElement : navBuilder.getNavigationForFolder(
              rootPath,
              Visibility.all,
              CmsResourceFilter.ONLY_VISIBLE)) {
              if ((navElement != null) && navElement.isInNavigation()) {
                  result.add(navElement.getResource());
              }
          }
          return result;
      }
  
      /**
       * Internal method for getting sitemap sub entries for a given root path.<p>
       *
       * @param rootPath the root path
       * @param isRoot true if this method is used to get the root entries of a sitemap
       * @param filter the filter string (only relevant if isRoot is true)
       * @return the list of sitemap sub-entry beans
       *
       * @throws CmsException if something goes wrong
       */
      protected List<CmsSitemapEntryBean> getSubEntriesInternal(String rootPath, boolean isRoot, String filter)
      throws CmsException {
  
          CmsObject rootCms = OpenCms.initCmsObject(getCmsObject());
          rootCms.getRequestContext().setSiteRoot("");
          CmsJspNavBuilder navBuilder = new CmsJspNavBuilder(rootCms);
          if (isRoot) {
              if (CmsStringUtil.isEmpty(filter)) {
                  List<CmsSitemapEntryBean> result = new ArrayList<CmsSitemapEntryBean>();
                  for (CmsJspNavElement navElement : navBuilder.getNavigationForFolder(
                      rootPath,
                      Visibility.all,
                      CmsResourceFilter.ONLY_VISIBLE)) {
                      if ((navElement != null) && navElement.isInNavigation()) {
                          CmsSitemapEntryBean nextEntry = prepareSitemapEntry(rootCms, navElement, false, true);
                          result.add(nextEntry);
                      }
                  }
                  CmsJspNavElement navElement = navBuilder.getNavigationForResource(
                      rootPath,
                      CmsResourceFilter.ONLY_VISIBLE);
                  if (navElement == null) {
                      return result;
                  }
                  CmsSitemapEntryBean root = prepareSitemapEntry(rootCms, navElement, isRoot, true);
                  root.setChildren(result);
                  return Collections.singletonList(root);
              } else {
                  CmsGalleryFilteredNavTreeBuilder navTreeBuilder = new CmsGalleryFilteredNavTreeBuilder(
                      rootCms,
                      rootPath);
                  navTreeBuilder.initTree(filter);
                  if (navTreeBuilder.hasMatches()) {
                      return Lists.newArrayList(convertNavigationTreeToBean(rootCms, navTreeBuilder.getRoot(), true));
                  } else {
                      return Lists.newArrayList();
                  }
              }
          } else {
              List<CmsSitemapEntryBean> result = new ArrayList<CmsSitemapEntryBean>();
              for (CmsJspNavElement navElement : navBuilder.getNavigationForFolder(
                  rootPath,
                  Visibility.all,
                  CmsResourceFilter.ONLY_VISIBLE)) {
                  if ((navElement != null) && navElement.isInNavigation()) {
                      CmsSitemapEntryBean nextEntry = prepareSitemapEntry(rootCms, navElement, false, true);
                      result.add(nextEntry);
                  }
              }
              return result;
          }
      }
  
      /**
       * Gets the type names from the gallery data bean.<p>
       *
       * @param data the gallery data bean
       * @return the type names
       */
      protected List<String> getTypeNames(CmsGalleryDataBean data) {
  
          List<String> types = new ArrayList<String>();
          for (CmsResourceTypeBean info : data.getTypes()) {
              types.add(info.getType());
          }
          return types;
      }
  
      /**
       * Checks whether a given resource is a sitemap entry.<p>
       *
       * This is used for preselected entries in the gallery widget.<p>
       *
       *
       * @param cms the current CMS context
       * @param resource the resource to check
       * @return true if the resource is a sitemap entry
       *
       * @throws CmsException if something goes wrong
       */
      protected boolean isSitemapEntry(CmsObject cms, CmsResource resource) throws CmsException {
  
          CmsResourceFilter filter = CmsResourceFilter.ONLY_VISIBLE_NO_DELETED;
          List<CmsResource> ancestors = new ArrayList<CmsResource>();
          CmsResource currentResource = resource;
          String siteRoot = OpenCms.getSiteManager().getSiteRoot(resource.getRootPath());
          if (CmsStringUtil.isEmpty(siteRoot)) {
              return false;
          }
          while (true) {
              CmsResource parent = cms.readParentFolder(currentResource.getStructureId());
              if ((parent == null) || !cms.existsResource(parent.getStructureId(), filter)) {
                  break;
              }
              ancestors.add(parent);
              if (CmsStringUtil.comparePaths(siteRoot, parent.getRootPath())) {
                  break;
              }
              currentResource = parent;
          }
          Collections.reverse(ancestors);
          boolean first = true;
          for (CmsResource ancestor : ancestors) {
              if (first) {
                  if (null == OpenCms.getSiteManager().getSiteRoot(ancestor.getRootPath())) {
                      return false;
                  }
              } else {
                  if (!hasNavigationProperty(cms, ancestor)) {
                      return false;
                  }
              }
              first = false;
          }
          if (resource.isFile()) {
              if (ancestors.isEmpty()) {
                  return false;
              }
              CmsResource defaultFile = cms.readDefaultFile(ancestors.get(ancestors.size() - 1), filter);
              if (!resource.equals(defaultFile)) {
                  return false;
              }
          } else {
              if (!hasNavigationProperty(cms, resource)) {
                  return false;
              }
          }
          return true;
      }
  
      /**
       * Reads the resources for a collection of structure ids and returns the list of resources which could be read.<p>
       *
       * @param structureIds the structure ids for which we want to read the resources
       * @param filter the filter used to read the resource
       *
       * @return the list of resources for the given structure id
       */
      protected List<CmsResource> readAll(Collection<CmsUUID> structureIds, CmsResourceFilter filter) {
  
          List<CmsResource> result = new ArrayList<CmsResource>();
          for (CmsUUID id : structureIds) {
              try {
                  result.add(getCmsObject().readResource(id, filter));
              } catch (CmsException e) {
                  LOG.info(e.getLocalizedMessage(), e);
              }
          }
          return result;
      }
  
      /**
       * Gets the sitemap tree open state.<p>
       *
       * @param treeToken the tree token to use
       *
       * @return the sitemap tree open state
       */
      CmsTreeOpenState getSitemapTreeState(String treeToken) {
  
          return (CmsTreeOpenState)(getRequest().getSession().getAttribute(
              getTreeOpenStateAttributeName(I_CmsGalleryProviderConstants.TREE_SITEMAP, treeToken)));
      }
  
      /**
       * Gets the VFS tree open state.<p>
       *
       * @param treeToken the tree token
       *
       * @return the VFS tree open state
       */
      CmsTreeOpenState getVfsTreeState(String treeToken) {
  
          return (CmsTreeOpenState)(getRequest().getSession().getAttribute(
              getTreeOpenStateAttributeName(I_CmsGalleryProviderConstants.TREE_VFS, treeToken)));
      }
  
      /**
       * Returns the workplace locale from the current user's settings.<p>
       *
       * @return the workplace locale
       */
      Locale getWorkplaceLocale() {
  
          if (m_wpLocale == null) {
              m_wpLocale = OpenCms.getWorkplaceManager().getWorkplaceLocale(getCmsObject());
          }
          return m_wpLocale;
      }
  
      /**
       * Creates the sitemap entry bean for a resource.<p>
       *
       * @param cms the current CMS context
       * @param resource the resource for which the sitemap entry bean should be created
       *
       * @return the created sitemap entry bean
       *
       * @throws CmsException if something goes wrong
       */
      CmsSitemapEntryBean internalCreateSitemapEntryBean(CmsObject cms, CmsResource resource) throws CmsException {
  
          cms = OpenCms.initCmsObject(cms);
          cms.getRequestContext().setSiteRoot("");
          CmsJspNavBuilder navBuilder = new CmsJspNavBuilder(cms);
          CmsJspNavElement entry = navBuilder.getNavigationForResource(resource.getRootPath());
          if (entry == null) {
              // may be null for expired resources
              return null;
          }
          return prepareSitemapEntry(cms, entry, false, true);
      }
  
      /**
       * Checks if the current user has write permissions on the given resource.<p>
       *
       * @param cms the current cms context
       * @param resource the resource to check
       *
       * @return <code>true</code> if the current user has write permissions on the given resource
       */
      boolean isEditable(CmsObject cms, CmsResource resource) {
  
          try {
              return cms.hasPermissions(resource, CmsPermissionSet.ACCESS_WRITE, false, CmsResourceFilter.ALL);
          } catch (CmsException e) {
              return false;
          }
      }
  
      /**
       * Adds galleries for a given type.<p>
       *
       * @param galleryTypeInfos the gallery type infos
       * @param typeName the type name
       *
       * @throws CmsLoaderException if something goes wrong
       */
      @SuppressWarnings("deprecation")
      private void addGalleriesForType(Map<String, CmsGalleryTypeInfo> galleryTypeInfos, String typeName)
      throws CmsLoaderException {
  
          I_CmsResourceType contentType = getResourceManager().getResourceType(typeName);
          for (I_CmsResourceType galleryType : contentType.getGalleryTypes()) {
  
              if (galleryTypeInfos.containsKey(galleryType.getTypeName())) {
                  CmsGalleryTypeInfo typeInfo = galleryTypeInfos.get(galleryType.getTypeName());
                  typeInfo.addContentType(contentType);
              } else {
                  CmsGalleryTypeInfo typeInfo;
  
                  typeInfo = new CmsGalleryTypeInfo(
                      galleryType,
                      contentType,
                      getGalleriesByType(galleryType.getTypeId()));
  
                  galleryTypeInfos.put(galleryType.getTypeName(), typeInfo);
              }
  
          }
      }
  
      /**
       * Returns the map with the available galleries.<p>
       *
       * The map uses gallery path as the key and stores the CmsGalleriesListInfoBean as the value.<p>
       * @param contextPath the site path used as the current context
       * @param galleryTypes the galleries
       *
       * @return the map with gallery info beans
       */
      private List<CmsGalleryFolderBean> buildGalleriesList(
          String contextPath,
          Map<String, CmsGalleryTypeInfo> galleryTypes) {
  
          CmsObject cms = getCmsObject();
          Locale wpLocale = getWorkplaceLocale();
          String rootPath = cms.getRequestContext().addSiteRoot(contextPath);
          CmsADEConfigData sitemapConfig = OpenCms.getADEManager().lookupConfigurationWithCache(cms, rootPath);
          String subsitePath = sitemapConfig.getBasePath();
          CmsSite site = OpenCms.getSiteManager().getSiteForRootPath(rootPath);
  
          List<CmsGalleryFolderBean> list = new ArrayList<CmsGalleryFolderBean>();
          if (galleryTypes == null) {
              return list;
          }
          for (Entry<String, CmsGalleryTypeInfo> ent : galleryTypes.entrySet()) {
              CmsGalleryTypeInfo tInfo = ent.getValue();
              ArrayList<String> contentTypes = new ArrayList<String>();
              Iterator<I_CmsResourceType> it = tInfo.getContentTypes().iterator();
              while (it.hasNext()) {
                  contentTypes.add(String.valueOf(it.next().getTypeName()));
              }
              for (CmsResource res : tInfo.getGalleries()) {
                  CmsGalleryFolderBean bean = new CmsGalleryFolderBean();
                  String sitePath = getCmsObject().getSitePath(res);
                  String title = "";
                  try {
                      // read the gallery title
                      title = getCmsObject().readPropertyObject(
                          sitePath,
                          CmsPropertyDefinition.PROPERTY_TITLE,
                          false,
                          getWorkplaceLocale()).getValue("");
                  } catch (CmsException e) {
                      // error reading title property
                      logError(e);
                  }
                  // sitepath as gallery id
                  bean.setPath(sitePath);
                  bean.setId(res.getStructureId());
                  // content types
                  bean.setContentTypes(contentTypes);
                  // title
                  bean.setTitle(title);
                  CmsGalleryGroup group = null;
                  String groupLabel = null;
                  if ((site != null)
                      && (subsitePath != null)
                      && CmsStringUtil.isPrefixPath(subsitePath, res.getRootPath())
                      && !new CmsPath(subsitePath).equals(new CmsPath(site.getSiteRoot()))) {
                      // we are in a proper subsite, not just the site itself
                      group = CmsGalleryGroup.subsite;
  
                      String labelSuffix = cms.getRequestContext().removeSiteRoot(subsitePath);
                      try {
                          Map<String, String> properties = CmsProperty.toMap(
                              cms.readPropertyObjects(cms.getRequestContext().removeSiteRoot(subsitePath), false));
                          for (String propName : new String[] {
                              CmsPropertyDefinition.PROPERTY_TITLE,
                              CmsPropertyDefinition.PROPERTY_NAVTEXT}) {
                              String propValue = properties.get(propName);
                              if (propValue != null) {
                                  labelSuffix = propValue;
                                  break;
                              }
                          }
                      } catch (Exception e) {
                          LOG.error(e.getLocalizedMessage(), e);
                      }
                      groupLabel = Messages.get().getBundle(wpLocale).key(
                          Messages.GUI_GALLERIES_GROUP_SUBSITE_1,
                          labelSuffix);
                  } else if ((site != null) && CmsStringUtil.isPrefixPath(site.getSiteRoot(), res.getRootPath())) {
                      group = CmsGalleryGroup.site;
                      String labelSuffix;
                      labelSuffix = site.getTitle();
                      if (CmsStringUtil.isEmptyOrWhitespaceOnly(labelSuffix)) {
                          labelSuffix = site.getSiteRoot();
                      }
                      groupLabel = Messages.get().getBundle(wpLocale).key(
                          Messages.GUI_GALLERIES_GROUP_SITE_1,
                          labelSuffix);
                  } else if (CmsStringUtil.isPrefixPath(CmsResource.VFS_FOLDER_SYSTEM, res.getRootPath())
                      || OpenCms.getSiteManager().startsWithShared(res.getRootPath())) {
                      group = CmsGalleryGroup.shared;
                      groupLabel = Messages.get().getBundle(wpLocale).key(Messages.GUI_GALLERIES_GROUP_SHARED_0);
                  } else {
                      group = CmsGalleryGroup.other;
                      groupLabel = Messages.get().getBundle(wpLocale).key(Messages.GUI_GALLERIES_GROUP_OTHER_0);
                  }
                  bean.setGroup(group);
                  bean.setGroupLabel(groupLabel);
                  bean.setResourceType(tInfo.getResourceType().getTypeName());
                  bean.setUploadAction(tInfo.getResourceType().getConfiguration().get("gallery.upload.action"));
  
                  boolean isOptimizeGalleryType = Arrays.asList("imagegallery", "downloadgallery").stream().anyMatch(
                      typeName -> OpenCms.getResourceManager().matchResourceType(typeName, res.getTypeId()));
  
                  // For performance reasons, we only do a general role check for EDITOR which does not take the concrete gallery path or context path into account.
                  bean.setOptimizable(
                      OpenCms.getRoleManager().hasRole(getCmsObject(), CmsRole.EDITOR) && isOptimizeGalleryType);
  
                  bean.setEditable(isEditable(getCmsObject(), res));
                  bean.setBigIconClasses(
                      CmsIconUtil.getIconClasses(CmsIconUtil.getDisplayType(getCmsObject(), res), sitePath, false));
                  list.add(bean);
              }
          }
          return list;
      }
  
      /**
       * Returns a map with the available locales.<p>
       *
       * The map entry key is the current locale and the value the localized nice name.<p>
       *
       * @return the map representation of all available locales
       */
      private Map<String, String> buildLocalesMap() {
  
          TreeMap<String, String> localesMap = new TreeMap<String, String>();
          Iterator<Locale> it = OpenCms.getLocaleManager().getAvailableLocales().iterator();
          while (it.hasNext()) {
              Locale locale = it.next();
              localesMap.put(locale.toString(), locale.getDisplayName(getWorkplaceLocale()));
          }
          return localesMap;
      }
  
      /**
       * Returns the list of beans for the given search results.<p>
       *
       * @param searchResult the list of search results
       * @param presetResult the search result which corresponds to a preset value in the editor
       * @param checkDeactivated checks if search results should be shown as deactivated
       *
       * @return the list with the current search results
       */
      private List<CmsResultItemBean> buildSearchResultList(
          List<CmsGallerySearchResult> searchResult,
          CmsGallerySearchResult presetResult,
          Predicate<CmsResource> checkDeactivated) {
  
          ArrayList<CmsResultItemBean> list = new ArrayList<CmsResultItemBean>();
          if ((searchResult == null) || (searchResult.size() == 0)) {
              return list;
          }
          CmsObject cms = getCmsObject();
          for (CmsGallerySearchResult sResult : searchResult) {
              try {
                  CmsResultItemBean bean = buildSingleSearchResultItem(cms, sResult, presetResult, checkDeactivated);
                  list.add(bean);
              } catch (Exception e) {
                  logError(e);
              }
          }
          return list;
      }
  
      /**
       * Builds a single search result list item for the client from a server-side search result.<p>
       *
       * @param cms the current CMS context
       * @param sResult the server-side search result
       * @param presetResult the preselected result
       * @param checkDeactivated checks if search results should be shown as deactivated
       *
       * @return the client side search result item
       *
       * @throws CmsException if something goes wrong
       * @throws ParseException if date parsing fails
       */
      private CmsResultItemBean buildSingleSearchResultItem(
          CmsObject cms,
          CmsGallerySearchResult sResult,
          CmsGallerySearchResult presetResult,
          Predicate<CmsResource> checkDeactivated)
      throws CmsException, ParseException {
  
          Locale wpLocale = getWorkplaceLocale();
          CmsResultItemBean bean = new CmsResultItemBean();
          if (sResult == presetResult) {
              bean.setPreset(true);
          }
          bean.setReleasedAndNotExpired(sResult.isReleaseAndNotExpired(cms));
          String path = sResult.getPath();
          path = cms.getRequestContext().removeSiteRoot(path);
  
          // resource path as id
          bean.setPath(path);
  
          // title
          String rawTitle = CmsStringUtil.isEmptyOrWhitespaceOnly(sResult.getTitle())
          ? CmsResource.getName(sResult.getPath())
          : sResult.getTitle();
          bean.setTitle(rawTitle);
          bean.setRawTitle(rawTitle);
          // resource type
          bean.setType(sResult.getResourceType());
          CmsResource resultResource = cms.readResource(
              new CmsUUID(sResult.getStructureId()),
              CmsResourceFilter.ONLY_VISIBLE_NO_DELETED);
          bean.setBigIconClasses(
              CmsIconUtil.getIconClasses(CmsIconUtil.getDisplayType(cms, resultResource), path, false));
          String detailType = CmsResourceIcon.getDefaultFileOrDetailType(cms, resultResource);
          if (detailType != null) {
              bean.setSmallIconClasses(CmsIconUtil.getIconClasses(detailType, null, true));
          }
          // structured id
          bean.setClientId(sResult.getStructureId());
  
          CmsVfsService.addLockInfo(cms, resultResource, bean);
          String extension = CmsResource.getExtension(resultResource.getRootPath());
          String suffix = extension != null ? "." + extension : "";
  
          String permalinkId = sResult.getStructureId().toString();
          String permalink = CmsStringUtil.joinPaths(
              OpenCms.getSystemInfo().getOpenCmsContext(),
              CmsPermalinkResourceHandler.PERMALINK_HANDLER,
              permalinkId) + suffix;
  
          bean.setViewLink(permalink);
          // set nice resource type name as subtitle
          I_CmsResourceType type = OpenCms.getResourceManager().getResourceType(sResult.getResourceType());
          String resourceTypeDisplayName = CmsWorkplaceMessages.getResourceTypeName(wpLocale, type.getTypeName());
          String description = sResult.getDescription();
          if (CmsStringUtil.isNotEmptyOrWhitespaceOnly(description)) {
              bean.setDescription(description);
              bean.addAdditionalInfo(
                  Messages.get().getBundle(getWorkplaceLocale()).key(Messages.GUI_RESULT_LABEL_DESCRIPTION_0),
                  description);
              if (sResult.getResourceType().equals(CmsResourceTypeFunctionConfig.TYPE_NAME)) {
                  bean.setSubTitle(description);
              }
          } else {
              bean.setDescription(resourceTypeDisplayName);
          }
          bean.setUserLastModified(sResult.getUserLastModified());
          Date lastModDate = sResult.getDateLastModified();
          String formattedDate = lastModDate != null
          ? CmsDateUtil.getDateTime(lastModDate, DateFormat.MEDIUM, wpLocale)
          : "";
          bean.setDateLastModified(formattedDate);
          if (!type.getTypeName().equals(CmsResourceTypeImage.getStaticTypeName())) {
              bean.addAdditionalInfo(
                  Messages.get().getBundle(getWorkplaceLocale()).key(Messages.GUI_RESULT_LABEL_RESOURCE_TYPE_0),
                  resourceTypeDisplayName);
          }
          if (CmsStringUtil.isNotEmptyOrWhitespaceOnly(sResult.getExcerpt())) {
              bean.addAdditionalInfo(
                  Messages.get().getBundle(getWorkplaceLocale()).key(Messages.GUI_RESULT_LABEL_EXCERPT_0),
                  sResult.getExcerpt(),
                  CmsListInfoBean.CSS_CLASS_MULTI_LINE);
          }
          if (type instanceof CmsResourceTypeImage) {
              CmsProperty copyrightProp = cms.readPropertyObject(
                  resultResource,
                  CmsPropertyDefinition.PROPERTY_COPYRIGHT,
                  false);
              if (!copyrightProp.isNullProperty()) {
                  bean.addAdditionalInfo(
                      Messages.get().getBundle(getWorkplaceLocale()).key(Messages.GUI_RESULT_LABEL_COPYRIGHT_0),
                      copyrightProp.getValue());
              }
              CmsProperty imageDimensionProp = cms.readPropertyObject(
                  resultResource,
                  CmsPropertyDefinition.PROPERTY_IMAGE_SIZE,
                  false);
              if (!imageDimensionProp.isNullProperty()) {
                  String dimensions = imageDimensionProp.getValue();
                  dimensions = dimensions.substring(2).replace(",h:", " x ");
                  bean.setDimension(dimensions);
                  bean.addAdditionalInfo(
                      Messages.get().getBundle(getWorkplaceLocale()).key(Messages.GUI_RESULT_LABEL_DIMENSION_0),
                      dimensions);
              }
          }
  
          if (type instanceof CmsResourceTypeXmlContent) {
              CmsProperty elementModelProperty = cms.readPropertyObject(
                  resultResource,
                  CmsPropertyDefinition.PROPERTY_ELEMENT_MODEL,
                  true);
              if (!elementModelProperty.isNullProperty()) {
                  if (Boolean.valueOf(elementModelProperty.getValue()).booleanValue()) {
                      bean.setIsCopyModel(true);
                  }
              }
          }
          bean.setResourceState(resultResource.getState());
          bean.addAdditionalInfo(
              Messages.get().getBundle(getWorkplaceLocale()).key(Messages.GUI_RESULT_LABEL_SIZE_0),
              (sResult.getLength() / 1000) + " kb");
          if (lastModDate != null) {
              bean.addAdditionalInfo(
                  Messages.get().getBundle(getWorkplaceLocale()).key(Messages.GUI_RESULT_LABEL_DATE_CHANGED_0),
                  CmsDateUtil.getDate(lastModDate, DateFormat.SHORT, getWorkplaceLocale()));
          }
          if ((sResult.getDateExpired().getTime() != CmsResource.DATE_EXPIRED_DEFAULT)
              && !sResult.getDateExpired().equals(CmsSearchFieldMapping.getDefaultDateExpired())) {
              bean.addAdditionalInfo(
                  Messages.get().getBundle(getWorkplaceLocale()).key(Messages.GUI_RESULT_LABEL_DATE_EXPIRED_0),
                  CmsDateUtil.getDate(sResult.getDateExpired(), DateFormat.SHORT, getWorkplaceLocale()));
          }
  
          bean.setNoEditReson(
              new CmsResourceUtil(cms, resultResource).getNoEditReason(
                  OpenCms.getWorkplaceManager().getWorkplaceLocale(cms)));
          bean.setMarkChangedState(true);
          if (checkDeactivated.test(resultResource)) {
              bean.setDeactivated(true);
          }
          return bean;
      }
  
      /**
       * Generates a map with all available content types.<p>
       *
       * The map uses resource type name as the key and stores the CmsTypesListInfoBean as the value.
       *
       * @param types the resource types
       * @param creatableTypes the creatable types
       *
       * @return the map containing the available resource types
       */
      private List<CmsResourceTypeBean> buildTypesList(List<I_CmsResourceType> types, Set<String> creatableTypes) {
  
          ArrayList<CmsResourceTypeBean> list = new ArrayList<CmsResourceTypeBean>();
          if (types == null) {
              return list;
          }
          Map<I_CmsResourceType, I_CmsPreviewProvider> typeProviderMapping = getPreviewProviderForTypes(types);
          Iterator<I_CmsResourceType> it = types.iterator();
          while (it.hasNext()) {
  
              I_CmsResourceType type = it.next();
              try {
                  CmsResourceTypeBean bean = createTypeBean(
                      type,
                      typeProviderMapping.get(type),
                      creatableTypes.contains(type.getTypeName()));
                  list.add(bean);
              } catch (Exception e) {
                  if (type != null) {
                      log(
                          Messages.get().getBundle(getWorkplaceLocale()).key(
                              Messages.ERR_BUILD_TYPE_LIST_1,
                              type.getTypeName()),
                          e);
                  }
              }
          }
          return list;
      }
  
      /**
       * Recursively builds the VFS entry bean for the quick filtering function in the folder tab.<p<
       *
       * @param resource the resource
       * @param childMap map from parent to child resources
       * @param filterMatches the resources matching the filter
       * @param parentPaths root paths of resources which are not leaves
       * @param isRoot true if this the root node
       *
       * @return the VFS entry bean for the client
       *
       * @throws CmsException if something goes wrong
       */
      private CmsVfsEntryBean buildVfsEntryBeanForQuickSearch(
          CmsResource resource,
          Multimap<CmsResource, CmsResource> childMap,
          Set<CmsResource> filterMatches,
          Set<String> parentPaths,
          boolean isRoot)
      throws CmsException {
  
          CmsObject cms = getCmsObject();
          String title = cms.readPropertyObject(resource, CmsPropertyDefinition.PROPERTY_TITLE, false).getValue();
          boolean isMatch = filterMatches.contains(resource);
          List<CmsVfsEntryBean> childBeans = Lists.newArrayList();
  
          Collection<CmsResource> children = childMap.get(resource);
          if (!children.isEmpty()) {
              for (CmsResource child : children) {
                  CmsVfsEntryBean childBean = buildVfsEntryBeanForQuickSearch(
                      child,
                      childMap,
                      filterMatches,
                      parentPaths,
                      false);
                  childBeans.add(childBean);
              }
          } else if (filterMatches.contains(resource)) {
              if (parentPaths.contains(resource.getRootPath())) {
                  childBeans = null;
              }
              // otherwise childBeans remains an empty list
          }
  
          String rootPath = resource.getRootPath();
          CmsVfsEntryBean result = new CmsVfsEntryBean(
              rootPath,
              resource.getStructureId(),
              title,
              CmsIconUtil.getIconClasses(CmsIconUtil.getDisplayType(cms, resource), resource.getName(), true),
              isRoot,
              isEditable(cms, resource),
              childBeans,
              isMatch);
          String siteRoot = null;
          if (OpenCms.getSiteManager().startsWithShared(rootPath)) {
              siteRoot = OpenCms.getSiteManager().getSharedFolder();
          } else {
              String tempSiteRoot = OpenCms.getSiteManager().getSiteRoot(rootPath);
              if (tempSiteRoot != null) {
                  siteRoot = tempSiteRoot;
              } else {
                  siteRoot = "";
              }
          }
          result.setSiteRoot(siteRoot);
          return result;
      }
  
      /**
       * Helper method to construct a sitemap entry bean for the sitemap tab filter functionality.<p>
       *
       * @param cms the CMS context
       * @param node the root node of the filtered tree
       * @param isRoot true if this is the root node
       *
       * @return the sitemap entry bean
       */
      private CmsSitemapEntryBean convertNavigationTreeToBean(CmsObject cms, NavigationNode node, boolean isRoot) {
  
          CmsSitemapEntryBean bean = null;
          try {
              bean = prepareSitemapEntry(cms, node.getNavElement(), isRoot, false);
              bean.setSearchMatch(node.isMatch());
              List<NavigationNode> children = node.getChildren();
              List<CmsSitemapEntryBean> childBeans = Lists.newArrayList();
              if (children.size() > 0) {
                  for (NavigationNode child : children) {
                      childBeans.add(convertNavigationTreeToBean(cms, child, false));
                  }
              } else if (node.isLeaf()) {
                  childBeans = Lists.newArrayList();
              } else {
                  // no children in filter result, but can still load children by clicking on tree item
                  childBeans = null;
              }
              bean.setChildren(childBeans);
  
          } catch (CmsException e) {
              LOG.error(e.getLocalizedMessage(), e);
          }
          return bean;
      }
  
      /**
       * Returns a list of resource types by a request parameter.<p>
       *
       * @param resourceTypes the resource types parameter
       *
       * @return the resource types
       */
      private List<I_CmsResourceType> convertTypeNamesToTypes(List<String> resourceTypes) {
  
          List<I_CmsResourceType> result = new ArrayList<I_CmsResourceType>();
          if (resourceTypes != null) {
              for (String type : resourceTypes) {
                  try {
                      result.add(getResourceManager().getResourceType(type.trim()));
                  } catch (Exception e) {
                      logError(e);
                  }
              }
          }
  
          return result;
      }
  
      /**
       * Creates a resource type bean for the given type.<p>
       *
       * @param type the resource type
       * @param preview the preview provider
       * @param creatable if the type may be created by the current user
       *
       * @return the resource type bean
       */
      @SuppressWarnings("deprecation")
      private CmsResourceTypeBean createTypeBean(
          I_CmsResourceType type,
          I_CmsPreviewProvider preview,
          boolean creatable) {
  
          CmsResourceTypeBean result = new CmsResourceTypeBean();
  
          result.setResourceType(type.getTypeName());
          result.setTypeId(type.getTypeId());
          Locale wpLocale = getWorkplaceLocale();
          // type title and subtitle
          result.setTitle(CmsWorkplaceMessages.getResourceTypeName(wpLocale, type.getTypeName()));
          result.setSubTitle(CmsWorkplaceMessages.getResourceTypeDescription(wpLocale, type.getTypeName()));
          result.setBigIconClasses(CmsIconUtil.getIconClasses(type.getTypeName(), null, false));
          // gallery id of corresponding galleries
          ArrayList<String> galleryNames = new ArrayList<String>();
          Iterator<I_CmsResourceType> galleryTypes = type.getGalleryTypes().iterator();
          while (galleryTypes.hasNext()) {
              I_CmsResourceType galleryType = galleryTypes.next();
              galleryNames.add(galleryType.getTypeName());
          }
          result.setGalleryTypeNames(galleryNames);
          if (preview != null) {
              result.setPreviewProviderName(preview.getPreviewName());
          }
          if (type.isFolder()) {
              result.setVisibility(TypeVisibility.hidden);
          }
          result.setCreatableType(creatable);
          return result;
      }
  
      /**
       * Returns the search object containing the list with search results and the path to the specified resource.<p>
       *
       * @param resourceName the given resource
       * @param data the gallery data bean
       *
       * @return the gallery search object containing the current search parameter and the search result list
       *
       * @throws CmsException if the search fails
       */
      private CmsGallerySearchBean findResourceInGallery(String resourceName, CmsGalleryDataBean data)
      throws CmsException {
  
          CmsResource resource = null;
          int pos = resourceName.indexOf("?");
          String resName = resourceName;
          String query = "";
          if (pos > -1) {
              query = resourceName.substring(pos);
              resName = resourceName.substring(0, pos);
          }
          String resNameWithoutServer = OpenCms.getLinkManager().getRootPath(getCmsObject(), resName);
          if (resNameWithoutServer != null) {
              // also cut off fragment if it has one
              resName = resNameWithoutServer.replaceFirst("#.*$", "");
          }
          CmsObject cms = getCmsObject();
          try {
              log("reading resource: " + resName);
              resource = cms.readResource(resName, CmsResourceFilter.ONLY_VISIBLE_NO_DELETED);
          } catch (CmsException e) {
              String originalSiteRoot = cms.getRequestContext().getSiteRoot();
              try {
                  cms.getRequestContext().setSiteRoot("");
                  resource = cms.readResource(resName, CmsResourceFilter.ONLY_VISIBLE_NO_DELETED);
              } catch (CmsException e2) {
                  logError(e);
                  return null;
              } finally {
                  cms.getRequestContext().setSiteRoot(originalSiteRoot);
              }
          }
          boolean typeOk = true;
          if ((data.getTypes() != null) && !data.getTypes().isEmpty()) {
              typeOk = false;
              for (CmsResourceTypeBean type : data.getTypes()) {
                  if (OpenCms.getResourceManager().matchResourceType(type.getType(), resource.getTypeId())) {
                      typeOk = true;
                  }
              }
          }
          if (!typeOk) {
              LOG.debug(
                  "Selected resource " + resource.getRootPath() + " has invalid type for configured gallery widget.");
              return null;
          }
          ArrayList<String> types = new ArrayList<String>();
          String resType = OpenCms.getResourceManager().getResourceType(resource).getTypeName();
          types.add(resType);
          Set<String> folders = null;
          if (data.getStartFolderFilter() != null) {
              for (String folder : data.getStartFolderFilter()) {
                  if (resource.getRootPath().startsWith(folder)) {
                      folders = data.getStartFolderFilter();
                      break;
                  }
              }
          }
          CmsGallerySearchBean initialSearchObj = new CmsGallerySearchBean();
          initialSearchObj.setGalleryMode(data.getMode());
          initialSearchObj.setGalleryStoragePrefix(data.getGalleryStoragePrefix());
          initialSearchObj.setIncludeExpired(data.getIncludeExpiredDefault());
          initialSearchObj.setIgnoreSearchExclude(true);
          initialSearchObj.setTypes(types);
          if (folders != null) {
              initialSearchObj.setFolders(folders);
          }
          ArrayList<String> galleries = new ArrayList<String>();
          for (CmsGalleryFolderBean gallery : data.getGalleries()) {
              String galleryPath = gallery.getPath();
              String galleryRootPath = cms.addSiteRoot(galleryPath);
              String folderPath = CmsResource.getFolderPath(resName);
              if (galleryPath.equals(folderPath) || galleryRootPath.equals(folderPath)) {
                  galleries.add(gallery.getPath());
                  initialSearchObj.setGalleries(galleries);
                  break;
              }
          }
          if (galleries.isEmpty()) {
              ArrayList<String> vfsFolders = new ArrayList<String>();
              vfsFolders.add(CmsResource.getFolderPath(resource.getRootPath()));
              initialSearchObj.setFolders(new HashSet<String>(vfsFolders));
          }
          initialSearchObj.setLocale(data.getLocale());
          CmsGallerySearchBean searchObj = new CmsGallerySearchBean(initialSearchObj);
          CmsWorkplaceSettings wpSettings = getWorkplaceSettings();
          String resultOrderKey = convertModeToResultOrderKey(data.getMode());
          SortParams sortOrder = wpSettings.getLastGalleryResultOrder(resultOrderKey);
          if (sortOrder == null) {
              sortOrder = SortParams.dateLastModified_desc;
          }
          try {
              // Make sure that the SortParams name corresponds to a valid CmsGallerySortParam name
              searchObj.setSortOrder(CmsGallerySearchParameters.CmsGallerySortParam.valueOf(sortOrder.toString()).name());
          } catch (Exception e) {
              LOG.error(e.getLocalizedMessage(), e);
              searchObj.setSortOrder(CmsGallerySearchParameters.CmsGallerySortParam.dateLastModified_desc.name());
          }
  
          int currentPage = 1;
          boolean found = false;
          searchObj.setPage(currentPage);
          CmsGallerySearchParameters params = prepareSearchParams(searchObj, CmsAddContentRestriction.EMPTY);
          CmsObject searchCms = getSearchCms(searchObj);
          org.opencms.search.galleries.CmsGallerySearch searchBean = new org.opencms.search.galleries.CmsGallerySearch();
          searchBean.init(searchCms);
          searchBean.setIndexForProject(searchCms);
          CmsGallerySearchResultList searchResults = null;
          CmsGallerySearchResultList totalResults = new CmsGallerySearchResultList();
          CmsGallerySearchResult foundItem = null;
          while (!found) {
              params.setResultPage(currentPage);
              searchResults = searchBean.getResult(params);
              totalResults.append(searchResults);
              Iterator<CmsGallerySearchResult> resultsIt = searchResults.listIterator();
              while (resultsIt.hasNext()) {
                  CmsGallerySearchResult searchResult = resultsIt.next();
                  if (searchResult.getPath().equals(resource.getRootPath())) {
                      found = true;
                      foundItem = searchResult;
                      break;
                  }
              }
              if (!found && ((searchResults.getHitCount() / (currentPage * params.getMatchesPerPage())) >= 1)) {
                  currentPage++;
              } else {
                  break;
              }
              if (searchResults.getHitCount() > INITIAL_SEARCH_MAX_RESULTS) {
                  // in case the hit count is too large, don't continue the search to avoid slow load times
                  break;
              }
          }
          boolean hasResults = searchResults != null;
          searchResults = totalResults;
          if (hasResults) {
              initialSearchObj.setSortOrder(params.getSortOrder().name());
              initialSearchObj.setResultCount(searchResults.getHitCount());
              initialSearchObj.setPage(params.getResultPage());
              initialSearchObj.setResults(buildSearchResultList(searchResults, foundItem, res -> false));
              initialSearchObj.setPage(1);
              initialSearchObj.setLastPage(currentPage);
              initialSearchObj.setTabId(I_CmsGalleryProviderConstants.GalleryTabId.cms_tab_results.name());
              initialSearchObj.setResourcePath(resName + query);
              initialSearchObj.setResourceType(resType);
              updateNoUploadReason(searchCms, searchObj);
          } else {
              log("could not find selected resource");
          }
          if ((resource != null)) {
              if (isSitemapEntry(cms, resource)) {
                  A_CmsTreeTabDataPreloader<CmsSitemapEntryBean> loader = new A_CmsTreeTabDataPreloader<CmsSitemapEntryBean>() {
  
                      @Override
                      protected CmsSitemapEntryBean createEntry(CmsObject innerCms, CmsResource innerResource)
                      throws CmsException {
  
                          return internalCreateSitemapEntryBean(innerCms, innerResource);
                      }
  
                      /**
                       * @see org.opencms.ade.galleries.A_CmsTreeTabDataPreloader#getChildren(org.opencms.file.CmsResource)
                       */
                      @Override
                      protected List<CmsResource> getChildren(CmsResource parent) throws CmsException {
  
                          return getSitemapSubEntryResources(parent.getRootPath());
                      }
                  };
                  CmsSitemapEntryBean entryBean = loader.preloadData(
                      cms,
                      Sets.newHashSet(Collections.singletonList(resource)),
                      null);
                  initialSearchObj.setSitemapPreloadData(entryBean);
              } else if (resource.isFolder()) {
                  A_CmsTreeTabDataPreloader<CmsVfsEntryBean> loader = new A_CmsTreeTabDataPreloader<CmsVfsEntryBean>() {
  
                      @Override
                      protected CmsVfsEntryBean createEntry(CmsObject innerCms, CmsResource innerResource)
                      throws CmsException {
  
                          String title = innerCms.readPropertyObject(
                              innerResource,
                              CmsPropertyDefinition.PROPERTY_TITLE,
                              false).getValue();
                          return internalCreateVfsEntryBean(
                              getCmsObject(),
                              innerResource,
                              title,
                              true,
                              isEditable(innerCms, innerResource),
                              null,
                              false);
                      }
                  };
                  CmsVfsEntryBean entryBean = loader.preloadData(
                      cms,
                      Sets.newHashSet(Collections.singletonList(resource)),
                      null);
                  initialSearchObj.setVfsPreloadData(entryBean);
              }
          }
  
          return initialSearchObj;
      }
  
      /**
       * Get default types for gallery together with visibility.<p>
       *
       * @return the default types
       */
      private List<I_CmsResourceType> getDefaultTypesForGallery() {
  
          return OpenCms.getResourceManager().getResourceTypes();
      }
  
      /**
       * Generates a list of available galleries for the given gallery-type.<p>
       *
       * @param galleryTypeId the gallery-type
       *
       * @return the list of galleries
       *
       */
      @SuppressWarnings("deprecation")
      private List<CmsResource> getGalleriesByType(int galleryTypeId) {
  
          Set<CmsResource> galleries = new HashSet<CmsResource>();
  
          // We swallow errors in this method because we don't  want a failure to read some folders (e.g. because of permission problems) to
          // cause an empty gallery list as a result
  
          try {
              galleries.addAll(
                  getCmsObject().readResources(
                      "/",
                      CmsResourceFilter.ONLY_VISIBLE_NO_DELETED.addRequireType(galleryTypeId)));
          } catch (Exception e) {
              LOG.error("Could not read site galleries: " + e.getLocalizedMessage(), e);
          }
  
          String siteRoot = getCmsObject().getRequestContext().getSiteRoot();
          // if the current site is NOT the root site - add all other galleries from the system path
  
          try {
              if (!siteRoot.equals("")) {
                  List<CmsResource> systemGalleries = null;
                  // get the galleries in the /system/ folder
                  systemGalleries = getCmsObject().readResources(
                      CmsWorkplace.VFS_PATH_SYSTEM,
                      CmsResourceFilter.ONLY_VISIBLE_NO_DELETED.addRequireType(galleryTypeId));
                  if (systemGalleries != null) {
                      // add the found system galleries to the result
                      galleries.addAll(systemGalleries);
                  }
              }
          } catch (Exception e) {
              LOG.info("Could not read system galleries: " + e.getLocalizedMessage(), e);
          }
  
          try {
              if (!OpenCms.getSiteManager().isSharedFolder(siteRoot)) {
                  String shared = OpenCms.getSiteManager().getSharedFolder();
                  List<CmsResource> sharedGalleries = getCmsObject().readResources(
                      shared,
                      CmsResourceFilter.ONLY_VISIBLE_NO_DELETED.addRequireType(galleryTypeId));
                  if (sharedGalleries != null) {
                      galleries.addAll(sharedGalleries);
                  }
              }
          } catch (Exception e) {
              LOG.info("Could not read shared galleries: " + e.getLocalizedMessage(), e);
          }
          List<CmsResource> galleriesList = new ArrayList<>(galleries);
          Collections.sort(
              galleriesList,
              (a, b) -> ComparisonChain.start().compare(a.getRootPath(), b.getRootPath()).result());
          return galleriesList;
      }
  
      /**
       * Helper method for getting the initial gallery settings.<p>
       *
       * @param conf the gallery configration
       * @return the gallery settings
       *
       * @throws CmsRpcException if something goes wrong
       */
      private CmsGalleryDataBean getInitialSettingsInternal(CmsGalleryConfiguration conf) throws CmsRpcException {
  
          CmsObject cms = getCmsObject();
          CmsGalleryDataBean data = new CmsGalleryDataBean();
          data.setMode(conf.getGalleryMode());
          data.setResultViewType(readResultViewType());
          boolean galleryShowInvalidDefault = Boolean.parseBoolean(
              getWorkplaceSettings().getUserSettings().getAdditionalPreference(PREF_GALLERY_SHOW_INVALID_DEFAULT, true));
          data.setIncludeExpiredDefault(galleryShowInvalidDefault);
  
          data.setGalleryStoragePrefix(conf.getGalleryStoragePrefix());
          data.setLocales(buildLocalesMap());
          if (CmsStringUtil.isNotEmptyOrWhitespaceOnly(conf.getLocale())) {
              data.setLocale(conf.getLocale());
          } else {
              data.setLocale(getCmsObject().getRequestContext().getLocale().toString());
          }
          data.setVfsRootFolders(getRootEntries());
          data.setScope(getWorkplaceSettings().getLastSearchScope());
          String resultOrderKey = convertModeToResultOrderKey(conf.getGalleryMode());
          data.setSortOrder(getWorkplaceSettings().getLastGalleryResultOrder(resultOrderKey));
  
          List<CmsResourceTypeBean> types = null;
          data.setTabIds(conf.getGalleryMode().getTabs());
          switch (conf.getGalleryMode()) {
              case editor:
              case view:
              case adeView:
              case widget:
                  if (conf.getTabIds() != null) {
                      data.setTabIds(conf.getTabIds());
                  }
                  data.setCurrentElement(conf.getCurrentElement());
                  String referencePath = conf.getReferencePath();
                  if (CmsStringUtil.isEmptyOrWhitespaceOnly(referencePath)) {
                      referencePath = conf.getGalleryPath();
                  }
                  data.setReferenceSitePath(referencePath);
                  types = getResourceTypeBeans(
                      conf.getGalleryMode(),
                      data.getReferenceSitePath(),
                      conf.getResourceTypes(),
                      conf.getSearchTypes());
                  data.setTypes(types);
                  Map<String, CmsGalleryTypeInfo> galleryTypeInfos = readGalleryInfosByTypeBeans(types);
                  // in case the 'gallerytypes' parameter is set, allow only the given galleries
                  if (conf.getGalleryTypes() != null) {
                      Map<String, CmsGalleryTypeInfo> infos = new HashMap<String, CmsGalleryTypeInfo>();
                      for (int i = 0; i < conf.getGalleryTypes().length; i++) {
                          CmsGalleryTypeInfo typeInfo = galleryTypeInfos.get(conf.getGalleryTypes()[i]);
                          if (typeInfo != null) {
                              infos.put(conf.getGalleryTypes()[i], typeInfo);
                          }
                      }
                      galleryTypeInfos = infos;
                  }
  
                  String uri = cms.getRequestContext().getUri();
                  String galleryContext = referencePath;
                  if (conf.getPageId() != null) {
                      // This covers the case where the user is in the page editor,
                      // then edits an element of the page with the content editor, and then
                      // opens the gallery from a WYSIWYG field in the content editor.
                      try {
                          CmsResource page = cms.readResource(
                              new CmsUUID(conf.getPageId()),
                              CmsResourceFilter.IGNORE_EXPIRATION);
                          galleryContext = cms.getSitePath(page);
                      } catch (Exception e) {
                          LOG.error(e.getLocalizedMessage(), e);
                      }
                  } else if (!uri.endsWith(".gwt") && !uri.endsWith(".jsp")) {
                      galleryContext = uri;
                  }
                  if (galleryContext == null) {
                      galleryContext = "/";
                  }
                  data.setGalleries(buildGalleriesList(galleryContext, galleryTypeInfos));
                  String startGallery = conf.getGalleryPath();
                  // check if the configured gallery path really is an existing gallery
                  boolean galleryAvailable = false;
                  for (CmsGalleryFolderBean folderBean : data.getGalleries()) {
                      if (folderBean.getPath().equals(startGallery)) {
                          galleryAvailable = true;
                          break;
                      }
                  }
                  data.setStartGallery(galleryAvailable ? startGallery : null);
                  if (CmsStringUtil.isNotEmptyOrWhitespaceOnly(conf.getStartFolder())) {
                      try {
                          CmsObject cloneCms = OpenCms.initCmsObject(getCmsObject());
                          if (CmsStringUtil.isNotEmptyOrWhitespaceOnly(conf.getStartSite())) {
                              cloneCms.getRequestContext().setSiteRoot(conf.getStartSite());
                          }
                          if (cloneCms.existsResource(conf.getStartFolder(), CmsResourceFilter.ONLY_VISIBLE_NO_DELETED)) {
                              data.setStartFolder(cloneCms.getRequestContext().addSiteRoot(conf.getStartFolder()));
                          }
                      } catch (CmsException e) {
                          log(e.getMessage(), e);
                      }
                  }
  
                  if (CmsStringUtil.isEmptyOrWhitespaceOnly(data.getStartGallery()) && !data.getGalleries().isEmpty()) {
  
                      String key = "";
                      if (!types.isEmpty() && !CmsStringUtil.isEmptyOrWhitespaceOnly(conf.getReferencePath())) {
                          key = types.get(0).getType();
                          try {
                              CmsResource refResource = getCmsObject().readResource(conf.getReferencePath());
                              String referenceType = OpenCms.getResourceManager().getResourceType(
                                  refResource).getTypeName();
                              key = CmsGallerySearchBean.getGalleryStorageKey(
                                  data.getGalleryStoragePrefix(),
                                  referenceType);
                          } catch (Exception e) {
                              LOG.error("Could not read reference resource: " + conf.getReferencePath());
                          }
                      }
  
                      if (!data.getGalleries().isEmpty()) {
                          startGallery = getWorkplaceSettings().getLastUsedGallery(key);
                          if (CmsStringUtil.isEmptyOrWhitespaceOnly(startGallery)) {
                              // check the user preferences for any configured start gallery
                              String galleryTypeName = data.getGalleries().get(0).getType();
                              startGallery = getWorkplaceSettings().getUserSettings().getStartGallery(
                                  galleryTypeName,
                                  getCmsObject());
                              if (CmsWorkplace.INPUT_DEFAULT.equals(startGallery)) {
                                  startGallery = OpenCms.getWorkplaceManager().getDefaultUserSettings().getStartGallery(
                                      galleryTypeName);
                              }
                              if (CmsStringUtil.isNotEmptyOrWhitespaceOnly(startGallery)) {
                                  startGallery = getCmsObject().getRequestContext().removeSiteRoot(startGallery);
                              }
                          }
                      }
                      // check if the gallery is available in this site and still exists
                      if (!conf.isResultsSelectable()) {
                          // if selecting results is explicitly disabled, opening the start gallery does not make much sense
                          data.setStartGallery(null);
                      } else if (CmsStringUtil.isNotEmptyOrWhitespaceOnly(startGallery)) {
                          boolean galleryExists = getCmsObject().existsResource(startGallery);
                          if (galleryExists) {
                              data.setStartGallery(startGallery);
                          } else {
                              LOG.error("Could not read start gallery: " + startGallery);
                              data.setStartGallery(null);
                          }
                      } else {
                          data.setStartGallery(null);
                      }
                  }
  
                  GalleryTabId defaultTab = conf.getTabConfiguration().getDefaultTab();
                  data.setTabConfiguration(conf.getTabConfiguration());
                  if (CmsStringUtil.isEmptyOrWhitespaceOnly(data.getStartGallery())
                      && CmsStringUtil.isEmptyOrWhitespaceOnly(data.getCurrentElement())
                      && CmsStringUtil.isEmptyOrWhitespaceOnly(data.getStartFolder())) {
                      data.setStartTab(defaultTab);
                  } else {
                      data.setStartTab(GalleryTabId.cms_tab_results);
                  }
                  break;
              case ade:
                  data.setReferenceSitePath(getCmsObject().getRequestContext().getUri());
                  types = getResourceTypeBeans(
                      conf.getGalleryMode(),
                      data.getReferenceSitePath(),
                      conf.getResourceTypes(),
                      conf.getSearchTypes());
                  data.setTypes(types);
                  Map<String, CmsGalleryTypeInfo> adeGalleryTypeInfos = readGalleryInfosByTypeBeans(types);
                  data.setGalleries(buildGalleriesList(data.getReferenceSitePath(), adeGalleryTypeInfos));
                  data.setStartTab(GalleryTabId.cms_tab_types);
                  break;
              default:
                  break;
          }
  
          if (CmsStringUtil.isEmptyOrWhitespaceOnly(data.getStartGallery())) {
              Set<String> folderFilter = readFolderFilters();
              data.setStartFolderFilter(folderFilter);
              if ((folderFilter != null) && !folderFilter.isEmpty()) {
                  try {
                      data.setVfsPreloadData(
                          generateVfsPreloadData(
                              getCmsObject(),
                              CmsGalleryService.getVfsTreeState(getRequest(), conf.getTreeToken()),
                              folderFilter));
                  } catch (Exception e) {
                      LOG.error(e.getLocalizedMessage(), e);
                  }
              }
          }
  
          CmsSiteSelectorOptionBuilder optionBuilder = new CmsSiteSelectorOptionBuilder(getCmsObject());
          optionBuilder.addNormalSites(true, getWorkplaceSettings().getUserSettings().getStartFolder());
          optionBuilder.addSharedSite();
          data.setVfsSiteSelectorOptions(optionBuilder.getOptions());
  
          CmsSiteSelectorOptionBuilder sitemapOptionBuilder = new CmsSiteSelectorOptionBuilder(getCmsObject());
          sitemapOptionBuilder.addNormalSites(false, null);
          if (data.getReferenceSitePath() != null) {
              sitemapOptionBuilder.addCurrentSubsite(getCmsObject().addSiteRoot(data.getReferenceSitePath()));
          }
          data.setSitemapSiteSelectorOptions(sitemapOptionBuilder.getOptions());
          data.setDefaultScope(OpenCms.getWorkplaceManager().getGalleryDefaultScope());
          return data;
      }
  
      /**
       * Reads the preview provider configuration and generates needed type-provider mappings.<p>
       *
       * @param types the resource types
       *
       * @return a map from resource types to preview providers
       */
      private Map<I_CmsResourceType, I_CmsPreviewProvider> getPreviewProviderForTypes(List<I_CmsResourceType> types) {
  
          Map<String, I_CmsPreviewProvider> previewProviderMap = new HashMap<String, I_CmsPreviewProvider>();
          Map<I_CmsResourceType, I_CmsPreviewProvider> typeProviderMapping = new HashMap<I_CmsResourceType, I_CmsPreviewProvider>();
          for (I_CmsResourceType type : types) {
              String providerClass = type.getGalleryPreviewProvider();
              if (CmsStringUtil.isNotEmptyOrWhitespaceOnly(providerClass)) {
                  providerClass = providerClass.trim();
                  try {
                      if (previewProviderMap.containsKey(providerClass)) {
                          typeProviderMapping.put(type, previewProviderMap.get(providerClass));
                      } else {
                          I_CmsPreviewProvider previewProvider = (I_CmsPreviewProvider)Class.forName(
                              providerClass).newInstance();
                          previewProviderMap.put(providerClass, previewProvider);
                          typeProviderMapping.put(type, previewProvider);
                      }
                  } catch (Exception e) {
                      logError(
                          new CmsException(
                              Messages.get().container(
                                  Messages.ERR_INSTANCING_PREVIEW_PROVIDER_2,
                                  providerClass,
                                  type.getTypeName()),
                              e));
                  }
              }
          }
          return typeProviderMapping;
      }
  
      /**
       * Returns the resourceManager.<p>
       *
       * @return the resourceManager
       */
      private CmsResourceManager getResourceManager() {
  
          if (m_resourceManager == null) {
              m_resourceManager = OpenCms.getResourceManager();
          }
          return m_resourceManager;
      }
  
      /**
       * Returns the resource types configured to be used within the given gallery mode.<p>
       *
       * @param galleryMode the gallery mode
       * @param referenceSitePath the reference site-path to check permissions for
       * @param resourceTypesList the resource types parameter
       * @param typesForTypeTab the types which should be shown in the types tab according to the gallery configuration
       *
       * @return the resource types
       */
      private List<CmsResourceTypeBean> getResourceTypeBeans(
          GalleryMode galleryMode,
          String referenceSitePath,
          List<String> resourceTypesList,
          final List<String> typesForTypeTab) {
  
          List<I_CmsResourceType> resourceTypes = null;
          Set<String> creatableTypes = null;
          switch (galleryMode) {
              case editor:
              case view:
              case adeView:
              case widget:
                  resourceTypes = convertTypeNamesToTypes(resourceTypesList);
                  if (resourceTypes.size() == 0) {
                      resourceTypes = Lists.newArrayList(getDefaultTypesForGallery());
                  }
                  creatableTypes = Collections.<String> emptySet();
                  break;
              case ade:
                  throw new IllegalStateException("This code should never be called");
              // ADE case is handled by container page service
              default:
                  resourceTypes = Collections.<I_CmsResourceType> emptyList();
                  creatableTypes = Collections.<String> emptySet();
          }
          return buildTypesList(resourceTypes, creatableTypes, Collections.<String> emptySet(), typesForTypeTab);
      }
  
      /**
       * Returns the VFS root entries.<p>
       *
       * @return the VFS root entries
       *
       * @throws CmsRpcException if something goes wrong
       */
      private List<CmsVfsEntryBean> getRootEntries() throws CmsRpcException {
  
          List<CmsVfsEntryBean> rootFolders = new ArrayList<CmsVfsEntryBean>();
          CmsObject cms = getCmsObject();
          try {
              String path = "/";
              if (!cms.existsResource(path, CmsResourceFilter.ONLY_VISIBLE_NO_DELETED)) {
                  String startFolder = getWorkplaceSettings().getUserSettings().getStartFolder();
                  if (cms.existsResource(startFolder, CmsResourceFilter.ONLY_VISIBLE_NO_DELETED)) {
                      path = startFolder;
                  } else {
                      path = null;
                  }
              }
              if (path != null) {
                  CmsResource rootFolderResource = getCmsObject().readResource(
                      path,
                      CmsResourceFilter.ONLY_VISIBLE_NO_DELETED);
                  String title = cms.readPropertyObject(path, CmsPropertyDefinition.PROPERTY_TITLE, false).getValue();
                  rootFolders.add(
                      internalCreateVfsEntryBean(
                          getCmsObject(),
                          rootFolderResource,
                          title,
                          true,
                          isEditable(getCmsObject(), rootFolderResource),
                          null,
                          false));
              }
  
          } catch (CmsException e) {
              error(e);
          }
          return rootFolders;
      }
  
      /**
       * Returns the workplace settings of the current user.<p>
       *
       * @return the workplace settings
       */
      private CmsWorkplaceSettings getWorkplaceSettings() {
  
          if (m_workplaceSettings == null) {
              m_workplaceSettings = CmsWorkplace.getWorkplaceSettings(getCmsObject(), getRequest());
          }
          return m_workplaceSettings;
      }
  
      /**
       * Checks whether a resource has a navigation property.<p>
       *
       * @param cms the current CMS context
       * @param resource the resource to use
       *
       * @return true if the resource has a navigation property
       *
       * @throws CmsException if something goes wrong
       */
      private boolean hasNavigationProperty(CmsObject cms, CmsResource resource) throws CmsException {
  
          List<CmsProperty> props = cms.readPropertyObjects(resource, false);
          Map<String, String> propMap = CmsProperty.toMap(props);
          return propMap.containsKey(CmsPropertyDefinition.PROPERTY_NAVPOS)
              || propMap.containsKey(CmsPropertyDefinition.PROPERTY_NAVTEXT);
      }
  
      /**
       * Returns the search parameters for the given query data.<p>
       *
       * @param searchData the query data
       * @param replacementConfig the replacement configuration
       *
       * @return the prepared search parameters
       */
      private CmsGallerySearchParameters prepareSearchParams(
          CmsGallerySearchBean searchData,
          CmsAddContentRestriction replacementConfig) {
  
          // create a new search parameter object
          CmsGallerySearchParameters params = new CmsGallerySearchParameters();
          CmsObject cms = getCmsObject();
  
          // set the selected types to the parameters
          if (searchData.getServerSearchTypes() != null) {
              params.setResourceTypes(searchData.getServerSearchTypes());
          }
  
          // set the selected galleries to the parameters
          if (searchData.getGalleries() != null) {
              List<String> paramGalleries = new ArrayList<String>();
              for (String gallerySitePath : searchData.getGalleries()) {
                  paramGalleries.add(cms.getRequestContext().addSiteRoot(gallerySitePath));
              }
              params.setGalleries(paramGalleries);
          }
  
          // set the sort order for the galleries to the parameters
          CmsGallerySearchParameters.CmsGallerySortParam sortOrder;
          String temp = searchData.getSortOrder();
          try {
              sortOrder = CmsGallerySearchParameters.CmsGallerySortParam.valueOf(temp);
          } catch (Exception e) {
              sortOrder = CmsGallerySearchParameters.CmsGallerySortParam.DEFAULT;
          }
          params.setSortOrder(sortOrder);
          if (searchData.getScope() == null) {
              params.setScope(OpenCms.getWorkplaceManager().getGalleryDefaultScope());
          } else {
              params.setScope(searchData.getScope());
          }
          params.setReferencePath(searchData.getReferencePath());
  
          // set the selected folders to the parameters
          params.setFolders(new ArrayList<String>(searchData.getFolders()));
  
          // set the categories to the parameters
          if (searchData.getCategories() != null) {
              params.setCategories(searchData.getCategories());
          }
  
          // set the search query to the parameters
          if (!CmsStringUtil.isEmptyOrWhitespaceOnly(searchData.getQuery())) {
              params.setSearchWords(searchData.getQuery());
          }
  
          // set the result page to the parameters
          int page = searchData.getPage();
          params.setResultPage(page);
  
          // set the locale to the parameters
          String locale = searchData.getLocale();
          if (CmsStringUtil.isEmptyOrWhitespaceOnly(locale)) {
              locale = getCmsObject().getRequestContext().getLocale().toString();
          }
          params.setSearchLocale(locale);
  
          // set the matches per page to the parameters
          params.setMatchesPerPage(searchData.getMatchesPerPage());
  
          // get the date range input
          long dateCreatedStart = searchData.getDateCreatedStart();
          long dateCreatedEnd = searchData.getDateCreatedEnd();
          long dateModifiedStart = searchData.getDateModifiedStart();
          long dateModifiedEnd = searchData.getDateModifiedEnd();
  
          // set the date created range to the parameters
          if ((dateCreatedStart != -1L) && (dateCreatedEnd != -1L)) {
              params.setDateCreatedTimeRange(dateCreatedStart, dateCreatedEnd);
          } else if (dateCreatedStart != -1L) {
              params.setDateCreatedTimeRange(dateCreatedStart, Long.MAX_VALUE);
          } else if (dateCreatedEnd != -1L) {
              params.setDateCreatedTimeRange(Long.MIN_VALUE, dateCreatedEnd);
          }
  
          // set the date modified range to the parameters
          if ((dateModifiedStart != -1L) && (dateModifiedEnd != -1L)) {
              params.setDateLastModifiedTimeRange(dateModifiedStart, dateModifiedEnd);
          } else if (dateModifiedStart != -1L) {
              params.setDateLastModifiedTimeRange(dateModifiedStart, Long.MAX_VALUE);
          } else if (dateModifiedEnd != -1L) {
              params.setDateLastModifiedTimeRange(Long.MIN_VALUE, dateModifiedEnd);
          }
          params.setIgnoreSearchExclude(searchData.isIgnoreSearchExclude());
          if (GalleryMode.ade.equals(searchData.getGalleryMode())) {
              if (searchData.getTypes().isEmpty() || !Collections.disjoint(FUNCTION_TYPES, searchData.getTypes())) {
                  CmsADEConfigData config = OpenCms.getADEManager().lookupConfiguration(
                      cms,
                      cms.getRequestContext().addSiteRoot(searchData.getReferencePath()));
                  CmsFormatterConfigurationCacheState formatterState = OpenCms.getADEManager().getCachedFormatters(
                      cms.getRequestContext().getCurrentProject().isOnlineProject());
                  params.setFunctionAvailability(config.getDynamicFunctionAvailability(formatterState));
                  if (config.isHideNonMatchingFunctions()) {
                      if (searchData.getContainerInfo() != null) {
                          Set<CmsUUID> excludedFunctions = getExcludedFunctionsForContainerInfo(
                              cms,
                              config,
                              searchData.getContainerInfo());
                          params.setExcludedFunctions(excludedFunctions);
                      }
                  }
              }
              CmsTemplateContextInfo templateContextInfo = searchData.getTemplateContextInfo();
              if ((templateContextInfo != null)
                  && (templateContextInfo.getContextProvider() != null)
                  && (templateContextInfo.getCurrentContext() != null)) {
                  I_CmsTemplateContextProvider provider = OpenCms.getTemplateContextManager().getTemplateContextProvider(
                      templateContextInfo.getContextProvider());
                  if (provider != null) {
                      Set<CmsUUID> allowedFunctions = provider.getFunctionsForGallery(
                          cms,
                          templateContextInfo.getCurrentContext());
                      // allowed functions may be null - this is fine
                      params.setIncludedFunctions(allowedFunctions);
                      String compatibility = provider.getTemplateCompatibility(templateContextInfo.getCurrentContext());
                      params.setTemplateCompatibility(compatibility);
                  }
              }
              if (replacementConfig != null) {
                  // if the search replacement configuration didn't already handle the replacement,
                  // exclude the type from search results (this happens when searching for multiple types).
                  Set<String> excludedTypes = replacementConfig.getTypes();
                  if ((excludedTypes.size() > 0) && (params.getResourceTypes() != null)) {
                      List<String> types = new ArrayList<>(params.getResourceTypes());
                      boolean removed = types.removeAll(excludedTypes);
                      if (removed && (types.size() == 0)) {
                          params.setForceEmptyResult(true);
                      }
                      params.setResourceTypes(types);
                  }
              }
          }
  
          // set include expired/unreleased
          if (searchData.isIncludeExpired()) {
              params.setIncludeExpired(true);
          }
          return params;
      }
  
      /**
       * Prepares a sitemap entry bean from the given navigation element.<p>
       *
       * @param cms the cms context
       * @param navElement the navigation element
       * @param isRoot <code>true</code> if this is a site root entry
       * @param checkHasChildren if true, check if the entry has any children; set to false if you want to handle the list of children manually
       *
       * @return the sitemap entry
       *
       * @throws CmsException if something goes wrong reading types and resources
       */
      private CmsSitemapEntryBean prepareSitemapEntry(
          CmsObject cms,
          CmsJspNavElement navElement,
          boolean isRoot,
          boolean checkHasChildren)
      throws CmsException {
  
          CmsResource ownResource = navElement.getResource();
          CmsResource defaultFileResource = null;
          CmsJspNavBuilder navBuilder = new CmsJspNavBuilder(cms);
          if (ownResource.isFolder() && !navElement.isNavigationLevel()) {
              try {
                  defaultFileResource = cms.readDefaultFile(ownResource, CmsResourceFilter.ONLY_VISIBLE);
              } catch (CmsPermissionViolationException e) {
                  // user has insufficient rights, can be ignored
              }
          }
          String type;
          if (defaultFileResource != null) {
              type = OpenCms.getResourceManager().getResourceType(defaultFileResource.getTypeId()).getTypeName();
          } else {
              type = OpenCms.getResourceManager().getResourceType(ownResource.getTypeId()).getTypeName();
          }
          // make sure not to show ??? NavText ???
          String title = null;
          if (CmsStringUtil.isNotEmptyOrWhitespaceOnly(navElement.getProperty(CmsPropertyDefinition.PROPERTY_NAVTEXT))) {
              title = navElement.getProperty(CmsPropertyDefinition.PROPERTY_NAVTEXT);
          } else if (CmsStringUtil.isNotEmptyOrWhitespaceOnly(
              navElement.getProperty(CmsPropertyDefinition.PROPERTY_TITLE))) {
              title = navElement.getProperty(CmsPropertyDefinition.PROPERTY_TITLE);
          } else {
              title = navElement.getFileName();
              if (title.contains("/")) {
                  title = title.substring(0, title.indexOf("/"));
              }
          }
          String childPath = navElement.getResource().getRootPath();
          boolean noChildren = true;
  
          if (checkHasChildren) {
              List<CmsJspNavElement> childNav = navBuilder.getNavigationForFolder(
                  childPath,
                  Visibility.all,
                  CmsResourceFilter.ONLY_VISIBLE);
              for (CmsJspNavElement childNavEntry : childNav) {
                  if (childNavEntry.isInNavigation()) {
                      noChildren = false;
                      break;
                  }
              }
          }
  
          CmsSitemapEntryBean result = new CmsSitemapEntryBean(
              navElement.getResource().getRootPath(),
              navElement.getResourceName(),
              ownResource.getStructureId(),
              title,
              type,
              ownResource.isFolder(),
              isRoot,
              navElement.isHiddenNavigationEntry());
          result.setSiteRoot(OpenCms.getSiteManager().getSiteRoot(ownResource.getRootPath()));
          String displayType = CmsIconUtil.getDisplayType(
              cms,
              defaultFileResource != null ? defaultFileResource : ownResource);
          result.setBigIconClasses(
              CmsIconUtil.getIconClasses(
                  displayType,
                  defaultFileResource != null ? defaultFileResource.getName() : ownResource.getName(),
                  false));
          result.setSmallIconClasses(
              CmsIconUtil.getIconClasses(
                  displayType,
                  defaultFileResource != null ? defaultFileResource.getName() : ownResource.getName(),
                  true));
          if (checkHasChildren && noChildren) {
              result.setChildren(new ArrayList<CmsSitemapEntryBean>());
          }
  
          return result;
      }
  
      /**
       * Reads the folder filters for the current site.<p>
       *
       * @return the folder filters
       */
      private Set<String> readFolderFilters() {
  
          JSONObject storedFilters = readUserFolderFilters();
          Set<String> result = null;
          if (storedFilters.has(getCmsObject().getRequestContext().getSiteRoot())) {
              try {
                  org.opencms.json.JSONArray folders = storedFilters.getJSONArray(
                      getCmsObject().getRequestContext().getSiteRoot());
                  result = new HashSet<String>();
                  for (int i = 0; i < folders.length(); i++) {
                      result.add(folders.getString(i));
                  }
              } catch (JSONException e) {
                  LOG.error(e.getLocalizedMessage(), e);
              }
          }
          return result;
      }
  
      /**
       * Returns a map with gallery type names associated with the list of available galleries for this type.<p>
       *
       * @param resourceTypes the resources types to collect the galleries for
       *
       * @return a map with gallery type and  the associated galleries
       */
      private Map<String, CmsGalleryTypeInfo> readGalleryInfosByTypeBeans(List<CmsResourceTypeBean> resourceTypes) {
  
          Map<String, CmsGalleryTypeInfo> galleryTypeInfos = new HashMap<String, CmsGalleryTypeInfo>();
          for (CmsResourceTypeBean typeBean : resourceTypes) {
              try {
                  if (!typeBean.isDeactivated()) {
                      addGalleriesForType(galleryTypeInfos, typeBean.getType());
                  }
              } catch (CmsLoaderException e1) {
                  logError(e1);
              }
          }
          return galleryTypeInfos;
      }
  
      /**
       * Returns a map with gallery type names associated with the list of available galleries for this type.<p>
       *
       * @param resourceTypes the resources types to collect the galleries for
       *
       * @return a map with gallery type and  the associated galleries
       */
      private Map<String, CmsGalleryTypeInfo> readGalleryInfosByTypeNames(List<String> resourceTypes) {
  
          Map<String, CmsGalleryTypeInfo> galleryTypeInfos = new HashMap<String, CmsGalleryTypeInfo>();
          for (String typeName : resourceTypes) {
              try {
                  addGalleriesForType(galleryTypeInfos, typeName);
              } catch (CmsLoaderException e1) {
                  logError(e1);
              }
          }
          return galleryTypeInfos;
      }
  
      /**
       * Reads the result view type from the current user.<p>
       *
       * @return the result view type
       */
      private String readResultViewType() {
  
          return (String)getCmsObject().getRequestContext().getCurrentUser().getAdditionalInfo(
              RESULT_VIEW_TYPE_ADD_INFO_KEY);
      }
  
      /**
       * Reads the users folder filters from the additional info.<p>
       *
       * @return the folder filters
       */
      private JSONObject readUserFolderFilters() {
  
          CmsUser user = getCmsObject().getRequestContext().getCurrentUser();
          String addInfo = (String)user.getAdditionalInfo(FOLDER_FILTER_ADD_INFO_KEY);
          JSONObject result = null;
          if (CmsStringUtil.isNotEmptyOrWhitespaceOnly(addInfo)) {
              try {
                  result = new JSONObject(addInfo);
              } catch (JSONException e) {
                  LOG.error(e.getLocalizedMessage(), e);
              }
          }
          if (result == null) {
              result = new JSONObject();
          }
          return result;
      }
  
      /**
       * Returns the gallery search object containing the results for the current parameter.<p>
       *
       * @param searchObj the current search object
       *
       * @return the search result
       *
       * @throws CmsException if the search fails
       */
      private CmsGallerySearchBean search(CmsGallerySearchBean searchObj) throws CmsException {
  
          CmsGallerySearchBean searchObjBean = new CmsGallerySearchBean(searchObj);
          if (searchObj == null) {
              return searchObjBean;
          }
          // store folder filter
          storeFolderFilter(searchObj.getFolders());
          CmsObject cms = getCmsObject();
          CmsADEConfigData config = OpenCms.getADEManager().lookupConfigurationWithCache(
              cms,
              cms.getRequestContext().addSiteRoot(cms.getRequestContext().getUri()));
          CmsAddContentRestriction replacementConfig = config.getAddContentRestriction();
          CmsGallerySearchResultList searchResults = null;
          List<String> types = searchObj.getTypes();
          Predicate<CmsResource> deactivatedCheck = res -> false;
          if (searchObj.getGalleryMode().equals(GalleryMode.ade)
              && types.contains(CmsResourceTypeFunctionConfig.TYPE_NAME)
              && !config.isHideNonMatchingFunctions()
              && (searchObj.getContainerInfo() != null)) {
              final Set<CmsUUID> excludedFunctions = getExcludedFunctionsForContainerInfo(
                  cms,
                  config,
                  searchObj.getContainerInfo());
              deactivatedCheck = res -> excludedFunctions.contains(res.getStructureId());
          }
  
          searchObjBean.setReplacedResults(false);
          if (searchObj.getGalleryMode().equals(GalleryMode.ade)
              && (types.size() == 1)
              && replacementConfig.getTypes().contains(types.iterator().next())) {
  
              List<CmsGallerySearchResult> items = replacementConfig.getResult(cms, types.iterator().next());
              searchResults = new CmsGallerySearchResultList();
              searchResults.addAll(items);
              searchObjBean.setReplacedResults(true);
              searchObjBean.setPage(1);
              searchObjBean.setFolders(new HashSet<>());
              searchObjBean.setGalleries(new ArrayList<>());
              searchObjBean.setCategories(new ArrayList<>());
              // searchObjBean.setMatchesPerPage(searchResults.size());
              searchObjBean.setNoUploadReason(
                  Messages.get().getBundle(m_wpLocale).key(Messages.GUI_NO_UPLOAD_FOR_REPLACED_SEARCH_RESULTS_0));
              searchObjBean.setResults(buildSearchResultList(searchResults, null, deactivatedCheck));
              searchObjBean.setResultCount(searchResults.size());
              if (searchObjBean.getResultCount() > 0) {
                  CmsADESessionCache cache = CmsADESessionCache.getCache(getRequest(), getCmsObject());
                  cache.setLastPageEditorGallerySearch(searchObj);
              }
  
          } else {
              // search
              CmsGallerySearchParameters params = prepareSearchParams(searchObj, replacementConfig);
              CmsObject searchCms = getSearchCms(searchObj);
              searchResults = OpenCms.getSearchManager().getIndexSolr("Solr Offline").gallerySearch(searchCms, params);
              searchResults.calculatePages(params.getResultPage(), params.getMatchesPerPage());
  
              // set only the result dependent search params for this search
              // the user dependent params(galleries, types etc.) remain unchanged
              searchObjBean.setSortOrder(params.getSortOrder().name());
              searchObjBean.setScope(params.getScope());
              searchObjBean.setResultCount(searchResults.getHitCount());
              searchObjBean.setPage(params.getResultPage());
              searchObjBean.setLastPage(params.getResultPage());
              searchObjBean.setResults(buildSearchResultList(searchResults, null, deactivatedCheck));
              if (searchObj.getGalleryMode().equals(GalleryMode.ade)) {
                  if (searchObjBean.getResultCount() > 0) {
                      CmsADESessionCache cache = CmsADESessionCache.getCache(getRequest(), getCmsObject());
                      cache.setLastPageEditorGallerySearch(searchObj);
                  }
              }
              updateNoUploadReason(searchCms, searchObjBean);
          }
          return searchObjBean;
      }
  
      /**
       * Sets the last opened gallery information for the current user.<p>
       *
       * @param searchObject the current search
       */
      private void setLastOpenedGallery(CmsGallerySearchBean searchObject) {
  
          if ((searchObject.getGalleries() != null)
              && (searchObject.getGalleries().size() <= 1) // if the size is 0, the user has actively deselected the galleries, so we want to handle this case too
              && searchObject.haveGalleriesChanged()) {
              String galleryPath = searchObject.getGalleries().isEmpty() ? null : searchObject.getGalleries().get(0);
              CmsWorkplaceSettings settings = getWorkplaceSettings();
              if (searchObject.getGalleryMode() == GalleryMode.adeView) {
                  settings.setLastUsedGallery("" + GalleryMode.adeView, galleryPath);
              } else {
                  String referencePath = searchObject.getReferencePath();
  
                  String referenceTypeName = "";
  
                  try {
                      CmsObject cms = getCmsObject();
                      CmsResource referenceResource = cms.readResource(referencePath);
                      I_CmsResourceType referenceType = OpenCms.getResourceManager().getResourceType(referenceResource);
                      referenceTypeName = referenceType.getTypeName();
                  } catch (CmsException e) {
                      LOG.error(e.getLocalizedMessage(), e);
                  }
                  settings.setLastUsedGallery(
                      CmsGallerySearchBean.getGalleryStorageKey(
                          searchObject.getGalleryStoragePrefix(),
                          referenceTypeName),
                      galleryPath);
              }
          }
      }
  
      /**
       * Stores the folder filters for the current site.<p>
       *
       * @param folders the folder filters
       */
      private void storeFolderFilter(Set<String> folders) {
  
          JSONObject storedFilters = readUserFolderFilters();
          try {
              storedFilters.put(getCmsObject().getRequestContext().getSiteRoot(), folders);
              CmsUser user = getCmsObject().getRequestContext().getCurrentUser();
              user.setAdditionalInfo(FOLDER_FILTER_ADD_INFO_KEY, storedFilters.toString());
              getCmsObject().writeUser(user);
          } catch (Exception e) {
              LOG.error(e.getLocalizedMessage(), e);
          }
      }
  
      /**
       * Checks the current users permissions on the upload target folder to update the no upload reason.<p>
       *
       * @param searchCms the cms context
       * @param searchObj the search data
       */
      private void updateNoUploadReason(CmsObject searchCms, CmsGallerySearchBean searchObj) {
  
          if ((searchObj.getGalleries().size() + searchObj.getFolders().size()) == 1) {
              String target = !searchObj.getGalleries().isEmpty()
              ? searchObj.getGalleries().get(0)
              : searchObj.getFolders().iterator().next();
              try {
                  CmsResource targetRes;
                  if (searchCms.existsResource(target, CmsResourceFilter.ONLY_VISIBLE_NO_DELETED)) {
                      targetRes = searchCms.readResource(target, CmsResourceFilter.ONLY_VISIBLE_NO_DELETED);
                  } else {
                      CmsObject rootCms = OpenCms.initCmsObject(searchCms);
                      rootCms.getRequestContext().setSiteRoot("");
                      targetRes = rootCms.readResource(target, CmsResourceFilter.ONLY_VISIBLE_NO_DELETED);
                  }
                  searchObj.setNoUploadReason(
                      new CmsResourceUtil(searchCms, targetRes).getNoEditReason(getWorkplaceLocale(), true));
              } catch (CmsException e) {
                  searchObj.setNoUploadReason(e.getLocalizedMessage(getWorkplaceLocale()));
              }
          } else {
              searchObj.setNoUploadReason(null);
          }
      }
  }
  