Custom Alias Resolver for Sitecore media item

Working on different projects and different type of tasks is always give an opportunity to learn something new as with my last implementation we came across with a very different requirement where client ask us to make the media url’s more friendly as Sitecore content tree URL’s. Or in other words if I say they ask us to build something which look like a Content Page but render like a media item. And that media can be sections related (Means URL for that media can be manageable as content tree architecture) So here I am going to write what I have build so far to complete this task.

We have created a Data Template (Media Reference) type with Droptree field. Droptree field we provided to map the media item. My plan was to give an option to create a simple content item with Media Reference template type and map the media item into droptree field.

It was easy to create but now the task was render the mapped media item onto media reference page. Here what I already know there is no out of the box functionality available to achieve this I need to customize any of the Sitecore pipeline which actually run the Item URL but render the mapped media item.

After reading few articles I got the understanding that If I can create Custom Alias Resolver than I can achieve this and I started digging into Sitecore dll’s for Alias Resolver.

I have created a class CustomAliasResolver and inherited by HttpRequestProcessor.

After that I copied the code of Alias Resolver into this class and made the tweaks as per my need. See below code to achieve this –


public override void Process(HttpRequestArgs args)
        {
            Assert.ArgumentNotNull((object)args, "args");
            Database database = Context.Database;
            if (database == null)
            {
                Tracer.Warning((object)"There is no context database.");
            }
            else
            {
                Profiler.StartOperation("Resolve custom alias.");
                if (ProcessItem(args))
                {
                    if (Context.Item != null)
                    {
                        string mediaUrl = MediaManager.GetMediaUrl((MediaItem)Context.Item);
                        if (!string.IsNullOrEmpty(mediaUrl))
                        {
                            string handler = HandlerUtil.GetHandler(mediaUrl);
                            if (!string.IsNullOrEmpty(handler))
                            {
                                Context.Data.RawUrl = mediaUrl;
                                args.Context.RewritePath(handler, mediaUrl, args.Url.QueryString, true);
                                args.AbortPipeline();
                            }
                            Context.Page.FilePath = mediaUrl;
                        }
                    }
                }
                Profiler.EndOperation();
            }
        }


        private static bool IsMediaItem(Item item)
        {
            Assert.ArgumentNotNull(item, "item");
            return item.Paths.IsMediaItem && item.TemplateID != TemplateIDs.MediaFolder;
        }

        private static string GetAbsoluteMediaUrl(MediaItem mediaItem)
        {
            string relativeUrl = MediaManager.GetMediaUrl(mediaItem);
            return WebUtil.GetFullUrl(relativeUrl);
        }


        private bool ProcessExternalUrl(HttpRequestArgs args)
        {
            string targetUrl = Context.Database.Aliases.GetTargetUrl(args.LocalPath);
            if (targetUrl.Length > 0)
                return this.ProcessExternalUrl(targetUrl);
            return false;
        }


        private bool ProcessExternalUrl(string path)
        {
            if (Context.Page.FilePath.Length > 0)
                return false;
            Context.Page.FilePath = path;
            return true;
        }


        private bool ProcessItem(HttpRequestArgs args)
        {
            var item = Sitecore.Context.Database.GetItem(args.Url.ItemPath);
            if (item != null && item.TemplateID == Templates.MediaReference.ID)
            {
                if (!string.IsNullOrEmpty(item[Templates.MediaReference.Fields.ContentReference]))
                {
                    ID targetId = new ID(item[Templates.MediaReference.Fields.ContentReference]);
                    if (!targetId.IsNull)
                    {
                        Item target = args.GetItem(targetId);
                        if (target != null)
                            this.ProcessItem(args, target);
                        return true;
                    }
                }

            }
            Tracer.Error((object)("An alias for \"" + args.LocalPath + "\" exists, but points to a non-existing item."));
            return false;
        }

       
        private void ProcessItem(HttpRequestArgs args, Item target)
        {
            if (Context.Item != null)
                return;
            Context.Item = target;
            Tracer.Info((object)("Using alias for \"" + args.LocalPath + "\" which points to \"" + (object)target.ID + "\""));
        }

After that I created a .config file and added below pipeline code into that config file. In below code added new processor to handle the custom Alias Resolver requests. And I added it just before  “AliasResolver” processor.

<sitecore>
  <pipelines>
    <httpRequestBegin>
        <processor patch:before="processor[@type='Sitecore.Pipelines.HttpRequest.AliasResolver, Sitecore.Kernel']" type="SitecoreDemo.Feature.MediaReference.CustomAliasResolver.CustomAliasResolver, SitecoreDemo.Feature.MediaReference"/>
    </httpRequestBegin>
   </pipelines>
 </sitecore>

After the above configuration and code I was able to render Media Item over the Sitecore Item. This code was for media item but we can achieve multiple similar types of requirement with the same approach.

Advertisements
Posted in Sitecore | Tagged , , | Leave a comment

Authenticate External System user’s into Sitecore as Virtual user

In my recent project we got a requirement where need to manage client current site user’s into Sitecore. First we thought to get User DB from client side and configure that with our current solution and use them in current implementation. But as far as client is having there own team to manage there UMS (user management system) which is build in java also they are regularly updating user details in there system. And it was a huge data of user ‘s which was very difficult to manage in Sitecore.

At the final staged we agreed to get the data or user details with the help of web services.

For example if we need to validate user for login than we need to provide the username and password of user and web service was resposible to validate the user as well as provide the required details of user for example  – Name, email id, phone etc.

Now here we started thinking to manage that web service user as Sitecore extranet user so we don’t need to worry about the security permission with Sitecore. Because many of the items in content tree was covered under Role based security.

First we started thinking to create a user in Sitecore at the time of login and then consider the user as extranet users and at the time of log out remove that user from the Sitecore. But after some search on Google I found Virtual user concept and it was really nice to manage the same thing which we were looking for because we were also looking for the virtual set up of user not completely into Sitecore. But after creating virtual user it work as a Sitecore extranet user.

Here we move with our study on virtual user  –

At the time of login user is providing username and password and click on sign in button first we are calling the web service that is basically validating the user from client UMS and than sending the user details in response.

Once we are getting the valid user response then we created virtual user and assign the values of user properties, below is the sample API to create virtual user in Sitecore –

var virtualUser = Sitecore.Security.Authentication.AuthenticationManager.BuildVirtualUser(userName, true);

here username is domain/username.

We also need to maintains multiple custom profile properties so we have created custom user profile item as given in this post.

Below is the sample API for assigning the custom values to custom user profile.

 virtualUser.Profile.FullName = webserviceResponse.response.firstName ?? String.Empty + webserviceResponse.response.lastName ?? String.Empty;
 virtualUser.Profile.Email = webserviceResponse.response.email ?? String.Empty;
 virtualUser.Roles.Add(Sitecore.Security.Accounts.Role.FromName("domain\\RoleName"));  
 virtualUser.Profile.SetCustomProperty(Constants.UserCustomProfileCons.FirstName, webserviceResponse.response.firstName ?? String.Empty);
 virtualUser.Profile.SetCustomProperty(Constants.UserCustomProfileCons.LastName, webserviceResponse.response.lastName ?? String.Empty);
 virtualUser.Profile.SetCustomProperty(Constants.UserCustomProfileCons.profileUID, webserviceResponse.response.profileUID ?? String.Empty);
 virtualUser.Profile.Save();

After storing all the custom profile values with virtual user we logged in virtual user by using Sitecore Authentication code as given below –

AuthenticationManager.LoginVirtualUser(virtualUser)

Now this work as same as Sitecore extranet user login. If we want to check if user is authenticated or not we can use the Sitecore API for same as below –

Sitecore.Context.User.IsAuthenticated;
This API with return true if you are logged in with Virtual user

Below are some examples to get user details on other pages after login with virtual user –

//To Get the username of current logged in user
Sitecore.Context.User.Name;

// TO get the value of any of the custom property
Sitecore.Context.User.Profile.GetCustomProperty("CustomPropertyName");

We have tested it with multiple login at the same time and it is working perfect for us. Please write your feedback in comment section if you find any issue with this approach or if we can make it more better.

 

Posted in Sitecore, Sitecore Authentication, Sitecore Security | Tagged , , , | Leave a comment

Custom Collection of Items in Sitecore Multilist field datasource

I recently got a requirement to show upcoming News (News which date is greater then today’s date) into multilist field on Sitecore Item from News collections in content tree. So after looking into the requirement first thing came in my mind to create a custom Multilist field as we need to show dynamic collections of news on the bases  of current date (Today’s date). Or we need to provide custom datasource to multilist which should be driven by our custom code.

As much as I know about datasource is we can use fast query and query in that but I was not sure we can use custom code also in Datasource field. And I am sure many of us don’t know about the same.

I can see few of blog post about the same but I think because of keyword or meta data it is very difficult to find so I thought to write it again.

First I have created a class file and inherited that class with IDataSource interface (We need to provide reference of Sitecore.Buckets.dll for IDataSource).  And then I have implemented ListQuery method which is defined in IDataSource interface.

ListQuery method return type is Array of items so here we can do our custom code to get the array of items and return that collection as given in below script  –

In below code I am returning upcoming news items  –

public Item[] ListQuery(Item item)
        {
            item = Sitecore.Context.ContentDatabase.GetItem("/sitecore/content/Habitat/Home/Modules/Feature/News/News");
            return item.Axes.GetDescendants().Where(i=>i.IsDerived(Templates.NewsArticle.ID)).Where(i=>DateUtil.IsoDateToDateTime(i[Templates.NewsArticle.Fields.Date])>DateTime.Now).ToArray();
        }
        

And then I logged in to Sitecore and defined the Datasource in datasource field. Now in datasource field we need to define  code:NameSpace.ClassName, AssmeblyName as per given in below screenshot.

We will come with something new in next post…..

Posted in Sitecore, Sitecore Multilist, Sitecore Search | Tagged , , , | Leave a comment

Track search Keywords on Sitecore website

While working on Sitecore search we got a requirement where we need to store keyword s which is visitor will search in our website. First I thought to store them into Sitecore Database by creating items. But once I started looking into analytics feature I got that Sitecore analytics provides feature to store Site searches (Keywords). And also it provide more feature after storing them.

So I planned to use the analytics to store our site search keywords into analytics.

I created below function to store the keyword into sitecore analytics database  –


public bool IsActive => Tracker.Current != null && Tracker.Current.IsActive;

 public virtual void TrackSiteSearch(Item pageEventItem, string query)
        {
            Assert.ArgumentNotNull(pageEventItem, nameof(pageEventItem));
            Assert.IsNotNull(pageEventItem, $"Cannot find page event: {pageEventItem}");
            if (this.IsActive)
            {
                var pageEventData = new PageEventData("Search", [Search Event Item ID])
                {
                    ItemId = pageEventItem.ID.ToGuid(),
                    Data = query,
                    DataKey = query,
                    Text = query
                };
                var interaction = Tracker.Current.Session.Interaction;
                if (interaction != null)
                {
                    interaction.CurrentPage.Register(pageEventData);
                }
            }
        }

[Search Event Item ID] – It is the item id of the search event item created on /sitecore/system/Settings/Analytics/Page Events/Search location of Sitecore.

After creating this function you just need to call the function after getting the search results as below –

  if (!string.IsNullOrWhiteSpace(query) && results.Any())
     {
          TrackSiteSearch(ContextItem.Item, query.QueryText);
     }

This will store keyword into analytics database whenever visitor will perform search.

Where we can see the stored keywords in Sitecore analytics reports –

1. Open the ExperienceAnalytics dashboard from Launch pad as given in below screenshot

launch pad.png

2. Navigate to Behavior section and click on Internal search it will show complete report of searched keywords with all other analytics details as shown below.

 

launch pad2

 

In next post we will discuss how to show top searches keywords in site.

 

Posted in Sitecore, Sitecore Analytics | Tagged , | Leave a comment

Sitecore Search and filtering

This is not something you can’t find on internet but I am sure I’ll write some content or tips which is not easily available. I worked on search many times and every time I came across with different scenarios.

Here I am writing about my recent work with Sitecore search (Lucene & Solr). I have used filtering in our search with the help of predicate builder. It gives more capabilities to apply multiple filters with single line code. I would love to use predicate builder while working on search or filtering the results.

Here are some tips which can help you to get specific results as per your requirements  –

Sitecore Search allows you to use Linq to perform your search queries and uses the IQueryable<T> interface.

using (var context = ContentSearchManager.GetIndex(ContextItem).CreateSearchContext())
{

var queryable = context.GetQueryable<SearchResultItem>();

}

Here ContextItem is SitecoreIndexableItem item. you don’t need to specify the index name here. It will automatically pick the index as per current item and database of current item. And queryable is a collection of index results.

Till yet we haven’t applied any type of filtering it is just to get the indexed items from the indexes which is created by Solr or Lucene.

Now to apply filters define a predicate builder as given below –

      var predicate = PredicateBuilder.True<SearchResultItem>();

  1. Get items for specific template type  –
    predicate = predicate.And<SearchResultItem>(i => i.TemplateId == [Template ID]);
  2. Get items under specific node or Items from specific location in Sitecore –
    predicate = predicate.And<SearchResultItem>(i => i.Paths.Contains([Location Item ID]));
  3. Get items for context language only  –
    predicate = predicate.And<SearchResultItem>(item => item.Language == Context.Language.Name);
  4. Get items which contains specific Keyword –
    predicate = predicate.And<SearchResultItem>(i => i.Content.Contains(Keyword));

Now once you applied all the conditions or created all the predicates then you just need to pass it into linq  –

var results= queryable .Where(builder).Select(i => (Item)i.GetItem());

it will give you the list of filtered items as per your filtering conditions.

I hope this post will be helpful to start with search and filtering in next post we will discuss about Indexing custom fields and search on specific field values.

 

 

 

Posted in Sitecore, Sitecore Search | Tagged | Leave a comment

Increase Limit of items in Multi-list field of Sitecore

After A long time I am back to write something here for Sitecore Community. I didn’t stop sharing good things but I change the place for sometime and I was writing good blog posts http://www.nttdatasitecore.com/blog you can read it may be helpful.

Today I am writing about a small condition or setting which I recently came across, Increase the limit of items in Multilist field.

How about we came to address this point. We have created 200+ items under single node and on another item we created a Multilist field. Now we need to assign the datasource of this multilist field to show all the 200+ items which we have created earlier.

We started with basic approach to assign the datasource by Sitecore query. And here we came to know that only few items are showing in Multilist for selection not all. From here we started thinking what could be the reason for same and as we know if we are using Sitecore query so there is a limit setting defined in sitecore.config to control the no of items in query as given below.

<setting name=”Query.MaxItems” value=”100″ />

We increased the item limit from 100 to 1000 and after making this changes we are able to see all the items for selection in Mulitlist field.

Sometime a small setting can save your lots of time.

Happy Coding!!

 

 

 

Posted in Sitecore, Sitecore DMS | Leave a comment

Custom Field Type For Sitecore Web Form For Marketers

I have seen many questions about adding custom field in WFFM and adding session or querystring values in WFFM form field on form load.

I also got a same request to add querystring value in form field on load. WFFM module doesn’t provide any out of the box functionality to add session or querysting value in form field.

So finally I decided to create Custom Field in WFFM and add querystring value in that field. I started digging into Sitecore.Forms.Custom.dll and found code for SingleLineText field.

I created a custom field item with name CustomSingleLineText under /sitecore/system/Modules/Web Forms for Marketers/Settings/Field Types/Custom folder inside Sitecore and created a class in my class library project.

I extended code of SingleLineText field class from Sitecore.Forms.Custom.dll and added logic to show Session or Querystring value in text field on form load, see below code.

using Sitecore.Form.Core.Attributes;
using Sitecore.Form.Core.Visual;
using Sitecore.Form.Web.UI.Controls;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.ComponentModel.Design;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Web.UI;
using System.Web.UI.WebControls;

namespace Demo.Halper
{
     [Designer("System.Windows.Forms.Design.ParentControlDesigner, System.Design", typeof(IDesigner))]
     public class CustomSingleLineText : RegexInputControl
     {
         private static readonly string baseCssClassName = "scfSingleLineTextBorder";
        [VisualCategory("Validation"), VisualProperty("Maximum Length:", 2000), DefaultValue(256)]
        public int MaxLength
        {
            get
            {
               return this.textbox.MaxLength;
            }
            set
            {
               this.textbox.MaxLength = value;
            }
         }
         [VisualCategory("Validation"), VisualProperty("Minimum Length:", 1000), DefaultValue(0)]
         public int MinLength
         {
            get;
            set;
         }
         [VisualFieldType(typeof(CssClassField)), VisualProperty("Css Class:", 600), DefaultValue("scfSingleLineTextBorder")]
         public new string CssClass
         {
            get
            {
                return base.CssClass;
            }
            set
            {
                base.CssClass = value;
            }
         }
         public CustomSingleLineText(HtmlTextWriterTag tag)
         : base(tag)
         {
             this.MaxLength = 256;
             this.MinLength = 0;
             this.CssClass = CustomSingleLineText.baseCssClassName;
          }
          public CustomSingleLineText()
          : this(HtmlTextWriterTag.Div)
          {
          }
          protected override void OnInit(EventArgs e)
          {
              this.textbox.CssClass = "scfSingleLineTextBox";
              this.help.CssClass = "scfSingleLineTextUsefulInfo";
              this.generalPanel.CssClass = "scfSingleLineGeneralPanel";
              this.title.CssClass = "scfSingleLineTextLabel";
              this.textbox.TextMode = TextBoxMode.SingleLine;
              this.Controls.AddAt(0, this.generalPanel);
              this.Controls.AddAt(0, this.title);
              this.generalPanel.Controls.AddAt(0, this.help);
              this.generalPanel.Controls.AddAt(0, this.textbox);
              // Please add your login for session or querysting value
              this.textbox.Text = "Add your session or querystring value";
          }
     }
}

After created class and added custom logic I have added values in Assembly and Class fields on CustomSingleLineText field item in Sitecore. To show Querysting value on form field I used custom field instead of default Single Line Text field.

Happy Coding 🙂

Posted in Sitecore, Sitecore web form for marketers | Tagged , , , , , , , , , , | 1 Comment