Showing posts with label Configuration. Show all posts
Showing posts with label Configuration. Show all posts

November 25, 2016

Tips about the fallback in Sitecore

Tip number 1

Problem: The fallback is disabled by default in the item:save events.
Tip: You can use the following code:
using(new FallbackDisabler(FallbackStates.Default))
{  
   db.GetItem()
   ...
}

Tip number 2

Problem: The fallback is not active in the scheduled tasks.
Tip: In the FieldFallback_00.config, you need to add the site 'scheduler' in the 'sites' tag.

Tip number 3

Problem: When you use the Link database to retrieve an item, you have a ItemLink (the link between the 2 objects). If you do a .GetSourceItem() on this ItemLink, the fallback is not applied.
Tip: You need to retrieve this item with the standard method: db.GetItem(itemId).

Tip number 4

Problem: The ancestor fallback didn't work in the other languages.
Tip: To enable the ancestor fallback on a field, you need to select the template's field and check the EnableAncestorFallback checkbox. But, be careful because this checkbox is not shared. So two solutions here: check it in every languages or change this checkbox to shared.

June 23, 2014

Setup a continuous integration for Sitecore with TDS - TFS 2013 and Octopus - Step 4: Align the version number

This is a last post of a series of post about the setup of a continuous integration for Sitecore. If you have miss the 4 previous post, here is the links:
  1. Setup a continuous integration for Sitecore with TDS - TFS 2013 and Octopus - Architecture
  2. Setup a continuous integration for Sitecore with TDS - TFS 2013 and Octopus - Step 1: Customize the TFS workflow
  3. Setup a continuous integration for Sitecore with TDS - TFS 2013 and Octopus - Step 2: Configure TFS
  4. Setup a continuous integration for Sitecore with TDS - TFS 2013 and Octopus - Step 3: Configure octopus
If you stop here you workflow should be ok but, I had another requirement: the version number. I would like to have the same reference number into every steps of my process:
  • The build number who appear in TFS
  • The assemblies files
  • The nuget packages
  • The octopus versions
To do that:
  1. Change the "Build number format" into the section "5. Advanced" of the TFS build definition by: $(BuildDefinitionName)_$(Date:1MMdd)$(Rev:.r). Be carefull with this number format. You can see that I have use 1MMdd and not YYYYMMdd as it is by default. The reason of that is that the Assembly number must between 0 and 65535. So you have a solution for the next 6 year by changing the 1 by another number :)
  2. Add a file .ps1 anywhere in the sources with the following content and do a check-in in TFS
  3. In the section 2.5 "Pre-build script path", refer to the powershell file you have created.
  4. In the section 2.5 "Pre-build script arguments", put the following value "-assemblyVersion 1.1.J.B -fileAssemblyVersion 1.1.J.B -nugetVersion 1.1.J.B". This mean that the powersell will replace the .J.B of the different files (assembly, assemblyVersion, nuget) by the same numbers as the build number of TFS
Here is the content of the powershell script to rename the versions:
param
(
 [string]$assemblyVersion,
 [string]$fileAssemblyVersion,
 [string]$nugetVersion
)

#Update the AssemblyInfo.cs and the .nuspec file to replace the X.X.J.B version number by the correct one depending of the number from TFS
function Update-SourceVersion
{
  param
  (
    [string]$SrcPath,
    [string]$assemblyVersion, 
    [string]$fileAssemblyVersion,
 [string]$nugetVersion
  ) 
  
    $buildNumber = $env:TF_BUILD_BUILDNUMBER
 Write-Host "env:TF_BUILD_BUILDNUMBER: $buildNumber"
 
    if ($buildNumber -eq $null)
    {
        $buildIncrementalNumber = 0
    }
    else
    {
        $splitted = $buildNumber.Split('.')
        $buildIncrementalNumber = $splitted[$splitted.Length - 1]
    }
    
    if ($fileAssemblyVersion -eq "")
    {
        $fileAssemblyVersion = $assemblyVersion
    }
     
    Write-Host "Executing Update-SourceVersion in path $SrcPath, Version is $assemblyVersion and File Version is $fileAssemblyVersion"
    
    
    $AllVersionFiles = Get-ChildItem $SrcPath AssemblyInfo.cs -recurse
    
     
    $jdate = Get-Date -format 1MMdd
 
 Write-Host "Infos: jdate: $jdate, buildIncrementalNumber: $buildIncrementalNumber"
 
    $assemblyVersion = $assemblyVersion.Replace("J", $jdate).Replace("B", $buildIncrementalNumber)
    $fileAssemblyVersion = $fileAssemblyVersion.Replace("J", $jdate).Replace("B", $buildIncrementalNumber)
     
    Write-Host "Transformed Assembly Version is $assemblyVersion and Transformed File Version is $fileAssemblyVersion"
        
    foreach ($file in $AllVersionFiles) 
    { 
        Write-Host "Modifying file " + $file.FullName
        #save the file for restore
        $backFile = $file.FullName + "._ORI"
        $tempFile = $file.FullName + ".tmp"
        Copy-Item $file.FullName $backFile
        #now load all content of the original file and rewrite modified to the same file
        Get-Content $file.FullName |
        %{$_ -replace 'AssemblyVersion\("[0-9]+(\.([0-9]+|\*)){1,3}"\)', "AssemblyVersion(""$assemblyVersion"")" } |
        %{$_ -replace 'AssemblyFileVersion\("[0-9]+(\.([0-9]+|\*)){1,3}"\)', "AssemblyFileVersion(""$fileAssemblyVersion"")" }  > $tempFile
        Move-Item $tempFile $file.FullName -force
    }
 
 $nugetVersion = $nugetVersion.Replace("J", $jdate).Replace("B", $buildIncrementalNumber)
    Write-Host "Transformed Nuspec Version is $nugetVersion"
  
    $AllNugetFiles = Get-ChildItem $SrcPath *.nuspec -recurse
 $replaceExp = '<file src="$1" target="$2.' + $nugetVersion + '.update" />'
 
 foreach ($file in $AllNugetFiles) 
    { 
        Write-Host "Modifying file " + $file.FullName
        #save the file for restore
        $backFile = $file.FullName + "._ORI"
        $tempFile = $file.FullName + ".tmp"
        Copy-Item $file.FullName $backFile
        #now load all content of the original file and rewrite modified to the same file
        Get-Content $file.FullName |
        %{$_ -replace '<version>[0-9]+(\.([0-9]+|\*)){1,3}</version>', "<version>$nugetVersion</version>" }  |
  %{$_ -replace '<file src="(.*NationalLottery\.DeHub\..*)" target="(.*)\.[0-9]+(\.([0-9]+|\*|J|B)){1,3}.update" />', $replaceExp } > $tempFile
        Move-Item $tempFile $file.FullName -force
    } 
}

Write-Host "Running Pre Build Scripts"
 
$scriptRoot = Split-Path -Parent -Path $MyInvocation.MyCommand.Definition

$scriptRootBuildFunctions = "$scriptRoot\TFSUtils.psm1"
Write-Host "Script root: $scriptRootBuildFunctions"

if ($assemblyVersion -eq "")
{
 $assemblyVersion = "1.1.0.0"
 $fileAssemblyVersion = "1.1.J.B"
}

$srcPath = "$scriptRoot/../"

Update-SourceVersion $srcPath $assemblyVersion $fileAssemblyVersion $nugetVersion

If you use the script to create and deploy the Octopus packages as describe in the previous post, don't forget to complete the "Post-build script arguments" to "-releaseVersion 1.1.J.B" to use the same versioning system and having this number as release number in Octopus.

Ok that is it now. I hope that this tutorial will help you in your deployment process.
You also have a video presented to the Sitecore Virtual User Group who could be useful for it here: http://sitecoreug.org/events/January

Here is the index of all the post who compose this tutorial:
  1. Setup a continuous integration for Sitecore with TDS - TFS 2013 and Octopus - Architecture
  2. Setup a continuous integration for Sitecore with TDS - TFS 2013 and Octopus - Step 1: Customize the TFS workflow
  3. Setup a continuous integration for Sitecore with TDS - TFS 2013 and Octopus - Step 2: Configure TFS
  4. Setup a continuous integration for Sitecore with TDS - TFS 2013 and Octopus - Step 3: Configure octopus
  5. Setup a continuous integration for Sitecore with TDS - TFS 2013 and Octopus - Step 4: Align the version number

Setup a continuous integration for Sitecore with TDS - TFS 2013 and Octopus - Step 3: Configure octopus

This is the 4th post of a series of post about the setup of a continuous integration for Sitecore.  If you have miss the 3 first post here are the links:
  1. Setup a continuous integration for Sitecore with TDS - TFS 2013 and Octopus - Architecture
  2. Setup a continuous integration for Sitecore with TDS - TFS 2013 and Octopus - Step 1: Customize the TFS workflow
  3. Setup a continuous integration for Sitecore with TDS - TFS 2013 and Octopus - Step 2: Configure TFS
Now our TFS is configured correctly, the package are created and published to the nuget server. We can focus on octopus now.

I will not explain how to setup you Octopus environments, machines, ... but only the deployment process I use. 

Here is the steps:
  1. Start Deployment: A notification email to the different member of the project with the details of the packages who will be deployed.
  2. Clean Environment: A cleanup of the files before the deployment to be sure that if we have remove a file it will not be on the project anymore. This is a powershell script with the following content: 
    #RootFolder : the root folder of the project (we should be able to get it from the environment)  
    $rootFolder = "E:\Websites\XXX\Website"   
    
    function Remove-Item-If-Exist
    {
     param
     (
      [string]$pathToDelete,
      [switch]$force,
      [switch]$recurse
     )
     
     If (Test-Path $pathToDelete){
      Remove-Item $pathToDelete -Force:$force -Recurse:$recurse
     }
    } 
     
    #Cleanup the old files  
    Remove-Item-If-Exist "$rootFolder\Design" -Force -Recurse  
    Remove-Item-If-Exist "$rootFolder\layouts\Layouts" -Force -Recurse  
    Remove-Item-If-Exist "$rootFolder\layouts\Sublayouts" -Force -Recurse  
    Remove-Item-If-Exist "$rootFolder\layouts\UserControls" -Force -Recurse  
    Remove-Item-If-Exist "$rootFolder\bin\PROJECTNAMESPACE.*.dll" -Force   
    Remove-Item-If-Exist "$rootFolder\App_Config\Include\PROJECTNAMESPACE.*.config" -Force
  3. Deploy Files: Deploy the NuGet package. You may have multiple time this step if you have multiple nuget package with the website files to deploy.
  4. Deploy TDS: Deploy the NuGet package with the TDS files in a temporary folder. 
  5. Install TDS Packages: This is a powershell to install and publish the TDS packages. I have create a custom tool for it who scan the files in a specific folder and install the packages. If you need to do this kind of program you should take a look at the code of TDS. Basically it install a webservice into your deploy folder, use this webservice to install your packages and then remove this webservice.
    A great alternative could be this tool: https://github.com/adoprog/Sitecore-Deployment-Helpers
To automate the creation of the release into octopus when the build is triggered into TFS we can add a post release script into TFS to create and deploy this release.

To do that:

  1. Add a new file with the extention .ps1 somewhere in your sources and chech-in this file in TFS
  2. Edit your TFS build definition 
  3. In the tab "Process", section 2.5
  4. In the section "Post-build script path", select your .ps1 file
  5. The parameter "Post-build script arguments" I have "-releaseVersion 1.1.J.B" please refer to the post "Step 4: Align the versions numbers" for this.
  6. The content of the powershell file is the following:
    param
    (
     [string]$releaseVersion
    )
     
    #Constants
    $octopusApiUrl = "http://10.0.2.50:8088/api"
    $octopusApiKey = "API-RX6UIIWTB2ZBUWU0TRIYXXXXXXX"
    
    $projectName = "XXX"
    $scriptRoot = Split-Path -Parent -Path $MyInvocation.MyCommand.Definition
     
    #Functions
    
    # Get the formated TFS version number. Replace the tokens J and B by the current build number
    function Format-TFS-Version
    {
     param
     (
      [string]$versionToFormat
     )
     
     $buildNumber = $env:TF_BUILD_BUILDNUMBER
     Write-Host "env:TF_BUILD_BUILDNUMBER: $buildNumber"
     
        if ($buildNumber -eq $null)
        {
            $buildIncrementalNumber = 0
        }
        else
        {
            $splitted = $buildNumber.Split('.')
            $buildIncrementalNumber = $splitted[$splitted.Length - 1]
        }
        
     $jdate = Get-Date -format 1MMdd
     
     return $versionToFormat.Replace("J", $jdate).Replace("B", $buildIncrementalNumber)
    }
    
    #Create a release in Octopus with the specifiic version of the nuget packages
    function Octopus-Create-Release
    {
     param
     (
      [string]$releaseVersion
     )
     
     $releaseVersion = Format-TFS-Version $releaseVersion
     
     $corePackage = "--package=XXX.Core:$releaseVersion"
     $corporatePackage = "--package=XXX.Corporate:$releaseVersion"
     $scriptPackage = "--package=XXX.Tools.SqlScriptsUpdate:$releaseVersion"
     
     Write-Host "Create the octopus release by calling $scriptRoot\Octopus\octo.exe create-release --project=$projectName --server=$octopusApiUrl --apiKey=$octopusApiKey --version $releaseVersion $corePackage $corporatePackage $scriptPackage"
     
     &$scriptRoot\Octopus\octo.exe create-release --project=$projectName --server=$octopusApiUrl --apiKey=$octopusApiKey --version $releaseVersion $corePackage $corporatePackage $scriptPackage  
    }
    
    #Deploy a release in Octopus
    function Octopus-Deploy-Release
    {
     param
     (
      [string]$releaseVersion,
      [string]$deployToEnvironment
     )
     
     Write-Host "Deploy the octopus release by calling $scriptRoot\Octopus\octo.exe  deploy-release --project=$projectName --version=$releaseVersion --deployto=$deployToEnvironment --server=$octopusApiUrl --apiKey=$octopusApiKey"
    
     &$scriptRoot\Octopus\octo.exe deploy-release --project=$projectName --version=$releaseVersion --deployto=$deployToEnvironment --server=$octopusApiUrl --apiKey=$octopusApiKey
    }
    
    
    
    #Main program
    $releaseVersion = Format-TFS-Version $releaseVersion
    Write-Host "Release version: $releaseVersion"
    
    Octopus-Create-Release $releaseVersion
    
    Octopus-Deploy-Release $releaseVersion Integration
    


Ok now your process should be completed. The last post is optional but allow you to have a single ID during the whole build process.

Next steps:
  1. Setup a continuous integration for Sitecore with TDS - TFS 2013 and Octopus - Architecture
  2. Setup a continuous integration for Sitecore with TDS - TFS 2013 and Octopus - Step 1: Customize the TFS workflow
  3. Setup a continuous integration for Sitecore with TDS - TFS 2013 and Octopus - Step 2: Configure TFS
  4. Setup a continuous integration for Sitecore with TDS - TFS 2013 and Octopus - Step 3: Configure octopus
  5. Setup a continuous integration for Sitecore with TDS - TFS 2013 and Octopus - Step 4: Align the version number

Setup a continuous integration for Sitecore with TDS - TFS 2013 and Octopus - Step 2: Configure TFS

This is the third post of a series of post about the setup of a continuous integration for Sitecore.  If you have miss the 2 first post here are the links:
  1. Setup a continuous integration for Sitecore with TDS - TFS 2013 and Octopus - Architecture
  2. Setup a continuous integration for Sitecore with TDS - TFS 2013 and Octopus - Step 1: Customize the TFS workflow
Now that we have our custom build template we are ready to configure it.

TDS settings:
  1. Set "TDS : Generate packages" to true to generate the TDS packages
  2. Set "TFS : IsDesktopBuild" to false because I don't want to deploy my TDS packages on the current server which is the build server. I need to embed those packages into nuget packages.
Octopus settings:
  1. Set "Octopack : API Key of the nuget server". You can find this setting into the app setting  "apiKey" of the nuget server
  2. Set "Octopack : Publish packaged to http" to the url of the nuget server it should look like : "http://YOUR_DOMAIN/api/v2/package/"
  3. Set "Octopack : Run OctoPack" to true. This will run the octopack command to generate and publish the nuget packages to the server.
Octopack:
To generate the octopus packages, I use the nuget package called OctoPack. It is really easy to use: you just need to install this nuget package in every web project. This will generate the package for you when you build with msbuild with the custom parameters we have set. 
I also create another project who will be used to deploy my TDS packages and install the Octopack package in this project. To do that, you can create an empty project (a console project in my case) and add a .nuspec file. The content of this nuspec should refer the different .update packages generated by TDS as in the following example:
<?xml version="1.0"?>
<package >
  <metadata>
    <id>XXX.TDSDeploy</id>
    <version>1.0.0</version>
    <authors>Vangansewinkel Benjamin</authors>
    <owners>Vangansewinkel Benjamin</owners>
    <projectUrl>http://www.xxx.be</projectUrl>
    <iconUrl>http://www.xxx.be/design/images/generalImages/favicon.ico</iconUrl>
    <requireLicenseAcceptance>false</requireLicenseAcceptance>
    <description>XXX TDS package</description>
    <releaseNotes></releaseNotes>
    <copyright>Copyright 2014</copyright>
  </metadata>

  <files>
    <file src="..\..\..\..\..\bin\_Packages\XXX.CoreTDS\XXX.CoreTDS.scitems.update" target="content\TdsPackages\XXX.CoreTDS.1.1.J.B.update" />
    <file src="..\..\..\..\..\bin\_Packages\XXX.LayoutsTDS\XXX.LayoutsTDS.scitems.update" target="content\TdsPackages\XXX.LayoutsTDS.1.1.J.B.update" />
    <file src="..\..\..\..\..\bin\_Packages\XXX.TemplatesTDS\XXX.TemplatesTDS.scitems.update" target="content\TdsPackages\XXX.TemplatesTDS.1.1.J.B.update" />
  </files>
</package>

Setup a continuous integration for Sitecore with TDS - TFS 2013 and Octopus - Step 1: Customize the TFS workflow

This is the second part of my series of post about how to Setup a continuous integration for Sitecore with TDS - TFS 2013 and Octopus. If you didn't have read the post about the general architecture you should read it first.

The first step of this tutorial is: "How to customize the Team Foundation Server (TFS) 2013 workflow".
  1. Into the TFS "build" tab of visual studio, right click on the build definition to customize and click on "Edit Build Definition"
  2. In the "Process" tab, click on show details and then download you current template. This will download a xaml file. Save it somewhere in your project and rename the file.
  3. Now you can open this file in visual studio or in a notepad++ for example. I will use both tools depending of what I need to change. Sometime it is easier to edit in notepad, sometime in visual studio.
  4. First thing to do is adding a section with the octopus parameter and another one with the TDS options as in this screenshot:
  5. To do that, wa need to create 5 new variables by adding the following code before </x:Members>
    <x:Property Name="Octopus_OctoPackPublishApiKey" Type="InArgument(x:String)" />
    <x:Property Name="Octopus_OctoPackPublishPackageToHttp" Type="InArgument(x:String)" />
    <x:Property Name="Octopus_RunOctoPack" Type="InArgument(x:Boolean)" />
    <x:Property Name="TDS_IsDesktopBuild" Type="InArgument(x:Boolean)" />
    <x:Property Name="TDS_GeneratePackage" Type="InArgument(x:Boolean)" />
  6. Add those parameter into the UI by adding the following code before </mtbw:ProcessParameterMetadataCollection>
    <mtbw:ProcessParameterMetadata BrowsableWhen="Always" Category="#500 Octopus" Description="Set this to true to run OctoPack" DisplayName="OctoPack : Run OctoPack" ParameterName="Octopus_RunOctoPack" />
    <mtbw:ProcessParameterMetadata BrowsableWhen="Always" Category="#500 Octopus" Description="Url where the octopack need to be published" DisplayName="OctoPack : Publish package to http" ParameterName="Octopus_OctoPackPublishPackageToHttp" />
    <mtbw:ProcessParameterMetadata BrowsableWhen="Always" Category="#500 Octopus" Description="API Key of the nuget server for the publish" DisplayName="OctoPack : API Key of the nuget server" ParameterName="Octopus_OctoPackPublishApiKey" />
    <mtbw:ProcessParameterMetadata BrowsableWhen="Always" Category="#600 TDS" Description="Set this to true if it is a desktop build" DisplayName="TDS : IsDesktopBuild" ParameterName="TDS_IsDesktopBuild" />
    <mtbw:ProcessParameterMetadata BrowsableWhen="Always" Category="#600 TDS" Description="Set this to true to generate the packages" DisplayName="TDS : Generate packages" ParameterName="TDS_GeneratePackage" />
  7. Switch to Visual Studio because it will be easier to edit. Click on the "Run MsBuild" and the in the properties window, replace the "CommandLineArgument" by the following one:
    String.Format("/p:SkipInvalidConfigurations=true {0} /p:OctoPackPublishPackageToHttp={1} /p:OctoPackPublishApiKey={2} /p:RunOctoPack={3} /p:IsDesktopBuild={4} /p:GeneratePackage={5}", AdvancedBuildSettings.GetValue(Of String)("MSBuildArguments", String.Empty), [Octopus_OctoPackPublishPackageToHttp], [Octopus_OctoPackPublishApiKey], [Octopus_RunOctoPack], [TDS_IsDesktopBuild], [TDS_GeneratePackage])
    If you prefer to do that in notepad, don't forget to escape the characters.
    This change will add some extra parameters to the msbuild command.
  8. Check-in this workflow into TFS.
  9. Edit your build definition again. In the "Process" tab, expand the "Build process template" section and click on "New..."
With those steps you should have your TFS template with your custom fields. In the next post I will explain how to use it.

Next steps:
  1. Setup a continuous integration for Sitecore with TDS - TFS 2013 and Octopus - Architecture
  2. Setup a continuous integration for Sitecore with TDS - TFS 2013 and Octopus - Step 1: Customize the TFS workflow
  3. Setup a continuous integration for Sitecore with TDS - TFS 2013 and Octopus - Step 2: Configure TFS
  4. Setup a continuous integration for Sitecore with TDS - TFS 2013 and Octopus - Step 3: Configure octopus
  5. Setup a continuous integration for Sitecore with TDS - TFS 2013 and Octopus - Step 4: Align the version number

June 18, 2014

Setup a continuous integration for Sitecore with TDS - TFS 2013 and Octopus

This week I have setup a complete fully automatic continuous integration for a website in Sitecore.

I have use the following architecture

Here is the workflow:
  1. The TFS 2013 Build Server get the sources from TFS 2013 Source Control
  2. The TFS 2013 Build Server execute a pre-build powershell to:
    1. Rename the versions into the AssemblyInfo.cs
    2. Rename the versions into the NuSpec files
  3. The TFS 2013 Build Server execute a msbuild command. This msbuild command do the following stuff:
    1. Create the Team Developpment for Sitecore (TDS) packages
    2. Create the Octopus nuget packages
    3. Push the Octopus nuget packages on the nuget server
  4. The TFS 2013 Build Server execute a post-build powershell to:
    1. Create an Octopus release
    2. Deploy the Octopus release on integration. This will:
      1. Send an email to the administrator
      2. Cleanup the old files
      3. Deploy my Octopus nuget packages of code
      4. Deploy my Octopus nuget packages of TDS
      5. Install the SQL Scripts
      6. Install the TDS packages and modules


The configuration of those different steps are detailed here:
  1. Setup a continuous integration for Sitecore with TDS - TFS 2013 and Octopus - Architecture
  2. Setup a continuous integration for Sitecore with TDS - TFS 2013 and Octopus - Step 1: Customize the TFS workflow
  3. Setup a continuous integration for Sitecore with TDS - TFS 2013 and Octopus - Step 2: Configure TFS
  4. Setup a continuous integration for Sitecore with TDS - TFS 2013 and Octopus - Step 3: Configure octopus
  5. Setup a continuous integration for Sitecore with TDS - TFS 2013 and Octopus - Step 4: Align the version number

June 5, 2014

Sitecore 7.5 : Setting up xDB without Mongo experience?

As a MVP, I have the chance to test Sitecore 7.5. The main change in this version is the migration of DMS in xDB (Mongo) in place of MsSql. I didn't had any experience at all with mongo or any NoSql database yet so I will share this first experience here.
The first logical step is to downloaded mongo for windows here: http://www.mongodb.org/downloads
Then I will try to follow the steps of this page: http://docs.mongodb.org/manual/tutorial/install-mongodb-on-windows/
For my first test, I have try to run it manually:
  1. I have create a data folder: C:\Program Files\MongoDB 2.6 Standard\data\db
  2. Then in command line I have type the following command:
    cd C:\Program Files\MongoDB 2.6 Standard\bin
     mongod.exe --dbpath "C:\Program Files\MongoDB 2.6 Standard\data\db"
    Two things here:
    • You need to use the mongod.exe and not the mongo.exe. Mongod.exe is the service and mongo.exe the client.
    • If I didn't specify the path to the data folder it doesn't work event if I move it below \bin.

So ok, I have a running mongo server now. But, if I don't want to run it manually each time I restart so, let's configure it as a windows service:
  1. Create a directory for the logs: C:\Program Files\MongoDB 2.6 Standard\logs
  2. Create a new empty file for the configuration: C:\Program Files\MongoDB 2.6 Standard\bin\mongod.cfg and add the following content in it:
    logpath="C:\Program Files\MongoDB 2.6 Standard\logs\mongo.log" 
    dbpath="C:\Program Files\MongoDB 2.6 Standard\data\db"
  3. Now, register the windows service by executing the following command:
    sc.exe create MongoDB binPath= "\"C:\Program Files\MongoDB 2.6 Standard\bin\mongod.exe\" --service --config=\"C:\Program Files\MongoDB 2.6 Standard\bin\mongod.cfg\"" DisplayName= "MongoDB 2.6 Standard" start= "auto"
  4. By curiosity, I do a little check to be sure that the service is registered by typing "services.msc" and this is ok I see that the service: 
  5. To be sure that this service work correctly, let's start it:
    sc.exe start MongoDB
    If you have the message: "The service did not respond to the start or control request in a timely fashion." this probably mean that your cfg file is not correctly
Now, if you want, you can test to add a new db and some records by following this guide: http://docs.mongodb.org/manual/tutorial/getting-started/
Ok now, my mongo service is up and running, let's install Sitecore 7.5! I will not explain every steps here because it is the same as into the previous version but the interesting stuffs for this setup are into the connectionstrings.config. We have now the following connectionstring who use mongo (if you use DMS):

<add name="analytics" connectionString="mongodb://localhost/analytics" />
<add name="tracking.live" connectionString="mongodb://localhost/tracking_live" />
<add name="tracking.history" connectionString="mongodb://localhost/tracking_history" />
<add name="automation.live" connectionString="mongodb://localhost/automation_live" />
<add name="automation.history" connectionString="mongodb://localhost/automation_history" />
<add name="session" connectionString="mongodb://localhost/session" />
For the basic config on my developer's computer this is ok like this. I can run my new instance of sitecore. And.... ok no crash at least :-)
Let's check if my dbs has been created into mongo:
  1. In command line, I type:
    mongo
    Response is:
    MongoDB shell version: 2.6.0
    connecting to: test
    Type:
    show dbs
    Response is:
    admin               (empty)
    analytics           (empty)
    automation_history  (empty)
    automation_live     (empty)
    local               0.078GB
    mydb                0.078GB
    tracking_history    (empty)
    tracking_live       (empty)
So ok! This mean that my dbs has been correctly created!
Let's browse a page or two into the website and check my Experience Profile Dashboard...Bingo! I have a record in my dashboard so it work:

April 14, 2014

Migrate from SVN to TFS (or git)

This time the post wll be not directly about Sitecore but for a task I had for a sitecore project: migrate from SVN to TFS

You have of course some tools who allow you to do that but most of the time you need to pay for it and in fact it is really simple to do that in command line.

In TFS 2013, you have two option for the source control:

  • GIT
  • TFS

So I will explain both by using a powershell script. To do that, you just need to create a file with the ps1 extension and then execute it from a powershell command window. If you have the warning who say that the file must be signed you need to execute the following command first:

Set-ExecutionPolicy Unrestricted

  • For both scenarios you will need to migrate first from SVN to a local git repository. So the first thing to do (if not yet done) is install git: http://git-scm.com/downloads
  • For the second step, you need to migrate from svn to your local git by doing the following code:
    $svnPathToMigrate = "http://svn.lbigroup.be:6060/svn/my-project/trunk"
    
    $targetGitPath = "c:\Projects\Tests\GitOfMyProject"
     
    #Create the folder for the local git 
    md $targetGitPath
    #Browse into this folder
    cd $targetGitPath
    
    #Create the empty git repository
    git svn init $svnPathToMigrate --no-metadata
    #Get the sources from svn into the git repository
    git svn fetch
    
  • Depending of the source control system
    • For Git:
      $tfsGitTarget = "http://betfs01:8080/tfs/MyProjectCollection/_git/MyProject"
      #Synch the local git with the TFS collection
      git remote add TfsGit $tfsGitTarget
      git push TfsGit master
      
    • For TFS:
      • You first need to install git tf
      • $tfsTargetCollection = "http://betfs01:8080/tfs/MyProjectCollection"
        $tfsTargetProject = "$/MyProject"
        #Push to TFS
        git tf configure $tfsTargetCollection $tfsTargetProject
        Git tf checkin --deep --no-lock
        

October 9, 2012

Create your own pipeline

As you know sitecore use a lot of pipeline but did you already try to create your own pipeline for your custom actions?
To create your own pipeline in sitecore it is easy, you will need to:
  • Add your pipeline and porocessors in the web.config (or externalize it in the \app_config\include folder)
    <configuration xmlns:patch="http://www.sitecore.net/xmlconfig/">
      <sitecore>
        <pipelines>
          <myCustomPipeline>        
            <processor type="MyNamespace.MyProcessor, MyDll" />          
          </myCustomPipeline>
        </pipelines>
      </sitecore>
    </configuration>
  • After you may call it with this code and t will wall all the processors in this pipeline:
    PipelineArgs args = new PipelineArgs();
    CorePipeline.Run("myCustomPipeline", args);
Simple doesn’t it? You may also use your own argument if you need to pass some additional properties to the processors. To do that, you just need to create a new class who inherit from PipelineArgs.

July 16, 2012

Create a custom remote event in sitecore - Demo

I have multiple comments on the post about the custom remote events, so I will give you a more complete example. If you need more information about how those events work please refer to the previous post.

You can download the full exaple as a zip file here.

In this example, I have also added a parameter in the event.

  • The event:
    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    using System.Runtime.Serialization;
    
    namespace BOL.Events
    {
        [DataContract]
        public class ClearCacheEvent
        {
            public ClearCacheEvent(string cacheName)
            {
                this.CacheName = cacheName;
            }
    
            // Properties
            [DataMember]
            public string CacheName { get; protected set; }
    
        }
    }
  • The event argument with the parameter:
    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    using Sitecore.Events;
    
    namespace BOL.Events
    {
        [Serializable]
        public class ClearCacheEventArgs : EventArgs, IPassNativeEventArgs
        {
            public string CacheName { get; set; }
    
            public ClearCacheEventArgs(string cacheName)
            {
                this.CacheName = cacheName;
            }
        }
    }
    
  • The event handler:
    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    using Sitecore.Diagnostics;
    using Sitecore.Events;
    
    namespace BOL.Events
    {
        public class ClearCacheEventHandler
        {
            /// 
            /// This methos is used to raise the local event
            /// 
            /// 
            public static void Run(ClearCacheEvent @event)
            {
                Log.Info("ClearCacheEventHandler - Run", typeof(ClearCacheEventHandler));
                ClearCacheEventArgs args = new ClearCacheEventArgs(@event.CacheName);
                Event.RaiseEvent("clearcache:remote", new object[] { args });
            }
        }
    }
  • The hook to register the event:
    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    using Sitecore.Events.Hooks;
    
    namespace BOL.Events
    {
        public class ClearCacheHook : IHook
        {
            public void Initialize()
            {
                Sitecore.Eventing.EventManager.Subscribe(new Action { ClearCacheEventHandler.Run });
            }
        }
    }
    
  • The code to trigger the event:
    public class TriggerEvent
    {
     public void Trigger()
     {
      ClearCacheEvent inst = new ClearCacheEvent("Navigation");
      Sitecore.Eventing.EventManager.QueueEvent(inst);
     }
    }
  • The config file to register the event:
    <configuration xmlns:patch="http://www.sitecore.net/xmlconfig/">
      <sitecore>
     <hooks>  
      <hook type="BOL.Events.ClearCacheHook, BOL"/>  
     </hooks> 
     
     <events>
      <event name="clearcache:remote">
       <handler type="Shurgard.Events.ClearStoreCache, Shurgard.Events" method="OnRemoteClearStoreCache"/>          
      </event>
     <events>
     
      </sitecore>
    </configuration>

March 14, 2012

Add a custom section in the config

Here is the method if you need to a custom section into you config file.

Here is my example of config that I need to include into my \App_Config\Include folder:
<configuration xmlns:patch="http://www.sitecore.net/xmlconfig/">
  <sitecore>
 <MyCustomSection>
  <MyCustomElement Name="XXX" MyProperty="YYY" />
  <MyCustomElement Name="AAA" MyProperty="BBB" />
 </MyCustomSection>
  </sitecore>
</configuration>

And here is a way to read it:
using System.Collections.Generic;
using Sitecore.Configuration;
using System.Xml;
using Sitecore.Xml;

/// <summary>
/// TODO: Update summary.
/// </summary>
public class MyConfigElement
{
 //The two properties corresponding to the properties into the config
 public string Name { get; set; }
 public string MyProperty { get; set; }

 //The method who return the list of my custom elements
 public static List<MyConfigElement> GetListMyCustomElements()
 {
  List<MyConfigElement> lst = new List<MyConfigElement>();
  
  //Read the configuration nodes
  foreach (XmlNode node in Factory.GetConfigNodes("MyCustomSection/MyCustomElement"))
  {
   //Create a element of this type
   MyConfigElement elem = new MyConfigElement();
   elem.Name = XmlUtil.GetAttribute("Name", node);
   elem.MyProperty = XmlUtil.GetAttribute("MyProperty", node);
   lst.Add(elem);
  }
  return lst;
 }
}

Don’t forget to use a property “Name” to avoid the conflicts in the include folder (see this post: http://sitecoreblog.blogspot.com/2012/02/config-patching-system-for-external.html)

March 2, 2012

Include the sc namespace

When you have multiple projects in a single solution you will maybe have this error message when you try to use some sitecore controls like the sc:Text:

You have to simple solutions to fix this problem:

You may add this line on the top of each files:
<%@ Register Assembly="Sitecore.Kernel" Namespace="Sitecore.Web.UI.WebControls" TagPrefix="sc" %>


Or you may add a web.config into your project with the minimal information to include this namespace.
But don’t deploy this web.config in your solution because it will override the correct sitecore web.config
<?xml version="1.0"?>
<configuration>
  <system.web>
    <pages validateRequest="false">
      <controls>
        <add tagPrefix="sc" namespace="Sitecore.Web.UI.WebControls" assembly="Sitecore.Kernel" />
        <add tagPrefix="asp" namespace="System.Web.UI" assembly="System.Web.Extensions, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35" />
        <add tagPrefix="asp" namespace="System.Web.UI.WebControls" assembly="System.Web.Extensions, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35" />
        <add tagPrefix="sc" namespace="Sitecore.Web.UI.WebControls" assembly="Sitecore.Analytics" />
      </controls>
    </pages>
  </system.web>
</configuration>

February 15, 2012

Config patching system for the external config files

This very good post http://www.thescrewballdivision.com/playing-with-sitecore-include-files explain the available commands for the patching very well so I will not copy it but I will add an important information.

When you need to add some informations in a tag the key used for the merge is the property name.

Example:

By default in the web.config you have this:
<event name="publish:end">
 <handler type="Sitecore.Publishing.HtmlCacheClearer, Sitecore.Kernel" method="ClearCache">
   <sites hint="list">    
      <site>website</site>
   </sites>
 </handler>
</event> 

And if you add this in a separated config file:
<event name="publish:end">
 <handler type="Sitecore.Publishing.HtmlCacheClearer, Sitecore.Kernel" method="ClearCache">
   <sites hint="list">    
     <site>mysite</site>
   </sites>
 </handler>
</event> 

The merged web.config will be:
<event name="publish:end">
 <handler type="Sitecore.Publishing.HtmlCacheClearer, Sitecore.Kernel" method="ClearCache">
   <sites hint="list">    
      <site>mysite</site>
   </sites>
 </handler>
</event> 

To avoid this and combine the two tags without removing the existing one you only have to add a name property in the site tag:
<event name="publish:end">
 <handler type="Sitecore.Publishing.HtmlCacheClearer, Sitecore.Kernel" method="ClearCache">
   <sites hint="list">    
      <site name="mysite">mysite</site>
   </sites>
 </handler>
</event> 

Like this you will have the expected result:
<event name="publish:end">
 <handler type="Sitecore.Publishing.HtmlCacheClearer, Sitecore.Kernel" method="ClearCache">
   <sites hint="list">  
      <site>website</site>
      <site name="mysite">mysite</site>
   </sites>
 </handler>
</event> 

February 7, 2012

Share the config file : how to handle the path like the dataFolder

When we use SVN or TFS normally we commit some .config file an especially the web.config. The problem happened when all the computers doesn’t have the same path structure. For example I will not use the same data folder as my colleague.

You have multiple solutions to handle including having different build configuration but I think that the easiest way to handle it is by simply using the virtual directories.

To create a virtual directory:
  1. In IIS right click on your website and select “Add Virtual Directory”
  2. The alias is the folder name (in your config file) and the physical path is the real folder
  3. In the config, the datafolder will look like
      <sc.variable name="dataFolder" value="/Data" />
      

February 6, 2012

Remote events how to investigate

I had some issues with the sitecore remote events not triggered on the content delivery. I will explain how you can investigate why it could happened and how to fix it.


First of all, how sitecore handle the remote events when you use the scalability?


When you publish an item in sitecore, sitecore will write a line in the EventQueue table of the corresponding database with all the necessary data including the InstanceName of the server who have triggered the event.

After that sitecore have an timer who check in this table if they are any event to trigger. First remark, sitecore look at the InstanceName field to know if this instance need to trigger this record or not so this instance need to be unique (be carefull with the copy paste) ;-).


Here is what sitecore add in the web database in the EventQueue when you publish an item:
Id 82480059-8CF4-4D2E-82B1-9C00323F12F2
EventType Sitecore.Eventing.Remote.PublishEndRemoteEvent, Sitecore.Kernel, Version=6.0.0.0, Culture=neutral, PublicKeyToken=null
InstanceType Sitecore.Eventing.Remote.PublishEndRemoteEvent, Sitecore.Kernel, Version=6.0.0.0, Culture=neutral, PublicKeyToken=null
InstanceData {"CompareRevisions":true,"Deep":false,"FromDate":"\/Date(-5339901827822+0200)\/","LanguageName":"en","Mode":3,"PublishDate":"\/Date(1328532964271+0100)\/","PublishingTargets":["{8E080626-DDC3-4EF4-A1D1-F0BE4A200254}","{8E080626-DDC3-4EF4-A1D1-F0BE4A200254}"],"RepublishAll":false,"RootItemId":"ebff6e2a-9149-4660-9d55-dec34282f856","SourceDatabaseName":"master","TargetDatabaseName":"web"}
InstanceName GRNQ6R1-sitecore650up3
RaiseLocally 0
RaiseGlobally 1
UserName sitecore\admin
Stamp 0x0000000000079C0F
Created 2012-02-06 12:56:07.033


The most important columns are :
  • "InstanceName": it is the server who have triggered the event. (setting "InstanceName" from ScalabilitySettings.config)
  • "EventType": The type of event: item:saved:remote, publish:end:remote, ...
  • "InstanceData": The data required to perform this operation

This is the first method to see if your events are trigered.

For the second method, you need to set in the web.config of each server the <events timingLevel="high"> setting (this setting is set to custom by default).
With this modification you should have any line like this for each event handled by sitecore in your log on both content delivery and content management instances.
Heartbeat 14:22:05 INFO  Event started: publish:statusUpdated
Heartbeat 14:22:05 INFO  Executed: Sitecore.Publishing.RemotePublishingEventHandler.OnStatusUpdated(). Elapsed: 4,23265309264199
Heartbeat 14:22:05 INFO  Event ended: publish:statusUpdated. Elapsed: 4,33442834380877

If even with all those informations your events are not triggered or triggered but not received in by the other server you can try to cleanup the temporary event tables.
On each database execute those scripts:
DELETE FROM EventQueue;
DELETE FROM [Properties] WHERE  [Key] LIKE 'EQStamp%';

I don't know exactly what is the EQStamp% setting but it was the solution provided by the support and it has solved my problem :) If anybody know what is is exactly you can post a comment.

Be also careful to the exception in your events because if one event of the pipeline throw an exception the other one will be not executed.

June 23, 2011

Create a custom remote event in sitecore

Recently, I had to clear some cache on the frontend server from my backend.
To do that I have implemented a custom remote event. I did not find some documentation on sdn so here is the procedure (thank you to the Sergey Kravchenko from the sitecore support).

  1. Create your custom remote event class.  Remote event class is a usual class inherited from System.Object and marked with a [DataContract] attribute.
     
    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    using System.Runtime.Serialization;
    
    namespace XXX
    {
        [DataContract]
        public class ClearCacheEvent
        {
            public ClearCacheEvent()
            {
    
            }
        }
    }
    
  2. Create a custom event arguments class, to store the arguments of the event. It also, can be empty. Both Remote event class and event arguments class will be used to add event to the EventQueue.
    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    using Sitecore.Events;
    
    namespace XXX
    {
        public class ClearCacheEventArgs : EventArgs, IPassNativeEventArgs
        {
            public ClearCacheEventArgs(ClearCacheEvent @event)
            {
                
            }
        }
    }
    
  3. Create your custom event handler
    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    using Sitecore.Diagnostics;
    using Sitecore.Events;
    
    namespace XXX
    {
        public class ClearCacheEventHandler
        {
            /// <summary>
            /// The methos is the method that you need to implement as you do normally
            /// </summary>
            /// <param name="sender"></param>
            /// <param name="e"></param>
            public virtual void OnClearCacheRemote(object sender, EventArgs e)
            {
                Log.Info("Tadaa", this);
            }
    
            /// <summary>
            /// This methos is used to raise the local event
            /// </summary>
            /// <param name="event"></param>
            public static void Run(ClearCacheEvent @event)
            {
                Log.Info("ClearCacheEventHandler - Run", typeof(ClearCacheEventHandler));
                ClearCacheEventArgs args = new ClearCacheEventArgs(@event);
                Event.RaiseEvent("clearcache:remote", new object[] { args });
            }
        }
    }
    
  4. Create a custom Hook to bind event from the EventQueue and the "local event" in the CD server web config. This hook will call the method you have added in the event handler ("Run" method of the ClearCacheEventHandler class in our case)
    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    using Sitecore.Events.Hooks;
    
    namespace XXX
    {
        public class ClearCacheHook : IHook
        {
            public void Initialize()
            {
                Sitecore.Eventing.EventManager.Subscribe(new Action(ClearCacheEventHandler.Run));
            }
        }
    }
    
  5. Trigger the event in your code
    ClearCacheEvent inst = new ClearCacheEvent();
    Sitecore.Eventing.EventManager.QueueEvent<ClearCacheEvent>(inst);
    
  6. Configure the web.config (or add it in a separated file in the /include folder it is cleaner)

    The hook:
    <hooks>
        <hook type="XXX.ClearCacheHook, XXX"/>
    </hooks>
    
    The event:
    <events>
        <event name="clearcache:remote">
            <handler type="XXX.ClearCacheEventHandler, XXX" method="OnClearCacheRemote"/>
        </event>
    </events>
    

How does it work?
  1. Your code add an event in the   EventQueue.
  2. The EventQueue is checked by the special task:
    <eventQueue>
        <processingInterval>00:00:02</processingInterval>
    </eventQueue>
    
  3. The Hook call a method who raise the corresponding local event.
  4. Your local is triggered.

May 26, 2011

Write you messages in a separated file

It is not always easy with a sitecore "out of the box" to know witch messages come from your custom code and witch messages come from sitecore himself.
Here is a small tip to write you custom messages in a separated file:

First of all, we will prefix all our messages with a custom prefix "SH - " in my case:
Log.Info("SH - My message", typeof(MyObject));
Log.Error("SH - My message", typeof(MyObject));
...

And then to write it in a custom log file change in the web.config in the log4net section:
<appender name="MyFileAppender" type="log4net.Appender.SitecoreLogFileAppender, Sitecore.Logging">
    <file value="$(dataFolder)/logs/mylog.{date}.txt" />
 <filter type="log4net.Filter.StringMatchFilter">
    <regexToMatch value="^SH .*" />
    </filter>
    <filter type="log4net.Filter.DenyAllFilter" />
    <appendToFile value="true" />
    <layout type="log4net.Layout.PatternLayout">
    <conversionPattern value="%4t %d{ABSOLUTE} %-5p %m%n" />
    </layout>
</appender>
You will need to adapt the regex with your custom prefix of course.

And second point after the logFileAppender of sitecore add you custom one:
<root>
    <priority value="DEBUG"/>
    <appender-ref ref="LogFileAppender"/>
    <appender-ref ref="MyFileAppender" />
</root>

You have a lot of example of what you can do with log4net here: http://logging.apache.org/log4net/release/config-examples.html

EDIT: You have an alternative syntaxt and more info about the filters here https://sitecoreblog.blogspot.be/2017/03/log4net-add-stacktrace-in-logs.html