Today I needed a quick report to find out the number of "pages" on our site. I came up with a quick estimate using the Sitecore PowerShell Extensions module.
I hope this encourages you to spend a little more time in SPE.
// michael
Friday, April 10, 2015
Thursday, February 26, 2015
Move Workstation To New OU With PowerShell
Question came up at work today on how to move a workstation in Active Directory from one OU to another. Here's what I came up with.
I hope this helps someone!
// michael
I hope this helps someone!
Import-Module ActiveDirectory
$computers = "PC1","PC2" # Optionally use Get-Content -Path C:\computers.txt
$computers | ForEach-Object {
$computer = "$($_)$"; Get-ADComputer -Filter { SamAccountName -eq $computer } |
Move-ADObject -TargetPath "OU=Retired Workstations,OU=Company,DC=company,DC=corp"
}
// michael
Tuesday, January 20, 2015
Sitecore Code Editor 1.6 Preview
Developing on the Sitecore platform has been some of the most enjoyable time in my career. For me, the excitement of discovering new aspects of the platform and building features for a module that I'm going to just give away is well worth the late night investment. I hope those of you that use the module find it helpful. Enjoy!
Today I would like to outline the changes included in v1.6 of the Sitecore Code Editor Module. Those of you that have never seen or heard of this module, it provides an improved text editing experience using the Ace Code Editor plugin. I'll outline some of the enhancements or fixes then go through each in greater detail.
Changes:
Mime types have been added for files with content for LESS, SCSS, Windows PowerShell, and others. These can be found in Sitecore.SharedSource.CodeEditor.config.
Today I would like to outline the changes included in v1.6 of the Sitecore Code Editor Module. Those of you that have never seen or heard of this module, it provides an improved text editing experience using the Ace Code Editor plugin. I'll outline some of the enhancements or fixes then go through each in greater detail.
Changes:
- Added persistent user settings for the editor.
- Added configurable height and width for the editor window.
- Added scrolling to the content editor window.
- Updated with new mimetypes.
- Fixed issue with media blobs not appearing in module packages.
- Fixed code template share setting.
The nice thing about this release is it was heavily influenced by community requests. You may have noticed while using the code editor on most computer screens the modal window was just not quite large enough. Now the module supports storing the modal window width, height. In addition, settings that effect the text include font size, font type, and theme.
The content window in the Content Editor now supports scrolling for large sets of text.
Mime types have been added for files with content for LESS, SCSS, Windows PowerShell, and others. These can be found in Sitecore.SharedSource.CodeEditor.config.
The Code Attachment field type was removed to correct an issue where media item blobs were not properly included in the packages. I figured out that I can add command buttons in the content editor in code rather than defining in the core database as a new field type. Thanks to John West for posting that article a few years ago :)
I've reverted back to the original Attachment system type but changed the control.
I've reverted back to the original Attachment system type but changed the control.
Saturday, November 1, 2014
Sitecore PowerShell Extended with Gutters
Recently I challenged myself to find integrations with Sitecore PowerShell Extensions that have not yet been published. I saw this cool article by ParTechIT and knew it was something I had to try. Of course I have to tell someone when I get it figured out.
Here's the final result.
@adamnaj @MichaelWest101 @ParTechIT shit just got real!
— Mike Reynolds (@mike_i_reynolds) November 1, 2014
After having already extended with pipelines I didn't expect this to take very long. Hopefully those reading this will learn something, decide to share it, and point out areas of improvement. Feel free to comment or make suggestions. I expect to add this to a future release of Sitecore PowerShell Extensions (SPE).
User Story:
As a spe user, I can create scripts to run when rendering gutters so that I don't have to compile the GutterRenderer.
As a spe user, the script can be configured just like any other GutterRender, so that I don't have to further complicate the setup.
Acceptance Criteria:
Second we need to create a new Gutter library and a Publication Status script. We'll come back to the content of the script later.
User Story:
As a spe user, I can create scripts to run when rendering gutters so that I don't have to compile the GutterRenderer.
As a spe user, the script can be configured just like any other GutterRender, so that I don't have to further complicate the setup.
Acceptance Criteria:
- The gutter rendering scripts must reside under the following path:
- /sitecore/system/Modules/PowerShell/Script Library/Content Editor/Gutters
- The GutterRenderer must be configured under the following path:
- /sitecore/content/Applications/Content Editor/Gutters
- The example GutterRenderer must be stolen.
Some concepts you will see in this article:
- Creating a GutterRenderer using Windows PowerShell code in SPE.
- Configuring a GutterRenderer in Sitecore
First we begin with creating a new class in our Sitecore.SharedSource.PowerShell library. The class to create in this example is called GutterStatusRenderer.
using System;
using Cognifide.PowerShell.PowerShellIntegrations.Host;
using Cognifide.PowerShell.PowerShellIntegrations.Settings;
using Sitecore.Configuration;
using Sitecore.Data;
using Sitecore.Data.Items;
using Sitecore.Diagnostics;
using Sitecore.Shell.Applications.ContentEditor.Gutters;
namespace Sitecore.SharedSource.Gutters
{
// Inherit from the GutterRenderer in order to override the GetIconDescriptor.
public class GutterStatusRenderer : GutterRenderer
{
// We override the GetIconDescriptor so a script can be called in it's place.
protected override GutterIconDescriptor GetIconDescriptor(Item item)
{
// The scriptId parameter is configured when we create a new gutter
// here /sitecore/content/Applications/Content Editor/Gutters
if (!Parameters.ContainsKey("scriptId")) return null;
var scriptId = new ID(Parameters["scriptId"]);
var db = Factory.GetDatabase("master");
var scriptItem = db.GetItem(scriptId);
// If a script is configured but does not exist then return.
if (scriptItem == null) return null;
// Create a new session for running the script.
using (var session = new ScriptSession(ApplicationNames.Default))
{
var script = (scriptItem.Fields[ScriptItemFieldNames.Script] != null)
? scriptItem.Fields[ScriptItemFieldNames.Script].Value
: String.Empty;
// We will need the item variable in the script.
session.SetVariable("item", item);
try
{
// Any objects written to the pipeline in the script will be returned.
var output = session.ExecuteScriptPart(script, false);
foreach (var result in output)
{
if (result.GetType() == typeof (GutterIconDescriptor))
{
return (GutterIconDescriptor) result;
}
}
}
catch (Exception ex)
{
Log.Error(ex.Message, this);
}
}
return null;
}
}
}
Second we need to create a new Gutter library and a Publication Status script. We'll come back to the content of the script later.
Third we need to create a new Gutter in the "core" database.
If you recall from the source code above, the scriptId indicates which script to call for this GutterRenderer. This will allow you to use the same GutterStatusRenderer class for all your gutter needs.
Finally we need to write our script. You'll notice that it's almost exactly what was stolen from ParTechIT, in PowerShell form.
<#
Adapted from:
http://www.partechit.nl/en/blog/2013/03/display-item-publication-status-in-the-sitecore-gutter
#>
# The $item variable is populated in the GutterStatusRenderer class using session.SetVariable.
if(-not $item) {
Write-Log "The item is null."
return $null
}
$publishingTargetsFolderId = New-Object Sitecore.Data.ID "{D9E44555-02A6-407A-B4FC-96B9026CAADD}"
$targetDatabaseFieldId = New-Object Sitecore.Data.ID "{39ECFD90-55D2-49D8-B513-99D15573DE41}"
$existsInAll = $true
$existsInOne = $false
# Find the publishing targets item folder
$publishingTargetsFolder = [Sitecore.Context]::ContentDatabase.GetItem($publishingTargetsFolderId)
if ($publishingTargetsFolder -eq $null) {
return $null
}
# Retrieve the publishing targets database names
# Check for item existance in publishing targets
foreach($publishingTargetDatabase in $publishingTargetsFolder.GetChildren()) {
Write-Log "Checking the $($publishingTargetDatabase[$targetDatabaseFieldId]) for the existence of $($item.ID)"
if([Sitecore.Data.Database]::GetDatabase($publishingTargetDatabase[$targetDatabaseFieldId]).GetItem($item.ID)) {
$existsInOne = $true
} else {
$existsInAll = $false
}
}
# Return descriptor with tooltip and icon
$tooltip = [Sitecore.Globalization.Translate]::Text("This item has not yet been published")
$icon = "People/16x16/flag_red.png"
if ($existsInAll) {
$tooltip = [Sitecore.Globalization.Translate]::Text("This item has been published to all targets")
$icon = "People/16x16/flag_green.png"
Write-Log "Exists in all"
} elseif ($existsInOne) {
$tooltip = [Sitecore.Globalization.Translate]::Text("This item has been published to at least one target")
$icon = "People/16x16/flag_yellow.png"
Write-Log "Exists in one"
}
$gutter = New-Object Sitecore.Shell.Applications.ContentEditor.Gutters.GutterIconDescriptor
$gutter.Icon = $icon
$gutter.Tooltip = $tooltip
$gutter.Click = [String]::Format("item:publish(id={0})", $item.ID)
$gutter
Here's the final result.
That's pretty much it. Happy coding!
References:
- http://www.partechit.nl/en/blog/2013/03/display-item-publication-status-in-the-sitecore-gutter
- http://michaellwest.blogspot.com/2014/10/sitecore-powershell-extended-with-pipelines.html
// Mikey
Saturday, October 25, 2014
Sitecore PowerShell Extensions Tip 2 - Set Random Wallpaper
As of late I've spent more time using Sitecore PowerShell Extensions.Today I decided to change the wallpaper with a random image from the media library.
I hope this encourages you to spend a little more time in SPE.
# Get all the first level items in the images folder.
$items = Get-ChildItem -Path "master:\media library\images\"
# Select an item at random.
$item = $items[(Get-Random -Maximum ($items.length - 1))]
$url = [Sitecore.Resources.Media.MediaManager]::GetMediaUrl($item)
# Get the user in need of a refreshed wallpaper.
$user = Get-User -Identity "sitecore\admin" -Authenticated
$user.Profile.SetCustomProperty("Wallpaper", $url)
$user.Profile.Save();
I hope this encourages you to spend a little more time in SPE.
Friday, October 24, 2014
Wednesday, October 22, 2014
Sitecore PowerShell Extended with Pipelines
Every once in a while I have what I think is a cool idea. Then I have to tell someone.
@adamnaj @mike_i_reynolds Extended #Sitecore to run scripts during loggingin loggedin, logout. On logout the context user is anonymous. Why?
— Michael West (@MichaelWest101) October 19, 2014
@MichaelWest101 @mike_i_reynolds This sounds like an absolutely awesome idea!
— Adam Najmanowicz (@adamnaj) October 19, 2014
Thank you Adam for the encouragement. This took me a few days to finally write it all down. Hopefully those reading this will learn something, decide to share it, and point out areas of improvement. Feel free to comment or make suggestions. I expect to add this to a future release of Sitecore PowerShell Extensions (SPE).User Story:
As a spe user, I can create scripts to run during user logging in, successful login, and logout so that I can automate tasks that are tedious.
Acceptance Criteria:
- The scripts must fit into one of the available pipelines provided by Sitecore.
- loggingin
- loggedin
- logout
- The example scripts used must be stolen.
@MichaelWest101 @adamnaj @sitecorejohn I stole from john. You stole from me. John will steal from you. ;)
— Mike Reynolds (@mike_i_reynolds) October 19, 2014
Some concepts you will see in this article:- Config include files
- Pipelines
- Configuration Factory with hint attribute and raw: prefix
First we begin with creating a new library project in Visual Studio. When we are complete with the example, we'll have a project that looks like this:
Second we need to reference Sitecore.Kernel and Cognifide.PowerShell libraries. You'll find the Cognifide.PowerShell library in the bin directory after installing SPE.
Third we will create our new pipeline processor which will execute our PowerShell scripts. Below is the skeleton of the class.
using Sitecore.Pipelines;
namespace Sitecore.SharedSource.Pipelines
{
public abstract class PipelineProcessor<TPipelineArgs> where TPipelineArgs : PipelineArgs
{
protected void Process(TPipelineArgs args)
{
}
}
}
We can go ahead and create our three pipelines to extend the PipelineProcessor. Below is the complete implementation of each pipeline.
using Sitecore.Pipelines.LoggedIn;
namespace Sitecore.SharedSource.Pipelines.LoggedIn
{
public class LoggedInScript : PipelineProcessor<LoggedInArgs> { }
}
using Sitecore.Pipelines.LoggingIn;
namespace Sitecore.SharedSource.Pipelines.LoggingIn
{
public class LoggingInScript : PipelineProcessor<LoggingInArgs> { }
}
using Sitecore.Pipelines.Logout;
namespace Sitecore.SharedSource.Pipelines.Logout
{
public class LogoutScript : PipelineProcessor<LogoutArgs> { }
}
Let's go ahead and setup our script libraries in Sitecore before we create the include config and finish out the implementation of running the scripts.
Now that we have the three pipeline libraries, we can create the include config to map to those. I prefer this solution so I don't have to hard code the GUID for each in compiled code.
Now that we have the three pipeline libraries, we can create the include config to map to those. I prefer this solution so I don't have to hard code the GUID for each in compiled code.
<configuration xmlns:patch="http://www.sitecore.net/xmlconfig/">
<sitecore>
<processors>
<loggingin argsType="Sitecore.Pipelines.LoggingIn.LoggingInArgs">
<!-- Pipeline to run scripts while the user is logging in. -->
<processor patch:after="processor[position()=last()]" mode="on" type="Sitecore.Sharedsource.Pipelines.LoggingIn.LoggingInScript, Sitecore.SharedSource.PowerShell">
<config hint="raw:Config">
<!-- /sitecore/system/Modules/PowerShell/Script Library/Pipelines/LoggingIn -->
<libraryId>{83C826B6-C478-43D9-92BD-E5589F50DA27}</libraryId>
</config>
</processor>
</loggingin>
<loggedin argsType="Sitecore.Pipelines.LoggedIn.LoggedInArgs">
<!-- Pipeline to run scripts after the user is logged in. -->
<processor patch:after="processor[position()=last()]" mode="on" type="Sitecore.Sharedsource.Pipelines.LoggedIn.LoggedInScript, Sitecore.SharedSource.PowerShell">
<config hint="raw:Config">
<!-- /sitecore/system/Modules/PowerShell/Script Library/Pipelines/LoggedIn -->
<libraryId>{D0226A69-F15D-4CBF-812C-BFE3F14936C5}</libraryId>
</config>
</processor>
</loggedin>
<logout argsType="Sitecore.Pipelines.Logout.LogoutArgs">
<!-- Pipeline to run scripts when the user logs out. -->
<processor patch:after="*[@type='Sitecore.Pipelines.Logout.CheckModified, Sitecore.Kernel']" mode="on" type="Sitecore.Sharedsource.Pipelines.Logout.LogoutScript, Sitecore.SharedSource.PowerShell">
<config hint="raw:Config">
<!-- /sitecore/system/Modules/PowerShell/Script Library/Pipelines/Logout -->
<libraryId>{EE098609-4CA4-4FEE-8A86-3AB410AB9C38}</libraryId>
</config>
</processor>
</logout>
</processors>
</sitecore>
</configuration>
The pipeline is pretty standard. I did have to place the LogoutScript pipeline to be placed right after CheckModified, otherwise the username will be anonymous.
Notice the config section inside the processor. I found an example here by Partech which helped to setup the parameters in the config. John West has a nice article explaining the different options.
With that said, I'll show the remaining implementation of the PipelineProcessor. The code follows a few steps:
- Read the libraryId configured for the specified pipeline. A static collection would create a problem in this example, so be sure to leave it as it is below.
- If the library item contains any scripts, then continue.
- For each script defined in the pipeline library create a new session and execute the script. The args parameter is passed as a session variable for use in the scripts.
public abstract class PipelineProcessor<TPipelineArgs> where TPipelineArgs : PipelineArgs
{
protected PipelineProcessor()
{
Configuration = new Dictionary<string, string>();
}
protected void Process(TPipelineArgs args)
{
Assert.ArgumentNotNull(args, "args");
Assert.IsNotNullOrEmpty(Configuration["libraryId"], "The configuration setting 'libraryId' must exist.");
var libraryId = new ID(Configuration["libraryId"]);
var db = Factory.GetDatabase("master");
var libraryItem = db.GetItem(libraryId);
if (!libraryItem.HasChildren) return;
foreach (var scriptItem in libraryItem.Children.ToList())
{
using (var session = new ScriptSession(ApplicationNames.Default))
{
var script = (scriptItem.Fields[ScriptItemFieldNames.Script] != null)
? scriptItem.Fields[ScriptItemFieldNames.Script].Value
: String.Empty;
session.SetVariable("args", args);
try
{
session.ExecuteScriptPart(script, false);
}
catch (Exception ex)
{
Log.Error(ex.Message, this);
}
}
}
}
protected Dictionary<string, string> Configuration { get; private set; }
public void Config(XmlNode node)
{
Configuration.Add(node.Name, node.InnerText);
}
}
Finally, create your scripts in the libraries and watch it in action. Each of my example scripts are adapted from other articles and are linked at the end of this post.
That's pretty much it. If you really want to mess with your colleagues, write a script to send them a phony email every time they login and logout.
References:
- http://www.sitecore.net/Learn/Blogs/Technical-Blogs/John-West-Sitecore-Blog/Posts/2011/02/The-Sitecore-ASPNET-CMS-Configuration-Factory.aspx
- http://www.partechit.nl/en/blog/2014/09/configurable-pipeline-processors-and-event-handlers
- http://www.matthewkenny.com/2014/10/custom-sitecore-pipelines/
- Some scripts you can use. The random desktop background is really useful.
- http://www.sitecore.net/Learn/Blogs/Technical-Blogs/John-West-Sitecore-Blog/Posts/2012/12/Automatically-Show-the-Quick-Info-Section-in-the-Content-Editor-of-the-Sitecore-ASPNET-CMS.aspx
- http://www.sitecore.net/Learn/Blogs/Technical-Blogs/John-West-Sitecore-Blog/Posts/2010/07/Randomize-Sitecore-Desktop-Background-Image.aspx
- http://sitecorejunkie.com/2013/06/08/enforce-password-expiration-in-the-sitecore-cms/
- http://sitecorejunkie.com/2013/09/24/unlock-sitecore-users-items-during-logout/
Subscribe to:
Posts (Atom)










