Category Archives: SPEAK

Sitecore SPEAK – Dialog Rendering

The standard way to add/create dialog’s in SPEAK is to add the Dialog Window control, and then add the controls required by the dialog to the Dialog Window’s placeholders.
This has a number of disadvantages

  • Each new page that requires the dialog has to add all the renderings/settings that the dialog requires.
  • The code related for the dialog is added to the code page.
  • If you need to add/remove controls from the dialog you have to update all the pages that use the dialog
  • If you have a page with a lot of dialog’s the page will have a LOT of controls and all the code

In order to address the afore mentioned issues and enable reuse of dialog’s across a number of pages, we introduced the concept of a Dialog Rendering item; which contains all the renderings required by the Dialog, and defines its own sub-page code which contains the code required by the dialog.
In this blog we will create a attachment dialog, as the uploader control that comes with SPEAK is very cool (look out for an upcoming blog post)..

attachment dialog

Adding/showing a dialog

There are a 4 steps in order to add and then show a dialog:

  1. Create the Dialog Rendering Template. (Whilst this is not required, as you could add the layouts to any item, I would strongly recommend defining a template).
  2. Create the Dialog Rendering item
    1. Add the Dialog Window Control and add the required controls to the Dialog Window placeholder.
    2. Add a SubPageCode control and create the JavaScript which provides the functionality required by the dialog.rendering item
  3. Add the dialog to the page using Client side insert renderings.
    1. The guid is the rendering item id (i.e. the attachment item defined in step 2).
    2. Then save the dialog in the subpage controls list with a unique name.
contextApp.insertRendering("{26D93861-13E8-4416-8319-0D03094A19CB}", { $el: $("body") }, function (subApp) {
        contextApp["showAddAttachmentDialog"] = subApp;

      });

  1. To show the dialog the show function on the DialogWindow control has to be called. There are a number of ways this could be achieved, but I decided to implement it as follows
    1. In the initialize function for the dialog (SubPageCode JavaScript), set up an event handler to listen for a show event i.e. add:attachment:dialog:show.
    2. Define a showDialog function which calls the DialogWindow.Show().code
    3. To show the dialog, trigger the event and pass the in the required parameters.events & paramter

Context Issue

The dialog does not have access to the page’s context and controls; therefore you pass the required context via the event parameters.
This in fact reduces dependencies as the only dependency is the event parameters; therefore the dialog is not aware of where it is called from.

Sitecore SPEAK – Pipelines

As promised here is a post about how to create a simple client side Sitecore SPEAK pipeline.

For an introduction to pipelines, see my first blog about Pipelines. I am going to create a pipeline which serves no purpose at all, it just writes “step 1”, “step 2” & “Step 3” to the console. There are 5 steps to implement a pipeline:

  1. Declare the pipeline
  2. Declare individual pipeline steps
  3. Add the steps to the pipeline
  4. Include JavaScript
  5. Execute the pipeline

How to declare a pipeline

Firstly you check if the pipeline is already declared, and if not you create it.


Sitecore.Pipelines.MyFirstPipeline = Sitecore.Pipelines.MyFirstPipeline ||
                                 new Sitecore.Pipelines.Pipeline("MyFirstPipeline");

Declare the step

Declare the pipeline step by creating an object which has a priority property and an execute function which accepts a context variable. The execute function is the where you write the code for this step, in this case log “step 1” to the console, or abort if the context.hasRun == true.


var step1 =
 {
   priority: 1,
   execute: function (context) {
     if(context.hasRun==true){
       context.aborted = true;
       return;
     }
     console.log("step 1");
   }
 };

The priority property defines the sequence that the steps are executed in, and as I mentioned in my previous post this is not ideal 😦 but Anders and I had an idea how to solve this so please read this great post by Anders.

But back to the point. If you want to abort a pipeline, you just have to set the property abort on the context to true, and then no more steps will be executed.

Add a step to the pipeline

Its easy to add a step to a pipeline, you just call the add method.

Sitecore.Pipelines.MyFirstPipeline.add(step1);

Not that it is a requirement, but I would suggest that all the code for a single step is contained in a single JavaScript file.


define(["sitecore" ], function (Sitecore) {
  Sitecore.Pipelines.MyFirstPipeline = sitecore.Pipelines.MyFirstPipeline || new Sitecore.Pipelines.Pipeline("MyFirstPipeline");
  var step1 =
  {
    priority: 1,
    execute: function (context) {
     if(context.hasRun==true){
       context.aborted = true;
       return;
     }
     console.log("step 1");
    }
  };
  Sitecore.Pipelines.MyFirstPipeline.add(step1);
});

Include JavaScript

Before we can call the pipeline we need to ensure that the JavaScript is loaded.
SPEAK wraps up the requireJS framework, so we can define which JavaScript files should be included.
I would recommend a single JavaScript file for each pipeline that is responsible for loading all the pipeline steps. Therefore when you want to execute a pipeline you only have to include one file. Also if you add steps to a pipeline you only have to update a single file and not all the places where the pipeline is executed.

var basePath = "/-/speak/v1/mfp/";
define(
    [
        basePath + "MyFirstPipeline.Step1.js",
        basePath + "MyFirstPipeline.Step1.js",
        basePath + "MyFirstPipeline.Step1.js"
    ]
)

When creating pipelines I tend to use the following naming convention:

[Pipeline Name].[Step Name].js and the file that includes all the steps is called [Pipeline].js, for example the file structure for MyFirstPipeline would be as follows.:

file structe

BasePath

So how does Sitecore know where to look for the JavaScript files, and what is that base path?

If you take a look in the Sitecore.Speak.config file in the include folder, you will see there is a speak.client.resolveScript section, which defines a server side pipeline that is responsible for resolving scripts.

The Sitecore.Resources.Pipelines.ResolveScript.Controls processor is used to load all the scripts required by controls. It has a section where you can define one or more sources. Take a look at the source which has the category “mfp” which I added so that the JavaScript for the application is included correctly.

  • Category – used to match which source to use to locate files.
  • Folder – the folder to search.
  • Deep – should it search sub folders.
  • Pattern – pattern to select files in the folders.
<processor type="Sitecore.Resources.Pipelines.ResolveScript.Controls, Sitecore.Speak.Client">
  <sources hint="raw:AddSource">
    <source folder="/sitecore/shell/client/Speak/Assets" deep="true" category="assets" pattern="*.js" />
    <source folder="/sitecore/shell/client/Speak/Layouts/Renderings" deep="true" category="controls" pattern="*.js,*.css" />
    <source folder="/sitecore/shell/client" deep="true" category="client" pattern="*.js,*.css" />
    <source folder="/sitecore/shell/client/speak/layouts/Renderings/Resources/Rules/ConditionsAndActions" deep="true" category="rules" pattern="*.js" />
    <source folder="/sitecore/shell/client/Business Component Library/Layouts/Renderings" deep="true" category="business" pattern="*.js,*.css" />
    <source folder="/sitecore/shell/client/Applications/MyFirstPipeline" deep="true" category="mfp" pattern="*.js,*.css" />
  </sources>
</processor>

This explains the mfp part of the base url (/-/speak/v1/mfp/) but what about the rest? The following setting specifies the prefix that should trigger the HTTP request customer handler for SPEAK.

<setting name="Speak.Html.RequireJsCustomHandler" value="/-/speak/v1/" />

Execute the pipeline

At last we are now ready to execute our pipeline, so we create the context, pass in the application and call execute. The context contains the pipeline specific information required by each step in the pipeline.
The first time the pipeline is executed all 3 steps are completed. The second time it will abort at the first step as the context property hasRun is set to true.


define(["sitecore", "/-/speak/v1/mfp/MyFirstPipeline.js"], function (Sitecore) {
 return Sitecore.Definitions.App.extend({
   initialized: function () {

   //clone the context, unless you want the changes to the context to be persisted
   var context = clone(this.currentContext);
   context.testAbort = false;

   //run the pipeline
   Sitecore.Pipelines.MyFirstPipeline.execute({ app: this, currentContext: context });

   //run the pipeline but it will abort
   context.hasRun= true;
   Sitecore.Pipelines.MyFirstPipeline.execute({ app: this, currentContext: context });
 }
 });
});

Well I hope you find this post helpful, as ever take it easy, Alan

SPEAK – Sitecore concept change and pipelines

A major concept change from sheer UI to SPEAK; is that continuation/control flow has moved from the server to the client. Now the client is responsible for control flow/state validation and the server is ideally responsible for answering simple/stateless requests.

Effectively a lot of server side C# code is moving to JavaScript, and to help with the complexity and to mirror the functionality/concepts available server side, Sitecore has introduced as part of the SPEAK framework client-side pipelines.

Client Pipelines

This is a very brief introduction and I will go into more detail in follow up post.

  1. A pipeline is a set of one or more steps that can be executed in a specific order; each step shares the same context.
  2. Each step can change the context and pass it to the next step until all steps have been completed, and or the pipeline is aborted.
  3. Each step can contain both client and server side logic or just client side logic.

The diagram below illustrates a simple pipeline that is executed when a button is clicked.

Pipeline

Example

So what can we use pipeline for? well one example could be when you delete an item, the idea is to keep each step as short and sweet as possible:

  1. Step 1 – Can Delete – check if the current user has the required permissions to delete the item.
  2. Step 2 – Can Lock – the item might be locked
  3. Step 3 – Confirm – prompt the user to ensure they really want to delete the item.
  4. Step 4 – Check Clone Links – as you have to warn the user that any cloned items will become real items.
  5. Step 5 – Execute – delete the item
  6. Step 6 – Has links – show the broken link dialog to fix any broken links
  7. Step 7 – Un-clone items.

Limitations

Unfortunately the current implementation does not provide the ability to define the order the steps are executed in, using a configuration file or the web.config. The order is defined by a property called priority that is defined within the JavaScript itself, see the code below.

define(["sitecore"], function (Sitecore) {
 Sitecore.Pipelines.MyFirstPipeline = Sitecore.Pipelines.MyFirstPipeline ||
                                      new Sitecore.Pipelines.Pipeline("MyFirstPipeline");
 var myFirstPipeline =
 {
   priority: 1,
   execute: function (context) {
     console.log("My first pipeline");
   }
 };
 Sitecore.Pipelines.ValidateMessagePageLoad.add(myFirstPipeline);
});

There are number drawbacks:

  1. Each step needs to know about other steps in order to be called in the appropriate sequence.
  2. Difficult to see a clear picture for a Pipeline (in which order steps are executed).
  3. If you want to insert/remove an extra step it an existing Sitecore speak pipeline you have to modify Sitecore’s source code.
  4. It increases the complexity of upgrade.

Hope

I hope that future version of SPEAK will support declarative declaration of pipeline steps, similar to what is available server side (i.e. pipelines defined in the web.config and or include configuration files).

My next post will go into more detail about how to create and initiate pipelines, hope you enjoyed my first ever post, Alan