Wednesday, June 17, 2015

How to Reset All Values for a Field using PowerShell

Standard
Today I ran into a situation where we had a Multilist with Search field for a bunch of items that had values set to items that no longer existed in our instance. This is often the case when you are in development and deleting and adding sections of the tree while testing things.

As you know, Sitecore tends to be unhappy when this happens. A common error message is the following:

Guid should contain 32 digits with 4 dashes (xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx).

So, I thought it would be a good exercise to write up a quick PowerShell script that would iterate over items in my tree, and reset the values for a field that I specify.

All you need to do is set the $fieldToReset and $path variable values to suit your needs.

 $fieldToReset = "My Field Name";  
 $path = "master:\sitecore\content\MyItem";  
 $items = Get-ChildItem -Path $path -Recurse   
 $items | ForEach-Object { if ($_.fieldToReset -ne $null) {$_.fieldToReset = ""} }  

Bless you PowerShell!


Monday, June 15, 2015

Schedule Full and Incremental Content Backups using PowerShell Extensions

Standard
During my studies in University, and working for a smaller company in the past where I was involved in the hardware side of the IT world,  I discovered the power of command-line scripts when automating daily tasks such as server backups.

Looking back at late 2006, when Microsoft first introduced PowerShell,  they gave us a more advanced command-line interface where we could do some really fancy stuff. There was a new syntax we had to learn, but with enough time, it was as familiar as the good old MS-DOS prompt.



Enter Sitecore PowerShell Extensions

Having been in the Sitecore world for quite some time, I was excited to find Sitecore PowerShell Extensions on the marketplace.

As Alistair Deneys put it at the 2012 Symposium:
"There is nothing you can not do with PowerShell Console, because you're inside the Sitecore application. You can call the Sitecore API"

I have been using the module religiously for the past year, and am truly impressed by the work of the contributors.

If you know a little PowerShell, and haven't tried it before, I suggest you download and give it a bash.

This post assumes that you have the module installed. As I am running XP 8.0 Update 2, I installed version 3.0 in my instance.

The Use Case

As I mentioned in an earlier post, one of the projects that I am working on involves building out a couple new sites that have an E-Commerce back end that supports thousands of products that we are integrating into Sitecore via Commerce Connect.

One of the features that the client requested was the ability to perform batch product updates using Microsoft Excel. We ended up building an admin app where they could export and import CSV files and manipulate the content within Sitecore, using the data within these files. Our tree structure looks like this:

You may already be thinking that this sounds like trouble, and trust me, we had the same thoughts.

Wouldn't it be nice if we could have an automated daily backup strategy that would enable us to revert to a backup set relatively quickly in case something went wrong? Just like a server backup script?

PowerShell Backup Scripts

After doing some research and testing, I formulated the following scripts that create either a full or incremental package of all the items in the Master database that are located at this path in the content tree: "/sitecore/content/Data/Product-Repository/Products". You can modify this path to suite your needs.

Full Backup Script

 $packageBackupName = "Client Products Full Package Backup";  
 $sourceItems = Get-ChildItem -Path "master:\sitecore\content\Data\Product-Repository\Products"  
 $package = new-package $packageBackupName;  
 $package.Sources.Clear();  
 Write-Log "$packageBackupName started";  
 $Author = [Sitecore.Context]::User.Profile.FullName;  
 $Publisher = [Sitecore.SecurityModel.License.License]::Licensee  
 $DayOfWeek = [datetime]::Now.DayofWeek  
 $package.Metadata.Author = $Author;  
 $package.Metadata.Publisher = $Publisher;  
 $package.Metadata.Version = "";  
 $package.Metadata.Readme = "";  
 $packageSource = $sourceItems | New-ItemSource -Name "Content Source" -InstallMode Overwrite -MergeMode Merge  
 $package.Sources.Add($packageSource);  
 Export-Package -Project $package -Path "$packageBackupName-$DayOfWeek" -Zip  
 Write-Log "$packageBackupName completed"  

Incremental Backup Script

Note: This script will generate a package of items located under the target path that have been added or changed within the last 2 days. You can modify this day value by changing the "$changedDays" variable below.

 $packageBackupName = "Client Products Incremental Package Backup";  
 $changedDays = 2;  
 $sourceItems = Get-ChildItem -Recurse -Path "master:\sitecore\content\Data\Product-Repository\Products" | Where-Object  { $_."__Updated" -gt (Get-Date).AddDays(-$changedDays) -or $_."__Created" -gt (Get-Date).AddDays(-$changedDays)}  
 $package = new-package $packageBackupName;  
 $package.Sources.Clear();  
 Write-Log "$packageBackupName started";  
 $Author = [Sitecore.Context]::User.Profile.FullName;  
 $Publisher = [Sitecore.SecurityModel.License.License]::Licensee  
 $DayOfWeek = [datetime]::Now.DayofWeek  
 $package.Metadata.Author = $Author;  
 $package.Metadata.Publisher = $Publisher;  
 $package.Metadata.Version = "";  
 $package.Metadata.Readme = "";  
 $packageSource = $sourceItems | New-ItemSource -Name "Content Source" -InstallMode Overwrite -MergeMode Merge  
 $package.Sources.Add($packageSource);  
 Export-Package -Project $package -Path "$packageBackupName-$DayOfWeek" -Zip  
 Write-Log "$packageBackupName completed"  

Running Scripts and Package Default Location



Setting up Scheduled Tasks

You can certainly run these scripts on demand to create packages, but the real magic would be to let these scripts run on a schedule so that once they are set up, they would take care of themselves.

This can be achieved within a few simple steps:

1. Navigate to /sitecore/system/Modules/PowerShell/Script Library/Task Management/Tasks and create a new Powershell Script Library item, that will contain your 2 scripts. I called mine "Content Backups".

2. Within this new script library item, create 2 new PowerShell Script items, and paste in each respective PowerShell script. I called my items "Full Product Backup" and "Incremental Product Backup".



3. Navigate to "/sitecore/system/Tasks/Schedules" and create a Schedule Item to invoke the scripts housed in the Items above. To learn more about Sitecore Scheduled Tasks, please see John West‘s post discussing them).

  3.1 Set the command to "Commands/PowerShellScriptCommands"
  3.2 Set items to the location of your PowerShell Script Item for each script that you created in step
2.
  3.3 Set the schedule item to your preferred value.




I set my Full Backup to ||1|13.00:00 so that it will run once on Sunday and the interval will be 13 hours, so it will only be executed once within the day.

I set my Incremental Backup to ||126|13.00:00 so that it will run Monday - Saturday, once during the day (same as above).

Reviewing and Testing your Scheduled Tasks

Checking that you set up everything correctly and testing it out is easy. Simply open up the Task Manager within the PowerShell Toolbox:



Once launched, you can review the scheduled tasks that you set up. To test either of them, click on the item and then click the "Execute Now" button.





Managing Countries and Regions within the same site (Part 1) : Getting Started

Standard

Background

In one of the projects that I am working on, we are re-platforming a United States and Canada website from Drupal to Sitecore. During the early phases of the project, there was a lot of talk about a "lift-and-shift". As you know, that's never the case. The sites ended up being completely re-done, and the E-Commerce functionality re-worked.

Technologies and integrations include:

  1. Sitecore XP 8 Update-2
  2. Sitecore Commerce Connect 8.0
  3. Adobe Scene7 (custom connector on the Media Framework)
  4. RightNow CRM (custom connector)
  5. Shopatron E-Commerce checkout and inventory lookup.


The Use Case and The Kicker

As I mentioned before, we are re-platforming 2 independent Drupal sites for different countries. The sites are almost identical. Out of the gate, every page on the US site would exist on the Canada site. The only things that would be different would be products (some would appear on the US site and not on Canada),  pricing, and some Shopatron specific API information that we had to pass to Shopatron when a user was ready to complete the checkout process.

Easy enough. We could do all this within a single site with a bit of logic right?

The kicker was that down the road, content authors wanted the ability to add pages and pieces of content that were country-specific. For example, if there was a specific product promotion, they wanted to be able to add their own landing pages, and wanted the ability to display new call to actions (CTAs) on the home and other pages of their site.We couldn't have US pages and CTAs show up on Canada's site and vice versa.

Would we have to build out the US site and then copy it over to create the Canada site?

The Solution

Andy Uzick was in town during the architectural debate, and so we went out to lunch and started throwing out some ideas between his dirty jokes.

This is what we decided:

  1. Stick with a single site for the North America Region.
  2. Create regional settings that we could link to the site's context and items within the site.
  3. Create a "region resolver" pipeline processor within the HttpRequestBegin pipeline that would take a look at the context of the site that was running to determine the region, and then decide if the page or other content should be displayed or not.
  4. Build a custom condition for the Sitecore Rules Engine that content authors could utilize to display regional specific content for pages that existed on both sites.
  5.  

Creating the Region Settings

The first template we created was the Region template. This would be used to house or site specific regional data:



Next, we created the region content items based on the Region template in our global / shared location in the content tree, so that data could be consumed by our specific data templates:


We then created a base Site Region template with a multilist field that we configured to pull in the regions that we had set up in the global / shared location:


We made sure that the standard values had both regions selected:


We then set the necessary templates that needed regional specific settings such as pages and products, to inherit from the Site Region base template:


Finally, this is what one of the Page items looked like that had the regional settings in place:


At this point, we had everything ready content-wise to start coding against.

Looking Forward

Next up will be Part 2, The Region Resolver -  I will walk through how we built out the region resolver pipeline processor within the HttpBeginRequest pipeline

Part 3,  The Region Custom Condition - I will demonstrate building out the custom condition for the Sitecore Rules Engine.

Part 4, Regions in Action - I will put everything together, and show it in action.