Backend JavaScript
  • 04 Aug 2022
  • 19 Minutes to read
  • Contributors
  • Dark
    Light
  • PDF

Backend JavaScript

  • Dark
    Light
  • PDF

The system uses ECMAScript E5/E5.1 for execute code in the backend. For general information about JavaScript there is plenty of information available, e.g. on Wikipedia or MDN. The main difference is that E5 does not offer the convenience of async/await and variables share the same scope in a function. However E5 still offers a very convenient way to interact with external services because of the scope chain in JavaScript. The backend comes with a few extra add on functions which are described here.

HTTP

system.http(args): This function initiates a HTTP request. The function takes a single argument that contains an object with keys that control the operation. The following keys are available:

  • url: The URL for the request, e.g. "https://vodia.com/request?p=1".
  • method: The method for the request, default is "GET".
  • logurl: The URL that will be used for logging purposes. If not present the url key will be used. This key can be used e.g. to mask parameters in the URL.
  • body: The body for the request.
  • verify: A flag that tells the subsystem to check the validity of the certificate in a HTTPS request. The default is true.
  • attempts: The number of attempts to try the request, the default is 1.
  • readTimeout: The timeout for receiving a response in ms, the default is 10000.
  • connectTimeout: The timeout for connecting to the server in ms, the default is 10000.
  • header: An array with additional headers for the request. Each entry is an object with the keys name and value. If the key secret is set in that object, the header value is masked, e.g. for authentication headers.
  • domain: The tenant context, mostly used for logging purposes.
  • callback: A function that will be called after completion.

The callback function takes the following arguments:

  • code: The code, e.g. 200 for a success response.
  • data: The body of the response
  • headers: The headers of the response. This is an array of objects with key names name and value.

Email

system.email(args): This function is used to send an email. The function takes a single argument that contains an object with keys that control the operation. The following keys are available:

  • domain: The tenant for the request, if available.
  • from: The senders address, optional. The address is an object with two keys named name and adr.
  • user: The user context for the email (optional).
  • to: The recipient address, optional. The address is an object with two keys named name and adr. If the to key is not present, the system will send the email to the user email address.
  • template: The name of the email template to use for the email, for example "email_performance.htm".
  • vars: Variables that should be used when rendering the template. The variables are stored in an object where the keys contain the strings that can be used in the templates.
  • html, text and body: The content for the message (if no template is used). The system will use the html content when available, otherwise the text content when available, otherwise the body wit the raw email body content.
  • attachments: Attachments for the email as array (optional). Each attachment is an object containing the following keys:
    • id: An ID for the attachment that can be used in the email as reference (e.g. for images).
    • type: The MIME type for the attachment, for example "image/png".
    • content: The content of the attachment either as string or as UInt8Array.
  • header: An optional array of additional headers for the email. Each entry is a object with the keys name and value.

system.parseEmailAdr(adr): This function parse an email address and returns an object with the keys name and adr. For example system.parseEmailAdr("Joe Doe <joe.doe@vodia.com>") would return { name:"Joe Doe", adr: "joe.doe@vodia.com" }.

Core JavaScript

  • setTimeout(func, ms): This function schedules a call to func after ms milliseconds. It returns a handle to the timeout that can be used to cancel the timeout.

  • clearTimeout(id): This function can be used to clear a timeout previously scheduled with setTimeout.

  • system.format(text, ...): This function is used to simplify the formatting of strings with variable input. For example system.format("Hello %s you have %d messages", "Joe Doe", 12) would return the string "Hello Joe Doe you have 12 messages". Arguments can be of the type bool, int and string. The following formats are available:

    • %s: Insert the variable content as string.
    • %d: Insert the variable content as integer number.
    • %u: Insert the variable content as unsigned integer number.
    • %x: Insert the variable content as hexadecimal string.
    • %m: Insert the variable content as XML encoded string.
    • %j: Insert the variable content as JSON encoded string.
    • %r: Insert the variable content as URL encoded string.
    • %%: Insert a %character.
  • system.date(tz, time): Returns an object that contains the current time. The optional tz contains the name of the timezone to use. If time is present it contains the number of seconds since 1970 to be used. If time is before 1980 it is interpreted as offset to the current time. The function returns an object with the following keys:

    • year: The full year, e.g. 2022
    • month: The month (1-12)
    • day: The day (1-31)
    • hours: The hours (0-23)
    • minutes: The minutes (0-59)
    • seconds: The seconds (0-59)
    • wday: The day of the week (0 = Sunday)
    • week: The week of the year (0..52)
    • milliseconds: The milliseconds part of the time
  • system.timeZone(domain, user): Returns the timezone for the tenant and user. Both domain and user are optional, except that domain must be available when user is available.

  • system.lang(lang, file, item): Returns a language asset from the dictionary. lang is the language (defaults to "en"), file the name of the file and item the name of the item. For example system.lang("en", "button", "save") would return "Save".

  • system.getPage(file, domain, user): This function returns the content of a template in the tenant and user context.

  • system.hasAudioFile(name): Returns true if the audio file exists. For example system.hasAudioFile("audio_moh/moh.wav") should return true.

  • system.addAudioFile(name, content): Adds an audio file.

  • system.version(): This function returns information about the PBX version as object. It contains the key version which contains the version number (e.g. "69.0"), 'arch' which contains the OS (e.g. "CentOS64") and build which contains the build date.

  • system.globalNumber(number, country, area): This function converts a number into a global number in the context of the country and area code.

  • system.localNumber(number, country, area): This function converts a number into a local number in the context of the country and area code.

  • system.displayNumber(number, country, area): This function converts a number into a human readable number in the context of the country and area code.

  • system.setCallInfo(call, info): This function can be used to update caller-ID information. call is the identifier of the call, typically provided by one of the callbacks in the integrations framework. info is a object that is passed to the front end, for example {"name":"Joe Doe","company":"ACME car sales"}.

  • toHexString(array): Converts a UInt8Array into a hexadecimal string.

  • fromHexString(data): Converts a hexadecimal string into a UInt8Array.

  • toBase64String(array): Converts a UInt8Array into a base64 string.

  • fromBase64String(data): Converts a base64 string into a UInt8Array.

  • system.getIP(adr): Get the IP address that would be presented when contacting the address adr.

  • system.httpRepresentation(domain): This function will look up how the HTTP presentation of the tenant would look like. It returns an object with the following keys:

    • scheme: Either http or https.
    • address: The address that would be presented to a browser for the tenant.
    • port: The port that would be presented to a browser for the tenant. If empty it means the default port should be used.
    • http-port: The HTTP port for the system.
    • https-port: The HTTPS port for the system.
  • system.restApi(obj): This function triggers the execution of a REST API call, similar to an invocation from a browser on system admin level. The following keys are used:

    • domain: The optional tenant context, used for logging only.
    • method: The method to be used, defaults to "GET".
    • path: The path to be used, must be present. For example "/rest/system/status" would return the system status.
    • body: The optional body for the request.
    • content-type: The optional content-type for the request.
    • hostname: The optional hostname for the request.
    • args: Optional arguments for the request, for example "group=1&search=all".
  • system.deleteFile(filename): This function deletes a file on the filesystem.

  • system.writeFile(filename, content): This function write a file to the filesystem.

  • system.readFile(filename, silent): This function reads a file from the filesystem. If set to true the silent argument will suppress warning if the file could not be read.

Cryptography

  • crypto.random(begin, end): This function returns a random number in the range between begin (including) and end (excluding). For example, system.random(128, 255) could return 128, 200, 255, but not -100, 127 or 255.
  • crypto.randomBytes(size, pattern): This function returns size random bytes. If the optional pattern parameter is present, the result is a string that contains only characters in the pattern. Otherwise this function returns an UInt8Array with the random bytes.
  • crypto.createHmac(algorithm, key): This function creates a HMAC object. The following algorithms are available: md5, sha1 and sha256. The digest can be base64 or hex. See the following example:
var hmac = crypto.createHmac('sha256', 'keykeykey')
hmac.update('testtesttest')
var digest = hmac.digest('hex')
  • crypto.createSign(algorithm): This function is used to sign content. Currently only rsa-sha256 is available. The signature can be returned in base64 (default). See the following example:
var signer = crypto.createSign('RSA-SHA256')
signer.update('texttobesigned')
var signed = signer.sign('keykeykey', 'base64')

Data access

  • system.setting(name, value): This function is used to access system settings. If the value is provided, this function will set the setting value. Otherwise, it will return the value of the setting.
  • system.createAccounts(obj): This function creates new accounts from a CSV table. The object must contain a key with the name csv and a string that contains the account information.

Internal tables

The system stores most of the data in internal tables. These tables operate in memory and return results synchronously. Tables include user, domains, domain_alias, users, user_alias, admins, extensions, extlog, attendants, callingcards, autocallback, hunts, hoots, srvflags, ivrnodes, alerts, acds, conferences, orbits, schedules, wipers, registrations, regidx, dial_plan, dial_plan_entry, cobjs, legs, messages, adrbook, trunks, rates, colines, mohs, pnp_parms, accesslist, webpages, macs, cseqs, cells, certificates, audiofiles, outbounds, locations, billcfg, billdata, billjob and syncedcontacts.
For each table the following functions are available:

  • add(write): Add a new row. If write is true, the new row is committed immediately.
  • remove(id): Remove the row with the id id.
  • get(id, column): Returns the content of the column with the name column in the row with the id id. If the field name is * the call returns all columns in one object.
  • set(id, column, value, write): Write the content of the column with the name column in the row with the id id with value value. Unless write is false the value is committed immediately. If column is "" but write is true, this will trigger the writing of the row.
  • has(id): Returns true if the row with the id id exists.
  • count(col1, val1, col2, val2): Count how many rows match the search. The search can contain 0, 1 or 2 columns.
  • search(col1, val1, col2, val2, col3, val3): Search for values in the table. The search can contain 0, 1, 2 or 3 columns. The result is an array with the ID that matched the search.

External tables

In addition to the internal tables, the system maintains a list of external tables. These tables store the data no in memory and allow much larger size than the internal tables. However queries are performed asynchronously. Tables include cdr, cdrt, cdre, cdri, recs, billcdr, gstatm, astatm, gstatw, astatw, gstatd, astatd, gstath, astath, chat and chist.
For each table the following functions are available:

  • add(write): Add a new row. If write is true, the new row is committed immediately.
  • remove(id): Remove the row with the id id.
  • get(id, column): Returns the content of the column with the name column in the row with the id id.
  • set(id, column, value, write): Write the content of the column with the name column in the row with the id id with value value. Unless write is false the value is committed immediately.
  • has(id): Returns true if the row with the id id exists.
  • count(col1, val1, col2, val2, callback): Count how many rows match the search. The search can contain 0, 1 or 2 columns. The system uses the callback function to return the result.
  • search(col1, val1, col2, val2, col3, val3, callback): Search for values in the table. The search can contain 0, 1, 2 or 3 columns. The result is an array with the ID that matched the search. The system uses the callback function to return the result.

Tenant information

In order to simplify the access to tenant information, there are two functions available that access access of a tenant.
Domain.get(id, col): Return the value of the column for the tenant with the ID id. The ID can be the name of the tenant as a string (e.g. "localhost") or the index as a number. In order to get the index, a special query Domain.get(name, "*") will return the index.
Domain.set(id, col, val): Set the value of the tenant column with a value.
Domain.globalNumber(id, local): Return a number in global format in the tenant with the ID id. For example a number in the USA would be shown as +16173998147.
Domain.localNumber(id, global): Return a number in local format in the tenant with the ID id. For example a number in the USA would be shown as 6173998147.
Domain.displayNumber(id, global): Return a number in huan readable in the tenant with the ID id. For example a number in the USA would be shown as (617) 399 8147.

Account information

Because of the way the database is structured, access to account information requires two queries. In order to simplify that, there are functions that simplify that into one step.

Account.get(domain, user, column): Retrieves the value of the column for the user. domain and user can be either a string or a number that identifies the tenant or account. The column is the name of the column that should be retrieved. In addition to the standard field names a few additional column names are available:

  • alias-name: Retries the primary alias name of the account.
  • display-name: Retries the display name of the account.
  • language: Retries the language for the account.
  • eani-number: Retries the EANI number for the account.
  • eani-name: Retries the EANI name for the account.
  • agents: Returns all agents for the queue.

Account.set(domain, user, column, value): This function set the value of a column.
system.findUser(domain, account): This function searches for the account in the tenant and returns the ID if found.
system.setLocation(obj): This function set the location.

Address book

In order to simlify the access to the internal address book, there is a JavaScript class for the address book available. An address book entry has the following columns:

Name Description
number The landline number in global format (starting with +)
mobile The mobile number in global format (starting with +)
fax The FAX number in global format (starting with +)
display_number The number as it was entered
display_mobile The number as it was entered
display_fax The number as it was entered
first First name
name Last name
company Company
email Email address field
position Position, title
speed Speed dial index
type Address book type
tag Generic tag that can be used to store additional information, for example an external address book identifier for easy matching
cmc Client matter code, or customer ID
comment A general comment field
expires The day when the entry expires (e.g. 20081224)
agent The last agent this contact was talking to
dnc Flag to check if that number must not be called
melody Ring melody associated with that contact
category Category

In order to use the address book, an object first must be created. Then on this object, there are several functions available.

AddressBook(domain, user) creates a new object that can be used later to access the address book of the user or the tenant. domain is the tenant identifier (either a number or a string) and must be present. user is optional and the identifier for the user account (either a number or a string).
search(field, number, callback) or search(number, callback) searches for a specific entry in the address book. If the field is present, it will search specifically that field, otherwise it will try number, mobile and fax. Searchable fields are number, mobile, fax, speed, tag, cmc and category. After completing the search, it will call the callback function with the address book entry ID as argument or false when not found.
get(id, field) will return content from the address book entry with the provided id. If field is a string, it will return only that column. If it is an array of stings, it will return an object with the specifield columns. Otherwise it will return all columns in one object.
update(id, fields, callback) updates an address book entry with the id. fields must be an object that contains the column names and values for the update. When done, the callback will be called, if available. If the display fields for the numbers are not provided, the system will automatically generate human readable entries.
create(fields, callback) creates a new address book entry and then uses the update the update function on the new record.
erase(id, callback) erases a entry and calls the callback when done.

Example code:

var adr = new AddressBook("localhost");
adr.search("6173998147", function(id) {
  adr.update(id, "fax", "6171234567");
);

MySQL

In order to access an external MySQL database the backend provides an object with the name MySQL. The constructor contains the following keys:

  • address: The address of the database (default is "127.0.0.1").
  • database: The name of the database.
  • username: The username for the database (default is "root").
  • password: The password to be used.

query(text, callback) can be used to perform a query on the database. The text contains the SQL query to be sent to the server. callback is the optional function that is being called when the result is available. The callback function takes three arguments: the first is the return code for the operation, the second is an array with the name of the columns and the third is an array with the content of the rows.
encode(text): This function encodes a string so that it can be used in a query.

var q = "1234"
var sql = new MySQL({ database: "pbx", password: "secret" })
var query = system.format("SELECT * FROM table WHERE col='%s'", q)
sql.query(query, function(code, cols, rows) {
  console.trace("SCRIPT", 5, "", system.format("Code %d: %s", code, JSON.stringify(cols)), JSON.stringify(rows))
})

XML processing

While parsing and generating JSON arguments is natively supported in JavaScript, XML requires more effort. For this the backend offers the SAX object.

write(text): This function feeds text into the XML parser.

on(event, func): This function defines callbacks when certain events are triggered. The following events are available:

  • text(content): This event is triggered when text is available.
  • opentag(name): This event is triggered when a tag is opened.
  • closetag(name): This event is triggered when a tag is closed.
  • attribute(name, value): This event is triggered when an attribute has been found.
  • comment(text): This event is triggered when a comment has been found.
  • opencdata(): This is triggered when a [CDATA[ segment starts.
  • cdata(data): This event reports the CDATA content.
  • closecdata(): This is triggered when a [CDATA[ segment ends.
  • sgmldeclaration(decl): Triggered when a SGML declaration is available.
  • doctype(type): This is triggered when a DOCTYPE is available.
  • processinginstruction(name, body): This is triggered when a processing instruction was found.

A typical use of XML looks like this:

var result = {}
var s = new SAX()
var tags = []
s.on('text', function(text) {
  if (tags.length > 2 && tags[1] == 'Location') {
    switch (tags[2]) {
      case 'latitude': result.latitude = text; break;
      case 'longitude': result.longitude = text; break;
    }
  }
})
s.on('opentag', function(tag) { tags.push(tag); })
s.on('closetag', function(tag) { tags.pop(); })
s.write(xml)

Imaging

The backend provides functions for generating PNG images. The images are created by allocating a new PNG object: var png = new PNG().

With a PNG object the following methods are possible:

  • png.size(width, height): Set the size of the image.
  • png.donut(inner, outer, values, space): Draws a donut image. The donut is drawn in the center of the PNG image. The inner argument determines the radius of the inner circle, and the outer argument the size of the outer circle. The space defines how much space is kept between the donut segments, if present. The values are an array of segments. Each segment consists of another array where the first element is the radius and the second element is the color for the segment.
  • png.fill(x, y, width, height, color): This function will fill the image with a box starting at (x, y) and with the width, height and color provided as the argument.
  • png.graph(x, y, width, height, values, linecolor, fillcolor1, fillcolor2, linewidth): This function is used to draw a bar chart. The values for the graph are in the values array. The colors for the graph are in the linecolor, fillcolor1, fillcolor2 parameters. The system will draw a gradient if fillcolor1 and fillcolor2 different.
  • png.insert(data, x, y, sx, sy): This function is used to insert an image into the PNG image. For example it can be used to prepare an image in another PNG object and then insert that image into another image. The image can be scaled on the x- and y-axis by integer factors (default is 1).
  • png.color(red, green, blue, alpha): This function returns a color. If there is only one argument in the range 0 to 255, the resulting color is a grey color. If there are three arguments in the range 0 to 255 each, the result will be a color with the red, green and blue components. And if there are four arguments, the result will be a color consisting of the three colors and the transparency (0 being fully transparent and 255 being fully opaque).
  • png.write("12.123", x, y, color, horizontal, vertical, font): This function is used to write text into the image. The character set is limited to numbers at this point. The horizontal alignment can be left (default), center or right. The vertical alignment can be top (default), middle or bottom
  • png.serialize(): This function returns the binary presentation of the image as UInt8Array.

SIP

  • system.parseSipContact(contact): This function parses a SIP contact and returns an object with the following keys:
    • name: This key contains the display name part of the contact.
    • uri: The URI of the contact as string.
    • content: This key contains the contact string excluding the headers.
    • tag: If there was a tag, this key contains the tag.
    • header: An array with the headers for the contact except the tag header in the form [name, value].
  • system.parseSipUri(uri): This function parses a SIP URI and returns an object with the following keys:
    • original: The original input to the function.
    • scheme: The scheme for the URI, e.g. "sip".
    • user: The user part of the URI.
    • pass: The password part of the URI.
    • host: The host part of the URI (excluding the port)
    • port: The port part of the URI.
    • headers: An object that contains the headers of the URI.
    • parameters: An object that contains the parameters of the URI.
  • system.parseSipRoute(array, route): This function parses a SIP route header and adds them to the array of route elements as strings.

Was this article helpful?