Saturday, 19 November 2016

Error: The Command InsertSitecoreBucketLink is not implemented yet

Leave a Comment
Recently I’ve faced strange issue while working with Sitecore 8.1 update 3 solution. I was unable to insert bucket link while working with RTE. I was getting an error message as "The Command InsertSitecoreBucketLink is not implemented yet". The Same error was occurring on vanilla Sitecore 8.1 update 3 solution as well.


I’ve created a support ticket with Sitecore support team and they have registered the described issue as a bug in the Sitecore 8.1 update 3 version. To resolve the issue, they have provided a hotfix patch. Reach out to Sitecore support and use the reference number 107388 to get the required files. Below are the steps to apply the patch:
  1. Put the attached Sitecore.Support.107388.dll assembly into the \bin folder.
  2. Back up the \sitecore\shell\Applications\Buckets\MiniResults.aspx file and replace it with the attached one.
  3. Back up the \sitecore\shell\Controls\Rich Text Editor\RichText Commands.js file and replace it with the attached one.
  4. Clear your browser cache.
Note: This issue is present with Sitecore 8.2 initial release as well.

Comments and suggestions are most welcome. Happy coding!  
Read More...

Tuesday, 15 November 2016

Part 3: Sitecore 8.2 with SOLR 6.2

Leave a Comment
Hello, Sitecore enthusiasts! This is the third article of Sitecore 8.2 with Solr 6.2 blog series.

Configuring Sitecore to work with our Solr instance

With Sitecore 8.2, search and indexing no longer require a third-party DI container to enable Solr and there is no need to install a Solr support package. 

Disable Lucene Config File

Make sure all Lucene search related configuration files are disabled and Solr search configuration files are enabled. Follow below steps to switch configuration files so that Solr is enabled and Lucene is disabled: 
  1. Navigate to the website Include folder: \Website\App_Config\Include\ and disable the following Lucene configuration files by adding .disabled to the file name extension:
    • Sitecore.ContentSearch.Lucene.DefaultIndexConfiguration.config
    • Sitecore.ContentSearch.Lucene.DefaultIndexConfiguration.Xdb.config
    • Sitecore.ContentSearch.Lucene.Index.Analytics.config
    • Sitecore.ContentSearch.Lucene.Index.Core.config
    • Sitecore.ContentSearch.Lucene.Index.Master.config
    • Sitecore.ContentSearch.Lucene.Index.Web.config
    • Sitecore.ContentSearch.Lucene.Indexes.Sharded.Core.config
    • Sitecore.ContentSearch.Lucene.Indexes.Sharded.Master.config
    • Sitecore.ContentSearch.Lucene.Indexes.Sharded.Web.config
    • Sitecore.Marketing.Definitions.MarketingAssets.Repositories.Lucene.Index.Master.config
    • Sitecore.Marketing.Definitions.MarketingAssets.Repositories.Lucene.Index.Web.config
    • Sitecore.Marketing.Definitions.MarketingAssets.Repositories.Lucene.IndexConfiguration.config
    • Sitecore.Marketing.Lucene.Index.Master.config
    • Sitecore.Marketing.Lucene.Index.Web.config
    • Sitecore.Marketing.Lucene.IndexConfiguration.config
    • Sitecore.Speak.ContentSearch.Lucene.config
  2. Navigate to \Website\App_Config\Include\FXM and disable the following Lucene configuration files by adding .disabled to the file name extension:
    • Sitecore.FXM.Lucene.DomainsSearch.DefaultIndexConfiguration.config
    • Sitecore.FXM.Lucene.DomainsSearch.Index.Master.config
    • Sitecore.FXM.Lucene.DomainsSearch.Index.Web.config
  3. Navigate to \Website\App_Config\Include\ListManagement and disable the following Lucene configuration files by adding .disabled to the file name extension:
    • Sitecore.ListManagement.Lucene.Index.List.config
    • Sitecore.ListManagement.Lucene.IndexConfiguration.config
  4. Navigate to \Website\App_Config\Include\Social and disable the following Lucene configuration files by adding .disabled to the file name extension:
    • Sitecore.Social.Lucene.Index.Analytics.Facebook.config
    • Sitecore.Social.Lucene.Index.Master.config
    • Sitecore.Social.Lucene.Index.Web.config
    • Sitecore.Social.Lucene.IndexConfiguration.config
  5. Navigate to \Website\App_Config\Include\ContentTesting and disable the following Lucene configuration files by adding .disabled to the file name extension:
    • Sitecore.ContentTesting.Lucene.IndexConfiguration.config

Enable Solr Config File

  1. Enable the Sitecore.ContentSearch.Solr.*.config files by removing .example/.disabled from the file name. Configuration file extension should be .config.
    • Sitecore.ContentSearch.Solr.DefaultIndexConfiguration.config
    • Sitecore.ContentSearch.Solr.DefaultIndexConfiguration.Xdb.config
    • Sitecore.ContentSearch.Solr.Index.Analytics.config
    • Sitecore.ContentSearch.Solr.Index.Core.config
    • Sitecore.ContentSearch.Solr.Index.Master.config
    • Sitecore.ContentSearch.Solr.Index.Web.config
    • Sitecore.Marketing.Definitions.MarketingAssets.Repositories.Solr.Index.Master.config
    • Sitecore.Marketing.Definitions.MarketingAssets.Repositories.Solr.Index.Web.config
    • Sitecore.Marketing.Definitions.MarketingAssets.Repositories.Solr.IndexConfiguration.config
    • Sitecore.Marketing.Solr.Index.Master.config
    • Sitecore.Marketing.Solr.Index.Web.config
    • Sitecore.Marketing.Solr.IndexConfiguration.config
    • Sitecore.Speak.ContentSearch.Solr.config
  2. Navigate to \Website\App_Config\Include\FXM and enable below configuration files by removing .example/.disabled from the file name:
    • Sitecore.FXM.Solr.DomainsSearch.DefaultIndexConfiguration.config
    • Sitecore.FXM.Solr.DomainsSearch.Index.Master.config
    • Sitecore.FXM.Solr.DomainsSearch.Index.Web.config
  3. Navigate to \Website\App_Config\Include\ListManagement and enable below configuration files by removing .example/.disabled from the file name:
    • Sitecore.ListManagement.Solr.Index.List.config
    • Sitecore.ListManagement.Solr.IndexConfiguration.config
  4. Navigate to \Website\App_Config\Include\Social and and enable below configuration files by removing .example/.disabled from the file name:
    • Sitecore.Social.Solr.Index.Master.config
    • Sitecore.Social.Solr.Index.Web.config
    • Sitecore.Social.Solr.IndexConfiguration.config
  5. Navigate to \Website\App_Config\Include\ContentTesting and and enable below configuration files by removing .example/.disabled from the file name:
    • Sitecore.ContentTesting.Solr.IndexConfiguration.config

Configure Solr specific settings in configuration file:

Open Sitecore.ContentSearch.Solr.DefaultIndexConfiguration.config file and update below settings:
  1. Specifying a Solr Service Address: This is the base url of Solr server. Update this accordingly.
  2. <setting name="ContentSearch.Solr.ServiceBaseAddress" value="http://localhost:8090/solr" />
  3. Enabling a Search Provider: This setting tells that which provider is currently being used. Enable the Solr as below:
  4. <setting name="ContentSearch.Provider" value="Solr" />

Rebuild search index

Perform below steps to rebuild the Sitecore search indexes:
  1. In the Control Panel, and click Indexing manager.
  2. Click Select all, and then click Rebuild.
  3. Indexes should be rebuilt without error.
That's it! Sitecore and Solr are configured now. In upcoming blog posts, I'll cover few common search techniques or functionalities using Solr as search provider. Comments and suggestions are most welcome. Happy coding!
Read More...

Friday, 11 November 2016

Part 2: Sitecore 8.2 with SOLR 6.2

Leave a Comment
Hello, Sitecore enthusiasts! This is the second article of Sitecore 8.2 with Solr 6.2 blog series.

Configuring Solr to use with Sitecore

Generate an XML Schema for Solr

Solr needs a defined XML schema when working with documents. Follow below steps to generate Solr Schema XML file:
  1. Duplicate basic_configs folder under C:\Bitnami\solr-6.2.1-2\apache-solr\server\solr\configsets and rename duplicated folder as sitecore_configs.
  2. Verify that that you have a file named schema.xml under the C:\Bitnami\solr-6.2.1-2\apache-solr\server\solr\configsets\sitecore_configs\conf folder. If you don't have a schema.xml file under the folder then you will need to perform below extra steps, as explained in solution 2 of the Solr Compatibility Table:
    • Create schema.xml file by duplicating and renaming the managed-schema file present in C:\Bitnami\solr-6.2.1-2\apache-solr\server\solr\configsets\sitecore_configs\conf folder
    • Switch to ClassicIndexSchemaFactory by adding the following line in the end to the SolrConfig.xml file present in C:\Bitnami\solr-6.2.1-2\apache-solr\server\solr\configsets\sitecore_configs\conf folder:
      <schemaFactory class="ClassicIndexSchemaFactory" />
  3. Open schema.xml file and make the following changes:
    • Enclose all <field> and <dynamicField> elements in a <fields> tag.
    • Enclose all <fieldType> elements in a <types> tag.
  4. Save schema.xml and also create a backup file of schema.xml.
  5. Log into your Sitecore instance.
  6. Navigate to Sitecore Control Panel.
  7. In the Control Panel, click Generate the Solr Schema.xml file link in Index section.
  8. In the dialog window, change the source file and target file to point to the newly created schema file (C:\Bitnami\solr-6.2.1-2\apache-solr\server\solr\configsets\sitecore_configs\conf\schema.xml) and click the ‘Generate’ button.
  9. Sitecore will create a modified schema.xml file, using the schema.xml file we created as a baseline in above steps and add all its necessary Solr specific fields needed for indexing, unique keys etc. 
  10. Open the newly generated schema file (C:\Bitnami\solr-6.2.1-2\apache-solr\server\solr\configsets\sitecore_configs\conf\schema.xml) and add below line:
    <fieldType name="pint" class="solr.TrieIntField" docValues="true" precisionStep="0" positionIncrementGap="0" />

Organize Solr core structure

  1. Copy the sitecore_configs (C:\Bitnami\solr-6.2.1-2\apache-solr\server\solr\configsets\sitecore_configs) folder and paste it in C:\Bitnami\solr-6.2.1-2\apache-solr\server\solr. Rename it to the sitecore_web_index.
  2. For each Sitecore index, we need to repeat above step 1 until we have a set of config directories with the following names:
    • sitecore_web_index
    • sitecore_master_index
    • sitecore_core_index
    • sitecore_analytics_index
    • social_messages_web
    • social_messages_master
    • sitecore_marketing_asset_index_master
    • sitecore_marketing_asset_index_web
    • sitecore_testing_index
    • sitecore_suggested_test_index
    • sitecore_fxm_master_index
    • sitecore_fxm_web_index
    • sitecore_list_index
    • sitecore_marketingdefinitions_master
    • sitecore_marketingdefinitions_web
  3. Once you have configured all the directories, navigate to the Solr admin page.
  4. After login, navigate to Core Admin page and click on Add Core button.
  5. Fill in the 'name' and 'instanceDir' fields with sitecore_web_index, and press the "Add Core" button to add the core into Solr. 
  6. You should be able to see that sitecore_web_index core is now present in the list of cores available in your Solr instance.
  7. Repeat these steps for all directories created earlier so that we'll have a Solr core for each directory we created.

That's it! Solr is ready to be used with Sitecore. Comments and suggestions are most welcome. Happy coding!
Read More...

Part 1: Sitecore 8.2 with SOLR 6.2

Leave a Comment
Hello, Sitecore enthusiasts! As you may be already aware that Lucene is the default search provider for Sitecore though Solr is recommended search provider in scaled environments. I’ll be writing series of blog posts related to Sitecore search implementation with Solr. I’ll be covering how to setup Solr 6.2 with Sitecore 8.2 instance and few common search techniques or functionalities using Solr as search provider. This is part 1 of Sitecore 8.2 search with Solr 6.2 blog posts series. I am working with Sitecore 8.2 initial release.
  1. Part 1: Installing Solr using the Bitnami Apache Solr Stack
  2. Part 2: Configuring Solr to use with Sitecore
  3. Part 3: Configuring Sitecore to work with Solr
  4. Part 4: Create a custom Solr Core
  5. Part 5: Work in progress

Installing Solr using the Bitnami Apache Solr Stack

  1. Download the Bitnami Apache Solr Stack Distribution that provides a one-click install solution for Apache Solr.
  2. Double-click on the installer (bitnami-solr-6.2.1-2-windows-installer.exe for the purposes of this article). The first screen will look like below:
  3. Click Next. In next screen, you should choose the folder to install Bitnami Apache Solr Stack. For this blog post, I am taking default installation path C:\Bitnami\solr-6.2.1-2
  4. Click Next. In next screen, you should create an administrator account for Bitnami Apache Solr Stack.
  5. Click Next. Next screen allows you to define the port that Apache Solr should run on. Choose a port which is not currently in use. For this article, we used port 8090.
  6. Click Next. Next screen shows offering up details about Bitnami Cloud Hosting. For this tutorial, I’ll make it uncheck.
  7. Click Next. Setup is now ready to begin installing Bitnami Apache Solr Stack on your computer.
  8. During the installation, Windows Firewall will alert you that it has blocked some features of this installation. Ensure that the check boxes for Domain networks, such as a workplace network and Private networks, such as my home or work network are checked. Complete this step by clicking the Allow access button.
  9. Once the installation is complete, leave the check box checked and click on Finish button.
  10. Your web browser will open a page like this:
  11. Click on Access Apache Solr link which will pop up one authentication window. Enter credentials of admin account and click Log In.
  12. Solr dashboard will be launched and should look like this:
  13. You now have Solr instance up and running. Now we have to configure Solr to be used with Sitecore.
Comments and suggestions are most welcome. Happy coding!
Read More...

Wednesday, 2 November 2016

Sitecore : Restore archived item programmatically

Leave a Comment
This short blog post contains information about how to restore archived item programmatically in Sitecore 8. Use the below code snippet to achieve the functionality:
void RestoreArchivedItems()
        {
            using (new SecurityDisabler())
            {
                DateTime archiveDate = new DateTime(2016, 11, 1);
                string originalLocationPathPrefix = "/sitecore/content/Home";
                string archivedBy = "sitecore\\admin";

                // get the archive database for the master database
                Sitecore.Data.Archiving.Archive archive = Sitecore.Data.Database.GetDatabase("master").Archives["archive"];

               // get archived items
                var archivedItems =
                    archive.GetEntries(0, int.MaxValue)
                            .Where(entry =>
                                entry.ArchiveDate > archiveDate &&
                                entry.OriginalLocation.StartsWith(originalLocationPathPrefix) && entry.ArchivedBy.Equals(archivedBy, StringComparison.OrdinalIgnoreCase)
                            ).ToList();

                foreach (var archiveItem in archivedItems)
                {
                    // restore the item
                    archive.RestoreItem(archiveItem.ArchivalId);
                }
            }
        }
Comments and suggestions are most welcome. Happy coding!
Read More...

Friday, 23 September 2016

Customizing PDF download behaviour for Sitecore multisite implementation

Leave a Comment
Recently I got a requirement from my client where I have to customize PDF rendering behaviour for Sitecore multisite implementation. For few websites, PDF files should be rendered in web browser itself and for few websites, web browser should prompt the user to save the PDF files. I am working with Sitecore 8.2 initial release.

PDF rendering behaviour is being managed by <forceDownload> element for PDF file media type under \mediaLibrary\mediaTypes configuration node. This setting can be found in Sitecore.Config file in \Website\App_Config folder. A true value for the forceDownload element causes Sitecore to apply an HTTP "Content-Disposition = attachment; filename=" header when linking to the .ashx URL of the media item, causing the browser to prompt the user to open/save as rather than opening the media item in the browser.


Disabling/Enabling forceDownload behaviour for PDF file will impact all the websites but I need flexibility to disable/enable this behaviour across multiple websites. This behaviour can be achieved in two ways:
  1. Forcing download on client side using HTML 5 download attribute
  2. Override MediaRequestHandler and modify "Content-Disposition" in context response header
  1. Forcing download on client side using HTML 5 download attribute
    Use HTML5 download attribute on your anchor links. The download attribute specifies that the target will be downloaded when a user clicks on the hyperlink. However this attribute is not supported by all the browsers.
  2. Override MediaRequestHandler and modify "Content-Disposition" in context response header
    Below are the steps to implement the desired behaviour across multiple websites:
    • Create a new class library project in visual studio.
    • Add reference to Sitecore.Kernel.dll
    • Add a custom attribute in your site definition. For example, "PdfForceDownloadDisabled"
      <site name="mysite" patch:before="site[@name='website']"
                  virtualFolder="/"
                  physicalFolder="/"
                  rootPath="/sitecore/content"
                  startItem="/MySite"
                  database="web"
                  domain="extranet"
                  allowDebug="true"
                  cacheHtml="true"
                  htmlCacheSize="50MB"
                  enablePreview="true"
                  enableWebEdit="true"
                  enableDebugger="true"
           PdfForceDownloadDisabled="true"
           hostName="local.mysite.com"
                  disableClientData="false"/>
    • Implement a custom Extension method :
      using Sitecore.Diagnostics;
      using Sitecore.Sites;
      using System;
      
      namespace CustomMedia
      {
          public static class UtilityManager
          {
              public static bool PdfForceDownloadDisabled(this SiteContext site)
              {
                  Assert.IsNotNull(site, "Site cannot be null");
      
                  try
                  {               
                      string PdfForceDownloadDisabled = site.Properties["PdfForceDownloadDisabled"];
                      if (!String.IsNullOrEmpty(PdfForceDownloadDisabled))
                          return Convert.ToBoolean(PdfForceDownloadDisabled); 
                  }
                  catch (Exception)
                  {
                      return false;
                  }
                  return false;
              }       
          }
      }
      
    • Override "protected override bool DoProcessRequest(HttpContext context, MediaRequest request, Sitecore.Resources.Media.Media media)" method and modify "Content-Disposition" in context response header.
      using Sitecore;
      using Sitecore.Configuration;
      using Sitecore.Data.Items;
      using Sitecore.Diagnostics;
      using Sitecore.Events;
      using Sitecore.Resources.Media;
      using Sitecore.Resources.Media.Streaming;
      using Sitecore.Web;
      using System.Web;
      
      namespace CustomMedia
      {
          public class MediaRequestHandler : Sitecore.Resources.Media.MediaRequestHandler
          {
              protected override bool DoProcessRequest(HttpContext context, MediaRequest request, Sitecore.Resources.Media.Media media)
              {
                  Assert.ArgumentNotNull(context, "context");
                  Assert.ArgumentNotNull(request, "request");
                  Assert.ArgumentNotNull(media, "media");
                  if (this.Modified(context, media, request.Options) == Tristate.False)
                  {
                      Event.RaiseEvent("media:request", new object[] { request });
                      this.SendMediaHeaders(media, context);
                      context.Response.StatusCode = 0x130;
                      return true;
                  }
                  this.ProcessImageDimensions(request, media);
      
                  MediaStream mediaStream = media.GetStream(request.Options);
      
                  if (mediaStream == null)
                  {
                      return false;
                  }
                  Event.RaiseEvent("media:request", new object[] { request });
                  if (Settings.Media.EnableRangeRetrievalRequest && Settings.Media.CachingEnabled)
                  {
                      using (mediaStream)
                      {
                          this.SendMediaHeaders(media, context);
                          new RangeRetrievalResponse(RangeRetrievalRequest.BuildRequest(context, media), mediaStream).ExecuteRequest(context);
                          //Update Header   - Custom Add             
                          this.UpdateHeader(media, context, mediaStream);
                          return true;
                      }
                  }
                  this.SendMediaHeaders(media, context);
                  this.SendStreamHeaders(mediaStream, context);
                  using (mediaStream)
                  {
                      context.Response.AddHeader("Content-Length", ((long)mediaStream.Stream.Length).ToString());
                      WebUtil.TransmitStream(mediaStream.Stream, context.Response, Settings.Media.StreamBufferSize);
                  }
                  return true;
              }
      
              private void ProcessImageDimensions(MediaRequest request, Sitecore.Resources.Media.Media media)
              {
                  int num;
                  int num2;
                  Assert.ArgumentNotNull(request, "request");
                  Assert.ArgumentNotNull(media, "media");
                  Item innerItem = media.MediaData.MediaItem.InnerItem;
                  int.TryParse(innerItem["Height"], out num2);
                  int.TryParse(innerItem["Width"], out num);
                  bool flag = false;
                  int maxHeight = Settings.Media.Resizing.MaxHeight;
                  if ((maxHeight != 0) && (request.Options.Height > System.Math.Max(maxHeight, num2)))
                  {
                      flag = true;
                      request.Options.Height = System.Math.Max(maxHeight, num2);
                  }
                  int maxWidth = Settings.Media.Resizing.MaxWidth;
                  if ((maxWidth != 0) && (request.Options.Width > System.Math.Max(maxWidth, num)))
                  {
                      flag = true;
                      request.Options.Width = System.Math.Max(maxWidth, num);
                  }
                  if (flag)
                  {
                      Log.Warn($"Requested image exceeds allowed size limits. Requested URL:{request.InnerRequest.RawUrl}", this);
                  }
              }
      
              private void UpdateHeader(Sitecore.Resources.Media.Media media, HttpContext context, MediaStream stream)
              {
                  if (media.Extension.ToLower() == "pdf" && Sitecore.Context.Site.PdfForceDownloadDisabled())
                  {
                      context.Response.Headers["Content-Disposition"] = "inline; filename=\"" + stream.FileName + "\"";
                  }
              }
          }
      }
      
    • In web.config, replace <add verb="*" path="sitecore_media.ashx" type="Sitecore.Resources.Media.MediaRequestHandler, Sitecore.Kernel" name="Sitecore.MediaRequestHandler" />  with the following line:
      <add verb="*" path="sitecore_media.ashx" type="CustomMedia.MediaRequestHandler, {your assembly name}" name="Sitecore.MediaRequestHandler" />
    • Deploy your assembly in bin folder of your Sitecore website.
Comments and suggestions are most welcome. Happy coding!
Read More...

Tuesday, 20 September 2016

Sitecore Commerce Server Manager : Profile Definition 404 Error

Leave a Comment
I’ve been working with Sitecore commerce server manager for a while and recently I have stuck with one error related to Sitecore commerce server manager. I was unable to navigate through Profile Definitions in commerce server manager. I was getting HTTP 404 The webpage cannot be found error.


I’ve checked stack trace in fiddler and found out that http://<server-name>/widgets/profilebldrHTC/ProfileEdit.htm was throwing 404 error.


It seems that Sitecore Commerce Server creates virtual folder named Widgets in the IIS default website. This virtual folder points to C:\Program Files (x86)\Commerce Server 11\Widgets directory.

Follow below steps to resolve the 404 issue:
  1. Open IIS.
  2. Navigate to default website in IIS.
  3. Right click on default website and click Add Virtual Directory.
  4. Enter Widgets in Alias section. 
  5. Enter physical path as C:\Program Files (x86)\Commerce Server 11\Widgets
  6. Click OK.
  7.  Restart default website and refresh Sitecore commerce server manager. 404 error should have been gone now.
Comments and suggestions are most welcome. Happy coding!
Read More...

Saturday, 10 September 2016

Ajax call in Sitecore without registering routes

Leave a Comment
I have already blogged about How to make Ajax call in Sitecore MVC where I was registering routes in RouteConfig. In this blog post I am going to explain how to make Ajax call in Sitecore MVC without registering routes in RouteConfig. This method is relatively very easy with less code changes. I am using Sitecore 8.2 initial release version for this blog post.

I’ve created a data template in Sitecore which is having three fields as shown in figure.


I’ve created few items based on newly created data template. See below figure:


I’ve created a basic controller rendering which is displaying books in a dropdown list. Below is the code of controller rendering for your reference:

Controller Code

public class BookDetailsController : SitecoreController
    {       
        public override ActionResult Index()
        {           
            List<SelectListItem> bookItems = new List<SelectListItem>();
            bookItems.Add(new SelectListItem { Text = "--Select Book--", Value = ""});
            bookItems.Add(new SelectListItem { Text = "Learn ASP", Value = "{EC2B22FE-8A6E-431F-8114-6B2944AE81B8}" });
            bookItems.Add(new SelectListItem { Text = "Learn MVC", Value = "{EBB9F7D5-22DB-4B7A-A275-42D9B5C88715}" });
            bookItems.Add(new SelectListItem { Text = "Learn SITECORE", Value = "{C89AE332-9845-4379-8C45-E8D58AAA5685}" });
            ViewBag.Books = bookItems;          
            return View();
        }    
}  

MVC View Code

Select Any Book :
@Html.DropDownList("Books")

<div id="BookDetail" style="white-space: pre;">

</div>
On change event of dropdown list; I’ll display book details (book title, book author and book language) of selected book. I’ll use AJAX to achieve this functionality.

Create a MVC controller action: I’ve written GetBookDetails action with [HttpPost] attribute in BookDetails Controller where I am passing Sitecore item id of selected book as input parameter. I’ll get book details by using item id of book item and return book details as JsonResult.
 [HttpPost]
        public JsonResult GetBookDetails(string itemId)
        {
            Book book = new Book();
            if (Sitecore.Data.ID.IsID(itemId))
            {
                Item item = Sitecore.Context.Database.GetItem(Sitecore.Data.ID.Parse(itemId));
                if (item != null)
                {
                    book.BookTitle = item.Fields["Book Title"].Value;
                    book.BookAuthor = item.Fields["Author"].Value;
                    book.BookLanguage = item.Fields["Language"].Value;
                }
            }
            return Json(book);
        }
public class Book
    {
        public string BookTitle { get; set; }
        public string BookAuthor { get; set; }
        public string BookLanguage { get; set; }
    } 
Implement AJAX call: I’ll use jQuery to make Ajax call. In the below code, I am reading the value of selected book from dropdown list and passing book item id as an input parameter while making Ajax call to get book details. Use the returned JsonResult set to update the HTML div BookDetail. Notice the value of url while making ajax request.
url: "api/Sitecore/BookDetails/GetBookDetails"
<script type="text/javascript" src="http://ajax.aspnetcdn.com/ajax/jQuery/jquery-2.1.1.js"> </script>
<script type="text/javascript">
    $(function () {
        $("#Books").bind("keyup change", function () {
            var itemId = $(this).val();
            if (itemId != "") {
                $.ajax({
                    url: "api/Sitecore/BookDetails/GetBookDetails",
                    type: "POST",
                    data: { itemId: itemId },
                    context: this,
                    success: function (data) {
                        var BookString = "Book Title: " + data.BookTitle + "\n" + "Book Author: " + data.BookAuthor + "\n" + "Book Language:" + data.BookLanguage;
                        $("#BookDetail").text(BookString);
                        console.log("success", data);
                    },
                    error: function (data) {
                        console.log("error", data);
                    }
                });
            }
            else {
                $("#BookDetail").text("");
            }
        });
    });
</script>
Related read:
Comments and suggestions are most welcome. Happy coding!
Read More...

Friday, 12 August 2016

Storing Sitecore Media in the Shared File System Folder

Leave a Comment
In this blog post, I am going to explain how to store Sitecore media items in the Shared File System Folder. By default, Sitecore stores media assets in database but this behaviour can be customized so that Sitecore can store media assets into File System. I recommend checking Storing Sitecore Media in the Database vs. the File System post by Sitecore MVP Mark Ursino for pros/cons of both techniques.

Below steps are required to store Sitecore media items in the Shared File System Folder:
  1. Open Sitecore.config ( Sitecore 8.1) or web.config (Sitecore 8.0) file and set the Media.UploadAsFiles setting to true:
    <setting name="Media.UploadAsFiles" value="true">
  2. Open IIS and navigate to website node. Right click the website node and click the Add Virtual Directory.
  3. In the Add Virtual Directory dialog box, specify the following information:
    Alias: Type a name for the virtual directory. Choose a short and meaningful name. This alias name will be used at Step 4.
    Physical Path: Type Shared File System Folder path.

    Click the Connect as button and provide credentials to connect to Shared File System Folder. Make sure the user account is having write access to Shared File System Folder else you will get into authorization issues.


    Click on Test Settings button to verify the connection.

  4. Open Sitecore.config ( Sitecore 8.1) or web.config (Sitecore 8.0) file and change the value of Media.FileFolder setting to alias name of virtual directory.
    <setting name="Media.FileFolder" value="/SharedMedia">
  5. Recycle application pool.

Troubleshooting:

You might see below exception in log file:
Exception: System.UnauthorizedAccessException Message: Access to the path '\\Network-26\SharedMedia\1\C\C' is denied. Source: mscorlib at System.IO.__Error.WinIOError(Int32 errorCode, String maybeFullPath) at System.IO.Directory.InternalCreateDirectory(String fullPath, String path, Object dirSecurityObj, Boolean checkHost) at System.IO.Directory.InternalCreateDirectoryHelper(String path, Boolean checkHost) at Sitecore.Resources.Media.MediaData.SetFileStream(MediaStream mediaStream) at Sitecore.Resources.Media.Media.SetStream(MediaStream mediaStream) at Sitecore.Resources.Media.MediaCreator.AttachStreamToMediaItem(Stream stream, String itemPath, String fileName, MediaCreatorOptions options) at Sitecore.Resources.Media.MediaCreator.CreateFromStream(Stream stream, String filePath, MediaCreatorOptions options) at Sitecore.Resources.Media.MediaUploader.UploadToDatabase(List`1 list) at Sitecore.Resources.Media.MediaUploader.Upload() at Sitecore.Pipelines.Upload.Save.Process(UploadArgs args)
To resolve above issue; make sure the user account (used in Step 3) is having write access to Shared File System Folder. Comments and suggestions are most welcome. Happy coding!
Read More...

Monday, 18 July 2016

Sitecore error while rebuilding reporting database

Leave a Comment
Recently I stuck with following issue while rebuilding reporting database: (FYI, Reporting database can be rebuild by visiting url <sitename> /sitecore/admin/RebuildReportingDB.aspx)
Connection string used by aggregation/historyTaskManager/ReportingStorage is missing.
Sitecore xDB uses SQL Server and MongoDB which need to be kept in sync to provide accurate data for various Sitecore reporting applications.
  • MongoDB collection database - This stores all captured experience information from your website in a loosely structured way.
    MongoDB collection database connection string:
    <add name="analytics" connectionString="mongodb://localhost/analytics"/>
  • SQL Server primary reporting database - stores information extracted from the collection database in a form suitable for reporting.
    Primary reporting database connection string:
    <add name="reporting" connectionString="user id= _sql_server_user;password= _user_password_@123;Data Source=(local);Database=Sitecore_Analytics"/>
By default Sitecore xDB must always keep the reporting database in sync with the collection database but there are certain circumstances when you may need to perform a complete rebuild of the reporting database. Check here various reasons for rebuilding reporting database.

When you rebuild the reporting database, to avoid disruption during the rebuild process, you connect a Secondary reporting database (SQL Server) to store all aggregated data as it is reprocessed. Secondary reporting database stores historical data during the rebuilding of the reporting database.

Secondary reporting database was not configured in my scenario therefore I was facing above error. Perform below steps to connect and configure a secondary reporting database:
  1. Take a clean copy of the Sitecore_Analytics database from your Sitecore distribution to use as your secondary reporting database. 
  2. Attach this database to SQL server instance and name it Sitecore_ Analytics_Secondary.
  3. Open your connectionstrings.config file and add a reference to you secondary reporting database. Use the name reporting.secondary in the connection string.
    <add name="reporting.secondary" connectionString="user id= _sql_server_user;password= _user_password_@123;Data Source=(local);Database= Sitecore_ Analytics_Secondary "/>
Rebuild the reporting database and above error should be gone now. Comments and suggestions are most welcome. Happy coding!

Related articles:
  1. Rebuilding the reporting database 
  2. Reasons for rebuilding the reporting database 
  3. xDB Processing overview
  4. Troubleshooting xDB data issues
Read More...

Thursday, 19 May 2016

Message is not getting created in EXM

Leave a Comment
I have been working on Sitecore EXM (Email Experience Manager) for one POC and faced few issues with the EXM module. Recently I stuck in a situation where clicking the Create button does not create a new message in EXM. I am working with Sitecore 8 Update 5 and EXM 3.1 rev. 150811 (Update-1).
Clicking on create button doesn’t perform any action and the dialog window just hangs. A quick Google search has redirected me to this knowledge base article but unfortunately the given solution didn’t work in my case. On troubleshooting MessageCreationDialogBase.js (location : /sitecore/shell/client/Applications/ECM/EmailCampaign.Client/Dialog/MessageCreationDialogBase.js) I have found that .aspx extension is getting appended to createType.
I’ve modified javascript code and added below lines above switch statement code block:
    if(createType.includes('.aspx'))
    {
     createType = createType.slice(0,-5);
    }
Updated code:
   if(createType.includes('.aspx'))
    {
     createType = createType.slice(0,-5);
    }
          switch (createType) {
            case "ExistingTemplate":
              this.createExistingTemplate(contextApp, eventInfo);
              break;
            case "ExistingPage":
              this.createExistingPage(contextApp, eventInfo);
              break;
            case "ImportHtml":
              this.createImportHtml(contextApp);
              break;
            default:
          }
Clear browser cache and make sure that updated javascript file is getting loaded. Comments and suggestions are most welcome. Happy coding! 
Read More...

Sunday, 15 May 2016

Error : HTTPS connection is required for Orders and Profiles webservices

Leave a Comment
I've been working with Sitecore Commerce 8 powered by Commerce Server now for a while and trying to configure the demo store which is based upon Reference Storefront solution provided by Sitecore commerce product team.  I’ve successfully managed to get the demo store running but I have faced below error while opening Commerce Server Customer and Orders Manager business tool:
HTTPS connection is required for Orders and Profiles webservices. 
By default the Commerce Server Customer and Orders Manager business tool requires that the connections to the Orders and Profiles web services should be over HTTPS. This behaviour can be changed to allow it to work over HTTP instead.  However HTTP connection configuration is not recommended for LIVE or PRODUCTION environment as you should always use HTTPS to ensure data is secure but HTTP connection can be useful in development and test scenarios where HTTPS communications are not easily available.

Follow below steps so that Commerce Server Customer and Orders Manager business tool allow HTTP for connectivity to the Profile and Orders web services:
  1. Navigate to C:\Program Files (x86)\Commerce Server 11\Business User Applications directory.
  2. Open the file CustomerandOrdersManager.exe.config in notepad.
  3. Search for below entry under <applicationsettings> section.
    <setting name="AllowHTTP" serializeAs="String">
                    <value>False</value>
    </setting>
  4. Change this entry as shown below:
     <setting name="AllowHTTP" serializeAs="String">
                    <value>True</value>
    </setting>
  5. Save the config file.
  6. Restart IIS or recycle application pool of profiles web service.
  7. Open Commerce Server Customer and Orders Manager business tool and enter standard HTTP connection strings for the Orders and Profiles Web Services URLs in Site Connections window.


Comments and suggestions are most welcome. Happy coding! 
Read More...

Wednesday, 2 March 2016

Always select Publish Related Items checkbox in publishing dialog

Leave a Comment
Recently I’d stumbled upon a post on Sitecore Community regarding how to make Publish Related Items checkbox always checked based on a specific User Role.

Original question: I need to make 'Publish Related Items' checked every time any content editor publishes an item. It will be nice if I can do it via Roles. But, if I can’t then I would like to default it for every user in Sitecore.

This can be achieved by overriding sitecore\shell\Applications\Dialogs\Publish\Publish.xml form and make PublishRelatedItems checkbox checked and disabled. Below steps illustrates how to make PublishRelatedItems checkbox always checked based on a specific User Role:
  1. Create a new C# class library project and override the OnLoad method of the PublishForm class and add the following code:
    using Sitecore;
    using Sitecore.Diagnostics;
    using Sitecore.Shell;
    using Sitecore.Shell.Applications.Dialogs.Publish;
    using Sitecore.Web;
    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    using System.Threading.Tasks;
    
    namespace SitecoreCustom
    {
        class CustomPublishForm : PublishForm
        {
            public CustomPublishForm()
                : base()
            {
    
            }
    
            protected override void OnLoad(System.EventArgs e)
            {
                base.OnLoad(e);
                //Modify role as per your requirement
                if (Sitecore.Context.User.IsInRole("sitecore\\ContentEditor"))
                {
                    base.PublishRelatedItems.Checked = true;
                    base.PublishRelatedItems.Disabled = true;
                }
            }
        }
    }
    
    
  2. Compile the solution and put dll in bin folder of website root.
  3. Duplicate Publish.xml file and paste it at \Website\sitecore\shell\Override\Applications\Dialogs\Publish\
  4. Open Publish.xml which we have just duplicated and update Publish Form code-beside class to use our custom code-beside class instead of Sitecore's default class.
    You have to make below change:
    <WizardForm CodeBeside="SitecoreCustom.CustomPublishForm,SitecoreCustom">
    The format is “<namespace>.<class name>, <assembly name>”. For example: SitecoreCustom is namespace, CustomPublishForm is class name and SitecoreCustom is assembly name.
  5. Login into Sitecore with user who is part of sitecore\\ContentEditor role (or any other specific role as per your requirement)
  6. Select an item and try to publish it. You will see that Publish Related Items checkbox is already checked and disabled.
Comments and suggestions are most welcome. Happy coding!
Read More...

Monday, 15 February 2016

Commerce Server Error : The requested profile could not be retrieved

Leave a Comment
I've been working with Sitecore Commerce 8 powered by Commerce Server now for a while and trying to configure the demo store which is based upon Reference Storefront solution provided by Sitecore commerce product team.  I’ve successfully managed to get the demo store running after following configuration steps however I’d faced few issues while setting it up. In my upcoming blog posts I’ll be sharing my learning on some troubleshooting I went through.

I was trying to create a user account in demo storefront website. I got below error once I clicked on create user button after entering relevant details in create an account form.   
Register: The requested profile could not be retrieved because the key name provided does not exist or is not an indexed property. The profile type is 'UserObject'. The key name provided is ‘GeneralInfo.ExternallD’.
In order to resolve above issue, I did following steps:
  1. Verify that profile schema is correct. If profile schema is incorrect then import profile schema again.
  2. After importing schema, modify Sitecore Commerce 8.0 powered by Commerce Server Profile Schema for Sitecore Integration as described here.
  3. I have found that profile database schema was not correct and u_external_id column was missing in UserObject table of profile database.
  4. Make the following changes in UserObject table of profile database:
    Add column: u_external_id NVARCHAR(256) NOT NULL
    IF NOT EXISTS (SELECT * FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_NAME = 'userobject' AND COLUMN_NAME = 'u_external_id')
    BEGIN
     ALTER TABLE dbo.UserObject ADD u_external_id nvarchar(256) NOT NULL
    END
    GO
    
    Create Index on: u_external_id, UNIQUE NONCLUSTERED INDEX and ASC
    IF  EXISTS (SELECT * FROM sys.indexes WHERE object_id = OBJECT_ID(N'[dbo].[UserObject]') AND name = N'IX_UserObject_ExternalId')
     DROP INDEX [IX_UserObject_ExternalId] ON [dbo].[UserObject]
    GO
    
    IF NOT EXISTS (SELECT * FROM sys.indexes WHERE object_id = OBJECT_ID(N'[dbo].[UserObject]') AND name = N'IX_UserObject_ExternalId')
    CREATE UNIQUE NONCLUSTERED INDEX [IX_UserObject_ExternalId] ON [dbo].[UserObject]
    (
     [u_external_id] ASC
    )WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, SORT_IN_TEMPDB = OFF, IGNORE_DUP_KEY = OFF, DROP_EXISTING = OFF, ONLINE = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
    GO
    
  5. Update Sitecore Commerce 8.0 powered by Commerce Server Profile Schema. Add a new GeneralInfo.ExternalId profile property that maps to the external id column, it must be required and unique. This blog explains how to add new property to user object in commerce server profile system.
  6. Refresh the cache and reload the register account page. You should no longer get the above error message and should be able to create new user account in demo storefront website.
Comments and suggestions are most welcome. Happy coding!  
Read More...

Commerce Server : How to add a Property to a Profile Definition

Leave a Comment
In this blog post I am going to explain how to add a new Property to a Profile Definition in Sitecore Commerce Server. For example, I’ll show how to add a new property ExternalId to User object into Profile Definition of Sitecore Commerce Server.

Add new column in SQL Server table
  1. Make the following changes in UserObject table of profile database in SQL server:
    Add column: u_external_id NVARCHAR(256) NOT NULL
IF NOT EXISTS (SELECT * FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_NAME = 'userobject' AND COLUMN_NAME = 'u_external_id')
BEGIN
 ALTER TABLE dbo.UserObject ADD u_external_id nvarchar(256) NOT NULL
END
GO
Create new Data member in Commerce Server Manager
  1. Open Commerce Server Manager. Expand Commerce Server Manager -> Global Resources -> Profiles -> Profile Catalog -> Data Sources -> ProfileService_SQLSource -> Data Objects. Right click the data object you want to add a data member to, and then click New Data Member. I am going to add new data member to User object.
  2. In the New Data Member dialog box, complete below fields:
    Field NameDetails
    Member NameSelect an available data member name from the list. Data members which have already been added to a data object will be unavailable for selection and appear dimmed. The member name must match the name of the column to which the data member maps in SQL server table. The member name can contain a maximum of 127 alphanumeric characters and the underscore (_) symbol. The member name cannot contain spaces.
    Display NameType a display name that contains a maximum of 127 Unicode characters.
    DescriptionType a description for the data member. The description can contain a maximum of 127 Unicode characters.
    TypeSelect the appropriate data type for the data member. The data type must be the same as the property that you will map with the data member.
    Multi-valuedSelect this option to enable the collection of multiple values that are associated with the data member.
    RequiredSpecifies that this property is required on your Web site. However, the Profiles Schema Manager does not enforce this attribute.
    IndexedSelect this option if you want the data indexed on this data member.
    Primary keySelect this option to designate this data member as the primary key. You must select one data member in each data object to be the primary key.
  3. Click Add. The data member appears in the data member list in the New Data Member dialog box.
  4. Click Finished.
Map the data member to a property in profile object.
  1. Open Commerce Server Manager. Expand Commerce Server Manager -> Global Resources -> Profiles -> Profile Catalog -> Profile Definitions. Left click the profile definition object where you want to add a new property. I’ve selected User object profile definition.
  2. Select appropriate information group and then click on Add to add new property.
  3. The property will be appeared in general information group with a new name called New Property 1.
  4. Select New Property 1. You can see a property editor in the right hand section.
  5. Fill the fields in Attributes section. 
  6. Fill the fields in Advanced attributes section. Click on Map to data to map the newly created data member to new property.
  7. Fill the fields in Custom attributes section as per requirement.
  8. Click on apply to save the changes of new property. Now the new property is added to User object.
Comments and suggestions are most welcome. Happy coding!
Read More...