Saturday, 17 March 2018

Building my own Learning System - Part 4

Building my own Learning System - Part 4



In Part 1 of this blog series I covered the problem I was trying to solve (on-boarding/accrediting internal/external staff using common content, but without opening up everything to the entire world) and the data model I was using to manage this. Part 2 was around the fledgeling user interface and a fake service to prove confidence in the method. Part 3 covered the backend, or at least the initial implementation of this - as long as there is a local interface implementation to connect to it, the concrete backend can live anywhere.

Now that I’ve been through the building blocks, it’s time to get into the code and also mention a couple of interesting features that I’ve put in place and share the front end code.

Show me the code!

The front end code lives at


If you want to try this out yourself, here’s the approach I’d recommend. 

First configure MyDomain - you can’t use lightning components without this.

While you could just deploy the front end code using the Salesforce CLI (or one of the legacy tools, such as ant) I’d recommend using the unmanaged packages. There are two of these, containing the following items:

  • The custom metadata types to configure the endpoints and the implementation of the service
    <salesforce URL>/packaging/installPackage.apexp?p0=04t0O000001Ehcd

  • Everything else - UI lightning components, data accessor, service implementation
    <salesforce URL>/packaging/installPackage.apexp?p0=04t0O000001Ehd2

I’ve split these into two packages because the configuration should be static, so ideally that will be installed once and only the contents of custom metadata types will change. The package containing everything else will change as new features are added. While as an unmanaged package this can’t be upgraded, as the data is stored elsewhere (the training content endpoints) uninstalling the old version and installing the new one doesn’t lose anything so seems like a reasonable approach. Why an unmanaged package I hear you ask? Mainly because this is unlikely to hit the app exchange so I’d be asking everyone to trust me and install code that they couldn’t see in their orgs. While I’m a trustworthy guy, this didn’t feel like the right thing to do

The backend code doesn’t really lend itself to an unmanaged package, as there will be plenty of data to recreate, and I didn’t want to use a managed package for the reason mentioned above, so I’d recommend using the Salesforce CLI or similar to deploy via metadata.

Of course you can always install the code in your own packaging org and build your own package (managed or unmanaged) from it. Worst case is you might have to do some jiggery pokery when I push new features, as I won’t be taking that into account. 


To begin with, I’d recommend configuring things to use my example endpoint via the following steps:

  1. Create a new instance of the Training_Config custom metadata type with the following settings:
    Label/Name : Default
    Service Implementation : TrainingServiceRemoteImpl

  2. Create a new instance of the Training_Endpoint metadata type with the following settings:
    Label: Bob Buzzard
    Name: Bob_Buzzard
    Path: /services/apexrest/TrainAPI
    Rewrite Image Links: Checked

  3. Add the training endpoint hostname to your remote sites, otherwise you’ll get errors when attempting to callout

  4. Edit the Training lightning app page and make it available for your profile

Then navigate to said page and away you go.

Note:t the front end sends your email address to the back end - this is purely used to identify your requests, but you are trusting me not to spam you (I won’t because what’s in it for me?).

Interesting Features

  • Restricting Access to Paths

    As you may want to beta test content, a training endpoint has the concept of opening up a training path to a selected group of users. In the sample back end we only know about the user’s email address, so this is how it is controlled. You can create a Candidate Restrictions sobject instance, which defines a domain and the addresses with that domain that are or are not allowed access, and then link this to a Training Path via the Training Path Candidate Restriction junction object, If there are no restrictions, a training path is open for anyone to access. Not that this shouldn’t be consider any kind of secure authorisation system, it’s purely a simple way to stop people being presented with a path before you are read for them to see it. If you need to lock things down, protect the endpoint via authentication

  • Wait Your Turn

    If you specify the Hours Between Attempts field on a training path and a user answers the questions incorrectly, they will be made to wait until at least that number of hours have elapsed before trying again. Hopefully this will cut down on the number of people guessing their way through paths. Probably not, but you can only go so far without reinventing web assessor!

Caveat Emptor

The error handling is fairly basic, mainly because the errors are typically down to bad data/setup at the remote endpoint, so I usually catch them before users do. 

 Nothing is labels yet - that’s on my todo list, but it’s all hardcoded English strings for now.


If you hit any problems, raise an issue in the appropriate git repo. I’ve done quite a bit of testing, but if there’s one thing 30+ years in the software industry has taught me, it’s that as soon as I let anyone loose on my stuff it gets broken. I may just take it down your issue on my invisible typewriter and file it in the bin, but equally I might fix it, so it’s worth rolling the dice. 

In the next instalment of this series, I’ll share the backend code and what you need to do to create your own training endpoint and paths.

Related Posts


Sunday, 11 March 2018

Turning on the Lightning Locker Service

Turning on the Lightning Locker Service



This week I turned on the Locker service for an application that I wrote several years ago. It’s a few “pages” built from a fairly large number of custom Lightning components with a lot of JavaScript business logic. The application itself works fine without the Locker service, but there’s more and more standard components and features that I’d like to use, but that are only available in API 40+. I also have a JavaScript library that some of the components interact with, so I needed to upgrade all of my components at the same time, or risk some of them using a different window object.

I’ve made various attempts at this in the past, but always been defeated by weird errors that I was unable to isolate or reproduce. aura:if was often in the vicinity though, so it’s always my prime suspect. The last attempt was about 6 months ago and I’d created quite a few applications running on the latest API in that time, and I was hopeful that nth time is the charm, so I ran my script to update all of the meta-xml files to API 41 (there are 300 of them, so not really something that can be done manually) and deployed the app to my dev org. Here’s what I found.


Deployment Time

The first attempt failed with 19 errors, including the following:

  • Invalid Aura API - $A.util.json.encode. 
    This shows how long this application has been around - some of the very early examples in the Lightning Components Developers Guide etc used this method, but it’s been advised against for a while. I thought I’d cleared them all out, but had obviously missed one or two. This is a simple fix, just use JSON.stringify instead.

  • Invalid Aura API - $A.util.format.
    This is around replacing tokens such as {0} in strings/labels etc and can be replaced with the String.format standard JavaScript function, so rather than:

        $A.util.format(<string>, val);

    you would have

        <string>.replace(‘{0]’, val);

  • Invalid Aura API - this was thrown from a configuration item being passed to a JavaScript library function containing the text ‘onSelect’. I’m pretty sure that this was a false positive, but as this was something that I’d created an alternative pure Lightning version of, I don’t think I’ll be needing it going forward so sacked it off.

The biggest issue was : Invalid SecureWindow API, top was blacklisted. Even though this is a Lightning Experience application and uses LEX navigation, there’s still one place where I surface part of the application through Lightning Out, when creating a child object. Even though I’ve overridden the new action, this isn’t respected so I end up in the regular modal style new component.Thus I navigate to a Visualforce page instead and use Lightning Components for Visualforce to surface the contents. This is all fine until I want to get back to the man page for the app. I can’t use force:navigateTo methods by default, as these are only available in LEX, and if I set the window.location to a new value, that just changes the Visualforce iFrame embedded in the page. Thus I used, as this changes the URL for the outermost window of the app.

There is a solution to this though - I can create my own handler for force:navigateToSObject event in the Visualforce page, and as there is no locer service in Visualforce I’m free to tinker with the outer window location to my heart’s content. Make sure that you add the dependency reference to the event to your Lightning app though, e.g.

    <aura:dependency resource="markup://force:navigateToSObject" type="EVENT"/>

I didn’t to begin with and spent a lot of time trying to figure out why it wasn’t working!

Run Time

I only hit two issues at runtime, and the biggest hurdle was actually getting the errors surfaced - I ended up taking a binary chop approach to find the problem component - commenting out half of the functionality at a time until i was able to narrow things down, then surrounding lots of code with try/catch exception handlers.

  1. Missing ‘var’ when using a variable, e.g.

        for (i=0; i<len; i++)

    Without the locker service, this will try to find a variable named ‘i’ against through the scope chain and, if it doesn’t find one, create it in the global scope. Almost certainly not what is required and in my cases definitely not. With the locker service, ES5 strict mode is enabled and this generates a reference error

  2. Getting non-existant attributes, e.g.

        var prop=cmp.get(‘prop’)

    Spot the problem? Missing the ‘v.’ namespace for the attribute, although in one case this was there but the attribute hadn’t been declared in the markup.

  3. Breaking encapsulation
    In this case, I was programmatically finding a component in the ‘ui’ namespace and changing an attribute. This is exactly what the locker service was created to stop, so no surprises there was an error. I’d completely forgotten it was there - it was a workaround to a bug with the standard select component where I couldn’t dynamically set the multi attribute based on one of my component attributes. It’s fixed now, and probably was years ago, so I just removed the offending code.

In my app, these are genuine bugs and it’s good to get them out of the way. Things obviously still work at the moment, but are fragile - in the first case, if there is an existing variable in the scope chain I’ll overwrite it’s value, which never ends well.


While I haven’t exhaustively tested every aspect of my app, the weird errors that I’ve seen in the past aren’t appear this time around. I’m sure that they weren’t all down to the locker service - I’ve fixed plenty of issues in my app over the years, but the locker service definitely made things more difficult to track down and proved to have plenty of gaps when used in anger. But for my purposes, it’s ready for prime time.

Related Posts

Saturday, 3 March 2018

Building my own Learning System - Part 3

Building my own Learning System - Part 3



In Part 1 of this blog series I covered the problem I was trying to solve (on-boarding/accrediting internal and external users with the same content, but without opening up all my content to everyone) and the data model to support this. Part 2 covered the user interface and a faker to allow me to check that my idea had legs without building the whole thing - if I’m going to fail, I like to get it over with as quickly as possible. This wasn’t the case though, so I then proceeded to build out the backend.

There are any number of ways to implement the backend, both on Salesforce and elsewhere. As I’ve created an Apex interface that the front end works against, any mechanism can be supported simply by creating a new implementation of the interface that knows how to talk tot the remote endpoint. For the purposed of my sample implementation I went with e REST endpoint surfaced via a site, as this means I don’t have to worry about authentication, can focus on the business problem and can easily use to test. As to whether this is appropriate for a real training endpoint is a topic for discussion - it depends on how sensitive the information is and whether there is any issue with someone getting hold of it. I decide for each endpoint based on these and other factors.


Again in the interests of simplicity, I went with a REST API that exposes a single POST method that acts as a dispatcher. The body of the request contains the underlying “method” that should be invoked, and any parameters required for that method. While this might not please the purists, as I don’t have an endpoint per object, I didn’t want to have to create a new Apex class to implement each method that I added, especially for a sample implementation.

The API implements much the same interface as my faker, but with additional parameters to identify the candidate taking the training. As I’m not using authentication, I identify users by their email address. Note that this is about identification rather than authentication or authorisation, as anyone can choose any email address.


The client makes use of a new implementation of the same API as the faker, that simply sends a request to the remote REST API. As I want to support multiple endpoints, these are configured via a custom metadata type in the client org. This means that a user has a number of training histories and badge totals, one per endpoint,

In my sample REST API, the training step details are retrieved from rich text area fields on sObjects. Any images that have been added via the rich text editor will be returned with a relative URL which will obviously point to nothing in the client org. To fix this, the custom metadata for an endpoint has a checkbox to indicate if images should be rewritten. If this is checked then the endpoint hostname is prepended to the image links, which works a treat.

As nothing is now stored in the client org, a user can install the training metadata components in any Salesforce org and, as long as they reuse the same email address that they’ve previously taken training with and configure the endpoints appropriately, they can pick up where they left off. This was a key feature for me as I really didn’t want to have to install the training paths along with the client software, as it makes things must more resistant to change. 

That’s it for part 3 - in part 4 I’ll go through the installation and configuration and, all things being equal, share the code too.

Related Posts