Among the many unique features in Deki Wiki, is the ability to extend the platfrom in any programming language of your choosing. This is possible, because Deki Wiki, at its heart, is a distributed application platform built on web-services. All you need to create extensions in other programming languages is to describe your extension in XML and then support the DekiScript XML schema.
The first step is to create the XML manifest for your extension. Here is a sample:
<extension>
<title>My Extension</title>
<description>This extension contains my functions.</description>
<namespace>my</namespace>
<function>
<name>print</name>
<uri>http://my-server/my-print</uri>
<description>Writes a string or "Hello World!" if no parameter is provided.</description>
<param name="text" type="str" optional="true">Text to write (default: "Hello World!")</param>
<return type="str" />
</function>
</extension>
This manifest shows that the extension contains one function called print, which takes one optional parameter text that must be of type str. All functions in this extension have the prefix my. So to invoke the print function, we have to type my.print. To execute the function, Deki Wiki must POST the parameters to http://my-server/my-print using DekiScript XML.
The smallest, valid extension is give below. Anything less and Deki Wiki will not be able to use it.
<extension>
<title>My Extension</title>
<function>
<name>print</name>
<uri>http://my-server/my-print</uri>
</function>
</extension>
This extension has a title and single function print. The extension namespace is assumed to be the empty namespace. Also, the function's return type is assumed to any.
Since the schema is recursive, it's usually easier to understand the structure of valid documents by looking at some examples. The following table shows DekiScript expressions and their corresponding XML encoding side-by-side.
| DekiScript | XML |
null | <value type="nil" /> |
true | <value type="bool">true</value> |
1.5 | <value type="num">1.5</value> |
"Hello \"World!!!\"" | <value type="str">Hello World!!!</value> |
| NOTE: URI literals do not exist in DekiScript. A URI can only be obtained through evaluation. This limitation is due to security constraints. http://localhost | <value type="uri">http://localhost</value> |
{ }
| <value type="map" /> |
{ first: "text", second: 1.23 }
| <value type="map">
<value key="first" type="str">text</value>
<value key="second" type="num">1.23</value>
</value> |
[ ] | <value type="list" /> |
[ "text", 1.23 ] | <value type="list">
<value key="#" type="str">text</value>
<value key="#" type="num">1.23</value>
</value> |
| NOTE: XML literals do not exist in DekiScript. An XML document can only be obtained through evaluation. This limitation is due to security constraints. <doc>
<elem attr="value">content</elem>
</doc>
| <value type="xml">
<doc>
<elem attr="value">content</elem>
</doc>
</value> |
Deki Wiki invokes extension functions through a HTTP POST request operation. The encoding of the parameters depends on the notation used to make the invocation. DekiScript supports two kinds of invocations: by-position and by-name. The difference lies in how the parameter collection is created.
The following function call uses by-position parameters.
my.print( "hi!" )
While this function call uses by-name parameters.
my.print{ text: "hi!" }
By-position parameters get encoded into a list, while by-name parameters are encoded into a map. This is the only difference between the two invocation notations. It is assumed that the recipient of the invocation can determine the meaning of parameters for either notation.
The return value is always encoded as a list, although, Deki Wiki only uses the first entry.
Here is a sample request using the extension manifest provided at the top of this document. We assume that the parameters where provide by-position.
POST /my-print HTTP/1.0
Host: my-server
Content-Length: 123
Content-Type: application/xml; charset=UTF-8
<value type="list">
<value key="#" type="str">Hi!</value>
</value>
The server response follows a similar pattern:
HTTP/1.0 200 OK
Date: Mon, 23 May 2005 22:38:34 GMT
Server: Apache/1.3.3.7 (Unix) (Red-Hat/Linux)
Content-Length: 438
Content-Type: application/xml; charset=UTF-8
<value type="list">
<value key="#" type="str">Print response: Hi!</value>
</value>
NOTE: Deki Wiki uses the generic "application/xml" content-type in its invocation request and expects a response with the same content-type back. This is an implementation oversight and will be corrected in the future. However, for backwards compatibility, Deki Wiki will continue to support this overly generic request/response pattern.
This page describes how to create an extension for Deki Wiki in any programming language. All that is needed is an XML extension manifest to describe the functions present in the extension. The manifest can be written by hand or can be generated by a tool. When registering a remote service, Deki Wiki does a GET request on the supplied URI to fetch the manifest and process it. After that, the functions are available for invocation. Deki Wiki converts DekiScript expressions into their XML representation and then does a POST operation on the function's URI. The response is decoded in a similar fashion. All response are simply inlined into the invoking document, replacing the function call-site.
The extension manifest is fetched by Deki Wiki using a GET request when a remote service is registered. Below is the extension manifest schema given in the compact RelaxNG syntax.
grammar {
start = element extension {
# full name of the extension (e.g. MindTouch Dapper Extension)
element title { text }
# (optional) copyright notice (e.g. Copyright MindTouch 2008)
& element copyright { text }?
# (optional) description of the functionality contained in this extension
# (e.g. This extension contains functions for embedding and processing dapps)
& element description { text }?
# (optional) uri to the license description page
& element uri.license { text }?
# (optional) uri to the help documentation page
& element uri.help { text }?
# (optional) uri to extension logo (100x75px)
& element uri.logo { text }?
# (optional) prefix for all extension functions (e.g. dapper)
& element namespace { text }?
# (optional) short title for extension; used by the extension dialog (e.g. Dapper)
& element label { text }?
# (optional) requirements section listing the prerequisites for running this extension (Deki Wiki 1.9.0b and later)
& requires?
# (optional) configuration section listing the config settings (Deki Wiki 1.9.0b and later)
& config?
# one or more functions
& function+
}
requires = element requires {
attribute host { text }
}
config = element config {
param*
}
function = element function {
# (optional) transform
attribute transform { text }?
# name of the function (e.g. table; combined with the extension prefix, this becomes dapper.table)
& element name { text }
# web-service uri corresponding to the function
& element uri {
attribute protocol { string "dekiscriptxml" | string "xmlrpc" | string "get" }?,
text
}
# (optional) description of the purpose of the function
# (e.g. This function embeds the outcome of a dapp as a table)
& element description { text }?
# (optional) access restrictions for function (NOTE: not yet used)
& element access { string "public" | string "internal" | string "private" }?
# (optional) return type of function invocation
& element return { type }?
# function parameters
& param*
}
param = element param {
# parameter name
attribute name { text }
# parameter type
& type
# (optional) indicator if parameter is optional or not
& attribute optional { string "true" | string "false" }?
# (optional) parameter description (e.g. name of dapp to execute)
& text
}
type = attribute type {
string "any"
| string "nil"
| string "bool"
| string "num"
| string "str"
| string "uri"
| string "map"
| string "list"
| string "xml"
}
}
The XML schema is used by Deki Wiki to encode the parameters passed to the function invocation when rendering a wiki page. Similarly, the return value is decoded by Deki Wiki and then embedded into the wiki page.
Below is the schem for DekiScript XML in compact RelaxNG syntax.
grammar {
start = value
value = element value {
attribute key {
string "#"
| xsd:string { minLength = "1" }
}?,
(
attribute type { string "nil" }
) | (
attribute type { string "bool" },
xsd:boolean
) | (
attribute type { string "num" },
xsd:double
) | (
attribute type { string "str" },
text
) | (
attribute type { string "uri" },
xsd:anyURI
) | (
attribute type { string "map" },
value*
) | (
attribute type { string "list" },
value*
) | (
attribute type { string "xml" },
xml
)
}
xml = element * {
(
attribute * { text }
| text
| xml
)*
}
}