Backend JavaScript
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 istrue
.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 keysname
andvalue
. If the keysecret
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 responseheaders
: The headers of the response. This is an array of objects with key namesname
andvalue
.
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 namedname
andadr
.user
: The user context for the email (optional).to
: The recipient address, optional. The address is an object with two keys namedname
andadr
. If theto
key is not present, the system will send the email to theuser
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
andbody
: 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 keysname
andvalue
.
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 examplesystem.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 typebool
,int
andstring
. 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 optionaltz
contains the name of the timezone to use. Iftime
is present it contains the number of seconds since 1970 to be used. Iftime
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. 2022month
: 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 anditem
the name of the item. For examplesystem.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 examplesystem.hasAudioFile("audio_moh/moh.wav")
should returntrue
. -
system.addAudioFile(name, content)
: Adds an audio file. -
system.version()
: This function returns information about the PBX version as object. It contains the keyversion
which contains the version number (e.g. "69.0"), 'arch' which contains the OS (e.g. "CentOS64") andbuild
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 addressadr
. -
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
: Eitherhttp
orhttps
.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 totrue
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
andsha256
. The digest can bebase64
orhex
. 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 onlyrsa-sha256
is available. The signature can be returned inbase64
(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 namecsv
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. Ifwrite
is true, the new row is committed immediately.remove(id)
: Remove the row with the idid
.get(id, column)
: Returns the content of the column with the namecolumn
in the row with the idid
. 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 namecolumn
in the row with the idid
with valuevalue
. Unless write isfalse
the value is committed immediately. Ifcolumn
is""
butwrite
istrue
, this will trigger the writing of the row.has(id)
: Returnstrue
if the row with the idid
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. Ifwrite
is true, the new row is committed immediately.remove(id)
: Remove the row with the idid
.get(id, column)
: Returns the content of the column with the namecolumn
in the row with the idid
.set(id, column, value, write)
: Write the content of the column with the namecolumn
in the row with the idid
with valuevalue
. Unless write isfalse
the value is committed immediately.has(id)
: Returnstrue
if the row with the idid
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 beleft
(default),center
orright
. The vertical alignment can betop
(default),middle
orbottom
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.