Sitecore Content Hub – How to generate public link using scripts?

Sitecore Content Hub allow user to create public link and they can use those public links on web application, social channel or any other external/internal application.

In this page, you can find the steps to create public Link. Using this link steps, user can generate Public Links by manually interact with the Sitecore Content Hub.

Sitecore Content Hub provides powerful features Trigger, Action and Script which user can utilize to make this process automatic.

Here, I am going to explain the steps which make this process preprogrammed.

Let’s start the implementation, our first step is to create Script into content hub. If you are new in Sitecore Content Hub, Please refer this page to create basic script.

Add below code inside this new Script which you have created using above page.

using System.Linq;
using System.Threading.Tasks;

var assetId = Context.TargetId;
string OriginalRendition = "downloadOriginal";


// Check if public links don't exist yet
var query = Query.CreateQuery(entities => from e in entities
                                            where e.DefinitionName == "M.PublicLink"
                                            && e.Parent("AssetToPublicLink") == assetId.Value                                            
                                            && e.Property("IsDisabled") == false
                                            select e);
query.Take = 1;

var result = await MClient.Querying.QueryIdsAsync(query);
if (result.TotalNumberOfResults > 0)
{
    MClient.Logger.Info("Public links already exist for asset with id '" + assetId + "'");
    return;
}

// Create public links
await CreateForRendition(OriginalRendition, assetId.Value);
MClient.Logger.Info("Created public link 'downloadOriginal' for asset with id '" + assetId + "'");

async Task CreateForRendition(string rendition, long assetId)
{
    var publicLink = await MClient.EntityFactory.CreateAsync("M.PublicLink");

    if (publicLink.CanDoLazyLoading())
    {
        await publicLink.LoadMembersAsync(new PropertyLoadOption("Resource"), new RelationLoadOption("AssetToPublicLink"));
    }

    publicLink.SetPropertyValue("Resource", rendition);

    var relation = publicLink.GetRelation<IChildToManyParentsRelation>("AssetToPublicLink");
    if (relation == null)
    {
        MClient.Logger.Error("Unable to create public link: no AssetToPublicLink relation found.");
        return;
    }

    relation.Parents.Add(assetId);

    await MClient.Entities.SaveAsync(publicLink);
    return;
}

Note – This script referes “downloadOriginal” rendition only for public link creation.

Once the script is created, Build and Publish in content hub. Now, it is ready for use.

Next step is to create an Action using the Action tiles in the manage section and mapped with the script which you have created in previous step. you have to select “Action scripts” type while creating an Action. If you are new in Sitecore Content Hub, Please refer this page to create Action.

Now you are on your final step where you need to create Trigger in content hub. If you are new in Sitecore Content Hub, Please refer this page to create trigger.

A trigger is a set of actions that are automatically executed after specific events and under specific conditions. You need to configure following details into Trigger based on your requirement.

In General Tab add;

  • Objective – Select Entity modification.
  • Execution type – Select In background.

In Conditions Tab update;


In Actions Tab add;

Now implementation is done and whenever an asset is moved into approved state, It will trigger the action which we have created and action will invoke script which will generate public link for specific asset.

Sitecore Content Hub

Couple of months ago, I have started my learning of content hub. During the learning time, I have many questions related content hub such as why content hub?, what to do? and where to apply?

In this blog post, I would like to share my basic understanding of sitecore content hub.

Now a days, Increasing the use of devices in digital world, businesses want to display content everywhere. Content Hub provide ability act as a single source of truth for content. Means business content lives in a single place and it is consume by multiple source.

Content Hub is a SaaS Product which provide a set of services based on the content as service model. It allows you to seamlessly deliver organized content across any channel that other applications can easily consume.

Content Hub is built-in with multiple module which club to gather to enable a governance structure to manage Marketing, Assets and Product Information. In other term, We can call it as a Single source of truth (SSOT) for content.

About the Content Hub Licensing, It is available three editions “Professional”, “Corporate”, and “Enterprise”. Click

Below are the core modules of Sitecore Content Hub.

Sitecore DAM

Digital Asset Management is a module of Sitecore Content Hub. It’s core responsible to manage assets (Image, Video). There are many feature available to manage assets such as Create, Select, Edit, Analyze, Review, Search, Share, Download and Secure.

Sitecore PCM

Product Content Management is mainly use to manage product information and it comes from any source such as enterprise resource planning (ERP), master data management (MDM), or Excel sheets, and valuable data is often spread across the business teams (product, shipping, marketing, and sales).

To Streamline your content process in a single platform supporting multiple brands, complex product ranges, lifecycle, and localization.

One of the powerful feature of PCM is workflow which will help to provide an environment to create/publish product information in step by step flow such as verification, approval and rejection. It provide flexibility to implement custom workflow based on organization requirement.

Sitecore CMP

This module is mainly discover for the marketing content management. There are number of pre define template which we can use to manage content such as blog, social media, recipe, email and white paper etc.

It provide flexibility to integrate CMP Module to external different channels to publish content on different social media, blog or notify users by email etc.

It also support workflow feature which means, We can enable a process where all the content should flow and once it is publishable then and only then we can publish it.

Bug Fix in Sitecore 8.2 IR Delete Media Item

Recently, I was working on requirement of external media storage enable in sitecore 8.2 IR. During the development, I have observed some abnormal behaviour when delete any media item, which is created from copy or duplicate command.

After dig this behaviour in details, I found that the media item created using Duplicate/Copy To operation, both items (actual item and copied item) share the same blob reference in blobs table. So while we perform Delete operation on new copied item, it will remove media item as well as blob from blob table and due to that existing media item returns 404 Error.

In sitecore, There are two option available to manage delete operation and it is manage by below config entry of sitecore.config file.

<setting name=”RecycleBinActive” value=”false”/>

Option 1:  RecycleBinActive setting is enabled in sitecore instance, then delete command work fine. Because the delete operation not remove blob entry from database.

Option 2: RecycleBinActive setting is disabled in sitecore instance, then the issue will be occur in delete operation, Because, while we perform Delete operation on new copied item, it will remove media item as well as blob from blob table and due to that existing media item returns 404 Error.

In our case, Sitecore Support provided us a patch (# 227925) that solved the problem.

Hope this helps you guys…!

Unit testing

Hi Guys, Unit testing is becoming more and more popular in software development and it’s good to have application with unit tested. There are numerous frameworks, tools, and development processes can be used to perform unit testing. Here in this post, I am going to provide basic introduction of unit testing.

What is unit testing?

Unit testing is the code through which units of source code (actual functionality code) are tested to verify. Performing unit tests is a way to ensure that all functionalities of an application are working as they should.

It is not mandatory to implement unit test for each and every method and functionality. But it should cover all the main functionality code.

Unit testing framework.

There are number of unit testing frameworks are available in market. Each has it’s own matrix and features. Based on your project requirement, you can select unit testing framework.

Two most popular unit testing frameworks in the .NET world is the open source NUnit and the commercial MsTest.

Benefits of Unit Testing.

One of the main benefits of unit testing is that, it makes the coding process more Agile and improve quality of the code.

Issues are found at an early stage, so unit testing helps reduce the cost of bug fixes.

Unit testing reduces defects in the newly developed features or reduces bugs when changing the existing functionality.

Unit testing helps simplify the debugging process. If a test fails, then only the latest changes made in the code need to be debugged.

In next post, we will cover basic sample example of unit test project and different tools which we can use in our unit test project for better result….

 

Habitat project Setup

Recently, I have started working on Habitat setup and I am new to Helix guideline and Habitat project. Below links, I have used to learn more about Helix.

Helix Document – http://helix.sitecore.net/

Habitat Project – https://github.com/Sitecore/Habitat

Habitat Setup Wiki – https://github.com/Sitecore/Habitat

After this, I have started to setup Habitat project in my local system. During the setup of habitat project, I have faced some of the challenges, which I though it may be helpful to you as well while doing habitat setup.

Error occurs while setup Habitat Project.

Error – 1

Sync.ps1 cannot be loaded because running scripts is disabled on this system.


PS [HabitatProjectFolder]\scripts\Unicorn> .\Sync.ps1 -secret 749CABBC85EAD20CE55E2C6066FdBE3f5D2115d96s8A8B24DB6ED1FD60613086 -url http://habitat.dev.local/unicorn.aspx
.\Sync.ps1 : File C:\Sitecore\Sitecore-Habitat\Habitat-master\scripts\Unicorn\Sync.ps1 cannot be loaded because running
scripts is disabled on this system. For more information, see about_Execution_Policies at
http://go.microsoft.com/fwlink/?LinkID=135170.
At line:1 char:1
+ .\Sync.ps1 -secret 749CABBC85EAD20CE55E2C6066F1BE375D2115696C8A8B24DB ...
+ ~~~~~~~~~~
    + CategoryInfo          : SecurityError: (:) [], PSSecurityException
    + FullyQualifiedErrorId : UnauthorizedAccess

For Continues execution of script, we need to set windows powershell execution policies by executing below command on Powershell window.

PS [HabitatProjectFolder]\scripts\Unicorn> Set-ExecutionPolicy Unrestricted

Error – 2

Invoke-WebRequest :       The resource cannot be found.

PS [HabitatProjectFolder]\scripts\Unicorn> .\Sync.ps1 -secret 749CABBC85EAD20CE55E2C6066F1BE375D2115696C8A8B24DB6ED1FD60613086 -url http://habitat.dev.local/unicorn.aspx
Sync-Unicorn: Preparing authorization for http://habitat.dev.local/unicorn.aspx?verb=Sync&configuration=
Invoke-WebRequest : 
    
        The resource cannot be found.

Please make sure that, Your Sitecore website(http://habitat.dev.local) which you have link with habitat setup is up and running.

Error – 3

[08:57:43] Starting ’05-Sync-Unicorn’… –  Execution stuck

I have dig “[HabitatProjectFolder]/gulpfile.js” file in which, it was triggered “[HabitatProjectFolder]/scripts/Unicorn/Unicorn.psm1 and “[HabitatProjectFolder]/scripts/Unicorn/sync.psm1” files. All the commands are written on this file. I have commented out “$ErrorActionPreference = ‘Stop’” from both of the files by adding “#” before the line (Available on first line of files).

Now, I am able to see actual error in Task Runner Explorer of visual studio 2015.

Error – 4

In my case, I am getting Could not load file or assembly file MicroCHAP.dll or one of its dependencies”

I have unblock following files in the code repository, you can do it by right click on each one of them and click on “properties” – “General” – “Unblock”:

  • Habitat-master\scripts\Unicorn\MicroCHAP.dll
  • Habitat-master\scripts\Unicorn\Sync.ps1
  • Habitat-master\scripts\Unicorn\Unicorn.psm1.

Error – 5

“Rainbow – SERIALIZATION FOLDER PATH MAX LENGTH” – File Path Length cross 150 char length.

  • Max Length is 150 character, if your habitat project folder structure path takes more the 150 char then it gives error.

How to get sitecore unused media item using SQL Query

As we all know, Sitecore use link database to store referrers of item and using those referrers, we can identify unused media item.

Below are the steps which we need to execute to get unused media item.

Step 1: 

Identify database in which all the media item referrers are stored. For that, we need to check below configuration node in web.config/Sitecore.config file (default sitecore use core database to store referrers).

<!-- LINK DATABASE -->
<LinkDatabase type="Sitecore.Data.$(database).$(database)LinkDatabase, Sitecore.Kernel">

<param connectionStringName="core"/>

</LinkDatabase>

Step 2:

Below SQL query to get unused image/file item list.

Note:

Replace below tokens as per requirement.

#MediaRootItemID#: This token use to provide root media item (result of this query return all the unused descendant media items of given root media item).

#TemplateID#: if user exclude some of the items, which was created using specific template then replace those template ID in this token.

WITH descendantsItem AS(
SELECT ID,TemplateID
FROM [Master].[dbo].Items
WHERE ID = '#MediaRootItemID#'
UNION ALL
SELECT i.ID,i.TemplateID
FROM [Master].[dbo].Items i
INNER JOIN descendantsItem di
ON di.ID = i.ParentID ),LinksItem AS(
SELECT *
FROM [Core].[DBO].Links
WHERE TargetItemID in (
SELECT id
FROM descendantsItem
where descendantsItem.TemplateID != '{FE5DD826-48C6-436D-B87A-7C4210C7413B}'))
SELECT Id
FROM descendantsItem
WHERE descendantsItem.ID NOT IN (
SELECT TargetItemID
FROM LinksItem) AND TemplateID != '{FE5DD826-48C6-436D-B87A-7C4210C7413B}'
GO

 

Sitecore Item Operations Tool

I am really very excited to write this post because this post related to my first tool that have contributed in Sitecore Market Place (https://marketplace.sitecore.net). Finally they have approved and published the “Item Operations Tool” module and it is available on Sitecore marketplace.

This tool is use for doing some basic operations on item. So have given name as “Item Operations Tool”. Mainly this tool use for comparison of item with live item, count sub items and expands all the dependents of item

Download installation package from below link.

https://marketplace.sitecore.net/Modules/I/Item_Operations_Tool.aspx

Installation

Step -1: Download installation package from above link.

Step -2: installed downloaded package into local Sitecore.

Step -3: add following entry into Commands.config

<command name="item:ItemComparer" type="CustomCommands.ItemComparer,CustomCommands"/>

<command name="item:countSubItems" type="CustomCommands.CountSubItems,CustomCommands" />

<command name="item:ExpandDescendants" type="CustomCommands.ExpandDescendants,CustomCommands" />

Step -4: Restart Sitecore instance.

When you open Sitecore instance you can see new menu item in side version –> Item in below image.

Sitecore Item Operation Tool

Item Operations

Item Comparer

I am going to provide a tool in Content Editor that will allow users to compare contents of master and web databases. (Sitecore Ribbon: Version -> Item -> Item Compare)

– This will show all fields of both databases to compare.

– The default selected item and language should be compared.

Once you click on Item Compare button.

Compare Item

This will show all fields of both databases to compare.

Note:

The differences are highlighted in blue and red.

Blue: Apply on field value to highlight difference of field value.

Red: Apply on field title to identify that the field value is different form live field value).

Count Subitems

You can Count the number of sub items (all descendant sub items) of particular item. To do so, please follow below steps:

  1. Navigate to particular item of which you want to get sub items count.

Click on “Count SubItems” from ribbon as shown in below image.

count subitem in sitecore

Expand subitems

You can expand all sub items (all descendant sub items) of particular item. To do so, please follow below steps:

  1. Navigate to particular item of which you want to expand SubItems.

Click on “Expand SubItems” from ribbon as shown in below image to expand sub items (all descendant sub items) of particular item.

Expand Subitem

Sitecore PowerShell Extensions

Couple of week ago I started study of Sitecore PowerShell and have learned some basic concepts of Sitecore PowerShell. Here I am writing those my learning of Sitecore PowerShell so maybe this will help you to understand Sitecore PowerShell.

What is Sitecore PowerShell?

Sitecore PowerShell is a module that allow user to access PowerShell (Console, Integrated Scripting Environment & plugs into various Sitecore pipelines) with in Sitecore Desktop environment. A powerful tool for the Sitecore admin & developer. Using Sitecore PowerShell user can do anything because it simply call Sitecore API to perform operation.

How to Install Sitecore PowerShell Extensions?

Download the Console installation package from Sitecore Marketplace.

Link: https://marketplace.sitecore.net/en/Modules/Sitecore_PowerShell_console.aspx

Once you install downloaded package you will see a couple of additional icons in your Start menu. Sitecore Desktop Installation of Sitecore PowerShell Extensions successfully done.

In above screenshot start menu items highlighted in yellow these are the PowerShell Icons.

  1. PowerShell Console.

PowerShell Console

  1. PowerShell ISE

Sitecore PowerShell 03

User can perform scripting using PowerShell Console as well as PowerShell ISE.

Sample scripts

Working with pages

For the same of brevity I’m assuming your location is already somewhere within a Sitecore tree (e.g. you did something akin to cd master:\content prior to executing the following scripts.

List all properties & fields available for a particular page (including Sitecore properties defined in the item template).

get-childitem | get-member -memberType property*

List all items in the CMS of which template name contains “Article”

get-childitem -recurse `

| where-object { $_.TemplateName -match “Article” } `

| format-table DisplayName, Name, TemplateName

List all subitems and how many days ago were they modified

get-childitem -recurse `

| format-table -auto Name, `

@{Label=”Days since modified”; Expression={ `

[datetime]::Now.Subtract($_.__Updated).Days} }

Delete all pages that have not been modified for the last 365 days.

get-childitem -recurse `

| where-object {[datetime]::Now.Subtract($_.__Updated).Days -gt 365 } `

| remove-item

List all items Updated over the last 24 hours and who changed them.

get-childitem -recurse `

| where-object { $_.__Updated -gt [datetime]::Now.AddDays(-1) } `

| format-table -property DisplayName, “__Updated By”, {$_.Paths.Path}

List all pages that have their “Text” field filled in.

get-childitem -recurse `

| where-object { $_.Text -ne $null } `

| format-table -property DisplayName, {$_.Paths.Path}

Make a nice reviewer’s comment on all pages with their “Text” property filled in.

get-childitem -recurse `

| where-object { $_.Text -ne $null } `

| foreach-object { $_.ReviewersComment = “Great job providing content!” }

Add a warning to the beginning of “Text” property for all pages that have not been saved for the last 180 days.

get-childitem -recurse `

| where-object {`

[datetime]::Now.Subtract($_.__Updated).Days -ge 180 `

-and $_.Text -ne $null }

| foreach-object {$_.Text = “<p style=’color:red’>Old content. Review pending!</p>” + $_.Text }

Replace a string in a property on all pages with another string (in this sample – removing the warning the last sample added).

$old_content = “<p style=’color:red’>Old content. Review pending!</p>”

$new_content = “”;

get-childitem -recurse `

| where-object {$_.Text -match $old_content} `

| foreach-object {$_.Text = $_.Text.Replace($old_content, $new_content) }

Display the 10 most recently changed pages ordered in the reverse chronological order or changes. Display the page name, who changed it and when as well as the page status.

get-childitem -recurse `

| sort-object -property __Updated -descending `

| select-object -First 10 `

| format-table -property DisplayName, “__Updated By”, __Updated

Working with Media Library / files

Removes all .xml files from the “Old Xml Files” in Media library.

cd “master:\media library\Old Xml Files”

get-childitem -recurse `

| where-object { $_.Extension -match “xml” } `

| remove-item

Copy all files from the “staging” folder to the “production” folder in Media Library.

Copying files within media library is done as follows. This can also be done for items in the content branch.

copy-item “master:\media library\staging\*” “master:\media library\production\”

Displaying Images in ASP.NET Using HttpHandlers

Now you know what HttpHandlers are and how to setup them, we will write our own httpHandler to read the images from a database. Remember, this article explains creating httpHandler using the generic handler (.ashx). Since the .ashx file extension is already mapped in the IIS to the ASP.NET ISAPI filter, you do not have to configure your web.config file.

Create a new website in Visual Studio. Right-click on the project in the solution explorer and select Add New Item. From the available templates, add the new file of type Generic Handler and name it ImageHandler.ashx

Visual Studio opens a new file with the following content.

<%@ WebHandler Language="C#" Class="getImage" %>
 using System;
 using System.Web;
 
 public class getImage : IHttpHandler {
     
     public void ProcessRequest (HttpContext context) {
         context.Response.ContentType = "text/plain";
         context.Response.Write("Hello World");
     }
 
     public bool IsReusable {
         get  {
             return false;
         }
    }
 }

By default, the IHttpHandler is implemented. We will modify the code in ProcessRequest to read images from database. We are going to use the DataTable to read the data from the database.

using System;
using System.Collections.Generic;
using System.Data;
using System.Data.SqlClient;
using System.Linq;
using System.Web;

namespace UserManagement
{
/// <summary>
/// Summary description for ImageHandler
/// </summary>
public class ImageHandler : IHttpHandler
{

    public void ProcessRequest(HttpContext context)
    {
       string _imageId;
       if (context.Request.QueryString["id"] != null)
          _imageId = context.Request.QueryString["id"];
      else
      throw new ArgumentException("No parameter specified");


      SqlConnection objcon = new SqlConnection(System.Configuration.ConfigurationManager.
      ConnectionStrings["master"].ConnectionString);
      SqlCommand cmd = new SqlCommand();
      cmd.Connection = objcon;
      cmd.CommandText = "SELECT [Id],[Data] FROM [dbo].[ImageTable] where ID like '" + _imageId + "'";
      cmd.CommandType = System.Data.CommandType.Text;

      SqlDataAdapter da = new SqlDataAdapter(cmd);
      DataTable dt = new DataTable();
      da.Fill(dt);



      if (dt.Rows.Count > 0)
      {
         if (dt.Rows[0]["Data"] != DBNull.Value)
         {
             context.Response.ContentType = "image/jpeg";
             context.Response.BinaryWrite((byte[])dt.Rows[0]["Data"]);
         }
     }
   }
   public bool IsReusable
   {
      get { return false;}
   }
  }
}

now add image tage on Default.aspx page like.
<img alt=”test” src=”ImageHandler.ashx?id=9b27f3b5-9844-4ac9-8e60-7c6f1f0bb91f” />

Now run your application and the page (Default.aspx) displays the image served through HttpHandler.

C#.Net- How to Convert SQL to XML String and render output using XslCompiledTransform in Asp.net

Here I will explain how to convert datatable to xml string in asp.net using C#

Code for faching data from sql database

using (SqlConnection objcon = new SqlConnection(ConfigurationManager.ConnectionStrings[“TestDB”].ConnectionString))
{
objcon.Open();
SqlDataAdapter da = new SqlDataAdapter(“select * from Emp_Info”, objcon);
DataTable dt = new DataTable(“empInfo”);
da.Fill(dt);

using (TextWriter writer = new StringWriter())
{
dt.WriteXml(writer);
Response.Write(new XMLHelper().GetValue(@”C:\Tushar\SQLToXMLExample\XSLT\Demo.xslt”, writer.ToString()));
}
}
XMLHelper that generate Output string using XSLT file and XML String

public class XMLHelper
{
public XMLHelper()
{ }
public string GetValue(string templatePath, string xmlString)
{
XDocument xmlObj = XDocument.Parse(xmlString);
XDocument result = GetResultXml(templatePath, xmlObj);

return (result == null) ? string.Empty : result.Document.ToString();
}
public XDocument GetResultXml(string templatePath, XDocument xmlObj)
{
XDocument result = new XDocument();
using (XmlWriter writer = result.CreateWriter())
{
XslCompiledTransform xslt = new XslCompiledTransform();
xslt.Load(templatePath);
xslt.Transform(xmlObj.CreateReader(), writer);
}
return result;
}
}

XSLT File — Sample Demo

<?xml version=”1.0″ encoding=”utf-8″?>
<xsl:stylesheet version=”1.0″ xmlns:xsl=”http://www.w3.org/1999/XSL/Transform&#8221;
xmlns:msxsl=”urn:schemas-microsoft-com:xslt” exclude-result-prefixes=”msxsl”
>
<xsl:output method=”html” omit-xml-declaration=”yes” indent=”yes”/>

<xsl:template match=”@* | node()”>
<html>
<body>
<table>
<tr>
<xsl:for-each select=”/*/node()”>
<xsl:if test=”position()=1″>
<xsl:for-each select=”*”>
<td>
<xsl:value-of select=”local-name()”/>
</td>
</xsl:for-each>
</xsl:if>
</xsl:for-each>
</tr>
<xsl:for-each select=”*”>
<tr>
<xsl:for-each select=”*”>
<td>
<xsl:value-of select=”.”/>
</td>
</xsl:for-each>
</tr>
</xsl:for-each>
</table>
</body>
</html>
</xsl:template>
</xsl:stylesheet>