tag:blogger.com,1999:blog-6303248877201067862024-03-22T00:54:19.622+01:00Sitecore BlogVangansewinkel Benjaminhttp://www.blogger.com/profile/01696678283653731912noreply@blogger.comBlogger100125tag:blogger.com,1999:blog-630324887720106786.post-89739255749443742392018-11-21T08:46:00.000+01:002018-11-22T11:13:18.453+01:00Sitecore Contact Utilities also available for the List ManagementA very common requirement for our customers is the possibility to create some rules based on the values stores in the contact’s facets.<br />
<br />
To do that, Adam Conn have create two modules who work in combinations: the <a href="https://github.com/adamconn/sitecore-adaptive-rules" target="_blank">Sitecore Adaptive Rules</a> and the <a href="https://github.com/adamconn/sitecore-contact-utilities" target="_blank">Sitecore Contact Utilities</a>. Watch out the two following videos to know how those two modules works: <a href="https://www.youtube.com/watch?v=M2m2vJ5diEI&feature=youtu.be" target="_blank">Sitecore Adaptive Rules</a>, <a href="https://www.youtube.com/watch?v=ajSZar5aShc" target="_blank">Sitecore Contact Utilities</a>.<br />
<br />
What I have done on my own github is:<br />
<ol>
<li><b>Correcting some bugs</b>. For example, if you have twice the same values in two different facets it can mix both values in the facet. </li>
<li><b>Improving the performances</b>: The Adam Conn implementation was based on the IDTables for caching but this implementation was very slow when you have like 30+ facets. So I have refactor the code to be able to plug your own provider and I propose a “In memory provider” by default. Trust me this makes a lot of differences in term of performances! </li>
<li><b>List management</b>: the Adam Conn rules are only available for renderings personalization. But I also needed the same system to segment the contact lists. So I have adapted the code to be able to make this segmentation. This was really a huge work because when you personalize a component you just receive a single contact and return true a false as result of the rule. But, when you want to play with segmentation rule, you need to return a lambda who will be executed on the indexing provider (Lucene, Azure Search or Solr).<br /><div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEghSBTfdn1VYQQ90YZG4YSnTdpBUyDyPGlofJotDnDgSTx47rzH-gbJAjd-IHZGw0hSeaoolbI6LYJcwUJhQGPn9uAPRGI58bgbiy4_wk0rIRJ26LtQ5__7mVzkI5WK1WxWYnjl8Jq2Ibk/s1600/2018-11-21_0845.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="614" data-original-width="504" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEghSBTfdn1VYQQ90YZG4YSnTdpBUyDyPGlofJotDnDgSTx47rzH-gbJAjd-IHZGw0hSeaoolbI6LYJcwUJhQGPn9uAPRGI58bgbiy4_wk0rIRJ26LtQ5__7mVzkI5WK1WxWYnjl8Jq2Ibk/s1600/2018-11-21_0845.png" /></a></div>
</li>
</ol>
I have worked for months on this module so I will be glad if it can help your customers too and help the community!<br />
<br />
You can find the two modules here:<br />
<br />
<ul>
<li><a href="https://github.com/VGBenjamin/sitecore-adaptive-rules">https://github.com/VGBenjamin/sitecore-adaptive-rules</a> </li>
<li><a href="https://github.com/VGBenjamin/sitecore-contact-utilities">https://github.com/VGBenjamin/sitecore-contact-utilities</a> </li>
</ul>
I want to thank again Adam Conn for you amazing job on it too. I have done a pull request on the original repository too so if you read it do not hesitate to merge it with yours if you want to!<br />
<br />
Enjoy<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<br /></div>
<br />Vangansewinkel Benjaminhttp://www.blogger.com/profile/01696678283653731912noreply@blogger.com0tag:blogger.com,1999:blog-630324887720106786.post-83306811948434027352018-11-09T15:35:00.002+01:002018-11-09T15:36:00.596+01:00The evil code debugging tool is hereToday I have released a neew tool to be able to debug your live website by executing all the code you want easilly.<br />
You gonna love it but you will not never tell anybody that your are using it :-D<br />
<br />
Who never had an incomprehensible error in a sitecore website in production and want o be able to execute a line of code IN THE CONTEXT OF YOUR SITE.<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiLzi4D86uDBc5GWKA3m4y7DY8up568KwsM83dehnab12uZ2Nv3IVd7KPJUuxF8tXZzO05gBDQGfgc0tFCZRHwPj_w4XcuRLU8zpV8v7q11QKSx9vTPExFtDfglSsJIfPN-NYQledLf3z0/s1600/Screenshot-1.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="810" data-original-width="1600" height="324" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiLzi4D86uDBc5GWKA3m4y7DY8up568KwsM83dehnab12uZ2Nv3IVd7KPJUuxF8tXZzO05gBDQGfgc0tFCZRHwPj_w4XcuRLU8zpV8v7q11QKSx9vTPExFtDfglSsJIfPN-NYQledLf3z0/s640/Screenshot-1.png" width="640" /></a></div>
<br />
<br />
This tool is available on the sitecore marketplace: <a href="https://marketplace.sitecore.net/en/Modules/U/UniversalDebugger.aspx">https://marketplace.sitecore.net/en/Modules/U/UniversalDebugger.aspx</a> and on github: <a href="https://github.com/VGBenjamin/UniversalDebugger">https://github.com/VGBenjamin/UniversalDebugger</a>Vangansewinkel Benjaminhttp://www.blogger.com/profile/01696678283653731912noreply@blogger.com0tag:blogger.com,1999:blog-630324887720106786.post-91728735175654222992018-06-18T08:15:00.000+02:002018-06-25T13:30:45.935+02:00Sitecore 8.2 : System.FormatException: Unrecognized Guid formatIn Sitecore 8.2.6 and later, you could have a message like: <i>System.FormatException: Unrecognized Guid format. Actual value: ---> System.FormatException: Unrecognized Guid format</i> when you try to use the link database.<br />
For example, if you rebuild the link database.
This message is due to some general links not correctly formatted. In the previous versions of Sitecore it could happend that a general link contains an emtpy id attribute or no id at all like this:<br />
<pre class="brush: xml"><link text="Description" linktype="internal" class="" title="myTitle" target="" querystring="" id="" /></pre>
But this is not valid anymore in Sitecore 8.2.6+.
To detect those link the easiest solution is running the following Powershell script:
<br />
<pre class="brush: powershell">Add-Type -AssemblyName Sitecore.Kernel
function Resolve-Error ($ErrorRecord=$Error[0])
{
$ErrorRecord | Format-List * -Force
$ErrorRecord.InvocationInfo |Format-List *
$Exception = $ErrorRecord.Exception
for ($i = 0; $Exception; $i++, ($Exception = $Exception.InnerException))
{ "$i" * 80
$Exception |Format-List * -Force
}
}
function Test-Fields($item) {
$anyState = [Sitecore.Links.ItemLinkState]::Any
$item.Fields.ReadAll()
for ($j = 0; $j -lt $item.Fields.Count; $j++)
{
$field = $item.Fields[$j];
if ($field -ne $null)
{
$field2 = [Sitecore.Data.Fields.FieldTypeManager]::GetField($field)
if ($field2 -ne $null)
{
Try {
$linksValidationResult = New-Object -TypeName "Sitecore.Links.LinksValidationResult" -ArgumentList $field, $anyState
$field2.ValidateLinks($linksValidationResult);
}
Catch {
Write-Host "Error on item: $($item.ID). Field: $($field.Name). Path: $($item.Paths.FullPath). Language: $($item.Language.Name). Value: $($field.Value)"
}
}
}
}
}
function ExecOnPath($path) {
Write-Host "Execute on: $path"
Get-ChildItem -Path "master:$path" -Recurse -Language * | ForEach-Object { Test-Fields $_ }
}
ExecOnPath -Path "/sitecore/content/Home"
Write-Host "Done!"</pre>
If you also want to fix the empty links at the same time you can add those extra lines into the catch clause:
<br />
<pre class="brush: powershell">if($field.Value -eq '<link linktype="internal" />') {
$item.Editing.BeginEdit()
$item[$field.Name] = ""
$item.Editing.EndEdit()
Write-Host "Fixed automatically!"
} else {
Write-Host "Cannot fix it automatically..."
}</pre>
<pre class="brush: powershell">
</pre>
<pre class="brush: powershell">You can find the original thread who helped me to develop this script here: <a href="https://sitecore.stackexchange.com/questions/11282/rebuilding-link-database-system-formatexception-unrecognized-guid-format">https://sitecore.stackexchange.com/questions/11282/rebuilding-link-database-system-formatexception-unrecognized-guid-format</a></pre>
Vangansewinkel Benjaminhttp://www.blogger.com/profile/01696678283653731912noreply@blogger.com0tag:blogger.com,1999:blog-630324887720106786.post-60541174704839040262017-03-09T15:12:00.001+01:002017-03-09T15:12:50.202+01:00Log4net - Add the stacktrace in the logsThe log4net version included in Sitecore doesn't contains the stacktrace. That mean that you cannot use %stacktrace or %stacktracedetail in the conversionsPattern.<br />
<br />
As a workaround, you can override the appender.<br />
First create the following class:<br />
<pre class="brush: csharp">public class SitecoreLogFileWithStackAppender : SitecoreLogFileAppender
{
protected override void Append(LoggingEvent loggingEvent)
{
var msg = loggingEvent.MessageObject as string;
if (msg != null)
{
var prop = loggingEvent.GetType().GetField("m_data", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance);
var mData = (LoggingEventData)prop.GetValue(loggingEvent);
mData.Message = $"{msg}{Environment.NewLine}{Environment.StackTrace}";
prop.SetValue(loggingEvent, mData);
}
base.Append(loggingEvent);
}
}</pre>
Now you are able to configure the log4net appender like this:
<pre class="brush: xml"><appender name="StackFileAppender" type="MyNamespace.SitecoreLogFileWithStackAppender, MyDll">
<file value="$(dataFolder)/logs/log.Stacktraces.{date}.txt" />
<appendToFile value="true" />
<layout type="log4net.Layout.PatternLayout">
<conversionPattern value="%5t %d{yyyy-MM-dd HH:mm:ss.fff} %-5p %c - %m%n%n" />
</layout>
</appender></pre>
For my project I have choose to log all the errors, and all the messages who begin by [SD] in a separated fil with the stacktrace. The config is the following:
<pre class="brush: xml"><appender name="StackFileAppender" type="MyNamespace.SitecoreLogFileWithStackAppender, MyDll">
<file value="$(dataFolder)/logs/log.Stacktraces.{date}.txt" />
<filter type="log4net.Filter.StringMatchFilter">
<stringToMatch value="[SD]"></stringToMatch>
</filter>
<filter type="log4net.Filter.DenyAllFilter" />
<appendToFile value="true" />
<layout type="log4net.Layout.PatternLayout">
<conversionPattern value="%5t %d{yyyy-MM-dd HH:mm:ss.fff} %-5p %c - %m%n%n" />
</layout>
</appender></pre>
<br />Vangansewinkel Benjaminhttp://www.blogger.com/profile/01696678283653731912noreply@blogger.com1tag:blogger.com,1999:blog-630324887720106786.post-38768114913236789032017-01-27T07:42:00.003+01:002017-01-27T07:42:52.511+01:00Impossible to add a language in SitecoreI had an issue very recently: I cannot add a language in Sitcore anymore. When I tried to add it, the add language wizard was freezing without any error messages.<br />
<br />
The resolution of this issue was very simple I just had the '-' character in the InvalidItemNameChars like this:<br />
<br />
<pre class="brush: csharp"><setting name="InvalidItemNameChars">
<patch:attribute name="value">\/:?&quot;&lt;&gt;|[].-</patch:attribute>
</setting></pre>
So juste remove it from this patch during the add language process and then re-add it and the problem is fix!Vangansewinkel Benjaminhttp://www.blogger.com/profile/01696678283653731912noreply@blogger.com7tag:blogger.com,1999:blog-630324887720106786.post-17049472492020863692016-11-25T08:14:00.002+01:002016-11-25T08:14:36.270+01:00Tips about the fallback in Sitecore<h3>
Tip number 1</h3>
<u>Problem</u>: The fallback is disabled by default in the item:save events.<br />
<u>Tip</u>: You can use the following code:<br />
<pre class="brush: csharp">using(new FallbackDisabler(FallbackStates.Default))
{
db.GetItem()
...
}</pre>
<br />
<h3>
Tip number 2</h3>
<div>
<u>Problem</u>: The fallback is not active in the scheduled tasks.</div>
<div>
<u>Tip</u>: In the FieldFallback_00.config, you need to add the site 'scheduler' in the 'sites' tag.</div>
<div>
<br /></div>
<h3>
Tip number 3</h3>
<div>
<u>Problem</u>: 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.</div>
<div>
<u>Tip</u>: You need to retrieve this item with the standard method: db.GetItem(itemId).</div>
<div>
<br /></div>
<h3>
Tip number 4</h3>
<div>
<u>Problem</u>: The ancestor fallback didn't work in the other languages.</div>
<div>
<u>Tip</u>: 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.</div>
Vangansewinkel Benjaminhttp://www.blogger.com/profile/01696678283653731912noreply@blogger.com1tag:blogger.com,1999:blog-630324887720106786.post-68539179279310998532016-11-10T07:24:00.000+01:002016-11-10T07:24:13.444+01:00Sitecore Utility - Sanitize QueryRecently i had an issue with the standard Sitecore queries on a bucket item: when an Sitecore item begin with a 0 the queries may return no results.<br />
This is of course an issue with the items who are in a date based bucket. You may also have some unexpected results if you have some of the reserved keywords in the item name as "and", "or", "self", "children", "parent", "sibilings", ...<br />
<div>
So, I have create a function to "sanitize" your queries before the Sitecore queries.</div>
<div>
<br /></div>
<div>
And an example of the SanitizeQuery: the SanitizeQuery function:<br />
<pre class="brush: csharp">var myQuery = "/sitecore/content/TestSites/Test/09/28/15/56/*".SanitizeQuery();
var item = Sitecore.Context.ContentDatabase.SelectSingleItem(myQuery);</pre>
</div>
<div>
<br /></div>
I would like to create a collection of utils function on github with all those kind of reusable functions. Like this you can easilly extract the functions you are interested in or the whole project.<br />
You will find this repository here: <a href="https://github.com/VGBenjamin/Sitecore.Utility">https://github.com/VGBenjamin/Sitecore.Utility</a><br />
If you have functions that you would like to share in this directory you can of course propose it this could become a new collaborative repository.Vangansewinkel Benjaminhttp://www.blogger.com/profile/01696678283653731912noreply@blogger.com0tag:blogger.com,1999:blog-630324887720106786.post-12975803748108617362016-07-07T07:01:00.006+02:002016-07-07T07:01:36.716+02:00Glass.Mapper - Retreive items without versionToday I will give you a very small trick to retrieve an item with Glass.Mapper even if the item didn't have any version in the current language. This is be helpful when you have an item with only some shared fields. Of course, the non-shared fields retrieved will be empty if you didn't have a version in the selected language.<br />
<br />
The trick is very simple: you just need to surround it by a VersionCountDisabler<br />
<br />
Here is two examples:
<br />
<pre class="brush: csharp">using(new VersionCountDisabler())
{
var model = sitecoreService.GetItem<MyModel>("/sitecore/content/home");
}</pre>
<pre class="brush: csharp">using(new VersionCountDisabler())
{
var model = myItem.Cast<T>(item, isLazy, inferType);
}</pre>
You can find it in the official documentation but this isn't a very well-known feature: <a href="http://www.glass.lu/Mapper/Sc/Documentation/VersionCountDisabler">http://www.glass.lu/Mapper/Sc/Documentation/VersionCountDisabler</a><br />
<br />
In bonus, here is the way to know if you are in a VersionCountDisabler mode in your code. This is usefull if you wrap the code with a custom method as I do.<br />
<br />
<pre class="brush: csharp">bool isInVersionCountDisabler = (Switcher<VersionCountState, VersionCountState>.CurrentValue == VersionCountState.Disabled);</pre>Vangansewinkel Benjaminhttp://www.blogger.com/profile/01696678283653731912noreply@blogger.com2tag:blogger.com,1999:blog-630324887720106786.post-49834805065709096642016-06-30T08:27:00.000+02:002016-06-30T08:47:51.811+02:00Scan and Install all the packages in a folderSometimes ago I have published the PackageInstaller on github and on the <a href="https://marketplace.sitecore.net/Modules/P/Package_Installer.aspx?sc_lang=en" target="_blank">marketplace</a>.<br />
<br />
This is a useful script to install a Sitecore package in command line. One of the scenarios, I like to cover with this script is the installation of the missing packages on the different machines. This mean on each developer’s machine but also on staging and production.<br />
<br />
To do that I have developed the following script who scan a folder and install all the packages. Then it will write the installed packages into a text file to avoid the reinstallation on this machine the next time. Of course this file shouldn't be committed :-)
<br />
<br />
<pre class="brush:powershell"><#
.SYNOPSIS
Install all the sitecore modules who are not installed yet. And save the list of those modules to avoid to reinstall it next time.
.PARAMETER modulesPath
The folder who contains the modules to install
.PARAMETER allreadyInstalledModulesPath
The folder who contains the file .txt.user with the allready installed modules
.PARAMETER packageInstallerExe
The path to the file Sidewalk.SC.PackageInstaller.Client.exe
.PARAMETER packageInstallerSln
The path to the file \Sidewalk.SC.PackageInstaller.sln used to build the solution only if the Sidewalk.SC.PackageInstaller.Client.exe is not found
.PARAMETER msBuildPath
The path to the file msbuild.exe used to build the solution only if the Sidewalk.SC.PackageInstaller.Client.exe is not found
.PARAMETER sitecoreUrl
The sitecore url where the modules needs to be installed
.PARAMETER sitecoreDeployFolder
The sitecore Website folder where the modules need to be installed
.EXAMPLE
InstallMissingModules.ps1 -sitecoreUrl "http://mysitecore.sandbox.local" -sitecoreDeployFolder "C:\inetpub\wwwroot\mysitecore.sandbox\Website" -Verbose
#>
[CmdletBinding()]
param(
[Parameter(Mandatory=$false)][string]$modulesPath = "..\..\..\Modules\",
[Parameter(Mandatory=$false)][string]$allreadyInstalledModulesPath = "..\..\..\Modules\allreadyInstalledModules.txt.user",
[Parameter(Mandatory=$true)][string]$sitecoreUrl,
[Parameter(Mandatory=$true)][string]$sitecoreDeployFolder,
[switch]$whatIf
)
$fileSystemModulePath = "$currentDir\..\..\Powershell\Libraries\FileSystem.psm1"
function Get-ScriptDirectory {
Split-Path -parent $PSCommandPath
}
$currentDir = Get-ScriptDirectory
try {
$modulesPathFullPath = Resolve-Path $modulesPath
Write-Verbose -Message "Module directory: $($modulesPathFullPath.Path)"
} catch {
throw new FileNotFoundException("The modules path is not found. $($_.Exception.Message)")
}
$packageInstallerExe = "$currentDir\PackageInstaller\SC.PackageInstaller.Client.exe"
if(-not (Test-Path $packageInstallerExe)){
Write-Verbose -Message "The package installer exe is not found."
#Download the zip file from github
$modulePath = Resolve-Path $fileSystemModulePath
Import-Module $modulePath.Path
$zipPath = "$currentDir\PackageInstaller.zip"
Get-RemoteFile -url "https://github.com/VGBenjamin/PackageInstaller/raw/master/Sidewalk.PackageInstaller.zip" -targetFile $zipPath -Verbose:$PSBoundParameters['Verbose']
Write-Verbose -Message "Extracting the zipfile"
try
{
Expand-Zip -zipPath $zipPath -destination "$currentDir\PackageInstaller\" -Verbose:$PSBoundParameters['Verbose'] -createDestinationFolderIfNeeded
} catch [Exception]
{
echo $_.Exception|format-list -force
}
}
$allreadyInstalledModules = New-Object 'System.Collections.Generic.HashSet[String]'
$allreadyInstalledModulesPathFillPath = "$currentDir$allreadyInstalledModulesPath"
if(Test-Path $allreadyInstalledModulesPathFillPath) {
Write-Verbose -Message "Loading the list of allready installed modules: $allreadyInstalledModulesPathFillPath"
Get-Content $allreadyInstalledModulesPathFillPath | ForEach-Object { $allreadyInstalledModules.Add($_ ) | Out-Null }
Write-Verbose -Message "Modules allready installed: $allreadyInstalledModules"
}
foreach($module in Get-ChildItem -Path $modulesPathFullPath.Path -Include @("*.zip","*.update") -Recurse) {
if(-not ($allreadyInstalledModules.Contains($module.Name))) {
Write-Verbose -Message "Installing the module: $($module.FullName)"
if($whatIf) {
Write-Host "WhatIf : Installing the module with the parameters: $packageInstallerExe -sitecoreUrl $sitecoreUrl -sitecoreDeployFolder $sitecoreDeployFolder -packagePath $($module.FullName) -connector tds"
} else {
try {
& "$packageInstallerExe" -sitecoreUrl $sitecoreUrl -sitecoreDeployFolder $sitecoreDeployFolder -packagePath "$($module.FullName)" -connector "tds" | Out-String
$allreadyInstalledModules.Add($module.Name) | Out-Null
} catch [Exception]
{
echo $_.Exception|format-list -force
}
}
}
}
Set-Content -Path $allreadyInstalledModulesPathFillPath -Value ($allreadyInstalledModules | Out-String) -WhatIf:$whatIf</pre>
This script is also available here: <a href="https://raw.githubusercontent.com/VGBenjamin/PackageInstaller/master/InstallMissingModules-github.ps1">https://raw.githubusercontent.com/VGBenjamin/PackageInstaller/master/InstallMissingModules-github.ps1</a><br />
<br />
The dependency FileSystem.psm1 is available here: <a href="https://raw.githubusercontent.com/VGBenjamin/PackageInstaller/master/FileSystem.psm1">https://raw.githubusercontent.com/VGBenjamin/PackageInstaller/master/FileSystem.psm1</a><br />
<br />
TheVangansewinkel Benjaminhttp://www.blogger.com/profile/01696678283653731912noreply@blogger.com0tag:blogger.com,1999:blog-630324887720106786.post-77278669210384335682016-01-15T09:36:00.001+01:002016-01-15T09:36:29.916+01:00Deep drive into the Sitecore Client Pipelines with SPEAKThis post is very complex. If you read it from a rss or anything else I really suggest to read it directly into my blog because I have create a slideshow who is suppose to be more readable :-)<br />
<br />
This is the forth post<br />
<br />
<ul>
<li><a href="http://sitecoreblog.blogspot.be/2015/11/customize-experienceeditor-part-1.html" target="_blank">Customize the ExperienceEditor Part 1 - Include your custom JS</a></li>
<li><a href="http://sitecoreblog.blogspot.be/2015/11/customize-experienceeditor-part-2.html" target="_blank">Customize the ExperienceEditor Part 2 - Override the Sitecore functions in the JS</a></li>
<li><a href="http://sitecoreblog.blogspot.be/2015/11/customize-experienceeditor-part-3.html" target="_blank">Customize the ExperienceEditor Part 3 - Accessing to the registry from the javascript</a></li>
<li><a href="http://sitecoreblog.blogspot.com/2015/11/customize-experienceeditor-part-4-deep.html" target="_blank">Customize the ExperienceEditor Part 4 - Deep Drive into the Sitecore Client Pipelines</a></li>
</ul>
<iframe src="//www.slideshare.net/slideshow/embed_code/key/Ej1SXgJKtyC7vg" width="595" height="485" frameborder="0" marginwidth="0" marginheight="0" scrolling="no" style="border:1px solid #CCC; border-width:1px; margin-bottom:5px; max-width: 100%;" allowfullscreen> </iframe> <div style="margin-bottom:5px"> <strong> <a href="//www.slideshare.net/BenjaminVangansewink/sitecore-deep-drive-into-the-sitecore-client-pipelines" title="Sitecore - Deep drive into the Sitecore Client pipelines" target="_blank">Sitecore - Deep drive into the Sitecore Client pipelines</a> </strong> from <strong><a href="//www.slideshare.net/BenjaminVangansewink" target="_blank">Benjamin Vangansewinkel</a></strong> </div>Vangansewinkel Benjaminhttp://www.blogger.com/profile/01696678283653731912noreply@blogger.com1tag:blogger.com,1999:blog-630324887720106786.post-61216344919763428392015-11-16T13:22:00.001+01:002015-11-16T13:22:58.543+01:00Customize the ExperienceEditor Part 3 - Accessing to the registry from the javascriptHere is my other post of the same series about the customization of the client without modifying the Sitecore JS:
<br />
<ul>
<li><a href="http://sitecoreblog.blogspot.be/2015/11/customize-experienceeditor-part-1.html" target="_blank">Customize the ExperienceEditor Part 1 - Include your custom JS</a></li>
<li><a href="http://sitecoreblog.blogspot.be/2015/11/customize-experienceeditor-part-2.html" target="_blank">Customize the ExperienceEditor Part 2 - Override the Sitecore functions in the JS</a></li>
<li><a href="http://sitecoreblog.blogspot.be/2015/11/customize-experienceeditor-part-3.html" target="_blank">Customize the ExperienceEditor Part 3 - Accessing to the registry from the javascript</a></li>
</ul>
<div>
If you need to save boolean into the registry like the collapsed state of an element to keep the content editor's preferences you can do:</div>
<div>
<br /></div>
<div>
<h3>
Retreive the current state:</h3>
<pre class="javascript" name="code">Sitecore.ExperienceEditor.PipelinesUtil.generateRequestProcessor("ExperienceEditor.ToggleRegistryKey.Get",
function (response) {
mystate = response.responseValue.value;
},{ value: "/Current_User/Page Editor/MyModule/MyValue" }).execute();
</pre>
Where
<br />
<ul>
<li>function(response) is the function executed when you will receive the response.</li>
<li>response.responseValue.value is a boolean with the returned value. The default value of this registry is true (stored as "on" in the registry)</li>
<li>MyRegistryKey.IsCollapsed is the a unique key in the registry for the user.</li>
</ul>
</div>
<div>
<h3>
Toggle the value when the user collapse the control for example</h3>
<pre class="javascript" name="code">Sitecore.ExperienceEditor.PipelinesUtil.generateRequestProcessor("ExperienceEditor.ToggleRegistryKey.Toggle",
function (response) {
top.sidebar.isCollapsed = response.responseValue.value;
}, { value: "/Current_User/Page Editor/MyModule/MyValue" }).execute();
</pre>
</div>
<div>
You can use the same method to call the different requests commands from the Sitecore config node sitecore.experienceeditor.speak.requests who are stored by default in Sitecore.ExperienceEditor.Speak.Requests.config
</div>
Vangansewinkel Benjaminhttp://www.blogger.com/profile/01696678283653731912noreply@blogger.com1tag:blogger.com,1999:blog-630324887720106786.post-56187591133779033872015-11-12T12:08:00.000+01:002015-11-12T12:08:35.469+01:00Install your packages in command lineSome times ago I had publish a series of post about the automatisation of the deployment process.
In this build process I install my packages in command line. I have finally rewrite and release this tool. It is now available on <a href="https://marketplace.sitecore.net/Modules/P/Package_Installer.aspx?sc_lang=en" target="_blank">the marketplace</a> and <a href="https://github.com/VGBenjamin/PackageInstaller" target="_blank">github</a>.<br />
<h2>
The project</h2>
This project allow you to install the Sitecore packages files in command line. It support both the .zip and .update files.<br />
If you like this project you can visit and let a comment on my blog: http://sitecoreblog.blogspot.be/<br />
<br />
<h2>
Usage</h2>
<h3>
Install</h3>
You need to download and extract the PackageInstaller zip file or build the source.<br />
<b><u>Arguments</u></b><br />
-p, --packagePath=PACKAGE PATH The PACKAGE PATH is the path to the package. The package must be located in a folder reachable by the web server.<br />
<br />
-u, --sitecoreUrl=SITECORE URL The SITECORE URL is the url to the root of the Sitecore server.<br />
<br />
-f, --sitecoreDeployFolder=SITECORE DEPLOY FOLDER The SITECORE DEPLOY FOLDER is the UNC path to the Sitecore web root.<br />
<br />
-c, --connector=INSTALLATON MODE The INSTALLATON MODE could be tds or sitecore.<br />
<br />
--pb, --publish Publish some items.<br />
<br />
--pbc, --publishChildrenItems Publish the children items also. Need to be use with the -publish option. If you don't specify this flag you need to specify the paramter - publishRootItem<br />
<br />
--pbsdb, --publishSourceDb=VALUE The source database to publish from (master if ommited). Need to be use with the -publish option.<br />
<br />
--pbtdb, --publishTargetDb=VALUE The target database to publish to (web if ommited). Need to be use with the -publish option.<br />
<br />
--pbl, --publishLanguage=VALUE The language to publish (all if ommited). Need to be use with the -publish option.<br />
<br />
--pbi, --publishRootItem=VALUE The root item to publish (all if ommited). Need to be use with the -publish option.<br />
<br />
--pbm, --publishMode=VALUE The publish mode must be one of those values: Full, Incremental, SingleItem, Smart (Full if ommited). Need to be use with the -publish option.<br />
<br />
--pbt, --publishTargets=VALUE The publish target separated by a coma if multiple targets. Need to be use with the - publish option.<br />
<br />
-h, --help Show this message and exit.<br />
<br />
--rc, --removeconnector Remove the conenctor after the installation. it will remodify the bin folder so you should consider to let the conenctor for a better performance. --ssl Accept the self registered ssl certificate<br />
<br />
<b><u>Usage Examples</u></b><br />
<b>Install a sitecore package</b><br />
Sidewalk.SC.PackageInstaller.Client.exe -sitecoreUrl "http://sc72rev140228" -sitecoreDeployFolder "C:\inetpub\wwwroot\sc72rev140228\Website" -packagePath "C:\temp\TestPkg.zip" -connector "sitecore"<br />
<br />
<b>Install a TDS package</b><br />
Sidewalk.SC.PackageInstaller.Client.exe -sitecoreUrl "http://sc72rev140228" -sitecoreDeployFolder "C:\inetpub\wwwroot\sc72rev140228\Website" -packagePath "\Examples\TestPackage.TDS.update" -connector "tds"<br />
<h2>
Sources</h2>
The sources are available here: https://github.com/VGBenjamin/PackageInstaller<br />
<h2>
Issues</h2>
If you have some issues please open a ticket on <a href="https://github.com/VGBenjamin/PackageInstaller">https://github.com/VGBenjamin/PackageInstaller</a> I will be notified<br />
<br />
<br />
<br />Vangansewinkel Benjaminhttp://www.blogger.com/profile/01696678283653731912noreply@blogger.com0tag:blogger.com,1999:blog-630324887720106786.post-49096096847372925772015-11-09T08:41:00.002+01:002015-11-09T08:42:43.128+01:00Small Tip : Click on elements in ExperienceEditor<div class="separator" style="clear: both; text-align: center;">
<br /></div>
<div class="separator" style="clear: both; text-align: left;">
You probably already had some issues in ExperienceEditor with clickable elements, who are not clickable anymore because Sitecore override the click on this zone. In the following screenshot, we had the problem on a carrousel to click on the left and right arrows.</div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhQ6s6v13jAXwyHy6-VTGqUDVKfHjbJrJ5bBfCCeM1PUOBtS7GzFqWa51QbcLyIVvlYnIYbaGBbTfTwhtzNI5aYhMXih0_LPNPtdqn0BpyLbVHLW5cikmWdtNCNoT0oDIbFS27Djn6-8EE/s1600/carousel-edit.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="264" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhQ6s6v13jAXwyHy6-VTGqUDVKfHjbJrJ5bBfCCeM1PUOBtS7GzFqWa51QbcLyIVvlYnIYbaGBbTfTwhtzNI5aYhMXih0_LPNPtdqn0BpyLbVHLW5cikmWdtNCNoT0oDIbFS27Djn6-8EE/s640/carousel-edit.png" width="640" /></a></div>
<br />
The tip is very simple: just use <b>ctrl+click</b> in place of just clicking the element.Vangansewinkel Benjaminhttp://www.blogger.com/profile/01696678283653731912noreply@blogger.com0tag:blogger.com,1999:blog-630324887720106786.post-31772726835285720962015-11-05T07:51:00.003+01:002015-11-16T13:57:09.123+01:00Customize the ExperienceEditor Part 2 - Override the Sitecore functions in the JSHere is my other posts of the same series about the customization of the Sitecore client without modifying the Sitecore JS:
<br />
<ul>
<li><a href="http://sitecoreblog.blogspot.be/2015/11/customize-experienceeditor-part-1.html" target="_blank">Customize the ExperienceEditor Part 1 - Include your custom JS</a></li>
<li><a href="http://sitecoreblog.blogspot.be/2015/11/customize-experienceeditor-part-2.html" target="_blank">Customize the ExperienceEditor Part 2 - Override the Sitecore functions in the JS</a></li>
</ul>
<div>
Today I will show you how I have inherit and override the javascript function defined by Sitecore with as few side effects as possible.</div>
<div>
You just need to store the prototype of the original Sitecore's function, redefine this prototype and call the original function.<br />
Here is the skeleton if the method of sitecore is:
<br />
<pre class="JScript" name="code">sitecoreObject.prototype.methodName = function(param1) {
//Do stuffs
}</pre>
<br />
This method can be override like this:
<br />
<pre class="JScript" name="code">var originalProcess = sitecoreObject.prototype.methodName;
sitecoreObject.prototype.methodName = function (param1) {
//Do my stuffs
//Call teh original method and apply the correct context to this method
originalProcess.call(this, request, command, name);
};</pre>
<br />
Here is a real life. I will explain the client pipelines in a next post but the following method is responsible of the processing of the different processors:
<br />
<pre class="JScript" name="code">scSitecore.prototype.process = function (request, command, name) {
name = (name == null ? command.command : name);
this.state.pipeline = request.pipeline;
var r;
switch (name) {
//The different case of the different processors names
}
};</pre>
And I need to add my custom processor in it so basically adding another case:
<br />
<pre class="JScript" name="code">var originalProcess = currentScJsWindow.scSitecore.prototype.process;
currentScJsWindow.scSitecore.prototype.process = function (request, command, name) {
var localName = (name == null ? command.command : name);
this.state.pipeline = request.pipeline;
var r;
if (localName === "ShowDialogInSidebar") {
//Do my studd
} else {
//Call the normal handler
originalProcess.call(this, request, command, name);
}
};</pre>
</div>Vangansewinkel Benjaminhttp://www.blogger.com/profile/01696678283653731912noreply@blogger.com0tag:blogger.com,1999:blog-630324887720106786.post-12226298967793943182015-11-04T08:42:00.000+01:002015-11-05T07:53:11.692+01:00Customize the ExperienceEditor Part 1 - Include your custom JSRecently I had to customize the Sitecore ExperienceEditor. It was a very tricky experience mainly because nothing is documented officially and almost nothing on the blogs.<br />
So I will write a series of post about this to help you in this task.<br />
I want to customize the ExperienceEditor but I don't want to touch to the base JS files of Sitecore to have as few side effect as possible and still be able to upgrade to the newer versions.<br />
<br />
Here is the different posts of this serie:<br />
<br />
<ul>
<li><a href="http://sitecoreblog.blogspot.be/2015/11/customize-experienceeditor-part-1.html" target="_blank">Customize the ExperienceEditor Part 1 - Include your custom JS</a></li>
<li><a href="http://sitecoreblog.blogspot.be/2015/11/customize-experienceeditor-part-2.html" target="_blank">Customize the ExperienceEditor Part 2 - Override the Sitecore functions in the JS</a></li>
</ul>
<div>
To include my custom JS file the first thing I have try is add my JS into the Sitecore JS bundle <i>\sitecore\shell\Applications\Page Modes\Ouput</i> it seem that it is possible by overriding the method <i>Sitecore.Web.UI.WebControls.WebEditRibbon.Render()</i> but I didn't want to decompile the code and change the existing functionality.</div>
<div>
My workaround for it is adding a checkbox into the view section of the ribbon. When you add those kind of buttons, you are able to attach a JS to this checkbox.</div>
<div>
To do that:</div>
<div>
<ul>
<li>Open Sitecore Rocks</li>
<li>In the core DB, go to /sitecore/content/Applications/WebEdit/Ribbons/WebEdit/View/Show</li>
<li>Duplicate one of the child item of this node and change the fields you need to control the text</li>
<li>Right click on the item, task design layout (shortcut: ctrl + u) </li>
<li>Click on Add Rendering <div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiCE5_rho6Jell4lEqxQso1_DXvRfac_gxnqHmXVjlZ_BAmVau_RRPvKLshEluWyT1GndJ5VzMDOVdaeMRgUoAuHy6OXNqj8RkLtsh6ANE7TDe0MZdKvYWARg0ltryzOZNvWxXSRZQNx2Q/s1600/2015-11-04_0824_-_AddRendering.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="176" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiCE5_rho6Jell4lEqxQso1_DXvRfac_gxnqHmXVjlZ_BAmVau_RRPvKLshEluWyT1GndJ5VzMDOVdaeMRgUoAuHy6OXNqj8RkLtsh6ANE7TDe0MZdKvYWARg0ltryzOZNvWxXSRZQNx2Q/s640/2015-11-04_0824_-_AddRendering.png" width="640" /></a></div>
</li>
<li>Search and select SmallCheckButton<br /><div style="text-align: left;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgmZvvFJ0S2mHbp57JKvI3DxGUgXe39vgCvtrHiNHXkaOIcJ9qEUJRxpybvLANYNVqy8Aqqlv3-D0dmAF0nJ1Ou8InhhA9L6Z2KcO_F0nOfozZDx-SvMn12fxGplVlL8p_6elv_zQO-zSU/s1600/2015-11-04_0826_-_SmallCheck.png" imageanchor="1" style="clear: left; margin-bottom: 1em; margin-right: 1em; text-align: center;"><img border="0" height="200" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgmZvvFJ0S2mHbp57JKvI3DxGUgXe39vgCvtrHiNHXkaOIcJ9qEUJRxpybvLANYNVqy8Aqqlv3-D0dmAF0nJ1Ou8InhhA9L6Z2KcO_F0nOfozZDx-SvMn12fxGplVlL8p_6elv_zQO-zSU/s640/2015-11-04_0826_-_SmallCheck.png" width="640" /></a></div>
</li>
<li>Edit the properties of you button and set the field. The one who is really interesting to include the JS is the PageCodeScriptFileName<br /><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjjhOifA3PO3xVZ0XDZGX5WOEHnTa2PLKD188u1839Or3-YQEosELMEAMVdyCuYt9zTtTwOwIU5clL-x2Cn3JnShNhg7TmFaymW1yBJBCxTb7lNhbUjAL0qDTOx5C_UYgHkVq2E7x4b46U/s1600/2015-11-04_0828_-_ButtonDetail.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em; text-align: center;"><img border="0" height="640" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjjhOifA3PO3xVZ0XDZGX5WOEHnTa2PLKD188u1839Or3-YQEosELMEAMVdyCuYt9zTtTwOwIU5clL-x2Cn3JnShNhg7TmFaymW1yBJBCxTb7lNhbUjAL0qDTOx5C_UYgHkVq2E7x4b46U/s640/2015-11-04_0828_-_ButtonDetail.png" width="472" /></a></li>
</ul>
<div>
Now you are able to do what you want in the JS. But don't forget that your JS is into the ribbon's iFrame. So if you need to access to the page elements or the Sitecore's javascript objects you shoud prefix by top. Example: top.JQuery("#container")<br />
<br />
In the next post I will explain how to override the Sitecore functions in the JS.<br />
<br /></div>
</div>
Vangansewinkel Benjaminhttp://www.blogger.com/profile/01696678283653731912noreply@blogger.com0tag:blogger.com,1999:blog-630324887720106786.post-39398435349254302102015-08-31T08:00:00.000+02:002015-09-07T07:36:33.406+02:00Setup the Fortis code generation with TDSSetup the Fortis code generation with TDS
<p>I use now <a href="http://fortis.ws/">Fortis</a> to generate my C# classes Models who correspond to the Sitecore Templates. If you don't know Fortis you should read it first: <a href="http://fortis.ws/about/">http://fortis.ws/about/</a>.
It is really valuable to have this kind of strongly typed model in our projects because it improve the productivity and reduce the errors in the code. We had internal solution to generate those models before but the guys of Fortis have really done a great job on it.</p>
<p>The easiest way to generate those class for me is by using TDS. Here is the steps to setup the code generation:
<ol>
<li>Enable the code generation in your TDS project. To do that:
<ol>
<li>Right click on the project, then properties</li>
<li>Check Enable Code Generation</li>
<li>In "Target Project", specify the project where the class will be generated.</li>
<li>In "code generation target file", specify the relative path in the target project where the class will be generated including the class name.</li>
</ol>
</li>
<li>Now you should have a folder named "Code Generation Templates" below your TDS project. Open this folder in windows explorer.</li>
<li>Create a first file named Header.tt and add the following content in it:
<pre name="code" class="csharp"><#@ template language="C#" #>
<#
// The Header Template is used to generate code that goes at the top of the generated code file. This template is executed only once.
// it will typically generate #using statements or add other one time only elements to the generated file.
//Parameters passed to Template for code generation
// Model: The ProjectHeder object contains information about the TDS project generating the code and the project where the
// generated code will reside.
#>
<#@ parameter name="Model" type="HedgehogDevelopment.SitecoreProject.VSIP.CodeGeneration.Models.ProjectHeader" #>
<#
// DefaultNamespace: The DefaultNamespace parameter contains the default namespace of the project where the generated
// code file resides.
#>
<#@ parameter name="DefaultNamespace" type="System.String" #>
<#
/* The project header class:
public class ProjectHeader
{
/// <summary>
/// The name of the TDS project
/// </summary>
public string TDSProjectName { get; set; }
/// <summary>
/// The full path to the TDS project file
/// </summary>
public string TDSProjectPath { get; set; }
/// <summary>
/// The name of the target project
/// </summary>
public string TargetProjectName { get; set; }
/// <summary>
/// The full path to the target project
/// </summary>
public string TargetProjectPath { get; set; }
/// <summary>
/// The full path to the generated project file
/// </summary>
public string GeneratedFilePath { get; set; }
/// <summary>
/// Gets or sets the base namespace as set in the TDS project.
/// </summary>
public string BaseNamespace { get; set; }
}
*/
#>
using System;
using System.Collections.Generic;
using Sitecore.Data.Items;
using Sitecore.ContentSearch;
using Sitecore.ContentSearch.Linq.Common;
using Fortis.Model;
using Fortis.Model.Fields;
using Fortis.Providers;</pre>
</li>
<li>Create a second file in the same directory named, Item.tt with the following content:
<pre name="code" class="csharp"><#@ template language="C#" debug="true" #>
<#@ assembly name="System.Core.dll" #>
<#@ import namespace="System.Collections" #>
<#@ import namespace="System.Collections.Generic" #>
<#@ import namespace="System.Globalization" #>
<#@ import namespace="System.Linq" #>
<#@ import namespace="System.Text" #>
<#@ import namespace="System.Text.RegularExpressions" #>
<#@ import namespace="HedgehogDevelopment.SitecoreProject.VSIP.CodeGeneration.Models" #>
<#
// The Item template is called for every Sitecore item elegible for code generation. TDS will execute this T4 template every time a
// template or field on a template changes in TDS. The T4 template is responsible for generating code for only the Sitecore item TDS
// passes to the template. TDS will join all created templates together to create a single file.
//
// Version 4 of TDS only supports generating code for Sitecore Template items.
// Parameters passed to the T4 Template for code generation
// Model: This parameter contains information about the Sitecore Item to be generated. The Model will always be a type that inherits from SitecoreItem.
#>
<#@ parameter name="Model" type="HedgehogDevelopment.SitecoreProject.VSIP.CodeGeneration.Models.SitecoreItem" #>
<#
// DefaultNamespace: The DefaultNamespace parameter contains the default namespace of the project where the generated
// code file resides.
#>
<#@ parameter name="DefaultNamespace" type="System.String" #>
<#
/* The following types are used during code generation:
/// <summary>
/// Represents the SitecoreItem to be passed to the T4 template. Any object that is a SitecoreItem will inherit from this object.
/// </summary>
public class SitecoreItem
{
/// <summary>
/// The Sitecore item ID.
/// </summary>
public Guid ID { get; set; }
/// <summary>
/// The name of the Sitecore item. This may be different than the Display Name.
/// </summary>
public string Name { get; set; }
/// <summary>
/// The path to the item from the Sitecore root.
/// </summary>
public string Path { get; set; }
/// <summary>
/// Any custom data associated with the item. This data can be set on the property page associated with the Sitecore item in the solution explorer.
/// </summary>
public string Data { get; set; }
/// <summary>
/// The Parent SitecoreItem in the Sitecore hierarchy.
/// </summary>
public SitecoreItem Parent { get; set; }
/// <summary>
/// The name of the template the item is based on
/// </summary>
public string TemplateName { get; set; }
/// <summary>
/// The ID of the template the item is based on
/// </summary>
public Guid TemplateId { get; set; }
/// <summary>
/// Additional sitecore fields. These fields are set on the Code Generation Property page.
/// The key in the dictionary is the Field name, the value is the value of the field.
/// </summary>
public Dictionary<string, string> SitecoreFields;
/// <summary>
/// The calculated Namespace for the item. Each Sitecore item above the template is represented as part of the namespace.
/// A new Namespace can be set at any item in the items property page. This allows the code generation namespace to be arranged differently
/// than the Sitecore template hierarchy.
/// </summary>
public string Namespace { get; set; }
}
/// <summary>
/// Represents Template specific information for code generation.
/// </summary>
public class SitecoreTemplate : SitecoreItem
{
/// <summary>
/// The namespace broken out into individual segments.
/// </summary>
public IEnumerable<string> NamespaceSegments { get; }
/// <summary>
/// A list of all templates this template inherits from.
/// </summary>
public List<SitecoreTemplate> BaseTemplates { get; set; }
/// <summary>
/// A list of Sitecore Fields that make up this sitecore template.
/// </summary>
public List<SitecoreField> Fields { get; set; }
}
/// <summary>
/// Represents Field specific information for code generation.
/// </summary>
public class SitecoreField : SitecoreItem
{
/// <summary>
/// The type of the field from the template editor.
/// </summary>
public string Type { get; set; }
}
*/
#>
<#
SitecoreTemplate template = Model as SitecoreTemplate;
if (template == null)
{
return string.Empty;
}
var baseTemplates = template.BaseTemplates;
var baseTemplatesRecursive = RecursiveBaseTemplateList(template);
var combinedTemplateList = new List<SitecoreTemplate>(baseTemplatesRecursive);
combinedTemplateList.Add(template);
#>
#region <#= template.Name #> (<#= RelativeNamespace(template) #>)
namespace <#= FullNamespace(template) #>
{
<#
var isRenderingParametersTemplate = HasRenderingOptionsBase(combinedTemplateList);
if (isRenderingParametersTemplate)
{
#>
using Fortis.Model.RenderingParameters;
<#
}
#>
/// <summary>
/// <para>Template interface</para>
/// <para>Template: <#= template.Name #></para>
/// <para>ID: <#= template.ID.ToString("b").ToUpper() #></para>
/// <para><#= template.Path #></para>
/// </summary>
[TemplateMapping("<#= template.ID.ToString("b").ToUpper() #>", "<#= GetTemplateMappingType(isRenderingParametersTemplate, true) #>")]
public partial interface <#= InterfaceName(template.Name) #> : <#= GetBaseTemplateInterface(isRenderingParametersTemplate) #> <#
foreach (var baseTemplate in baseTemplates)
{
#>, <#= FullNamespace(baseTemplate) + "." + InterfaceName(baseTemplate.Name) #><#
}
#>
{
<#
foreach(var field in template.Fields)
{
#>
/// <summary>
/// <para>Template: <#= template.Name #></para><para>Field: <#= TitleCase(field.Name) #></para><para>Data type: <#= field.Type #></para>
/// </summary>
<# if (!isRenderingParametersTemplate && IsSupportedSearchFieldType(field.Type)) { #>
[IndexField("<#= field.Name.Replace(" ", "_").ToLowerInvariant() + GetFieldTypeSearchAffix(field.Type) #>")]
<# } #>
<#= GetFieldWrapperTypeInterface(field.Type) #> <#= TitleCase(field.Name) #> { get; }
<# if (!isRenderingParametersTemplate && IsSupportedSearchFieldType(field.Type)) { #>
/// <summary>
/// <para>Template: <#= template.Name #></para><para>Field: <#= TitleCase(field.Name) #></para><para>Data type: <#= field.Type #></para>
/// </summary>
[IndexField("<#= field.Name.Replace(" ", "_").ToLowerInvariant() #>")]
<# } #>
<#= GetReturnType(GetFieldWrapperType(field.Type)) #> <#= TitleCase(field.Name) #>Value { get; }
<#
}
#>
}
/// <summary>
/// <para>Template class</para><para><#= template.Path #></para>
/// </summary>
[PredefinedQuery("TemplateId", ComparisonType.Equal, "<#= template.ID.ToString("b").ToUpper() #>", typeof(Guid))]
[TemplateMapping("<#= template.ID.ToString("b").ToUpper() #>", "<#= GetTemplateMappingType(isRenderingParametersTemplate, false) #>")]
public partial class <#= ClassName(template.Name) #> : <#= GetBaseTemplateClass(isRenderingParametersTemplate) #>, <#= InterfaceName(template.Name) #>
{
<# if (!isRenderingParametersTemplate) { #>
private Item _item = null;
public <#= ClassName(template.Name) #>(ISpawnProvider spawnProvider) : base(null, spawnProvider) { }
public <#= ClassName(template.Name) #>(Guid id, ISpawnProvider spawnProvider) : base(id, spawnProvider) { }
public <#= ClassName(template.Name) #>(Guid id, Dictionary<string, object> lazyFields, ISpawnProvider spawnProvider) : base(id, lazyFields, spawnProvider) { }
<# } #>
public <#= ClassName(template.Name) #>(<#= GetConstructorParameters(isRenderingParametersTemplate) #>, ISpawnProvider spawnProvider) : base(<#= GetBaseConstructorParameters(isRenderingParametersTemplate) #>, spawnProvider)
{
<# if (!isRenderingParametersTemplate) { #>
_item = item;
<# } #>
}
<#
foreach(var fieldTemplate in combinedTemplateList)
{
foreach(var field in fieldTemplate.Fields)
{
#>
public const string <#= TitleCase(field.Name) #>FieldName = "<#= field.Name #>";
/// <summary><para>Template: <#= template.Name #></para><para>Field: <#= TitleCase(field.Name) #></para><para>Data type: <#= field.Type #></para></summary>
<# if (!isRenderingParametersTemplate && IsSupportedSearchFieldType(field.Type)) { #>
[IndexField("<#= field.Name.Replace(" ", "_").ToLowerInvariant() + GetFieldTypeSearchAffix(field.Type) #>")]
<# } #>
public virtual <#= GetFieldWrapperTypeInterface(field.Type) #> <#= TitleCase(field.Name) #>
{
<# if (isRenderingParametersTemplate) { #>
get { return (Fortis.Model.RenderingParameters.Fields.<#= GetFieldWrapperType(field.Type) #>)GetField("<#= field.Name #>", "<#= field.Type.ToLower() #>"); }
<# } else { #>
get { return GetField<<#= GetFieldWrapperType(field.Type) #>>("<#= field.Name #>"<# if (IsSupportedSearchFieldType(field.Type)) { #>, "<#= field.Name.Replace(" ", "_").ToLowerInvariant() + GetFieldTypeSearchAffix(field.Type) #>"<# } #>); }
<# } #>
}
/// <summary><para>Template: <#= template.Name #></para><para>Field: <#= TitleCase(field.Name) #></para><para>Data type: <#= field.Type #></para></summary>
<# if (!isRenderingParametersTemplate && IsSupportedSearchFieldType(field.Type)) { #>
[IndexField("<#= field.Name.Replace(" ", "_").ToLowerInvariant() #>")]
<# } #>
public <#= GetReturnType(GetFieldWrapperType(field.Type)) #> <#= TitleCase(field.Name) #>Value
{
get { return <#= TitleCase(field.Name) #>.Value; }
}
<#
}
}
#>
}
}
#endregion
<#+
private const string SitecoreSystemTemplatePath = "/sitecore/templates/System/";
private const string ClientTemplatePath = "/sitecore/templates/User Defined/";
public string GetBaseTemplateInterface(bool isRenderingParametersTemplate)
{
return InterfaceName(GetBaseTemplateClass(isRenderingParametersTemplate));
}
public string GetBaseTemplateClass(bool isRenderingParametersTemplate)
{
return isRenderingParametersTemplate ? "RenderingParameterWrapper" : "CustomItemWrapper";
}
public string GetConstructorParameters(bool isRenderingParametersTemplate)
{
return (isRenderingParametersTemplate ? "Dictionary<string, string> " : "Item ") + GetBaseConstructorParameters(isRenderingParametersTemplate);
}
public string GetBaseConstructorParameters(bool isRenderingParametersTemplate)
{
return isRenderingParametersTemplate ? "parameters" : "item";
}
public string GetTemplateMappingType(bool isRenderingParametersTemplate, bool isInterface)
{
return (isInterface ? "Interface" : string.Empty) + (isRenderingParametersTemplate ? "RenderingParameter" : isInterface ? "Map" : string.Empty);
}
public string GetReturnType(string fieldType)
{
switch (fieldType)
{
case "BooleanFieldWrapper":
case "IBooleanFieldWrapper":
return "bool";
case "DateTimeFieldWrapper":
case "IDateTimeFieldWrapper":
return "DateTime";
case "ListFieldWrapper":
case "IListFieldWrapper":
return "IEnumerable<Guid>";
case "IntegerFieldWrapper":
case "IIntegerFieldWrapper":
return "long";
case "NumberFieldWrapper":
case "INumberFieldWrapper":
return "float";
default:
return "string";
}
}
public bool IsSupportedSearchFieldType(string typeName)
{
switch (typeName.ToLower())
{
case "checkbox":
case "date":
case "datetime":
case "checklist":
case "treelist":
case "treelist with search":
case "treelistex":
case "multilist":
case "multilist with search":
case "droplink":
case "droptree":
case "general link":
case "general link with search":
case "text":
case "single-line text":
case "multi-line text":
case "rich text":
case "number":
case "integer":
case "tags":
return true;
default:
return false;
}
}
public string GetFieldTypeSearchAffix(string typeName)
{
var affix = "FieldWrapper";
switch (typeName.ToLower())
{
case "checkbox":
affix = "_b";
break;
case "date":
case "datetime":
affix = "_tdt";
break;
case "checklist":
case "treelist":
case "treelist with search":
case "treelistex":
case "multilist":
case "multilist with search":
case "tags":
affix = "_sm";
break;
case "droplink":
case "droptree":
affix = "_s";
break;
case "general link":
case "general link with search":
case "text":
case "single-line text":
case "multi-line text":
case "rich text":
affix = "_t";
break;
case "number":
affix = "_tf";
break;
case "integer":
affix = "_tl";
break;
default:
throw new Exception("No mapping for " + typeName);
}
return affix;
}
public string GetFieldWrapperTypeInterface(string typeName)
{
return "I" + GetFieldWrapperType(typeName);
}
public string GetFieldWrapperType(string typeName)
{
var wrapperType = "FieldWrapper";
switch (typeName.ToLower())
{
case "checkbox":
wrapperType = "BooleanFieldWrapper";
break;
case "image":
wrapperType = "ImageFieldWrapper";
break;
case "file":
wrapperType = "FileFieldWrapper";
break;
case "date":
case "datetime":
wrapperType = "DateTimeFieldWrapper";
break;
case "checklist":
case "treelist":
case "treelist with search":
case "treelistex":
case "multilist":
case "multilist with search":
case "tags":
wrapperType = "ListFieldWrapper";
break;
case "droplink":
case "droptree":
wrapperType = "LinkFieldWrapper";
break;
case "general link":
case "general link with search":
wrapperType = "GeneralLinkFieldWrapper";
break;
case "text":
case "single-line text":
case "multi-line text":
wrapperType = "TextFieldWrapper";
break;
case "rich text":
wrapperType = "RichTextFieldWrapper";
break;
case "number":
wrapperType = "NumberFieldWrapper";
break;
case "integer":
wrapperType = "IntegerFieldWrapper";
break;
default:
wrapperType = "TextFieldWrapper";
break;
}
return wrapperType;
}
public string ClassName(string name)
{
return TitleCase(name);
}
public string InterfaceName(string name)
{
return "I" + TitleCase(name);
}
public string TitleCase(string name)
{
name = Regex.Replace(name, "([a-z](?=[A-Z])|[A-Z](?=[A-Z][a-z]))", "$1 ");
name = CultureInfo.InvariantCulture.TextInfo.ToTitleCase(name);
name = Regex.Replace(name, @"[^a-zA-Z0-9]", String.Empty);
var firstChar = 0;
if (int.TryParse(name.Substring(0, 1), out firstChar))
{
var numberToWord = string.Empty;
switch(firstChar)
{
case 0:
numberToWord = "Zero";
break;
case 1:
numberToWord = "One";
break;
case 2:
numberToWord = "Two";
break;
case 3:
numberToWord = "Three";
break;
case 4:
numberToWord = "Four";
break;
case 5:
numberToWord = "Five";
break;
case 6:
numberToWord = "Six";
break;
case 7:
numberToWord = "Seven";
break;
case 8:
numberToWord = "Eight";
break;
case 9:
numberToWord = "Nine";
break;
}
name = numberToWord + name.Remove(0, 1);
}
return name;
}
public string RelativeNamespace(SitecoreTemplate template)
{
var relativeNamespace = string.Empty;
if (template.Path.StartsWith(SitecoreSystemTemplatePath))
{
relativeNamespace = "ScSystem";
}
else if (template.Path.StartsWith(ClientTemplatePath))
{
var paths = template.Path.Replace(ClientTemplatePath, string.Empty).Split('/');
//relativeNamespace = TitleCase(paths[0]);
relativeNamespace = "UserDefined";
}
else
{
relativeNamespace = "Custom";
}
return relativeNamespace;
}
public string FullNamespace(SitecoreTemplate template)
{
return DefaultNamespace + ".Templates." + RelativeNamespace(template);
}
public IEnumerable<SitecoreTemplate> RecursiveBaseTemplateList(SitecoreTemplate template)
{
var list = new List<SitecoreTemplate>();
if (template == null || template.BaseTemplates == null)
{
return list;
}
foreach (var baseTemplate in template.BaseTemplates)
{
if (!list.Any(t => t.ID == baseTemplate.ID))
{
list.Add(baseTemplate);
}
foreach (var innerBaseTemplate in RecursiveBaseTemplateList(baseTemplate))
{
if (!list.Any(t => t.ID == innerBaseTemplate.ID))
{
list.Add(innerBaseTemplate);
}
}
}
return list;
}
public bool HasRenderingOptionsBase(IEnumerable<SitecoreTemplate> templateItems)
{
var renderingParameterTemplateId = "8CA06D6A-B353-44E8-BC31-B528C7306971".ToLower();
return templateItems.Any(t => t.ID.ToString() == renderingParameterTemplateId);
}
#></pre></li>
<li><u>Remark:</u> Those two files come from <a href="https://github.com/Fortis-Collection/fortis/tree/master/TDS/Code%20Generation%20Templates">https://github.com/Fortis-Collection/fortis/tree/master/TDS/Code%20Generation%20Templates</a> but I have improve 2-3 stuffs. For example, in the github file the rendering parameters method doesn't work because the standard rendering parameter Guid is not the correct one, I have moved the namespaces to have it only once, ...</li>
<li>In Visual Studio, below the TDS project, right click on Code Generation Templates | Add | Existing Item... and select the two tt files.</li>
<li>Now, return into the TDS project properties and set the Header Transorm file and the Base Transform File to the two tt files.</li>
</ol>
</p>
<p><b>If you need some rendering parameters you will see that they will not be detected as rendering parameters. To fix it you just have to include the template: \sitecore\templates\System\Layout\Rendering Parameters\Standard Rendering Parameters in TDS. Of course you can set the property "Code Generation Template" to none if you don't want to generate the class for this one but at least it will fix your issue.</b></p>
Vangansewinkel Benjaminhttp://www.blogger.com/profile/01696678283653731912noreply@blogger.com0tag:blogger.com,1999:blog-630324887720106786.post-14209812468387377042015-05-05T08:51:00.005+02:002015-05-05T08:51:55.647+02:00Activate the debugger in the third party dllsAll the advanced dotnet developers has already been frustrated because we are not able to debug step by step in the third party dlls. Of course you have tools like ILSpy or reflector to see the decompiled code but sometime it isn't enough to understand what really happens.<br />
With a few manipulation you will be able to debug it and even (most of the time) have the watch values<br />
<br />
<ul>
<li>Download and install <a href="https://www.jetbrains.com/decompiler/">dotpeek</a></li>
<li>
In visual studio, click on tools, options <div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhP0ZJm8Q8CfQZkoA0jKNT2NEExta1_8JKo-koU5RcNP3qrdzXtJObAtsfB-vczffRKgvrW6znd6238madRp6uhJYRfdmP3SNIGF9V2iz7DoA4v-yz8r0g07Dm8z19w8AUSPcIh6OXN3Mo/s1600/1-Options.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="298" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhP0ZJm8Q8CfQZkoA0jKNT2NEExta1_8JKo-koU5RcNP3qrdzXtJObAtsfB-vczffRKgvrW6znd6238madRp6uhJYRfdmP3SNIGF9V2iz7DoA4v-yz8r0g07Dm8z19w8AUSPcIh6OXN3Mo/s400/1-Options.png" width="400" /></a></div>
<ul>
<li>In "debugging", unckeck the box "Enable just my code" <div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiYB7GMlyLhoNgPPdtBEtj7lJ0058ilBJfOQqYBcOTbWgQku6_gu-_VwyTgvQNc9Xtng2Cl0zXDVY9ndk9FUgAifdZRKbO7uEMob-CsFiVXwIHwm6X3NrmxB6mghegDr0Xr3LyCdcFhnjs/s1600/2-enableJustMyCode.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="288" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiYB7GMlyLhoNgPPdtBEtj7lJ0058ilBJfOQqYBcOTbWgQku6_gu-_VwyTgvQNc9Xtng2Cl0zXDVY9ndk9FUgAifdZRKbO7uEMob-CsFiVXwIHwm6X3NrmxB6mghegDr0Xr3LyCdcFhnjs/s640/2-enableJustMyCode.png" width="640" /></a></div>
</li>
<li>In "Debugging" - "Symbols", note the value of the field "Cache symbols in the directory" for the next step <div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhyVxLl0Ut6ywbcleLtXtHi2RWbW3_5-0wMD74Wa9ItfLzsndesw7RpfYBC6AmwACtKQJXiInN5_s24eaKKGyQJCWMqWwYO-11k6J2_7q0iDrv8PnaDlsuec3DTDIGxh5G0QRGV69zPkwk/s1600/3-SymbolDir.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="288" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhyVxLl0Ut6ywbcleLtXtHi2RWbW3_5-0wMD74Wa9ItfLzsndesw7RpfYBC6AmwACtKQJXiInN5_s24eaKKGyQJCWMqWwYO-11k6J2_7q0iDrv8PnaDlsuec3DTDIGxh5G0QRGV69zPkwk/s640/3-SymbolDir.png" width="640" /></a></div>
</li>
</ul>
</li>
<li>In dotPeek:
<ul>
<li>Drag'n drop the assembly into the "Assembly Explorer"</li>
<li>Right click on the assembly and click on "generate pdb" <div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhHbEGX0T_mRseMi8MTr9tSqeDb03EXZQYh0hqpxojP3JPyC2rG8AHMgLt0FfIJRudPQg3LiVG8JzcuzoLGtkkBLD2aLc0TLv7qca7kiEvp7lrNCWnL6VwZgdTxpd7DiLc2Al8zC0UzdKA/s1600/4-generate-pdb.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="248" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhHbEGX0T_mRseMi8MTr9tSqeDb03EXZQYh0hqpxojP3JPyC2rG8AHMgLt0FfIJRudPQg3LiVG8JzcuzoLGtkkBLD2aLc0TLv7qca7kiEvp7lrNCWnL6VwZgdTxpd7DiLc2Al8zC0UzdKA/s400/4-generate-pdb.png" width="400" /></a></div>
</li>
<li>In the field "Destination folder", past the value of the "Cache symbols in the directory" from the previous step and click on generate.</li>
</ul>
</li>
</ul>
If you stop now you should be able to attach to your process, step into the decompiled dll. But you will be not able to see the values of the variables. <br />
<ul>
<li>
Two options to start visual studio in the correct mode:
<ul>
<li>To start it once type the following lines into a cmd prompt:<pre class="bash" name="code">set COMPLUS_ZapDisable=1
cd /d C:\Program Files (x86)\Microsoft Visual Studio 12.0\Common7\IDE
start devenv.exe
exit</pre>
</li>
<li>To set it permanently set this in the registry:<pre class="bash" name="code">HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Session Manager\Environment
Add Key: COMPLUS_ZAPDISABLE
Type: REG_SZ (String)
Value: 1</pre>
</li>
</ul>
</li>
<li>You also need to disable the JIT optimization for the dll. To do that, create a .ini file with the same name as the dll on the same place (in your bin folder). Example for Sitecore.Kernel.dll, the name must be: Sitecore.Kernel.ini. Set the content of the file to:
<pre class="bash" name="code">[.NET Framework Debugging Control]
GenerateTrackingInfo=1
AllowOptimize=0</pre>
</li>
</ul>
Now you should be able to add the watch on the variables. <br /><br />
Enjoy!
Vangansewinkel Benjaminhttp://www.blogger.com/profile/01696678283653731912noreply@blogger.com1tag:blogger.com,1999:blog-630324887720106786.post-29680430934317616382015-02-26T08:31:00.001+01:002015-02-26T08:31:36.212+01:00Reading the the Windows Azure Diagnostics TableThis article is not specific to Sitecore but with Sitecore Azure you have the possibility to store your logs into what they call WAD Tables.
To read it you have multiple choices:
<br />
<ul>
<li>The <a href="https://azurestorageexplorer.codeplex.com/">Azure Storage Explorer</a> but when your logs are too big you will just have a OutOfMemeryException</li>
<li>The <a href="https://marketplace.sitecore.net/en/Modules/Sitecore_Log_Analyzer.aspx">SCLA (Sitecore Log Analyser)</a> you need to uncomment something in the web.config to activate the Azure possibility. But again when you will have big logs it will be not usable anymore.</li>
<li>Visual Studio. If you go to the server explorer, then on Azure, then Storage, Tables, and click on the WADLogsTable. As a limitation you will not be able to retrieve more than 10000 records.</li>
<li>This <a href="http://www.linqpad.net/">LinqPad</a> with the <a href="http://www.linqpad.net/richclient/datacontextdrivers.aspx">Azure Table Storage Driver</a>. I will explain this possibility more in details below.</li>
</ul>
When those table grow up it become just impossible to execute some custom requests on the custom fields. In fact, they are just two fields who stay queryable: PartitionKey and RowKey.
But of course for the sitecore logs what you need to retrieve is all the events during a certain time range. The interesting thing about the PartitionKey is the fact that you can match a datetime to this field.
You just have to do the following code to retrieve the value of those values:
<br />
<pre class="csharp" name="code">string.Format("0{0}", (new DateTime(2015, 02, 26)).Ticks)</pre>
If you want more details the following article explain it in details: <a href="http://gauravmantri.com/2012/02/17/effective-way-of-fetching-diagnostics-data-from-windows-azure-diagnostics-table-hint-use-partitionkey/">http://gauravmantri.com/2012/02/17/effective-way-of-fetching-diagnostics-data-from-windows-azure-diagnostics-table-hint-use-partitionkey/</a>
By using this trick you are able to build and execute a query on the PartitionKey by using Visual Studio but, as I said, you are limited to 10000 records max.<br />
If you need to retrieve all the records of a single day one of the solution is LinqPad. To be able to query those table you need to:
<br />
<ul>
<li>Download <a href="http://www.linqpad.net/richclient/datacontextdrivers.aspx">Azure Table Storage Driver</a></li>
<li>Install this driver by:
<ul>
<li>Open LinqPad</li>
<li>Click on "Add connection"</li>
<li>Click on "View more drivers..."</li>
<li>Click on browse and select your lpx file</li>
<li>Click on close</li>
</ul>
</li>
</ul>
If you switch LinqPad in "c# Statement(s)" mode in the language dropdown. you are able to write query like this one:
<br />
<pre class="csharp" name="code">(from l in WADLogsTable
where l.PartitionKey.CompareTo(new DateTime(2015, 02, 13, 0,0,0).Ticks.ToString("d19")) > 0
&& l.PartitionKey.CompareTo(new DateTime(2015, 02, 14, 0, 0, 0).Ticks.ToString("d19")) < 0
select new
{
DateTime = new DateTime(l.EventTickCount.Value),
l.Message,
l.Level
}).ToList().Dump();</pre>
But as you see LinqPad is also limited to 1000 records because of the azure storage api. the workaround for it is to execute the request hour by hour in a loop like this:
<br />
<pre class="csharp" name="code">for(int i = 0; i < 24; i++) {
(from l in WADLogsTable
where l.PartitionKey.CompareTo(new DateTime(2015, 02, 13, i,0,0).Ticks.ToString("d19")) > 0
&& l.PartitionKey.CompareTo(new DateTime(2015, 02, 13, i+1, 0, 0).Ticks.ToString("d19")) < 0
select new
{
DateTime = new DateTime(l.EventTickCount.Value),
l.Message,
l.Level
}).ToList().Dump();
}</pre>
Vangansewinkel Benjaminhttp://www.blogger.com/profile/01696678283653731912noreply@blogger.com0tag:blogger.com,1999:blog-630324887720106786.post-91001494168618084192014-12-03T08:20:00.000+01:002014-12-05T07:36:48.673+01:00Add a custom section in the content editorHere the procedure to add a custom section after the Quick Info section in the content editor. Like this:<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg8VdMWI6TAAV9fuAg9T1Uun2A_3gDGTLhBya6wVhUj8wzs7LckbB_ARIICNpq562W_rQhLChPUk9WyqZAUbxigCCFx1C6QPv8ke_xRYgYb8dlhHnJKIaGLBNDLgpkrcmZMzqrF8aSDv_A/s1600/custom_Section.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg8VdMWI6TAAV9fuAg9T1Uun2A_3gDGTLhBya6wVhUj8wzs7LckbB_ARIICNpq562W_rQhLChPUk9WyqZAUbxigCCFx1C6QPv8ke_xRYgYb8dlhHnJKIaGLBNDLgpkrcmZMzqrF8aSDv_A/s1600/custom_Section.png" height="354" width="640" /></a></div>
<br />
<ul>
<li>Create a new class like this:
<pre class="csharp" name="code">using Sitecore.Shell.Applications.ContentEditor.Pipelines.RenderContentEditor;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace SitecoreTestCustomizeContentEditor
{
public class TestSection
{
public void Process(RenderContentEditorArgs args)
{
args.EditorFormatter.RenderSectionBegin(args.Parent, "CustomInfo", "CustomInfo", "CustomInfo", "Applications/16x16/information.png", true, true);
args.EditorFormatter.AddLiteralControl(args.Parent, "<div>test value</div>");
args.EditorFormatter.RenderSectionEnd(args.Parent, true, true);
}
}
}</pre>
</li>
<li>Deploy this class in your bin folder</li>
<li>Add a pipeline into web.config in the renderContentEditor section like this
<pre class="xml" name="code"><renderContentEditor>
<processor type="SitecoreTestCustomizeContentEditor.TestSection, SitecoreTestCustomizeContentEditor" />
<processor type="Sitecore.Shell.Applications.ContentEditor.Pipelines.RenderContentEditor.RenderSkinedContentEditor, Sitecore.Client" />
<processor type="Sitecore.Shell.Applications.ContentEditor.Pipelines.RenderContentEditor.RenderStandardContentEditor, Sitecore.Client" />
</renderContentEditor></pre>
</li>
</ul>
Vangansewinkel Benjaminhttp://www.blogger.com/profile/01696678283653731912noreply@blogger.com5tag:blogger.com,1999:blog-630324887720106786.post-69403311012438049082014-10-22T06:55:00.002+02:002014-10-22T07:19:02.550+02:00New on the marketplace : Image Optimizer V2<p>I have just release a new module on the marketplace today: <a href="https://marketplace.sitecore.net/en/Modules/Image_optimizer_V2.aspx">Image Optimizer V2</a></p><br />
<h3>History</h3>
<p>The first version has been developed by <b>MIKAEL HÖGBERG</b> is available <a href="https://marketplace.sitecore.net/en/Modules/ImageOptimizer.aspx">here</a>.
Here is his blog post about it: <a href="http://mikael.com/2013/08/image-optimizer-module/">http://mikael.com/2013/08/image-optimizer-module/</a>
A very big thank you for this module!</p><br />
<h3>
New features</h3>
<p>This version 2 is now based on jpegtran and pngquant to compress the pictures but you can use your favorite tool if you prefer.
The biggest addition of this module is a wizard similar to the publishing wizard which allow you to compress a complete folder in place of compressing picture per picture. </p><br />
<h3>
Installation</h3>
<p><ul>
<li>Install the packages with the /sitecore/admin/UpdateInstallationWizard.aspx</li>
<li>Download pngquant here: <a href="http://pngquant.org/">http://pngquant.org/</a> under the section "Command Line" / "Binary for windows" and save this file in the root of your data folder.</li>
<li>Download jpegtran here: http://jpegclub.org/jpegtran.exe and save this file in the root of your data folder.</li>
</ul></p>
<h3>
Advanced configurations</h3>
<p>The configuration file is available in _App_Config\Include\ImageOptimizer.config. The following setting are available
<br />
<ul>
<li>imageOptimizer.pngCommand: The path to the png commande line tool for the png</li>
<li>imageOptimizer.pngOptions: The command line arguments</li>
<li>imageOptimizer.jpgCommand: The path to the png commande line tool for the jpg</li>
<li>imageOptimizer.jpgOptions: The command line arguments</li>
<li>imageOptimizer.PoolingInterval: The pool interval for the wizard you shouldn't change it</li>
</ul></p>
Vangansewinkel Benjaminhttp://www.blogger.com/profile/01696678283653731912noreply@blogger.com4tag:blogger.com,1999:blog-630324887720106786.post-32841074927938536562014-09-17T11:15:00.001+02:002014-09-17T11:15:35.985+02:00Sitecore symposium 2014 - BarcelonaFor those who didn't have the chance to go the the Sitecore symposium at Barcelona this year, I will send some info, previews, ...<br />
<br />
<h2>
Day 1 - Session 1 : Keynote</h2>
In the keynote we have mainly seen the new features on the future Sitecore 8. Some screenshots of the Sitecore 8 will be uploaded when I will be back for the symposium. The good news are:
<br />
<ul>
<li>Sitecore 7.5 will be release in about 2 week</li>
<li>Sitecore 8 should be release before the end of the year</li>
</ul>
I can already have multiple conclusion about this first sessions:
<br />
<ul>
<li>The speak UI is more living than ever. Most ot the applications are or will be rewrites using this new "language"</li>
<li>After the login you are redirected to the Speak Dashboard</li>
<li>It is amazing to see how far they go with this new personalization and content testing</li>
</ul>
<br />
<br />
I will be honest, I was not very enthusiast when I saw the program of this year. But after the keynote. I think that it will be really exciting finally.<br />
<h2>
Day 1 - Session 2: Why did we do that?</h2>
This session was mainly an history of the Sitecore developments. They also have announced a new partnership with Coveo. Coveo is a new search available in Sitecore and the great thing here is that they are a free version for Sitecore 7. The non free version include some additional stuffs like the possibility for the content editor to change the filters, the boosts, ...
The possibility to integrate some external datasource and the advanced configuration. After a small visit to they stand I really thing that I will investigate this product.<br />
<h2>
Day 1 - Session 3: Visit the Emerald City: Understanding the Sitecore architecture</h2>
The main things here we have learn are:
<br />
<ul>
<li>Sitecore 7.5+ : The new architecture for the analytics data collection. In summary, you collect all the data into the Mongo DB. Then those data are process by some pipeline to inject it into the reporting database. When a data need to be retreive the service layer decide if he need to take it from the mongo db when it is a personal information like the identity card of a visitor or for the reporting db if it is some into about the a group of user. </li>
<li>Sitecore 7.2+: The new publishing process is now multi-thread </li>
</ul>
The rest was mainly some basic architecture stuffs like the pipelines, ...<br />
,
<br />
<h2>
Day 1 - Session 4 : Compiling, personalized Sitecore StoreFronts with any commerce platform</h2>
First coding session! <br />A session who explain how to customize the pipelines and the SPEAK applications to add a new section into the "events" tab of the user identity card. It was an interesting session to have a quick overview of the SPEAK framework and a look at the eCommerce API. <br />
The thing I will remember about this session is the fact that the dashboard that Sitecore provide for the User Experience are not static at all and really me to be modified by the developers. If we need to track some custom events and have a view on it.<br />
<h2>
Day 1 - Session 5 : Our princess is in another castle_ Tracking activity on non-Sitecore sites</h2>
Definitivelly the best session of the day. With this system we are able to track the statistics for a non Sitecore site. Just create a new site into the interface and after that, you will see your site with the same kind of ribbon than a normal sitecore site in webedit.<br />
The developer is suppose to: add a line of javascript into the non sitecore site. After that he will define some place, and element who will trigger the events to track by using this ribbon.
After that, the marketer, is able to associate those elements to a analytics goal or campaign<br />
With this tool you are also able to add a sublayout .... into you old website! This is done by the same javascript and wy using the same ribbon. Just select the place to replace with your sitecore content and insert the sublayout. Select a datasource if needed of course and it is done! You are even able to do personalisation on it.<br />
<h2>
Day 1 - Session 6 : </h2>
The Skynet project : nothing to do with Terminator. This project is made to learn from the previous experiences of content testing and after that he is able to propose some segmentation of users. For example if you have 2 variants of content on a first sublayout and 3 variants of content in a second sublayout, the system will test it with the user and after that he will proposed something like: for the users form Belgium between 6AM and 4PM you should do variant 1 and it will impact 6,5% of the visits, ...<br />
<h2>
<br /></h2>
Vangansewinkel Benjaminhttp://www.blogger.com/profile/01696678283653731912noreply@blogger.com0tag:blogger.com,1999:blog-630324887720106786.post-60422671250889547982014-07-15T15:48:00.001+02:002014-07-15T15:48:45.278+02:00Fixing “The breakpoint will not currently be hit. No symbols have been loaded for this document.”If you use Visual Studio you have already seen this message at least one time: The breakpoint will not currently be hit. No symbols have been loaded for this document.” and your breakpont is white and never hit.<br />
<br />
Here is the things you should try if you have this message:<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<br /></div>
<ul>
<li>Clean the solution:</li>
<ul>
<li>Right click on the solution</li>
<li>Click on "Clean Solution"</li>
</ul>
<li>Check the environment constants<br /><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiLv5gJZH1ZcTiiR6OcRUBnQb23ynyinfO_XhEMGTlB3XJ98Bot46hk6LZHeVfmjbsYmzaQLFLfFhLTULitNDZ0CTludvSBOCilCH9vxTKI15Owd4FtO78MrE6iiZGtFhW385FnbMJM6nM/s1600/Debug_1_2014-07-15_1543.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em; text-align: center;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiLv5gJZH1ZcTiiR6OcRUBnQb23ynyinfO_XhEMGTlB3XJ98Bot46hk6LZHeVfmjbsYmzaQLFLfFhLTULitNDZ0CTludvSBOCilCH9vxTKI15Owd4FtO78MrE6iiZGtFhW385FnbMJM6nM/s1600/Debug_1_2014-07-15_1543.png" height="365" width="400" /></a></li>
<ul>
<li>Right click on the project</li>
<li>Click on "Properties"</li>
<li>Click on the "Build" tab</li>
<li>Check the box "Define DEBUG constant"</li>
<li>Check the box "Define TRACE constant"</li>
<li>Rebuild all</li>
</ul>
</ul>
<br />
<ul><ul></ul>
<li>Select the "Debug info" to full<br /><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEipqrnwhad9xURRmUj0EcaL_2j-UOScmKDo-m1XC2cbkCpgmL5RpobaEJBviYlMK7lM9ZSuLTf6Yg-a0goLSEEX0x0F_4QtjBDy0mDOeBo4cuQhK4Vo4BROgO17zI41MV0rJSIBdIItqsE/s1600/Debug_2_2014-07-15_1546.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em; text-align: center;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEipqrnwhad9xURRmUj0EcaL_2j-UOScmKDo-m1XC2cbkCpgmL5RpobaEJBviYlMK7lM9ZSuLTf6Yg-a0goLSEEX0x0F_4QtjBDy0mDOeBo4cuQhK4Vo4BROgO17zI41MV0rJSIBdIItqsE/s1600/Debug_2_2014-07-15_1546.png" height="372" width="400" /></a></li>
<ul>
<li>Right click on the project</li>
<li>Click on "Properties"</li>
<li>Click on the "Build" tab</li>
<li>Click on "Advanced" </li>
<li>Select "full" in the dropdown "Debug info"</li>
<li>Rebuild all</li>
</ul>
</ul>
Vangansewinkel Benjaminhttp://www.blogger.com/profile/01696678283653731912noreply@blogger.com8tag:blogger.com,1999:blog-630324887720106786.post-78669772098529543332014-06-23T11:43:00.003+02:002014-06-23T11:44:12.995+02:00Setup a continuous integration for Sitecore with TDS - TFS 2013 and Octopus - Step 4: Align the version numberThis 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:<br />
<ol>
<li><a href="http://sitecoreblog.blogspot.be/2014/06/setup-continuous-integration-for.html" target="_blank">Setup a continuous integration for Sitecore with TDS - TFS 2013 and Octopus - Architecture</a></li>
<li><a href="http://sitecoreblog.blogspot.be/2014/06/setup-continuous-integration-for_23.html" target="_blank">Setup a continuous integration for Sitecore with TDS - TFS 2013 and Octopus - Step 1: Customize the TFS workflow</a></li>
<li><a href="http://sitecoreblog.blogspot.be/2014/06/setup-continuous-integration-for_6788.html" target="_blank">Setup a continuous integration for Sitecore with TDS - TFS 2013 and Octopus - Step 2: Configure TFS</a></li>
<li><a href="http://sitecoreblog.blogspot.be/2014/06/this-is-4th-post-of-series-of-post.html" target="_blank">Setup a continuous integration for Sitecore with TDS - TFS 2013 and Octopus - Step 3: Configure octopus</a></li>
</ol>
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:<br />
<ul>
<li>The build number who appear in TFS</li>
<li>The assemblies files</li>
<li>The nuget packages</li>
<li>The octopus versions</li>
</ul>
To do that:<br />
<ol>
<li>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 :)<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgxr5JmbHtnWfWYs_ST3jHm9QAZHnMMX_hKE_3belYcvRZUZ4V73YkSu6GrjwIwg13qWa4bx8tX-3SDAjL0A-ixCmL94-5C8U0yWh7ppZI3_c7TcI3akPuvgkX2P_QSoioMVX3YrkOVghw/s1600/TFS_Build_Number_2014-06-23_1026.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgxr5JmbHtnWfWYs_ST3jHm9QAZHnMMX_hKE_3belYcvRZUZ4V73YkSu6GrjwIwg13qWa4bx8tX-3SDAjL0A-ixCmL94-5C8U0yWh7ppZI3_c7TcI3akPuvgkX2P_QSoioMVX3YrkOVghw/s1600/TFS_Build_Number_2014-06-23_1026.png" height="70" width="640" /></a></div>
</li>
<li>Add a file .ps1 anywhere in the sources with the following content and do a check-in in TFS</li>
<li>In the section 2.5 "Pre-build script path", refer to the powershell file you have created.</li>
<li>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</li>
</ol>
Here is the content of the powershell script to rename the versions:
<br />
<pre class="csharp" name="code">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</pre>
<br />
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. <br />
<br />
Ok that is it now. I hope that this tutorial will help you in your deployment process.<br />
You also have a video presented to the Sitecore Virtual User Group who could be useful for it here: <a href="http://sitecoreug.org/events/January">http://sitecoreug.org/events/January</a><br />
<br />
Here is the index of all the post who compose this tutorial:<br />
<ol>
<li><a href="http://sitecoreblog.blogspot.be/2014/06/setup-continuous-integration-for.html" target="_blank">Setup a continuous integration for Sitecore with TDS - TFS 2013 and Octopus - Architecture</a></li>
<li><a href="http://sitecoreblog.blogspot.be/2014/06/setup-continuous-integration-for_23.html" target="_blank">Setup a continuous integration for Sitecore with TDS - TFS 2013 and Octopus - Step 1: Customize the TFS workflow</a></li>
<li><a href="http://sitecoreblog.blogspot.be/2014/06/setup-continuous-integration-for_6788.html" target="_blank">Setup a continuous integration for Sitecore with TDS - TFS 2013 and Octopus - Step 2: Configure TFS</a></li>
<li><a href="http://sitecoreblog.blogspot.be/2014/06/this-is-4th-post-of-series-of-post.html" target="_blank">Setup a continuous integration for Sitecore with TDS - TFS 2013 and Octopus - Step 3: Configure octopus</a></li>
<li><a href="http://sitecoreblog.blogspot.be/2014/06/setup-continuous-integration-for_9000.html" target="_blank">Setup a continuous integration for Sitecore with TDS - TFS 2013 and Octopus - Step 4: Align the version number</a></li>
</ol>
Vangansewinkel Benjaminhttp://www.blogger.com/profile/01696678283653731912noreply@blogger.com0tag:blogger.com,1999:blog-630324887720106786.post-52438654771102054992014-06-23T10:18:00.002+02:002014-06-23T12:35:14.729+02:00Setup a continuous integration for Sitecore with TDS - TFS 2013 and Octopus - Step 3: Configure octopusThis 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:<br />
<ol>
<li><a href="http://sitecoreblog.blogspot.be/2014/06/setup-continuous-integration-for.html" target="_blank">Setup a continuous integration for Sitecore with TDS - TFS 2013 and Octopus - Architecture</a></li>
<li><a href="http://sitecoreblog.blogspot.be/2014/06/setup-continuous-integration-for_23.html" target="_blank">Setup a continuous integration for Sitecore with TDS - TFS 2013 and Octopus - Step 1: Customize the TFS workflow</a></li>
<li><a href="http://sitecoreblog.blogspot.be/2014/06/setup-continuous-integration-for_6788.html" target="_blank">Setup a continuous integration for Sitecore with TDS - TFS 2013 and Octopus - Step 2: Configure TFS</a></li>
</ol>
<div>
Now our TFS is configured correctly, the package are created and published to the nuget server. We can focus on octopus now.</div>
<div>
<br /></div>
<div>
I will not explain how to setup you Octopus environments, machines, ... but only the deployment process I use. </div>
<div>
<br /></div>
<div>
Here is the steps:</div>
<div>
<ol>
<li><b>Start Deployment</b>: A notification email to the different member of the project with the details of the packages who will be deployed.</li>
<li><b>Clean Environment</b>: 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: <pre class="csharp" name="code">#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</pre>
</li>
<li><b>Deploy Files</b>: Deploy the NuGet package. You may have multiple time this step if you have multiple nuget package with the website files to deploy.</li>
<li><b>Deploy TDS</b>: Deploy the NuGet package with the TDS files in a temporary folder. </li>
<li><b>Install TDS Packages</b>: 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.<br />A great alternative could be this tool: <a href="https://github.com/adoprog/Sitecore-Deployment-Helpers">https://github.com/adoprog/Sitecore-Deployment-Helpers</a></li>
</ol>
</div>
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.<br />
<br />
To do that:<br />
<br />
<ol>
<li>Add a new file with the extention .ps1 somewhere in your sources and chech-in this file in TFS</li>
<li>Edit your TFS build definition </li>
<li>In the tab "Process", section 2.5</li>
<li>In the section "Post-build script path", select your .ps1 file<br /><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg-NcKGtlpi1s2svV4RT_wR3J8TtFZYXVGA1nzFbhU2lHNQN57XhKsM-ISgkT9HiyL2JpTBduMcEXgouc_XQNLfq9EPHrlcdxFk-7XS4sebvsKVahX9RZwaXqBhT5noSTKL0FQENaYr2q0/s1600/TFS-Post-Build-2014-06-23_1010.png" imageanchor="1" style="clear: left; float: left; margin-bottom: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg-NcKGtlpi1s2svV4RT_wR3J8TtFZYXVGA1nzFbhU2lHNQN57XhKsM-ISgkT9HiyL2JpTBduMcEXgouc_XQNLfq9EPHrlcdxFk-7XS4sebvsKVahX9RZwaXqBhT5noSTKL0FQENaYr2q0/s1600/TFS-Post-Build-2014-06-23_1010.png" height="148" width="640" /></a></li>
<li>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.</li>
<li>The content of the powershell file is the following: <pre class="csharp" name="code">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
</pre>
</li>
</ol>
<br />
<br />
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.<br />
<br />
Next steps:<br />
<ol>
<li><a href="http://sitecoreblog.blogspot.be/2014/06/setup-continuous-integration-for.html" target="_blank">Setup a continuous integration for Sitecore with TDS - TFS 2013 and Octopus - Architecture</a></li>
<li><a href="http://sitecoreblog.blogspot.be/2014/06/setup-continuous-integration-for_23.html" target="_blank">Setup a continuous integration for Sitecore with TDS - TFS 2013 and Octopus - Step 1: Customize the TFS workflow</a></li>
<li><a href="http://sitecoreblog.blogspot.be/2014/06/setup-continuous-integration-for_6788.html" target="_blank">Setup a continuous integration for Sitecore with TDS - TFS 2013 and Octopus - Step 2: Configure TFS</a></li>
<li><a href="http://sitecoreblog.blogspot.be/2014/06/this-is-4th-post-of-series-of-post.html" target="_blank">Setup a continuous integration for Sitecore with TDS - TFS 2013 and Octopus - Step 3: Configure octopus</a></li>
<li><a href="http://sitecoreblog.blogspot.be/2014/06/setup-continuous-integration-for_9000.html" target="_blank">Setup a continuous integration for Sitecore with TDS - TFS 2013 and Octopus - Step 4: Align the version number</a></li>
</ol>Vangansewinkel Benjaminhttp://www.blogger.com/profile/01696678283653731912noreply@blogger.com0tag:blogger.com,1999:blog-630324887720106786.post-63653763530577851012014-06-23T08:39:00.000+02:002014-06-23T11:44:27.006+02:00Setup a continuous integration for Sitecore with TDS - TFS 2013 and Octopus - Step 2: Configure TFSThis 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:<br />
<ol>
<li><a href="http://sitecoreblog.blogspot.be/2014/06/setup-continuous-integration-for.html" target="_blank">Setup a continuous integration for Sitecore with TDS - TFS 2013 and Octopus - Architecture</a></li>
<li><a href="http://sitecoreblog.blogspot.be/2014/06/setup-continuous-integration-for_23.html" target="_blank">Setup a continuous integration for Sitecore with TDS - TFS 2013 and Octopus - Step 1: Customize the TFS workflow</a></li>
</ol>
<div class="separator" style="clear: both; text-align: center;">
</div>
<div>
Now that we have our custom build template we are ready to configure it.</div>
<div class="separator" style="clear: both; text-align: center;">
</div>
<div class="separator" style="clear: both; text-align: center;">
<br /></div>
<div>
<b>TDS settings:</b></div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiO_fIUiuRucxlUa5PD3wm-dq-vs6IDFZ4caiYWVvM0tNKzQ7Uf-m7I2eFMjDWta1Fg9YKr8I02st57Wjfk90gienROEBQUpv7JG7gc7tdTE4RdBHKkLt65BYphFiUQhtlFU8WRz29Z1SQ/s1600/TFS_-_TDS_2014-06-23_0818.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiO_fIUiuRucxlUa5PD3wm-dq-vs6IDFZ4caiYWVvM0tNKzQ7Uf-m7I2eFMjDWta1Fg9YKr8I02st57Wjfk90gienROEBQUpv7JG7gc7tdTE4RdBHKkLt65BYphFiUQhtlFU8WRz29Z1SQ/s1600/TFS_-_TDS_2014-06-23_0818.png" height="62" width="640" /></a></div>
<div>
<ol>
<li>Set "TDS : Generate packages" to true to generate the TDS packages</li>
<li>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.</li>
</ol>
</div>
<div class="separator" style="clear: both; text-align: left;">
<b>Octopus settings:</b></div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj3sGDqZIO3m-ds4pYITwq12YAThaDoxPP6jpaBRpU2H7LIcdue408eUcrxxhPBZdBoo0NxCZQaQd3l7It9tFTNYGwlZMb0Y4FcDTWo7D_DfhwTusi3G7tp4vWmr5NhlW9nTxzn_y72stc/s1600/TFS_-_Octopus_section_-_2014-06-23_0812.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj3sGDqZIO3m-ds4pYITwq12YAThaDoxPP6jpaBRpU2H7LIcdue408eUcrxxhPBZdBoo0NxCZQaQd3l7It9tFTNYGwlZMb0Y4FcDTWo7D_DfhwTusi3G7tp4vWmr5NhlW9nTxzn_y72stc/s1600/TFS_-_Octopus_section_-_2014-06-23_0812.png" height="68" width="640" /></a></div>
<div>
<ol>
<li>Set "Octopack : API Key of the nuget server". You can find this setting into the app setting "apiKey" of the nuget server</li>
<li>Set "Octopack : Publish packaged to http" to the url of the nuget server it should look like : "http://YOUR_DOMAIN/api/v2/package/"</li>
<li>Set "Octopack : Run OctoPack" to true. This will run the octopack command to generate and publish the nuget packages to the server.</li>
</ol>
</div>
<div>
<div>
<b>Octopack:</b></div>
</div>
<div>
To generate the octopus packages, I use the nuget package called OctoPack. It is really easy to use: you just need to install <a href="https://www.nuget.org/packages/OctoPack/" target="_blank">this nuget package</a> in every web project. This will generate the package for you when you build with msbuild with the custom parameters we have set. </div>
<div>
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:</div>
<pre class="xml" name="code"><?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></pre>
<div>
<br />
Next steps:<br />
<ol>
<li><a href="http://sitecoreblog.blogspot.be/2014/06/setup-continuous-integration-for.html" target="_blank">Setup a continuous integration for Sitecore with TDS - TFS 2013 and Octopus - Architecture</a></li>
<li><a href="http://sitecoreblog.blogspot.be/2014/06/setup-continuous-integration-for_23.html" target="_blank">Setup a continuous integration for Sitecore with TDS - TFS 2013 and Octopus - Step 1: Customize the TFS workflow</a></li>
<li><a href="http://sitecoreblog.blogspot.be/2014/06/setup-continuous-integration-for_6788.html" target="_blank">Setup a continuous integration for Sitecore with TDS - TFS 2013 and Octopus - Step 2: Configure TFS</a></li>
<li><a href="http://sitecoreblog.blogspot.be/2014/06/this-is-4th-post-of-series-of-post.html" target="_blank">Setup a continuous integration for Sitecore with TDS - TFS 2013 and Octopus - Step 3: Configure octopus</a></li>
<li><a href="http://sitecoreblog.blogspot.be/2014/06/setup-continuous-integration-for_9000.html" target="_blank">Setup a continuous integration for Sitecore with TDS - TFS 2013 and Octopus - Step 4: Align the version number</a></li>
</ol>
</div>
Vangansewinkel Benjaminhttp://www.blogger.com/profile/01696678283653731912noreply@blogger.com0