Reading some of the blogs from the Sitecore community I find it pretty apparent that we didn’t do a great job advocating the optimizations that PowerShell Extensions have introduced for working with Sitecore items. This blog attempts to rectify this problem to a degree.

group_construction_workers_400_clr_8597

How do I retrieve my Sitecore items the PowerShell way?

The most natural way to retrieve Sitecore items is with use of Get-Item and Get-ChildItem commandlets. That is because those 2 commandlets add a PowerShell wrapping around them that allows the functionalities that I’m going to describe in the next section of this blog after I’ll tell you all about retrieving items.

If you have retrieved your item directly using the Sitecore API you can still add the nice wrapper when you pipe them through the Wrap-Item commandlet as well. Some of those enhancements work in the older versions of PowerShell Extensions but I would encourage you to upgrade to the latest version (2.7 at the time this blog was written) to leverage the full potential of the environment.

Getting Items based on path

If you know the path to your item and the database you can simply retrieve your item as follows:

PS master:\>Get-Item master:/content/home

Name Children Languages                Id                                     TemplateName
---- -------- ---------                --                                     ------------
Home True     {en, de-DE, es-ES, pt... {110D559F-DEA5-42EA-9C1C-8A5DF7E70EF9} Sample Item

You’ve noticed we’ve skipped the /sitecore part in the path – that’s because this item is represented by the root item of the drive: master: The above will return the latest version of the item in your current language. But what if you want the item in another language? No problem – let’s retrieve the Danish version of Home

PS master:\>Get-Item master:/content/home -Language da | Format-Table DisplayName, Language, Id, Version, TemplateName

DisplayName Language ID                                     Version TemplateName
----------- -------- --                                     ------- ------------
Hjem        da       {110D559F-DEA5-42EA-9C1C-8A5DF7E70EF9} 1       Sample Item

I’ve formatted the output above to show you that indeed the right language war returned. If you want to get all languages – the commandlet will support wildcards for both -Language and -Version parameter. The following returns the latest version for all languages of an item:

PS master:\>Get-Item master:/content/home -Language * | Format-Table DisplayName, Language, Id, Version, TemplateName
 
DisplayName Language ID                                     Version TemplateName
----------- -------- --                                     ------- ------------
Home        en       {110D559F-DEA5-42EA-9C1C-8A5DF7E70EF9} 1       Sample Item
Home        de-DE    {110D559F-DEA5-42EA-9C1C-8A5DF7E70EF9} 1       Sample Item
Home        pl-PL    {110D559F-DEA5-42EA-9C1C-8A5DF7E70EF9} 1       Sample Item
Home        en-US    {110D559F-DEA5-42EA-9C1C-8A5DF7E70EF9} 3       Sample Item
Home        en-GB    {110D559F-DEA5-42EA-9C1C-8A5DF7E70EF9} 1       Sample Item
Hjem        da       {110D559F-DEA5-42EA-9C1C-8A5DF7E70EF9} 1       Sample Item

You can see that the en-US above shows the version number as 3… meaning there are other versions… let’s retrieve the item in all languages and all versions…

PS master:\>Get-Item master:/content/home -Language * -Version *| Format-Table DisplayName, Language, Id, Version, TemplateName
 
DisplayName Language ID                                     Version TemplateName
----------- -------- --                                     ------- ------------
Home        en       {110D559F-DEA5-42EA-9C1C-8A5DF7E70EF9} 1       Sample Item
Home        de-DE    {110D559F-DEA5-42EA-9C1C-8A5DF7E70EF9} 1       Sample Item
Home        pl-PL    {110D559F-DEA5-42EA-9C1C-8A5DF7E70EF9} 1       Sample Item
Home        en-US    {110D559F-DEA5-42EA-9C1C-8A5DF7E70EF9} 1       Sample Item
Home        en-US    {110D559F-DEA5-42EA-9C1C-8A5DF7E70EF9} 2       Sample Item
Home        en-US    {110D559F-DEA5-42EA-9C1C-8A5DF7E70EF9} 3       Sample Item
Home        en-GB    {110D559F-DEA5-42EA-9C1C-8A5DF7E70EF9} 1       Sample Item
Hjem        da       {110D559F-DEA5-42EA-9C1C-8A5DF7E70EF9} 1       Sample Item

So you can see that those can be used in conjunction to retrieve all possible variants of the item in every language and every version. Filters like en-* and as expected an execution with such filter would return all items in the English language, ignoring the region.

Similarly Get-ChildItem supports this functionality for its children.

PS master:\>Get-ChildItem master:/content -Language * -Version * | Format-Table DisplayName, Language, Id, Version, TemplateName
 
DisplayName         Language ID                                     Version TemplateName
-----------         -------- --                                     ------- ------------
Copy of Home        en       {503713E5-F9EE-4847-AEAF-DD13FD853004} 1       Sample Item
Home                de-DE    {503713E5-F9EE-4847-AEAF-DD13FD853004} 1       Sample Item
Copy of Home        pl-PL    {503713E5-F9EE-4847-AEAF-DD13FD853004} 1       Sample Item
Copy of Home        en-US    {503713E5-F9EE-4847-AEAF-DD13FD853004} 1       Sample Item
Hjem                da       {503713E5-F9EE-4847-AEAF-DD13FD853004} 1       Sample Item
Home                en       {110D559F-DEA5-42EA-9C1C-8A5DF7E70EF9} 1       Sample Item
Home                de-DE    {110D559F-DEA5-42EA-9C1C-8A5DF7E70EF9} 1       Sample Item
Home                pl-PL    {110D559F-DEA5-42EA-9C1C-8A5DF7E70EF9} 1       Sample Item
Home                en-US    {110D559F-DEA5-42EA-9C1C-8A5DF7E70EF9} 1       Sample Item
Home                en-US    {110D559F-DEA5-42EA-9C1C-8A5DF7E70EF9} 2       Sample Item
Home                en-US    {110D559F-DEA5-42EA-9C1C-8A5DF7E70EF9} 3       Sample Item
Home                en-GB    {110D559F-DEA5-42EA-9C1C-8A5DF7E70EF9} 1       Sample Item
Hjem                da       {110D559F-DEA5-42EA-9C1C-8A5DF7E70EF9} 1       Sample Item
CognifideCom        en       {6A1EC18E-AF9B-443E-84C7-5528F2363A10} 1       TenantTemplate
Demo                en       {4F02AEDF-1CC6-4B84-8B6E-F5CB465F8AD9} 1       TenantTemplate
GetEngaged          en       {68AD4037-EE50-4615-BA2E-AE11B1D3F6CC} 1       TenantTemplate
GetEngaged          de-DE    {68AD4037-EE50-4615-BA2E-AE11B1D3F6CC} 1       TenantTemplate
GetEngaged          es-ES    {68AD4037-EE50-4615-BA2E-AE11B1D3F6CC} 1       TenantTemplate
GetEngaged          pt-BR    {68AD4037-EE50-4615-BA2E-AE11B1D3F6CC} 1       TenantTemplate
GetEngaged          pl-PL    {68AD4037-EE50-4615-BA2E-AE11B1D3F6CC} 1       TenantTemplate
RetailTheatre       en       {436460CF-33E2-47C0-90B5-F856630266E3} 1       Node
Showcase            en       {DB8C05B8-25B5-42DE-B6CB-4ACE186283DA} 1       TenantTemplate
Zengage             en       {D55FE1D5-1CAC-4A2E-9DFE-D624D0F51886} 1       TenantTemplate

Getting large number of filtered items with Sitecore queries

Sometimes however it’s not the most efficient to operate on items by traversing the tree using Get-ChildItem especially if you need to work on large trees but need to select only a few items of e.g. a specific template. For this we’ve introduced support for the Sitecore query within our provider. Below example fetches all items in /sitecore/content/ branch in the Master database for items of Sample Item template:

PS master:\>Get-Item master: -Query "/sitecore/content//*[@@templatename='Sample Item']"
 
Name                             Children Languages                Id                                     TemplateName
----                             -------- ---------                --                                     ------------
Copy of Home                     True     {en, de-DE, es-ES, pt... {503713E5-F9EE-4847-AEAF-DD13FD853004} Sample Item
Home                             True     {en, de-DE, es-ES, pt... {110D559F-DEA5-42EA-9C1C-8A5DF7E70EF9} Sample Item

Naturally the -Language parameter still works…

PS master:\>Get-Item master: -Query "/sitecore/content//*[@@templatename='Sample Item']" -Language * -Version * | Format-Table DisplayName, Language, Id, Version, TemplateName -auto
 
DisplayName  Language ID                                     Version TemplateName
-----------  -------- --                                     ------- ------------
Copy of Home en       {503713E5-F9EE-4847-AEAF-DD13FD853004} 1       Sample Item
Home         de-DE    {503713E5-F9EE-4847-AEAF-DD13FD853004} 1       Sample Item
Copy of Home pl-PL    {503713E5-F9EE-4847-AEAF-DD13FD853004} 1       Sample Item
Copy of Home en-US    {503713E5-F9EE-4847-AEAF-DD13FD853004} 1       Sample Item
Hjem         da       {503713E5-F9EE-4847-AEAF-DD13FD853004} 1       Sample Item
Home         en       {110D559F-DEA5-42EA-9C1C-8A5DF7E70EF9} 1       Sample Item
Home         de-DE    {110D559F-DEA5-42EA-9C1C-8A5DF7E70EF9} 1       Sample Item
Home         pl-PL    {110D559F-DEA5-42EA-9C1C-8A5DF7E70EF9} 1       Sample Item
Home         en-US    {110D559F-DEA5-42EA-9C1C-8A5DF7E70EF9} 1       Sample Item
Home         en-US    {110D559F-DEA5-42EA-9C1C-8A5DF7E70EF9} 2       Sample Item
Home         en-US    {110D559F-DEA5-42EA-9C1C-8A5DF7E70EF9} 3       Sample Item
Home         en-GB    {110D559F-DEA5-42EA-9C1C-8A5DF7E70EF9} 1       Sample Item
Hjem         da       {110D559F-DEA5-42EA-9C1C-8A5DF7E70EF9} 1       Sample Item

Using ID to address an item.

You can also fetch for items by ID like:

PS master:\>Get-Item master: -ID "{110D559F-DEA5-42EA-9C1C-8A5DF7E70EF9}"
 
Name  Children Languages                Id                                     TemplateName
----  -------- ---------                --                                     ------------
Home  True     {en, de-DE, es-ES, pt... {110D559F-DEA5-42EA-9C1C-8A5DF7E70EF9} Sample Item

Using Item Uri

You can also query item using its Uri (which contains version and language information encoded directly in it):

PS master:\>Get-Item master: -Uri "sitecore://master/{110D559F-DEA5-42EA-9C1C-8A5DF7E70EF9}?lang=en&ver=1"
 
Name Children Languages                Id                                     TemplateName
---- -------- ---------                --                                     ------------
Home True     {en, de-DE, es-ES, pt... {110D559F-DEA5-42EA-9C1C-8A5DF7E70EF9} Sample Item

In both of the above cases you need to specify a database (like master:) as the path – that’s because PowerShell needs to know in context of which provider those parameters are supposed to be executed as other providers are not support those for obvious reasons. (e.g. files don’t have versions or support for languages).

So what are those item extensions I was talking at the beginning?

I often see the following two ways of accessing and changing fields used in scripts:

Set-ItemProperty -Path master:/content/home -Name "Title" -Value "New Title"

Or something that would feel very natural for a Sitecore developer:

$item = Get-Item master:/content/home
$item.Editing.BeginEdit()
$item["Title"] = "New Title"
$item.Editing.EndEdit()

And I’m it makes me cry a little when I see it… not because it won’t work. You haven’t done anything wrong – it’s just me who failed to communicate that there are much better ways of doing those operations (which really prompted me to write this blog). The items that you’re getting from the provider give you a better way of doing it as they turn all the fields into semi-native PowerShell properties so instead of doing the above you can do:

$item = Get-Item master:/content/home
$item.Title = "New Title"

… or even shorter if you don’t want to use a variable:

(Get-Item master:/content/home).Title = "New Title"

But wait – there’s more… it doesn’t work for string properties exclusively… there are a few other hidden gems in those properties. For example if we detect that the field is a Date or Datetime field, we will return System.DateTime typed value from a field rather than the string Sitecore stores internally.

PS master:\>(Get-Item master:/content/home).__Created 
Monday, April 07, 2008 1:59:00 PM

And not just read – you can also assign System.DateTime value to such “native” property:

PS master:\>(Get-Item master:/content/home).__Created = [DateTime]::Now

… now let’s read it again:

PS master:\>(Get-Item master:/content/home).__Created 
Monday, October 13, 2014 1:59:41 AM

Great we’ve just changed it! Our property handlers take care of all the necessary .Editing.BeginEdit-s and .Editing.EndEdit-s.

It works for assigning content items and media items as well. If your item has link fields in it, you can assign other items to them and not worry about the link format – we will do all the plumbing for you.

To provide an example – I’ve extended my home with additional fields as follows:

image

I can do the following to assign an image to my Image field

(Get-Item master:/content/home).Image = Get-Item 'master:\media library\logo'

Easy enough, isn’t it? Let us (the PowerShell Extensions) detect the field type for you and worry about what to call! Now let’s assign a content item to GeneralLink and Link fields… just like you expected:

(Get-Item master:/content/home).GeneralLink = Get-Item 'master:\content\CognifideCom'

Even better – if you assign a media item to it, it will detect that and do the right thing assigning it as a media link.

What about fields that accept lists of items? We’ve got your back here as well… Let’s assign all children of /sitecore/content/ item to the ItemList field:

(Get-Item master:/content/home).ItemList = Get-ChildItem 'master:\content\'

Ok, so let’s see how our item looks in the Content editor after all the assignments that we’ve just performed:

image

Great! Looks like it worked…

It’s funny how those little improvements make your scripts much more succinct and understandable – try it for yourself!

When not to use the automated properties?

As with every rule there is an exception to this one. Those automated properties perform the $item.Editing.BeginEdit() and $item.Editing.EndEdit() every time as they should which results in saving the item after every assignment. If you’re assigning multiple properties on one item this might be detrimental to performance of your script. In such cases you might want to choose to call $item.Editing.BeginEdit() yourself before modifying the item call the $item[“field name”] = “new value” for each property you want to modify, and then end the burst with the $item.Editing.EndEdit(). But that is very situational and will usually only be required if you’re working with a large volume of data. In those cases you might also want to introduce the Sitecore.Data.BulkUpdateContext trick that Bartek has used in his blog post.

Godspeed and happy scripting!

1 Star2 Stars3 Stars4 Stars5 Stars (2 votes, average: 5.00 out of 5)
Loading...



This entry (Permalink) was posted on Sunday, October 12th, 2014 at 11:27 pm and is filed under Best Practices, Code Samples, PowerShell, Sitecore, Software Development, Solution, Web applications. You can follow any responses to this entry through the RSS 2.0 feed. You can leave a response , or trackback from your own site.



  • Thanks for writing this Adam!

    I most certainly learned something today! :D

    Mike

  • Hey Mike,

    It’s actually your post that was the biggest trigger for this article :)

    If a team member wasn’t aware of it then how can I expect anyone else to be using those?!

  • Pierre Sapinault

    Hey Adam,

    Really love SPE and use it extensively. Just one question after using it so much. Is it possible to create a language version automatically when editing a field if the language doesn’t exist already for that item?
    Let me explain, i’m doing something like this (maybe i do it wrong), I’m often doing some content migration and have to populate fields from some other items. I parse all languages of the source item and want to get the values there and copy them to the newly created item.

    This is how i would like to go about it :
    $SourceItems = Get-Item -Path $path -Language *
    foreach ($sourceItem in $SourceItems)
    {
    $newItem = Get-Item -Path $newPath -Language $sourceItem.Language.Name
    $newItem.”Title” = $sourceItem.Fields[“Menu Title”].Value
    $newItem.”Text” = $sourceItem.Fields[“Page Text”].Value
    }

    This throws an error is the language version doesn’t exist because you cannot edit on a null-value.
    To go around this i make sure i have an English version “en” and then run it like this:

    $SourceItems = Get-Item -Path $path -Language *
    foreach ($sourceItem in $SourceItems)
    {
    $newItemEN = Get-Item -Path $newPath -Language en
    $newItemEN | Add-ItemLanguage -TargetLanguage $sourceItem.Language.Name -IfExist Skip | Out-Null
    $newItem = Get-Item -Path $newPath -Language $sourceItem.Language.Name
    $newItem.”Title” = $sourceItem.Fields[“Menu Title”].Value
    $newItem.”Text” = $sourceItem.Fields[“Page Text”].Value
    }

    What do you think?

    Thanks in advance
    Pierre

  • The second sample looks exactly as it should.
    Only thing that might have saved you some typing would be to have the last 2 lines to look as follows:
    $newItem."Title" = $sourceItem."Menu Title"
    $newItem."Text" = $sourceItem."Page Text"

    Other than this I wouldn’t have done it any way different.

  • Pierre Sapinault

    Ah ok so no possibility of creating a version just with $newItem.”Title” = $sourceItem.”Menu Title” without calling first a version i don’t need ($newItemEN) and adding the language version i need to edit, and then calling the correct language version :( ?

  • Not sure about SPE 3.3 but in SPE 4.0 you should be able to do:
    Add-ItemLanguage -Path $newPath -Language "new-lang"
    That would simplify your script by a line (no need to Get-Item).

    Unfortunately in 3.3 (and 4.0 as far as it goes for now) this will not work fully with -IfExist Skip.
    If the language exists SPE will not return the existing item; only if the item is created will it be returned by the cmdlet.

    If you could report the idea on our Github issue tracker I’ll see that it is implemented before we release 4.0 (hopefully this week)

  • Pierre Sapinault

    this is done, issue #578 :)

  • Standa

    Hello, is it possible to specify language to the rendered item ID? I would like to open an item with set language.

  • tommycogar609

    Creative post . For my two cents if people are requiring a a form , my family filled out and faxed a fillable form here https://goo.gl/aNbuph.

  • LISBETH TARIN

    Good article, Thanks!