A critical vulnerability was discovered in React Server Components (Next.js). Our systems remain protected but we advise to update packages to newest version. Learn More

Le Giang
Apr 2, 2018
  3924
(2 votes)

Implement search provider for pages, blocks and media

Currently when I type to search in navigationtree, surprisingly there is no result returned even for pages, block or media. It seems that someone forgot to implement searching feature or it is being underplayed smile.

In this post, I would like show you how to implement a search provider for pages, blocks and media and you can customize to fit your need.

For pages:

[SearchProvider]
    public class DefaultPageSearchProvider : ContentSearchProviderBase<IContent, ContentType>
    {
        public DefaultPageSearchProvider(LocalizationService localizationService, ISiteDefinitionResolver siteDefinitionResolver, IContentTypeRepository<ContentType> contentTypeRepository, EditUrlResolver editUrlResolver, ServiceAccessor<SiteDefinition> currentSiteDefinition, LanguageResolver languageResolver, UrlResolver urlResolver, TemplateResolver templateResolver, UIDescriptorRegistry uiDescriptorRegistry) : base(localizationService, siteDefinitionResolver, contentTypeRepository, editUrlResolver, currentSiteDefinition, languageResolver, urlResolver, templateResolver, uiDescriptorRegistry)
        {

        }

        /// <summary>
        ///
        /// </summary>
        public override string Area
        {
            get
            {
                return "cms/pages";
            }
        }

        /// <summary>
        ///
        /// </summary>
        public override string Category
        {
            get
            {
                return "pages";
            }
        }

        protected override string IconCssClass
        {
            get
            {
                return "epi-iconObjectPage";
            }
        }

        public override IEnumerable<SearchResult> Search(Query query)
        {
            var contentRepository = ServiceLocator.Current.GetInstance<IContentRepository>();
            var pageQueryService = ServiceLocator.Current.GetInstance<IPageCriteriaQueryable>();
            PropertyCriteriaCollection crits = new PropertyCriteriaCollection();

            PropertyCriteria nameCriteria = new PropertyCriteria();
            nameCriteria.Name = MetaDataProperties.PageName;
            nameCriteria.Value = query.SearchQuery;
            nameCriteria.Type = PropertyDataType.String;
            nameCriteria.Required = true;
            nameCriteria.Condition = CompareCondition.Contained;

            crits.Add(nameCriteria);
            //Add criteria so search is performed against all providers
            crits.Add(new PropertyCriteria
            {
                Name = "EPI:MultipleSearch",
                Value = "*"
            });

            PageDataCollection pages = null;
            try
            {
                pages = pageQueryService.FindAllPagesWithCriteria(ContentReference.RootPage, crits, null, LanguageSelector.MasterLanguage());
            }
            catch (NotImplementedException)
            {
                // If the provider hasn't implemented FindAllPagesWithCriteria, call old FindPagesWithCriteria instead.
                pages = pageQueryService.FindPagesWithCriteria(ContentReference.RootPage, crits, null, LanguageSelector.MasterLanguage());
            }
            return pages.Select(CreateSearchResult);
        }
    }

For blocks

 [SearchProvider]
    public class DefaultBlockSearchProvider : ContentSearchProviderBase<IContent, ContentType>
    {
        public DefaultBlockSearchProvider(LocalizationService localizationService, ISiteDefinitionResolver siteDefinitionResolver, IContentTypeRepository<ContentType> contentTypeRepository, EditUrlResolver editUrlResolver, ServiceAccessor<SiteDefinition> currentSiteDefinition, LanguageResolver languageResolver, UrlResolver urlResolver, TemplateResolver templateResolver, UIDescriptorRegistry uiDescriptorRegistry) : base(localizationService, siteDefinitionResolver, contentTypeRepository, editUrlResolver, currentSiteDefinition, languageResolver, urlResolver, templateResolver, uiDescriptorRegistry)
        {

        }

        /// <summary>
        ///
        /// </summary>
        public override string Area
        {
            get
            {
                return "cms/blocks";
            }
        }

        /// <summary>
        ///
        /// </summary>
        public override string Category
        {
            get
            {
                return "blocks";
            }
        }

        protected override string IconCssClass
        {
            get
            {
                return "epi-objectIcon";
            }
        }

        public override IEnumerable<SearchResult> Search(Query query)
        {
            ContentReference root = null;
            if (query.SearchRoots != null && query.SearchRoots.Count() > 0)
            {
                root = new ContentReference(query.SearchRoots.First());
            }
            else
            {
                root = new ContentReference();
            }

            var contentRepository = ServiceLocator.Current.GetInstance<IContentRepository>();
            var contentTypeRepository = ServiceLocator.Current.GetInstance<IContentTypeRepository>();
            var contentProviderManager = ServiceLocator.Current.GetInstance<IContentProviderManager>();
            var blockTypes = contentTypeRepository.List().Where(x => typeof(BlockData).IsAssignableFrom(x.ModelType));
            var provider = contentProviderManager.GetProvider(root);

            if (provider != null && provider.HasCapability(ContentProviderCapabilities.Search))
            {
                List<IContent> blocks = new List<IContent>();
                foreach(var blockType in blockTypes)
                {
                    blocks.AddRange(provider.ListContentOfContentType(blockType).Where(x => x.Name.IndexOf(query.SearchQuery, StringComparison.InvariantCultureIgnoreCase) >= 0).Select(x => contentRepository.Get<IContent>(x.ContentLink)));
                }

                return blocks.Select(CreateSearchResult);
            }

            return Enumerable.Empty<SearchResult>();
        }
    }

For media:

[SearchProvider]
    public class DefaultMediaSearchProvider : ContentSearchProviderBase<IContent, ContentType>
    {
        public DefaultMediaSearchProvider(LocalizationService localizationService,
            ISiteDefinitionResolver siteDefinitionResolver,
            IContentTypeRepository<ContentType> contentTypeRepository,
            EditUrlResolver editUrlResolver,
            ServiceAccessor<SiteDefinition> currentSiteDefinition,
            LanguageResolver languageResolver,
            UrlResolver urlResolver,
            TemplateResolver templateResolver,
            UIDescriptorRegistry uiDescriptorRegistry) : base(localizationService,
                siteDefinitionResolver,
                contentTypeRepository,
                editUrlResolver,
                currentSiteDefinition,
                languageResolver,
                urlResolver,
                templateResolver,
                uiDescriptorRegistry)
        {

        }

        /// <summary>
        ///
        /// </summary>
        public override string Area
        {
            get
            {
                return "CMS/files";
            }
        }

        /// <summary>
        ///
        /// </summary>
        public override string Category
        {
            get
            {
                return "media";
            }
        }

        protected override string IconCssClass
        {
            get
            {
                return "";
            }
        }

        public override IEnumerable<SearchResult> Search(Query query)
        {
            ContentReference root = null;
            if (query.SearchRoots != null && query.SearchRoots.Count() > 0)
            {
                root = new ContentReference(query.SearchRoots.First());
            }
            else
            {
                root = new ContentReference();
            }

            var contentRepository = ServiceLocator.Current.GetInstance<IContentRepository>();
            var contentTypeRepository = ServiceLocator.Current.GetInstance<IContentTypeRepository>();
            var contentProviderManager = ServiceLocator.Current.GetInstance<IContentProviderManager>();
            var mediaTypes = contentTypeRepository.List().Where(x => typeof(MediaData).IsAssignableFrom(x.ModelType));
            var provider = contentProviderManager.GetProvider(root);

            if (provider != null && provider.HasCapability(ContentProviderCapabilities.Search))
            {
                List<IContent> mediaList = new List<IContent>();
                foreach (var blockType in mediaTypes)
                {
                    mediaList.AddRange(provider.ListContentOfContentType(blockType).Where(x => x.Name.IndexOf(query.SearchQuery, StringComparison.InvariantCultureIgnoreCase) >= 0).Select(x => contentRepository.Get<IContent>(x.ContentLink)));
                }

                return mediaList.Select(CreateSearchResult);
            }

            return Enumerable.Empty<SearchResult>();
        }
    }

After adding these search providers, now I can enjoy the filtering feature. You can customize the code to fit your need and improve the performance (for example: implement caching mechanism).

Apr 02, 2018

Comments

Please login to comment.
Latest blogs
Looking back at Optimizely in 2025

Explore Optimizely's architectural shift in 2025, which removed coordination cost through a unified execution loop. Learn how agentic Opal AI and...

Andy Blyth | Dec 17, 2025 |

Cleaning Up Content Graph Webhooks in PaaS CMS: Scheduled Job

The Problem Bit of a niche issue, but we are building a headless solution where the presentation layer is hosted on Netlify, when in a regular...

Minesh Shah (Netcel) | Dec 17, 2025

A day in the life of an Optimizely OMVP - OptiGraphExtensions v2.0: Enhanced Search Control with Language Support and Synonym Slots

Supercharge your Optimizely Graph search experience with powerful new features for multilingual sites and fine-grained search tuning. As search...

Graham Carr | Dec 16, 2025

A day in the life of an Optimizely OMVP - Optimizely Opal: Specialized Agents, Workflows, and Tools Explained

The AI landscape in digital experience platforms has shifted dramatically. At Opticon 2025, Optimizely unveiled the next evolution of Optimizely Op...

Graham Carr | Dec 16, 2025

Optimizely CMS - Learning by Doing: EP09 - Create Hero, Breadcrumb's and Integrate SEO : Demo

  Episode 9  is Live!! The latest installment of my  Learning by Doing: Build Series  on  Optimizely Episode 9 CMS 12  is now available on YouTube!...

Ratish | Dec 15, 2025 |

Building simple Opal tools for product search and content creation

Optimizely Opal tools make it easy for AI agents to call your APIs – in this post we’ll build a small ASP.NET host that exposes two of them: one fo...

Pär Wissmark | Dec 13, 2025 |