Home » PowerShell

Category Archives: PowerShell

Add to the Sitecore Gutter Using Sitecore PowerShell Extensions

Last Wednesday I had the pleasure of presenting Sitecore PowerShell Extensions (SPE) at the Milwaukee Sitecore Meetup. The goal of my presentation was to share how easy it is to add, execute and reuse PowerShell scripts in SPE, and I did this on version 3.0 of SPE in a Sitecore XP 8 instance.

In my presentation, I showed how quickly one can add a custom Sitecore gutter icon with SPE, and used the following script to demonstrate that:

<#
    .NAME 
        Item Has 20 Or More Sub-items Gutter

    .SYNOPSIS
        Renders gutters indicated whether the item has more than 20 sub-items.
     
    .NOTES
        Mike Reynolds
#>

$item = Get-Item .
$gutter = New-Object Sitecore.Shell.Applications.ContentEditor.Gutters.GutterIconDescriptor
if($item.Children.Count -gt 20) {
    $gutter.Icon = "Applications/16x16/delete.png"
    $gutter.Tooltip = "This Item has more than 20 sub-items!"
    
} else {
    $gutter.Icon = "Applications/16x16/check2.png"
    $gutter.Tooltip = "This Item has 20 or less sub-items."
}

$gutter

The script above creates a new Sitecore.Shell.Applications.ContentEditor.Gutters.GutterIconDescriptor instance — this class is defined in Sitecore.Kernel.dll — and sets a certain icon and tooltip on it if the context Item has more than 20 sub-items. If the Item has 20 or less sub-items, a different icon and tooltip are used.

The script then outputs the GutterIconDescriptor instance.

I then saved the above script to the Gutter integration point in my SPE module:

gutter-script-ise

Now that it’s saved, we have to sync it to the Sitecore Gutter:

sync-gutter

We should be good to go! Let’s test this out!

I went to my content tree; right-clicked in the gutter area; and was presented with the gutter menu:

gutter-menu

After clicking my new gutter menu option, I was presented with gutter icons next to Items in my tree:

cat-page-one-more-20-sub-items

As you can see, the Item with the X icon has more than 20 sub-items:

more-than-20-sub-items-expanded

For comparison, the following Item has a green check-mark icon next to it which indicates it has 20 or less sub-items (this Item actually has 10 sub-items):

less-than-20-sub-items

If you have any thoughts and/or suggestions on this, or have ideas for other gutter icon scripts that can be incorporated into SPE, please share in a comment.

If you would like to watch the Milwaukee Sitecore Meetup presentation where I showed the above — you’ll also get to see some cool Sitecore PowerShell Extensions stuff from Adam Brauer, Senior Product Engineer at Active Commerce, in this presentation as well — have a look below:

Until next time, keep on learning and sharing!

Bucket Items in Sitecore using a Custom Commandlet in Sitecore PowerShell Extensions

Last Wednesday I had the privilege to present Sitecore PowerShell Extensions (SPE) at the Milwaukee Sitecore Meetup. During my presentation, I demonstrated how easy it is to add, execute and reuse PowerShell scripts in SPE, and I showcased version 3.0 of SPE on Sitecore XP 8.

Unfortunately, I ran out of time before showing how one can go about creating a custom commandlet in SPE, and hope to make it up to everyone by sharing the commandlet I wrote for the presentation in this post.

I wrote the following commandlet to convert an Item into an Item Bucket in Sitecore:

using System;
using System.Management.Automation;

using Sitecore.Data.Items;
using Sitecore.Shell.Framework.Commands;

using Cognifide.PowerShell.Commandlets;
using Cognifide.PowerShell.Commandlets.Interactive.Messages;

namespace Sitecore.Sandbox.SPE.Commandlets.Buckets
{
    [Cmdlet(VerbsData.ConvertTo, "Bucket"), OutputType(new Type[] { typeof(Item) })]
    public class ConvertToBucketCommand : BaseItemCommand
    {
        protected override void ProcessItem(Item item)
        {
            try
            {
                PutMessage(new ShellCommandInItemContextMessage(item, "item:bucket"));   
            }
            catch (Exception exception)
            {
                WriteError(new ErrorRecord(exception, "sitecore_new_bucket_error", ErrorCategory.NotSpecified, Item));
            }

            WriteItem(Item);
        }
    }
}

The above commandlet implements the ProcessItem() method — this method is declared abstract in one of the ancestor classes of the class above — and leverages the framework of SPE to invoke a Sheer UI command to bucket the Item passed to the method — one of the ancestor classes of this class passes the Item to be processed.

The above highlights how in SPE we are employing the Template method pattern for many “out of the box” commandlets. This involves inheriting from an abstract base class — Cognifide.PowerShell.Commandlets.BaseItemCommand in Cognifide.PowerShell.dll (this assembly comes with the SPE module) is an example of one of these base classes — and implementing methods that are defined as abstract. The parent or an ancestor class will do the brunt of the work behind the scenes, and use your method implementation for specifics.

As a side note, we also provide method hooks as well — these are virtual methods defined on a base or ancestor class — which you can override to change how they work to meet your particular needs.

I then wired the above up using a Sitecore include configuration file:

<?xml version="1.0" encoding="utf-8" ?>
<configuration xmlns:patch="http://www.sitecore.net/xmlconfig/">
  <sitecore>
    <powershell>
      <commandlets>
        <add Name="Custom Bucket Commandlets" type="*, Sitecore.Sandbox.SPE" />
      </commandlets>
    </powershell>
  </sitecore>
</configuration>

I deployed the above to my Sitecore instance; loaded up the Integrated Scripting Environment (ISE) in SPE; and saw that my commandlet was registered using the Control-Space shortcut key:

convert-to-bucket-ise-control-space

Let’s take this for a spin. Let’s convert the Home Item into an Item Bucket:

home-before-bucket

Here’s my script to do that:

ise-convert-home-bucket

I clicked the execute button, and then got this confirmation dialog:

ise-convert-home-bucket-confirm

I then clicked the “Ok” button and was immediately presented with this dialog:

ise-convert-home-bucket-processing

As you can see it worked! The Home Item in my content tree is now an Item Bucket:

home-after-bucket

If you have any thoughts on this or ideas for other custom commandlets for SPE, please share in a comment.

If you would like to watch the Milwaukee Sitecore Meetup presentation where I showcased Sitecore PowerShell Extensions — and as a bonus you’ll also get to see some real-life application of SPE from Adam Brauer, Senior Product Engineer at Active Commerce, in this presentation as well — it has been recorded for posterity, and you can watch it here:

Until next time, stay curious, keep experimenting, and let’s keep on sharing all the Sitecore things!

Bucket and Unbucket Items via Custom Item Context Menu Options Using Sitecore PowerShell Extensions

Last Wednesday I was honored to present Sitecore PowerShell Extensions (SPE) at the Milwaukee Sitecore Meetup. My presentation was all about how easy it is to add, execute and reuse PowerShell scripts in SPE, and I showcased this using version 3.0 of SPE on Sitecore XP 8.

During the presentation, I demonstrated how one can go about adding custom Item Context Menu options using SPE, and did so with the following scripts which bucket and unbucket Sitecore Items:

 <#
    .NAME 
        Convert To Item Bucket

    .SYNOPSIS
        Converts the context item to an Item Bucket
     
    .NOTES
        Mike Reynolds
#>
 
$item = Get-Item .

if($item."__Is Bucket" -eq "1") {
   return 
}

Get-ChildItem . -Recurse | %{ $_.__Bucketable = "1" }
Invoke-ShellCommand -Name "item:bucket" -Item $item
Close-Window

The above PowerShell script basically checks to see if an Item is an Item Bucket and does nothing if it is. If the Item is not an Item Bucket, we make sure all sub-items are bucketable — we just tick the “__Bucketable” Checkbox field on them — and invoke the Sheer UI command for converting an Item into an Item Bucket — this is done via the Invoke-ShellCommand commandlet that ships with SPE — and then close the script execution dialog users are presented with when SPE executes a script in the Item Context Menu.

The following script does the exact opposite of the script above — it converts an Item Bucket back to a regular Sitecore Item using the Sheer UI command for unbucketing:

<#
    .NAME 
        Convert Item Bucket To A Regular Item

    .SYNOPSIS
        Converts an Item Bucket to a regular Item
     
    .NOTES
        Mike Reynolds
#>

$item = Get-Item .

if($item."__Is Bucket" -ne "1") {
   return 
}

Invoke-ShellCommand -Name "item:unbucket" -Item $item
Close-Window
 

I then saved the above scripts to a Context Menu integration point in a SPE module I created during the presentation using the SPE Integrated Scripting Environment (ISE) (to get to the ISE in SPE, go to Sitecore ==> Development Tools ==> PowerShell ISE in the Sitecore Start menu of the Sitecore Desktop):

Saved-context-menu-script

I’ve omitted screenshots on saving the “Convert Item Bucket To A Regular Item” script for brevity.

I then set rules in the “Show if rules are met or not defined” field on both Context Menu script Items — we only want the “Convert To Item Bucket” Context Menu option to show when the Item isn’t an Item Bucket, and the “Convert Item Bucket To A Regular Item” Context Menu option to show when the Item it is an Item Bucket:

The “Convert To Item Bucket” Context Menu item (this Item was saved to /sitecore/system/Modules/PowerShell/Script Library/SitecoreUG Module/Content Editor/Context Menu/Convert To Item Bucket in my Sitecore instance):

set-rules-convert-to-bucket

The “Convert Item Bucket To A Regular Item” Context Menu item:

set-rules-convert-to-unbucket

After saving the above, I navigated to an Item in my content tree that has sub-items, right-clicked on it, and clicked on the “Convert To Item Bucket” Context Menu option:

convert-to-bucket-right-click

I was then presented with a confirmation dialog:

convert-to-bucket-confirm

As you can see the Item is now an Item Bucket:

item-is-a-bucket

I right-clicked on the Item again, and clicked on the “Convert Item Bucket To A Regular Item” Context Menu option:

convert-to-unbucket-right-click

I was presented with another confirmation dialog:

convert-to-ubucket-confirm

As you can see the Item is no longer an Item Bucket:

no-longer-a-bucket

If you have any thoughts on this or ideas for other Context Menu PowerShell scripts for SPE, please drop a comment.

If you would like to watch the Milwaukee Sitecore Meetup presentation where I showed the above — you’ll also get to see some cool Sitecore PowerShell Extensions stuff from Adam Brauer, Senior Product Engineer at Active Commerce, in this presentation as well — have a look below:

Until next time, have a Sitecoretastic day!

Add Scripts to the PowerShell Toolbox in Sitecore PowerShell Extensions

During our ‘Take charge of your Sitecore instance using Sitecore tools’ session at Sitecore Symposium 2014 Las Vegas, Sitecore MVP Sean Holmesby and I shared how easy it is to leverage/extend popular Sitecore development tools out there, and built up a fictitious Sitecore website where we pulled in #SitecoreSelfie Tweets.

The code that pulls in these Tweets is supposed to follow a naming convention where Tweet IDs are appended to Media Library Item names, as you can see here:

sean-profile-image

Sadly, right before our talk, I mistakenly 😉 made a code change which broke our naming convention for some images:

sean-selfie-image

Upon further investigation, we had discovered our issue was much larger than anticipated: all Selfie Media Library Item names do not end with their Tweet IDs:

no-tweet-ids

To fix this, I decided to create a PowerShell Toolbox script in Sitecore PowerShell Extensions using the following script:

<#
    .SYNOPSIS
        Rename selfie image items to include tweet ID where missing.
     
    .NOTES
        Mike Reynolds
#>
$items = Get-ChildItem -Path "master:\sitecore\content\Social-Media\Twitter\Tweets" -Recurse | Where-Object { $_.TemplateName -eq "Tweet" }

$changedItems = @()
foreach($item in $items) {
	$tweetID = $item["TweetID"]
	$selfieImageField = [Sitecore.Data.Fields.ImageField]$item.Fields["SelfieImage"]
	$selfieImage = $selfieImageField.MediaItem
	if($selfieImage -ne $null -and -not $selfieImage.Name.EndsWith($tweetID)) {
		$oldName = $selfieImage.Name
		$newName = $oldName + "_" + $tweetID
		$selfieImage.Editing.BeginEdit()
		$selfieImage.Name = $newName
		$selfieImage.Editing.EndEdit()
		
		$changedItem = New-Object PSObject -Property @{            
		    Icon = $selfieImage.Appearance.Icon
			OldName = $oldName
			NewName = $newName  
			Path = $selfieImage.Paths.Path
			Alt = $selfieImage["Alt"]
			Title = $selfieImage["Title"]
			Width = $selfieImage["Width"]
			Height = $selfieImage["Height"]
			MimeType = $selfieImage["Mime Type"]
			Size = $selfieImage["Size"]           
		}
		
		$changedItems += $changedItem
	}
}

if($changedItems.Count -gt 0) {
    $changedItems |
        Show-ListView -Property @{Label="Icon"; Expression={$_.Icon} },
            @{Label="Old Name"; Expression={$_.OldName} },
    		@{Label="New Name"; Expression={$_.NewName} },
    		@{Label="Path"; Expression={$_.Path} },
            @{Label="Alt"; Expression={$_.Alt} },
    		@{Label="Title"; Expression={$_.Title} },
            @{Label="Width"; Expression={$_.Width} },
            @{Label="Height"; Expression={$_.Height} },
            @{Label="Mime Type"; Expression={$_.MimeType} },
    		@{Label="Size"; Expression={$_.Size} }
} else {
    Show-Alert "There are no selfie image items missing tweet IDs in their name."
}
Close-Window

The above PowerShell script grabs all Tweet Items in Sitecore; ascertains whether referenced Selfie images in the Media Library — these are referenced in the “SelfieImage” field on the Tweet Items — end with the Tweet IDs of their referring Tweet Items (the Tweet ID is stored in a field on the Tweet Item); and renames the Selfie images to include their Tweet IDs if not. The script also launches a dialog showing the images that have changed.

To save the above script in the PowerShell Toolbox, I launched the PowerShell Integrated Scripting Environment (ISE) in Sitecore PowerShell Extensions:

powershell-ise-context-menu

I pasted in the above script, and saved it in the PowerShell Toolbox library:

toolbox-save-as

As you can see, our new script is in the PowerShell Toolbox:

new-script-in-toolbox

I then clicked the new PowerShell Toolbox option, and was presented with the following dialog:

selfie-toolbox-script-results

The above dialog gives information about the images along with their old and new Item names.

I then navigated to where these images live in the Media Library, and see that they were all renamed to include Tweet IDs:

selfie-images-tweet-ids

If you have any thoughts on this, or suggestions for other PowerShell Toolbox scripts, please share in a comment.

Until next time, have a #SitecoreSelfie type of day!

Make Bulk Item Updates using Sitecore PowerShell Extensions

In my Sitecore PowerShell Extensions presentation at the Sitecore User Group Conference 2014, I demonstrated how simple it is to make bulk Item updates — perform the same update to multiple Sitecore items — using a simple PowerShell script, and thought I would write down what I had shown.

Sadly, I do not remember which script I had shared with the audience — the scratchpad text file I referenced during my presentation contains multiple scripts for making bulk updates to Items (if you attended my talk, and remember exactly what I had shown, please drop a comment).

Since I cannot recall which script I had shown — please forgive me 😉 — let’s look at the following PowerShell script (this might be the script I had shown):

@(Get-Item .) + (Get-ChildItem -r .) | ForEach-Object { Expand-Token $_ }

This script grabs the context Item — this is denoted by a period — within the PowerShell ISE via the Get-Item command, and puts it into an array so that we can concatenate it with an array of all of its descendants — this is returned by the Get-ChildItem command with the -r parameter (r stands for recursive). The script then iterates over all Items in the resulting array, passes each to the Expand-Token command — this command is offered “out of the box” in Sitecore PowerShell Extensions — which expands tokens in every field on the Item.

Let’s see this in action!

My home Item has some tokens in its Title field:

home-tokens

One of its descendants also has tokens in its Title field:

descendant-tokens

I opened up the PowerShell ISE, wrote my script, and executed:

powershell-ise-tokens

As you can see, the tokens on the home Item were expanded:

home-tokens-expanded

They were also expanded on the home Item’s descendant:

descendant-tokens-expanded

If you have any thoughts or questions on this, please share in a comment.

Execute PowerShell Scripts in Scheduled Tasks using Sitecore PowerShell Extensions

At the Sitecore User Group Conference 2014, I demonstrated how to invoke PowerShell scripts in a Sitecore Scheduled Task using Sitecore PowerShell Extensions, and felt I should pen what I had shown in a blog post — yes, you guessed it: this is that blog post. 😉

In my presentation, I shared the following PowerShell script with the audience:

ForEach($site in [Sitecore.Configuration.Factory]::GetSiteNames()) {
    $siteInfo = [Sitecore.Configuration.Factory]::GetSiteInfo($site)
    if($siteInfo -ne $null) {
         $siteInfo.HtmlCache.Clear()   
         $logEntry = [string]::Format("HtmlCache.Clear() invoked for {0}", $siteInfo.Name)
         Write-Log $logEntry
    }
}

The script above iterates over all sites in your Sitecore instance, clears the Html Cache for each, and creates log entries expressing the Html Cache was cleared for all sites processed.

You would probably never have to use a script like the one above. I only wrote it for demonstration purposes since I couldn’t think of a more practical example to show. If you can think of any practical examples, or feel the script above has some practicality, please share in a comment.

I wrote, tested, and saved the above script in the PowerShell ISE:

powershell-ise-task

The PowerShell script was saved to a new Item created by the dialog above:

task-location-script-library

I then created a Schedule Item to invoke the script housed in the Item above (to learn more about Sitecore Scheduled Tasks, please see John West‘s post discussing them):

create-schedule-item-spe

I saved my Item, waited a bit, and opened up my latest Sitecore log file:

html-cache-clear-spe

As you can see, the Html Cache was cleared for each site in my Sitecore instance.

If you have any questions/comments/thoughts on this, please drop a comment.

Build a Custom Command in Sitecore PowerShell Extensions

In my Sitecore PowerShell Extensions presentation at the Sitecore User Group Conference 2014, I showed the audience how easy it is to build custom commands for Sitecore PowerShell Extensions, and thought it would be a good idea to distill what I had shown into a blog post for future reference. This blog post embodies that endeavor.

During my presentation, I shared an example of using the template method pattern for two commands using the following base class:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Management.Automation;

using Sitecore.Data.Items;

using Cognifide.PowerShell.PowerShellIntegrations.Commandlets;

namespace Sitecore.Sandbox.SPE.Commandlets.Data
{
    public abstract class EditItemCommand : BaseCommand
    {
        protected override void ProcessRecord()
        {
            ProcessItem(Item);
            if (!Recurse.IsPresent)
            {
                return;
            }
            
            ProcessItems(Item.Children, true);
        }

        private void ProcessItems(IEnumerable<Item> items, bool recursive)
        {
            foreach (Item item in items)
            {
                ProcessItem(item);
                if (recursive && item.Children.Any())
                {
                    ProcessItems(item.Children, recursive);
                }
            }
        }

        private void ProcessItem(Item item)
        {
            item.Editing.BeginEdit();
            try
            {
                EditItem(item);
                item.Editing.EndEdit();
            }
            catch (Exception exception)
            {
                item.Editing.CancelEdit();
                throw exception;
            }

            WriteItem(item);
        }

        protected abstract void EditItem(Item item);

        [Parameter(ValueFromPipeline = true, ValueFromPipelineByPropertyName = true)]
        public Item Item { get; set; }

        [Parameter]
        public SwitchParameter Recurse { get; set; }
    }
}

The class above defines the basic algorithm for editing an Item — the editing part occurs in the EditItem() method which must be defined by subclasses — and all of its descendants when the Recurse switch is supplied to the command. When the Recursive switch is supplied, recursion is employed to process all descendants of the Item once editing of the supplied Item is complete.

The following subclass of the EditItemCommand class above protects a supplied Item in its implementation of the EditItem() method:

using System;
using System.Management.Automation;

using Sitecore.Data.Items;

namespace Sitecore.Sandbox.SPE.Commandlets.Data
{
    [OutputType(new Type[] { typeof(Sitecore.Data.Items.Item) }), Cmdlet("Protect", "Item")]
    public class ProtectItemCommand : EditItemCommand
    {
        protected override void EditItem(Item item)
        {
            item.Appearance.ReadOnly = true;
        }
    }
}

Conversely, the following subclass of the EditItemCommand class unprotects the passed Item in its EditItem() method implementation:

using System;
using System.Management.Automation;

using Sitecore.Data.Items;

namespace Sitecore.Sandbox.SPE.Commandlets.Data
{
    [OutputType(new Type[] { typeof(Sitecore.Data.Items.Item) }), Cmdlet("Unprotect", "Item")]
    public class UnprotectItemCommand : EditItemCommand
    {
        protected override void EditItem(Item item)
        {
            item.Appearance.ReadOnly = false;
        }
    }
}

The verb and noun for each command is defined in the Cmdlet class attribute set on each command class declaration.

I then registered all of the above in Sitecore using the following configuration file:

<configuration xmlns:patch="http://www.sitecore.net/xmlconfig/">
  <sitecore>
    <powershell>
      <commandlets>
            <add Name="Sitecore Sandbox Commandlets" type="*, Sitecore.Sandbox" />
      </commandlets>
    </powershell>
  </sitecore>                                                                                    
</configuration>

Since everything looks copacetic — you got to love a developer’s optimism 😉 — I built and deployed all of the above to my Sitecore instance.

Let’s take this for a spin!

I selected my home Item knowing it is not protected:

not-protected-home

I then looked to see if it had an unprotected descendant, and found the following item:

not-protected-page-three

I then ran a script on the home Item using our new command to protect an item, and supplied the Recurse switch to protect all descendants:

protect-item-command-powershell-ise

As you can see, the home Item is now protected:

protected-home

Its descendant is also protected:

protected-page-three

Let’s now unprotect them. I ran a script on the home Item using our new command to unprotect an item, and supplied the Recurse switch to process all descendants:

unprotect-item-command-powershell-ise

As you can see, the home Item is now unprotected again:

not-protected-again-home

Its descendant is also unprotected:

not-protected-again-page-three

If you have any thoughts or ideas around improving anything you’ve seen in this post, or have other ideas for commands that should be included in Sitecore PowerShell Extensions, please drop a comment.

I would also like to point out that I had written a previous blog post on creating a custom command in Sitecore PowerShell Extensions. You might want to go check that out as well.

Until next time, have a scriptastic day! 🙂