ObjectScript Server Pages
ObjectScript Server Pages
(CSP)
Version 2018.1
2022-10-20
InterSystems®, InterSystems IRIS®, IntegratedML®, InterSystems HealthShare®, InterSystems HealthShare Care Community®, HealthShare
Unified Care Record®, InterSystems Caché®, and InterSystems Ensemble® are registered trademarks of InterSystems
Corporation.InterSystems IRIS for Health™ and HealthShare® CMS Solution Pack™ are trademarks of InterSystems Corporation.
All other brand or product names used herein are trademarks or registered trademarks of their respective companies or organizations.
This document contains trade secret and confidential information which is the property of InterSystems Corporation, One Memorial Drive,
Cambridge, MA 02142, or its affiliates, and is furnished for the sole purpose of the operation and maintenance of the products of InterSystems
Corporation. No part of this publication is to be used for any other purpose, and this publication is not to be reproduced, copied, disclosed,
transmitted, stored in a retrieval system or translated into any human or computer language, in any form, by any means, in whole or in part,
without the express prior written consent of InterSystems Corporation.
The copying, use and disposition of this document and the software programs described herein is prohibited except to the limited extent
set forth in the standard software license agreement(s) of InterSystems Corporation covering such programs and related documentation.
InterSystems Corporation makes no representations and warranties concerning such software programs other than those set forth in such
standard software license agreement(s). In addition, the liability of InterSystems Corporation for any losses or damages relating to or arising
out of the use of such software programs is limited in the manner set forth in such standard software license agreement(s).
THE FOREGOING IS A GENERAL SUMMARY OF THE RESTRICTIONS AND LIMITATIONS IMPOSED BY INTERSYSTEMS
CORPORATION ON THE USE OF, AND LIABILITY ARISING FROM, ITS COMPUTER SOFTWARE. FOR COMPLETE INFORMATION
REFERENCE SHOULD BE MADE TO THE STANDARD SOFTWARE LICENSE AGREEMENT(S) OF INTERSYSTEMS CORPORATION,
COPIES OF WHICH WILL BE MADE AVAILABLE UPON REQUEST.
InterSystems Corporation disclaims responsibility for errors which may appear in this document, and it reserves the right, in its sole discretion
and without notice, to make substitutions and modifications in the products and practices described in this document.
Appendixes are:
• CSP Error Notes
• Localization and Tag-Based Development
• Frequently Asked Questions About CSP
CSP is well-suited for database applications. In addition to providing rapid access to the built-in Caché database, it provides
a number of features essential for Web-based database applications including
• Session management
• Page authentication
• Ability to perform interactive database operations from in a web page.
Note: To run Zen-based applications, it is necessary that you enable the Serve Files option and properly configure your
web server. See the section Static Files in this book for more information.
After installing the CSP Gateway, consult the CSP Gateway Configuration Guide to map file extensions for your system.
Note: To prevent runtime errors, for High Availability configurations running over CSP, InterSystems recommends that
you use a hardware load balancer with sticky session support enabled.
http://localhost/csp/bin/Systems/Module.cxw
• CSP Gateway Configuration Guide, when you install Caché, the CSP Gateway is installed automatically and works
for most sites. If you need to configure the CSP Gateway manually, use the advanced configuration guide.
Class Definition
Class Test.Hello Extends %CSP.Page [ ProcedureBlock ]
{
ClassMethod OnPage() As %Status
{
&html<<html>
<head>
</head>
<body>>
;To do...
&html<</body>
</html>>
Quit $$$OK
}
}
; To do...
ObjectScript
Write "<b>Hello, World</b>",!
8. Save and compile the new class with Build > Compile.
9. Select View > Web Page
2. The web server passes this request to the CSP Gateway which passes the request to a Caché CSP server. In our case,
the browser, the web server, and the Caché Application server are all running on the same machine. In a real deployment,
these would probably be on separate machines.
3. The CSP server looks for a class called Test.Hello and invokes its OnPage method.
4. Any output that the OnPage method writes to the principal device (using the Write command), is sent back to the
browser (via the CSP Gateway and the web server).
These example shows the heart of CSP; the rest of CSP's functionality is built on top of this behavior.
The following is an example of adding more code. Insert the following lines after the line containing Hello, World:
ObjectScript
Write "<ul>",!
For i = 1:1:10 {
Write "<LI> This is item ", i,!
}
Write "</ul>",!
Now your page contains an unordered (bulletted) list of 10 items. Note that, in this context, Caché uses the exclamation
point (!) character to write a carriage return to the principal device.
CSP
<html>
<body>
<b>Hello, World!</b>
</body>
</html>
As with the previous example, you see Hello, World! displayed in the browser.
A CSP application can consist of a single CSP page or a set of pages. A CSP application acts as a unit, using settings that
apply to the whole application. The system provides csp/user as the default CSP application. For more information on CSP
applications, see the section “CSP Application Settings” in this book.
You can also create an HTML file using a text editor or HTML editor. Save this file as Hello.csp in the local directory
cachesys/csp/user (where cachesys is where you installed Caché).
The Hello.csp page works as follows:
1. The browser sends a request for Hello.csp to the local web server
2. The web server passes this request to the CSP Gateway (connected to the web server) which, in turn, passes the request
to a Caché CSP server.
3. The Caché CSP server looks for the file Hello.csp, and hands it to the CSP compiler.
4. The CSP compiler creates a new class called csp.Hello with an OnPage method that writes out the contents of the
Hello.csp file. (It actually generates a set of methods each of which are, in turn, called from the OnPage method). This
compilation step only occurs when the .csp file is newer than the generated class; subsequent requests are sent directly
to the generated class.
5. The CSP server invokes the newly generated OnPage method and its output is sent to the browser as in the previous
example.
As with the case of programmatic development, this is a purposefully oversimplified example included for pedagogical
reasons. The CSP compiler is actually a specialized XML/HTML processing engine that can:
• Process server-side scripts and expressions in an HTML page
• Perform server-side actions when certain HTML tags are recognized.
As with the programmatic example, you can make this page more interesting by adding programming logic. For example:
CSP
<html>
<body>
<b>Hello, World!</b>
<script language="Cache" runat="server">
// this code is executed on the server
Write "<ul>",!
For i = 1:1:10 {
Write "<li> This is item ", i,!
}
Write "</ul>",!
</script>
</body>
</html>
As with the programmatic example, the resulting page displays an unordered (bulletted) list of 10 items.
For more information on installing and configuring the CSP Gateway with your web server, reference the CSP Gateway
Configuration Guide.
The web server and the CSP server may be implemented by one or many computers. During development, all three compo-
nents (web server, CSP Gateway, and CSP server) may be on a single PC. In a large scale deployment, there may be mul-
tiple web servers and CSP servers in two- or three-tier configurations.
This book treats these components (web server, CSP Gateway, CSP server) as though there were one of each. It describes
CSP as though it were only serving HTML pages, although CSP can also serve XML pages, as well as other text formats
and binary formats, such as images.
The CSP Gateway is a shared library, a .dll file, or a CGI script. It does the following:
• Determines which Caché server to send a request to.
• Sends requests to the correct Caché server.
• Maintains connections to the Caché server (to avoid having to continually open new connections).
The CSP server is a process running on a Caché server that is dedicated to serving requests from the CSP Gateway. It does
the following:
• Receives an HTTP request for an application
• Checks the Application Configuration Settings (set in the Management Portal and saved in the .cpf file).
• Runs the associated class (either written directly or generated from a CSP page) which sends HTML back to the CSP
Gateway which sends it to the web server and back to the browser.
content. To run Zen applications on Caché, you must configure your web server to allow the Caché server to serve all static
files via the CSP Gateway.
Note: To run Zen-based applications, enable the Serve Files option and configure your web server to allow static files
to be served by the Caché server. Then the Zen framework will be able to deliver dependent images and JavaScript
helper documents.
If you need to override this behavior to specify a character set for JavaScript files, set the global
^%SYS("CSP","MimeFileClassify","JS") to the list value $listbuild(contenttype, binary, charset). For example,
SET ^%SYS("CSP", "MimeFileClassify", "JS") = $listbuild("text/javascript", 0 ,"ISO-8859-1")
This sets the older content-type and uses the ISO-8859-1 character set. Also, if the Caché translate table is defined to be
something other than an empty string or if the global node ^%SYS("CSP","DefaultFileCharset") is set to a null value, Caché
will use this character set for all JavaScript and other text files. By default, neither of these global nodes are set.
Configuring the Web Server to Allow Static Files to be Served by the Caché Server
Configure the web server to allow static files to be served by the Caché server.
By default, the InterSystems-provided installation script does not look for, or configure an external web server. If you run
a Custom Install, you can choose the option to configure any previously-installed IIS or Apache web server to enable CSP
support. The installation script creates the /csp virtual directory and creates mappings for the .csp, .cls, .zen and .cxw
extensions to be handled by the gateway. This is sufficient for CSP to function normally via that web server. It does not
enable support for the Serve Files feature. To make this happen, you must manually configure Apache or IIS to map specific
file extensions to be handled via the Gateway. This is by design because opening the database server up to exposure via
this mechanism is a security risk and should not be done invisibly.
See the section “Registering Additional File Types with CSP” in the CSP Gateway Configuration Guide for instructions
for your web server.
If you have configured the web server itself to serve static files, be sure that the static content is present on every single
web server.
Microsoft IIS Define a virtual directory as described in “Configuring a New URL on a Microsoft IIS web
server”.
The following table shows examples of accessing a CSP page using combinations of web servers and Caché instances:
Note: CSP is case-sensitive. Specify your path names consistently when you are configuring CSP.
Any request (URL) for CSP content includes a directory name. This directory name must correspond to either a virtual
directory defined by the web server or a subdirectory of a virtual directory. This virtual directory must have at least read
and execute privileges defined in order for CSP content to be served.
If you opt to serve static files from the web server, the web server looks for static files (such as .html or .jpg) in the physical
directory defined for the virtual directory. Neither the web server nor the Caché server looks for .csp files in the physical
directory; .csp files are stored in the machine on which the Caché server is running. If both the web server and Caché are
running on the same machine (as is recommended during application development, for example) then both may, coincidentally,
look in the same location for static and .csp files — and this is how Caché configures itself and the local web server during
installation.
During installation, Caché detects if an IIS server is running and attempts to configure it to define a virtual directory named
/csp. This is how requests to both /csp/samples and /csp/user (which are subdirectories of /csp ) are both sent to the local
Caché installation.
If you add a new CSP application, you do not have to perform any IIS configuration if the URL path for the new application
also starts with /csp. For example, /csp/myapp uses the IIS virtual directory defined for /csp. If you do not want your
application path to start with /csp, then you need to define a new virtual directory for IIS that corresponds to your application
path.
For example, to define a CSP application that uses the URL path /myapp, do the following:
1. Open the IIS manager (note: every version of Windows has a unique way to do this; typically this is available via the
Windows Control Panel).
2. Define a virtual directory called /myapp by right-clicking Default Web Site and selecting New > Virtual Directory.
(For an example of how to do this, see the “Add Virtual Directories in IIS ” section in the CSP Gateway Configuration
Guide).
3. Grant read and execute permissions for this directory.
4. If you want the web server to serve static content, specify the physical directory in which you plan to store static content.
You also have to perform additional CSP Gateway and CSP server configuration as described in the following sections.
For information on configuring the CSP Gateway, navigate to System Administration > Configuration > CSP Gateway
Management and click Help. For more detailed information, see CSP Gateway Configuration Guide.
Note: Localization of the CSP Web Gateway Management pages is based solely on the contents of the CSPres.xml
installed (if any). If no localization file is present then the CSP Web Gateway Management pages default to using
the embedded English text. The language settings of the browser have no influence on this mechanism.
You can define a list of servers (Caché or Ensemble servers that might run CSP applications) that this CSP Gateway can
access. Each server has a logical name, a TCP/IP address, a TCP/IP port number (the default is 1972), and an enabled or
disabled flag. In addition, you can configure the minimum and maximum number of connections made to this server as
well as timeout and logging values.
Since each server has a logical name, the CSP Gateway makes it easy to connect applications to particular servers by name
and later change the characteristics of a server in one place without having to reconfigure every application using the server.
After the initial installation, the CSP Gateway has one logical server defined, LOCAL, which is defined to connect to the
local copy of Caché.
To add one or more servers that you want the CSP Gateway to be able to access, open the CSP Gateway Manager as
described in the previous section and click Server Access. See the section “Accessing CSP on Multiple Caché Servers”
in the CSP Gateway Configuration Guide for details of the procedure.
Example of the default LOCAL server in the CSP.ini file:
LOCAL=Enabled
...
[LOCAL]
Ip_Address=127.0.0.1
TCP_Port=1972
Minimum_Server_Connections=3
A CSP application is a set of pages or classes that are addressed using a given URL. For example, all the CSP sample pages
are part of the /csp/samples application. An application may contain subdirectories, for example /csp/samples/cinema.
The CSP Gateway Manager lets you define the URL path that a CSP application uses to connect to a Caché server. CSP
considers all files within a particular URL directory (or subdirectory thereof) to be part of the same application.
By default, the CSP Gateway defines a single application path, /csp, which sends all CSP requests to the logical server
LOCAL. Requests to /csp/samples and /csp/user are both sent to the local Caché installation.
If you create a new CSP application with a URL that starts with /csp, you do not have to change the CSP Gateway config-
uration. Your new application, such as /csp/myapp, uses the CSP application settings defined for /csp. If you do not want
your URL path to start with /csp, then you need to define a new CSP application within the CSP Gateway that corresponds
to your URL path.
For example, to define a CSP application that begins with the URL path /myapp, do the following:
1. Open the CSP Gateway Manager by navigating to System Administration > Configuration > CSP Gateway Management
in the Management Portal.
2. Select Application Access.
3. Click Add Application.
4. Enter /myapp into the Application Path field.
5. Select the Default Server where the application is from the list (these are defined in the “Defining Server Access”
section).
6. Click Submit to save the /myapp application access configuration.
For details on the other fields on the Application Access page, click Help.
• %ZEN.SVGComponent.svgPage and %ZEN.Dialog.* are allowed, with the following additional conditions
Checking for allowed classes is performed in addition to checking the setting in the CSP application. You can view and
change the application settings by navigating to System Administration > Security > Applications > Web Applications on
the Management Portal. So a class reference must pass both sets of tests in order to be allowed. (See in the Permitted Classes
setting in the table in the section “ Editing Web Application Settings ” in this book.)
To permit access to additional classes, configure the global ^SYS("Security","CSP","category") in the %SYS
namespace, where category is AllowClass, AllowPrefix, or AllowPercent. The following sections describe these
options.
Important: Checking is done by applying the default rules first, then the categories in the order listed.
Also, each keyword can be invoked more than once. This means that you can make an entire package
accessible, and then restrict access to one class in that package.
zw ^SYS("Security", "CSP")
The system then displays one line for each node, showing its current value. For example:
^SYS("Security","CSP")=1
^SYS("Security","CSP","AllowClass","/csp/samples/","%CSP.UI.Portal.About")=1
^SYS("Security","CSP","AllowClass","/csp/samples/","%SOAP.WebServiceInfo")=1
^SYS("Security","CSP","AllowClass","/csp/samples/","%SOAP.WebServiceInvoke")=1
^SYS("Security","CSP","AllowPrefix","/csp/samples/","%DeepSee.")=1
Important: If your application relies on invoking any class other than those listed as allowed at the beginning of the
“ Enabling Application Access to %CSP Pages” section, it could potentially be unsafe to use. InterSystems
recommends that you determine if calling this class is required, and perform a risk assessment for your
deployment, so that you understand the implications of making the class available.
To enable a given web application to invoke a particular class, use the following command in the %SYS namespace:
Set ^SYS("Security", "CSP", "AllowClass", "web-app-name", "package.class") = value
Where:
• web-app-name is the name of the web application, followed by a trailing slash.
To enable all web applications to use the given class or package, specify web-app-name as 0; in this case, you can
omit the enclosing quotes.
• package.class is the fully qualified name of a class. If you omit class, then all classes in the specified package are
allowed.
• value is either 1 or 0.
If you specify this as 1, the web application can invoke this class (or package).
If you specify this as 0, this web application cannot invoke this class (or package).
For example, to enable the /csp/webapps application to use the class %User.Page, you would use the following command:
Or to enable all web applications to use the %User.Page, you would use the following command:
Set ^SYS("Security", "CSP", "AllowClass", 0, "%User.Page") = 1
For another example, to enable the /csp/myapp application to use all classes in the %User package except for the %User.Other
class, you would use the following two commands:
Set ^SYS("Security", "CSP", "AllowClass", "/csp/myapp/", "%User") = 1
Set ^SYS("Security", "CSP", "AllowClass", "/csp/myapp/", "%User.Other") = 0
Important: If your application relies on invoking any class other than those listed above, it could potentially be unsafe
to use. InterSystems recommends that you determine if calling this class is required, and perform a risk
assessment for your deployment, so that you understand the implications of making the class available.
To enable a given web application to invoke classes or packages that begin with the same set of characters, use the following
command in the %SYS namespace:
Set ^SYS("Security", "CSP", "AllowPrefix", "web-app-name", "prefix") = value
Where:
• web-app-name is the name of the web application, followed by a trailing slash.
To enable all web applications to use the given classes or packages, specify web-app-name as 0; in this case, you can
omit the enclosing quotes.
• prefix is the first characters in the name.
• value is either 1 or 0.
If you specify this as 1, the web application can invoke these classes (or packages).
If you specify this as 0, this web application cannot invoke these classes (or packages).
For example, to enable the /csp/webapps application to invoke the entire MyApp package, use the following command:
Set ^SYS("Security", "CSP", "AllowPrefix", "/csp/webapps/", "MyApp.") = 1
Note that prefix is "MyApp." and the period in the prefix means that the web application cannot access the packages such
as MyAppUtils. The web application can, however, access the packages MyApp.Utils and MyApp.UnitTests.
For another example, to enable all applications to access all packages that begin with My, use the following command:
Set ^SYS("Security", "CSP", "AllowPrefix", 0, "My") = 1
For another example, suppose that the /csp/myapp application should be able to access all classes in the %MyPkg package
except for the class %MyPkg.Class1. In that case you would use the following two commands:
Set ^SYS("Security", "CSP", "AllowClass", "/csp/myapp/", "%MyPkg.Class1") = 0
Set ^SYS("Security", "CSP", "AllowPrefix", "/csp/myapp/", "%MyPkg.") = 1
Important: If your application relies on invoking any class other than those listed above, it could potentially be unsafe
to use. InterSystems recommends that you determine if calling this class is required, and perform a risk
assessment for your deployment, so that you understand the implications of making the class available.
To enable all web applications to use all packages that begin with the % character, use the following command in the %SYS
namespace:
Set ^SYS("Security", "CSP", "AllowPercent") = 1
Note: Or use the value 0 to explicitly forbid any web application from accessing these packages.
Where web-app-name is the web application’s name with a trailing slash. The previous command is equivalent to the fol-
lowing commands:
Set ^SYS("Security","CSP","AllowClass","web-app-name","%DeepSee.") = 1
Set ^SYS("Security","CSP","AllowClass","web-app-name","%CSP.UI.Portal.About")=1
Where web-app-name is the web application’s name with a trailing clash. Note that the first line uses %DeepSee. with a
trailing period.
Or to enable all applications to use DeepSee, use the following variation:
Do EnableDeepSee^%SYS.cspServer(0)
For example, to enable the /csp/webapp web application to use DeepSee, use the following command:
Do EnableDeepSee^%SYS.cspServer("/csp/webapp/")
To disallow a specific web application from using DeepSee, use the following command:
Set ^SYS("Security", "CSP", "AllowPrefix", "web-app-name", "%DeepSee.") = 0
The General tab holds fields that specify information needed for basic operation of the application. See “Editing an
Application” in the Caché Security Administration Guide for more information on these fields.
The Application Roles tab lets you select roles to which to assign the user during use of the application. The Application
Roles that you select here are added to the set of roles to which the user is already assigned.
The Matching Roles tab lets you assign the application user to additional roles during use of the application, based on current
role assignments.
For more on roles and custom login pages, see “Applications ” in the book Caché Security Administration Guide.
4. Click Save.
5. Click the Application Roles tab to select roles to assign the user to during use of the application. These Application
Roles are added to the set of roles the user is already assigned to.
6. Click the Matching Roles tab to assign the application user to additional roles during use of the application, based on
current role assignments.
• The Caché server (on which the CSP server runs the requested CSP application)
Note: One of the key strengths of Caché is that there is no real difference between an application server and a data
server; you can configure your application to use as many, or as few, machines as necessary based on your
requirements. This is done independently of application logic and database schema. Whether a particular system
is an application server or a data server (or both) is simply a matter of configuration.
Component Purpose
http:// protocol
localhost web server address
[<port_no>] optionally, the port number that the web server is running on; defaults to port 80
/csp/samples/ directory
object.csp file name and extension
?OBJID=2 query
The protocol and server address are handled by the web server and are not relevant to the CSP server. The directory is used
to determine which CSP application the URL refers to. Every Caché configuration can define a number of CSP applications,
which are identified by the directory portion of a URL. Each CSP application specifies a number of settings used by all
requests with a given URL directory. The most important of these are the Caché Namespace in which the request is to be
executed and the authentication mechanism, which specifies what kind of connections to the application can be established.
To create and modify CSP applications, navigate to System Administration > Security > Applications > Web Applications
on the Management Portal.
The name of the class to handle the request is determined from the file name using the following algorithm, based on the
CSP application:
• If the file extension is .cls, then use the file name as a class name.
• If the file extension is .csp, then create a class name using csp (or the package name specified in the configuration)
as a package name and the file name as the class name. If this class does not exist, or is out of date, then the CSP
compiler creates a class from a CSP source file (if autocompile is on). This file has the same name and extension
as that in the URL.
to a class called menu contained within the package csp running in the Caché namespace associated with the directory
/csp/samples (in this case the SAMPLES namespace).
If the URL directory /csp/accounting is associated with the Caché namespace ACCOUNTING, then the CSP server dispatches
this URL:
http://localhost:57772/csp/accounting/Ledger.csp
to a class called ledger contained within the package csp running in the Caché namespace ACCOUNTING.
Note that URL file names used with CSP have a number of restrictions:
• They must be valid Caché class names (they cannot contain white space or punctuation characters (except for dots and
percent characters (%25)) and cannot start with numeric characters).
• They must not conflict with other class names already in use.
Note: If a .csp file is placed within a subdirectory of a defined directory, then the subdirectory name becomes part of
the package name for the %CSP.Page class used for the page. For example, if the URL directory /csp/samples is
defined as a CSP application, then /csp/samples/myapp/page.csp refers to a class named csp.myapp.page.
Class Definition
Class MyApp.Page Extends %CSP.Page
{
ClassMethod OnPage() As %Status
{
Write "<html>",!
Write "<body>",!
Write "My page",!
Write "</body>",!
Write "</html>",!
Quit $$$OK
}
}
The OnPostHTTP method is provided as a place to perform any operations you wish to perform after processing of the
HTTP request is complete.
If you are creating pages using .csp files, you can provide values for these parameters using the csp:class tag:
CSP
<csp:class PRIVATE="1">
Resource[:Permission]
Resource is any of the resources set on this system. Navigate to System Administration > Security > Resources for a list of
resources.
Permission is one of USE, READ, or WRITE. Optional; default is USE.
Example
R1,R2|R3,R3|R4
This example means the user must have resource R1 AND one of (R2 OR R3) AND one of (R3 OR R4). If the user has
R1,R3 they can run the page. If the user has R1,R4, they cannot run the page, as they do not meet the R2 OR R3 condition.
The vertical bar (|) OR condition takes precedence over the comma (,) AND condition.
LICENSEERRORPAGE
If the following error is generated, CSP looks at the value of the LICENSEERRORPAGE parameter:
"" — Returns the HTTP/1.1 404 Page Not Found error (default)
Path to a static HTML page — Displays the named static page, such as /csp/samples/static.html.
PAGENOTFOUNDERRORPAGE
If any of the following errors are generated, CSP looks at the value of the PAGENOTFOUNDERRORPAGE parameter:
"" — Return the HTTP/1.1 404 Page not found error (default)
Path to a static HTML page — Displays the named static page, such as /csp/samples/static.html.
OTHERSTATICERRORPAGE
If any other errors are generated, CSP looks at the value of the OTHERSTATICERRORPAGE parameter.
OTHERSTATICERRORPAGE can have the following three values:
"" — Obtains a license and displays the standard error page (the default)
Path to a static HTML page — Displays the named static page, such as /csp/samples/static.html.
/csp/user/MyPage.csp?A=10&a=20&B=30&B=40
Data is a multidimensional property and each value stored within it has 2 subscripts: the name of the parameter and the
index number of the parameter (parameters can occur multiple times within a URL as with B above). Note that parameter
names are case-sensitive.
Also note that it does not matter if an incoming HTTP request is a GET or a POST request: the Data property represents the
parameter values in exactly the same way.
You can use the ObjectScript $Data ($D) function to test if a given parameter value is defined:
If ($Data(%request.Data("parm",1))) {
}
If you wish to refer to a parameter but are not sure if it is defined, you can use the ObjectScript $Get function:
Write $Get(%request.Data("parm",1))
You can find out how many values are defined for a particular parameter name using the Count method of the %CSP.Request
object:
For i = 1:1:%request.Count("parm") {
Write %request.Data("parm",i)
}
Write %request.CgiEnvs("HTTP_USER_AGENT")
For information on the CGI environment variables you can use, see the section “ CGI Environment Variables ” in the CSP
Gateway Configuration Guide.
For an example using MIME data, refer to the upload.csp page in the CSP samples.
Class Definition
Class MyApp.Page Extends %CSP.Page
{
// ...
If you use CSP classes to create a CSP page, use the following code to set header values:
If you use HTML to create a CSP page, use the following code to set header values:
<head></head>
<script language="Cache" method="OnPreHTTP" arguments="" returntype="%Boolean">
Do %response.SetCookie("name","value")
Quit 1
</script>
<body></body>
<body>
<p>COOKIES:</p>
<ul>
<script language="Cache" runat="server">
Set cookie=%request.NextCookie("")
While cookie'="" {
For count=1:1:%request.CountCookie(cookie) {
Write "<li>",cookie," - ",..EscapeHTML(%request.GetCookie(cookie,count)),"</li>",!
}
Set cookie=%request.NextCookie(cookie)
}
</script>
</ul>
</body>
A cookie definition can include an expiration date and a path in this format:
Do %response.SetCookie("NAME","VALUE",expireData,path)
A blank expireData field defines an in-memory cookie. If, however, you specify a value for the expireData field this
becomes a permanent cookie that is removed at the time specified. The format for the expireData field is Wdy,
DD-Mon-YYYY HH:MM:SS GMT, for example: Wednesday, 24-Mar-2004 18:12:00 GMT.
For the first request of a session, the NewSession property of the %CSP.Session object is set to 1. For all subsequent requests
it is set to 0:
If (%session.NewSession = 1) {
// this is a new session
}
4.1.2 Session ID
A CSP application can find its particular session ID via the SessionId property of the %CSP.Session object:
When a session ends, the CSP server deletes the persistent %CSP.Session object and decrements the session license count,
if appropriate. If the session ended because of a timeout or server action, it also invokes the OnEndSession method of the
session event class (if it is present).
Certain Zen components, notably tablePane, store temporary data in ^CacheTemp.zenData, and this is typically
cleaned up automatically by the default Event Class. However, if you define your own custom event class, you must
explicitly call %ZEN.Controller.OnEndSession() in the OnEndSession() callback method in your event class. Otherwise
the temp data is not cleaned up.
Parameter Use
Parameter Use
CacheUserName From the login page, contains the username to log
in; for example, CacheUserName="fred"
CacheSecurityCancel The presence of this name indicates that the user has
cancelled out of the Login Security Token page.
Parameter Use
CacheNoRedirect A page P is requested, but it is unauthenticated, so
the Login page is displayed. After the user submits
the information from the login page, usually the page
request for P is redirected back to the browser. (This
stops the browser from asking the user to press the
<Resend> button before its shows P.) This behavior
can be short-circuited by passing
CacheNoRedirect=1
Set %session.Data("MyData") = 22
Then a subsequent request to the same session (regardless of which class handles the request) sees this value within the
%CSP.Session object:
The ability to store application-specific data within the %CSP.Session is a very powerful feature but should be used correctly.
Refer to the section “ State Management” for a further discussion.
Write %session.Data("MyData")
Write %session.Data("MyData",1) * 5
If you refer to a node of the Data array that has no value, there is an <UNDEFINED> (undefined) error at runtime. To avoid
this, use the ObjectScript $Get function:
Kill %session.Data("MyData")
By default, there is no event class defined and a timeout simply ends the current session.
CSP
<a href="page2.csp?DATA=#(data)#">Page 2</A>
When the CSP serves the page containing this link, the expression #(data)# is replaced with the value of the server variable
data in the text sent to the client. When the user selects this link to page2.csp, the CSP server has access to the value of
DATA via the %request object. If needed, CSP can encode such data. Refer to “Authentication and Encryption ” for more
details.
If the page contains a form, you can place state information within hidden fields:
CSP
<form>
<input type="HIDDEN" name="DATA" value="#(data)#">
<input type="SUBMIT">
</form>
As with the hyperlink example, when this form is sent to the client, the expression #(data)# is replaced with the value of
the variable data. When the user submits this form, the value of DATA is available via the %request object.
To automatically insert values into all links and forms, use %response.Context.
Class Definition
Class MyApp.Page Extends %CSP.Page
{
//...
The server can later retrieve this information using the %CSP.Request object's Cookies property.
Storing information within a cookie is useful for information that you want to remember past the end of a session. (To do
this, you must set an expiration date as, by default, cookies end when the browser closes.) For example, you could
remember a username in a cookie so that in a subsequent session they would not have to reenter this information. See an
HTML manual for information regarding different kinds of cookies and their formats.
CSP
<a href="page2.csp?PI=314159">Page 2</a>
CSP
<a href="page2.csp?CSPToken=8762KJH987JLJ">Page 2</a>
When the user selects this link, the encrypted parameter CSPToken is sent to the CSP server. The server then decrypts it
and place its decrypted contents into the %request object. If the encrypted value has been modified or sent from a different
session then the server throws an error. You can use the %CSP.Request class IsEncrypted method to determine whether
a parameter value was originally encrypted.
The CSP compiler automatically detects all the places where a URL can occur within an HTML document and performs
encryption as needed (based on the class parameters of the target page as described in the following section). If you are
creating a page programmatically you can get the same behavior using the Link method of the %CSP.Page class.
If you are passing a link as an argument to a function, always use the Link method of the %CSP.Page class, rather than
the #url()# directive, as in the following example:
window.showModalDialog('#(..Link("locks.csp"))#','',windowFeatures);
window.showModalDialog('#url(locks.csp)#','',windowFeatures);
If you need to provide an encrypted URL within a .csp file in a place that is not detected by the CSP compiler, use the
#url()# directive. For example, in a client-side JavaScript function, where the link is a parameter, you can use:
CSP
<script language=JavaScript>
function NextPage()
{
// jump to next page
CSPPage.document.location = '#url(nextpage.csp)#';
}
</script>
<A HREF='http://myserver/csp/samples/test2.csp'>
Link to private page - absolute path</A>
The user also cannot bookmark a private page for later use because the encrypted token used to protect the private page is
only valid for the current session.
Private pages work as follows. The %CSP.Page subclass responsible for the page has its class parameter PRIVATE set to
1. A URL requesting this page must contain a valid, encrypted CSPToken value in its query string. Any links to this page
processed by CSP automatically have an encrypted CSPToken value.
Note that because ENCODED=2 removes unencrypted parameters from the url it can disable components such as the Zen
<form> element.
CSP
<html>
<body>
Select an account:<br>
<a href="account.csp?ACCOUNTID=100">Checking</a>
<a href="account.csp?ACCOUNTID=105">Saving</a>
</body>
</html>
CSP
<html>
<csp:class private=1 encoded=2>
<body>
Account Balance: <b>$#(..GetBalance())#</b>
</body>
</html>
The CSP server sends the following HTML to the client when list.csp is requested:
CSP
<html>
<body>
Select an account:<br>
<a href="account.csp?CSPToken=fSVnWw0jKIs">Checking</a>
<a href="account.csp?CSPToken=1tLL6NKgysXi">Saving</a>
</body>
</html>
Notice that only the encrypted values for ACCOUNTID are sent to the client.
When account.csp is processed, it sees the decrypted value for ACCOUNTID (referenced in its GetBalance method).
4. The protected.csp page is displayed saying Your Account Balance is: 500
5. Notice that the URL contains an encrypted CSPToken (everything after CSPTaken=). This is the 500 that you entered
encrypted.
6. Navigate to the end of this URL and enter &BALANCE=8000 and press Enter.
7. The protected.csp page is displayed. It accepted the unencrypted addition to the URL and acted on it by displaying
the HACKER ALERT! marquee. If ENCODED had been set to 2, it would have ignored the unencrypted entry.
See the section “Considerations in Choosing Your Strategy” for more information for deciding what strategy to use.
When deciding whether or not to use Login Cookies, here are some considerations:
• The login cookie is updated to a new user whenever the user logs in with a password.
• Login cookies are not generated for an unauthenticated login (as UnknownUser).
• Login cookies are not generated when logging in through API calls.
• Login cookie sessions are independent once that session has been authenticated. So logging out or timing out in one
session does not affect the other sessions.
• Authentication from a Login-Cookie application cannot be shared with a password-only (non-Login-Cookie) application.
For authenticated applications in a group, for consistent behavior, use Login Cookies for all or for none.
If By-Session sharing is required, then the best solution is to name all applications so they can be given the same Session
Cookie Path. You may have to rename your applications because the Session Cookie Path must be a substring of the
application name.
If this cannot be done and session sharing is required, then you have to put the CSPSHARE parameter in links that jump
from one application to another. The target application page is placed in the same session as the source application's pages.
The source's session is determined either from the CSPCHD parameter or the session cookie.
See the section “Considerations in Choosing Your Strategy” for more information.
4.5.1.5 CSPSHARE
When CSP receives a request from a browser it does a series of checks to see if the sessionId it receives is a valid one.
These checks include:
• Whether the User-Agent is the same as that of previous requests from this sessionId
• If cookies are being used, whether this sessionId comes from a cookie or from a CSPCHD parameter
If you pass a CSPSHARE=1 query parameter, CPS turns off this checking. Then you can construct a link to another CSP
application and include the current sessionId, using CSPCHD=sessionId, so that this link runs in the same session as your
existing page. In addition, if CSPSHARE=1 when you construct a link, CSP automatically inserts the CSPCHD=sessionId
in to the link. If you manually insert a link with Write statements, you may need to insert the sessionId manually.
For example, if you have an application that requests an http page from an https page (or an https page from an http
page), add CSPSHARE=1 to the link as follows:
#(..Link(%request.URL_"?CSPSHARE=1"))#
CSPSHARE=1 forces the link construction to add CSPCHD to share the sessionId even if Caché detects that cookies are
enabled.
See the section “Considerations about CSPSHARE” for more information.
A session's sticky login is lost when the session is ended. The group's sticky login is lost when all the sessions containing
any of the group's applications are ended.
After the initial login, a group has an associated sticky login object which it attempts to use when entering one of the group's
applications. The sticky login is not updated when an application in the group is entered as UnknownUser as this would
have the effect of moving all other applications in the group to the unauthenticated security context.
If the sticky login contains a two-factor authenticated user, that two-factor authentication is used for non-two-factor appli-
cations, so long as the username authentication matches in the two applications.
Recommended: CacheLogout=end
The recommended way to logout of a CSP session is to link to the application home page passing a URL that contains the
string, CacheLogout=end. This ends the current session – releases any license acquired, deletes existing session data,
and removes the security context of the session – before it attempts to run the home page.
If this CSP application requires authentication, there is no session and no authenticated user. In this case, Caché does not
run the home page logic but displays the login page instead. When the user submits a valid login this starts this new session
and then displays the home page.
Set EndSession? =1
This kills the session. The session's sticky context is destroyed. OnEndSession is called. If the session contains a By-Session
group, then the group is destroyed. If the session contains a By-Id application, then that application is removed from the
group which continues to exist unless this was the only application in the group. Login cookies are unaffected. By-Session
groups lose their data. However, for By-Id groups, the sticky-login for the group is unaffected by a singular destruction
and the other members of the group remain logged in.
In addition, for By-Session groups, the destruction disperses the members of the group and if the member applications are
reentered, it cannot be guaranteed that they will be reintegrated into the same new session or (if they were grouped using
CSPSHARE) sent to diverse sessions.
Session Logout
The session is logged out. Its sticky context is destroyed. If the session contains a by-session group, then all the applications
in the group lose their authentication. If the session contains an application from a by-id group, then group loses its sticky
context and all the applications in the group are logged out.
In addition, OnLogout is called. The login cookie is destroyed.
The session continues to exist, so data is retained for By-Session groups.
8. When a group loses its authentication, refreshing or going to an open page from a group application requires that the
user re-login.
9. Ending a session containing a By-Session application requires that the user re-login when refreshing any page of any
application in that by-session group. Killing a session containing a By-ID application does not require any logins unless
that session's application was the only member of the group.
10. Logging out a session logs out all members of the session's group, even if they are in different sessions. Refreshing
any of the group's pages requires a new login. However, for By-ID groups, one login logs in the entire group. For By-
Session groups, one login logs in the entire group as long the CSP Gateway is able to direct the dispersed applications
back to a newly constructed session object.
11. Logging out does not destroy the session, so any session data continues to exist.
12. One cannot have same application logged in to two different users in different tabs of the same browser.
13. Authentication is shared within a single browser only. This runtime identifier is stored in the %Session object.
14. Grouping allows you to share authentication with users that are in the same group (By-ID) or the same session (By-
Session). If you want to share authentication from applications that are outside your specified group, use Login
Cookies. If you want to send authentication to applications outside your specified group, use CSPSHARE=1. (See the
section “Considerations about CSPSHARE ” in this book.)
For example, when the following simple CSP document, hello.csp is compiled,
CSP
<html>
<body>
Hello!
</body>
</html>
Class Definition
Class csp.hello extends %CSP.Page
{
When a user requests the hello.csp page from a browser, the CSP server invokes the generated OnPage method and the
original text of the CSP document is sent to the browser for display.
You can explicitly compile a CSP source file into a class. This is useful for finding errors.
1. Open the CSP source file in Studio.
2. Select Build > Compile.
You can also compile a CSP source file from the Caché command line (the terminal) using the $System.CSP API (as
shown in the example). This method loads and compiles the CSP file with the URL path (not physical path)
/csp/user/mypage.csp. The c (compile) flag compiles the generated class. The k flag (keep) preserves the generated inter-
mediate code to be viewed.
Do $System.CSP.LoadPage("/csp/user/mypage.csp","ck")
When you compile a CSP document, the result is a Caché class that executes ObjectScript or Basic code. Keep this in mind
to help you to develop correct application logic as well as perform troubleshooting. You may, in fact, find examining the
code generated by the CSP compiler a useful way to learn more about both CSP and CSP markup language.
It is also important to keep track of what code is executed on the CSP server (as it prepares a response to an HTTP request)
and what code is to be executed on the HTTP client (such as HTML and JavaScript).
For an example, see the basic.csp application included in the CSP samples (click source to view the source).
Within a CSP document, runtime expressions as well as the contents of any server-side <script> tags must use the default
language for the page (or else you receive a compile-time error). Or you can define a method in a different language and
invoke that from the default language.
5.2.2 Text
Any text contained within a CSP document (HTML or XML) that is not a CSP directive or special tag is sent unchanged
to the HTTP client requesting the page.
For example, a CSP document containing the following:
CSP
<b>Hello!</b>
Write "<b>Hello!</b>",!
CSP
<b>Hello!</b>
CSP
This page was compiled on: <b>##($ZDATETIME($H,3))##</b>
You can also define lines of code to be executed at page compilation time using the runat attribute of the <script> tag:
CSP
<script language="Cache" runat="compiler">
Note: You must write all compile-time expressions and code using ObjectScript.
Note: Note that name indirection is supported and argument indirection is not supported with the #(expr)# directive.
CSP
Two plus two equals <b>#(2 + 2)#</b>
CSP
Write "Two plus two equals <b>", (2 + 2), "</b>",!
CSP
The answer is <b>#(answer)#</b>.
CSP
Your current balance is: <b>#(account.Balance)#</b>.
CSP
<table>
<csp:while condition="result.Next()">
<tr><td>#(result.Get("BookTitle"))#</td></tr>
</csp:while>
</table>
CSP
<table bgcolor='#(%request.Data("tablecolor",1))#'></table>
A runtime expression can be anywhere within an CSP document where the #(expr)# structure can be used as legal HTML.
This includes within HTML text, as the value of an HTML element attribute, or within the definition of client-side JavaScript
code.
If the value of a runtime expression contains any special characters (such as < or > , angle brackets) you need to escape
them to send the correct escape sequence to the HTTP client. To escape them, use one of the escape methods provided by
the %CSP.Page class. See “Escaping and Quoting HTTP Output” for details. The example below shows the EscapeHTML
classmethod. Any characters that need to be escaped that exist in object.Description are replaced by their HTML escape
sequences when the method is run.
CSP
Description: <b>#(..EscapeHTML(object.Description))#</b>.
If a runtime expression is used in an HTML attribute value, any HTML entities found in the runtime expression are converted
to the characters that they represent before being converted to executable code. For example:
CSP
<ul>
<script language="cache" runat=server>
For i = 1:1:4 {
Write "<li>Item ",i,!
}
</script>
</ul>
Write "<ul>",!
For i = 1:1:4 {
Write "<li>Item ",i,!
}
Write "</ul>",!
CSP
<ul>
<li>Item 1
<li>Item 2
<li>Item 3
<li>Item 4
</ul>
#[ set x = a + b write x ]#
CSP
<script language="Cache" method="MakeList"
arguments="count:%Integer" returntype="%String">
New i
Write "<ol>",!
For i = 1:1:count {
Write "<li> Item",i,!
}
Write "</ol>",!
Quit ""
</script>
You can then invoke this method from elsewhere within the CSP document:
CSP
<hr>
#(..MakeList(100))#
You can also use inheritance (using the <csp:class> tag) to inherit previously defined methods into your page class or to
invoke the class methods of another class:
CSP
<hr>
#(##class(MyApp.Utilities).MakeList(100))#
CSP
<script language="SQL" name="query">
SELECT Name FROM MyApp.Employee ORDER BY Name
</script>
Typically you use a %ResultSet object created by the SQL script tag in conjunction with the <csp:while> tag (see csp:while
Tag) to display the results of the query.
The SQL <script> tag closes the instantiated %ResultSet object when the page is done executing.
You can specify parameters for the SQL query by using the ? character within the SQL text. You can provide values for
parameters using the P1, P2,...Pn attributes of the SQL <script> tag (where n is the number of parameters).
Here is example using the SQL <script> tag to display the purchases made by the current user. (The current user's User
ID is assumed to have been previously stored in the %session object):
CSP
<script language=SQL name=query P1=#(%session.Data("UserID"))#>
SELECT DateOfPurchase,ItemName,Price
FROM MyApp.Purchases
WHERE UserID = ?
ORDER BY DateOfPurchase
</script>
<hr>
Items purchased by: <b>#(%session.Data("UserID"))#</b>
<br>
<table>
<tr><th>Date</th><th>Item</th><th>Price</th></tr>
<csp:while condition="query.Next()">
<tr>
<td>#(..EscapeHTML(query.GetData(1)))#</td>
<td>#(..EscapeHTML(query.GetData(2)))#</td>
<td>#(..EscapeHTML(query.GetData(3)))#</td>
</tr>
</csp:while>
</table>
Using the <csp:query> tag, you can use a query defined as part of a Caché class to create a %ResultSet object:
CSP
<csp:query NAME="query" CLASSNAME="Sample.Person" QUERYNAME="ByName">
You can use the resulting %ResultSet object in the same way as you would with the SQL<script> tag.
CSP
<csp:class SUPER="%CSP.Page,MyApp.Utilities">
Here is an example of redefining the value of a class parameter: To redefine the value of the class parameter PRIVATE as
1 (to define a page as private), use:
CSP
<csp:class PRIVATE=1>
CSP
<csp:if condition='user="Jack"'>
Welcome Jack!
<csp:elseif condition='user="Jill"'>
Welcome Jill!
<csp:else>
Welcome!
</csp:if>
CSP
<script language=SQL name=query>
SELECT Name
FROM MyApp.Employee
ORDER BY Name
</script>
<csp:while condition="query.Next()">
#(..EscapeHTML(query.Get("Name")))#<BR>
</csp:while>
Using the <csp:while> tag's counter attribute you can define a counter variable that is initialized to zero (0) and
automatically incremented by one (1) at the start of every iteration.
Here, for example, is a way to use the <csp:while> tag to create an HTML table with 5 rows:
CSP
<table>
<csp:while counter="row" condition="row<5">
<tr><td>This is row #(row)#.</td></tr>
</csp:while>
</table>
Here is an example of using a not operator (a single quote) in a condition. Note that the condition cannot contain any spaces
and does not include start and end quotes. You can also state the condition using parentheses, as (mystr'=”QUIT”).
<csp:while condition=mystr'="QUIT">
//add code
</csp:while>
CSP
<ul>
<csp:loop counter="x" FROM="1" TO="5">
<li>Item #(x)#
</csp:loop>
</ul>
CSP
#(..EscapeHTML(x))#
If the value of x is <mytag>, the following escaped text is sent to the HTTP client:
CSP
<mytag>
Similarly, escaping is necessary when you send the value of HTML attributes:
CSP
<input type="BUTTON" value="#(..EscapeHTML(value))#">
If the value of value is <ABC>, this results in the following text being sent to the HTTP client, where the two angle brackets,
left and right, are replaced with their character sequence equivalents: < and > respectively:
CSP
<input type="BUTTON" value="<ABC>">
Place "" (quotation marks) around the #()# directive to make the resulting HTML attribute value quoted.
When sending output from a database to an HTTP client, it is good practice to escape it. For example, consider the following
expression that writes a username to a web page (assuming user is a reference to an object with a Name property):
CSP
User name: #(user.Name)#
If your application lets users enter their names into the database, you may find that a mischievous user may enter a name
containing HTML commands. If the following is written out to an HTTP client without HTML escape sequences, the page
may have unintended behavior.
CSP
Set user.Name = "<input type=button onclick=alert('Ha!');>"
CSP
<a href="page2?ZOOM=#(..EscapeURL(x))#">Link</A>
If the value of x is 100%, then the following text is sent to the HTTP client. The % character is escaped as %25.
CSP
<a href="page2?ZOOM=100%25">Link</A>
CSP
<script language="JavaScript">
function showMessage()
{
alert(#(..QuoteJS(x))#);
}
</script>
If the value of x is “Don't press this button!”, then the following text is sent to the HTTP client:
CSP
<script language="JavaScript">
function showMessage()
{
alert('Don\'t press this button!');
}
</script>
The advantages of using HTTP Submit are that client-side programming is simple and no client-side components are needed.
Its disadvantages are that the page is repainted by the client after a method call and server-side programming is more difficult.
If you use hyperevents, #server and #call are implemented using XMLHttpRequest. #call is asynchronous: if you (as a
user) enter a value on a web page, the page is not updated immediately; you may have moved to another page by the time
it is updated. #server is synchronous; the page is updated immediately on the return from the call.
Note that synchronous XMLHttpRequest is deprecated by many browsers, and in general, movement is toward only sup-
porting the asynchronous XMLHttpRequest.
HTTP Submit and hyperevents are described further in the sections below.
2. For Zen, the programmer may define ZenMethods which handle client-server interactions. These may be synchronous
or asynchronous depending on the methods signature:
CSP
<form name="MyForm" action="MyPage.csp" method="GET">
User Name: <input type="TEXT" name="USERNAME"><br>
<input type="SUBMIT" name="BUTTON1" value="OK">
</form>
This defines a simple form with a text field called USERNAME and a SUBMIT button called BUTTON1. The ACTION
attribute of the form specifies the URL to which the form is submitted. The METHOD attribute specifies which HTTP
protocol is used to submit the form: POST or GET.
2. When the user clicks BUTTON1, the SUBMIT button, the browser collects the values of all controls in the form and
submits them to the URL specified by the form's ACTION attribute. (Note that a page can submit back to itself either
by specifying its name with the ACTION attribute or by leaving the ACTION attribute blank.) Regardless of whether
a form is submitted via POST or GET, CSP treats the submitted values as if they were URL parameters. In this case,
submitting the form is equivalent to requesting the following URL:
MyPage.csp?USERNAME=Elvis&BUTTON1=OK
The name and value of the SUBMIT button is included. If there are multiple SUBMIT buttons on a form, only the data
button that was actually pressed is included in the request. This is the key to detecting when a SUBMIT has occurred.
3. The server code, in this case MyPage.csp, detects that a submit has occurred. It does this by testing for the name
BUTTON1 in the %request object:
CSP
<script language="Cache" runat="SERVER">
// test for a submit button
If ($Data(%request.Data("BUTTON1",1))) {
// this is a submit; call our method
Do ..MyMethod($Get(%request.Data("USERNAME",1)))
}
</script>
4. After invoking the desired server-side logic, the server code continues and returns HTML for display by the browser.
This could be a redisplay of the current form or a different page.
CSP
#server(classname.methodname(args,...))#
where classname is the name of a server-side Caché class and methodname is the name of a method in the class. args is a
list of client-side JavaScript arguments that are passed to the server-side method. Note that all code between the opening
and closing #signs must all be on a single line.
For example, to invoke a server-side method named Test in the Caché class MyPackage, use:
CSP
<script language="JavaScript">
function test(value)
{
// invoke server-side method Test
#server(MyPackage.Test(value))#;
}
</script>
The CSP compiler replaces each occurrence of #server directive with JavaScript code that invokes the server-side method.
From a given CSP page, you can invoke a method belonging to the class generated for it using ..MethodName syntax. For
example:
CSP
#server(..MyMethod(arg))#
CSP
#call(classname.methodname(args,...))#
where classname is the name of a server-side Caché class and methodname is the name of a method in the class. args is a
list of client-side JavaScript arguments that are passed to the server-side method. Note that all code between the opening
and closing #signs must all be on a single line.
For example, to invoke a server-side method named Test in the Caché class MyPackage, use:
CSP
<script language="JavaScript">
function test(value)
{
// invoke server-side method Test
#call(MyPackage.Test(value))#;
}
</script>
The CSP compiler replaces each occurrence of the #call directive with JavaScript code that invokes the server-side method.
From a given CSP page, you can invoke a method belonging to the class generated for it using ..MethodName syntax. For
example:
CSP
#call(..MyMethod(arg))#
CSP
<form name="Customer" method="POST">
Customer Name:
<input type="Text" name="CName"
onChange=#server(..Find(document.Customer.CName.value))# >
</form>
In this case, the Find method could be defined in the same CSP file as:
CSP
<script language="Cache" method="Find" arguments="name:%String">
// test if customer with name exists
// use embedded SQL query
New id,SQLCODE
&sql(SELECT ID INTO :id FROM MyApp.Customer WHERE Name = :name)
If (SQLCODE = 0) {
// customer was found
// send JavaScript back to client
&js<alert('Customer with name: #(name)# already exists.');>
}
</script>
This method communicates with the client by sending back JavaScript for execution.
Whenever a server-side method is invoked, any output it writes to the principal device is sent back to the client. There, it
is converted into a JavaScript function and executed by, and in the context of, the client page.
For example, if a server-side method executes the following lines of code:
JavaScript
CSPPage.document.title = 'New Title';
In this case, this changes the title displayed in the browser to New Title. Any valid JavaScript can be sent back to the
client in this fashion. Note that you must place a carriage return (using the ! character) at the end of each line of JavaScript
or the browser cannot execute it.
To make it easier to return JavaScript from a server method, ObjectScript supports embedded JavaScript using the &js<>
directive. This is a special language construct that lets you include lines of JavaScript in an ObjectScript method. When
the method containing the embedded JavaScript is compiled, the contents of the &js<> directive is converted to the
appropriate Write command statements. Embedded JavaScript can refer to ObjectScript expression using the #()# directive.
For example, a Caché method containing the following:
Set count = 10
&js<
for (var i = 0; i < #(count)#; i++) {
alert('This is pleasing!');
}
>
is equivalent to:
Set count = 10
Write "for (var i = 0; i < ", count, "; i++) {",!
Write " alert('This is pleasing!');",!
Write "}",!
When invoked from a client, this method displays a pleasing alert box 10 times.
&html<<html>
<head>
<script language=javascript>
function onServer()
{
alert(#server(..ServerMethod())#);
}
</script>
#(..HyperEventHead())#
</head>
<body>
<input type=button value="click here" onclick='onServer()' />
</body>
</html>>
Quit $$$OK
}
ClassMethod ServerMethod()
{
quit "from server"
}
Note: Within this section, anything mentioned for #server applies to #call as well unless noted otherwise.
Either the #server or #call directive can call a method on the Caché server from JavaScript in the web browser. This makes
CSP able to do things such as validate a field when you move off it rather than waiting for the submission of the form and,
so, give the user immediate feedback. There are several factors with using #server syntax that you should be aware of —
otherwise it is possible to produce applications that perform very slowly, or, in some cases, do not work at all.
There are two basic rules to keep in mind when using #server:
1. Never use #server in the onload event of a web page. This can fail and it is faster and easier to generate the data when
generating the web page in Caché.
2. Do not use #server in the onunload event of a web page. Use as few #server calls as possible and do as much work
inside each call as you can because they are expensive, involving a round trip from the browser to the server.
The reason this is not a good idea is because any code that you need to run inside the onload event can be run faster and
more easily when the page is generated from Caché. For example, suppose that you wish to setup an initial value for a
JavaScript variable that can be used later in the page (maybe in #server calls). So you are currently doing this:
CSP
<html>
<head>
<script language="JavaScript">
function LoadEvent()
{
var value=#server(..GetValue())#;
}
</script>
</head>
<body onload=LoadEvent();>
</body>
</html>
<script language="Cache" method="GetValue" returntype="%String">
Quit %session.Data("value")
</script>
However there is absolutely no need to call #server because the value of the JavaScript variable is already known in
%session.Data("value") when you are generating the page. Hence, it is much better to write:
CSP
<html>
<head>
<script language="JavaScript">
function LoadEvent()
{
var value='#(%session.Data("value"))#';
}
</script>
</head>
<body onload=LoadEvent();>
</body>
</html>
The same trick applies no matter what you are doing, if you are updating the value of a form element when the document
loads then change this to put the value in when the page is generated, for example:
CSP
<input type="text" name="TextBox" value='#(%request.Get("Value"))#'>
There is never any need to use a #server in the onload event of a page.
Because the page is unloading, it is difficult to know if the JavaScript returned from Caché will be executed or not and the
actual behavior depends on the browser. Also, if the user turns off the machine, you never get an onunload event. Your
application needs to be able to cope with this possibility in any case, probably by using timeouts on the %session object.
You can move the onunload #server logic code to, for example, the start of the next CSP page that the user clicks.
Note that this block of code uses the CSP keyword CSPPage to refer to the page itself, rather than the Javascript keyword
self. In this example, the two keywords work identically. We recommend using CSPPage as self can act unexpectedly in
different contexts.
CSP
<script language="JavaScript">
function UpdateForm()
{
CSPPage.document.form.Name.value = #server(..workGet("Name",objid))#;
CSPPage.document.form.Address.value = #server(..workGet("Address",objid))#;
CSPPage.document.form.DOB.value = #server(..workGet("DOB",objid))#;
}
</script>
The server code is shown here. (Normally it would use an object or SQL but here we are using a global to keep the code
small.)
CSP
<script language="Cache" method="workGet"
arguments="type:%String,id:%String" returntype="%String">
Quit $get(^work(id,type))
</script>
This single update is making three calls for new web pages from the Caché server! This can be converted to a single #server
call that updates all the values at once, the JavaScript becomes:
CSP
<script language="JavaScript">
function UpdateForm()
{
#server(..workGet(objid))#;
}
</script>
CSP
<script language="Cache" method="workGet"
arguments="id:%String" returntype="%String">
&js<CSPPage.document.form.Name.value = #($get(^work("Name",objid)))#;
CSPPage.document.form.Address.value = #($get(^work("Address",objid)))#;
CSPPage.document.form.DOB.value = #($get(^work("DOB",objid)))#;>
</script>
So, instead of multiple calls, you just pass the data once and then make Caché do all the work. If you have a more complex
JavaScript example, such as:
CSP
<script language="JavaScript">
function UpdateForm()
{
CSPPage.document.form.Name.value = #server(..workGet("Name",objid))#;
if (condition) {
CSPPage.document.form.DOB.value = #server(..workGet("DOB",objid))#;
}
else {
CSPPage.document.form.DOB.value = '';
}
}
</script>
then this should still only ever need one #server call. You just embed the whole if condition into the JavaScript returned
by the #server call, so the code workGet method ends up looking like:
CSP
<script language="Cache" method="workGet"
arguments="id:%String" returntype="%String">
&js<CSPPage.document.form.Name.value = #(^work("Name",objid))#;
if (condition) {
CSPPage.document.form.DOB.value = #(^work("DOB",objid))#;
}
else {
CSPPage.document.form.DOB.value = '';
}
>
</script>
5.5.4.2 Creating Custom HyperEvent Error Handler for #server and #call
If you call something with a hyperevent (#server or #call) and, on execution, it fails to communicate with the server for
some reason, then generates an error, CSP’s default behavior is to display the error in an alert box. If you want to handle
the error separately, such as log it or display a different message to the user, then write a cspRunServerMethodError
JavaScript function. The following example displays the error in an alert box like the default behavior:
function cspRunServerMethodError(errortext,error)
{
//alert('cspRunServerMethodError - cspHyperEventErrorHandler\n\nerrortext:' + errortext +
'\n\nerror-object:\n' + JSON.stringify(error, null, 4) );
if (error.code == '401') {
document.location.href = '#(..Link(%request.URL))#'; //reloads the page
}
else {
//...
}
return null;
Note that by using the Caché SQL Gateway, you can build object-based CSP applications that access data in third party
relational databases. Caché manages this in an application-transparent way; all the techniques described in this chapter
work regardless of whether you choose to store data in the built-in Caché database or a third party database.
CSP is flexible; you can build database applications using a variety of techniques ranging from using higher-level tags that
automatically bind data to HTML forms to writing server-side scripts that directly access data using objects. These techniques
are outlined below.
CSP
<html>
<body>
<script language="Cache" runat="SERVER">
// open an instance of Sample.Person
Set id = 1
Set person = ##class(Sample.Person).%OpenId(1)
</script>
<table border="1">
<tr><td>Name:</td><td>#(person.Name)#</td></tr>
<tr><td>SSN:</td><td>#(person.SSN)#</td></tr>
<tr><td>City:</td><td>#(person.Home.City)#</td></tr>
<tr><td>State:</td><td>#(person.Home.State)#</td></tr>
<tr><td>Zip:</td><td>#(person.Home.Zip)#</td></tr>
</table>
<script language="Cache" runat="SERVER">
// close the object
Set person = ""
</script>
</body>
</html>
If you would like to try this, copy the above code into a text file, save it as mytable.csp in your /cachesys/csp/samples
directory (cachesys is the installation directory for Caché), and then point your browser at:
http://localhost:57772/csp/samples/mytable.csp
Note: Be careful not to do any real work in the /csp/samples directory. If you upgrade Caché, it reinstalls the samples
and erases your work.
CSP
<html>
<body>
<script language="Cache" runat="SERVER">
// open an instance of Sample.Person
Set id = 1
Set person = ##class(Sample.Person).%OpenId(1)
If ($Data(%request.Data("SAVE",1))) {
// If "SUBMIT" is defined, then this is a submit
// Write the posted data into the object and save it
Set person.Name = $Get(%request.Data("Name",1))
Set person.SSN = $Get(%request.Data("SSN",1))
Set person.Home.City = $Get(%request.Data("City",1))
Do person.%Save()
}
</script>
<form method="POST">
<br>Name:
<input type="TEXT" name="Name" value="#(..EscapeHTML(person.Name))#">
<br>SSN:
<input type="TEXT" name="SSN" value="#(..EscapeHTML(person.SSN))#">
<br>City:
<input type="TEXT" name="City" value="#(..EscapeHTML(person.Home.City))#">
<br>
<input type="SUBMIT" name="SAVE" value="SAVE">
</form>
<script language="Cache" runat="SERVER">
// close the object
Set person = ""
</script>
</body>
</html>
%request.Data("txt",1) is a string value if the data is less than the local variable limit in Cache. If the data is larger than
this, CSP creates a stream with the value of the data in it. If long strings are disabled, the Caché variable limit is
32k. If long strings are enabled then the boundary is much bigger.
If you are creating a form that contains a field that can hold more than 32K of data, code it as follows:
Set value=%request.Data("fieldname",1)
If $isobject(value) {
; Treat this as a stream
} Else {
; Treat this as a regular string
}
If ($Data(%request.Data("SAVE",1))) {
// If "SUBMIT" is defined, then this is a submit
// Write the posted data into the object and save it
Set person.Name = $Get(%request.Data("Name",1))
Set person.SSN = $Get(%request.Data("SSN",1))
Set person.Home.City = $Get(%request.Data("City",1))
Do person.%Save()
}
CSP
<csp:object NAME="person" CLASSNAME="Sample.Person" OBJID="1">
<!-- Now use the object -->
Name: #(person.Name)# <br>
Home Address: #(person.Home.Street)#, #(person.Home.City)# <br>
In this case, the <csp:object> tag opens the object of class CLASSNAME with an Object ID of 1 and assigns it to the
variable person. In an actual application, the object ID is provided from the %request object:
CSP
<csp:object NAME="person" CLASSNAME="Sample.Person"
OBJID='#($Get(%request.Data("PersonID",1)))#'>
Name: #(person.Name)# <br>
Home Address: #(person.Home.Street)#, #(person.Home.City)# <br>
The expression,
$Get(%request.Data("PersonID",1))
The <csp:object> tag with a null OBJID attribute creates a new object of the specified class:
CSP
<csp:object NAME="person" CLASSNAME="Sample.Person" ObjID="">
Using the <csp:object> tag is equivalent to including server-side script that explicitly creates an object instance. Refer
to the CSP sample page object.csp for an example using the <csp:object> tag.
CSP
<html>
<head>
</head>
<body>
<csp:object NAME="person" CLASSNAME="Sample.Person" OBJID="1">
<form NAME="MyForm" cspbind="person">
<br>Name:
<input type="TEXT" name="Name" cspbind="Name" csprequired>
<br>SSN:
<input type="TEXT" name="SSN" cspbind="SSN">
<br>City:
<input type="TEXT" name="City" cspbind="Home.City">
<br>
<input type="BUTTON" name="SAVE" value="SAVE" OnClick="MyForm_save();">
</form>
</body>
</html>
This example uses the <csp:object> tag to open an instance of the Sample.Person class (in this case, with object ID of 1).
This object instance is named person. The example then binds the object instance to an HTML form by adding to its form
tag an attribute called cspbind with the value person.
The form contains three text input controls, Name, SSN, and City, which are bound to the object properties Name, SSN, and
Home.City, respectively, by adding to each of their input tags an attribute called cspbind whose value is the name of the
object property the control is to be bound to.
Note that the names of controls used in a bound form must be valid JavaScript identifiers.
The Name control also has an attribute called CSPREQUIRED. This indicates that this is a required field (it must be given
a value). The CSP compiler generates client-side JavaScript to test that the user provides a value for this field.
The last control on the form is a button that is defined to invoke the client-side JavaScript function MyForm_save when
it is clicked. The MyForm_save function is automatically generated by the CSP compiler. This function gathers the values
of the controls in the form and sends them to a server-side method (also automatically generated by the CSP compiler) that
reopens the object instance, applies the changes made to the properties, saves the object to the database, and sends JavaScript
to the client to update the values in the form to reflect what was saved.
Note that we have defined a HEAD section in this document. This is necessary when using a bound form as this is used a
location for any client-side JavaScript that may be generated by the CSP compiler when it processes a bound form.
By convention, the object ID of an object used in a bound form is specified by the URL parameter OBJID. This makes it
possible for a bound form to interact with prebuilt pages, such as those used by the CSP Search facility. To use the value
of a URL parameter as an object ID, use an expression referring to it in the csp:object tag:
CSP
<csp:object NAME="person"
CLASSNAME="Sample.Person" OBJID=#($G(%request.Data("OBJID",1)))#>
The cspbind attribute lets you bind to many different types of object properties. This is detailed in the following table:
Table 6–1: Effects of cspbind Attribute
The binding mechanism can be used with most of the available HTML input controls. This is detailed in the following
table:
Table 6–2: HTML Input Elements Supported by cspbind
Control Effect
input type=“TEXT ” Display the value of a property in a text control.
input type=“PASSWORD” Display the value of a property in a password control.
input type=“CHECKBOX ” Display the value (as a boolean) of a property in a check box
control.
input type=“RADIO ” Display the value of a property by selecting the radio button
whose value corresponds with the property value.
input type=“HIDDEN” Display the value of a property in a hidden control.
SELECT Display the value of a property by selecting the choice in the
SELECT list whose value corresponds with the property value.
You can populate the choices in the SELECT list using a class
query by also specifying CLASSNAME, QUERY, and optional
FIELD attributes. Refer to the CSP sample page form.csp for
an example.
IMAGE Display a binary stream property in an IMAGE tag.
TEXTAREA Display a property value as text in a TEXTAREA control.
Attribute Description
CAPTION Optional. A caption string displayed in the standard search page.
CLASSNAME Required. The name of the class upon which the search is performed.
FEATURES Optional. A string contains the features argument passed to the JavaScript
window.open method when a popup search window is used. This gives you
greater control over how popup windows are displayed.
MAXROWS Optional. Specifies the maximum number of rows to display in the search results
table. The default is 100.
Attribute Description
NAME Required. The name of the generated client-side JavaScript function that
invokes the search page.
OBJID The Object ID value of the object displayed when the search page was invoked.
This is used to redisplay the old page when the user cancels a search.
ONSELECT Optional. In a popup search page, the name of a JavaScript function to call
when the user selects a specific search result. This function is called with the
Object ID value of the selected object.
OPTIONS Optional. A comma-delimited list of search page options.These options include
"popup" to create a popup search window and "predicates" to display a drop
down list of search predicates.
ORDER Optional. A name of a field to order the search results on.
SELECT Optional. A comma-delimited list of fields to display in the search result table.
If not specified, the WHERE list is used as the SELECT list.
STARTVALUES Optional. A comma-delimited list of the names of controls in the form invoking
the search page whose contents are used as seed values in the search page.
The order of names in the list corresponds to the criteria fields (specified by
the WHERE attribute) in the search page.
TARGET Optional. In a non-popup search page, specifies the name of the page to which
the links in the search result window point. That is the page to display when
the user makes a selection. The default is the page invoking the search.
WHERE Required. A comma-delimited list of fields used as criteria for the search page.
These fields are also shown in the search result table unless the SELECT
attribute is specified.
For example, the following defines a JavaScript function, MySearch; this function displays a popup search window that
searches for Sample.Person objects by name:
CSP
<csp:search NAME="MySearch" WHERE="Name" CLASSNAME="Sample.Person"
OPTIONS="popup" STARTVALUES="Name" ONSELECT="MySearchSelect">
The ONSELECT callback function for this search page looks like this.
CSP
<script language="JavaScript">
function MySearchSelect(id)
{
#server(..MyFormLoad(id))#;
return true;
}
</script>
This function uses the CSP #server()# directive to invoke the server-side method MyFormLoad. The MyFormLoad
method is automatically generated as a result of binding the HTML form MyForm to an object using cspbind. This method
populates the contents of the form with the property values of an object with object ID id.
For additional examples, refer to the CSP sample pages form.csp and popform.csp.
ObjectScript
Set ^%ISCLOG = 2
You can view logging information in the ^ISCLOG global. This global logs events in Caché for use in debugging. For
reference, the log levels are as follows:
• 0 — Caché performs no logging.
• 1 — Caché logs only exceptional events (such as error messages).
• 2 — Caché logs detailed information, such as method ABC invoked with parameters X,Y,Z and returned
1234.
• 3 — Caché logs raw information such as data received from an HTTP request.
ObjectScript
Set ^%ISCLOG = 0
or
ObjectScript
Kill ^%ISCLOG
Field Definition
%category CSPServer: Logged from cspServer, cspServer2, %request, %response.
CSPSessionLogged from %session and parts of cspServer and cspServer2 which handle
a session. This allows watching the lifecycle of a session.
Field Definition
%level 1= Exceptions and errors.
3=Information from cspServer2: the part of handling the request which sets up the
%response, %session, %request, and hand-shaking/data transfer with the CSP Gateway.
%job The value of $job when the ISCLOG request was made. Matches the Cache-PID field from
the Event Log header.
%sessionid Entered when available. The value of sessionid at the time the ISCLOG request was
made. Matches the Session-ID field from the Event Log header.
%tag For the CSP Server, the tag contains the Request id from the gateway (when available).
This matches the Request-ID field from the Event Log header. Other loggers may set this
value to any value.
Available for use by creators of ISCLOG entries. Stores ID of the request sent to it by the
CSP Gateway. It can be used as a filter for generation of ISCLOG entries.
Set ^%ISCLOG("Tag","mytagvalue1")=1
Set ^%ISCLOG("Tag","mytagvalue2")=1
Only ISCLOG requests with no tag or with tags of "mytagvalue1" or "mytagvalue2" will be
recorded.
Messages in the CSPSession category also have CSPSession-Id=sessid after the method name. This is needed as
session events can be logged before the session is created or after it was destroyed, meaning the SessionId field is empty
in the ISCLOG entry.
Messages in the GatewayRegistry category also have CSPID=cspid(when available) after the method name. This
allows the tracking of an individual gateway request from the API call through the Gateway Request Handler.
&html<<div>"Hello world"</div>>
Include this:
2. When the code is compiled, the compiler generates entries in the message dictionary for each unique instance of the
$$$Text macro (and its related macros).
The message dictionary is a global and so can be easily viewed (for example) in the Management Portal. Caché provides
class methods to help with common tasks.
3. When development is complete, release engineers export the message dictionary for that domain or for all domains.
The result is one or more XML message files that contain the text strings in the original language.
4. Release engineers send these files to translators, requesting translated versions.
5. Release engineers import the translated XML message files into the same namespace from which the original was
exported.
Translated and original texts coexist in the message dictionary.
6. At runtime, the application chooses which text to display based on the browser default language.
For steps 1 and 2, also see the appendix “ Localization and Tag-Based Development.”
For information on exporting and importing the message dictionary, see the article String Localization and Message Dic-
tionaries.
Each of these macros takes three arguments: the default string, the domain to which this string belongs, and the language
code of the default string. When code is compiled, the compiler generates entries in the message dictionary for each unique
set of values of the arguments.
The %String returned by $$$Text may be assigned to a variable, which you can use to represent the message in subsequent
calls. For example:
Or, you can simply insert a $$$Text macro anywhere you need a string:
Argument Description
text Non-empty string. text must be a literal string. It cannot be the value of a CSP runtime
expression enclosed in #()# syntax. The format used for text may be:
"actualText"
Or:
"@textId@actualText"
If provided, the textId is used as the message ID. If @textId@ is not specified, the system
generates a new textId by calculating the 32–bit CRC (Cyclic Redundancy Check) of this
text. If the textId is specified and a message already exists with this ID, the existing message
is checked to see if it has the same text as actualText. If not, an error is reported.
domain (Optional) String specifying the domain for the new message. If not specified, domain
defaults to the value of the DOMAIN class parameter at compile time and %response.Domain
at runtime.
language (Optional) RFC1766 code specifying the language. Caché converts this string to all-lower-
case. If not specified, language defaults as follows:
• At compile time: $$$DefaultLanguage.
• At runtime: %response.Language, or if no value is defined for %response.Language
then $$$DefaultLanguage.
Tag-based CSP pages automatically acquire a value for %response.Language from browser
settings, so it is available as a default language. This is not true for class-based CSP pages,
which must explicitly set a value for %response.Language to use it as a default.
You can assign a value to %response.Language by giving it the return value of the
%Library.MessageDictionary class method MatchLanguage(), discussed later in this chapter.
Given a list of languages and a domain name, this method uses HTTP 1.1 matching rules
(RFC2616) to find the best-match language within the domain.
You can also use the $$$Text string as the first argument of the %response.FormatText method or a $$$FormatText macro.
Argument Description
domain (Optional) A string specifying the domain for the message. If not specified, domain
defaults to %response.Domain.
language (Optional) An RFC1766 code specifying the language. Caché converts this string to
all-lowercase. If not specified, language defaults to the value of %response.Language,
which automatically takes its runtime value from the browser settings.
id The message ID.
default The string to use if the message identified by language, domain, and id is not found.
arg1, arg2, and so Substitution text for the message arguments. All of these are optional, so you can use
on %response.GetText() even if the message has no arguments.
Argument Description
text The message text. Use a %String returned by %response.GetText or $$$Text.
arg1, arg2, and so Substitution text for the message arguments.
on
Argument Description
text The message text. Use a %String returned by %response.GetText or $$$Text.
arg1, arg2, and so Substitution text for the message arguments.
on
This finds the best language match to a language in the list of languages for the specified domain. The method uses HTTP
1.1 matching rules (RFC2616). The list of languages is a comma-separated list of RFC1766 format language names. Each
language in the list may be given an associated quality value which represents an estimate of the user’s preference for the
languages specified by the list of languages. The quality value defaults to q=1.
For example, da, en-gb;q=0.8, en;q=0.7 would mean: I prefer Danish, but will accept British
English and other types of English. A language from the list matches a supported language tag if it exactly
equals the tag, or if it exactly equals a prefix of the tag such that the first tag character following the prefix is a hyphen (-
). The special language asterisk (*), if present in the input list, matches every supported language not matched by any other
language present in the list.
The language quality factor assigned to a supported language tag is the quality value of the longest language in the list that
matches the language-tag. The language that is returned is the supported language that has been assigned the highest quality
factor.
The s flag (system) is an optional flag indicating whether system or application messages are to be matched.
CSP allows you to develop custom HTML tags for use in CSP files. The CSP markup language is itself implemented using
the custom tag mechanism. Custom tags provide a way:
• To provide HTML authors with additional functionality using familiar syntax
• To develop reusable components for web applications
If you are familiar with XML's XSL technology, you may recognize that CSP is a variant of XSL with additional features
added to generate HTML for applications.
The following CSP page example contains a custom tag, <my:COMPANY>, that displays your company name in an HTML
page:
<html>
<body>
<my:COMPANY>
</body>
</html>
When this page is processed, we want the CSP compiler to replace the <my:COMPANY> tag with suitable HTML text, for
instance:
<html>
<body>
<b>Octoglomerate</b>
</body>
</html>
The action for the CSP compiler to take for the <my:COMPANY> tag is defined in a rule file with a <csr:action> tag.
A rule file is an XML document with a .csr (Caché Server Rule) file extension that defines rules for recognizing tags and
the actions to be performed when those tags are found. It also can include additional information, such as a description of
the rule.
The rule file for the<my:COMPANY> tag might look like this and might be named company.csr:
CSP
<csr:rule name="myCOMPANY" match="my:COMPANY" empty>
<csr:action>
<b>Octoglomerate</b>
</csr:action>
</csr:rule>
A rule name has the same naming restrictions as Caché classes. (For information on Caché naming rules, see “Naming
Conventions ” in Using Caché Objects or “Syntax Rules” in Using Caché ObjectScript). A rule is active only in the Caché
namespace in which it is defined, except for rules starting with %, which are defined in the %SYS namespace and are visible
by all other namespaces.
The <csr:action> tag specifies the action to take. The contents of the <csr:action> tag must be HTML and any of
the following:
• #()# expression
• ##()## expression
• <script> tag
To load the company.csr rule file, move the file to the /cachesys/csp/user directory, start a Caché session (using the Terminal),
and execute the following command:
Do $System.CSP.LoadRuleFile("company.csr")
This loads the rule definitions in the specified file into the current namespace. You can also use Studio to load and compile
rule files with File > New > Caché Server Page. Save the file in the /csp/user directory. You can observe which rules are in
effect using the rulemgr.csp page provided with the CSP samples.
Much of CSP is implemented using rule files. You can view the system rules files in Studio, %SYS namespace, Workspace
window, Namespace tab, under CSP files.
AAA/*/BBB When a <BBB> tag is encountered nested anywhere in an <AAA> tag: <AAA><FFF>
<BBB></BBB> </FFF></AAA>
AAA[CCC] When an <AAA> tag with a CCC attribute (having any value) is encountered: <AAA
CCC=”10” ></AAA>
AAA[CCC=22] When an <AAA> tag with a CCC attribute having a value of “22” is encountered:
<AAA CCC=”22”><AAA>
AAA[CCC=22]/*/BBB When a <BBB> tag is encountered nested anywhere in an <AAA> tag that has a
CCC attribute with a value of “22” : <AAA CCC=”22” ><FFF> <BBB></BBB>
</FFF></AAA>
CSP
<csr:rule name="TODAY" match="TODAY" empty>
<csr:action>
Today is: <b>#($ZDATE($H))#</b>
</csr:action>
</csr:rule>
If you load this rule, you can place it in the body of a CSP page:
CSP
<TODAY>
CSP
<csr:rule name="LASTMOD" match="LASTMOD" empty>
<csr:action>
This page was last compiled on: <b>##($ZDATE($H))##</b>
</csr:action>
</csr:rule>
You can include compile time expressions in runtime expressions. In the case below, the first $H is evaluated at runtime,
giving the current date. The second is evaluated when the page is compiled returning the date on which the page was
compiled.
CSP
<csr:rule name="BIGLIST" match="BIGLIST" empty>
<csr:action>
<ul>
<script language="Cache" runat=server>
For i = 1:1:100 {
Write "<li>Item " _ i _ $C(13,10)
}
</script>
</ul>
</csr:action>
</csr:rule>
If you load this rule, you can place it in the body of a CSP page like this:
CSP
<BIGLIST>
And when the page is requested, an unordered list with 100 items is displayed.
CSP
<html>
<body>
Hello!
<MYTAG MSG="Welcome">
</body>
</html>
The server-side document object model is only created during page compilation; it does not exist at runtime (again for
efficiency). This is the main reason why actions may contain expressions and code that are executed at compile time: to
take advantage of the server-side document object model.
After the CSP compiler constructs the document object model, it processes the document by visiting the objects in the tree,
depth first, and firing the rules associated with each %CSP.Rule object in the tree and rendering the results into executable
code. %CSP.TextAtom objects, by definition, do not have rules, and thus they are rendered directly into executable code.
CSP
<MESSAGE VALUE="Yo!">
CSP
<csr:rule name="MESSAGE" match="MESSAGE" empty>
<csr:action>
<B><I>##(..GetAttribute("VALUE"))##</I></B>
</csr:action>
</csr:rule>
The MESSAGE rule is fired (that is, RenderStartTag is called) whenever the <MESSAGE> tag is encountered. Its actions
are to:
1. Write out a <B> and an <I> tag.
2. Write out the value of the tag object
3. Close the <B> and <I> tags.
CSP
<csr:rule name="ECHO" match="ECHO" >
<csr:action>
<csr:default>
</csr:action>
</csr:rule>
This tag is mainly used for cases where you want to change some aspect of a tag but not otherwise disturb it. For example,
if you want all tables on your CSP pages to have a red background, define a rule for the <table> tag:
CSP
<csr:rule name="REDTABLE" match="TABLE" >
<csr:action>
<script language="Cache" runat="COMPILER">
// set the bgcolor attribute for this element
Do ##this.SetAttribute("BGCOLOR","red")
</script>
<csr:default>
</csr:action>
</csr:rule>
When this rule is fired, it changes the value of the BGCOLOR attribute for any <TABLE> tag to red (using a compile-time
script) and then render the table tag (and its children) unaltered in every other respect.
CSP
<csr:rule name="MYBUTTON" match="FORM/*/MYBUTTON" empty>
<csr:action>
<csr:section NAME=HEAD>
<script language="JavaScript">
function MyButton()
{
alert('MyButton pressed!');
return true;
}
</script>
</csr:section>
CSP
<csr:rule name="%ELSE" match="CSP:IF/CSP:ELSE" empty>
<csr:class super=%CSP.RuleBlock>
<csr:action>
<SCRIPT LANGUAGE=Cache RUNAT=COMPILER>
New ifblock
Set ifblock=..GetCurrentBlock()
If ifblock'="" {
If ifblock.EndLabel="" Set ifblock.EndLabel=..GetNewLabel()
Do ..WriteServer(" Goto "_ifblock.EndLabel_" ;}")
Do ..WriteServer(ifblock.NextLabel_" ;{")
Set ifblock.NextLabel=""
}
</SCRIPT>
</csr:action>
</csr:rule>
CSP
<csr:description>
The <b>SCRIPT LANGUAGE=SQL</b> creates embedded SQL for a
DECLARE CURSOR statement in the class generated by the CSP page. The
declared cursor will be opened using an SQL OPEN statement and the
SQLCODE will be returned. It is the programmers responsibility to
display any error indicated by the SQLCODE value.<p>
The mode of the query is determined by the MODE attribute. The mode is
taken to be a runtime mode by default. If the COMPILERMODE attribute
is specified, then the mode is taken to be a compile time mode that
is defined by a generated #SQLCOMPILE statement.
<p>For example:
<EXAMPLE>
<SCRIPT LANGUAGE=SQL CURSOR=prsn>
select name,ssn from sample.person where ssn %STARTSWITH '2' order by ssn
</script>
<CSP:WHILE CURSOR=prsn INTO='Name,ssn' counter=x condition=(x<3)>
Name: #(Name)#<br>
SSN: #(ssn)#<br>
</CSP:WHILE>
</EXAMPLE>
<p>Will display all people whose ssn begins with 1:
<OUTPUT>
CSP
<csr:attribute
name=Type
description="Specify the default Content-Type"
type="contentType:STRING"
>
<csr:attribute
name=Charset
description="Specifies the default charset"
type="charSet:STRING"
>
<csr:attribute
name=NoCharSetConvert
description="Turns off the charset conversion"
type="noCharSetConvert:STRING"
>
Class Definition
Import User
CSP
<csr:action>
<script language="Cache" runat=server>
Set myfile="c:\temp.txt"
Open myfile:("FR":100)
Use myfile:()
Read var1
Close myfile
</script>
</csr:action>
results in the following RenderStartTag method being generated in the created rule class upon compilation:
The RenderStartTag method can contain other statements, depending on the structure of the rule definition. If the <script
runat=compiler> tag was specified in the action, resulting in the creation of CompilerMethod methods, the
CompilerMethod methods are called at the beginning of the RenderStartTag method using the following commands
(shown for the instance of CompilerMethod1):
In addition to different Write methods and calls to CompilerMethod methods, the RenderStartTag method can also
contain other commands depending on whether one or more csr tags were used in the <csr:action> definition.
Quit $$$PROCESSCHILDREN
This indicates that the children should be processed upon completion of the RenderStartTag method. The RenderEndTag
method is also written explicitly to the class and writes statements that occur after the <csr:children> tag is called (by
default, the RenderEndTag method does nothing).
Do ..RenderDefaultStartTag()
Quit $$$PROCESSCHILDREN
CSP
<script language="Cache" runat=compiler>
SET ^client(2,1,1)=..InnerText()
</script>
When the .csr file is compiled, the following method is generated in the associated rule class:
CSP
<csr:rule name="iscbarchart" match="isc:barchart" language="any">
<csr:action>
<table bgcolor='##(..GetAttribute("BGCOLOR"))##' border=0 cellspacing=0
style='border: ##(..GetAttribute("BORDER","solid blue"))##;'><tr>
<csr:children>
</tr></table>
</csr:action>
</csr:rule>
Upon compilation, an iscbarchart rule class is generated, with a call to process the children in the Quit statement of the
RenderStartTag method. The HTML that was present after the <csr:children> tag in the rule file is written in the
RenderEndTag method:
Class Definition
Import User
The method GetAttribute gets the value of the HTML attribute name for this element. The value already has any ##( )##
and ##''## expressions resolved.
The following example sets the name and size of an HTML grid:
Set tablename=##this.GetAttribute("NAME")
Set maxcols=##this.GetAttribute("COLS")
Set maxrows=##this.GetAttribute("ROWS")
The method QuoteAttribute gets the value of the HTML attribute name for this element. The value is quoted for substitution
with #()#, ##( )## and ##''## expressions resolved.
The following example is taken from the <csp:loop> tag, which contains four attributes: one of type string ( "counter"),
and three of type integer ("FROM", "STEP", and "TO"). It retrieves their values and prints them as strings on the CSP page:
CSP
<SCRIPT LANGUAGE=Cache RUNAT=COMPILER>
New counter,from,step,to
Set counter=..GetAttribute("counter","counter")
Set from=..QuoteAttribute("FROM",1)
Set step=..QuoteAttribute("STEP",1)
Set to=..QuoteAttribute("TO",1)
Do ..NewBlock()
Do ..WriteServer(" For "_counter_"="_from_":"_step_":"_to_" {")
</SCRIPT>
GetAttributesOrdered(ByRef paramsordered)
The method IsDefined indicates whether the HTML attribute name is defined.
If ..IsDefined("CAPTION") {
Do ..WriteServer(setCmd
_ " nvp(""CAPTION"") = "
_ ..QuoteAttribute("CAPTION"))
}
The method InnerText gets the text contained in the start and end tag.
This method can be used to collect and replace the contents of the tag with text specified by the language and domain
attributes.
Set %text=##class(%CSP.TagLanguage).GetText(##this,..InnerText())
The method SetAttribute sets the HTML attribute name for this element to value.
The following example sets the "NAME" attribute to the default ("FORM") if no "NAME" attribute is specified:
If ('..IsDefined("NAME")) {
Do ..SetAttribute("NAME","FORM")
}
The default OnMatch method for rules is to do nothing. OnMatch may be overridden by user rules.
I need to explain what the method does — in general; how does it check rules to see if they match.
One way to create common code to be inherited by all CSP pages in an application is to override the OnMatch method.
The OnMatch method for each matching rule is called after the selected rule is added to the rule DOM. When the rule is
matched -- if more than one rule matches with the same selectivity then each OnMatch will be called. The order should
not be depended on as it depends upon compile order and other factors.
The rule class instance passed to the OnMatch method is the selected rule that has been put into the DOM; this rule is the
rule that had the most specific match (according to rules that are very similar to the XSLT Selectivity rules). The OnMatch
from all matching rule classes is called using this most selective rule.
The OnMatch method can modify the DOM via the rule that is passed in. See the %URL.FORM rule in url.csr that we
ship and the attached sample. The sample rule is less selective than the built-in system rule; this allows default behavior to
continue. The OnMatch callback is designed for this purpose, since it is called when a rule matches, even if it is not the
most selective rule. As an alternative to the sample, one could create a custom rule that is added to the DOM and then fixes
things up further when the DOM is being transversed during page class generation. This option is possible, but is more
complex.
Instead of overriding the OnMatch method, you could put the code into your own rule. We do not recommend that you
overrivde system rules.
Overriding the system-supplied rules (especially html, head and body) requires great care and dependency on the internals
of the rule compiler. InterSystems recommends taking the approach that we take for the /csp/samples CSP pages where we
created the isc:SAMPLE rule and included it in every page. It is simple to write a routine that loops over existing pages
and adds the new custom tag.
appropriate commands necessary to exhibit the intended behavior when the page is requested. The %CSP.AbstractAtom
class contains the definition for these write methods:
• WriteText
• WriteCSPText
• WriteExpression
• WriteServer
• WriteCSPServer
The WriteText command generates a Write command in the CSP page class to write the contents of a line. It takes in two
arguments: the string to be written, and a carriage return line feed Boolean indicating whether a newline should be written.
The WriteCSPText command generates a Write command in the CSP page class to write the contents of a line with the
processing of ##()##, ##''##, #server, #url, and #()# expressions. It takes in two arguments: the string to be
written, and a carriage return line feed Boolean indicating whether a newline should be written. For example, the following
line in the body of a <csr:action> tag in a .csr rule file:
CSP
<B><I>##(##this.GetAttribute("VALUE"))##</I></B>
Do ..WriteCSPText("<B><I>##(##this.GetAttribute(""VALUE""))##</I></B>",0)
The WriteExpressionText command generates a write command in the CSP page class to write the text returned by an
ObjectScript expression. The text returned should already be properly quoted. It takes in two arguments: the string to be
written, and a carriage return line feed Boolean indicating whether a newline should be written.
The WriteServer command generates an ObjectScript command in the CSP page class that is in line. It takes in two argu-
ments: the string to be written, and a Boolean indicating whether to append the string to the previous statement.
The WriteCSPServer command generates an ObjectScript command in CSP page class that is in line with ##()##, #()#,
and ##''## resolved. It takes in two arguments: the string to be written, and a Boolean indicating whether to append the
string to the previous statement. For example, the following ObjectScript code in a .csr rule file:
CSP
<script language="Cache" runat=server>
Set myfile="c:\temp.txt"
Open myfile:(80:"C":"|")
Use myfile:()
Read ^client(3,1,1)
Close myfile
</script>
Surrounds the input string with quotes and resolves #()#, ##()##, ##''##, #server, and #url calls.
• <GRIDDATA>tag
Although the <GRIDDATA> attributes are handled in the rule definition for <GRID>, an empty rule is still necessary to
instantiate the <GRIDDATA> tag:
CSP
<csr:rule name="GridDataExample" match="/GRID/GRIDDATA">
<csr:description>
This purpose of this empty rule is to instantiate the GRIDDATA tag
into the Document Object Model.
</csr:description>
<csr:action>
<csr:default>
</csr:action>
</csr:rule>
• GridData
Class Definition
Import User
Class Definition
Import User
CSP
<html>
<head>
<title>Grid Example</title>
</head>
<body>
<grid cols="5" rows="5">
<griddata value="Cell-1-1" col="1" row="1">
</griddata>
<griddata value="Cell-2-1" col="2" row="1">
</griddata>
<griddata value="Cell-2-2" col="2" row="2">
</griddata>
<griddata value="Cell-2-3" col="2" row="3">
</griddata>
<griddata value="Cell-2-4" col="2" row="4">
</griddata>
<griddata value="Cell-2-5" col="2" row="5">
</griddata>
<griddata value="Cell-3-1" col="3" row="1">
</griddata>
<griddata value="Cell-4-1" col="4" row="1">
</griddata>
<griddata value="Cell-4-3" col="4" row="3">
</griddata>
<griddata value="Cell-5-1" col="5" row="1">
</griddata>
<griddata value="Cell-5-5" col="5" row="5">
</griddata>
</grid>
</body>
</html>
CSP
Cell-1-1 Cell-2-1 Cell-3-1 Cell-4-1 Cell-5-1
Cell-2-2
Cell-2-3 Cell-4-3
Cell-2-4
Cell-2-5 Cell-5-5
B.1 Introduction
During tag-based development of CSP pages, you can configure certain tags so that they substitute a message dictionary
entry for what would otherwise be literal text. Do this by providing the localization attributes language, domain, or textid
inside the tag. The following tags support these attributes:
• <csp:text>
• <span>
• <div>
• <input> (when the type is "SUBMIT", "BUTTON", or "RESET")
For the most part, these tags work only at runtime, when the values provided for language, domain, and textid indicate
which message to retrieve from the message dictionary.
However, in limited cases these tags serve different purposes at compile time and runtime. They can automatically generate
entries for the message dictionary at compile time, then retrieve and display these entries at runtime. The following sections
explain how this works:
• Localization Tags at Runtime
• Localization Tags at Compile Time
For a simple demonstration of a localized CSP application, enter the following URL while Caché is running: http://local-
host:57772/csp/samples/language.csp.
At runtime, when the CSP page displays, the tag replaces itself and its contents with text from the message dictionary. The
choice of text is specified by the language, domain, and textid attribute values supplied by the tag.
For example, the following syntax is replaced by the message specified by the fr (French) language, sample domain, and
menu message ID. The text provided inside the <csp:text> tag (Menu in this case) is ignored:
CSP
<csp:text textid="menu" language="fr" domain="sample">Menu</csp:text>
Defaults for language, domain, and textid are available if any of the attributes are omitted or empty (value ""):
• If language is not specified, the value of %response.Language is used.
• If domain is not specified, the value of %response.Domain is used.
• textid is required (with exceptions; see the section “Localization Tags at Compile Time ”)
CSP
<csp:text textid="sessionInfo" arg1="#(userName)#" arg2="#(roleID)#" />
When an <input> tag uses localization attributes (language, domain, or textid) the value attribute is ignored. The text
displayed on the button is the text from the message dictionary. If you want to localize the text on a <button> tag, use
the language, domain, or textid attributes of the <csp:text> tag.
The textid attribute may have the empty value "". If so, when you compile the tag-based CSP file a new message is auto-
matically generated in the message dictionary. The text for the generated message consists of the contents of the CSP tag.
Caché generates a message ID by calculating the 32–bit CRC (Cyclic Redundancy Check) of this text.
Only the <csp:text> tag permits you to actually omit the required textid attribute. The other localization tags require
you to at least provide an empty value "".
If a <csp:text> tag lacks a textid attribute, the system automatically generates a new message and message ID value. In
cases where you omit textid from <csp:text>, the text inside the tag may include an optional @textID@ string at the
beginning. textID is the message ID that you wish to assign to the message. For example:
CSP
<csp:text>@simpleMenu@Menu</csp:text>
In the above example, Caché does not generate the message ID. It generates a message whose text is Menu and gives it the
message ID simpleMenu.
When a CSP tag has been used to generate a message dictionary entry at compile time, it still works as a reference to retrieve
that entry at runtime. See the section “Localization Tags at Runtime.”
Troubleshooting
How do I fix a Zen error about zenutils.js or other js file?
The web server must be configured to serve .js files through the CSP Gateway. This is not done by the Caché installer, even
when the option to configure the external webserver is selected, due to security concerns. For details, see CSP Gateway
Configuration Guide. Find the section for your operating system and your configuration option. Then within your option,
find one or both of the sections (depending on the option) called “Mapping the CSP File Extensions” and “Registering
Additional File Types with CSP.”
On an Apache web server, add the following to the httpd.conf file (there are a few other ways, but this is easiest):
<Location /csp>
CSP On
SetHandler csp-handler-sa
</Location>
1. Open the Control Panel. Select Admin Tools > IIS Manager.
2. On menu at left, expand yourcomputername > WebSites > default website and click csp
3. Right-click CSP > properties.
4. On the Virtual tab, click Configuration.
5. Scroll through the list, and make sure that *.js is there.
Use the built-in Studio Debugger described in the chapter Using the Studio Debugger in the book Using Studio.
Don’t set breakpoints with Debug > View Breakpoints, as this seems error prone.
1. To debug CSP pages, you must check the option Tools > Options > Compiler > Keep Generated Source Code.
2. Open your Workspace window and add your CSP pages to the CSP folder, if they are not already there.
3. Compile your CSP and click the View Other Code icon in the toolbar (or select View > View Other Code). This lets you
see the .cls and .int files. For example, the file A.CSP generates CSP.A.CLS and CSP.1.INT.
4. In the .cls or .int file, right click the line of code where you want a breakpoint and select Debug > Breakpoints > Toggle
Breakpoint (or select the line and press F9).
5. Select Debug > Debug Target > ZEN or CSP page. From the dropdown, select the target CSP page on which the debugger
will run and click OK. (If you dragged your CSPs to the Workspace window, they appear in the dropdown list.)
6. Click Go on the Debug toolbar.
For example, if you have a flow: A -> B; that is, display page A and then follow a link to page B. And there is a bug in
page B, you would do the following:
1. Check that A.CSP and B.CSP are in the Workspace window.
2. Compile both.
3. Select View > Toolbars > Debug to open the Debug toolbar.
4. Select Debug > Debug Target > ZEN or CSP page. From the dropdown, select A.CSP and click OK.
5. Open B.CSP and select View > View Other Code to open csp.B.CLS.
6. In csp.B.CLS, right click the first line in the OnPageBODY() method and select Debug > Breakpoints > Toggle Breakpoint.
7. Click Go on the Debug toolbar.
8. Page A is displayed.
When compiling a CSP page with runat="server" in a script tag, the compiler runs the ObjectScript and converts it
into HTML to display on the page. However, after encountering a <script language="cache" runat="server">
tag, it looks for the </script> end tag to signify the end of the ObjectScript code, which, in this case, it finds in the
write statement. To get this to compile, break the </script> tag into two write statements:
CSP
<script language="Cache" runat="server">
write "<script language=javascript>", !
write "int x = 10; alert(x);", !
write "</script",">", !
</script>
When I use &js<alert(“Error!”);> to display an alert box, the text alert(“Error!”) is displayed instead
of an alert box. Why?
It is possible that you put this line inside a runat=“server” code section or inside a method called from a
runat=“server” block. To execute JavaScript as the page loads, add the <script language=“javascript”> tag
as shown in the previous answer.
The code &js<alert(“Error!”)> works inside a server side method called via a JavaScript event from the loaded
page.
Use #()# syntax. From inside your ObjectScript method, try something like this:
The QuoteJS method provided by the %CSP.Page class returns a properly quoted JavaScript string. It also correctly escapes
any quotes contained within the returned result.
I am getting the following error: HTTP Request failed. Unable to process hyperevent. What does this mean?
Hyperevent errors occur when the browser tries to communicate with the CSP broker applet but cannot. This could be
related to the code or your configuration. To decide if the problem is your code, load http://localhost:57772/csp/samples/zip-
code.csp (where 57772 is the Caché web server port number. Replace 57772 with the actual web server port, if necessary,
or leave it out if you have an external web server running (http://localhost/csp/samples/zipcode.csp), or if Caché is installed
remotely, replace localhost with the IP address or machine name.)
On the zip code page, click #server, and enter a zip code, such as 02142, in the Zip box and press Tab. If you do not receive
a hyperevent error, you are properly configured and your hyperevent error is likely caused by a coding error.
If the problem appears to be coding related, there are two things to look for. Never use #server calls as the page is loading.
This includes calling them in the OnLoad event of the <body> tag, either directly or from a JavaScript method called in
OnLoad. It also includes calling them from inside an &js<> line of a runat="server" code block. If you need to call
a method as the page loads, use ObjectScript syntax inside a runat="server" block.
CSP
<script language="cache" runat="server">
// if method is located in the page or in a class the page inherits from
d ..mymethod()
// if class cannot be called using dot syntax
d ##class(User.MyClass).mymethod()
</script>
I received an error message that suggests a problem in a line of my routine, but I can’t find the INT routine. Where
is it?
Depending on your current settings, you might not keep the source code when you compile a CSP page. To force Caché
to maintain this source code, you can do one of two things:
• Compile your CSP pages from the Studio with Keep Generated source code selected. In Studio, click Tools >
Options > Compiler > General Flags. Select the Keep generated source code check box. Then, to compile your CSP
page, click Build > Compile.
• Compile your CSP pages from the Terminal using the k flag to Keep generated source code. From the Terminal,
verify you are in the correct namepace. (If not, change namespaces by entering: Zn “<namespace>”.) To compile
your CSP page, enter: Do $System.CSP.LoadPage( “/csp/<namespace>/<pagename>.csp”, “ ck”). For example:
Do $System.CSP.LoadPage("/csp/samples/james.csp", "ck")
When I run my CSP page, #(variable)# shows up in the browser. Why isn’t this being replaced with the data
in the variable?
This indicates that your CSP page has not been properly parsed by the Caché CSP Compiler. Ensure that you are running
your pages through your web server as follows: http://localhost/csp/<namespace>/page.csp.
Why am I getting Invalid Character error messages when I try to load my CSP page?
If you are not loading your page through your web server, this error is common because the browser does not know how
to represent your CSP syntax. If your URL looks something like C:/install-dir/csp/user/mypage.csp, you are not loading
through your web server.
Your URL should be something like http://localhost:57772/csp/user/mypage.csp or http://localhost/csp/user/mypage.csp.
These messages can also result from #server()# calls which are not correctly translated to an ObjectScript method call.
From your browser, right-click and view the source of your page. If the source still contains #server, your syntax may
be incorrect. Ensure that it is properly formed as: #server(..methodname())#.
If you are passing strings to the method, they must be inside double quotes ( “ ”). Once your CSP page is compiled into
HTML, all #server()# calls are translated into a call to csprunservermethod().
Your web server is not properly passing your CSP page to the CSP Gateway for parsing.
Does CSP log errors? How can I increase logging and where does the log exist?
• If there is an internal error, such as an error in your custom error page, it is logged to the BACK^%ETN error log. If
you receive internal errors in this log that are not related to your custom error page, it may be a problem with the core
CSP engine and you should contact the InterSystems Worldwide Response Center (WRC).
• Other errors are logged by calling the user-defined error page where the user can add their own logging function.
When I try to load a CSP page, the following error appears: ERROR #5924: An error occurred and the specified
error page could not be displayed - please inform the web master. What does this mean and how can I solve it?
This error can result from a number of different issues. View the error log to get more specific information about the actual
error that occurred. In the Terminal, issue the following command:
d ^%ER
To view the resultant error log, navigate to System Operation > System Logs > Application Error Log in the Management
Portal and check the errors for the appropriate namespace. The errors are contained in folders by date.
If you set up a custom error page, this could mean that your custom error page has no mechanism to deal with an error in
the page you are calling. It could also mean that your custom error page itself generated an error. One way to trace this
error is to temporarily turn off your custom error page and attempt to load the CSP page.
If your CSP pages work locally, but not when called from another computer, it may be that you have a Single User version
of Caché or do not have a Caché license. Calling CSP pages from a remote machine requires both a full version of Caché
and a valid key with licenses available. Adding a Caché key to a version you have downloaded from the Internet does not
give it full functionality. You still need to receive a full version. See also Error 5924 in Appendix B Error Notes
I try to display a CSP page and nothing comes up at all, or I get a login screen, enter a valid username/password,
and it will not let me in. What is wrong? I am pretty sure that the CSP gateway is configured to talk to the correct
Caché instance, or I am using private Apache install that came with Caché so it is preconfigured.
Make sure that auditing of security events is turned on from the %SYS namespace. At the very least, audit Login, Logout
and LoginFailure.
1. In the terminal, in the %SYS namespace, enter Do ^SECURITY.
2. Select Auditing setup, Configure auditing events, and Create audit event.
3. Enter %SYSTEM/%Login/Login, %SYSTEM/%Login/Logout, and %SYSTEM/%Login/LoginFailure.
Try accessing the CSP page again and check the audit log to see if you can see any failures. Often this will tell you what
the problem is - such as a disabled service, an incorrect password, or that you do not have permission to run this CSP
application. For more information on auditing, see the chapter “Auditing ” in the Caché System Administration Guide.
In previous releases, when applications shared a session, they could share authentication and data only via the session
object.
There are two ways to share a session:
• Via the session cookie path.
• Putting CSPSHARE=1 in the link to the application page.
When the session times out, it is destroyed and its authentication is lost. If an existing page is reloaded, the user has to login
again. For applications connected via the session cookie path, they are 'logged in' also, because when you go to a page from
one of those applications you get the newly logged-in session.
Not so when sessions are shared via CSPSHARE=1. For example, start an application called SMP which is logged into
session X. Now click a link to another application called EMP. That link contains CSPSHARE=1. EMP does not have to
log in as it is put into authenticated session X. After a while session X times out and is destroyed. SMP and EMP are
without sessions.
Now click a tab containing a page from SMP and, as before, we are asked to log in again. SMP is now in authenticated
session Y. We then click a tab containing a page from EMP. There is now no connection between SMP and EMP. EMP is
asked to log in again and is put in authenticated session Z.
CSPSHARE is a very fragile way to try to share sessions and is easily severed. Once severed, multiple logins can ensue.
In this release: Use session-sharing only if you decide that data must be shared via the session object. If you need only
authentication sharing (and not data sharing), use other options.
Session—Sharing : If you require session sharing, it is best to name all applications with the same Session Cookie Path
(which now must be an exact match). You may have to rename your applications, such as, /csp/sys/tool/smp and
/csp/sys/tool/emp.
If you require session sharing and you can’t name all applications with the same Session Cookie Path, then use the CSP-
SHARE element. However, previous idiosyncrasies, such as multiple logins after time out, will contain to manifest them-
selves.Use CSPSHARE as a last resort.
Authentication-Sharing: If the design calls for sharing authentication information, but does not require sharing session data,
use one of the new authentication features.
• Login Cookies [Share Authentication when Enter Application]
Login Cookies hold information about the most-recently logged in user. When they are enabled, a newly— accessed
application tries to use that authentication.
For Login Cookies, each application is in a separate session. These sessions are independent once that session has been
authenticated. So logging out or timing out in one session does not affect the other sessions.
Unauthenticated logins are not saved in the Login Cookie. If application A logs in to user Q, then application B as
unauthenticated, then application C uses login cookies, application C will be logged in as Q.
• Group-By-Browser [Share Authentication Continuously]
If you want a group of applications to act as an authentication cluster, then use group-by-browser.
All applications stay in authentication sync. If one logs out, they are all logged out. If one logs into user Q, they are
all logged into user Q. (The only exception is that if any applications are unauthenticated, they are treated as pariahs
and ignored as far as authentication is concerned.)
To end a CSP session, set the %session.EndSession property to 1 in an ObjectScript method. If your CSP application times
out, the session is ended automatically by your CSP class.
I closed my CSP session, but Caché still reports that I am using a license. Why?
If you have visited only a single page and you logout or the session times out, CSP provides a grace period of 5–10 minutes,
during which it reserves the license for you, so that you can recapture the same license if you return quickly. The grace
period is the longer of:
• 5 minutes from the end of the session (a timeout or a logout) or
• the amount of time that would ensure 10 minutes from when the session started (ensuring a minimum session of 10
minutes)
The following table summarizes how and when licenses are released:
Here are some examples of how the grace period works when you have visited a single page:
• The user logs in at 12:00 and logs out at 12:15. The grace period is 5 minutes, so the license is free at 12:20.
• The user logs in at 12:00 and logs out at 12:03. The minimum license use time is 10 minutes, so the license is free at
12:10.
• The user logs in at 12:00 and closes the browser at 12:10. The timeout is set to 15 minutes, so the session ends at 12:25.
The grace period is 5 minutes, so the license is free at 12:30.
The default timeout on applications is set in each namespace to 900 seconds (15 minutes).
• To change the Timeout for all CSP pages within a certain namespace:
1. From the Caché cube, click Management Portal. Log in, if necessary.
2. On the main page of the Management Portal, navigate to System Administration > Security > Applications > Web
Applications.
3. On the Web Applications page, click Edit for the CSP application to configure.
4. In the Default Timeout field, enter a new value (in seconds) and click Save.
• To change the Timeout for a specific application, put the following inside your page, where x is the timeout value in
seconds.
s %session.AppTimeout = x
I want to perform cleanup or logging when a user CSP session times out. How can I do this?
CSP
<script language="cache" runat="server">
s %session.EventClass = "User.MyEventClass"
</script>
Note: At this point you cannot send information back to the browser (alerts or redirects).
http://myserver/csp/user/mypage.csp?id=3&name=bill
I want to forward the user to another web page if the session times out. How can I do this?
The easiest way to accomplish this is to set up a redirection in a metatag to occur just after your timeout:
<html>
<head>
<META HTTP-EQUIV="REFRESH" CONTENT="910;
URL=youhavetimedout.csp">
</head>
<body>
<script language="cache" runat="server">
%session.AppTimeout = 900
</script>
</body>
</html>
What are the %SYS.cspServer, %SYS.cspServer2, and %SYS.cspServer3 processes? Why might there be more
of them than expected on a system that is not very busy?
The %SYS.cspServer and %SYS.cspServer2 routines are the server routines that process CSP requests.
%SYS.cspServer3 handles asynchronous communication with the CSP Gateway, that is, whenever the Caché instance
needs to exchange information with the gateway outside of the context of a user request. For example, this is needed for
asynchronous web socket requests and interactions using the Gateway Manager interface (%CSP.Mgr.GatewayMgr, which
is used by ^CSPGWMGR and CSPButtons).
There are two types of CSP Gateway processes:
• Processes that serve CSP requests (sometimes known as worker processes). These processes use %SYS.cspServer and
%SYS.cspServer2. These processes are in %SYS.cspServer2 while they are idle and waiting for a new request; when
serving a request, they can be in any routine.
• Processes that manage the gateway (sometimes known as server processes). These processes are in %SYS.cspServer3
while idle.
The number of worker or server processes that exist on the system vary. The number depend on the configuration of the
web server(s) that are connected to Caché and the number of concurrent requests (that is, the load) that those web servers
are receiving.
Typically, there is one server process for each web server process that has a connection to Caché. However, other factors
can affect this:
• More than one web server can be connected to a single instance.
• Each web server can have multiple processes.
For example, for Apache, when using the Prefork Multi-Processing Module (MPM), the default behavior is to use one web
server process for each connection. In contrast, the worker and event MPMs use multiple connections under the same
webserver process; this is why these modules are recommended for use with CSP, rather than Prefork. On Windows, the
IIS application pool typically runs in a single process, but it can be configured to use multiple processes.
A webserver has settings to control the number of worker processes. In CSP, each webserver worker process is associated
with a connection to Caché and a Caché worker process (for example, a %SYS.cspServer2 process). The webserver
dynamically scales up the number of workers (up to the configured limit) based on load. There are also settings to control
when these workers are closed down, but this typically does not happen quickly. This is entirely controlled by the underlying
webserver, not the Web Gateway.
It is important to note that the Caché worker processes can serve any incoming request and are not associated with a partic-
ular user session. (The exception to this rule is a CSP application using State Aware or Preserve mode sessions, which is
a very old configuration option and is not recommended). The number of worker processes on a system roughly matches
the maximum number of concurrent requests that the server has recently received. There is no relation between the number
of worker processes and the current number of sessions. There can be many more worker processes than active sessions
and there can be many more active sessions than worker processes — it all depends on the application behavior.
Note also that CSP worker and server processes do not consume a license. The license is associated with the CSP session.
No, the license is associated with the CSP session. CSP processes themselves, such as CSP worker and server processes,
do not consume licenses.
A variable or expression can be incorporated into the page at runtime using “#(var)# ” or “#(expression)# ”. For example:
#(name)#, where name has been set
#($G(%request.Get(“Username”)))#, retrieves Username from the URL
#(2+7+3)#, displays 12 on the Webpage
The syntax “#()# ” replaces the expression inside the parenthesis with its runtime value. The syntax “##()## ” replaces the
variable or expression with its value when the page is compiled.
To illustrate the difference, place the following code sample inside a CSP page:
Runtime: #($P($H,",",2))#
Compile Time: ##($P($H,",",2))##
Open the page in a browser and refresh it a few times. Notice that the Runtime value changes each time the page is refreshed.
The Compile Time value retains the time the page was compiled; it changes only when the page is recompiled.
The #include directive allows you to include in your page any text: JavaScript, html, plain text, CSP.
The <csp:include> tag includes a properly formatted CSP page; it uses ServerSideRedirect to insert this page and then
return to processing the original page.
By default, the browser automatically compiles CSP pages when it loads them, if the pages have changed (based on their
timestamp). You can also manually compile your CSP pages in Studio or from the Terminal. In either case, you can control
whether to keep the generated source code.
zn "<namespace>"
Note: The “ k” flag tells the compiler to “Keep generated source code. ”
ObjectScript
Do $System.OBJ.ShowFlags()
ObjectScript
Do $System.OBJ.ShowQualifiers()
There are a few utility methods I call all the time. How can I avoid using ##class(Package.Class).method()?
Place these methods in a particular class and have your CSP page inherit from that class. Your CSP page can then access
these methods by using dot syntax. To do this, use the <csp:class> tag as follows:
<csp:class super="%CSP.Page,App.Utils">
A private page can only be viewed when called from another CSP page using the proper token. A private page cannot be
bookmarked or arrived at by simply typing its URL into the browser. To set a page as private, use the <csp:class> tag
as follows:
<csp:class private=1>
I have a set of JavaScript functions and a header that I want on all my pages. How should I include this?
<!--#include file="mystuff.inc"-->
This is a textual include, new in Caché 5. The text of the file is automatically included in the page before the page is compiled.
Therefore, you can include code that needs to be compiled such as #()# variables, the results of <csp:query> queries
and runat="server" code.
I want to use the <csp:search> tag, but I want to allow the user to search on fields other than ID. Can I do
this?
The <csp:search> tag has a WHERE attribute which allows you to specify a comma-delimited list of fields on which to
search.
There are several other attributes you can use to customize your <csp:search> functionality. See the <CSP:SEARCH>
entry of the CSP HTML Tag Reference guide.
Configuration
How do I configure a CSP application to serve pages in a subdirectory?
I want my users to load my CSP application by pointing their browsers to: http://mydomain.com/banking/login.csp;
I do not want /csp/ in the URL. How can I do this?
Use the Management Portal to set up a new CSP application called, for example, /myapp. This process is described in the
“Defining a New Application on the CSP Server” section of the CSP Configuration chapter of Using Caché Server Pages
(CSP).
I have Caché on a different machine than my web server. How can I configure this?
See the “Connecting to Remote Servers” chapter of the Caché System Administration Guide.
Miscellaneous
Can I use frames in my CSP application?
Yes. However, you should name the frameset page with a .csp extension. If you create a page called index.html and load
CSP pages into the left and right frames, you use two sessions and accordingly two Caché licenses, one for each CSP page.
This can cause confusion if you use the session object to store information and you also use unnecessary licenses.
If you call your frameset page index.csp, the result is a single session, which uses one license for that application. Both
CSP pages within the frames share this session and any information stored in it.
D $System.CSP.Show("/csp/user/mypage.csp")
This displays the HTTP headers, as well as the generated HTML source for the page.
• Use the Head method of the %Net.HttpRequest class.
In addition to CSP, I am running Crystal Reports which also uses a .csp extension. How can I make my Caché
Server Pages work?
Because CSP and Crystal Reports both use the .csp file extension, there is a conflict if you run both through your web
server. Whichever was installed later works, but the earlier application does not. To alleviate this conflict, configure your
web server to run one virtual directory for CSP and another for Crystal Reports.
To configure virtual directories using the Internet Services Manager:
1. From the Start menu, point to Settings, Control Panel, Administrative Tools, and then click Internet Services Manager.
2. Expand the first node, and then expand Default Web Site.
3. If CSP was installed last, right-click the Crystal virtual directory and choose Properties.
If Crystal Reports was installed last, right-click the csp virtual directory and choose Properties.
4. On the Virtual Directory tab of the Properties dialog box, click Configuration in the lower right portion of the box.
5. Click the App Mappings tab and scroll down to find the .csp mapping located near the bottom of this list.
6. If you installed CSP last, change the Executable Path for the .csp extension mapping to the location of the Crystal
Reports DLL, WSCInSAPI.dll. It is located in the WCS directory of the Crystal install directory. (For example, C:\Program
Files\Seagate Software\WCS)
If you installed Crystal Reports last, change the Executable Path for the .csp extension mapping to the location of
CSPms.dll, located in the /csp/bin directory of your Caché install directory. (For example, C:\CacheSys\CSP\bin).
7. Click OK.