Progress with user preference storage and retrieval
Dealt with a fairly complicated problem this morning, and it should be documented in detail because it's the sort of thing you can only figure out by trial and error. The problem is this:
We need to store user preferences in the eXist database, because they should be easily backed up, and they should be editable through an XQuery/XUpdate-based GUI (in the long run). By preferences here, I mean a range of different things, including user strings (labels and captions for the GUI), colours and fonts, and straightforward settings choices, such as the choice to use APA style. We've already figured out how to store CSS information in <xsl:attribute-set> nodes, then use XQuery to retrieve it formatted as a CSS file for the browser; that's a relatively simple issue, because the browser will always request the file directly, through a call to a URL which triggers a Cocoon pipeline. A more complex issue concerns strings for GUI captions etc. These are typically required DURING an XSLT transformation. An added wrinkle is that there are default values and possible user overrides, and the system needs to be able to deliver a set of values where the user overrides are chosen if they exist, but the defaults are returned if they're not.
This is the way I'm doing it:
First, I store the two sets of strings in two separate files in the database:
/db/teiJournal/settings/default/strings.xsl
/db/teiJournal/settings/user/strings.xsl
The format of these files is straight XSLT 2.0, and each file consists simply of a list of <xsl:variable> elements. Next, I create an XQuery file, getGuiStrings.xq, which can merge the two files to create one file, with user values where they exist, and default values where they don't. This is the meat of the file:
declare function f:getStrings() as element()*{
for $defV in doc('/db/teiJournal/settings/default/strings.xsl')//xsl:variable
let $varName := $defV/@name,
$userV := doc('/db/teiJournal/settings/user/strings.xsl')//xsl:variable[@name=$varName]
return if ($userV) then
$userV
else
$defV
};
(:
===================================================
DOCUMENT NODESET
---------------------------------------------------
:)
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="2.0">
{f:getStrings()}
</xsl:stylesheet>
(:
======================== END ========================
:)
Then we need a sitemap pipeline which makes it possible to access the output from this XQuery through a URL:
<map:match pattern="xsl/db/guiStrings.xsl"> <map:generate src="xq/getGuiStrings.xq" type="xquery"/> <map:serialize type="xml" /> </map:match>
Finally, the actual base XSLT file on the filesystem, which is called when producing output, must be able to import that file. That took a little figuring out; it requires the use of the cocoon:/ protocol:
<xsl:import href="cocoon:/xsl/db/guiStrings.xsl"/>
Without the Cocoon protocol, it won't work because the XSL engine will look on the filesystem instead of invoking the Cocoon pipeline.
Got this working with a simple example using the plain text rendering I built yesterday.