5 min read

Sitecore Powershell - trigger a job

I've been working on a small implementation in Sitecore that triggers a Sitecore job defined in code and shows results back to the content editor in Sitecore.

A job is a process that will be running in the background and is ideal for long running tasks.

If you haven't used a Sitecore Job yet, it is recommended to check out these resources:
https://sitecore-community.github.io/docs/documentation/Sitecore Fundamentals/Asynchronous Tasks/
2012 post :-) https://briancaos.wordpress.com/2012/02/06/using-sitecore-jobs/

You can easily trigger this job from a Sitecore Powershell script with the advantage being that you can hook it up in Sitecore wherever and however you want. Eg. a button in the ribbon or on specific items.
In the end, you call a Controller from Powershell that will trigger the job.

In my case, when a certain item in the tree is selected we show additional functionality in the Home ribbon: Import Zip and Import Zip Status.

The job itself is about processing a zip file and extracting the contents to a specific location in the Media Library. Maybe I'll talk about this in a later post.

The script itself

Functionality can be divided into different steps:

  1. Define the controller in the Powershell script
  2. Check if the job is not already running. I don't want multiple jobs running at the same time
  3. Receive zip file and store that file on the filesystem
  4. Recheck if job isn't running (upload in my case can take a while depending on the size of the zip)
  5. Start the background job which will unzip and process the files
  6. Show current job status and close the popup window

The biggest advantage of having things run in a background job is that it is not blocking the front-end users and the Sitecore environment will keep on running smoothly.

1. Define the controller

In this Controller, I have 2 methods defined: CheckJob and StartJob
So roughly shown, this is my controller:

public class ProcessZipController : System.Web.Mvc.Controller
{
    [HttpGet]
    public JsonResult CheckJob()
    {
    	if (Sitecore.Context.IsLoggedIn)
        {
            return Json(GetJobState(), JsonRequestBehavior.AllowGet);
        }
        return Json("Unauthorized", JsonRequestBehavior.AllowGet);
    }

    [HttpGet]
    public JsonResult StartJob(Sitecore.Data.Items.Item contextItem, string path)
    {
    	if (Sitecore.Context.IsLoggedIn)
        {
            new ProcessZipJob().StartJob(contextItem, path);
            return Json(GetJobState(), JsonRequestBehavior.AllowGet);
        }
        return Json("Unauthorized", JsonRequestBehavior.AllowGet);        
    }
}

Implement any security that is relevant for you. I only check if the user is logged in.

The most important line off course is new ProcessZipJob().StartJob(contextItem, path) where I trigger the job pipeline which will execute what I want.

In the powershell script, you have fetch this controller and call the methods on it:

$controllerType = [MyProject.Controllers.ProcessZipController]
$controller = [Sitecore.DependencyInjection.ServiceLocator]::ServiceProvider.GetService($controllerType) -as $controllerType

I'm leveraging the ServiceLocator to get an instance of my ProcessZipController. You can use an interface as well for this.

2. Check if the job is already running

I'm returnin a JSON object with the methods in the controller. Which you can just access as a Powershell object.

internal class JobProgress
{
   public bool Finished { get; set; }
   public bool Error { get; set; }
   public List<string> Messages { get; set; }
}

In the PS script I call the CheckJob method and then check the Finished boolean:

$jobProgress = $controller.CheckJob()
if(!$jobProgress.Data.Finished){
    Write-Host "----- Previous import has not been finished. Please wait. -----"
    Write-Host ""
    $jobProgress.Data.Messages
    Show-Result -Text
    Close-Window
    exit
}

3. Receive zip file and store that file on the filesystem

For my code to work, I need to work with a zip file. I'm using the Receive-File Powershell method. And do some additional checks afterwards such as checking if we're effectively dealing with a .zip file.

$tempFolderPath = [Sitecore.IO.FileUtil]::MapPath([Sitecore.Configuration.Settings]::TempFolderPath)
$uploadPath = $tempFolderPath + "\ZipUpload"
$uploadedFile = Receive-File -Path $uploadPath

if($uploadedFile -eq "cancel"){
    Write-Host "No file selected. Import cancelled."
    Close-Window
    exit
}
if(!$uploadedFile.EndsWith(".zip")){
    Write-Host "The selected file is not a zip file. Import cancelled."
    Close-Window
    exit
}

Write-Host "Zip file uploaded to temp folder."

This will show the Sitecore upload file popup:

4. Recheck the job

Same functionality as described in step 2. I had some problems when triggering the powershell script too fast while an upload was just finishing in another window. So the recheck was added.

You could also add this to the StartJob method in the controller as well.

5. StartJob

Here we call the controller method to start the job. I'm passing in:

  • the $SitecoreContextItem which will contain the item on which this script is triggered.
  • the $uploadedFile which contains the full path where the file was uploaded. This works on the local filesystem as well as Sitecore running on web apps.
$jobProgress = $controller.StartJob($SitecoreContextItem, $uploadedFile) 
Start-Sleep -Seconds 10
$jobProgress = $controller.CheckJob()

Clear-Host
Write-Host "----- Import is in progress -----"
Write-Host ""
$jobProgress.Data.Messages
Write-Host "..."
Write-Host "..."

Added a 10 second sleep to show more messages to the user. Otherwise it only processed a few files. And waiting 10 seconds for the content editors is still acceptable for this script. It can be omitted without problems.

The $jobProgress.Data.Messages object is used to pass messages along from the executed job. Its type is a list of strings.

For 10 seconds, the content editors will see this message:

6. Show status

Show-Result -Text
Close-Window
exit

Show-Result is used to show a dialog screen that displays the Write-Host output from the Powershell script. It is usefull to show some kind of status and progress.

The Close-Window is used to close the 'Import Zip' popup automatically after content editors close this window.


Bonus info

As an admin, you can follow-up the running jobs in /sitecore/admin/jobs.aspx

I have 2 entries. The first one is for the Sitecore PowerShell Extensions context itself. Which runs as soon as you run a powershell script.

The second one is the custom job that was triggered from the controller and will run untill it has completed all its steps.


Full script

# Step 1: Define the controller
$controllerType = [MyProject.Controllers.ProcessZipController]
$controller = [Sitecore.DependencyInjection.ServiceLocator]::ServiceProvider.GetService($controllerType) -as $controllerType

# Step 2: Check job
$jobProgress = $controller.CheckJob()
if(!$jobProgress.Data.Finished){
    Write-Host "----- Previous import has not been finished. Please wait. -----"
    Write-Host ""
    $jobProgress.Data.Messages
    Show-Result -Text
    Close-Window
    exit
}

# Step 3: Receive zip
$tempFolderPath = [Sitecore.IO.FileUtil]::MapPath([Sitecore.Configuration.Settings]::TempFolderPath)
$uploadPath = $tempFolderPath + "\ZipUpload"
$uploadedFile = Receive-File -Path $uploadPath

if($uploadedFile -eq "cancel"){
    Write-Host "No file selected. Import cancelled."
    Close-Window
    exit
}
if(!$uploadedFile.EndsWith(".zip")){
    Write-Host "The selected file is not a zip file. Import cancelled."
    Close-Window
    exit
}

Write-Host "Zip file uploaded to temp folder."

# Step 4: Recheck job
if(!$jobProgress.Data.Finished){
    Write-Host "Previous import has not been finished. Please wait."
    $jobProgress.Data.Messages
    Show-Result -Text
    Close-Window
    exit
}

# Step 5: start job
$jobProgress = $controller.StartJob($SitecoreContextItem, $uploadedFile) 
Start-Sleep -Seconds 10
$jobProgress = $controller.CheckJob()

Clear-Host
Write-Host "----- Import is in progress -----"
Write-Host ""
$jobProgress.Data.Messages
Write-Host "..."
Write-Host "..."

# Step 6: Show status
Show-Result -Text
Close-Window
exit