0 comments on “SXA Installation Pitfalls”

SXA Installation Pitfalls

When installing SXA on a fresh Sitecore instance, things will generally go pretty smoothly, however, when installing SXA on an existing Sitecore instance with customisations, it’s not necessarily quite so simple.  Here’s a summary of some of the issues that were encountered when recently installing SXA 1.4 onto a Sitecore 8.2.4 instance.  Hopefully this post will help if you encounter similar issues.

Dependency Injection

In this instance, there was code that was registering dependencies using wildcards.  Specifically, it was adding (among others) assemblies matching the pattern “*.Feature.*”.  This was wrongly picking up SXA assemblies and was giving errors in the Experience Editor like:

Error Rendering Controller: BrowserTitle. Action: Index: Could not create controller: 'BrowserTitle'.

To fix this, the Dependency Injection code was altered to exclude all assemblies matching the pattern “*.XA.*”.

Microsoft.CodeDom.Providers.DotNetCompilerPlatform.dll

The Microsoft.CodeDom.Providers.DotNetCompilerPlatform.dll file packaged in SXA 1.4 is an older version than what is being used throughout the solution we’re updating, so when trying to install the package, the installation failed halfway through. To fix this, the SXA install package was altered using 7-zip to replace the included DLL with the newer version.

Missing Rendering IDs (no null check)

This issue may be specific to this instance, and might be due to invalid data, but it’s worth mentioning, since SXA is not doing a null check and this problem may crop up for you, too.

When viewing a particular page, the following exception was thrown:

Exception: System.NullReferenceException
Message: Object reference not set to an instance of an object.
Source: Sitecore.XA.Feature.Composites
at Sitecore.XA.Feature.Composites.Pipelines.GetXmlBasedLayoutDefinition.InjectCompositeComponents.GetCompositeComponents(XElement layoutXml)
at Sitecore.XA.Feature.Composites.Pipelines.GetXmlBasedLayoutDefinition.InjectCompositeComponents.Process(GetXmlBasedLayoutDefinitionArgs args)

This turned out to be some renderings that had no ID, and SXA wasn’t doing a null check. The specific xmlLayout is shown below:


Editing the raw values and removing the renderings with a null ID fixes this problem.

To check if there were any other pages with this same problem, a small powershell script was written:

$items = Get-ChildItem -Path "master:/sitecore/content/Consumer/Home" -Recurse
foreach($item in $items) {
    $renderings = Get-Rendering -Item $item
    foreach($rendering in $renderings) {
        if($rendering.ItemID -eq $null) {
            Write-Host Item: $item.DisplayName $item.ID
        }
    }
}

Media Library/Project Folder

The SXA installation wants to create its own “Media Library/Project” folder, using a specific ID. If this item with the specific ID doesn’t exist, SXA will fail when creating a Tenant or Site.   Your instance may already have a folder by this name, in which case it will need to be renamed before the SXA installation, then it’s contents moved into the folder created by SXA.

Duplicate Navigation Controllers

For this instance, when adding the SXA navigation rendering to a page, the following exception occurs:

Multiple types were found that match the controller named 'Navigation'. This can happen if the route that services this request ('{*pathInfo}') does not specify namespaces to search for a controller that matches the request.

There may be other similar controllers in your instance where you get the same problem.

0 comments on “Geocoding Australian postcodes”

Geocoding Australian postcodes

While working on some code that allowed a user to perform a search using only a postcode, I discovered some strange behaviour with the Google Maps API.

According to the Google Maps API documentation (https://developers.google.com/maps/documentation/geocoding/intro#ComponentFiltering), component filtering allows you to filter by postal_code and country, which would suit this need perfectly. I gave this a try, and upon initial testing, it seemed that this was the solution, however, after further testing, it was found that for some postcodes (specifically, some in NSW and ACT), the geocoding query would return ZERO_RESULTS. Maybe this is because there is an overlap in the 2000 series postcodes ¯\_(ツ)_/¯.

An example of the URL I was using for this is shown below (note that postcode 2022 and country AU will return ZER0_RESULTS):

http://maps.googleapis.com/maps/api/geocode/json?components=country%3aAU%7Cpostal_code:2022&sensor=false&client=your_client&channel=web&language=en&signature=your_signature

There are many examples on Stack Overflow of people using this format to search by postcode and claim this to be the solution, but most of them are either from other countries, where this probably isn’t an issue, or they mustn’t have discovered this issue.

According to the Google Maps API documentation, you can use administrative_area (among other fields) to “influence” results, so I tried adding the state to this field, and I found that this made everything work properly. That means that the following URL will geocode the postcode 2022:

http://maps.googleapis.com/maps/api/geocode/json?&components=country%3aAU%7Cpostal_code%3a2022%7Cadministrative_area%3aNSW&sensor=false&client=your_client&channel=web&language=en&signature=your_signature

The issue I had then was that if the user is searching only using the postcode, I had to find a way to provide the state for that postcode to Google so it could geocode the postcode properly. To do this, I created a function that gives me the state based on a postcode as shown below (although this is not a perfect solution, because new postcodes are added from time to time. Potentially a call to an Australia Post API or similar may work better going forward):


public static string PostcodeToState(int postcode)
{
var postcodes = new List();
postcodes.AddRange(Enumerable.Range(1000,1000).Select(x => new KeyValuePair("NSW",x)));
postcodes.AddRange(Enumerable.Range(2000, 600).Select(x => new KeyValuePair("NSW", x)));
postcodes.AddRange(Enumerable.Range(2619, 280).Select(x => new KeyValuePair("NSW", x)));
postcodes.AddRange(Enumerable.Range(2921, 79).Select(x => new KeyValuePair("NSW", x)));

postcodes.AddRange(Enumerable.Range(200, 100).Select(x => new KeyValuePair("ACT", x)));
postcodes.AddRange(Enumerable.Range(2600, 19).Select(x => new KeyValuePair("ACT", x)));
postcodes.AddRange(Enumerable.Range(2900, 21).Select(x => new KeyValuePair("ACT", x)));

postcodes.AddRange(Enumerable.Range(3000, 1000).Select(x => new KeyValuePair("VIC", x)));
postcodes.AddRange(Enumerable.Range(8000, 1000).Select(x => new KeyValuePair("VIC", x)));

postcodes.AddRange(Enumerable.Range(4000, 1000).Select(x => new KeyValuePair("QLD", x)));
postcodes.AddRange(Enumerable.Range(9000, 1000).Select(x => new KeyValuePair("QLD", x)));

postcodes.AddRange(Enumerable.Range(5000, 1000).Select(x => new KeyValuePair("SA", x)));

postcodes.AddRange(Enumerable.Range(6000, 798).Select(x => new KeyValuePair("WA", x)));
postcodes.AddRange(Enumerable.Range(6800, 200).Select(x => new KeyValuePair("WA", x)));

postcodes.AddRange(Enumerable.Range(7000, 1000).Select(x => new KeyValuePair("TAS", x)));

postcodes.AddRange(Enumerable.Range(800, 200).Select(x => new KeyValuePair("NT", x)));

postcodes.Add(new KeyValuePair("ACT", 2620));
postcodes.Add(new KeyValuePair("NSW", 3644));
postcodes.Add(new KeyValuePair("NSW", 3707));

return postcodes.Where(x => x.Value == postcode).Select(x => x.Key).FirstOrDefault();
}

 

0 comments on “Responsive Images and Sitecore”

Responsive Images and Sitecore

responsive

By February 2018 Australians already spent more than double the amount of time on smartphones than on their desktop1. With the greater variety of devices consumers use to access sites, it’s important to serve images which appropriately cater to those devices.

“Responsive images” describes a technique where an image is served to the browser depending (usually) on the width of the browser window. Desktop browsers would generally receive a larger version of the image with a greater download size, and mobile devices a smaller version better suiting the smaller display size of the device and being quicker to download.

One of our clients has a Sitecore 7.2 site which uses both MVC and legacy code in ASP.NET WebForms. The legacy code had multiple ways of handling image resizing, mostly inline HTML hard coding of dimensions. My aim was to provide a single C# library function that could work both with legacy code and new development and allow consistent results.

imagetag

In the newer part of the website, the designer had used a JavaScript based image processor which would allow the browser to determine the optimal resolution of the image required and only request an appropriately sized image from the server.

As you can see from the above HTML sample each image tag has multiple URLs in the data-srcset attribute. From this list the JavaScript library determines the optimal size for the image, based on device and browser size, and only requests that version of the image from the server.

Sitecore provides a tool to provide the resized image files based on the request URL and also caches these resized images. We created a helper function that could take a Sitecore image as input and return all of the URLs representing valid image sizes as a delimited string. The valid sizes are determined by a setting in a config file. This return value is used to populate the data-srcset attribute of our img.

ImageCodeSample

Sitecore 7.5 and beyond require a hash code to be added to the URL2 to prevent Denial of Service attacks by making numerous requests for images of various sizes. Because our new helper function now provides a centralised and configurable way to deliver the required URL string, this will be quite easy to change after the upgrade.

References

  1. http://www.nielsen.com/au/en/press-room/2018/february-2018-digital-ratings.html
  2. http://www.seanholmesby.com/images-not-resizing-in-sitecore-7-5-sitecore-8-0/
0 comments on “Monitoring and Debugging Interaction Processing in Sitecore 9 on Azure PaaS”

Monitoring and Debugging Interaction Processing in Sitecore 9 on Azure PaaS

When configuring a new instance of Sitecore XP or maintaining an existing one, you may encounter a situation where your interactions report shows far fewer interactions than expected.
low-interactions
Where are my interactions?
One possible cause is interaction processing which hasn’t kept up with the interactions being logged on your website. In some cases this can be so slow that it appears collection, processing, and reporting aren’t working at all. Here are a few things you can look at to help you diagnose your issue.

 

Are interactions being recorded?

SELECT TOP 10 * FROM xdb_collection.Interactions ORDER BY StartDateTime ASC
Run this command in each of your shard databases to see the recent interactions which have been recorded. Compare the interactions being logged with the expected number and frequency of interactions in the environment you’re looking at.

 

How many interactions are waiting to be processed?

SELECT COUNT(*) FROM xdb_processing_pools.InteractionLiveProcessingPool
This command will indicate the number of interactions waiting to be processed. Monitoring the number of records in this table can give you an indication of the number of new records being created and the number of new interactions which are being queued for processing.
If the number of records is steadily building up, either processing isn’t working or it’s working too slowly to handle the workload.
If you’re collecting interactions but not seeing the size of the live interaction processing pool change at all, there might be an issue with aggregation.

If Analytics reports don’t look quite right, there are some things you can try:

Disable device detection

We encountered an issue with slow processing on a recent project. After logging an issue with Sitecore support, they advised:
Device detection has been known to cause the slowness in rebuilding reporting DB.
Try disabling device detection to determine if this has been impacting the speed of processing.

 

Check the CPU usage on your processing role

If you’re consistently seeing a high level of activity, you may need to scale your processing instances up or out.

high-average-cpu
Time for more instances…

Check connection strings

Use the Server Role Configuration Reference to ensure you have the correct settings on each of your servers

Check Application Insights errors

Check in Application Insights for any repeated error messages that might indicate misconfiguration.

 

millions-of-interactions
That’s more like it!

Helpful links

0 comments on “Downgrading Helix modules from Sitecore 8.2 to 7.2”

Downgrading Helix modules from Sitecore 8.2 to 7.2

Recently I had to update a Sitecore 7.2 site to use Helix architecture and bring some foundation modules in from a Sitecore 8.2 site.  There were several challenges with this process.  Here’s a few highlights:

Dependency Injection using Castle Windsor

For this site, Castle Windsor was being used for dependency injection.  In the Sitecore 8.2 site, dependencies are registered using a configurator in a .config patch.  This is not supported in Sitecore 7.2, so instead we have to do this in the Application_Start event in global.asax.cs.

In order to be able to use the same DI container throughout the code, I created a singleton container object (ContainerManager.Container):

public static class ContainerManager
 {
   private static IWindsorContainer _container;
   public static IWindsorContainer Container
   {
     get
     {
        if (_container != null) return _container;
        _container = new WindsorContainer();
        _container.Install(FromAssembly.This());
        return _container;
     }
   }
}

Then in global.asax.cs, I used that container to register the dependencies:

protected void Application_Start(object sender, EventArgs e)
 {
    _container = ContainerManager.Container;
    _container.Install(new RegisterGlassDependencies());
    ...

An example of the installer class for registering dependencies is shown below:

 public class RegisterGlassDependencies : IWindsorInstaller
 {
   public void Install(IWindsorContainer container, IConfigurationStore store)
   {
     container.Register(Component.For<ISitecoreContext>().ImplementedBy<SitecoreContext>());
     container.Register(Component.For<IGlassHtml>().ImplementedBy<GlassHtmlTemp>());
     container.Register(Component.For<IGlassFactory>().ImplementedBy<GlassFactory>());
   }
 }

In order for injected constructor parameters to resolve for Controllers, it is necessary to use a custom controller factory which uses the DI container to resolve the controller. There’s a fair bit of overriding going on, so here it comes.

Code for the controller factory shown below:

public class WindsorControllerFactory : DefaultControllerFactory
   {
       private readonly IWindsorContainer _container;
 
       public WindsorControllerFactory(IWindsorContainer container)
       {
           _container = container;
       }
 
       public override void ReleaseController(IController controller)
       {
           _container.Release(controller);
       }
 
       public override IController CreateController(RequestContext requestContext,string controllerName)
       {
           Assert.ArgumentNotNull(requestContext, "requestContext");
           Assert.ArgumentNotNull(controllerName, "controllerName");
           Type controllerType = null;
 
           if (TypeHelper.LooksLikeTypeName(controllerName))
           {
               controllerType = TypeHelper.GetType(controllerName);
           }
 
           if (controllerType == null)
           {
               controllerType = GetControllerType(
                   requestContext,
                   controllerName);
           }
 
           if (controllerType != null)
           {
               return (IController)_container.Resolve(controllerType);
           }
 
           return base.CreateController(requestContext, controllerName);
       }
   }

A pipeline was added with an “instead” patch for Sitecore.Mvc.Pipelines.Loader.InitializeControllerFactory.  This pipleine scans all assemblies and gets all classes based on IController then registers them with the Containermanager.Container.  Code shown below:

public class InitializeWindsorControllerFactory
   {
       public virtual void Process(ScapiPipelineArgs args)
       {
           SetupControllerFactory(args);
       }
 
       protected virtual void SetupControllerFactory(ScapiPipelineArgs args)
       {
           var container = ContainerManager.Container;
 
           //TODO: don't use hard-coded filter string
           var assemblies = GetAssemblies.GetByFilter("MyAssembly.*").Where(n => !n.FullName.StartsWith("MyAssembly.Service"))
               .Where(a => GetTypes.GetTypesImplementing<IController>(a).Any(x => x.Namespace != null && x.Namespace.StartsWith("MyNamespace")));
 
           foreach (var assembly in assemblies)
           {
               container.Register(Classes.FromAssembly(assembly).BasedOn<IController>().LifestyleTransient());
           }
 
           var controllerFactory = new WindsorControllerFactory(container);
 
           var scapiSitecoreControllerFactory = new
               ScapiSitecoreControllerFactory(controllerFactory);
 
           ControllerBuilder.Current.SetControllerFactory(scapiSitecoreControllerFactory);
       }
   }

Another pipeline was added with an “instead” patch for Sitecore.Mvc.Pipelines.Response.GetRenderer.GetControllerRenderer.  This pipeline allows us to use a custom controller renderer, as shown below:

public class GetControllerRenderer : Sitecore.Mvc.Pipelines.Response.GetRenderer.GetControllerRenderer
   {
       protected override Renderer GetRenderer(Rendering rendering, Sitecore.Mvc.Pipelines.Response.GetRenderer.GetRendererArgs args)
       {
           var renderer = base.GetRenderer(rendering, args);
           return !(renderer is ControllerRenderer) ? renderer : new CustomControllerRenderer(renderer as ControllerRenderer);
       }
   }

The custom controller renderer then allows us to use a custom controller runner as shown below:

public sealed class CustomControllerRenderer : ControllerRenderer
   {
       public CustomControllerRenderer(ControllerRenderer renderer)
       {
           ControllerName = renderer.ControllerName;
           ActionName = renderer.ActionName;
       }
 
       public override void Render(System.IO.TextWriter writer)
       {
           var controllerName = ControllerName;
           var actionName = ActionName;
           if (controllerName.IsWhiteSpaceOrNull() || actionName.IsWhiteSpaceOrNull())
           {
               return;
           }
           var controllerRunner = new CustomControllerRunner(controllerName, actionName);
           var value = controllerRunner.Execute();
           if (value.IsEmptyOrNull())
           {
               return;
           }
           writer.Write(value);
       }
 
   }

The custom controller runner then creates the controller using our custom controller factory with our DI container as a parameter.  This is our goal. Custom controller runner shown below:

public class CustomControllerRunner : ControllerRunner
   {
       public CustomControllerRunner(string controllerName, string actionName)
           : base(controllerName, actionName)
       { }
 
       protected override IController CreateController()
       {
           return CreateControllerUsingFactory();
       }
 
       private IController CreateControllerUsingFactory()
       {
           NeedRelease = true;
 
           var controllerFactory = new WindsorControllerFactory(ContainerManager.Container);
           return controllerFactory.CreateController(PageContext.Current.RequestContext, ControllerName);
       }
   }

There’s a few layers to it, but we got there in the end.

XUnit Tests

The tests that I moved over from the Sitecore 8.2 site used XUnit.  After moving them over, one of the errors I got in the Sitecore 7.2 site was:

Could not resolve type name: Sitecore.Data.DefaultDatabase, Sitecore.Kernel

To fix this,  for Sitecore versions prior to 8.2 all instances of ‘Sitecore.Data.DefaultDatabase, Sitecore.Kernel’ in the config files should be changed to ‘Sitecore.Data.Database, Sitecore.Kernel’.

One bit I got stuck on for a while was that there was a config file with this string in it that had not yet been added to the solution, but was there on the file system, and that was enough for this error to still be thrown.  It took a file system search for the string to track it down.

 

 

0 comments on “Advanced Scheduled Publishing”

Advanced Scheduled Publishing

Why do we want to schedule publishing?

  1. We want to schedule publishing to ensure that content scheduled to go live will go live when expected. By default, if a scheduled publish isn’t setup any content scheduled to go live will have to wait until the next manual publish after the scheduled date/time.
  2. We want to remove the publish option from editors so that publishing isn’t over utilised.
  3. Reducing the amount of publishes in a day reduces the number of times the Sitecore HTML cache is cleared and therefore increase site performance.

The out of the box scheduled publishing agent provided by Sitecore solves point 1 and 2 above but the issue is publishing at times when a publish is not required. The default publishing agent will publish every x interval all day. This is not efficient because it is publishing when possibly no changes have occurred and that will still cause the HTML cache to clear.

Our Advanced Scheduled Publishing Module provides the option to schedule a publish between a start and finish date and set the interval. For example 9-5 and every 60 minutes.

It also provides the option to set up one-off scheduled publishes at a specific time. So, for example, one publish at 2pm and one at 2am.

These two options can be used individually or in combination. In combination you can allow for a common scenario where we want to publish every 60mins from 9am – 6pm and then a one-off publish at 12am so that any content scheduled to be published will do so ready for the new day.

The configuration of these intervals are managed via a configuration item you create and manage in Sitecore. The documentation found here. The last publish time is updated here also, allowing editors to have more visibility on when the last publish occurred.

Our module is built on top of the Sitecore scheduled tasks so it will check for a time and interval within the range of the scheduled task frequency so, therefore, could occur slightly earlier or later depending on your frequency value. This is a well-documented limitation as the Sitecore scheduled tasks run within the context of a web application which could go down, or be taken down at any time.

Links

Sitecore Marketplace

Github repo

 

0 comments on “Getting settings from Sitecore config that aren’t in the settings section”

Getting settings from Sitecore config that aren’t in the settings section

Recently I had to write some code that got the value of the scheduling frequency in Sitecore config.  Normally, to get a value from Sitecore config, I’d simply use:

Sitecore.Configuration.Settings.GetSetting("yoursettingsname")

However, in this case, the value that I want is not in the <settings> node and the above code won’t work.  The value I want is in the <scheduling> node:

 <!-- SCHEDULING -->
 <scheduling>
 <!-- Time between checking for scheduled tasks waiting to execute -->
 <frequency>00:05:00</frequency>

To get around this, instead, I used:

Sitecore.Configuration.Factory.GetConfigNode("scheduling/frequency")?.InnerXml

The above code should work for any Sitecore config item just by specifying the correct Xpath location for the value you require.

1 comment on “Aceik’s Jason Horne Wins Sitecore “Most Valuable Professional” Award 2017”

Aceik’s Jason Horne Wins Sitecore “Most Valuable Professional” Award 2017

Elite distinction awarded for exceptional contributions to the Sitecore community sitecore_mvp_logo_technology_2017

Melbourne, Victoria, Australia — January 31, 2017, Aceik today announced that Jason Horne has been named a “Most Valuable Professional (MVP)” by Sitecore®, the global leader in experience management software. Jason Horne was one of only 215 Technologist worldwide to be named a Sitecore MVP this year.

Now in its eleventh year, Sitecore’s MVP program recognises individual technology, digital strategy, commerce, and cloud advocates who share their Sitecore passion and expertise to offer positive customer experiences that drive business results. The Sitecore MVP Award recognises the most active Sitecore experts from around the world who participate in online and offline communities to share their knowledge with other Sitecore partners and customers.

Aceik provides a complete range of Sitecore services; architecture, development, integration, support and maintenance, intranets, expert help, existing site audits and upgrades. As Sitecore specialists, Aceik strives to provide excellence in every Sitecore project we undertake.

Aceik contributes to the Sitecore community through thought leadership content on our blog, the Sitecore community blog and stack overflow. We co-founded the Melbourne Sitecore user group, founded the Sitecore ANZ certified developers group and continue to be involved in their ongoing management with the goal to share knowledge, learn and build the Sitecore community within Australia and New Zealand.

“The Sitecore MVP awards recognise and honour those who make substantial contributions to our loyal community of partners and customers,” said Pieter Brinkman, Director of Developer and Platform Evangelism, Sitecore. “MVPs consistently set a standard of excellence by delivering technical chops, enthusiasm and a commitment to giving back to the Sitecore community. They truly understand and deliver on the power of the Sitecore Experience Platform to create contextualised brand experiences for their consumers, driving revenue and loyalty for life.”

Sitecore’s experience platform combines web content management, omnichannel digital delivery, customer insight and engagement, and strategic digital marketing tools into a single, unified platform. The platform is incredibly easy to use, capturing every minute interaction—and intention—that customers and prospects have with a brand, both on a website and across other online and offline channels. The end-to-end experience technology works behind the scenes to deliver context marketing to individual customers so that they engage in relevant brand experiences that earn loyalty and achieve business results.

Aceik is a boutique web development company specialising in the technical implementation and support of Sitecore solutions. 100% of our work is Sitecore related and we pride ourselves on being experts in our field.

Jason Horne, Founder and Sitecore specialist
Aceik
jasonhorne@aceik.com.au
0426971867

More information can be found about the MVP Program on the Sitecore MVP site: http://www.sitecore.net/mvp

0 comments on “Advanced System Reporter Custom Reports Part 4”

Advanced System Reporter Custom Reports Part 4

Custom Viewer Report

For this example, we will build a report which displays a set of results based on a Sitecore Query with a custom viewer to display additional fields from your custom solution.

Requirements:

The report will display all items under the content path selected by the user. The report must also display the “Title” field in the report results in addition to the standard fields.

Let’s build it

Step 1: Open up the ASR source code project in Visual Studio. Find the ItemViewer class, then copy it to your solution in an appropriate library. You will need to reference the ASR and ASR.Reports DLLs.

Rename the viewer to “CustomItemViewer” for the purposes of this example.

Now we can add our custom field to the view by editing the “getColumnText” and “AvailableColumns” methods.

ASRpart8

ASRpart9.png

Step 2: Create a viewer item called “Custom Item Viewer” and configure as per the image. In the Viewer field click edit and add the new Title field to the list in the order you wish to display it in your report.

ASRpart10.png

Step 3: Create a report item called “Custom Item Viewer Report” and configure as per the image.

ASRpart11.png

Step 4: Run the ASR report and find your custom field displayed in the report results in the order you defined in the viewer.

 

0 comments on “Advanced System Reporter Custom Reports Part 3”

Advanced System Reporter Custom Reports Part 3

Simple Custom Parameters Report

For this example, we will build a report which displays a set of results based on a Sitecore Query and custom parameters.

Requirements:

The report will display all items under a path selected by the user in the content tree and only return items of a template selected by the user.

Let’s build it

Step 1: Create a new filter called “Items of Type” and configure as per the image.

ASRpart1

Step 2: Create a Parameter item called “TemplateID” and configure as per the image.

ASRpart2

Note that the root id refers to the root templates folder you wish the user to be able to filter from.

Step 3: Create a report item called “Items by Template” and configure as per the image.

ASRpart3

Step 4: Run the report from ASR. Here you can see we have run the report to return all articles within the categories node of the content tree.

ASRpart4