Pharos Scripting Language Primer
- Comments
- Variables and Namespaces
- Types and Constants
- Expressions and Operators
- User-Defined Functions
- Type Conversions
- Statements
- Namespace Function Interfaces
- Authentication (namespace "Auth")
- Database (namespace "DB")
- Input/Output (namespace "IO)
- List (namespace "List")
- Notify (namespace "Notify")
- Plug-ins (namespace "PlugIn)
- Print Jobs (namespace "PrintJob")
- Script (namespace "Script")
- String (namespace "String")
- User (namespace "User")
- Windows (namespace "Win32")
- Error Handling
- Handling of Unicode characters
A number of example scripts can be found on the Pharos disk image at \tools\plugins\scripts.
Comments
- Comments are annotations to script code that should help make it more readable
- C and C++ style comments are supported
- Comments are ignored by the script compiler, so they do not make the script run slower use them liberally
/* C style comments begin with a /* symbol, and can span multiple lines, ending with a */ symbol */ |
// C++ style comments begin with //, ending at the end of the line |
Variables and Namespaces
- A variable is a named entity that can contain a value
- A namespace is a named entity that can contain variables and functions
- Every variable exists in a namespace, which if not specified is the namespace local to the script
- Notice that the following statements are terminated by a semicolon ;, as are most statements
new a; |
// creates a new uninitialized variable, called a, in the local namespace |
a = PlugIn.Client; |
// sets a equal to the value of Client in namespace PlugIn |
Types and Constants
- A type describes the nature of a value, for example a bool type value can be only true or false
- A constant is a value written in the source code, for example 1.0 is a constant of type float
- The type of a variable is the type of its current value
- A variable can be assigned a value of any type at any time
new b = true; |
// bool type (true or false) |
new i = 1; |
// int type (32-bit signed integer) |
new f = 1.0; |
// float type (64-bit floating point number) |
new s = "1"; |
// string type (8-bit ASCII character with C escape codes e.g. "\r") |
new l = [false, 0, 0.1, "0", []] |
// list type (may contain any type) |
b = i; |
// now b has a value of type int |
b = true; |
// now b has a bool again |
Expressions and Operators
- An expression is a fragment of source code involving variables, constants and/or operators
- An operator is a symbol or word that operates on one expression, for a unary operator, or two expressions for a binary operator, e.g. =, + and and are binary operators, whereas not is a unary operator
- For all assignment operators except simple assignment, the second expression is converted to the type of the first
b = false; |
// assignment expression (simple) |
i += 1; |
// assignment expression (add) |
i -= 1; |
// assignment expression (subtract) |
i *= 2; |
// assignment expression (multiply) |
i /= 2; |
// assignment expression (divide) |
- Expressions referred to below are the code fragments between the = and the ;
- For binary operators the second expression is converted to the type of the first
s = "line\r\n"; |
// constant expression (string) |
s = i; |
// identifier expression (yields value of i) |
l = n.f(s); |
// function expression (in namespace n, passing parameter s) |
i = l[0]; |
// subscript expression (yields first element in list l) |
i = -i; |
// unary expression (minus) |
b = not b; |
// unary expression (not) |
f = f / i * 2; |
// multiplicative expression |
i = 3 mod 2; |
// multiplicative expression (remainder) |
i = i + (i 1); |
// additive expression (with parenthesized expression) |
b = i < 1; |
// relational expression (less-than) |
b = i <= 1; |
// relational expression (less-than-equal) |
b = i > 1; |
// relational expression (greater-than) |
b = i >= 1; |
// relational expression (greater-than-equal) |
b = i != 1; |
// equality expression (not-equal-to) |
b = i <> 1; |
// equality expression (not-equal-to) |
b = i == 1; |
// equality expression (equal-to) |
b = b and true; |
// logical expression (and) |
b = b or true; |
// logical expression (or) |
User- Defined Functions
Uniprint allows you to create your own functions when writing scripts. Functions are like small pieces of programs that can be defined anywhere in your script, and can be used anywhere after its definition. This gives you the ability to break down large scripts into smaller, reusable units. The following section provides you with the basic concepts of writing user-defined functions and examples to help you understand them.
You can find a number of sample scripts with user-defined functions on the Pharos disk at \tools\plugins\scripts. Sample scripts with user-defined functions include Logon - Authenticate and Translate Users.txt, Billing - Pharos Accounts Plus Online Accounts.txt, JobInformation - Notify User of Job Cost.txt.
Defining Functions
User-defined functions are created with the "function" keyword, the name of the function, and a list of parameters enclosed in parentheses (which may be empty). The body of the function is enclosed in curly braces.
function say_hello() {
} |
function IsAccountAvailable(name) { try {
}
}
|
A function can also take more than one parameter. In the following example, the function average has two parameters x and y. You can also use local variables, created in the usual way using "new".
function average(x, y) {
} |
Calling Functions
User-defined functions are called in the same way as built-in functions. To call a function, use the function name followed by a parentheses.
say_hello(); |
Returning Values
A function may optionally return a value using the "return" statement.The parameters and return value of a function may have any of the standard data types (boolean, integer, float, string, or list).
function average(x, y) {
} |
Example 1:
function say_hello() {
} function average(x, y) {
} say_hello(); // Prints "Hello world!" new a = 10; new b = 20; new c = average(a, b); // Sets c to 15 |
Inside the body of a function, you can use the function's parameters, as well as any global variables created outside the function. These exist only inside the function, and will disappear when the function returns. A local variable can have the same name as a global variable;the two variables will not interfere with each other.
Functions can call other functions, but a function may not call itself.
new a = 10; function times_a(x) {
}
function square(x) {
}
function add_squares(x, y) {
}
function factorial(n) {
} |
Type Conversions
- Type conversion occurs when a binary operator is used with expressions of two different types
- For binary operators (except simple assignment), the second expression is converted to the type of the first
The variables in the examples below have been assigned types as in the examples in the Types and Constants section above, i.e. b is a boolean value, i is an integer, f is a float, l is a list (of five values) and s is a string.
b = b or 0; |
// 0 converted to false |
b = b and 2; |
// 2 (or any non-zero integer or float) converted to true |
i = i + "2"; |
// "2" converted to 2 |
i = i + 2.6; |
// 2.6 converted to 2 by truncation |
i = i + l; |
// l is converted to 5 (length of l) |
f = f + true; |
// true converted to 1.0 |
f = f - "2.6"; |
// "2.6" converted to 2.6 |
s = "" + l; |
// l converted to "0, 0, 0.1, "0", []" |
Statements
- A script is made up of a sequence of any number of statements
- A statement is a fragment of source code that is syntactically complete and correct, and which will therefore compile successfully, e.g. a = 1 is an expression, whereas a = 1; is a complete expression statement
- All statements except if, else, while and compound statements are terminated by a semicolon ;
import "DB"; |
// import statement (makes DB namespace function calls available) |
new a; |
// new statement uninitialized (creates a) |
new b = false; |
// new statement with initializer (b is bool) |
a = 2; |
// expression statement |
f(a); |
// expression statement (value discarded) |
if (a < i) |
// if statement |
; |
// null statement (only do this if a < i) |
else |
// else statement |
a = f(a); |
// (otherwise do this when not (a < i)) |
while (a < i) |
// while statement |
{ |
// compound statement |
a = f(a); |
// (do this ...) |
b = f(b); |
// (and this while (a < i)) |
} |
// end compound statement |
try |
// try statement |
{ |
// try block compound statement |
a = f(a); |
// jump to catch block if error occurs |
throw |
// jump to catch block |
} |
// end try block |
catch |
// catch statement (required after a try block) |
{ |
// catch block compound statement (for error handling) |
exit |
// terminates the script early without error |
} |
// end catch block |
Namespace Function Interfaces
- Parameters to functions have a suffix which indicates the type expected, e.g. _int for an integer, _any for a value of any type
- Unexpected types will be type converted to the expected types
- Parameters with a suffix of _name expect a variable name of the type prefixed, eg string_name should be a string variable
- Following the function description will be the return value type, which may also be (any) for any type, or (none) for no return value
Authentication (namespace "Auth")
The Auth namespace is a simple way for scripts to verify a username and password against a third party directory, such as Active Directory or an LDAP server. More advanced functionality may require use of other tools instead of (or in addition to) these functions. In all functions, the default timeout value is 30 (seconds).
Depending on how your LDAP server is configured, you may need more than the submitted username to authenticate. Some LDAP servers will accept a simple username, while others will require more information to authenticate, such as Distinguished Name, or DN (e.g. CN=jsmith, OU=some_department, DC=mydomain, DC=com). If your LDAP server requires a DN to complete a bind, you will need to build this string from what the user types in. Consult your network administrator if necessary.
LdapAD(username_string, password_string, servername_string {, port_int{, timeout_int}}); // Verify the given username and password against Active Directory. // NOTE: LdapAD uses port 389 by default. |
LdapKerberos(username_string, password_string, servername_string {, port_int{, timeout_int}}); // Verify the given username and password against Active Directory, using Kerberos encryption. // |
LdapSsl(username_string, password_string, servername_string {, port_int{, timeout_int}}); // Verify the given username and password against an LDAP server using SSL to protect the connection. |
LdapPlainText(username_string, password_string, servername_string {, port_int{, timeout_int}}); // Verify the given username and password against an LDAP server without using any network encryption. |
The following shows a sample script using the "Auth" namespace
import "Auth"; PlugIn.Result = false; try {
} catch { PlugIn.Error = "The system was unable to verify your credentials at this time."; } |
Database (namespace "DB")
ColIndexFromName(column_string); // returns results column index (int) |
ColNameFromIndex(index_int); // returns results column name (string) |
ColumnByIndex(index_int); // returns results column value (any) |
ColumnByName(column_string); / returns results column value (any) |
GetNumColumns( ); // returns number of results columns (int) |
GetNumRows( ); // returns number of results rows (int) |
GetRow( ); // returns whether we got next results row (bool) As of Uniprint 7.2, you must check for available rows(i.e. making sure that DB.GetRow returns true) when calling DB.ColumnByName() or DB.ColumnByIndex(). In addition, you should always have a try/catch block around any DB.SQL() call. For example, you can have:
|
Online(); // returns whether or not the database is online, i.e. reachable from the |
ParametrizedSQL(sql_string{, parameter1{,parameter2...parameter n}}); // Creates a DB connection if none present, then executes
|
SQL(sql_string {,client_name_string}); // Creates a DB connection if none present, with a client name |
Pharos Systems recommends the use of DB.ParametrizedSQL() over DB.SQL(), to reduce the risk of SQL injection attacks.
Input/Output (namespace "IO")
DeleteFile(filename_string) // Deletes the file named filename_string and |
LoadFile(string_name, filename_string{, length_int}); // loads up to length_int |
PrintLine(line_string); // prints each line_string on a separate line (none) |
SaveFile(string_name, filename_string{, length_int}); // saves up to length_int (or all, if omitted) characters from |
List (namespace "List")
Concat(list_name, 0_list{, 1_list ...}); // concatenates 0_list etc to list variable list_name (no return value) |
Delete(list_name, pos_int); // deletes element number pos_int from list variable list_name, where |
Insert(list_name, pos_int, value_any); // inserts value_any into list variable list_name before element number |
Length(0_list{, 1_list ...}); // returns the combined length of 0_list etc (returns an int value) |
Notify (namespace "Notify")
BalloonBox(client_string, title_string, message_string, timeout_int{, style_int}) // Displays a system tray balloon message at the user workstation specified
by client_string |
MessageBox(client_string, message_string, timeout_int{, buttons_int}) //
sends message_string (with buttons_int) to a user's notify application
on machine client_string
|
QuestionBoxFloat(client_string, prompt_string, description_string, timeout_int, // displays a question box with prompt prompt_string, |
QuestionBoxInt(client_string, prompt_string, description_string, timeout_int, // displays a question box with prompt prompt_string, |
QuestionBoxList(client_string, prompt_string, description_string, timeout_int,
|
QuestionBoxMoney(client_string, prompt_string, description_string, timeout_int, // displays a question box with prompt prompt_string, |
QuestionBoxString(client_string, prompt_string, description_string,
timeout_int, // displays a question box with prompt prompt_string, |
Plug-ins (namespace "PlugIn")
See Plug-in Interfaces for information.
Print Jobs (namespace "PrintJob")
As of 8.3, the queue_name parameter is ignored.
Script (namespace "Script");
Defined(namespace_string); // Returns whether namespace_string is defined (returns a bool value) |
GetChildren(namespace_string); // Returns a list of the names of namespace children of namespace_string (returns a list value) |
GetParent(namespace_string); // Returns the name of the parent namespace of namespace_string (returns a string value) |
GetType(namespace_string); // Returns the type name of namespace_string (e.g. "bool", "string") (returns a string) |
GetErrorMessage(); // Returns the error message of the most recent exception (returns a string value) |
GetErrorType(); // Returns the error type of the most recent exception (e.g. "Script", "PSCom", "PS", "MFC", "General exception") (returns a string value) |
GetLineNumber(); // Returns the current execution line number (returns an int value) |
SetErrorMessage(error_message_string); // Sets the exception error message to error_message_string (no return value) |
SetErrorType(error_type_string); // Sets the exception error type to error_type_string (no return value) |
String (namespace "String")
Concat(string_name, 0_string{, 1_string ...}); // concatenates 0_string etc to string variable string_name (no return value) |
Delete(string_name, pos_int{, length_int}); // deletes length_int (or all if omitted) characters from string |
Encrypt(string_name); // encrypts a string using the same algorithm as the Database Server |
Find(range_string, target_string); // returns the zero-based index of the first occurrence of |
FindLast(range_string, target_string); // returns the zero-based index of the last occurrence of |
Insert(string_name, pos_int, insert_string); // inserts string insert_string into string variable string_name |
IsEmpty(test_string); // returns true if test_string is empty (""), false otherwise (returns a bool value) |
Left(string_name, num_int); // truncates string variable string_name to leave only its leftmost |
Length(0_string{, 1_string ...}); // returns the combined length of 0_string etc (returns an int value) |
LowerCase(string_name); // converts all characters in string variable string_name to lower |
Mid(string_name, pos_int, num_int); // truncates string variable string_name to leave only the num_int (or |
Right(string_name, num_int); // truncates string variable string_name to leave only its rightmost |
TrimLeft(string_name); // removes all whitespace (newline, space, tab) characters from the |
TrimRight(string_name); // removes all whitespace (newline, space, tab) characters from the |
UpperCase(string_name); // converts all characters in string variable string_name to uppercase (no return value) |
User (namespace "User")
To use the User namespace, create a new user with the User.Insert() function, or load an existing one with any of the User.GetUser*() functions. If no exception is thrown, the corresponding user record will then be available for use. Once a user has been loaded, all subsequent function calls (e.g. User.GetBalance() or User.SetProperty()) will apply to the currently loaded user. The same user record remains loaded until another call is made to the User.Insert() function or one of the User.GetUser*() functions.
Credit(amount_money, cashier_string {, purse_int{, record_transaction_int}}); // credits the user's balance by the specified amount |
Debit(amount_money, cashier_string {, purse_int{, record_transaction_int}}); // debits the user's balance by the specified amount |
GetBalance(purse_int); // returns the current balance of the specified purse (1, 2 or 3) |
GetProperty(property_string); // returns the value of the specified property. Possible properties are: |
GetUser(searchvalue_string {, column_string}); // loads a user record by the specified column from the users table |
GetUserByAlias(alias_string); // loads a user record by their alias |
GetUserByCardID(cardid_string); // loads a user record by their card ID |
GetUserByID(internalid_int); // loads a user record by their internal ID number |
GetUserByLogon(name_string); // loads a user record by their user name |
Insert(username_string {, firstname_string{, lastname_string}}); // creates a User record with the specified username |
SetProperty(property_string, value); // sets the value of the specified property. The type of the value depends
on the property. |
PasswordVerify(password_string); // authenticates the current job owner with the supplied password |
UserPasswordVerify(user_string, password_string); // authenticates the supplied user and password |
Windows (namespace "Win32")
ExecProcess(command_string {, timeout_int {, output_string}}); // execute command_string as another process, waiting for timeout_int |
GetJob(queue_string, id_int, data_list); // gets system information on job id_int in queue queue_string (list) TotalPages and PagesPrinted return the same value, which is the number of pages found by the Page Counter. As of 8.3, the GetJob function no longer supports retrieving values for Data Type, PrintProcessor, Parameters, DriverName, Priority and Position because it is not applicable to the new Secure Release Service. |
GetTempFileName({prefix_string {,pathname_string}}); // Returns a temporary filename prefixed by prefix_string (or prefix "Pha"
if omitted) |
GetTempPathName( ); // Returns the pathname of the Windows directory for temporary files (returns a string value) |
NetSend(user_string, message_string); // sends a network message message_string to user user_string (no return value) |
RegQueryValue(subkey_string, value_name_string {,key_int {,access_int}}) // Opens registry key subkey_string under root key key_int (or HKEY_LOCAL_MACHINE |
SearchPath(filename_string); // Searches for file filename_string and returns its 'short' path, without
spaces |
SetJob(queue_string, id_int, data_list); // sets system information for job id_int in queue queue_string As of 8.3, the SetJob function can now set the user and job name values. Values not supported by Win32.GetJob(..) cannot be set using Win32.SetJob(..). Win32.SetJob (..) will return an error instead of throwing an exception for an invalid field. This causes the scripts to continue working instead of stopping (due to an exception). |
SetJobCommand(queue_string, id_int, command_int); // Performs operation command_int (e.g. JOB_CONTROL_CANCEL, which is 3
- see Beginning with 8.3, this function will do nothing and always return true. |
Sleep(milliseconds_int); // Forces the script thread to sleep for milliseconds_int |
Error Handling
A script can respond to errors at run-time with the try-catch block statement. When an error occurs, or a throw statement is encountered inside a try block, the subsequent catch block is executed. Information about the nature of the error can be obtained by calling the following Script namespace functions in the catch block:
new error_message = Script.GetErrorMessage();
new error_type = Script.GetErrorType();
Script errors occur in several situations:
- run-time script errors (e.g. use of an undeclared variable) will have type "Script".
- communications errors (e.g. via the DB or Notify namespaces) will have type "PSCom".
- internal Pharos errors may have type "PS", "MFC" or "General exception".
Throw statements may provide error information by first calling:
Script.SetErrorMessage("My error message");
Script.SetErrorType("My error type");
Handling of Unicode characters
The script compiler that ships with Uniprint has been updated to support Unicode parameters and return values, as well as Unicode script contents (i.e. string literals, comments and so on).
All the standard scripts have also been updated to support Unicode. You can find these scripts on the Pharos disk image at \tools\plugins\scripts.
Pharos Components Interaction with Scripts
Pharos Database
When a script interacts with the Pharos database, Unicode is fully supported. If a script uses the function DB.ParametrizedSQL, parameters containing Unicode characters are handled correctly. Some existing scripts may contain SQL code that explicitly refers to the SQL type "varchar"; this need to be replaced by "nvarchar" if Unicode support is required.
You will also need to update scripts that query the database to support querying with Unicode values. Any SQL query that might include Unicode strings needs to be updated to include the "N" prefix. The following examples show how to create queries with Unicode values.
Examples:
This query will continue to work as expected:
select * from users where id = 'string'
To support Unicode characters, the prefix "N" should be added before the Unicode string:
select * from users where id = N'string'
Popup Client and Notify
While the scripts support Unicode internally, they sometimes interact with components of the system that do not support Unicode. For example, the Popup Client does not support Unicode characters in a print job's Username or Job Name, whether or not those were entered by the user in response to Popup Questions; in such cases, a script will receive a non-Unicode parameter. Similarly, in practice the functions in the Notify namespace support Unicode only to the extent that Pharos Notify itself does so.
Variable names within the script must contain only ASCII alphanumeric characters.