Wildcards Module for Sitecore 7+ using Content Search API

Will share the alternative implementation of Wildcards module of Adam Conn’s original idea.

The problem is that when it comes to Sitecore 7 or later this module appears not to be working out of the box due to changed Sitecore Rules Engine. And to be honest, working with rules may be a bit confusing and I found it hard to debug. However there is still a way to fix the original module https://github.com/madoibito/revised-sitecore-wildcard-module-for-sitecore7.1orlater/wiki/How-to-change-wildcard-module-items-for-Sitecore-7.1-or-later-step-by-step for Sitecore 7+, but I needed some more flexibility.
So decided to implement something alternative.

So the task is as before – supporting data-driven URLs via Sitecore Wildcard (*) items.

All source code can be found here: https://github.com/vhil/helpfulcore-wildcards
The packed Sitecore module is in the same repository in its root.

Feature overview:

  • Not using Rules Engine anymore
  • Built in page item resolving and URL generation
  • Separate configuration for Item Resolving using ContentSearch API
  • Separate configuration for item URL generation
  • Single place for configuring the wildcards route
  • Fully configurable, extendable, easy to be changed and debuged.

I am going to play around with my ‘Fortis Demo’ solution and all project-relalted code and items can be found here: https://github.com/vhil/fortis-demo

So lets imagine,  I want to implement a product page in my solution, but will keep all my products in the item bucket outside of the website tree.
I have created a new page type template ‘Product Page’ which contains few fields and made it bucketable:

  • Product ID
  • Content Title
  • Content Image
  • Content Body

And created an item bucket under the shared content folder outside the website tree:

products-bucket

And on the site I want to show my products under /products item and my url for example has to be like this:


http://fortis.demo/products/%5Bproduct-id%5D/%5Bproduct-item-name%5D

So I created item tree under the website with wildcards in their names


/sitecore/content/Sites/fortis-demo/products/*/*

products-wildcard-item

Ok. Now I will implement a rendering that will list me my products on the product overview page


/sitecore/content/Sites/fortis-demo/products.

products-overview

Currently my rendering generated product detail URLs which contain bucket item paths:


http://fortis.demo/sitecore/content/shared-content/products-repository/2016/02/21/20/03/product-a
http://fortis.demo/sitecore/content/shared-content/products-repository/2016/02/21/20/11/product-b

But I want my urls to match the format mentioned above.
So I install a “Helpfulcore Wildcards Module-1.0.0” module.

All I need to do now is to register and configure a ‘wildcard route’ for my project page. I go to /sitecore/system/Modules/Wildcards/Routes item and create a new ‘Wildcard Route’ item under it.

wildcard-route
‘Wildcard Route’ template has fields for two main sections which reflect the module behavior:

  • Url Generation
  • Item Resolving

Url Generation:

url-generation

  • Wildcard Items – here I am referencing my endpoint wildcard item (in my case /sitecore/content/Sites/fortis-demo/products/*/*). Note that this is a TreelistEx field – this is done for multisite support. In case of multiple sites on your sitecore instance  just add all wildcard items from each site here and the module will resolve the most suitable one while url generation.
  • Url Generation Rules – this is a ‘Name Value List’ field and here we configure which wildcard should be replaces with which value related to the item.

For this field I have implemented a custom item reader which accepts its own syntax. It is actually pretty simple:

  • Prefix your fields with ‘@’. For example: @__Display Name
  • Prefix your properties with ‘@@. For example: @@Name
  • Use ‘.’ delimiter for creating a chain

Examples

  • @@Name
  • @__Display Name
  • @@Paths.@@Path
  • @@Parent.@@Name
  • @Custom Link Field.@@Name
  • @@Template.@@InnerItem.@__Standard values.@@Paths.@@Path

Notes:

  • Property names ARE case sensitive
  • Field names are NOT case sensitive

So in my case I set two values for each wildcard item:


{0, @Product ID}
{1, @@Name}

Which means

  • the wildcard at position [0] will be replaced with ‘Product ID’ field value of the item
  • the wildcard at position [1] will be replaced with ‘Name’ property value of the sitecore Item class.

In order to help to debug this syntax I have created an admin page which you can access by clicking on the ‘Url Generation Rules’ field title. It will lead you to /sitecore/admin/wildcardtokenextractor.aspx?sc_site=website page.

wildcard-tokenextractor

Here you can type the path or ID of testing item and write your query for concrete wildcard replacement. And then when you are sure your query gives correct result, just copy it into the field.

Item resolving:

I am hooking into the presentation layer and not the Item Resolving process by adding the processor into the <mvc.getPageItem> pipeline.


<pipelines>
      <mvc.getPageItem>
        <processor type="Helpfulcore.Wildcards.Pipelines.Response.GetPageItem.GetFromWildcard, Helpfulcore.Wildcards"
                   patch:after="processor[@type='Sitecore.Mvc.Pipelines.Response.GetPageItem.GetFromOldContext, Sitecore.Mvc']" />
      </mvc.getPageItem>
    </pipelines>

The item resolving is being implemented using the content search API and there is few more fields in the ‘Wildcard Route’ template for Item Resolving:

item-resolving

  • Item Search Root Node – specify here the root item of your item repository so the content search looks into items only inside of this item.
  • Item Templates – specify templates that the content search should filter by
  • Item Resolving Rules – and here in the same manner specify field for each wildcard. But now each value should represent the content search index field name. Note that not all wildcards are required to be participating in the item resolving, you can simply skip some of them and leave required ones only.

in my case that will be:


{0, product_id}
{1, _name}

Thats it!

Now if I refresh the product overview page I will see next urls being generated for my product items:

And when I click them I can see all the content taken from the resolved page item.

Configuration

The module installs an include config file /App_Config/Include/Helpfulcore/Helpfulcore.Wildcards.config


<?xml version="1.0" encoding="utf-8" ?>
<configuration xmlns:patch="http://www.sitecore.net/xmlconfig/">
  <sitecore>
    <settings>
      <setting name="WildcardWrapSearchTermsInEncodedQuotes" value="false" />
      <setting name="WildcardTokenizedString" value=",-w-," />
      <setting name="WildcardProvider.RoutesPath" value="/sitecore/system/modules/wildcards/routes" />
    </settings>
	<wildcardManager defaultProvider="wildcardResolver">
      <providers>
        <add name="wildcardResolver" type="Helpfulcore.Wildcards.WildcardRouteResolver, Helpfulcore.Wildcards" />
      </providers>
    </wildcardManager>
	<wildcardItemResolver defaultProvider="contentSearch">
      <providers>
        <add name="contentSearch" type="Helpfulcore.Wildcards.ItemResolving.ContentSearchWildcardItemResolver, Helpfulcore.Wildcards" />
      </providers>
    </wildcardItemResolver>
    <urlGenerationTokenValueExtractor defaultProvider="itemReader">
      <providers>
        <add name="itemReader" type="Helpfulcore.Wildcards.UrlGeneration.TokenValueExtraction.ItemReaderTokenValueExtractor, Helpfulcore.Wildcards" />
      </providers>
    </urlGenerationTokenValueExtractor>
    <pipelines>
      <mvc.getPageItem>
        <processor type="Helpfulcore.Wildcards.Pipelines.Response.GetPageItem.GetFromWildcard, Helpfulcore.Wildcards"
                   patch:after="processor[@type='Sitecore.Mvc.Pipelines.Response.GetPageItem.GetFromOldContext, Sitecore.Mvc']" />
      </mvc.getPageItem>
    </pipelines>
    <linkManager defaultProvider="sitecore">
      <providers>
        <add name="sitecore">
          <patch:attribute name="type">Helpfulcore.Wildcards.UrlGeneration.WildcardLinkProvider, Helpfulcore.Wildcards</patch:attribute>
        </add>
      </providers>
    </linkManager>
    <events>
      <event name="publish:end">
        <handler type="Helpfulcore.Wildcards.Events.PublishEnd.WildcardsCachePurgeProcessor, Helpfulcore.Wildcards" method="ClearCache" />
      </event>
      <event name="publish:end:remote">
        <handler type="Helpfulcore.Wildcards.Events.PublishEnd.WildcardsCachePurgeProcessor, Helpfulcore.Wildcards" method="ClearCache" />
      </event>
    </events>
  </sitecore>
</configuration>

As you can see I have moved to the configuration pretty much everything. And in every class I implemented I tried to write virtual methods and members only so you can override whatever you want if you need some customization.
Also feel free to fork the repo and suggest a fix or improvement.

Module Contents:

Files:

  • /App_Config/Include/Helpfulcore/Helpfulcore.Wildcards.config
  • /sitecore/admin/WildcardTokenExtractor.aspx
  • /bin/Helpfulcore.Wildcards.dll

Items:

  • /sitecore/templates/Wildcards/Wildcard Route
  • /sitecore/system/Modules/Wildcards/Routes

Hope this will be helpful 🙂

Advertisements

6 thoughts on “Wildcards Module for Sitecore 7+ using Content Search API

  1. Hi Chris,

    Replied there.
    Try to set a sitecore setting “ItemResolving.FindBestMatch” to “Disabled”

    Disabled

    Also, I did not pack latest code into module package on github (shame on me)…
    It seems that setting the Page Item to new resolved item leads to changing the Context Item on Sitecore 8.x now, which I believe is not correct.
    And so I am saving the original item into HttpRequest as
    HttpContext.Current.Items[“Wildcards.OriginalItem”]
    so you can get the original item from there (for example if you need to build breadcrumbs feature)
    Anyway I am using this module at least on 3 projects now and it works OK.

    Like

  2. Would it be possible to have the product in the reposity as an item that is a source of a datasource rather than a page?

    Then wildcard would do the matching and bind the datasource to this item?

    Like

    1. There is a way to use presentation settings from the original item and setting the wildcard resolved item to “rendering item”.
      F.x you have wildcard item
      http://host/products/*
      when you request http://host/products/product-a,
      the presentation details (layout and renderings will be taken from http://host/products/* item)
      and then the dataSource of all renderings (rendering item) on it will be set to resolved “product-a” item from repository.

      Is that what you want to achieve? If so I will try to find it for you (just not in top of my head).

      Like

  3. Hi vladimirhil,
    thank you very much for providing the wildcard solution, works like a charm.

    Any idea on how to generate sitemap links for shared content which I resolve via your wildcards within a page?

    Cheers,
    Christopher

    Like

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s