Experience Sitecore ! | All posts tagged 'Package'

Experience Sitecore !

More than 200 articles about the best DXP by Martin Miles

Extracting package installation functionality out of Sitecore Commerce 9 SIF

In one of my previous posts, I've explained how Install-SitecoreConfiguration works in SIF and how to use it for package installation.

As time passed, more and more functionality got natively embedded into SIF, that included some useful things such as config transformations, publishing, rebuilding indexes, and of course, packages/modules installation. I decided to inspect what actually went into the platform and extracted this functionality out of Commerce product for those who want to benefit from package installations with SIF bit does not want to bother with XC9.

So, actually, there is not a big change compared with my previous post. On the lower level, there is still a temporal folder being exposed and *.aspx page placed for actual job to be done. The good thing is that plenty of that functionality now supported out of the box

For legal reasons I cannot include downloadable ready to use package, since the code and scripts belong to Sitecore who only can share it. Instead I will tell you how you can make it on your own copy of Sitecore Commerce 9.0 update 2.

You'll need three parts - Configuration, Modules and SiteUtilityPages, all carefully from within SIF.Sitecore.Commerce.1.2.14 folder:

  • Configurations (json files) taken out of Configuration\Commerce\Master_SingleServer.json and Configuration\SitecoreUtilities\InstallModule.json
  • Then you'll need three modules from Modules folder: InstallSitecoreConfiguration, ManageCommerceService, SitecoreUtilityTasks
  • Also you need SiteUtilityPages folder with InstallModules.aspx file within

If you do not keep a structure as from Commerce Installer (as didn't I) - please make adjust paths of dependencies correspondingly.

Last but not the least, you need to modify main Deploy-Sitecore-Commerce.ps1 PowerShell script, removing everything apart from package installation out of it. I renamed it into Install.ps1 and here is what I end up with:

param(
		[string]$SiteName = "habitat91.dev.local",	           # your sitecore instance name
		[string]$ModulePath = "p:\PowerShell Extensions-5.0.zip"   # path to the module to be installed
)

$global:DEPLOYMENT_DIRECTORY=Split-Path $MyInvocation.MyCommand.Path
$modulesPath=( Join-Path -Path $DEPLOYMENT_DIRECTORY -ChildPath "Modules" )
if ($env:PSModulePath -notlike "*$modulesPath*")
{
    $p = $env:PSModulePath + ";" + $modulesPath
    [Environment]::SetEnvironmentVariable("PSModulePath",$p)
}

$params = @{
		Path = Resolve-Path '.\Configuration\Master.json'	
		SiteName = $SiteName
		InstallDir = "$($Env:SYSTEMDRIVE)\inetpub\wwwroot\$SiteName"		
		SiteUtilitiesSrc = ( Join-Path -Path $DEPLOYMENT_DIRECTORY -ChildPath "SiteUtilityPages" )	

		ModuleFullPath = Resolve-Path -Path  $ModulePath
    }

Install-SitecoreConfiguration @params

and below there is my tree structure:


There are two parameters to be configured for Install.ps1 script to run:


In case you are working outside of inetpub\wwwroot folder, please also adjust the path for InstallDir variable.

Hope this helps your DevOps!


How to use Install-SitecoreConfiguration from SIF with your custom configuration on example of installing SPE and SXA along with Sitecore

This is an exercise resulting from a blog post by Rob Ahnemann and I will cover Install-SitecoreConfiguration in more details on an example of installing Sitecore modules SPE and SXA as a part of a Sitecore installation by SIF.

Install-SitecoreConfiguration is one of the most what actually SIF is. In my Sitecore installation script I have created a step called Install-Packages. Let's look at how it works:

function Install-Packages {
    Unblock-File .\build\PostInstall\Invoke-InstallPackageTask.psm1 
    Install-SitecoreConfiguration -Path .\build\PostInstall\install-sitecore-package.json -SiteName "$SolutionPrefix.$SitePostFix"
}

This installs a configuration called install-sitecore-package.json Opening it looks like below:

{
    "Parameters": {
         "SiteName": {
            "Type": "string",
            "DefaultValue": "Sitecore",
            "Description": "The name of the site to be deployed."
        },
        "InstallDirectory": {
            "Type": "string",
            "DefaultValue": "c:\\inetpub\\wwwroot",
            "Description": "Base folder to where website is deployed."
        }
    },
    "Variables": {
        // The sites full path on disk
        "Site.PhysicalPath": "[joinpath(parameter('InstallDirectory'), parameter('SiteName'))]",
		"Site.Url": "[concat('http://', parameter('SiteName'))]"

    },
    "Tasks": {
		"InstallPackages":{
			"Type": "InstallPackage",
            "Params": [
                {
                    "SiteFolder": "[variable('Site.PhysicalPath')]",
                    "SiteUrl": "[variable('Site.Url')]",
                    "PackagePath": ".\\build\\assets\\Modules\\Sitecore PowerShell Extensions-4.7.2 for Sitecore 8.zip"
                },
                {
                    "SiteFolder": "[variable('Site.PhysicalPath')]",
                    "SiteUrl": "[variable('Site.Url')]",
                    "PackagePath": ".\\build\\assets\\Modules\\Sitecore Experience Accelerator 1.6 rev. 180103 for 9.0.zip"
                }
            ]
		}
    },
	"Modules":[
		".\\build\\PostInstall\\Invoke-InstallPackageTask.psm1"
	]
}

Parameters are values that may be passed when Install-SitecoreConfiguration is called. Parameters must declare a Type and may declare a DefaultValue and Description. Parameters with no DefaultValue are required when Install-SitecoreConfiguration is called.

Variables are values calculated in a configuration. They can reference Parameters, other Variables, and config functions.

Tasks are separate units of work in a configuration. Each task is an action that will be completed when Install-SitecoreConfiguration is called. By default, tasks are applied in the order they are declared. Tasks may reference Parameters, Variables, and config functions.

Finally, the last line is actually referencing the actual task - a PowerShell module script (Invoke-InstallPackageTask.psm1) that will be run with given parameters:

Set-StrictMode -Version 2.0

Function Invoke-InstallPackageTask {
    [CmdletBinding(SupportsShouldProcess=$true)]
    param(
        [Parameter(Mandatory=$true)]
        [string]$SiteFolder,
		[Parameter(Mandatory=$true)]
        [string]$SiteUrl,
		[Parameter(Mandatory=$true)]
        [string]$PackagePath
    )

    Write-TaskInfo "Installing Package $PackagePath" -Tag 'PackageInstall'

    #Generate a random 10 digit folder name. For security 
	$folderKey = -join ((97..122) | Get-Random -Count 10 | % {[char]$_})
	
	#Generate a Access Key (hi there TDS)
	$accessKey = New-Guid
	
	Write-TaskInfo "Folder Key = $folderKey" -Tag 'PackageInstall'
	Write-TaskInfo "Access Guid = $accessKey" -Tag 'PackageInstall'

	#The path to the source Agent.  Should be in the same folder as I'm running
	$sourceAgentPath = Resolve-Path "PackageInstaller.asmx"
	
	#The folder on the Server where the Sitecore PackageInstaller folder is to be created
	$packageInstallPath = [IO.Path]::Combine($SiteFolder, 'sitecore', 'PackageInstaller')
	
	#The folder where the actuall install happens
	$destPath = [IO.Path]::Combine($SiteFolder, 'sitecore', 'PackageInstaller', $folderKey)

	#Full path including the installer name
	$fullFileDestPath = Join-Path $destPath "PackageInstaller.asmx"
	
	Write-TaskInfo "Source Agent [$sourceAgentPath]" -Tag 'PackageInstall'
	Write-TaskInfo "Dest AgentPath [$destPath]" -Tag 'PackageInstall'

	#Forcibly cread the folder 
	New-Item -ItemType Directory -Force -Path $destPath

	#Read contents of the file, and embed the security token
	(Get-Content $sourceAgentPath).replace('[TOKEN]', $accessKey) | Set-Content $fullFileDestPath

	#How do we get to Sitecore? This URL!
	$webURI= "$siteURL/sitecore/PackageInstaller/$folderKey/packageinstaller.asmx?WSDL"
	 
	Write-TaskInfo "Url $webURI" -Tag 'PackageInstall'
	
	#Do the install here
	$proxy = New-WebServiceProxy -uri $webURI
	$proxy.Timeout = 1800000

	#Invoke our proxy
	$proxy.InstallZipPackage($PackagePath, $accessKey)

	#Remove the folderKey
	Remove-Item $packageInstallPath -Recurse
}
Register-SitecoreInstallExtension -Command Invoke-InstallPackageTask -As InstallPackage -Type Task

What this task does is locates ASMX file, which is an actual handler and copies it into temp random folder within your Sitecore instance allowing you to execute package installer APIs that itself does require Sitecore context. 

Obviously, before running .\install-xp0.ps1 please make sure you have both installers for modules by their paths as per configuration parameters in PackagePath (from example above, they are):
    .\build\assets\Modules\Sitecore PowerShell Extensions-4.7.2 for Sitecore 8.zip
    .\build\assets\Modules\Sitecore Experience Accelerator 1.6 rev. 180103 for 9.0.zip

So, that's how we added new SitecoreConfiguration in order to achieve automated package installation of SPE and SXA by SIF. Since now new locally installed Sitecore instance already has both SPE and SXA pre-installed and ready to use. This approach also allows installing any other modules as you may need them pre-installed.

Finally, would highly recommend watching a great video by Thomas Eldblom about using SIF configurations:

UPDATE: there is another way of doing this by new built-in SIF functionality, please read the blog post here.


Got a handy new tool for "finding ends" of presentation items - Rendering Chrome for Components

I have recently started working on a new project with a non-transparent structure. By saying non-transparent I mean non trivial locations for the code and non-matching layout and rendering items to their filesystem counterparts. So I was looking for the convenient way to identify matched pairs.

Luckily I remembered JammyKam presenting something similar at London UserGroup in January 2017 and refereed to his blog in order to find Rendering Chrome for Components package (module - ?). So I will briefly go through his solution show how it helped me and what it does:


  1. Install package as normal
  2. Add a namespace into web.config from /Views folder:
    <add namespace="ForwardSlash.SC.RenderingChrome.HtmlHelpers"></add>
    
  3. Append this to a containers element so that it generates an attribute:
    @Html.Sitecore().ContainerChrome()
  4. Now, if you go to Experience Editor and open View tab, you'll see a new checkbox Highlight Renderings clicking which turns all magic on


Here's the result:


    It works not only in Chrome, as you see I run it in firebug.

    Hope it will help you as much as it already has helped me.

    References:

    - original post by Kamruz Jaman

    - sources on GitHub

    - presentation slides from Sitecore User Group London (January 2017) - 3.5MB

    How to make Content Editor search by plain GUIDs without braces and dashes? (part 1)

    How many times while inspecting the code you've encountered something similar to:

    
    

    Of course, you've guessed that A50CC32DAC854E3D9FC3A6BFDE1C577E is nothing else but GUID of an item being modified for URL. And of course, in order to look up what this item is, you've pasted it into search box of Content Editor, as the easiest way to search. Content Editor in turn did not find anything as obviously that value isn't a properly formatted GUID. As a mature Sitecore developer, probably had to append braces and insert dashes in appropriate format, so that items becomes discoverable via search.

    To overcome this, I created animprovement module that will allow you search by both plain and properly formatted GUIDs.

    As for a starting point, I decided to find out what exactly happens after user submits value into a search box. Looking up ajax POST call to the backend suggested me that it should be a pipeline somewhere behind Content Editor application. Decompiling Sitecore.Kernel and Sitecore.Client prompted me to search pipeline, that is located at Sitecore.Pipelines.Search namespace of Sitecore.Kernel.

    The very first step of that pipeline is called IDResolver and does exactly what he is named for: resolves an item for a search term in case it is properly formatted GUID and exists in database. If not - it passed arguments further down the pipeline.

    So the idea is to create an extra processor after IDResolver that will do basically the same business but for search terms that are plain GUIDs. Okay, here's the code:

        public class SearchByPlainGuids
        {
            public void Process(SearchArgs args)
            {
                Assert.ArgumentNotNull((object)args, "args");
    
                if (string.IsNullOrWhiteSpace(args.TextQuery))
                    return;
    
                if (args.Type == SearchType.ContentEditor && IsPlainGuid(args.TextQuery))
                {
                    Item obj = args.Database.GetItem(new ID(MakeGuidQuery(args.TextQuery)));
                    if (obj != null)
                    {
                        SearchResult result = SearchResult.FromItem(obj);
                        args.Result.AddResultToCategory(result, Translate.Text("Direct Hit"));
                    }
                    args.AbortPipeline();
                }
            }
    
            private string MakeGuidQuery(string plainGuid)
            {
                return "{" + plainGuid.Substring(0, 8) + "-" + plainGuid.Substring(8, 4) + "-" + plainGuid.Substring(12, 4)
                       + "-" + plainGuid.Substring(16, 4) + "-" + plainGuid.Substring(20, 12) + "}";
            }
    
            private bool IsPlainGuid(string query)
            {
                string pattern = @"^[0-9a-fA-F]{8}[0-9a-fA-F]{4}[0-9a-fA-F]{4}[0-9a-fA-F]{4}[0-9a-fA-F]{12}$";
    
                Regex regex = new Regex(pattern);
    
                return regex.IsMatch(query);
            }
        }
    

    To make this code function, let's build that into a DLL and reference that DLL from config patch file located within include folder:

    
    
    
    
    
          
        
      
    
    
    

    Voila! Now you're able to search by item GUID regardless of its format.


    Please feel free to download ready-to-use package (5.7KB) for Sitecore 8.1 and 8.2 or source code (9.1KB).

    P.S. It all looks and works well, however what can be done better? First of all, we've modifies search pipeline, so our processor is called with every call of given pipeline. Next, we add extra DLL and extra config include file to reference that DLL. Wouldn't it better, if GUIDs were checked and modified right before entering that pipeline, ideally at the front-end? Let's try to make this function at part 2 of this story.

    Productivity Improvement: Creating a Presentation Exists Gutter - get even faster access to item's Presentation Details

    Previously I have described how easily you can create a shortcut to Device Editor of Presentation Details right at the item's Context Menu - access that as much as in two clicks! But there's even easier (and more visual) way - create a specific Sitecore Gutter.
    So, what Gutters are? Gutters are sort of visual markers you can optionally enable / disable in your Content Editor. Have you seen a vertical bar, immediately left hand side from Sitecore tree? That is a Gutters Area and once you do right click on it - you may enable / disable some gutters already installed. Also, gutters can be clickable, and on click handler you may also call Sitecore commands, so why not to call our familiar item:setlayoutdetails that opens Device Editor dialog for corresponding item?

    So, let's create our own gutter. Every gutter is configured within core database under /sitecore/content/Applications/Content Editor/Gutters folder. We are going to create a new item called ... derived from /sitecore/templates/Sitecore Client/Content editor/Gutter Renderer template (all gutters derive from that one). There are only two fields we need to set there - Header which is just a gutter name and Type - fully qualified class name:

    So now let's implement PresentationExists class. Briefly, every gutter derives from GutterRenderer class which returns GutterIconDescriptor object when gutter should be shown next to corresponded item otherwise just null. Implementation below checks whether current item has a Layout associated, and if yes - it returns a GutterIconDescriptor with a item:setlayoutdetails command for that item.
    public class PresentationExists : GutterRenderer
    {
        protected override GutterIconDescriptor GetIconDescriptor(Item item)
        {
            if (item != null)
            {
                var layoutField = item.Fields[Sitecore.FieldIDs.LayoutField];
                var layoutDefinition = LayoutDefinition.Parse(LayoutField.GetFieldValue(layoutField));
    
                if (layoutDefinition != null && layoutDefinition.Devices.Count > 0)
                {
                    GutterIconDescriptor gutterIconDescriptor = new GutterIconDescriptor
                    {
                        Icon = "Applications/32x32/window_colors.png",
                        Tooltip = Translate.Text("Presentation is set for this item.")
                    };
    
                    if (item.Access.CanWrite() && !item.Appearance.ReadOnly)
                    {
                        gutterIconDescriptor.Click = string.Format("item:setlayoutdetails(id={0})", item.ID);
                    }
                    return gutterIconDescriptor;
                }
            }
    
            return null;
        }
    }
    
    So as soon as you compile and place resulting DLL under <web_root>\bin folder, your gutter wil work like below:

    Clicking that icon will immediately show Device Editor dialog. Job's done!

    Downloads: you can access source code at Sitecore Improvements project GitHub page, or you can download ready-to-use package by this link.

    Please note: improperly implemented gutters may affect performance of Content Editor, as the code above runs for each item. So please be extremely attentive on what you're doing within GetIconDescriptor() method.

    Unicorn - the simplest way to share Sitecore items in the soure control along with your code

    Annoyed of the necessity of creating packages with recent items in order to share that with your colleagues? Tired of items' versioning? Why not to version those items in source control then?


    My favorite tool for achieving that is Unicorn. It does exactly what it suppose to - syncs certain Sitecore items (recursively with children) within a directory, as configured, so that you are able to check-in the folder with all serialized items, so that your colleagues can sync that changes into their database; that means new features / fixes deliver items deltas simultaneously with their code counterparts.

    Here is the example of configuration, whatever sits under mentioned paths (recursively) will be serialized:

        
    
    
    
    
    
    

    The greatest thing I love about Unicorn is simplicity - as simple as the following:

    • It installs as NuGet package.
    • Consists of 2 DLLs and a config patch file.
    • Just one control page at the web root to perform sync / revert
    • Simple configuration file.

    From limitations I would only mention recursiveness - specifying an item in config processes it will all child items and that cannot be overridden. Of course, this is not a problem in more advanced solution - TDS, but are we here about simplicity?


    Installation from NuGet Package Manager Console:

    PM> Install-Package Unicorn
    

    See also: GitHub source code

    Update: now version 3 has been released with great, even revolutionary, changes and it became more friendly. So, please read:




    Web Forms for Marketers 8.0 - missing Save to Database action and making it work with SQL database again

    As we know, Sitecore version 8.0 incorporated popular the module called Web Forms for Marketers (WFFM), and it became an integral part of Sitecore. WFFM is still shipped as a package, however now through Sitecore SDN portal, rather than via Marketplace as before.

    So, let's assume you have a brand clean install of Sitecore (I have 8.0 Update 4 for this example). In order to download WFFM for 8.0 Update 4 please follow to https://dev.sitecore.net/Downloads/Sitecore_Experience_Platform/8_0/Sitecore_Experience_Platform_80_Update4.aspx and find download link below in the module section.

    Remark 1: zip archive you download is not a package - it is normal archive containing 2 child packages - first is package for CM instance and additionally contains all dialogs etc. while another is just CD installation package. For our demonstration, unpack Web Forms for Marketers 8.0 rev. 150625.zip into /Data/Packages directory and use Development Tools - Installation Wizard, as you normally install packages.

    Remark 2: ensure you have Mongo up and running, otherwise it will come to "package never installs" issue (more details about that bug here). it is required just for installation, after you have it installed you it is safe to turn off Mongo - WFFM will save data to database without any problems.

    Remark 3: it may take up to 5 minutes to install the package, so please do not panic, as soon as you got Mongo running - you'll reach the point when it prompts you for setting Placeholder settings.


    After WFFM installation is complete, you may configure a form. By default WFFM contains several pre-created forms as an example for people to play with it and get acknowledges with the module. For those who are new to WFFM here is a screenshot below displaying how to locate Form Designer:


    Selecting Form Designer above loads a screen with the list of existing forms. Let's pick Get Our Newsletter form and click OK. Form Designer will load form configuration screen with fields and Subscribe button at the bottom. A click to that button opens Submit panel left hand side with several settings for pre-save (validation), save and post-save (success page / success message).


    Remark 4: Those who used to work with previous versions of WFFM will be frustrated by missing Save to Database action from save actions menu. These are save actions that come out of box:


    There is nothing to worry about, Save to Database action is still there but is not displayed only because as it always happens. However:

    Remark 5: SQL database itself is not coming with the package, so you need to pick database backup from one of previous versions - its schema remains the same. To make you life easier, I have attached forms empty database backup so you may download it by this link. In WFFM 8.0 there is the setting that references connection string name (in you ConnectionStrings.config file) that is used by WFFM.

    <!-- CONNECTION STRING - Sets the name of the connection string -->
    <setting name="WFM.ConnectionString" value="wfm" />
    
    <add name="wfm" connectionString="Data Source=.;Initial Catalog=test2_Forms;Integrated Security=False;User ID=sa;Password=your_password" />
    

    To make Get Our Newsletter form work, we need to assign it to some page. Default landing page called Home will work well for our purpose. This is how you set a form to a placeholder:


    Image above displays webform, because we use default layout that comes with webforms aspx page. For MVC there is rendering called Form MVC. With next step below (as clicking Edit button) you need to specify which exact form should be served by this sublayout (or rendering for MVC):



    Now you may verify /Home page in Publish - Preview screen or publish web site and then load the page normally (http://your.site.name/) so you end up with something like on a screenshot below:


    After you fill in valid email and click Subscribe button, you'll get "Thank you for filling in the form" message, that's in case database was references correctly. if not - there will be message abut unexpected error and corresponding exception details will appear in log file.

    Let's get physically into database to verify the data has been stored correctly. In successful case you'll see one record in Form table and few records in Field table, one record per each form field.


    That's all!

    Following blog post will demonstrate how to make WFFM result screen display forms records from SQL database.


    Sitecore 8: re-indexing errors out and module installation never ends without MongoDB running

    What is happening? we got a commonly met point of frustration since recent - imagine, you have just installed an instance of Sitecore 8 and are trying to install some useful modules, for example Web Forms for Marketers 8.0 or PowerShell Module. And all you get is never-ending progress box dialog.


    However that occurs not only while installing a module, but also when trying to rebuild indexes via built-in Developer toolbar interface. Same story, but at least this time it tries to tell us something with View all messages section, unfortunately unsuccessfully - there are no any error messages seen once you expand this box.



    Why is it happening? Going through log files made me thinking there is something with xDB, it looks like sitecore tries to perform write operation into Mongo, but is not able to do. And because Sitecore 8 is now using modern client-based SPEAK interface instead of outdated SheerUI, the back-end where in fact an error occurs is not able to notify client about that (I believe is it not yet implemented and would be fixed with future updates).

    How to fix? Let's install and run MondoDB. After default windows installation, the easiest way of running Mongo would be just running its server with dbpath parameter to where DB placed. I say the easiest because there is a better alternative to run MongoDB as Windows service application, so that it will run on system start up.


    So, as soon Mongo is up and running, let's test our assumption and try to re-build Lucene indexes again:


    And bingo! It now works well! Hope this solution helps.

    Sitecore Desktop usability improvements (package)

    While playing with core database for some useful stuffies - I came across some improvements that can save little of my time in day-to-day activities. A screenshot below can say better than few paragraphs of text, so here it is:


    It works across all the versions of Sitecore, I in fact had opportunity to test that package against 6.6 and 8.0 Update 4.


    Enjoyed that and want to get for yourself?

    Download the package: Sitecore Desktop Improvements-1.1.zip (47.5KB)

    Previous version: Sitecore Desktop Improvements-1.0.zip (30.8KB)


    Attach to IIS - debugging Sitecore with just one hotkey!

    Debugging Sitecore in most cases requires you to open attach to a process menu in Visual Studio, then enable display for processes of all users, find appropriate IIS process and фееср to it. To much hassle, isn't it, especially taking into account that you may need to repeat that again and again hundreds / thousands times as a part of your work?

    Ok, here's the solution to take your debugging pain off: Attach To IIS extension for Visual Studio. At the moment there are extensions for 2012 and 2013 version, as I haven't tried it in 2015 preview.





    Download extensions for your Visual Studio version:

    Attach_To_IIS_2012.vsix (58.6KB)