NaviServer Built-in Commands – 5.0.0b1
ns_adp_register - Creating Custom ADP Tags
These commands enable the definition of custom ADP tags (application-specific, XML-like tags) which are expanded whenever ADP content (.adp files or ADP strings processed via ns_adp_parse) is processed. Registering application-specific ADP tags is a means to include script-driven, application-specific content in a web page, providing an alternative to embedding scripts in ADP pages via the <% script %> syntax as described in the ADP Overview man page.
Registered tags can either be tags with content -- consisting of an opening and a closing tag -- or tags defining empty elements, where no closing tag is defined. When both the tag and the endtag are specified in the registering command, the tags must have content; when the endtag is omitted in the registering command, empty elements are defined.
Registers an ADP snippet adpstring to be included when the specified tag is encountered while parsing the ADP content. The tag argument specifies the tag that will trigger the inclusion of the parsed ADP fragment adpstring.
If the optional endtag argument is specified, the content between the opening and closing tags is replaced by the adpstring, and the enclosed content is discarded. This behavior differs from the other tag registering commands, where the enclosed content can be processed or passed to the handler.
Note: Attributes specified in the tag are not accessible when using ns_adp_registeradp. If you need to access attributes or the enclosed content, consider using ns_adp_registerproc or ns_adp_registerscript.
# Register an ADP snippet to display the current date as tag "printdate" ns_adp_registeradp printdate { The current date is: <% ns_adp_puts [ns_httptime [ns_time]] %> }
Usage example in an ADP page:
... header ... <p>This is my page.</p> <printdate> ... footer ...
Registers a Tcl procedure to be evaluated when the given tag is encountered in ADP content. The procedure proc will be called with a variable number of arguments, one for each attribute value provided in the tag. If the endtag argument is specified, the procedure will also receive a final argument containing the content enclosed between the tags. No evaluation of the enclosed content is performed; it is passed as a single text block.
When the procedure is invoked, its result replaces the tag (and the enclosed content, if endtag is specified) in the output.
# Define the tag handler proc geturltag {args} { # Extract the URL from the last argument set url [lindex $args end] # Extract any options (attributes) set options [lrange $args 0 end-1] # Perform an HTTP GET request set response [ns_http run {*}$options $url] # Return the response body dict get $response body } # Register the tag handler with an opening and closing tag ns_adp_registerproc geturl /geturl geturltag
Usage example in an ADP page:
... header ... <p>This is my page. <geturl -timeout 3s >https://example.com/hello.html</geturl> <p>Next paragraph.</p> ... footer ...
Note: When using this registration command, the attribute values specified in the tag are passed as arguments to the Tcl procedure. The attribute names are not passed, only their values, and they are provided in the order they appear in the tag.
proc ::reporttags {args} { return $args } ns_adp_registerproc reporttags ::reporttags ns_adp_parse -string {<reporttags CamelCase x=X A=a>} # Returns: CamelCase X a
Registers a Tcl procedure proc to be evaluated when the given tag is encountered. This command provides access to both the attribute names and values, as well as the enclosed content if an endtag is specified.
When the tag is registered with an endtag, the Tcl proc is called with two arguments: first, the enclosed content as a string, and second, the ns_set with the tag's attributes. When registered without an end tag, the proc is called with a single argument (the ns_set with the arguments).
When the tag is parsed, it is replaced by the result of the registered proc.
# Define the tag handler proc onday {string attributes} { # Get the 'day' attribute set day [ns_set get -nocase $attributes day] # Check if the current date matches the specified day if {[ns_fmttime [ns_time] "%m/%d"] eq $day} { return $string } } # Register the tag handler with an opening and closing tag # ns_adp_registerscript onday /onday onday
Usage example in an ADP page:
... header ... <p>This is my page.</p> <onday day="12/25"><p>Merry Christmas and a Happy New Year</p></onday> <p>Next paragraph.</p> ... footer ...
Note: The major differences between ns_adp_registerscript and ns_adp_registerproc are the calling conventions and the passing of attributes:
With ns_adp_registerproc, only the values of the attributes are passed to the procedure, without the attribute names.
With ns_adp_registerscript, both attribute names and values are available, passed as an ns_set.
Attributes without a value (no equals sign) inside the tag are assigned their name as the value in the ns_set.
This command is useful when you need to access both attribute names and values, or when you need to process the enclosed content.
proc ::reporttags {attributes} { # Convert the ns_set to a list for display ns_set array $attributes } ns_adp_registerscript reporttags ::reporttags ns_adp_parse -string {<reporttags CamelCase x=X A=a>} # Returns: CamelCase CamelCase x X A a
The following is a simple way of handling conditional content in ADPs:
# Store content in a global variable proc remember {input tagset} { set tagname [ns_set get -nocase $tagset name] if {$tagname eq ""} { set ::_adp_memory($tagname) $input return "" } else { return $input } } # Retrieve and parse stored content proc recall {name} { if {[info exists ::_adp_memory($name)]} { set parsecommand [list ns_adp_parse -string] lappend parsecommand $::_adp_memory($name) ns_adp_puts -nonewline [uplevel $parsecommand] } else { ns_log Error "[ns_adp_argv 0]: Unable to recall" } }
If the preceding Tcl has been executed (perhaps during server startup), then the following ADP fragment displays the results of a database query in a table, or shows "No rows in result." if there are no rows:
<% set rows {} set db [ns_db gethandle] ns_db exec "select somecolumn from sometable" set row [ns_db bindargs $db] while {[ns_db getrow $db $row]} { lappend rows [ns_set get $row "somecolumn"] } ns_db releasehandle $db %> <remember name="has-rows_header"> <table> </remember> <remember name="hasrows_rows"> <tr><td><%=$column%></td></tr> </remember> <remember name="hasrows_footer"> </table> </remember> No rows in result. <remember name="norows"> <% if {[llength $rows] > 0} { recall "hasrows_header" foreach row $rows { set column $row recall "hasrows_rows" } recall "hasrows_footer" } else { recall "norows" } %>
In this example, the `remember` procedure stores different parts of the output, and the `recall` procedure retrieves and processes them conditionally based on whether there are rows from the database query.