Picking Custom Email Templates

tim2222
Tera Expert

We have had a need to allow users to compose new emails based on a template and data from the record, which they can then modify and/or verify before sending.

I've looked at a number of options for how to achieve this:

While notifications allow an email to be templated and use values from the record, there are a few shortcomings:

  • The user can only specify plain content by e.g. providing a public note.
  • The user cannot specify the recipient(s).
  • The user cannot provide attachments.

The email client can use templates that are conditional on the values on the record but this means the user has to change a value, save the record then launch the email client to pick a template. There are variations in the community by using mail scripts which can adapt based on a custom URL but that means a) whoever creates the templates needs to understand scripting b) a lot of duplication across the To, CC etc. fields.

Quick messages (starting in Jakarta?) don't support getting values from the record so aren't useful for templating.

In the end I've come up with a new solution, building on existing approaches but utilising the power of the Client Template system.

This post describes how to create a dialog window that will allow the user to pick from a list of available templates and launch the email client with that template.

Note: by default conditions and order fields Client Templates are hidden. Customise the form layout to show these fields and apply conditions / order to only show the templates you want.

What makes this work is a query business rule on the client templates table, which filters the results based on a URL parameter. I've actually built this in a custom app but you can't apply a query business rule from a scoped application so that had to go outside the app ...

Business Rule

FieldValue
NameFilter by URL parameter
Tablesys_email_client_template
Advancedchecked
Insertunchecked
Updateunchecked
Querychecked
Script

(function executeRule(current, previous /*null when async*/) {

     

      var url = gs.action.getGlideURI();

      var name = url.get('sys_email_client_template');

      if (!JSUtil.nil(name)) {

              current.addQuery('name', name);

      }

})(current, previous);

UI Action

FieldValue
NameCompose Email
Tablesc_req_item (** or whatever task table)
Form buttonchecked
Clientchecked
OnclickcomposeEmailDialog()
Conditiongs.hasRole('itil') && current.canWrite()
Script

function composeEmailDialog() {

      var dialog = new GlideDialogWindow('email_template_picker');

      dialog.setTitle("Choose Email Template");

      // blows up if we just specify "table" as the preference name - some kind of bug/conflict

      dialog.setPreference('sysparm_table', g_form.getTableName());

      dialog.setPreference('sysparm_sys_id', g_form.getUniqueValue());

      dialog.render();

}

UI Page

FieldValue
Nameemail_template_picker
HTML

<?xml version="1.0" encoding="utf-8" ?>

<j:jelly trim="false" xmlns:j="jelly:core" xmlns:g="glide" xmlns:j2="null" xmlns:g2="null">

      <g:evaluate var="jvar_table" expression="(RP.getWindowProperties()).sysparm_table" />

      <g:evaluate var="jvar_sys_id" expression="(RP.getWindowProperties()).sysparm_sys_id" />

      <g:evaluate var="jvar_template" object="true" jelly="true">

              var table = jelly.jvar_table;

              var sys_id = jelly.jvar_sys_id;

              var current = new GlideRecord(table);

              var template = new GlideRecord('sys_email_client_template'); // fake

             

              if (sys_id != '' ${AND} current.get(sys_id)) {

                      gs.include('CustomEmailTemplate');

                      var cet = new CustomEmailTemplate(current);

                      template = cet.listTemplates();

              }

             

              template;

      </g:evaluate>

      <g:evaluate var="jvar_template_count" jelly="true">

              jelly.jvar_template.getRowCount()

      </g:evaluate>

      <g:ui_form>

              <input type="hidden" name="table" value="${HTML:jvar_table}" />

              <input type="hidden" name="sys_id" value="${HTML:jvar_sys_id}" />

              <table width="100%">

                      <tr><td>

                              <select name="template">

                                      <j:while test="${jvar_template.next()}">

                                              <option value="${HTML:jvar_template.getValue('name')}">${HTML:jvar_template.getValue('name')}</option>

                                      </j:while>

                              </select>

                      </td></tr>

                      <tr><td>

                              <g:dialog_buttons_ok_cancel ok="return openCustomEmailTemplate($j($j(this).parents('form:first')))" ok_type="button" cancel_button="button" />

                      </td></tr>

              </table>

      </g:ui_form>

</j:jelly>

Client script

function openCustomEmailTemplate(form) {

      var table = form.find('input[name="table"]').val();

      var sys_id = form.find('input[name="sys_id"]').val();

      var template = form.find('select[name="template"]').val();

      var url = 'email_client.do?';

      url += jQuery.param({

              sysparm_table: table,

              sysparm_sys_id: sys_id,

              sysparm_target: table,

              sys_target: table,

              sys_uniqueValue: sys_id,

              sys_row: 0,

              sysparm_encoded_record: '',

              sys_email_client_template: template,

              sysparm_stack: 'no'

      });

     

      GlideDialogWindow.get().destroy();

     

      popupOpenEmailClient(url);

}

(function() {

// FIXME - this refers to the sys_id of the UI page - you will need to fix this!!!

      var form = $j('#form\\.63303692db5683006f3df57eaf961948');

      var c = form.find('select[name="template"]').find('option').length;

      if (c == 1) {

              openCustomEmailTemplate(form);

      }

})();

Script Include

FieldValue
NameCustomEmailTemplate
Script

var CustomEmailTemplate = Class.create();

CustomEmailTemplate.prototype = {

      initialize: function(current) {

              var self = this;

             

              self.current = current;

      },

     

      /*

        * Get a template query that matches the current record

        */

      listTemplates: function() {

              var template = new GlideRecord('sys_email_client_template');

              template.orderBy('name');

             

              var template_ids = [];

             

              var current = this.current;

             

              var gr = new GlideRecord('sys_email_client_template');

              gr.addQuery('table', current.getTableName());

              gr.query();

              while(gr.next()) {

                      if (GlideFilter.checkRecord(current, gr.condition)) {

                              template_ids.push(gr.sys_id + '');

                      }

              }              

             

              if (template_ids.length == 0) {

                      template.addQuery('sys_id', '-1'); // no match

              }

              else {

                      template.addQuery('sys_id', 'IN', template_ids);

              }

             

              template.query();

             

              return template;

      },

     

      type: 'CustomEmailTemplate'

};

Client callableunchecked

On Requested Items this should provide a new UI Action called "Compose Email". If you have more than one template defined, clicking this will launch a dialog that allows the user to pick a template to compose using. If only one template is defined the user will be launched straight into that template.

3 REPLIES 3

avid
Kilo Expert

Great work.

For some reason, i'm unable to use the "sys_email_client_template" parameter.

I've got 2 templates (test and test2) on the same table (sc_task), and no matter what value I'm setting in the  sys_email_client_template I always get the same random template (test2 in my case).
For example, that URL doesn't work and I'm always getting the test2 template, with or without the parameter:

https://MY_INSTANCE.service-now.com/email_client.do?sysparm_table=sc_task&sysparm_sys_id=f510a3b4db068bc0d982a3e84b9619e4&sysparm_target=sc_task&sys_target=sc_task&sys_uniqueValue=f510a3b4db068bc0d982a3e84b9619e4&sys_row=0&sysparm_encoded_record=&sys_email_client_template=test&sysparm_domain_restore=false&sysparm_stack=no

 

I also tried to put the template sys id instead of the template name but still without luck.

 

Any thoughts?

 

Thank you
Avi

Hi,

Can you confirm the business rule is working as expected?

Navigate to sys_email_client_template.LIST and add to the URL:

...&sys_email_client_template=test

You should only see the template listed with the name of "test".

 

Do you have any conditions on the templates?

 

The only code that should affect the email client is the business rule. Are the other scripts working for you i.e. you get a "Compose Email" button which provides you a choice of templates?

(One last, untested, suggestion - you can disable the email client via a system property - would this have an effect?)

All the best,
Tim

Great work, I was looking for this.

however it did not work completely, I had to fiddle the ui-page a litte. (change line 5 and 7 to the following values.) because it gave errors


<g:evaluate var="jvar_table" expression="RP.getWindowProperties().get('sysparm_table')" />

<g:evaluate var="jvar_sys_id" expression="RP.getWindowProperties().get('sysparm_sys_id')" />