Sitecore PowerShell Console cheat sheet – Part 2

Most of this post is also based on the Microsoft’s Windows PowerShell Quick Reference however despite the sharing scripting runtimes the nature of the both shells differ considerably as described in the previous post: Sitecore PowerShell Console cheat sheet – Part 1. In all cases where it made sense I’ve converted the samples to establish them in Sitecore scenarios.

How to Write Conditional Statements

To write an If statement use code similar to this:

$page = Get-item .;
$changedBy = $page."__Updated by";

if ($changedBy -eq "")
  { "Unspecified author - a system page?" }
elseif ($changedBy -eq $me)
  { "The page has been last edited by me!" }
else
  { "The page has been last edited by "+ $changedBy }

Instead of writing a series of If statements you can use a Switch statement, which is equivalent to VBScript’s Select Case statement:

$page = Get-Item .;
switch ($page.Language) {
    "en" {"This version is in English"}
    "pl" {"This version is in Polish"}
    "tlh-KX" {"This version is in Klingon?!"}
    default {"No idea what this language is!"}
  }

How to Write For and For Each Loops

Read the rest of this article »

Extending Sitecore ribbon with PowerShell scripts

Sitecore is built from the grounds up with extendibility in mind. Be that plugging into any place in its internal pipelines or any aspect of its User eXperience, therefore when I’ve managed to extend it’s context menu, I expected to have no problems whatsoever doing the same to its ribbon. Mind you I was right…

Using the PowerShell Console Module it took me less than 10 minutes total to add a nice piece of functionality that I thought was missing – Publish items I have modified.

ContextScriptsRibbon

Similarly to extending context menu – first I’ve created the script I wanted to execute that will take the current item and it’s sub-items and publish them by adding a new script item using the /sitecore/templates/Cognifide/PowerShell Script template in the core database. I’ve put it in the same place I store all my my scripts – in the /sitecore/content/Applications/PowerShell Console/Scripts branch but feel free to store them anywhere in the tree.

  • Filled in the Script body part with my script.
  • I decided I want to see the publishing results as I want to verify if the items I expected got published.

ContextScriptRibbonBody

Now the UI integration bits…

Since I wanted it nicely integrated with the publish button – I’ve created a Publish My Items item of template /sitecore/templates/System/Menus/Menu item within the /sitecore/content/Applications/Content Editor/Menues/Publish/ branch in the core database, set it’s icon and reference the script item in the Message field using the following pattern:

item:executescript(id=$Target,script={0937769B-998D-4580-B9FE-730C4CDABECD},scriptDb=core)

where the script guid is the ID of your script and the scriptDb is the database the script is located in.

ContextScriptsRibbonBinding

That’s it really. You can download the solution but I would strongly recommend you try the manual approach – it’s really exciting to see the puzzles click in.

The solution requires the Sitecore PowerShell Console from Cognifide, available for free from Sitecore Shared Source site.

Sample scripts for Sitecore PowerShell Console

Find out about your configuration:

List your available site providers. You will use the name provided in the "Name" column in place of where I use "cms" in the later samples.

get-psdrive `
  | where-object { $_.Provider.ToString() -eq "CmsItemProvider"} `
  | format-table Name, Root

Sample scripts for working with pages:

For the same of brevity I’m assuming your location is already somewhere within a Sitecore tree (e.g. you did something akin to cd master:\content prior to executing the following scripts.

List all properties & fields available for a particular page (including Sitecore properties defined in the item template).

get-childitem | get-member -memberType property*

List all items in the CMS of which template name contains "Article"

get-childitem -recurse `
  | where-object { $_.TemplateName -match "Article" } `
  | format-table DisplayName, Name, TemplateName

List all subitems and how many days ago were they modified

get-childitem -recurse `
  | format-table -auto Name, `
  @{Label="Days since modified"; Expression={ `
  [datetime]::Now.Subtract($_.__Updated).Days} }

Delete all pages that have not been modified for the last 365 days.

get-childitem -recurse `
  | where-object {[datetime]::Now.Subtract($_.__Updated).Days -gt 365 } `
  | remove-item

List all items Updated over the last 24 hours and who changed them.

get-childitem -recurse `
  | where-object { $_.__Updated -gt [datetime]::Now.AddDays(-1) } `
  | format-table -property DisplayName, "__Updated By", {$_.Paths.Path}

List all pages that have their "Text" field filled in.

get-childitem -recurse `
  | where-object { $_.Text -ne $null } `
  | format-table -property DisplayName, {$_.Paths.Path}

Make a nice reviewer’s comment on all pages with their "Text" property filled in.

get-childitem -recurse `
  | where-object { $_.Text -ne $null } `
  | foreach-object { $_.ReviewersComment = "Great job providing content!" }

Add a warning to the beginning of "Text" property for all pages that have not been saved for the last 180 days.

get-childitem -recurse `
  | where-object {`
  [datetime]::Now.Subtract($_.__Updated).Days -ge 180 `
  -and $_.Text -ne $null }
  | foreach-object {$_.Text = "<p style='color:red'>Old content. Review pending!</p>" + $_.Text }

Replace a string in a property on all pages with another string (in this sample – removing the warning the last sample added).

$old_content = "<p style='color:red'>Old content. Review pending!</p>"
$new_content = "";
get-childitem -recurse `
  | where-object {$_.Text -match $old_content} `
  | foreach-object {$_.Text = $_.Text.Replace($old_content, $new_content) }

Display the 10 most recently changed pages ordered in the reverse chronological order or changes. Display the page name, who changed it and when as well as the page status.

get-childitem -recurse `
  | sort-object -property __Updated -descending `
  | select-object -First 10 `
  | format-table -property DisplayName, "__Updated By", __Updated

Sample scripts for working with Media Library / files:

Removes all .xml files from the “Old Xml Files” in Media library.

cd "master:\media library\Old Xml Files"
get-childitem -recurse `
  | where-object { $_.Extension -match "xml" } `
  | remove-item

Copy all files from the "staging" folder to the "production" folder in Media Library.

Copying files within media library is done as follows. This can also be done for items in the content branch.

copy-item "master:\media library\staging\*" "master:\media library\production\"

We had a situation in the company this week which required us to deliver the whole EPiServer virtual path provider file structure to the client – zipped. Easy enough… go to the EpiServer VPP directory and… well… ok… hmm… so the path provider is versioning and as a consequence the physical organization of files on the disk does not make any sense for a human trying to browse it.

Fine! So let’s create a native provider and do a copy and paste within the file manager…. hmm an exception complaining about the provider incompatibility…

Naturally, my knee-jerk reaction is – let’s do it with the PowerShell… which I recall was doing something like this in it’s previous version… The example I’ve tested and placed in the “Samples” tab was:

cd VPP:\
cd \Documents\
get-childitem -recurse |
    copy-item -destination \DocumentsNonVersioningVPP\

This worked well but flattened the directory structure – in other words useless for our client.

I’ve tried what should work in a plain PowerShell:

cd VPP:\
copy-item -path vpp:\Documents\*
          -destination vpp:\DocumentsNonVersioningVPP\
          -recurse -force

Now that didn’t work at all, and turned out to be a bug in my PowerShell plugin’s PSDrive provider. Unfortunately when I attempted to fix it by implementing the copy in the naive way – using UnifiedDirectory’s  Copy method I’ve run into the same exception about incompatibility between the classes that I’ve seen when trying to copy the files form the file manager.

Mmmkay… I’ll just implement the recursion myself… Read the rest of this article »

Most of this is based on the Microsoft’s Windows PowerShell Quick Reference however despite the sharing scripting runtimes the nature of the both shells are pretty different (although the differences are not as vast as one might think).

 

Windows PowerShell PowerShell Console for EPiServer
Interactive – command can ask for confirmations and can be aborted. User can be solicited to provide input. Batch – all commands are being executed in one go, the script has no chance to ask questions, go or no-go decisions have to be solved within the script.
Supports colouring. Supports plain text output only.
Supports command line arguments for running scripts. All arguments are defined directly within the script or derived from context automatically.
Can access any file depending on the rights of the user. Can only access files the web application identity can write to. Cannot access files on user’s machine but rather operates on the server’s file system. Cannot operate with elevated privileges.

That said, I considered that enough of the Reference document is irrelevant in the EPiServer scenario that it’s beneficial for the users of the console to have a bespoke cheat sheet created especially for the purpose of this plugin.

The content & samples of the original cheat sheet has been adjusted to more closely reflect scenarios usable for an EPiServer admin or developer.

So here go the EPiServer specific tips

Read the rest of this article »

Context PowerShell Scripts in EPiServer

Ok, so I’ve got my shot of endorphins writing about PowerShell last week (damn, it’s nice to be able to code again!), and I got pretty determined on making it usable and achieving all the goals I’ve initially envisioned. and in the process build a usable tool and a library of scripts that people can use either directly or to modify to meet their needs.

The goal for this week: Context Scripts

Context scripts are the first step to break the scripting out of the admin realm and into the editor’s space. Those scripts will still be written by admins and developers but the goal is for them to be usable by the authors. The goal for those scripts can be as trivial as e.g. syndicating all the great functionality little plugins like this Unpublish button by Ted in one place and then mix and match them to your liking.

Some of the important bits:

  • Context scripts are something that is visible to users on “Scripts” page.
  • Scripts can be exposed to everyone or just the groups of your liking… you define it in the script.
  • Scripts are grouped in collections that are defined in *.psepi files that you drop into your application folder

How do I define a script collection?

Read the rest of this article »

It’s been a while since I had a chance to do any coding… turns out leading a development division tends to not have much to do with development… who knew?!

But I’ve finally got a moment to sit down and refresh the EPiServer PowerShell console and make it compatible with both CMS versions 5 & 6. You could technically use it before on CMS 6 but the looks of it was broken. (previous version available here)

What triggered it was a talk with Michael Sadler earlier this week. Michael is a technical consultant by day (and a musician by night) in our solutions team in London. We talked  how he was doing a content audit for a client in one of the other CMS’es we’re supporting. Which really sounded like a daunting task… The content got exported as XML and then he had to write a bunch of C# code to parse it and create statistics for e.g. how many people edit the content, who created the majority of the content etc… well I couldn’t resist but to brag…

get-childitem -recurse | Group-Object ChangedBy | 
Sort count -descending | format-table -property count, name

looks through all the pages, and counts how many articles by each author there are in the CMS. Naturally you can also do it on a sub-branch of content as well.

I’m sorry Michael you had to go through this without PowerShell…Winking smile

Deployment

The plugin will detect the version of EPiServer it’s running under and will skin itself appropriately to match the CMS style.

PowerShell_CMS5  PowerShell_CMS6

As far as I can tell, your PowerShell scripts will be interchangeable between the versions, as far as they themselves don’t touch any API that’s undergone a breaking change.

Again…

Read the rest of this article »

Eric Evans on Domain Driven Design

Eric Evans of Domain Driven Design will be giving a talk at PUT just before Eclipse DemoCamp, June 24th @ 18:00.  Cognifide are sponsoring the event and it would be a great chance for you guys to dust of those design skills.  Domain Driven Design is going to be course that we hope to run out of the Cognifide Office late this year with the a little help from our friends at Skills Matter.

Register for the Domain Driven Design that is platform-neutral and in fact, Eric gives .NET and Java versions of the courses.

More on Eric & DDD:
http://skillsmatter.com/course/design-architecture/domain-driven-design
http://skillsmatter.com/podcast/design-architecture/domain-driven-design
http://www.infoq.com/interviews/domain-driven-design-eric-evans

Advanced Language Manipulation Tool for EPiServer

Have you ever (or have your customers) created and edited a page in one language only to realize that their selected locale was wrong? Have you ever wished you could delete a master language branch of a page  after creating its localized counterpart but you could only delete the newly created slave language instead? Have a customer ever requested that they could copy a whole branch and you convert it to another language so that they could then translate in-place?

Well I have… and I’m sure I will. And so did Fredrikj on the our #epicode IRC channel ;).

Basically I had the tool that would convert from one language to another, but Fredrikj requested something that would switch master language of a page from one to another. Since I’ve already had some of the work done, I’ve updated the stored procedure I’ve written some time ago and slapped a nice GUI up on it. Here’s the result:

 

AndvancedLanguageTool

What the tool allows you to do is perform either language conversion or master branch switching on a selected page and all of its children (if you choose so).

The stored procedure have been updated to work on CMS5 R2 (will no longer work on R1 – but if you need that functionality, comment here or give me a shout and I’ll create a compatible version for you).

A word of caution though – I take no guarantee whatsoever about its operation. Especially, if you wreck your client’s database with it. I did what I could to prevent some of the obvious problems (like switching to a non existing master or converting to an existing one) but I will not be responsible if it won’t work for you. make a database backup and experiment there before you do any changes on the real data. That said – it works for me, so I think it should also work for you.

You can download the archive containing the tool here. unzip it to your EPiServer web application folder keeping the folder structure or the plugin reference will be wrong. Include the *.aspx and the *.cs files in your project and apply the SQL file to your database (The manipulation is performed by a stored procedure located in the file).

Also if you’re performing the change in a load balanced environment, you may need to restart the other servers once you do the changes. I reset the DataFactory cache, but I am not sure it propagates through to other servers.

Immediately after you implement the VirtualPathProvider proxy from my previous post you will notice a one fairly serious lack in it. Namely all the files within that provider will be hiding behind the registration form. That is not cool for a couple of reasons…

  • You may want to keep all of the files in one store – being forced to put them into a designated folder is not desired.
  • You may want to make some file freely available for some time and lock it after a while, or the other way around (e.g. to allow the robots to crawl it initially). having to move them is just silly and defeats the purpose.

So how do you discriminate the files that you want locked from those that you want to be publically available, and potentially from those that you want only the logged in users to be able to get?

Specifying the EPiServer File Metadata sweetness

One of the potential solutions would be to define a special rights group and check for that group for the people that have your “registered” magic-cookie. That however introduces a bogus group, and I would rather like to avoid that. However if you look into the FileSummary.config file that’s located in your web application folder you will find a slightly mysterious content. A bit of hacking reveals that you can actually add your own metadata to the file. For example adding the access rights based on what I’ve established above would look as follows (the content you can already find in the file that comes with the public templates that-we-all-oh-so-love is skipped):

Read the rest of this article »