Sidekick Power Tools

If you’ve not already seen the awesome Sitecore Sidekick – go and check it out!

I wanted to get my hands dirty so I thought I’d make a simple Sidekick tool. Ohmywordit’smuchnicerthanspeak. The evil that shall not SPEAK its name. To get started – really all you need to do is Nuget the Sidekick stuff in to your project – it sorts everything else out for you ready.

So, I’m just going to dive in to the project I made (using my SIM starter project 😉

solution

There are three bits to this:

  1. Config
  2. Resources
  3. Handler

I’ve kept this first project as simple as possible – doing the bare minimum viable tool.

Configuration

You only need to add one config file:

<?xml version="1.0"?>
<configuration xmlns:patch="http://www.sitecore.net/xmlconfig/">
<sitecore>
<pipelines>
<scsRegister>
<processor type="Kumquat.SidekickPowershellTool.PowershellToolHandler, Kumquat.SidekickPowershellTool">
<param name="roles"></param>
<!-- set to "true" to only allow admins-->
<param name="isAdmin">true</param>
<!-- leave blank for any users, seperate multiple users by | character -->
<param name="users"></param>
<param name="script">{81DC9869-F308-430E-A110-3F100A769C40}</param>
<param name="name">Run My Amazing Script</param>
</processor>
</scsRegister>
</pipelines>
</sitecore>
</configuration>

Most of this is just a copy of the config for other Sidekick tools. We’ve changed the processor type to match our project’s Handler and we’ve added a parameter called “script” which is either a path or an ID pointing to a script we want to run.

Resources

IMPORTANT: Remember to set your Resources as embedded – if you don’t – you’ll get 404s where awesome code should be.

We’ve got a few files in here – the .css and .svg files are pretty self explanatory. Then we have a .scs file – that’s our layout for the tool. Our one looks like this:

SCS Files

<div ng-controller="powershelltoolcontroller as vm">
  <a class="btn" ng-click="vm.runScript()">Execute this script</a>
<pre ng-if="vm.error" class="scserror">{{vm.error}}</pre>
<pre ng-if="vm.data" class="scsdata">{{vm.data}}</pre>
</div>

We specify which controller we’re using in the div. We’ve got a button which fires a runScript method and then some mark-up to display data and errors.

JS Files

The meat of our resources are found in the js files. The one which ties everthing together is the directive:

(function () {
	'use strict';

	angular
        .module('app')
        .directive('powershelltooldirective', powershelltooldirective);

	function powershelltooldirective() {

		var directive = {
		    templateUrl: "/scs/powershelltool.scs",
			restrict: 'EA'
		};
		return directive;
	}
})();

This basically points to our .scs layout file.

We have a lovely controller to, you know, control things:

(function () {
	'use strict';

	angular
        .module('app')
        .controller('powershelltoolcontroller', powershelltoolcontroller);

	powershelltoolcontroller.$inject = ['PSfactory', '$scope'];

	function powershelltoolcontroller(PSfactory, $scope) {
		$('#uframe').load(function () {
			this.height =
			this.contentWindow.document.body.offsetHeight + 'px';
		});

	    var vm = this;
		vm.runScript = function () {
		    PSfactory.runScript().then(function (response) {
		        vm.data = response.data;
		    }, function (response) {
		        vm.error = response.data;
		    });
		}

	}
})();

This controller updates the height (I borrowed this code from the examples) and also injects our factory – wiring up the v.runScript() method to the factory’s runScript method and updating the results.

And finally a factory to do some stuff (because I wanted to use one):

(function () {
    'use strict';

    angular
        .module('app')
        .factory('PSfactory', PSfactory);

    PSfactory.$inject = ['$http'];

    function PSfactory($http) {
        var service = {
            runScript: function () {
                return $http.post("/scs/runscript.scsvc");
            }
        };

        return service;

        function getData() { }
    }
})();

This does a HTTP POST to retrieve the file “/scs/runscript.scsvc” – which is a cheeky way of posting back to the Handler to tell the back-end code to do something.

The Handler

Finally – our back-end code comes in the form of a Handler:

namespace Kumquat.SidekickPowershellTool
{
    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Web;
    using System.Collections.Specialized;
    using Cognifide.PowerShell.Core.Host;
    using Sitecore.Data;
    using Sitecore.Data.Items;
    using Sitecore.SecurityModel;
    using SitecoreSidekick.Handlers;

    public class PowershellToolHandler : ScsHttpHandler
    {
        private string script;

        public PowershellToolHandler(string roles, string isAdmin, string users, string script, string name) : base(roles, isAdmin, users)
        {
            this.script = script;
            this.Name = name;
        }

        public override string Directive { get; set; } = "powershelltooldirective";
        public override NameValueCollection DirectiveAttributes { get; set; }
        public override string ResourcesPath { get; set; } = "Kumquat.SidekickPowershellTool.Resources";
        public override string Icon { get; } = "/scs/powershelltool.svg";
        public override string Name { get; } = "Run Some Script";
        public override string CssStyle { get; } = "width:98%";
        public override void ProcessRequest(HttpContextBase context)
        {
            var file = GetFile(context);
            if (file == "runscript.scsvc")
                ReturnJson(context, RunScript(context));
            else
                ProcessResourceRequest(context);
        }

        private object RunScript(HttpContextBase context)
        {
            //Code borrowed from here: http://stackoverflow.com/questions/40091185/running-a-sitecore-powershell-script-from-code
            using (ScriptSession scriptSession = ScriptSessionManager.NewSession("Default", true))
            {
                var database = Database.GetDatabase("master");
                Item speScriptItem = database.GetItem(this.script);
                string script = speScriptItem["Script"];
                if (!string.IsNullOrEmpty(script))
                {
                    List<object> results;
                    using (new SecurityDisabler())
                    {
                        results = scriptSession.ExecuteScriptPart(script, false);
                    }

                    return results;
                }
            }

            return "Script not found";
        }

        //public override bool AdminOnly { get; }
    }
}

There’s a fair bit going on here. The important things are the overriden proerties which ties things together (setting the title, directive and resources path) and then the code which gets launched through “ProcessRequest”.

var file = GetFile(context);
if (file == "runscript.scsvc")

This is how we hijack the file request and run some code. Neat! In our example we get a script item and run the script through Powershell.

In the next blog post I write on Sidekick – I think I’ll add some data to the post back so we can send some parameters to our Powershell script. Fun 🙂

Advertisements

One thought on “Sidekick Power Tools

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s