Pages

Showing posts with label Upgrade. Show all posts
Showing posts with label Upgrade. Show all posts

02 September, 2021

Sitecore Upgrade - Codebase Upgrade - Migrate from packages.config to PackageReference

We have been always using packages.config file to manage the nuget package references. Instead of config file, PackageReference has been introduced to provide benefits for the developers. 

Benefits:

  1. Instead of showing all the dependencies in the packages.config file, only top level dependencies will be added as part of PackageReference. This gives a clean and simple view on the references. 
  2. Ability to manage all the project level configuration like package references in a single csproj file itself.
  3. Adding and removing packages are faster compared to Packages.config file. 
Migration of Packages.config to PackageReferences
To migration all the existing references, right-click the References node in Solution Explorer, and select the Migrate packages.config to PackageReference command.


Default to PackageReferences:
In order to add packages to PackageReferences by default, open Tools > NuGet Package Manager > Package Manager Settings menu, change the Default package management format to PackageReference.



20 August, 2021

Sitecore Upgrade - Codebase Upgrade - Migrate to SDK Project Format

The older style of csproj has lot of cumbersome tasks like adding files, excluding files, complex in adding references and packages.config file maintenance. 

The new SDK project format was introduced along with Visual Studio 2017. Some of the pros includes:

  • Clean csproj file with the ability to modify the .NET framework or .NET core version
  • Ability to view/edit the csproj file without excluding the project in Visual Studio
  • No need of Packages.json file and include the nuget references as part of csproj file itself
  • No need to include each and every .cs file to the project. 
A typical old csproj format file: You will see so many lines which could have been defaulted somewhere else. 
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="12.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
  <Import Project="..\..\..\packages\Microsoft.CodeDom.Providers.DotNetCompilerPlatform.1.0.0\build\Microsoft.CodeDom.Providers.DotNetCompilerPlatform.props" Condition="Exists('..\..\..\packages\Microsoft.CodeDom.Providers.DotNetCompilerPlatform.1.0.0\build\Microsoft.CodeDom.Providers.DotNetCompilerPlatform.props')" />
  <Import Project="..\..\..\packages\Microsoft.Net.Compilers.1.0.0\build\Microsoft.Net.Compilers.props" Condition="Exists('..\..\..\packages\Microsoft.Net.Compilers.1.0.0\build\Microsoft.Net.Compilers.props')" />
  <Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
  <PropertyGroup>
    <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
    <Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
    <ProductVersion>
    </ProductVersion>
    <SchemaVersion>2.0</SchemaVersion>
    <ProjectGuid>{30EB7022-CCB9-48AB-96C1-3556565A51C1}</ProjectGuid>
    <ProjectTypeGuids>{349c5851-65df-11da-9384-00065b846f21};{fae04ec0-301f-11d3-bf4b-00c04f79efbc}</ProjectTypeGuids>
    <OutputType>Library</OutputType>
    <AppDesignerFolder>Properties</AppDesignerFolder>
    <RootNamespace>SitecoreDemo.Feature.Accounts</RootNamespace>
    <AssemblyName>SitecoreDemo.Feature.Accounts</AssemblyName>
    <TargetFrameworkVersion>v4.8</TargetFrameworkVersion>
    <UseIISExpress>true</UseIISExpress>
    <IISExpressSSLPort />
    <IISExpressAnonymousAuthentication />
    <IISExpressWindowsAuthentication />
    <IISExpressUseClassicPipelineMode />
    <UseGlobalApplicationHostFile />
    <SccProjectName>SAK</SccProjectName>
    <SccLocalPath>SAK</SccLocalPath>
    <SccAuxPath>SAK</SccAuxPath>
    <SccProvider>SAK</SccProvider>
    <NuGetPackageImportStamp>
    </NuGetPackageImportStamp>
    <Use64BitIISExpress />
    <TargetFrameworkProfile />
  </PropertyGroup>
  <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
    <DebugSymbols>true</DebugSymbols>
    <DebugType>full</DebugType>
    <Optimize>false</Optimize>
    <OutputPath>bin\</OutputPath>
    <DefineConstants>DEBUG;TRACE</DefineConstants>
    <ErrorReport>prompt</ErrorReport>
    <WarningLevel>4</WarningLevel>
  </PropertyGroup>
  <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
    <DebugType>pdbonly</DebugType>
    <Optimize>true</Optimize>
    <OutputPath>bin\</OutputPath>
    <DefineConstants>TRACE</DefineConstants>
    <ErrorReport>prompt</ErrorReport>
    <WarningLevel>4</WarningLevel>
  </PropertyGroup>
  <ItemGroup>
    <Reference Include="Microsoft.AI.Agent.Intercept, Version=1.2.1.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL">
      <HintPath>..\..\..\packages\Microsoft.ApplicationInsights.Agent.Intercept.1.2.1\lib\net45\Microsoft.AI.Agent.Intercept.dll</HintPath>
      <Private>True</Private>
    </Reference>
    <Reference Include="Microsoft.AI.DependencyCollector, Version=2.1.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL">
      <HintPath>..\..\..\packages\Microsoft.ApplicationInsights.DependencyCollector.2.1.0\lib\net45\Microsoft.AI.DependencyCollector.dll</HintPath>
      <Private>True</Private>
    </Reference>
    <Reference Include="Microsoft.AI.PerfCounterCollector, Version=2.1.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL">
      <HintPath>..\..\..\packages\Microsoft.ApplicationInsights.PerfCounterCollector.2.1.0\lib\net45\Microsoft.AI.PerfCounterCollector.dll</HintPath>
      <Private>True</Private>
    </Reference>
    <Reference Include="Microsoft.AI.ServerTelemetryChannel, Version=2.1.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL">
      <HintPath>..\..\..\packages\Microsoft.ApplicationInsights.WindowsServer.TelemetryChannel.2.1.0\lib\net45\Microsoft.AI.ServerTelemetryChannel.dll</HintPath>
      <Private>True</Private>
    </Reference>
    <Reference Include="Microsoft.AI.Web, Version=2.1.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL">
      <HintPath>..\..\..\packages\Microsoft.ApplicationInsights.Web.2.1.0\lib\net45\Microsoft.AI.Web.dll</HintPath>
      <Private>True</Private>
    </Reference>
    <Reference Include="Microsoft.AI.WindowsServer, Version=2.1.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL">
      <HintPath>..\..\..\packages\Microsoft.ApplicationInsights.WindowsServer.2.1.0\lib\net45\Microsoft.AI.WindowsServer.dll</HintPath>
      <Private>True</Private>
    </Reference>
    <Reference Include="Microsoft.ApplicationInsights, Version=2.1.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL">
      <HintPath>..\..\..\packages\Microsoft.ApplicationInsights.2.1.0\lib\net46\Microsoft.ApplicationInsights.dll</HintPath>
      <Private>True</Private>
    </Reference>
    <Reference Include="Microsoft.CodeDom.Providers.DotNetCompilerPlatform, Version=1.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL">
      <HintPath>..\..\..\packages\Microsoft.CodeDom.Providers.DotNetCompilerPlatform.1.0.0\lib\net45\Microsoft.CodeDom.Providers.DotNetCompilerPlatform.dll</HintPath>
      <Private>True</Private>
    </Reference>
    <Reference Include="Microsoft.CSharp" />
    <Reference Include="Newtonsoft.Json, Version=10.0.0.0, Culture=neutral, PublicKeyToken=30ad4fe6b2a6aeed, processorArchitecture=MSIL">
      <HintPath>..\..\..\packages\Newtonsoft.Json.10.0.3\lib\net45\Newtonsoft.Json.dll</HintPath>
      <Private>True</Private>
    </Reference>
    <Reference Include="RestSharp, Version=105.2.3.0, Culture=neutral, processorArchitecture=MSIL">
      <SpecificVersion>False</SpecificVersion>
      <HintPath>..\..\..\packages\RestSharp.105.2.3\lib\net46\RestSharp.dll</HintPath>
    </Reference>
    <Reference Include="System.Net.Http" />
    <Reference Include="System.Runtime.Serialization" />
    <Reference Include="System.Web.DynamicData" />
    <Reference Include="System.Web.Entity" />
    <Reference Include="System.Web.ApplicationServices" />
    <Reference Include="System.ComponentModel.DataAnnotations" />
    <Reference Include="System" />
    <Reference Include="System.Data" />
    <Reference Include="System.Core" />
    <Reference Include="System.Data.DataSetExtensions" />
    <Reference Include="System.Web.Extensions" />
    <Reference Include="System.Xml.Linq" />
    <Reference Include="System.Drawing" />
    <Reference Include="System.Web" />
    <Reference Include="System.Xml" />
    <Reference Include="System.Configuration" />
    <Reference Include="System.Web.Services" />
    <Reference Include="System.EnterpriseServices" />
  </ItemGroup>
  <ItemGroup>
    <Reference Include="System.Web.Razor">
      <HintPath>..\..\..\packages\Microsoft.AspNet.Razor.3.2.3\lib\net45\System.Web.Razor.dll</HintPath>
    </Reference>
    <Reference Include="System.Web.Webpages">
      <HintPath>..\..\..\packages\Microsoft.AspNet.Webpages.3.2.3\lib\net45\System.Web.Webpages.dll</HintPath>
    </Reference>
    <Reference Include="System.Web.Webpages.Deployment">
      <HintPath>..\..\..\packages\Microsoft.AspNet.Webpages.3.2.3\lib\net45\System.Web.Webpages.Deployment.dll</HintPath>
    </Reference>
    <Reference Include="System.Web.Webpages.Razor">
      <HintPath>..\..\..\packages\Microsoft.AspNet.Webpages.3.2.3\lib\net45\System.Web.Webpages.Razor.dll</HintPath>
    </Reference>
    <Reference Include="System.Web.Helpers">
      <HintPath>..\..\..\packages\Microsoft.AspNet.Webpages.3.2.3\lib\net45\System.Web.Helpers.dll</HintPath>
    </Reference>
    <Reference Include="Microsoft.Web.Infrastructure">
      <HintPath>..\..\..\packages\Microsoft.Web.Infrastructure.1.0.0.0\lib\net40\Microsoft.Web.Infrastructure.dll</HintPath>
    </Reference>
    <Reference Include="System.Web.Mvc">
      <HintPath>..\..\..\packages\Microsoft.AspNet.Mvc.5.2.3\lib\net45\System.Web.Mvc.dll</HintPath>
    </Reference>
  </ItemGroup>
  <ItemGroup>
    <Content Include="Views\ChangePassword\_ChangePassword.cshtml" />
    <Content Include="Views\ChangePassword\_ChangePasswordEdit.cshtml" />
    <Content Include="Views\Common\_SiteDownAlert.cshtml" />    
    <Content Include="Views\Common\_WelcomeBanner.cshtml" />
    <Content Include="Views\Notification\Preference.cshtml" />
    <Content Include="Views\Notification\UserInfo.cshtml" />
  </ItemGroup>
  <ItemGroup>
    <Compile Include="Attribute\SessionExpireFilterAttribute.cs" />    
    <Compile Include="BusinessLogic\ChangePassword.cs" />
    <Compile Include="Controllers\ChangePasswordController.cs" />
    <Compile Include="Constants.cs" />
    <Compile Include="Controllers\BaseController.cs" />
    <Compile Include="Controllers\NotificationController.cs" />
    <Compile Include="Controllers\ToolsController.cs" />
    <Compile Include="Helper\EnumHelper.cs" />
    <Compile Include="Models\Customer.cs" />
  </ItemGroup>
  <ItemGroup>
    <ProjectReference Include="..\Marketing\Marketing.csproj">
      <Project>{1117786e-0ae1-44b0-af00-1ec3adc173ee}</Project>
      <Name>Marketing</Name>
    </ProjectReference>
  </ItemGroup>
  <ItemGroup>
    <Content Include="web.config" />
    <None Include="web.Debug.config">
      <DependentUpon>web.config</DependentUpon>
    </None>
  </ItemGroup>
  <PropertyGroup>
    <VisualStudioVersion Condition="'$(VisualStudioVersion)' == ''">10.0</VisualStudioVersion>
    <VSToolsPath Condition="'$(VSToolsPath)' == ''">$(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion)</VSToolsPath>
  </PropertyGroup>
  <PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'DEV|AnyCPU'">
    <OutputPath>bin\</OutputPath>
  </PropertyGroup>
  <PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'PROD|AnyCPU'">
    <OutputPath>bin\</OutputPath>
  </PropertyGroup>
  <PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'UAT|AnyCPU'">
    <OutputPath>bin\</OutputPath>
  </PropertyGroup>
  <Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" />
  <Import Project="$(VSToolsPath)\WebApplications\Microsoft.WebApplication.targets" Condition="'$(VSToolsPath)' != ''" />
  <Import Project="$(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v10.0\WebApplications\Microsoft.WebApplication.targets" Condition="false" />
  <ProjectExtensions>
    <VisualStudio>
      <FlavorProperties GUID="{349c5851-65df-11da-9384-00065b846f21}">
        <WebProjectProperties>
          <UseIIS>False</UseIIS>
          <AutoAssignPort>True</AutoAssignPort>
          <DevelopmentServerPort>58420</DevelopmentServerPort>
          <DevelopmentServerVPath>/</DevelopmentServerVPath>
          <IISUrl>http://localhost:57567/</IISUrl>
          <NTLMAuthentication>False</NTLMAuthentication>
          <UseCustomServer>False</UseCustomServer>
          <CustomServerUrl>
          </CustomServerUrl>
          <SaveServerSettingsInUserFile>False</SaveServerSettingsInUserFile>
        </WebProjectProperties>
      </FlavorProperties>
    </VisualStudio>
  </ProjectExtensions>
  <Target Name="EnsureNuGetPackageBuildImports" BeforeTargets="PrepareForBuild">
    <PropertyGroup>
      <ErrorText>This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them.  For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}.</ErrorText>
    </PropertyGroup>
    <Error Condition="!Exists('..\..\..\packages\Microsoft.Net.Compilers.1.0.0\build\Microsoft.Net.Compilers.props')" Text="$([System.String]::Format('$(ErrorText)', '..\..\..\packages\Microsoft.Net.Compilers.1.0.0\build\Microsoft.Net.Compilers.props'))" />
    <Error Condition="!Exists('..\..\..\packages\Microsoft.CodeDom.Providers.DotNetCompilerPlatform.1.0.0\build\Microsoft.CodeDom.Providers.DotNetCompilerPlatform.props')" Text="$([System.String]::Format('$(ErrorText)', '..\..\..\packages\Microsoft.CodeDom.Providers.DotNetCompilerPlatform.1.0.0\build\Microsoft.CodeDom.Providers.DotNetCompilerPlatform.props'))" />
  </Target>
  <!-- To modify your build process, add your task inside one of the targets below and uncomment it. 
       Other similar extension points exist, see Microsoft.Common.targets.
  <Target Name="BeforeBuild">
  </Target>
  <Target Name="AfterBuild">
  </Target>
  -->
</Project>

Migrated to SDK format: A simple SDL format csproj file. Readable and easily modifiable. 
<Project Sdk="Microsoft.NET.Sdk">
  <PropertyGroup>
    <TargetFramework>net48</TargetFramework>
	<ApplicationIcon />
	<OutputType>Library</OutputType>
	<StartupObject />
	<Authors>Author</Authors>
	<Company>Company Name</Company>
	<GenerateAssemblyInfo>false</GenerateAssemblyInfo>
	<CodeAnalysisRuleSet>$(SolutionDir)\StyleCop.ruleset</CodeAnalysisRuleSet>
  </PropertyGroup> 
  <ItemGroup>
    <Content Include="Views\ChangePassword\_ChangePassword.cshtml" />
    <Content Include="Views\ChangePassword\_ChangePasswordEdit.cshtml" />
    <Content Include="Views\Common\_SiteDownAlert.cshtml" />    
    <Content Include="Views\Common\_WelcomeBanner.cshtml" />
    <Content Include="Views\Notification\Preference.cshtml" />
    <Content Include="Views\Notification\UserInfo.cshtml" />
    <Content Include="Web.config" />
  </ItemGroup>
  <ItemGroup>
    <PackageReference Include="Glass.Mapper.Sc.93.Core">
      <Version>5.6.160</Version>
    </PackageReference>
    <PackageReference Include="Sitecore.Mvc" Version="9.3.0" />
    <PackageReference Include="Newtonsoft.Json" Version="11.0.2" />
    <PackageReference Include="Microsoft.CodeDom.Providers.DotNetCompilerPlatform" Version="3.6.0" />
    <PackageReference Include="Sitecore.Kernel" Version="9.3.0" />
    <PackageReference Include="StyleCop.Analyzers" Version="1.1.118">
      <PrivateAssets>all</PrivateAssets>
      <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
    </PackageReference>
  </ItemGroup>
  <ItemGroup>
    <Reference Include="Microsoft.CSharp" />
    <Reference Include="System" />
  </ItemGroup>
  <ItemGroup>
    <Folder Include="Models\ExtensionModels\" />
  </ItemGroup>
  <ItemGroup>
    <ProjectReference Include="..\..\..\Foundation\Extensions\website\SitecoreDemo.Foundation.Extensions.csproj">
      <Project>{a53176ca-ff2g-4b2s-h6j3-1tfs2g94jfb5}</Project>
      <Name>SitecoreDemo.Foundation.Extensions</Name>
    </ProjectReference>
    <ProjectReference Include="..\..\..\Foundation\Marketing\website\SitecoreDemo.Foundation.Marketing.csproj" />
  </ItemGroup>
 </Project>

How to migrate:
There are few options available to migrate to SDK format.

Option 1:
An open source module which does most of the task for us. By simply calling the console app followed by our csproj file will convert most of the project files. We may need to clean up few reference related tasks which will discuss in the next post of the Upgrade series. 


Option 2:
Manually change the csproj file to SDK format. 
  1. Replace entire PropertyGroup with the simple SDK version.
    1. TargetFramework
    2. OutputType
    3. GenerateAssemblyInfo
  2. Remove all compiler related <Import />
  3. Remove all <ItemGroup /> containing .cs files
  4. Remove <ProjectExtensions /> and <Target />
Migrating from packages.json to PackageReferences will be explained in the next blog post. 

22 July, 2021

Coveo for Sitecore 5 with Sitecore Horizon 10

When we try to use Sitecore Horizon (10.0.1) with with a solution having Coveo integration, Sitecore Horizon throws the below exception in the UI. It does not load any sites or languages in the dropdown. When we try to remove site definition with name "coveo_website", it is loading fine. As per Coveo, we should not remove this site definition as it will impact Coveo integration. 

Reached out to awesome Sitecore community in Sitecore Slack for help and Jeff (@jflh) provided a simple workaround patch configuration in the Sitecore.Demo.Platform repo. Basically we are changing the Coveo_website site definition to an item that does not exist. Horizon will not consider this site as a valid site to be editable in the editor. 

<configuration xmlns:patch="http://www.sitecore.net/xmlconfig/" xmlns:coveo="http://www.sitecore.net/xmlconfig/coveo/">
	<sitecore coveo:require="!disabled">
		<sites>
			<!--
				DEMO WORKAROUND
				Coveo for Sitecore and Horizon modules are incompatible by default. Horizon handles the coveo_website as a content site.
				We are applying a workaround similar to what Unicorn had done to fix the same issue: https://github.com/SitecoreUnicorn/Unicorn/issues/398
				We change the coveo_website rootPath to an item that does not exist.
			-->
			<site name="coveo_website">
				<patch:attribute name="rootPath" value="/coveo/for/sitecore/module" />
			</site>
		</sites>
	</sitecore>
</configuration>

After adding this configuration patch, Horizon started to load our sites with Coveo_website site. 

Sitecore Slack Chat: https://sitecorechat.slack.com/archives/C0CF16R9C/p1626812378104900

20 July, 2021

Coveo for Sitecore Upgrade - HtmlContentInBodyWithRequestsProcessor to FetchPageContentProcessor - Length cannot be less than zero

After upgrading from Coveo for Sitecore 4 to version 5, it is noted that there is a new processor which replaces the old HtmlContentInBodyWithRequestsProcessor. New processor in Coveo 5 is called as FetchPageContentProcessor. Similar to the previous processor, it executes an HTTP request, get the page response and then sends the data to the Coveo cloud. Enabling this processor delays the indexing. 

In Coveo 4 for Sitecore

<configuration xmlns:x="http://www.sitecore.net/xmlconfig/" 
  xmlns:patch="http://www.sitecore.net/xmlconfig/" xmlns:coveo="http://www.sitecore.net/xmlconfig/coveo/">
  <sitecore coveo:require="!disabled">
    <pipelines>
      <coveoPostItemProcessingPipeline>
        <processor type="Coveo.SearchProvider.Processors.HtmlContentInBodyWithRequestsProcessor, Coveo.SearchProviderBase">
          <StartCommentText>BEGIN NOINDEX</StartCommentText>
          <EndCommentText>END NOINDEX</EndCommentText>
        </processor>
      </coveoPostItemProcessingPipeline>
    </pipelines>
  </sitecore>
</configuration>

In Coveo 5 for Sitecore (recommended by Coveo)

<coveoPostItemProcessingPipeline>
  <processor type="Coveo.SearchProvider.Processors.ExecuteGetBinaryDataPipeline, Coveo.SearchProviderBase" />
</coveoPostItemProcessingPipeline>
<coveoGetBinaryData>
  <processor type="Coveo.SearchProvider.Processors.FetchPageContentProcessor, Coveo.SearchProviderBase">
    <inboundFilter hint="list:AddInboundFilter">
      <itemsWithLayout type="Coveo.SearchProvider.Processors.FetchPageContent.Filters.ItemsWithLayout, Coveo.SearchProviderBase" />
    </inboundFilter>
    <preAuthentication hint="list:AddPreAuthenticator" />
    <postProcessing hint="list:AddPostProcessing">
      <processor type="Coveo.SearchProvider.Processors.FetchPageContent.PostProcessing.CleanHtml, Coveo.SearchProviderBase">
        <startComment>BEGIN NOINDEX</startComment>
        <endComment>END NOINDEX</endComment>
      </processor>
    </postProcessing>
  </processor>
</coveoGetBinaryData>

In Coveo 5, there is a post processing processor called CleanHtml which will be executed on the fetched content after the HTTP request. This processor helps you to guide Coveo to index only a certain section of your web page. 

For an example, if you want to remove header, footer, navigation from the index document, you can mark the section using Start Comment and End Comment. In this configuration, it will be <!-- BEGIN NOINDEX --> and <!-- END NOINDEX -->

In a Sitecore instance with Coveo 4, we had nested comments as below. Coveo indexing with the HtmlContentInBodyWithRequestsProcessor processor were able to handle the nested comments and remove the section and send the HTML content to Coveo. 

<!-- BEGIN NOINDEX -->
    <!-- BEGIN NOINDEX -->
    	Content 1
    <!-- END NOINDEX -->
    <!-- BEGIN NOINDEX -->
    	Content 2
    <!-- END NOINDEX -->
<!-- END NOINDEX -->

In Coveo 5, the new processor CleanHtml throws below exception if there is a nested comments. I have even decompiled both the processor and tested the output of the HTML with nested comments and CleanHtml processor throws exception while removing content. 

ManagedPoolThread #19 02:19:52 ERROR An error occurred while trying to clean the HTML, no cleaning will be done.
Exception: System.ArgumentOutOfRangeException
Message: Length cannot be less than zero.
Parameter name: length
Source: mscorlib
   at System.String.Substring(Int32 startIndex, Int32 length)
   at Coveo.SearchProvider.Utils.HtmlCleaner.CleanHtmlContent(String p_HtmlContent, String p_StartCommentText, String p_EndCommentText)
   at Coveo.SearchProvider.Processors.HtmlContentInBodyWithRequestsProcessor.CollectHttpWebResponsesForAllClickableUris(List`1 p_CoveoIndexableItems, Dictionary`2 p_CleanedBinaryDataByUri)

We do not see a way to prevent this error when having a nested comments. When overriding the CleanHtml processor with the old processor method which cleans the HTML, it works but I do not think it is a good way to use the old code and patch it with the new processor. Raised a Coveo ticket to see if there is any workaround. 

Update: As per Coveo, there is no workaround in the Sitecore side, nested tags will need to be removed. 

  1. Jeff (@jflh) suggested that we could use a custom processor which cleans the HTML based on CSS selector instead of CleanHtml processor.
  2. He also provided a suggestion to use a Chrome extension which can help you to decide the Html elements to clean by the processor.Very useful. 
  3. Coveo has an Indexing Pipeline Extension (IPE) which works in the same fashion as mentioned in the first point (custom processor). This requires us to remove all BEGIN and END NOINDEX tags. We need to add a custom class coveo-no-index and IPE will select those sections and remove it. 

Sitecore Slack Chathttps://sitecorechat.slack.com/archives/C0CF16R9C/p1626979876116500

Reference

  1. Index Page Content With the FetchPageContentProcessor
  2. Nested NOINDEX tags preventing HTML content from being fetched during indexing

16 July, 2021

Coveo for Sitecore Upgrade - Sitecore Item Fields missing in Index Documents

When we recently upgraded Coveo for Sitecore to version 5, we noticed certain fields used in the result template are no longer available in Coveo Index Documents. Due to that, we had different and incorrect search results in the webpage. 

It has been decided by Coveo in October 2018 that they no longer index certain Sitecore fields to Coveo Cloud to reduce the payload. Some of these fields are standard fields to run a query. Some of the important fields which we were missing in index: templateid, templatename, displayname, name, title.

Two solutions provided by Coveo:

  1. Referencing an alternate field instead
  2. Adding the legacy metadata into the documents programmatically

Coveo provided an article with a pipeline to index those (legacy) fields. With the addition of the pipeline, the payload of each document will be much higher and it can slow down your indexing time. Make sure to review the required fields and add only those fields in the indexes. 

You can refer the Coveo documentation to get hold the pipeline code and customize it based on your requirement. 

Reference: https://docs.coveo.com/en/2699/coveo-for-sitecore-v4/field-is-no-longer-indexed-in-the-sitecore-items

15 July, 2021

Coveo for Sitecore 5 - Steps to follow after restoring Sitecore master database

There are instances when we get the backup of Production database and restore it in development environment to troubleshoot peculiar issues or to update the latest content. In those cases, if we have Coveo for Sitecore, after we restore the Sitecore master database, the linking between your Sitecore instance and Coveo cloud breaks, decryption key become invalid and you will need to link it again. 

After you restore the master database, you may be getting following errors in the Sitecore log. 

13528 13:34:32 WARN  An error occurred while decrypting the SitecorePassword configuration element value. The element value will be used as is assuming it is unencrypted. You can encrypt this password by using the Coveo Command Center at /coveo/command-center/index.html#configuration.
Exception: System.Security.Cryptography.CryptographicException
Message: Padding is invalid and cannot be removed.
Source: mscorlib
   at System.Security.Cryptography.RijndaelManagedTransform.DecryptData(Byte[] inputBuffer, Int32 inputOffset, Int32 inputCount, Byte[]& outputBuffer, Int32 outputOffset, PaddingMode paddingMode, Boolean fLast)
   at System.Security.Cryptography.RijndaelManagedTransform.TransformFinalBlock(Byte[] inputBuffer, Int32 inputOffset, Int32 inputCount)
   at System.Security.Cryptography.CryptoStream.FlushFinalBlock()
   at System.Security.Cryptography.CryptoStream.Dispose(Boolean disposing)
   at System.IO.Stream.Close()
   at Coveo.Framework.Encryption.RijndaelEncrypter.DecryptData(Byte[] p_DataToDecrypt)
   at Coveo.Framework.Security.ConfigurationValueEncrypter.Decrypt(String p_EncryptedValue)
   at Coveo.Framework.Security.ConfigurationValueEncrypter.SafeDecrypt(String p_EncryptedValue, String p_WarnMessage)

13528 13:34:32 WARN  An error occurred while decrypting the SitecorePassword configuration element value. The element value will be used as is assuming it is unencrypted. You can encrypt this password by using the Coveo Command Center at /coveo/command-center/index.html#configuration.
Exception: System.Security.Cryptography.CryptographicException
Message: Padding is invalid and cannot be removed.
Source: mscorlib
   at System.Security.Cryptography.RijndaelManagedTransform.DecryptData(Byte[] inputBuffer, Int32 inputOffset, Int32 inputCount, Byte[]& outputBuffer, Int32 outputOffset, PaddingMode paddingMode, Boolean fLast)
   at System.Security.Cryptography.RijndaelManagedTransform.TransformFinalBlock(Byte[] inputBuffer, Int32 inputOffset, Int32 inputCount)
   at System.Security.Cryptography.CryptoStream.FlushFinalBlock()
   at System.Security.Cryptography.CryptoStream.Dispose(Boolean disposing)
   at System.IO.Stream.Close()
   at Coveo.Framework.Encryption.RijndaelEncrypter.DecryptData(Byte[] p_DataToDecrypt)
   at Coveo.Framework.Security.ConfigurationValueEncrypter.Decrypt(String p_EncryptedValue)
   at Coveo.Framework.Security.ConfigurationValueEncrypter.SafeDecrypt(String p_EncryptedValue, String p_WarnMessage)


ManagedPoolThread #1 13:35:34 ERROR An error while Initializing occurred
Exception: Coveo.Framework.Exceptions.CoveoIndexConfigurationException
Message: There was an error in the Coveo Index Configuration.: Either the SitecoreUsername or SitecorePassword configuration value is invalid. Please enter valid credentials.
Source: Coveo.Framework
   at Coveo.Framework.Configuration.CoveoIndexConfiguration.ValidateSitecoreCredentials()
   at Coveo.Framework.Configuration.CoveoIndexConfiguration.Validate()
   at Coveo.SearchProvider.ProviderIndexBase.Initialize()


ManagedPoolThread #1 13:35:34 WARN  Precondition failed: The parameter 'p_Communication' must not be null
ManagedPoolThread #1 13:35:34 ERROR Exception
Exception: System.Reflection.TargetInvocationException
Message: Exception has been thrown by the target of an invocation.
Source: mscorlib
   at System.RuntimeMethodHandle.InvokeMethod(Object target, Object[] arguments, Signature sig, Boolean constructor)
   at System.Reflection.RuntimeMethodInfo.UnsafeInvokeInternal(Object obj, Object[] parameters, Object[] arguments)
   at System.Reflection.RuntimeMethodInfo.Invoke(Object obj, BindingFlags invokeAttr, Binder binder, Object[] parameters, CultureInfo culture)
   at Sitecore.Reflection.ReflectionUtil.InvokeMethod(MethodInfo method, Object[] parameters, Object obj)
   at Sitecore.Jobs.JobRunner.RunMethod(JobArgs args)
   at (Object , Object )
   at Sitecore.Pipelines.CorePipeline.Run(PipelineArgs args)
   at Sitecore.Pipelines.DefaultCorePipelineManager.Run(String pipelineName, PipelineArgs args, String pipelineDomain, Boolean failIfNotExists)
   at Sitecore.Pipelines.DefaultCorePipelineManager.Run(String pipelineName, PipelineArgs args, String pipelineDomain)
   at Sitecore.Jobs.DefaultJob.DoExecute()
   at Sitecore.Abstractions.BaseJob.ThreadEntry(Object state)

Nested Exception

Exception: System.ArgumentNullException
Message: Precondition failed: The parameter 'p_Communication' must not be null
Parameter name: p_Communication
Source: Coveo.Framework
   at Coveo.Framework.CNL.Precondition.NotNull(Object p_Parameter, String p_ParameterName)
   at Coveo.SearchProvider.AbstractProviderUpdateContext..ctor(ISearchIndexSummary p_IndexSummary, IIndexCommunication p_Communication)
   at Coveo.SearchProvider.ProviderUpdateContext..ctor(ISearchIndex p_Index, IIndexCommunication p_Communication)
   at Coveo.SearchProvider.ProviderIndex.CreateUpdateContext()
   at Coveo.SearchProvider.ProviderIndex.Update(IEnumerable`1 p_IndexableInfo)

In order to link the connection, you will need to use Coveo Configuration page and follow the steps.

  1. Go to Sitecore Control Panel -> Coveo Search --> Configuration Manager
  2. Click Login. You will be redirected to Coveo Cloud login. After login, you may need to grant access by clicking Authorize button.


  3. Make sure proper Organization is selected and click Apply and Restart.


  4. Once the Sitecore instance restarts, there is a high chance that user account configured to connect to Sitecore may be locked. You can go to Sitecore User Manager, select that user and unlock it. 
  5. Once it is unlocked or if it is not locked, then you can go Coveo Search --> Configuration Manager --> Security --> Enter the password for the configured user and click Apply and Restart. If there is a success message, then it is working fine and Sitecore instance will restart. And you will be able to see all the indexes and fields in the Coveo Admin page. 

14 July, 2021

Sitecore Upgrade - Redis Session State crashes IIS App Pool

After upgrading Sitecore 9.0.1 to 10.0.1, I enabled the Redis Cache Session State for Content Delivery environment. App pool started to crash whenever the session ends. As part of SessionEnd, all the analytics session data will be pushed to xDB. At that time, there is an exception at the background thread and crashes the app pool. After multiple crashes, app pool stopped as well. 

ERROR Unhandled exception detected. The ASP.NET worker process will be terminated. 
Exception: System.Reflection.TargetInvocationException
Message: Exception has been thrown by the target of an invocation.
Source: mscorlib
   at System.RuntimeMethodHandle.SerializationInvoke(IRuntimeMethodInfo method, Object target, SerializationInfo info, StreamingContext& context)
   at System.Runtime.Serialization.ObjectManager.CompleteISerializableObject(Object obj, SerializationInfo info, StreamingContext context)
   at System.Runtime.Serialization.ObjectManager.FixupSpecialObject(ObjectHolder holder)
   at System.Runtime.Serialization.ObjectManager.DoFixups()
   at System.Runtime.Serialization.Formatters.Binary.ObjectReader.Deserialize(HeaderHandler handler, __BinaryParser serParser, Boolean fCheck, Boolean isCrossAppDomain, IMethodCallMessage methodCallMessage)
   at System.Runtime.Serialization.Formatters.Binary.BinaryFormatter.Deserialize(Stream serializationStream, HeaderHandler handler, Boolean fCheck, Boolean isCrossAppDomain, IMethodCallMessage methodCallMessage)
   at Sitecore.SessionProvider.Redis.BinarySerializer.Deserialize(Byte[] dataAsBytes)
   at Sitecore.SessionProvider.Redis.ChangeTrackingSessionStateItemCollection.GetData(String normalizedName)
   at System.Web.Util.AspCompatApplicationStep.AnyStaObjectsInSessionState(HttpSessionState session)
   at System.Web.HttpApplicationFactory.FireSessionOnEnd(HttpSessionState session, Object eventSource, EventArgs eventArgs)
   at System.Web.SessionState.SessionOnEndTargetWorkItem.RaiseOnEndCallback()
   at System.Web.Util.WorkItem.CallCallbackWithAssert(WorkItemCallback callback)
   at System.Web.Util.WorkItem.OnQueueUserWorkItemCompletion(Object state)
   at System.Threading.ExecutionContext.RunInternal(ExecutionContext executionContext, ContextCallback callback, Object state, Boolean preserveSyncCtx)
   at System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state, Boolean preserveSyncCtx)
   at System.Threading.QueueUserWorkItemCallback.System.Threading.IThreadPoolWorkItem.ExecuteWorkItem()
   at System.Threading.ThreadPoolWorkQueue.Dispatch()

Nested Exception

Exception: System.Runtime.Serialization.SerializationException
Message: Member 'f' was not found.
Source: mscorlib
   at System.Runtime.Serialization.SerializationInfo.GetElement(String name, Type& foundType)
   at System.Runtime.Serialization.SerializationInfo.GetByte(String name)
   at Sitecore.Analytics.Tracking.SessionSettings..ctor(SerializationInfo info, StreamingContext context)

One of the key part of the upgrade documentation is the post deployment steps. In that it is mentioned that we should clear the session state. Also it means that we should not use the same Redis Cache server for both the Sitecore version as it can cause conflict. We were trying to use the same Redis Cache server and once we brought in a new Redis Cache server dedicated for the new Sitecore environment, everything started to work without any crashes. Or You should clear the session data on an existing Redis Cache server and use it only for the new Sitecore version.

  • Clear the Session data before loading the upgraded site or
  • Configure a new dedicated Redis Cache Server

25 June, 2021

Sitecore Upgrade - Transferred Sitecore Membership User Password does not work - Possible Reasons

In case if you have migrated the password using Sitecore utility as mentioned here, your migrated password may not work. Some of the steps which are mentioned below may help. 

  1. Check whether the script ran successfully on the current server
    You may need to confirm whether the source and destination server used to migrate the password is correct and also whether the destination server has all the users and password. 

  2. Check the Membership Password hashAlgorithmType
    You may need to check whether the hashAlgorithmType is same in the source and the destination Sitecore instance. You can view it in the web.config file in <membership> node. Attribute name is hashAlgorithmType. For the migrated password to work, destination hashAlgorithmType should be same as source hashAlgorithmType. 

  3. Migration to Sitecore 9.2 with Identity Provider
    In case if the destination server is going to use Siteore Identity Provider to authenticate the Sitecore membership users, then you will need to set the same hashAlgorithmType in Identity server configuration. 

    In the Sitecore ID server, go to \sitecore\Sitecore.Plugin.IdentityServer\Config\identityServer.xml file and set the same hashAlgorithmType which is used in source server in <PasswordHashAlgorithm> tag. Do not forget to recycle the Identity server application pool after this change. 


Sitecore Upgrade - Transfer Sitecore Membership User/Roles and Password

As part of the Sitecore upgrade, if you have a requirement to transfer Sitecore Membership roles, users and their password, then you can follow this process. 

In case if you have upgraded the core database from one Sitecore version to another, you may not be able to use the same password unless you reset it. 

Transfer Users and Roles using Sitecore Package
To transfer the users and roles, you can use Sitecore Package or by serializing user accounts on one instance and de-serializing them on another instance or easiest way is it to use Sitecore PowerShell Extension. Below piece of script will get all the users and roles from the current version and provide a package.

Once the package is ready, you can use Sitecore Package Installer to install and click Continue Always if there is an user or role already available in the destination server, you can click Continue Always to proceed. 

Transfer Password to new environment
Bringing the users and roles to the new environment will not bring the password. It may be necessary to manually reset user passwords after the transfer. Sitecore provided a simple utility to transfer the passwords to the destination server. Sitecore KB

You have to place the admin web page, provide the source and destination core or security db connection string. You will get an option to select the users to migrate their passwords. In the Sitecore provided file, you have to select one user at a time to move from left to right. 

I modified to add a button to transfer all the available user's passwords in a single click of a button (Transfer All). You can get the updated file here in GitHub

Note: In order for the tool to list the users, you need to make sure the users are already transferred to the destination Sitecore instance. 



11 June, 2021

Sitecore - Post condition failed exception

After upgrading from Sitecore 9.0.1 to Sitecore 10.0.1, there was an error reported in CD environment. "Post condition failed"

This is due to the reference of Core Database in CD server. From Sitecore 9.2, Sitecore has removed the reference of core database in Content Delivery and introduced Security database connection string which can store the aspnet membership tables. 

In my case, I had a reference of Core database in a custom configuration which modified the prefetch cacheSize. Once I removed it, site loaded fine. 
<configuration xmlns:patch="http://www.sitecore.net/xmlconfig/">
  <sitecore>
    <databases>
      <database id="core">
        <dataProviders hint="list:AddDataProvider">
          <dataProvider param1="$(id)">
            <prefetch>
              <cacheSize>200MB</cacheSize>
            </prefetch>
          </dataProvider>
        </dataProviders>
      </database>      
  </sitecore>
</configuration>

Exception:
Event code: 3005 
Event message: An unhandled exception has occurred. 
Event time: 6/11/2021 10:32:01 AM 
Event time (UTC): 6/11/2021 3:32:01 PM 
Event ID: 8bb3c123c9f64b1dbf6201fddf35994b 
Event sequence: 2 
Event occurrence: 1 
Event detail code: 0 
 
Application information: 
    Application domain: /LM/W3SVC/2/ROOT-3-132678991184217924 
    Trust level: Full 
    Application Virtual Path: / 
    Application Path: C:\inetpub\wwwroot\cd\ 
    Machine name: CD 
 
Process information: 
    Process ID: 11156 
    Process name: w3wp.exe 
    Account name: IIS APPPOOL\cd 
 
Exception information: 
    Exception type: InvalidOperationException 
    Exception message: Post condition failed
   at Sitecore.Diagnostics.Assert.ResultNotNull[T](T result, String message)
   at Sitecore.Configuration.DefaultFactory.GetDatabase(String name)
   at Sitecore.Configuration.DefaultFactory.GetDatabases()
   at Sitecore.Data.Managers.DefaultItemManager.Initialize()
   at (Object , Object )
   at Sitecore.Pipelines.CorePipeline.Run(PipelineArgs args)
   at (Object , Object )
   at Sitecore.Pipelines.CorePipeline.Run(PipelineArgs args)
   at Sitecore.Pipelines.DefaultCorePipelineManager.Run(String pipelineName, PipelineArgs args, String pipelineDomain, Boolean failIfNotExists)
   at Sitecore.Pipelines.DefaultCorePipelineManager.Run(String pipelineName, PipelineArgs args, String pipelineDomain)
   at Sitecore.Nexus.Web.HttpModule.Application_Start()
   at Sitecore.Nexus.Web.HttpModule.Init(HttpApplication app)
   at System.Web.HttpApplication.RegisterEventSubscriptionsWithIIS(IntPtr appContext, HttpContext context, MethodInfo[] handlers)
   at System.Web.HttpApplication.InitSpecial(HttpApplicationState state, MethodInfo[] handlers, IntPtr appContext, HttpContext context)
   at System.Web.HttpApplicationFactory.GetSpecialApplicationInstance(IntPtr appContext, HttpContext context)
   at System.Web.Hosting.PipelineRuntime.InitializeApplication(IntPtr appContext)

 
 
Request information: 
    Request URL: https://cd:443/
    Request path: /
    User host address: 192.168.1.137 
    User:  
    Is authenticated: False 
    Authentication Type:  
    Thread account name: IIS APPPOOL\cd 
 
Thread information: 
    Thread ID: 31 
    Thread account name: IIS APPPOOL\cd 
    Is impersonating: False 
    Stack trace:    at Sitecore.Diagnostics.Assert.ResultNotNull[T](T result, String message)
   at Sitecore.Configuration.DefaultFactory.GetDatabase(String name)
   at Sitecore.Configuration.DefaultFactory.GetDatabases()
   at Sitecore.Data.Managers.DefaultItemManager.Initialize()
   at (Object , Object )
   at Sitecore.Pipelines.CorePipeline.Run(PipelineArgs args)
   at (Object , Object )
   at Sitecore.Pipelines.CorePipeline.Run(PipelineArgs args)
   at Sitecore.Pipelines.DefaultCorePipelineManager.Run(String pipelineName, PipelineArgs args, String pipelineDomain, Boolean failIfNotExists)
   at Sitecore.Pipelines.DefaultCorePipelineManager.Run(String pipelineName, PipelineArgs args, String pipelineDomain)
   at Sitecore.Nexus.Web.HttpModule.Application_Start()
   at Sitecore.Nexus.Web.HttpModule.Init(HttpApplication app)
   at System.Web.HttpApplication.RegisterEventSubscriptionsWithIIS(IntPtr appContext, HttpContext context, MethodInfo[] handlers)
   at System.Web.HttpApplication.InitSpecial(HttpApplicationState state, MethodInfo[] handlers, IntPtr appContext, HttpContext context)
   at System.Web.HttpApplicationFactory.GetSpecialApplicationInstance(IntPtr appContext, HttpContext context)
   at System.Web.Hosting.PipelineRuntime.InitializeApplication(IntPtr appContext)
 

28 May, 2021

Sitecore Upgrade - Codebase Upgrade Series - Remove unused references and nuget packages

As part of the Sitecore codebase upgrade series, in this blog, we are going to deal with unused references and packages. It is worth to cleanup the unused references and nuget packages to reduce the complexity, dependencies and effort to upgrade to be compatible with target Sitecore version. 

Remove Unused References:

Visual Studio 2019 has the inbuilt "Remove Unused References" option for .NET framework projects with .NET SDK project format and .NET core projects. If the project type is already in .NET SDK format, then right click on project or on the references and select Remove Unused References. Then select the list of references to remove and click Apply. As of Visual Studio 2019 v16.10.0, this option is turned off by default. To enable it, you can go to menu Tools > Options > Text Editor > C# > Advanced. Select the Remove Unused References command in Solution Explorer. 




Remove Unused Packages

To remove Unused packages, you may want to get ReSharper which can clean up the unused references and also remove the unused packages. If ReSharper is installed, you can refactor the entire solution or a particular project. Right Click on the Solution > Refactor > Remove Unused References. This opens up a window with the list of references and packages and you can select and remove it. This will remove the entry from Packages.config or from PackageReferences if it is used. 

There are other open source extensions in Visual Studio Gallery and few proprietary tools like CoreRush or Telerik® JustCode™ are available in the market which must be doing the same tasks. 



blockquote { margin: 0; } blockquote p { padding: 15px; background: #eee; border-radius: 5px; } blockquote p::before { content: '\201C'; } blockquote p::after { content: '\201D'; }