Tuesday, March 3, 2015

Working with Sitecore Names & Sitecore Powershell

If you're using Sitecore Powershell and have to create a large amount of items you will probably run into issues with allowable names. Sitecore, by default, forbids quite a few special characters (mainly those dealing with HTTP routing -- & for example) and Sitecore admins can further restrict these characters by using the InvalidItemNameChars setting in web.config.

There's a handy way to handle it automatically built into the sitecore API that you can use:

001
002
003
Add-Type -Path "C:\path\to\your\Sitecore.Kernel.dll"
  $fixedName= [Sitecore.Data.Items.ItemUtil]::ProposeValidItemName($originalName)


Just another reminder that in sitecore powershell you aren't limited to just the pre-defined powershell modules, you can add references to the sitecore kernel (or other relevant assemblies) and use the native sitecore API.

Wednesday, February 18, 2015

Change Insert Options with Sitecore Powershell

This was kind of an odd one to figure out, because it's not very obvious. In the Sitecore content editor UI it's fairly obvious and easy to set.

But what if I needed to set that same option on every single folder in our entire content tree? That could involve tens of thousands of items which is a whole lot of Intern Work.

There is, luckily, an easier way. After checking the Sitecore data content cookbook the field we are looking for is __masters. Like most Sitecore fields, it accepts a pipe delimited list of GUIDs. In our example snippet down below I'm going to retrieve every item under Master:\Content\Home that has the "folder" template and set our insert options on each to be the two items we want.

001
002
003
004
005
006
$allSitecoreFolders = get-childItem -Path "master:\content\home" -Recurse | Where-Object {$_.templatename -eq "Folder"}
foreach($folder in $allSitecoreFolders ){
    $folder.BeginEdit();
    $folder.fields["__masters"].Value="{A87A00B1-E6DB-45AB-8B54-636FEC3B5523}|{13DA436A-21B2-40AF-9926-9C8341C51EAA}"
    [void]$folder.EndEdit();
}

If you're wondering why, in my examples, I'm casting the results to [void] after EndEdit it's because EndEdit is returning a Boolean value and I'm not terribly interested in watching a lot of scrolling True False in my console window. It'll also prevent all sorts of messy behavior if you are relying on functions to return specific values due to how the terrible way Powershell handles Return keyword from methods.

Thursday, February 12, 2015

Sitecore Fast Query & LINQ

One of the nicest features of Sitecore is its Fast Query ability (pdf) and its support of IQueryable and LINQ. Being new to Sitecore I was greatly relieved that I wouldn't need to learn yet another query language and could instead use my existing knowledge of LINQ to query Sitecore.

The first task I had to handle was basically implementing a paged list to an MVC view. Pretty common task, familiar to any .NET MVC developer. The only real new thing I had to learn was actual fast query syntax:

    string _query = string.Format("fast:{0}//*[@@id='{1}']//*[@@templateid='{2}']",
        _context.Site.StartPath, pageID.ToString("B"), Templates.Ids.Article);
 
This XPATH type syntax is saying three things.
      Start our search at the site's start path (typically /sitecore/content/home)
      Return all child items of the page
      That have the given template ID (in this case stored in a constants file)

Once we have that fast query we can then order our results, start at the given index, and only take what we need using LINQ. No underlying knowledge of the Sitecore database is needed, it's all just LINQ.

        List<IArticle> GetNextArticles(int startIndex, int rows, Guid pageID)
{
 
    string _query = string.Format("fast:{0}//*[@@id='{1}']//*[@@templateid='{2}']",
        _context.Site.StartPath, pageID.ToString("B"), Templates.Ids.Article);
    
    var _results = _context.Query<IArticle>(_query)
      .OrderByDescending(x => x.ArticleDate)
      .Skip(startIndex)
      .Take(rows).ToList();
    
    if (_results == null)
    {
       return new List<IArticle>();
    }
    else
    {
        return _results;
    }
}

Monday, February 9, 2015

Working with images in Sitecore Powershell

Attaching images to Sitecore items is super easy thanks to the Sitecore API and Sitecore powershell. In this scenario, I've been given a few thousand images I need to attach to Sitecore items. While my initial thought was to invest in Interns to shove the work off to, there is a nicer, easier, more efficient way.

Getting my cue from this blog post on the basics of the Sitecore media API I just needed to translate that to the powershell way of doing it. First off, we're going to need access to the Sitecore API, and that's going to require this line to import in the namespace we'll need (Sitecore.Resources.Media) which is contained in Sitecore.Kernel.dll.
001
Add-Type -Path 'C:\pathtoyours\Sitecore.Kernel.dll'

Now that we've imported the DLL we have access to the full Sitecore Media Library API. This helper function takes care of uploading our image and returning us the GUID we need.

001
002
003
004
005
006
007
008
009
010
011
012
013
014
015
016
Function Upload-Image{
  param([io.fileinfo]$sourceFile,$destinationDir)

  $destinationPath = [string]::Format("{0}/{1}",$destinationDir.TrimEnd("/"),$sourceFile.basename)

  $uploader = new-object sitecore.resources.media.mediacreator
  $options = new-object sitecore.resources.media.mediacreatoroptions
  $options.AlternateText = $sourcefile.BaseName
  $options.Destination = $destinationPath
  $result = $uploader.CreateFromFile($sourceFile.FullName,$options)

   Return $result.id.Guid.ToString("B")
  
}


One other thing to keep in mind is special characters you may have to sanitize out of your filenames, you can see a list of currently configured special characters by checking the InvalidItemNameChars property on your Sitecore configuration

001
[Sitecore.Configuration.Settings]::InvalidItemNameChars


Once we have our GUID from our image, there's one last little piece we have to do before setting it and that's formatting the GUID into the correct Sitecore XML like so:
001
002
$imageSitecoreFormat = [string]::Format("<image mediaid=""{0}"" />",$imageGuid)

At this point we just set it on the Sitecore object and we're ready to go!
001
002
003
004
$sitecoreItem = get-item master:\content\somesitecoreitem
$sitecoreItem.beginedit()
$sitecoreItem.ImageField = $imageSitecoreFormat
[void]$sitecoreItem.EndEdit()


Friday, February 6, 2015

Working with Renderings in Sitecore Powershell

In the last few weeks I've been using the very useful Sitecore Powershell Extensions to perform a large amount content transfer operations.  The scenario is a pretty common one when using Sitecore and developing websites. Given a set of data create, load, and prepare hundreds or thousands of items into Sitecore. Yesterday.

This can be an incredibly time consuming operation and daunting operation. Especially if those items contain sub items which need images which need to link to other items, etc, etc. Lots of manual content entry which is costly, prone to error, and lacks any sort of verification or re-usability.

So enter Powershell. This is the kind of scenario Powershell really shines at. Fast (to write) scripts for one or few time use with the ability to load your content into Sitecore in the proverbial blink of an eye. Thankfully, the folks at cognifide made us a Sitecore powershell extension to help out.

One of the tasks that is surely familiar to any Sitecore admin is attaching controls to objects. While going through the UI works it is incredibly slow.  Not something you want to do dozens, much less hundreds of times.

Well, it turns out it isn't that super hard. We're going to use Powershell Here Strings  to store a generic Sitecore rendering XML and create/replace the GUIDs we need to. You can see the Sitecore specific way of doing this using their API via John West's blog on the subject.


The script is below, we have a group of pages and a group of controls. Our controls that we need to add are named the same as our pages. We simply find them, retrieve their sitecore ID and create a new GUID for the actual control object.

There are 3 attributes at play here in the XML we care about. The first is the ID, that is the actual Sitecore item we are setting. The second is the UID , this is a GUID unique to this instance of the rendering, hence why we generated a new GUID. The second is the s:ds attribute, which is our datasource for our Sitecore component. The fourth, you may notice, is the s:ph which is the placeholder attribute.

001
002
003
004
005
006
007
008
009
010
011
012
013
014
015
016
017
018
019
020
021
022
023
024


$pages= gci -Path "master:\path\to\our\pages" -Recurse
$controls = gci -Path "master:\content\data\ourcontrols" -Recurse | Where-Object {$_.templateName -eq "ourtemplatename"}

$replaceRenderersText  = @"
<r xmlns:p="p" xmlns:s="s" p:p="1" s:xsd="http://www.w3.org/2001/XMLSchema">
<d id="{FE5D7FDF-89C0-4D99-9AA3-B5FBD009C9F3}">
<r uid="NEWGUIDHERE" s:cnd="" s:ds="REPLACEME" s:id="{474D8968-D54C-4C78-8DDB-2BBF31C22B16}" s:par="" s:ph="/main/components" />
</d></r>
"@

foreach($page in $pages){
    $control = $controls | Where-Object {$_.name -eq $page.name}
    $newUnique = [guid]::NewGuid()
    $specificRendering = $replaceRenderersText.Replace("REPLACEME",$control.id.guid.Tostring("B")).Replace("NEWGUIDHERE",$newUnique.ToString("B"))
    if($category.__Renderings -ne $categoryRender){
        $category.BeginEdit()
        $category.__Renderings = $specificRendering
        [void]$category.EndEdit()
   }

You might be wondering where we got the the XML in the first place. I "cheated" by pulling the data from the Sitecore objects .__Renderings field.

 Ta-Da! Hours saved from mainly attaching many, many controls.