Surendra Sharma

Surendra Sharma

Search This Blog

Sunday, October 15, 2017

5 different ways of find and replace content in Sitecore

Find and Replace functionality in Sitecore
Find and Replace functionality in Sitecore


Find and replace technique is very useful for editors and developers when they want to change some or bulk of content in Sitecore instance. I am listing out 5 possible ways to do this operation in Sitecore.
 

1.Manual

Ofcourse even if you don’t know any other way, its always there. This is mostly a first approach for any non-technical user.

Advantage

·         You don’t need any tool for this.
·         Useful for less than 10 items

Disadvantage

·         As a human being, content editor can easily make a mistake with this approach
·         Don’t use this technique if changes are to be done in more than 10 items.

2 . Find and replace using Sitecore search


This is inbuilt and somewhat hidden functionality available in Sitecore which is pretty straight forward and very powerful. For this
1.    Select parent item under which we need to find some text
2.    Click on Search icon in right top section
3.    This should open search window
4.    Enter text to be search in box
5.    Click on search icon button. This should displayed searched items as a result.
6.    Click on “More Search Options” arrow in left side of search box.
7.    Select “Search Operations” which will displayed all operations that you can do with this search result.
8.    Select “Search and replace” option.
9.    Enter new text
10.  Click Ok.

Search and replace in Sitecore
Search and replace in Sitecore


That’s it. Your new text should be appeared in all the items that are part of find result.

Advatnage

·         Quick and default functionality available in Sitecore
·         No special tool required to run
·         One can filter result with additional search facets

Disadvantage

·         Sometimes this functionality not work even text is available in items. 

3. From database


Sometimes we want to demo existing projects to new client. At this time we want to assign new client company name and logo on all the places in existing Sitecore instance. For this kind of situation, one can use this database find and replace approach.

I already covered this approach in one of my article at http://www.sitecorelessons.com/2016/10/changing-data-from-sitecore-database.html

In short, you have to fire update stament in VersionedFields table as

Update VersionedFields set Value = 'www.sitecorelessons.com from Surendra Sharma' + Value where Id = '61C78797-DD31-41AB-976B-A6800F2E9403'

Clear the Sitecore cache and you will get the new data in Sitecore content tree.

Advantage

·         With single update statement, you can change the entire database.

Disadvantage

·         Require access of database
·         Only developers can used this approach. This is not for content editors
·         Find data and verify before updating
·         Can’t be rollback once you done the changes. [Can be used with combination of SQL transacations to roll back.]
·         Sitecore not recommended this approach as its very risky.

4.Code


As a developer, we can write a code to find the data and then replace it. Instead of writing in C# file, write code directly in “.aspx” file with server side tags to avoid code build. A sample code can be like given below which will find text in item’s all languages in all versions

<%@ page language="C#" autoeventwireup="true" %>


<%@ import namespace="System.IO" %>
<%@ import namespace="Sitecore.Data" %>
<%@ import namespace="Sitecore.Data.Items" %>
<%@ import namespace="Sitecore.Links" %>
<%@ import namespace="Sitecore.Globalization" %>
<%@ import namespace="System.Data.SqlClient" %>
<%@ import namespace="System.Data" %>
<%@ import namespace="Sitecore.Data.Fields" %>
<%@ import namespace="System.Text.RegularExpressions" %>
<%@ import namespace="System.Collections.Specialized" %>
<%@ import namespace="Sitecore.Data.Managers" %>
<%@ import namespace="Sitecore.Collections" %>
<%@ import namespace="Sitecore.SecurityModel" %>
<%@ import namespace="Sitecore.Data.Managers" %>


<!DOCTYPE html>

<html xmlns="http://www.w3.org/1999/xhtml">
<head id="Head2" runat="server">
<script runat="server">
StringBuilder log = new StringBuilder();

Sitecore.Data.Database db = null;

LanguageCollection languages = null;

protected void Page_Load(object sender, EventArgs e)
{

    db = Database.GetDatabase("master");

    languages = LanguageManager.GetLanguages(db);

    builderText.Text = GetJunkDataItem();
}


private string GetJunkDataItem()
{
    var objRootItem = db.GetItem("{AE4F3D24-5C19-4F0C-BD40-D8952F758F32}");

    //Get Home Item details
    WriteItemDetails(objRootItem);

    foreach (Item objChild in objRootItem.Children)
    {
        //Get Child of Home Item details
        WriteItemDetails(objChild);
        AddSubItem(objChild);
    }

    return log.ToString();
}

private void AddSubItem(Item child)
{
    foreach (Item objChildItem in child.Children)
    {
        WriteItemDetails(objChildItem);

        if (objChildItem.HasChildren)
        {
            AddSubItem(objChildItem);
        }
    }
}

private void WriteItemDetails(Item child)
{
    foreach (Language language in languages)
    {
        Item languageSpecificItem = db.GetItem(child.ID, language);
        if (languageSpecificItem != null && !languageSpecificItem.IsFallback && languageSpecificItem.Versions.Count > 0)
        {
            foreach (var versionItem in languageSpecificItem.Versions.GetVersions())
            {
                var searchFields = versionItem.Fields.Where(x => FieldTypeManager.GetField(x) is HtmlField
                && (x.Value.ToUpper().Contains("Oldtext"))).ToList();

                if (searchFields != null && searchFields.Count > 0)
                {
                    using (new Sitecore.SecurityModel.SecurityDisabler())
                    {
                        versionItem.Editing.BeginEdit();

                        foreach (var tempField in searchFields)
                        {
                            tempField.Value = tempField.Value.Replace("Oldtext", "Newtext");
                        }
                        versionItem.Editing.EndEdit();

                        log.Append(language.Name + ";" + languageSpecificItem.Version.Number + "--------<br/>");
                    }
                }
            }
        }
    }
}     
</script>
</head>
<body>

<form id="form2" runat="server">
        <asp:button id="Button1" visible="false" runat="server" text="Edit Blog Items" />
        <div>
            <asp:literal id="builderText" runat="server" />
            <asp:textbox id="builderText1" visible="false" runat="server" textmode="multiline" height="702px" width="1258px" />

        </div>
    </form>
</body>
</html>

Advantage

·         C# code is familiar to developer so its very easy to write this.

Disadvantage

·         Need to deploy the code or file on website environment
·         As URL may be publicly available, anybody can fire it which may corrupt data.  

5. Sitecore Powershell


You wondered if single command can do the find and replace work and that’s the magic of Sitecore Powershell. You can execute below script to find and replace in sitecore Powershell ISE

$old_content = "Oldcontent"
$new_content = "Newcontent";
get-childitem -recurse `
  | where-object {$_.Text -match $old_content} `
  | foreach-object {$_.Text = $_.Text.Replace($old_content, $new_content) }


Advantage

·         Whatever developers can do with C# code can be same done by Sitecore Powershell in one or fewer lines.
·         Easily create find text items report in well-format manner

Disadvantage

·         Need to install this module in Sitecore instance
·         Need to learn how to write and execute Powershell command.

I wish Powershell should be by default available in Sitecore installation.

Its feasible to comment out which is best way to find and replace approach as its entirely depend on situation and user skillset. But personally I like Sitecore Search-replace functionality and Powershell approach.

Let me know if you come across with any other way to find and replace content in Sitecore.

I hope you enjoy this Sitecore article. Stay tuned for more Sitecore related articles.

Till that happy Sitecoring :)

Please leave your comments or share this article if it’s useful for you.


Monday, October 9, 2017

Curious case of GatherContent module in Sitecore environment


Sitecore-GatherContent Integration
Sitecore-GatherContent Integration

I was trying to integrate GatherContent module in Sitecore.

By the way GatherContent is a collaboration tool where content is drafted, reviewed, analyzed and approved. After approval one can use this data in Sitecore.

It’s pretty simple module. Just download, install and configured it.
But after configuration, when I click on Test Connection- I was expecting either Pass or Fail result, but unfortunately I received error - "The remote server returned an error: (500) Internal Server Error."

I googled this GatherContent error for but was out of luck. So I tried to fix it with no directions. 

I started my investigation from configuration values in GatherContent item at “/sitecore/system/Modules/GatherContent”. Just to make sure, I filled same configuration values in another Sitecore instance and it was working fine. 

Now I was sure that it was my Sitecore project specific instance issue.

Next I checked Sitecore log files and found this error

Exception: System.Net.WebException
Message: The remote server returned an error: (500) Internal Server Error.
Source: System
   at System.Net.HttpWebRequest.GetResponse()
   at GatherContent.Connector.Website.Commands.TestConnection Command.Run(ClientPipelineArgs args)
  
This error gave me next pointer where I decided to decompile the "GatherContent.Connector.Website" DLL using "JetBrains dotPeek" tool.

I checked the code of Run() method in "GatherContent.Connector.Website.Commands.TestConnection" class as mentioned in above error message.

Below is a code for Run() method

Run() Method Code
Run() Method Code


As highlighted "/api/sitecore/mappings/try" is called by Run() method to check the HTTPStatus code. So this call is my next direction to scrutinize.

When I tried to access "http://sitecoreinstance/api/sitecore/mappings/try" from browser, I received error as "The controller for path '/api/sitecore/mappings/try' was not found or does not implement IController."

API Error
API Error


Till this point it was clear that as GatherContent is unable to find "/api/sitecore/mappings/try", that’s why reporting error "The remote server returned an error: (500)".

Next I decided to define all the controllers routing in Sitecore MVC project used by GatherCOntent.

Before that we need to find all controllers referred by GatherContent. Again our helper tool "JetBrains dotPeek" help here. Open DLL "GatherContent.Connector.WebControllers" and check controllers name in "GatherContent.Connector.WebControllers.Controllers" namespace as

GatherContent Controllers
GatherContent Controllers

Specify all these controller's Routing in your Sitecore MVC webproject Global.asax.cs file as


protected void Application_Start(object sender, EventArgs e)

{
    AreaRegistration.RegisterAllAreas();
    RegisterRoutes(RouteTable.Routes);
}

public static void RegisterRoutes(RouteCollection routes)
{
            routes.MapRoute(
    "base",
    "api/sitecore/base/{action}/{id}",
    new { controller = "base", id = UrlParameter.Optional }
    );
            routes.MapRoute(
    "droptree",
    "api/sitecore/droptree/{action}/{id}",
    new { controller = "droptree", id = UrlParameter.Optional }
    );

            routes.MapRoute(
    "import",
    "api/sitecore/import/{action}/{id}",
    new { controller = "import", id = UrlParameter.Optional }
    );

            routes.MapRoute(
    "mappings",
    "api/sitecore/mappings/{action}/{id}",
    new { controller = "mappings", id = UrlParameter.Optional }
    );

            routes.MapRoute(
    "update",
    "api/sitecore/update/{action}/{id}",
    new { controller = "update", id = UrlParameter.Optional }
    );

}

Build and publish this project.

Now try to test GatherContent connection and you should get "Success" message

GatherContent Test Connection Success Message
GatherContent Test Connection Success Message
Greattt, if you are reading this line, it means you are able to solve the issue which was not available on Google and GatherContent helpcenter.

I hope you like this Sitecore GatherContent investigation. Stay tuned for more Sitecore related details.

Till that happy Sitecoring :)

Please leave your comments or share this article if it’s useful for you.