Install Sitecore 9.3 using 3 Simple Commands

Sitecore 9.3 was announced at the Sitecore Symposium 2019 and I am excited to share 3 simple commands to get you going on your development machine or laptop. This release is now available from dev.sitecore.net to download.

Command 1 – Install Sitecore Pre-Requisites

As Sitecore moved from version 8.x to version 9.x, the installation process completely changed and required to install additional pre-requisites. There is a long list of pre-requisites and I have seen developers keeping a checklist of software that they need to install for a new instance of Sitecore on their machine or on client’s server. There is an alternative way, which has been part of the installation script since Sitecore 9.1 but not widely used. It is the Prerequisites.json file for installing pre-requisites. The easier and simpler way is to run this SIF command via PowerShell:

This command will not only download all of the required software but also install it on your behalf. I recently used this command on a vanilla VM and it took about 12 minutes to download and install everything. Notice in the screenshot below that it skips the already installed components:

Command 2 – Install Solr

For any Sitecore version 9.x to be installed locally, Solr (or Azure Search) is required to be installed and working correctly prior to installing Sitecore. Typically, a developer has to download and install Solr from a zip package, install it as a windows services and also install its SSL certificate (forgot to mention that you need to install Java SDK and setup its home path if you are on a brand new machine!).

Well, all of the above is now thing of the past, just open this Solr-SingleDeveloper.json file, update simple parameters like what version of Solr you want, what port number you would like to associate it with and what should be default install location and run this SIF command:

 

This is a MAGIC script and it did everything that would have taken anyone several hours to figure out, if it was their first time. Once the command was completed, I verified that the Solr was up and running over the specified SSL port!

 

Command 3 – Install Sitecore

Lastly, I opened up the XP0-SingleDeveloper.ps1 PowerShell script, change the essential parameters like site name and database parameters and ran the following script:

New Feature: By popular demand there is a new SIF parameter as SitePhysicalRoot.  If that is left empty, the default path will be \inetpub\wwwroot otherwise it will be the new path location.

If you have everything installed correctly then you will have a successful installation!!

Once the installation was complete, I was able to login (full marks for guessing the password)

I have started exploring the new features like the the new Experience Optimization Dashboard

 

Happy Sitecoring!

Sitecore Cortex – Getting Started with Auto-Tagging using Open Calais API

The latest release of Sitecore CMS 9.1 (initial release) ships with number of improvements and enhancements to the core platform. One of the key feature is introduction of the Sitecore CortexTM across the platform. Sitecore Cortex is a very broad topic and not the intent for the blog post, but in very short and non-tech words, Sitecore CortexTM can be described as the brain of Sitecore CMS that can analyze data and produce intelligent insights about the content.

One of the cool things that you can do right now with Sitecore CortexTM is auto-tagging. The integration with Open Calais API is out of the box, although you can integrate with any other provider by writing custom code. This post will show you to set up auto-tagging with Open Calais API provider.

What is Open Calais?

Open Calais analyzes the semantic content of input content using a combination of statistical, machine-learning, and custom pattern-based methods. The algorithms developed by the Text Metadata Services (TMS) group at Thomson Reuters output highly accurate and detailed metadata. For more detailed information, see the official Open Calais documentation FAQ.

Auto-Tagging Setup

Pre-requisites : You should have Sitecore 9.1 installed on your local machine or in Azure PaaS.

Step1 : Register a free account with Open Calais

Go to http://www.opencalais.com/opencalais-api/ and create a free account:

Step 2: Get your secret API Key

Click on the profile button and get your secret key. (Perm ID)

Step 3: The new Content Tagging Option

When you log-in to Sitecore 9.1, under the Home Tab, you will see a new Content Tagging option:

 

Step 4: Verify the initial tag field

Scroll down to the system defined Tagging section and verify there are no tags:

Step 5: Create a config patch

On your deployed folder, under location /site/App_Config/Sitecore/ContentTagging you will find files related to Open Calais settings. You can update the Sitecore.ContentTagging.OpenCalais.config file or a better option is to create a patch file as below and upload it to the /App_Config/Include folder location:

 

Step 6: Check the Tagging Tab

After you upload the file, you can go back to the content editor and check the ‘Content Tagging’ tab, it will be enabled

Step 7: Start Tagging

Click on the ‘Tag Item’ button and it will start tagging your content

Step 8: Review Tag Field

Once the tagging process is finished, if you review the tagging field again, it will be populated with the tags.

(You may need to click on another item and then click back on the item to see the tags)

 

That’s it, you are all configured to do auto-tagging.

 

Awesome Tommy Boy GIF - Find & Share on GIPHY

 

Reference: https://doc.sitecore.com/developers/91/sitecore-experience-management/en/configure-open-calais-as-your-content-tagging-provider.html

 

Sitecore Ship not working with 8.2-update-1 or failing silently

Back in May 2017, we started working an new and exciting project with Sitecore version 8.2 update-3. I was setting up the continuous integration/auto deployment for the project. Once CI/CD was setup as described in my previous blog posts I encountered the issue that Sitecore Ship module, which is responsible for synchronizing the Sitecore content items, was not working as expected. It was showing a 200 OK response in the Team City logs but in reality it was failing silently.

Doing a quick google about the issue I found at that I am not the only one having the issue, there are other people who are experiencing the same issue as well. I also came across this blog by another Sitecore developer who was facing exactly the same issue and he decided to switch to TDS package deployer instead of Sitecore Ship.

I haven’t personally tried out the TDS Sitecore package deployer before and looking at the blog post decided to take this approach. It was fairly easy to configure package deployer and after adjusting some of the Team City build steps, the .update package deployment started working!!

After testing package deployer with the continuous integration setup for few days, I realized that although the build process is copying across the .update files, there is no feedback from the package deployer (or a 200 OK response) that package has been installed successfully. In order to confirm, I have to RDP or FTP to the server and  check that .update files have been correctly installed or if they have failed for any reason.

So back to square 1, let’s figure out what is wrong with Sitecore Ship ?

Looking at the open issues list of the Sitecore Ship GitHub repository, I saw people reporting similar issues where Sitecore Ship was failing silently or not working on Azure Deployments.

Interestingly, there was also a pull request #69 submitted to fix this issue with 8.2 update-2 in Jan 2017 which had some really positive feedback.  As of today, this pull request has not been merged to the main repository or available via NuGet gallery.

In my case, the project was on 8.2 update-3 (one update higher than the submitted pull request version), and it wasn’t a straight forward fix. I was skeptical but I decided to repeat what has been done in pull request #69 and compile it against 8.2 update-3 version of the Sitecore and try it out.

This is what I did to get my fix:

1-Cloned the GitHub repository locally

2-Updated the solution’s .NET version to 4.5.2

3-Updated the Sitecore NuGet package version to 8.2 update 3

4-Modified the code for Sitecore.Ship.Update.UpdatePackageRunner class as mentioned in the comment section (and below)

5-Build and compiled the code and then replaced the dlls in the bin folder on QA server.

6-Ran the Team City build server script again and it worked like MAGIC!

The underlying issue, as mentioned in one of the comments ,was that there has been some updates to the Sitecore.Update.dll and the code that was responsible for deploying the items was not working any more. Also the solution’s target .NET version has to be upgraded to 4.5.2.

I believe that the pull request will be tested and merged into the main repository soon so everything is available via standard NuGet packages. However if have read till here and using either 8.2 update 3 or update 5 for your CI/CD setup, you can download the compiled dlls from my dropbox and give it a go!

For 8.2 udpate-4 you either wait for the NuGet package or do what I mentioned above.

Happy Sitecoring 🙂

 

 

C# – Limit Paragraph Length using Simple Extension Method

Happy New Year 2016 !

In most of the web applications, you will encounter a scenario where you have to display the summary of the page in limited amount of space. For example, on a section level page, sometimes you are required to display sub section summary information but due to design constraints, you can only display 80 characters.

The .NET framework provides .Substring() method to limit the string length and generate a new sub-string but this method is not clever enough to distinguish between words and empty spaces in a long paragraph and can result in a sub-string where a word has been truncated in the middle of being displayed, giving bad output to the end user.  How can we solve this ?

Here is a simple string extension method that will nicely truncate the paragraph and it will also add “…” at the end of the returned string value.

Step 1 : Add this extension method class in your project

namespace StringExtensions.LimitSentenceLength
{
    public static class StringExtensions
    {
        public static string LimitSentenceLength(this string paragraph, int maximumLenght)
        {
            //null check
            if (paragraph == null) return null;

            //less than maximum length, return as it is
            if (paragraph.Length <= maximumLenght) return paragraph; 
            //split the paragraph into indvidual words 
            string[] words = paragraph.Split(' '); 
            //initialize return variable 
            string paragraphToReturn = string.Empty; 
            //construct the return word 
            foreach (string word in words) 
           { 
            //check if adding 3 to current length and next word is more than maximum length. 
            if ((paragraphToReturn.Length + word.Length + 3) > maximumLenght)
            {
              //append "..."
              paragraphToReturn = paragraphToReturn.Trim() + "...";
              //exit foreach loop
              break;
             }
             //add next word and continue
             paragraphToReturn += word + " ";
            }
           return paragraphToReturn;
        }
    }
}

Step 2 : From your main application, you can call this method as following :

using System;

namespace StringExtensions.LimitSentenceLength
{
    class Program
    {
        private static string Paragraph => "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat.Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur.Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.";

        static void Main(string[] args)
        {
            Console.WriteLine(Paragraph.LimitSentenceLength(50));
            //Lorem ipsum dolor sit amet, consectetur...
            Console.WriteLine(Paragraph.LimitSentenceLength(100));
            //Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut...
            Console.WriteLine(Paragraph.LimitSentenceLength(150));
            //Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.Ut enim ad minim veniam,...
            Console.WriteLine(Paragraph.LimitSentenceLength(200));
            //Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut...
        }
   
    }
}

How simple is that !!!

Sample code is available on GitHub

Sitecore : Adding a Twitter Pod using LINQ To Twitter

In almost all the projects I have done in past year or so, a common requirement among the clients have been that they want the ability to add Twitter Pods on pages and also have the ability to change the account name based upon their campaign or department.

For the purpose of the post, I will be using Sitecore CMS and LINQ To Twitter library to show how simple it is to create a Twitter Pod/module.

Let suppose we only want to display top  tweet from an account/twitter handle, which is define in the Sitecore CMS

Steps:

1. Create a data template property in the CMS as ‘Twitter Account Name’ or whatever you like

2. Create a standard .NET User Control and lets call it ‘TwitterPod.ascx’

3. Reference LINQ to Twitter library in your project (download it from here http://linqtotwitter.codeplex.com/ and also read its documentation)

4. As per Twitter API 2.0 you need to get Consumer Key, Consumer Secret, Access Token, Access Token Secret values from http://dev.twitter.com for any applications that you are creating. Log-in there and follow the steps to get these values.

5. Add these Twitter API 2.0  values to web.config

6. Let suppose this is your .ascx control markup

<div><asp:HyperLink ID="HyperLinkFollowAccount" runat="server" Target="_blank"></asp:HyperLink></div>

<div class="twitter">
<asp:HyperLink ID="HyperLinkFirstTweet" runat="server">
<p><asp:Literal ID="LiteralFirstTweet" runat="server"></asp:Literal>
<p>Posted <asp:Literal ID="LiteralFirstPostedTime" runat="server"></asp:Literal></p>
</asp:HyperLink>
</div>

7. Add these ‘static strings’ to your User Control class

private static string ConsumerSecret

{

get { return WebConfigurationManager.AppSettings["ConsumerSecret"]; }

}

private static string ConsumerKey

{

get { return WebConfigurationManager.AppSettings["ConsumerKey"]; }

}

private static string AccessToken

{

get { return WebConfigurationManager.AppSettings["AccessToken"]; }

}

private static string AccessTokenSecret

{

get { return WebConfigurationManager.AppSettings["AccessTokenSecret"]; }

}

8.  Twitter API 2.0 requires OAuth Authentication, to get that add this to Page_Load of your User Control

protected void Page_Load(object sender, EventArgs e)
{
if (!Page.IsPostBack)
{
try
{
var auth = new SingleUserAuthorizer
{
Credentials = new InMemoryCredentials
{
ConsumerKey = ConsumerKey,
ConsumerSecret = ConsumerSecret,
OAuthToken = AccessToken,
AccessToken = AccessTokenSecret
}
};

using (var twitterCtx = new TwitterContext(auth))
{
LoadTwitterFeeds(twitterCtx);
}
}
catch(Exception ex)
{
Sitecore.Diagnostics.Log.Error(ex.Message, this);
}
}
}

9.  LoadTwitterFeeds(twitterCtx) is your custom function where you can add your logic of getting tweets via Twitter API 2.0 and displaying them

10.

private void LoadTwitterFeeds(TwitterContext service)
{
try
{

string accountName = Sitecore.Context.Item["Twitter Account Name"] as string;

var tweets = (from tweet in service.Status

where tweet.ScreenName == accountName &&
tweet.Count == 1 &&
tweet.ExcludeReplies == true &&
tweet.IncludeRetweets == true &&
tweet.Type == StatusType.User
select tweet).FirstOrDefault();

if (tweets != null)
{

HyperLinkFollowAccount.NavigateUrl = "http://twitter.com/" + accountName.Replace("@", "");
HyperLinkFollowAccount.Text = "Follow " + accountName + " on Twitter";

LiteralFirstTweet.Text = tweets.Text;
LiteralFirstPostedTime.Text = tweets.CreatedAt.ToString();
HyperLinkFirstTweet.NavigateUrl = "https://twitter.com/" + tweets.User.Identifier.ScreenName + "/status/" + tweets.StatusID.ToString();}

catch (Exception ex)
{
Sitecore.Diagnostics.Log.Error(ex.Message, this);
}
}

11. Create a Sublayout for this User Control and add it to the Sitecore Layouts.

Note: You can also pass in the ‘Twitter Account Name’ as a parameter of the SubLayout
Note++: Umbraco and Episerver CMS can also use the same code with different CMS based API.

Thanks

Sitecore : Getting image field in code behind

This is just a quick post to show how to get image URL via code behind.

This is mostly useful when you are binding children items to a repeater or returning image URL in a JSON webservice

public static string GetImageURL(Item currentItem)
{
          string imageURL = string.Empty;
          Sitecore.Data.Fields.ImageField imageField = currentItem.Fields["Image"];
          if (imageField != null && imageField.MediaItem != null)
          {
            Sitecore.Data.Items.MediaItem image = new Sitecore.Data.Items.MediaItem(imageField.MediaItem);
            imageURL = Sitecore.StringUtil.EnsurePrefix('/', Sitecore.Resources.Media.MediaManager.GetMediaUrl(image));
          }
return imageURL;
}

Cheers

Sitecore vs Umbraco – A developer’s view

One Friday afternoon, I was sitting at my desk and fixing bugs (as usual), a member of sales team came up to me and said

“Hey Naveed, we have this interesting new client and they are not sure about which CMS to go for, they have finalised Sitecore and Umbraco as the last two, can you help”

I replied: “Sure, why not, what are their business requirements ?”

Sales person: “They are medium to large size organisation and have usual requirements like news, events, forums, blogs, video content etc, they want a simple to use CMS with multiple authors and security levels”
I replied: “Ok, Sitecore and Umbraco are both equally powerful in content generation and management, they both are based on .NET stack (.NET,C#,SQL) and both are highly customisable, so far they both can be their potential CMS, do you have more specific requirements ?”

Sales person: “Yes, in future, they would like to integrate with Sharepoint and their back-end CRM system”

I replied :”Sitecore comes with a sharepoint connector out-of-box whereas for Umbraco we have to custom build one. As long as their back end CRM exposes API, we can integrate any of the CMS system with their back end, this will be custom build in both cases, do you have any other requirements ?”

Sales person: “It is not a requirement but in their wish list to have personalised content for different regions or profile history”

I replied: “Sitecore comes with Customer Engagement Platform (CEP) and Online Marketing Suite (OMS) which can make their life easy in future, however, Umbraco lacks these customised modules. So it looks like Sitecore can be their potential candidate for CMS, also do you have any idea about their budget for the website?”

Sales person: “Yes, it’s a five figure sum, not sure what exactly their budget is”

I replied: “If they are tight on budget and can compromise on add-ons then Umbraco would be good to start off with but will have high maintenance cost if they go down the route implementing customised modules. However, paying for Sitecore licenses up front will give them added benefit to use some of the modules out of the box and will have low maintenance cost, I guess best would be to give them a demo of both CMS systems to the client and let them make informed decision”

Sales person: “Sounds like a plan, ok I will let them know, cheers”

Conclusion:

No CMS is right or wrong for your business out of the box, you have evaluate them against your own business requirements for the website. Open source may be cheaper to start off with but will have maintenance costs down the line.

Disclaimer: I have worked with both CMS systems and hold them up in same respect for what they do, I am not a sales person or work for either of the CMS systems, this is just my opinion as a third party developer