Merge github.com:rsoulliere/Evergreen-DocBook
authorSteve Sheppard <sheppards.email@gmail.com>
Fri, 27 Aug 2010 21:39:51 +0000 (17:39 -0400)
committerSteve Sheppard <sheppards.email@gmail.com>
Fri, 27 Aug 2010 21:39:51 +0000 (17:39 -0400)
26 files changed:
1.6/admin/admin-intro.xml
1.6/admin/requirements-configuration.xml [new file with mode: 0644]
1.6/admin/serveradministration.xml
1.6/admin/sip.xml [new file with mode: 0644]
1.6/admin/z3950.xml [new file with mode: 0644]
1.6/development/customize_opac.xml [new file with mode: 0644]
1.6/development/development_intro.xml [new file with mode: 0644]
1.6/development/intro.xml [deleted file]
1.6/development/json.xml [new file with mode: 0644]
1.6/development/supercat.xml [new file with mode: 0644]
1.6/intro/releasenotes.xml
1.6/media/add_holdings-1.png [new file with mode: 0644]
1.6/media/add_holdings-2.png [new file with mode: 0644]
1.6/media/add_holdings-3.png [new file with mode: 0644]
1.6/media/add_holdings-4.png [new file with mode: 0644]
1.6/media/add_holdings-5.png [new file with mode: 0644]
1.6/media/small_logo_white.jpg
1.6/opac/intro.xml [deleted file]
1.6/opac/opac_intro.xml [new file with mode: 0644]
1.6/opac/search_URL.xml [new file with mode: 0644]
1.6/opac/searchresults.xml
1.6/reports/report-intro.xml [new file with mode: 0644]
1.6/reports/report-startingreporter.xml [new file with mode: 0644]
1.6/root.xml
1.6/stafftasks/cataloging.xml
1.6/stafftasks/stafftasks_intro.xml [new file with mode: 0644]

index cd030fb..7316f99 100644 (file)
@@ -1,6 +1,6 @@
-<partintro xmlns:xl="http://www.w3.org/1999/xlink">
-       <para>This part of the documentation is intended for Evergreen administrators and requires root access to your Evergreen server(s) and administrator access to the Evergreen staff client. It deals with maintaining servers, installation, upgrading, and configuring both system wide and local library settings. 
-       Some sections require understanding of linux system administration while others require an understanding of your system hierarchy of locations and users. Many procedures explained in the following 
-       chapters are accomplished with linux commands run from the terminal without a Graphical User Interface (GUI).</para>
-       <para>In order to accomplish some of the tasks, prerequisite knowledge or experience will be required and you may need to consult system administration documentation for your specific linux distribution if you have limited linux system experience. A vast ammount of free resources can be found on the on the web for various experinece levels. You might also consider consulting <link xl:href="http://www.postgresql.org/docs/">PostgreSQL</link> and <link xl:href="http://httpd.apache.org/docs/">Apache</link> documentation for a greater understanding of the software stack on which Evergreen is built.</para>
-</partintro>
+<partintro xmlns:xl="http://www.w3.org/1999/xlink" xml:id="admin_intro">\r
+       <para>This part of the documentation is intended for Evergreen administrators and requires root access to your Evergreen server(s) and administrator access to the Evergreen staff client. It deals with maintaining servers, installation, upgrading, and configuring both system wide and local library settings. \r
+       Some sections require understanding of linux system administration while others require an understanding of your system hierarchy of locations and users. Many procedures explained in the following \r
+       chapters are accomplished with linux commands run from the terminal without a Graphical User Interface (GUI).</para>\r
+       <para>In order to accomplish some of the tasks, prerequisite knowledge or experience will be required and you may need to consult system administration documentation for your specific linux distribution if you have limited linux system experience. A vast ammount of free resources can be found on the on the web for various experinece levels. You might also consider consulting <link xl:href="http://www.postgresql.org/docs/">PostgreSQL</link> and <link xl:href="http://httpd.apache.org/docs/">Apache</link> documentation for a greater understanding of the software stack on which Evergreen is built.</para>\r
+</partintro>\r
diff --git a/1.6/admin/requirements-configuration.xml b/1.6/admin/requirements-configuration.xml
new file mode 100644 (file)
index 0000000..c31a4d1
--- /dev/null
@@ -0,0 +1,82 @@
+<?xml version="1.0" encoding="utf-8"?>\r
+<chapter xml:id="requirements" xmlns="http://docbook.org/ns/docbook" version="5.0" xml:lang="EN"\r
+    xmlns:xi="http://www.w3.org/2001/XInclude" xmlns:xlink="http://www.w3.org/1999/xlink">\r
+    <info>\r
+        <title>System Requirements and Hardware Configurations</title>\r
+    </info>\r
+\r
+    <para>Evergreen is extremely scalable and can serve the need of a large range of libraries. The specific requirements and configuration of your system should be determined based on your \r
+       specific needs of your organization or consortium.</para>\r
+    \r
+       <section xml:id="requirements_server">\r
+               <info>\r
+                   <title>Server Minimum Requirements</title>\r
+               </info> \r
+                        <para>The following are the base requirements setting Evergreen up on a test server:</para>\r
+                       <itemizedlist>\r
+                               <listitem><para>An available desktop, server or virtual image</para></listitem>\r
+                               <listitem><para>512MB RAM</para></listitem>                     \r
+                               <listitem><para>Linux Operating System</para></listitem>\r
+                       </itemizedlist>\r
+                       <tip><para>Debian and Ubuntu are the most widely used Linux distributions for installing Evergreen and most development takes place on Debian based systems. If you are new \r
+                       to Linux, it is strongly recommended that you install Evergreen on the latest stable server edition of Debian (<ulink url="http://www.debian.org/">http://www.debian.org/</ulink>)\r
+                       or Ubuntu 10.04 Server(<ulink url="http://www.ubuntu.com/">http://www.ubuntu.com/</ulink>) since the installation instructions have been tested on these distributions. Debian and                              Ubuntu are free distributions of Linux.</para></tip>\r
+               </section>        \r
+               <section xml:id="hardwareconfigurations">\r
+                       <info>\r
+                               <title>Server Hardware Configurations and Clustering</title>\r
+                       </info>\r
+                       <para>As apparent in the previous section, the base hardware requirements for running a functional Evergreen server are extremely minimal. It is also possible\r
+                       to scale up your evergreen configuration to be spread your Evergreen resources and services over several or even many servers in a clustered approach for the purpose \r
+                       of system redundancy, load balancing and downtime reduction. This allows very large \r
+                       consortia to share one Evergreen system with hundreds of libraries with millions of records and millions of users, making the scalability of Evergreen almost infinite.</para>\r
+                       <para>Here are some example scenarios for networked server configurations:</para>\r
+                       <itemizedlist>\r
+                               <listitem><para>A small library library with 1 location, under 25,000 items and a few thousand users could easily run Evergreen on a single server \r
+                               (1 machine).</para></listitem>\r
+                               <listitem><para>A college or university with 1 million items and 20,000 users could run an Evergreen system using several servers balancing the load on their \r
+                               system by spreading services over multiple servers. It should host their PostgreSQL database on a separate server. They could also cluster the Evergreen services       \r
+                               strategically to minimize or eliminate any necessary downtown when upgrading Evergreen or other server software. Moreover, system redundancy will reduce the chance of \r
+                               unplanned catastrophic downtime caused by system failure since Evergreen will be running over several machines.</para></listitem>                       \r
+                               <listitem><para>A large library consortium with several public library systems and/or academic libraries with millions of users and items could run an Evergreen \r
+                               system over many servers with clusters for Evergreen services as well as a cluster for the Postgresql Database.</para></listitem>\r
+                       </itemizedlist>\r
+                       <para>The key to Evergreen scalability is in the openSRF configuration files <filename>/openils/conf/opensrf.xml</filename> and \r
+                       <filename>/openils/conf/opensrf_core.xml</filename>. \r
+                       By configuring these files, an administrator could cluster evergreen services over multiple hosts, change the host running a specific service \r
+                       or change the host of the postgreSQL database.</para> \r
+\r
+                       <note><para>The default configuration of Evergreen in the installation instructions assumes a single <emphasis>localhost</emphasis> server setup. For more complex \r
+                       multi-server clustered configurations, some server administration and database administration experience or knowledge will be required.</para></note>           \r
+               </section>    \r
+       \r
+               <section xml:id="requirements_staffclient">\r
+                       <info>\r
+                           <title>Staff Client Requirements</title>\r
+                       </info>\r
+       \r
+                       <para> Staff terminals connect to the central database using the Evergreen staff client, available for download from \r
+                       <link xlink:href="http://www.open-ils.org/downloads.php">The Evergreen download page</link>. The staff client must be installed on each staff workstation and requires at \r
+                       minimum: </para>\r
+                       <itemizedlist>\r
+                               <listitem><para>Windows (XP, Vista, or 7), Mac OS X, or Linux operating system</para></listitem>\r
+                               <listitem><para>a reliable high speed internet connection</para></listitem>\r
+                               <listitem><para>512Mb of RAM</para></listitem>\r
+                       </itemizedlist>\r
+                       <simplesect>\r
+                               <info>\r
+                                       <title>Barcode Scanners</title>   \r
+                               </info>\r
+                                       <para>Evergreen will work with virtually any barcode scanner – if it worked with your legacy system it should work on Evergreen.</para>\r
+                       </simplesect>\r
+                       <simplesect>\r
+                               <info>\r
+                                       <title>Printers</title>\r
+                               </info>\r
+                               <para>Evergreen can use any printer configured for your terminal to print receipts, check-out slips, holds\r
+                               lists, etc. The single exception is spine label printing, which is still under development. Evergreen\r
+                               currently formats spine labels for output to a label roll printer. If you do not have a roll printer\r
+                                manual formatting may be required.  For more on configuring receipt printers, see <link linkend="lsa-printer">Printer Settings</link>.</para>    \r
+                       </simplesect>    \r
+               </section>\r
+</chapter>\r
index 9891731..6a44d49 100644 (file)
@@ -1929,8 +1929,8 @@ xmlns:xl="http://www.w3.org/1999/xlink" version="5.0" xml:id="serveradministrati
                                <title>Deleting Billing Types</title>\r
                                <step><para>Check the checkbox of the billing type(s) you wish to delete.</para></step>\r
                                <step><para>Click <guibutton>Delete Selected</guibutton>.\r
-                                       <warning><para>The selected billing types will be deleted without a \r
-                                       verification alert.</para></warning>\r
+                                       <caution><para>The selected billing types will be deleted without a \r
+                                       verification alert.</para></caution>\r
                                </para></step>\r
                        </procedure>\r
                        <procedure>\r
@@ -1968,8 +1968,8 @@ xmlns:xl="http://www.w3.org/1999/xlink" version="5.0" xml:id="serveradministrati
                                <step><para>Check the check box(es) next to the circulation modifiers(s) you wish to \r
                                delete.</para></step>\r
                                <step><para>Click <guibutton>Delete Selected</guibutton> near the top of the page.\r
-                               <warning><para>The selected circulation modifiers will be deleted without a \r
-                               verification alert.</para></warning>\r
+                               <caution><para>The selected circulation modifiers will be deleted without a \r
+                               verification alert.</para></caution>\r
                                </para></step>\r
                        </procedure>\r
                        <procedure>\r
diff --git a/1.6/admin/sip.xml b/1.6/admin/sip.xml
new file mode 100644 (file)
index 0000000..8877939
--- /dev/null
@@ -0,0 +1,525 @@
+<?xml version="1.0" encoding="utf-8"?>\r
+<chapter xml:id="sipserver" xmlns="http://docbook.org/ns/docbook" version="5.0" xml:lang="EN"\r
+    xmlns:xi="http://www.w3.org/2001/XInclude" xmlns:xlink="http://www.w3.org/1999/xlink">\r
+       <info>\r
+               <title>SIP Server</title>\r
+       </info>\r
+       <para>SIP, standing for Standard Interchange Protocol, was developed by the 3M corporation to be a common protocol for data transfer between ILS' \r
+       (referred to in SIP as an <emphasis>ACS</emphasis>, or <emphasis>Automated Circulation System</emphasis>) and a third party device. Originally, the protocol was developed for \r
+       use with 3M SelfCheck (often abbreviated SC, not to be confused with Staff Client) systems, but has since expanded to other companies and devices. It is now common to find \r
+       SIP in use in several other vendors' SelfCheck systems, as well as other non-SelfCheck devices. Some examples include:</para>\r
+       <itemizedlist>\r
+               <listitem>Patron Authentication (computer access, subscription databases)</listitem>\r
+               <listitem>Automated Material Handling (AMH) - The automated sorting of items, often to bins or book carts, based on shelving location or other programmable criteria</listitem>\r
+       </itemizedlist>\r
+   \r
+       <section xml:id="Installing_SIP_Server">\r
+               <info>\r
+               <title>Installing the SIP Server</title>\r
+               </info>\r
+                 <para>This is a rough intro to installing the SIP server for Evergreen.</para>\r
+               <simplesect xml:id="Gettingthecode">\r
+                           <title>Getting the code</title>\r
+                               <para>Current SIP code lives at github:</para>\r
+                               <screen>cd /opt</screen>\r
+                               <screen>git clone git://github.com/atz/SIPServer.git SIPServer</screen>\r
+                               <para>Or use the old style:</para>\r
+                               <screen>$ cd /opt</screen>\r
+                               <screen>$ sudo cvs -d:pserver:anonymous@openncip.cvs.sourceforge.net:/cvsroot/openncip login</screen>\r
+                               <screen># when prompted for the CVS password, just hit Enter (sudo password may be req'd)</screen>\r
+                               <screen>$ sudo cvs -z3 -d:pserver:anonymous@openncip.cvs.sourceforge.net:/cvsroot/openncip co -P SIPServer</screen>\r
+                                                        \r
+               </simplesect>\r
+               <simplesect xml:id="Configuring_Server">\r
+                       <title>Configuring the Server</title>\r
+                       <procedure>\r
+                       <step>\r
+                               <para>Type the following commands from the command prompt:</para>\r
+                               <screen>$ sudo su opensrf</screen>\r
+                               <screen>$ cd /openils/conf</screen>\r
+                               <screen>$ cp oils_sip.xml.example oils_sip.xml</screen>\r
+                       </step>\r
+                       <step>\r
+                               <para>Edit oils_sip.xml. Change the commented out &lt;server-params&gt; section to this:</para>\r
+                               <screen>&lt;server-params</screen>\r
+                               <screen>min_servers='1'</screen>\r
+                               <screen>min_spare_servers='0'</screen>\r
+                               <screen>max_servers='25'</screen>\r
+                               <screen>/&gt;</screen>\r
+                       </step>\r
+                       <step>\r
+                               <para> max_servers will directly correspond to the number of allowed SIP clients. Set the number accordingly, but bear in mind that too many connections can \r
+                               exhaust memory. On a 4G RAM/4 CPU server (that is also running evergreen), I would recommend not exceeding 100 SIP client connections, \r
+                               give or take.</para>                            \r
+                       </step>\r
+                       </procedure>             \r
+               </simplesect>\r
+               <simplesect xml:id="Adding_SIP_users">\r
+                       <title>Adding SIP Users</title>\r
+                       <procedure>\r
+                               <step>\r
+                                       <para>Type the following commands from the command prompt:</para>\r
+                                       <screen>$ sudo su opensrf</screen>\r
+                                       <screen>$ cd /openils/conf</screen>\r
+                                       <screen>$ cp oils_sip.xml.example oils_sip.xml</screen>\r
+                               </step>\r
+                               <step>\r
+                                       <para> in the &lt;accounts&gt; section, add SIP client login information. Make sure that all &lt;logins&gt; use the same institution attribute, and make \r
+                                       sure the institution is listed in &lt;institutions&gt;. All attributes in the &lt;login&gt; section will be used by the SIP client.</para>\r
+                               \r
+                               </step>\r
+                               <step>\r
+                                       <para> In Evergreen, create a new profile group called SIP. This group should be a sub-group of Users (not Staff or Patrons). \r
+                                       Set Editing Permission as <emphasis>group_application.user.sip_client</emphasis> and give the group the following permissions:</para>\r
+                                       <literallayout>\r
+                                       COPY_CHECKIN\r
+                                       COPY_CHECKOUT\r
+                                       RENEW_CIRC      \r
+                                       VIEW_CIRCULATIONS\r
+                                       VIEW_COPY_CHECKOUT_HISTORY      \r
+                                       VIEW_PERMIT_CHECKOUT\r
+                                       VIEW_USER\r
+                                       VIEW_USER_FINES_SUMMARY\r
+                                       VIEW_USER_TRANSACTIONS\r
+                                       </literallayout>\r
+                                       <para>OR use SQL like:</para>\r
+                                       <screen>INSERT INTO permission.grp_tree (id,name,parent,description,application_perm)</screen> \r
+                                       <screen>VALUES (8, 'SIP', 1, 'SIP2 Client Systems', 'group_application.user.sip_client');</screen>\r
+                                       <screen></screen>\r
+                                       <screen>INSERT INTO permission.grp_perm_map (grp,perm,depth) </screen>\r
+                                       <screen>VALUES (8,15,0),(8,16,0),(8,17,0),(8,31,0),(8,32,0),(8,48,0),(8,54,0),(8,75,0),(8,82,0);</screen>\r
+                                       \r
+                                       <para>Verify:</para>\r
+                                       <screen>SELECT *</screen>\r
+                                       <screen>FROM permission.grp_perm_map JOIN permission.perm_list ON</screen> \r
+                                       <screen>permission.grp_perm_map.perm=permission.perm_list.id</screen> \r
+                                       <screen>WHERE grp=8;</screen>\r
+                                       \r
+                                       <para>Keep in mind that the id (8) may not necessarily be available on your system.</para>                              \r
+                               </step>\r
+                               <step>\r
+                                       <para>For each account created in the &lt;login&gt; section of oils_sip.xml, create a user (via the staff client user editor) that has the same username \r
+                                       and password and put that user into the SIP group.</para>\r
+                                       <note><para>The expiration date will affect the SIP users' connection, you might want to make a note of \r
+                                       this somewhere. </para></note>  \r
+                               </step>\r
+                       </procedure>             \r
+               </simplesect>\r
+               <simplesect xml:id="Running_SIP_server">\r
+                       <title>Running the server</title>\r
+                               <para>To start the SIP server type the following commands from the command prompt:</para>\r
+                               <screen>$ sudo su opensrf</screen>\r
+                               <screen>$ oils_ctl.sh -d /openils/var/run -s /openils/conf/oils_sip.xml -a [start|stop|restart]_sip</screen>    \r
+               </simplesect>\r
+               <simplesect xml:id="Logging-SIP">\r
+                       <title>Logging-SIP</title>\r
+                       <simplesect>\r
+                               <title>Syslog</title>\r
+                               <para>It is useful to log SIP requests to a separate file especially during initial setup by modifying your syslog config file.</para>\r
+                               <procedure>\r
+                                       <step>\r
+                                               <para>Edit syslog.conf.</para>\r
+                                               <screen>$ sudo vi /etc/syslog.conf  # maybe /etc/rsyslog.conf</screen>  \r
+                                       </step>\r
+                                       <step>\r
+                                               <para>Add this:</para>\r
+                                               <screen>local6.*                -/var/log/SIP_evergreen.log</screen>    \r
+                                       </step>\r
+                                       <step>\r
+                                               <para>Syslog expects the logfile to exist so create the file.</para>\r
+                                               <screen>$ sudo touch /var/log/SIP_evergreen.log</screen>        \r
+                                       </step>\r
+                                       <step>\r
+                                               <para>Restart sysklogd.</para>\r
+                                               <screen>$ sudo /etc/init.d/sysklogd restart</screen>    \r
+                                       </step>\r
+                               </procedure>            \r
+                       </simplesect>\r
+                       <simplesect>\r
+                               <title>Syslog-NG</title>\r
+                               \r
+                               <procedure>\r
+                                       <step>\r
+                                               <para>Edit logging config.</para>\r
+                                               <screen>sudo vi /etc/syslog-ng/syslog-ng.conf</screen>  \r
+                                       </step>\r
+                                       <step>\r
+                                               <para>Add:</para>\r
+                                               <screen># SIP2 for Evergreen</screen>   \r
+                                               <screen>filter    f_eg_sip { level(warn, err, crit) and facility(local6); };</screen>   \r
+                                               <screen>destination eg_sip { file("/var/log/SIP_evergreen.log"); };</screen>    \r
+                                               <screen>log { source(s_all); filter(f_eg_sip); destination(eg_sip); };</screen> \r
+                                       </step>\r
+                                       <step>\r
+                                               <para>Syslog-ng expects the logfile to exist so create the file.</para>\r
+                                               <screen>$ sudo touch /var/log/SIP_evergreen.log</screen>        \r
+                                       </step>\r
+                                       <step>\r
+                                               <para>Restart syslog-ng</para>\r
+                                               <screen>$ sudo /etc/init.d/syslog-ng restart\r
+                                               </screen>       \r
+                                       </step>\r
+                               </procedure>            \r
+                       </simplesect>\r
+               </simplesect>   \r
+               <simplesect xml:id="Testing_SIP_Connection">\r
+                       <title>Testing Your SIP Connection</title>\r
+                       <itemizedlist>\r
+                               <listitem>\r
+                                       <para>In the top level CVS checkout of the SIPServer code.</para>\r
+                                       <screen>$ cd SIPServer/t</screen>\r
+                               </listitem>\r
+                               <listitem>\r
+                                       <para> Edit SIPtest.pm, change the $instid, $server, $username, and $password variables. This will be enough to test connectivity. \r
+                                       To run all tests, you'll need to change all the variables in the Configuration section.</para>\r
+                                       <screen>$ PERL5LIB=../ perl 00sc_status.t</screen>\r
+                                       <para>This should produce something like:</para>\r
+                                       <screen>1..4</screen>\r
+                                       <screen>ok 1 - Invalid username</screen>\r
+                                       <screen>ok 2 - Invalid username</screen>\r
+                                       <screen>ok 3 - login</screen>\r
+                                       <screen>ok 4 - SC status</screen>\r
+                               </listitem>     \r
+                               <listitem>\r
+                                       <para> Don't be dismayed at “Invalid Username”. That's just one of the many tests that are run.</para>\r
+                               </listitem>                                             \r
+                       </itemizedlist>\r
+               </simplesect>\r
+               <simplesect xml:id="SIP-More_Testing">\r
+                       <title>More Testing</title>\r
+                       <procedure>\r
+                               <step>\r
+                                       <para>Once you have opened up either the SIP OR SIP2 ports to be accessible from outside you can do some testing via telnet. You can try this with localhost \r
+                                       if you so wish, but we want to prove that SIP2 works from non-localhost. Replace $instid, $server, $barcode, $username, and $password variables below as \r
+                                       necessary.</para>\r
+                                       <note><para>We are using 6001 here which is associated with SIP2 as per our configuration.</para></note>\r
+                                       <screen>$ telnet $server 6001</screen>\r
+                                       <screen>Connected to $server.</screen>\r
+                                       <screen>Escape character is '^]'.</screen>\r
+                                       <screen>9300CN**$username**|CO**$password**|CP**$instid**</screen>                                      \r
+                                       <para>You should get back.</para>\r
+                                       <screen>941</screen>\r
+                               </step>\r
+                               <step>\r
+                                       <para>Now just copy in the following line (with variables replaced) you don't need to hit enter, just paste!</para>\r
+                                       <screen>2300120080623    172148AO**$instid**|AA**$barcode**|AC$password|AD**$password**</screen>\r
+                                       <para>You will get back the patron information for $barcode (something similar to the what's below).</para>\r
+                                       <screen>24  Y           00120100113    170738AEFirstName MiddleName LastName|AA**$barcode**|BLY|CQY|BHUSD|BV0.00|AFOK|AO**$instid**|</screen>\r
+                                       <para>The response declares it is a valid patron <emphasis>(BLY)</emphasis> with a valid password <emphasis>(CQY)</emphasis> and shows the user's <emphasis>$name</emphasis>.</para>\r
+                               </step>\r
+                       </procedure>\r
+               </simplesect>\r
+       </section>\r
+       <section xml:id="SIP_Communication">\r
+               <info>\r
+                       <title>SIP Communication</title>\r
+               </info>\r
+               <para>SIP generally communicates over a TCP connection (either raw sockets or over telnet), but can also communicate via serial connections and other methods. In Evergreen, \r
+               the most common deployment is a RAW socket connection on port 6001.</para>\r
+               <para>SIP communication consists of strings of messages, each message request and response begin with a 2-digit <quote>command</quote> - Requests usually being an odd \r
+               number and responses usually increased by 1 to be an even number. The combination numbers for the request command and response is often referred to as a \r
+               <emphasis>Message Pair</emphasis> (for example, a 23 command is a request for patron status, a 24 response is a patron status, and the message pair 23/24 is \r
+               patron status message pair). The table in the next section shows the message pairs and a description of them.</para>\r
+               <para>For clarification, the <quote>Request</quote> is from the device (selfcheck or otherwise) to the ILS/ACS. The response is… the response \r
+               to the request ;).</para>\r
+               <para>Within each request and response, a number of fields (either a fixed width or separated with a '|' [pipe symbol] and preceeded with a 2-character field identifier) \r
+               are used. The fields vary between message pairs.</para>\r
+               <informaltable>\r
+                       <tgroup cols="4">\r
+                               <thead>\r
+                                       <row>\r
+                                               <entry>Pair</entry>\r
+                                               <entry>Name</entry>\r
+                                               <entry>Supported?</entry>\r
+                                               <entry>Details</entry>\r
+                                       </row>\r
+                               </thead>\r
+                               <tbody>\r
+                                       <row>\r
+                                               <entry>01</entry>\r
+                                               <entry>Block Patron</entry>\r
+                                               <entry>Yes</entry>\r
+                                               <entry><link linkend='SIP_Block_Patron'>01_Block_Patron</link> - ACS responds with 24 Patron Status Response</entry>\r
+                                       </row>\r
+                                       <row>\r
+                                               <entry>09/10</entry>\r
+                                               <entry>Checkin</entry>\r
+                                               <entry>Yes (with extensions)</entry>\r
+                                               <entry><link linkend='SIP_Checkin'>09/10_Checkin</link></entry>\r
+                                       </row>\r
+                                       <row>\r
+                                               <entry>11/12</entry>\r
+                                               <entry>Checkout</entry>\r
+                                               <entry>Yes (no renewals)</entry>\r
+                                               <entry><link linkend='SIP_Checkout'>11/12_Checkout</link></entry>\r
+                                       </row>\r
+                                       <row>\r
+                                               <entry>15/16</entry>\r
+                                               <entry>Hold</entry>\r
+                                               <entry>No</entry>\r
+                                               <entry><link linkend='SIP_Hold'>15/16_Hold</link></entry>\r
+                                       </row>\r
+                                       <row>\r
+                                               <entry>17/18</entry>\r
+                                               <entry>Item Information</entry>\r
+                                               <entry>Yes (no extensions)</entry>\r
+                                               <entry><link linkend='SIP_Item_Information'>17/18_Item_Information</link></entry>\r
+                                       </row>\r
+                                       <row>\r
+                                               <entry>19/20</entry>\r
+                                               <entry>Item Status Update</entry>\r
+                                               <entry>No</entry>\r
+                                               <entry><link linkend='SIP_Item_Status_Update'>19/20_Item_Status_Update</link> - Returns Patron Enable response, but doesn't make any changes in EG</entry>\r
+                                       </row>\r
+                                       <row>\r
+                                               <entry>23/24</entry>\r
+                                               <entry>Patron Status</entry>\r
+                                               <entry>Yes</entry>\r
+                                               <entry><link linkend='SIP_Patron_Status'>23/24_Patron_Status</link> - 63/64 <quote>Patron Information</quote> preferred</entry>\r
+                                       </row>\r
+                                       <row>\r
+                                               <entry>25/26</entry>\r
+                                               <entry>Patron Enable</entry>\r
+                                               <entry>No</entry>\r
+                                               <entry><link linkend='SIP_Patron_Enable'>25/26_Patron_Enable</link> - Used during system testing and validation</entry>\r
+                                       </row>\r
+                                       <row>\r
+                                               <entry>29/30</entry>\r
+                                               <entry>Renew</entry>\r
+                                               <entry>NO (maybe?)</entry>\r
+                                               <entry><link linkend='SIP_Renew'>29/30_Renew</link></entry>\r
+                                       </row>\r
+                                       <row>\r
+                                               <entry>35/36</entry>\r
+                                               <entry>End Session</entry>\r
+                                               <entry>Yes</entry>\r
+                                               <entry><link linkend='SIP_End_Session'>35/36_End_Session</link></entry>\r
+                                       </row>\r
+                                       <row>\r
+                                               <entry>37/38</entry>\r
+                                               <entry>Fee Paid</entry>\r
+                                               <entry>No</entry>\r
+                                               <entry><link linkend='SIP_Fee_Paid'>37/38_Fee_Paid</link></entry>\r
+                                       </row>\r
+                                       <row>\r
+                                               <entry>63/64</entry>\r
+                                               <entry>Patron Information</entry>\r
+                                               <entry>Yes (no extensions)</entry>\r
+                                               <entry><link linkend='SIP_Patron_Information'>63/64_Patron_Information</link></entry>\r
+                                       </row>\r
+                                       <row>\r
+                                               <entry>65/66</entry>\r
+                                               <entry>Renew All</entry>\r
+                                               <entry>No</entry>\r
+                                               <entry><link linkend='SIP_Renew_All'>65/66_Renew_All</link></entry>\r
+                                       </row>\r
+                                       <row>\r
+                                               <entry>93/94</entry>\r
+                                               <entry>Login</entry>\r
+                                               <entry>Yes</entry>\r
+                                               <entry><link linkend='SIP_Login'>93/94_Login</link> - Must be first command to Evergreen ACS (via socket) or SIP will terminate</entry>\r
+                                       </row>\r
+                                       <row>\r
+                                               <entry>97/96</entry>\r
+                                               <entry>Resend last message</entry>\r
+                                               <entry>Yes</entry>\r
+                                               <entry><link linkend='SIP_Resend'>97/96_Resend</link></entry>\r
+                                       </row>\r
+                                       <row>\r
+                                               <entry>99/98</entry>\r
+                                               <entry>SC/ACS Status</entry>\r
+                                               <entry>Yes</entry>\r
+                                               <entry><link linkend='SIP_SC_ACS_Status'>99/98_SC_and_ACS_Status</link></entry>\r
+                                       </row>\r
+                               </tbody>\r
+                       </tgroup>\r
+               </informaltable>\r
+               <simplesect xml:id="SIP_block_patron">\r
+                       <title>01 Block Patron</title>\r
+                       <para>A selfcheck will issue a Block Patron command if a patron leaves their card in a selfcheck machine or if the selfcheck detects tampering (such as attempts \r
+                       to disable multiple items during a single item checkout, multiple failed pin entries, etc).</para>\r
+                       <para>In Evergreen, this command does the following:</para>\r
+                               <itemizedlist>\r
+                                       <listitem>User alert message: 'CARD BLOCKED BY SELF-CHECK MACHINE' (this is independent of the AL 'Blocked Card Message' field).</listitem>\r
+                                       <listitem>Card is marked inactive.</listitem>\r
+                               </itemizedlist>\r
+                       <para>The request looks like:</para>\r
+                       <screen>01&lt;card retained&gt;&lt;date&gt;[fields AO, AL, AA, AC]</screen>\r
+                       <para><emphasis>Card Retained</emphasis>: A single character field of 'Y' or 'N' - tells the ACS whether the SC has retained the card (ex: left in the machine) or not.</para> \r
+                       <para><emphasis>Date</emphasis>: An 18 character field for the date/time when the block occurred.</para> \r
+                       <para><emphasis>Format</emphasis>: YYYYMMDDZZZZHHMMSS (ZZZZ being zone - 4 blanks when 'local time', ' Z' (3 blanks and a Z) represents UTC(GMT/Zulu)</para>\r
+                       <para><emphasis>Fields</emphasis>: See <link linkend='SIP_Fields'>Fields</link> for more details.</para>\r
+                       <para>The response is a 24 “Patron Status Response” with the following:</para>\r
+                       <itemizedlist>\r
+                                       <listitem>Charge privileges denied</listitem>\r
+                                       <listitem>Renewal privileges denied</listitem>\r
+                                       <listitem>Recall privileges denied (hard-coded in every 24 or 64 response)</listitem>\r
+                                       <listitem>hold privileges denied</listitem>\r
+                                       <listitem>Screen Message 1 (AF): 'blocked'</listitem>\r
+                                       <listitem>Patron</listitem>\r
+\r
+                       </itemizedlist>\r
+               </simplesect>\r
+               <simplesect xml:id="SIP_Checkin">\r
+                       <title>09/10 Checkin</title>\r
+                       <para>The request looks like:</para>\r
+                       <screen>09&lt;No block (Offline)&gt;&lt;xact date&gt;&lt;return date&gt;[Fields AP,AO,AB,AC,CH,BI]</screen>\r
+                       <para><emphasis>No Block (Offline)</emphasis>: A single character field of 'Y' or 'N' - Offline transactions are not currently supported so send 'N'.</para> \r
+                       <para><emphasis>xact date</emphasis>: an 18 character field for the date/time when the checkin occurred. Format: YYYYMMDDZZZZHHMMSS (ZZZZ being zone - \r
+                       4 blanks when 'local time', ' Z' (3 blanks and a Z) represents UTC(GMT/Zulu)</para> \r
+                       <para><emphasis>Fields</emphasis>: See <link linkend='SIP_Fields'>Fields</link> for more details.</para>\r
+                       <para>The response is a 10 <quote>Checkin Response</quote> with the following:</para>\r
+                       <screen>10&lt;resensitize>&lt;magnetic media&gt;&lt;alert&gt;&lt;xact date&gt;[Fields AO,AB,AQ,AJ,CL,AA,CK,CH,CR,CS,CT,CV,CY,DA,AF,AG]</screen>\r
+                       <para>Example (with a remote hold):</para>\r
+                       <screen>09N20100507    16593720100507    165937APCheckin Bin 5|AOBR1|AB1565921879|ACsip_01|</screen>\r
+                       <screen>101YNY20100623    165731AOBR1|AB1565921879|AQBR1|AJPerl 5 desktop reference|CK001|CSQA76.73.P33V76 1996|CTBR3|CY373827|DANicholas Richard Woodard|CV02|</screen>\r
+                       <para>Here you can see a hold alert for patron (CY) 373827, named (DA) Nicholas Richard Woodard, to be picked up at (CT) BR3. Since the transaction is happening \r
+                       at (AO) BR1, the alert type (CV) is 02 for “hold at remote library”. The possible values for CV are:</para>\r
+                       <itemizedlist>\r
+                                       <listitem>00: unknown</listitem>\r
+                                       <listitem>01: local hold</listitem>\r
+                                       <listitem>02: remote hold</listitem>\r
+                                       <listitem>03: ILL transfer (not used by EG)</listitem>\r
+                                       <listitem>04: transfer</listitem>\r
+                                       <listitem>99: other</listitem>\r
+                       </itemizedlist>\r
+                       <note>\r
+                               <para>the logic for EG to determine the content is magnetic_media comes from either legacy circ scripts or search_config_circ_modifier. The default is non-magnetic. \r
+                               The same is true for media_type (default 001). EG does not populate the collection_code because it does not really have any, but it will provide the call_number where \r
+                               available.</para>\r
+                               <para>Unlike the item_id (barcode), the title_id is actually a title string, unless the configuration forces the return of the bib ID.</para>\r
+                               <para>Don't be confused by the different branches that can show up in the same response line.</para>\r
+                               <itemizedlist>\r
+                                       <listitem>AO is where the transaction took place,</listitem>\r
+                                       <listitem>AQ is the <quote>permanent location</quote>, and</listitem>\r
+                                       <listitem>CT is the <quote>destination location</quote> (i.e., pickup lib for a hold or target lib for a transfer).</listitem>\r
+                               </itemizedlist>\r
+                       </note>\r
+               </simplesect>\r
+               <simplesect xml:id="SIP_Checkout">\r
+                       <title>11/12 Checkout</title>\r
+               </simplesect>\r
+               <simplesect xml:id="SIP_Hold">\r
+                       <title>15/16 Hold</title>\r
+                       <para>Not yet supported.</para>\r
+               </simplesect>\r
+               <simplesect xml:id="SIP_Item_Information">\r
+                       <title>17/18 Item Information</title>\r
+                       <para>The request looks like:</para>\r
+                       <screen>17&lt;xact_date&gt;[fields: AO,AB,AC]</screen>\r
+                       <para>The request is very terse. AC is optional.</para>\r
+                       <para>The following response structure is for SIP2. (Version 1 of the protocol had only 6 total fields.)</para>\r
+                       <screen>18&lt;circulation_status&gt;&lt;security_marker&gt;&lt;fee_type&gt;&lt;xact_date&gt;[fields: CF,AH,CJ,CM,AB,AJ,BG,BH,BV,CK,AQ,AP,CH,AF,AG,+CT,+CS]</screen>\r
+                       <para>Example:</para>\r
+                       <screen>1720060110    215612AOBR1|ABno_such_barcode|</screen>\r
+                       <screen>1801010120100609    162510ABno_such_barcode|AJ|</screen>\r
+                       <screen>1720060110    215612AOBR1|AB1565921879|</screen>\r
+                       <screen>1810020120100623    171415AB1565921879|AJPerl 5 desktop reference|CK001|AQBR1|APBR1|BGBR1|CTBR3|CSQA76.73.P33V76 1996|</screen>\r
+                       <para>The first case is with a bogus barcode. The latter shows an item with a circulation_status of <emphasis>10</emphasis> for <emphasis>in transit between libraries</emphasis>. \r
+                       The known values of circulation_status are enumerated in the spec.</para>\r
+                       <para>EXTENSIONS: The <emphasis>CT</emphasis> field for <emphasis>destination location</emphasis> and CS <emphasis>call number</emphasis> are used by Automated Material \r
+                       Handling systems.</para>\r
+               </simplesect>\r
+               <simplesect xml:id="SIP_Item_Status_Update">\r
+                       <title>19/20 Item Status Update</title>\r
+               </simplesect>\r
+               <simplesect xml:id="SIP_Patron_Status">\r
+                       <title>23/24 Patron Status</title>\r
+                       <para>Example:</para>\r
+                       <screen>2300120060101    084235AOUWOLS|AAbad_barcode|ACsip_01|ADbad_password|</screen>\r
+                       <screen>24YYYY          00120100507    013934AE|AAbad_barcode|BLN|AOUWOLS|</screen>\r
+                       <screen>2300120060101    084235AOCONS|AA999999|ACsip_01|ADbad_password|</screen>\r
+                       <screen>24  Y           00120100507    022318AEDoug Fiander|AA999999|BLY|CQN|BHUSD|BV0.00|AFOK|AOCONS|</screen>\r
+                       <screen>2300120060101    084235AOCONS|AA999999|ACsip_01|ADuserpassword|LY|CQN|BHUSD|BV0.00|AFOK|AOCONS|</screen>\r
+                       <screen>24  Y           00120100507    022803AEDoug Fiander|AA999999|BLY|CQY|BHUSD|BV0.00|AFOK|AOCONS|</screen>\r
+                       <orderedlist>\r
+                               <listitem>The <emphasis>BL</emphasis> field (SIP2, optional) is <emphasis>valid patron</emphasis>, so the <emphasis>N</emphasis> value means \r
+                               <emphasis>bad_barcode</emphasis> doesn't match a patron, the <emphasis>Y</emphasis> value means 999999 does.</listitem>\r
+                               <listitem>The <emphasis>CQ</emphasis> field (SIP2, optional) is <emphasis>valid password</emphasis>, so the <emphasis>N</emphasis> \r
+                               value means <emphasis>bad_password</emphasis> doesn't match 999999's password, the <emphasis>Y</emphasis> means <emphasis>userpassword</emphasis> \r
+                               does.</listitem>\r
+                       </orderedlist>\r
+                       <para>So if you were building the most basic SIP2 authentication client, you would check for <emphasis>|CQY|</emphasis> in the response to know the user's barcode and password \r
+                       are correct (<emphasis>|CQY|</emphasis> implies <emphasis>|BLY|</emphasis>, since you cannot check the password unless the barcode exists). However, in practice, \r
+                       depending on the application, there are other factors to consider in authentication, like whether the user is blocked from checkout, owes excessive fines, reported their \r
+                       card lost, etc. These limitations are reflected in the 14-character <emphasis>patron status</emphasis> string immediately following the <emphasis>24</emphasis> code. \r
+                       See the field definitions in your copy of the spec.</para>\r
+               </simplesect>\r
+               <simplesect xml:id="SIP_Patron_Enable">\r
+                       <title>25/26 Patron Enable</title>\r
+                       <para>Not yet supported.</para>\r
+               </simplesect>\r
+               <simplesect xml:id="sip_Renew">\r
+                       <title>29/30 Renew</title>\r
+                       <para>EG ACS status message indicates Renew is supported.</para>\r
+               </simplesect>\r
+               <simplesect xml:id="SIP_End_Session">\r
+                       <title>35/36 End Session</title>\r
+                       <screen>3520100505    115901AOBR1|AA999999|</screen>\r
+                       <screen>36Y20100507    161213AOCONS|AA999999|AFThank you!|</screen>\r
+                       <para>The Y/N code immediately after the 36 indicates success/failure. Failure is not particularly meaningful or important in this context, and for evergreen it \r
+                       is hardcoded Y.</para>  \r
+               </simplesect>\r
+               <simplesect xml:id="SIP_Fee_Paid">\r
+                       <title>37/38 Fee Paid</title>\r
+                       <para>Not implemented.</para>\r
+               </simplesect>\r
+               <simplesect xml:id="SIP_Patron_Information">\r
+                       <title>63/64 Patron Information</title>\r
+                       <para>Attempting to retrieve patron info with a bad barcode:</para>\r
+                       <screen>6300020060329    201700          AOBR1|AAbad_barcode|</screen>\r
+                       <screen>64YYYY          00020100623    141130000000000000000000000000AE|AAbad_barcode|BLN|AOBR1|</screen>\r
+                       <para>Attempting to retrieve patron info with a good barcode (but bad patron password):</para>\r
+                       <screen>6300020060329    201700          AOBR1|AA999999|ADbadpwd|</screen>\r
+                       <screen>64  Y           00020100623    141130000000000000000000000000AA999999|AEDavid J. Fiander|BHUSD|BV0.00|BD2 Meadowvale Dr. St Thomas, ON Canada</screen>\r
+                       <screen>90210|BEdjfiander@somemail.com|BF(519) 555 1234|AQBR1|BLY|CQN|PB19640925|PCPatrons|PIUnfiltered|AFOK|AOBR1|</screen>\r
+                       <para>See <link linkend='SIP_Patron_Status'>23/24 Patron Status</link> for info on <emphasis>BL</emphasis> and <emphasis>CQ</emphasis> fields.</para>\r
+               </simplesect>\r
+               <simplesect xml:id="SIP_Renew_All">\r
+                       <title>65/66 Renew All</title>\r
+                       <para>Not yet supported.</para>\r
+               </simplesect>\r
+               <simplesect xml:id="SIP_Login">\r
+                       <title>93/94 Login</title>\r
+                       <para>Example:</para>\r
+                       <screen>9300CNsip_01|CObad_value|CPBR1|</screen>\r
+                       <screen>[Connection closed by foreign host.]</screen>\r
+                       <screen>...</screen>\r
+                       <screen>9300CNsip_01|COsip_01|CPBR1|</screen>\r
+                       <screen>941</screen>\r
+                       <para>941 means successful terminal login. 940 or getting dropped means failure.</para>\r
+               </simplesect>\r
+               <simplesect xml:id="SIP_Resend">\r
+                       <title>97/96 Resend</title>\r
+               </simplesect>\r
+               <simplesect xml:id="SIP_SC_ACS_Status">\r
+                       <title>99/98 SC and ACS Status</title>\r
+                       <screen>99&lt;status code&gt;&lt;max print width&gt;&lt;protocol version&gt;</screen>\r
+                       <para>All 3 fields are required:</para>\r
+                       <itemizedlist>\r
+                                       <listitem>status code - 1 character:</listitem>\r
+                                       <itemizedlist>\r
+                                               <listitem>0: SC is OK</listitem>\r
+                                               <listitem>SC is out of paper</listitem>\r
+                                               <listitem>SC shutting down</listitem>           \r
+                                       </itemizedlist>\r
+                                       <listitem>max print width - 3 characters - the integer number of characters the client can print</listitem>\r
+                                       <listitem>protocol version - 4 characters - x.xx</listitem>\r
+                       </itemizedlist>\r
+                       <screen>98&lt;on-line status&gt;&lt;checkin ok&gt;&lt;checkout ok&gt;&lt;ACS renewal policy&gt;&lt;status update ok&gt;&lt;offline ok&gt;&lt;timeout period&gt;</screen>\r
+                       <screen>&lt;retries allowed&gt;&lt;date/time sync&gt;&lt;protocol version&gt;&lt;institution id&gt;&lt;library name&gt;&lt;supported messages&gt;&lt;terminal</screen> \r
+                       <screen>location&gt;&lt;screen message&gt;&lt;print line&gt;</screen>\r
+                       <para>Example:</para>\r
+                       <screen>9910302.00</screen>\r
+                       <screen>98YYYYNN60000320100510    1717202.00AOCONS|BXYYYYYYYYYNYNNNYN|</screen>\r
+                       <para>The Supported Messages field <emphasis>(BX)</emphasis> appears only in SIP2, and specifies whether 16 different SIP commands are supported by the ACS or not.</para>\r
+               </simplesect>\r
+               <simplesect xml:id="SIP_Fields">\r
+                       <title>Fields</title>\r
+                       <para>All fixed-length fields in a communication will appear before the first variable-length field. This allows for simple parsing. Variable-length fields are by \r
+                       definition delimited, though there will not necessarily be an initial delimiter between the last fixed-length field and the first variable-length one. It would be \r
+                       unnecessary, since you should know the exact position where that field begins already.</para>\r
+               </simplesect>\r
+       </section>\r
+</chapter>\r
+\r
diff --git a/1.6/admin/z3950.xml b/1.6/admin/z3950.xml
new file mode 100644 (file)
index 0000000..ef5b6ed
--- /dev/null
@@ -0,0 +1,197 @@
+<?xml version="1.0" encoding="utf-8"?>\r
+<chapter xml:id="z3950" xmlns="http://docbook.org/ns/docbook" version="5.0" xml:lang="EN"\r
+    xmlns:xi="http://www.w3.org/2001/XInclude" xmlns:xlink="http://www.w3.org/1999/xlink">\r
+    <info>\r
+        <title>SRU and Z39.50 Server</title>\r
+    </info>\r
+\r
+    <para>Evergreen is extremely scalable and can serve the need of a large range of libraries. The specific requirements and configuration of your system should be determined based on your \r
+       specific needs of your organization or consortium.</para>\r
+    \r
+       <section xml:id="Testing_SRU_yaz-client">\r
+               <info>\r
+                   <title>Testing SRU with yaz-client</title>\r
+               </info> \r
+                        <para>yaz-client is installed as a part of Index Data's YAZ software. Recent versions include support for querying SRU servers. Evergreen ships an SRU configuration \r
+                       that works out of the box. To search Evergreen with yaz-client, choose the <emphasis>GET</emphasis> query method and issue the <emphasis>find</emphasis> command. \r
+                       In the following example, we connect to the Evergreen test server <emphasis>dev.gapines.org</emphasis>; substitute this hostname with your own Evergreen server \r
+                       hostname:</para>\r
+                       <screen>\r
+                       $ yaz-client http://dev.gapines.org/opac/extras/sru\r
+                       Z> sru GET 1.1\r
+                       Z> find hemingway\r
+                       </screen>\r
+                        <para>If your database has records that match that term, you will get the corresponding MARCXML records in your response from yaz-client.</para>\r
+                        <para>Here's what the SRU request looks like as sent to the Evergreen web server:</para>\r
+                        <screen>GET /opac/extras/sru?version=1.1&amp;operation=searchRetrieve&amp;query=hemingway&amp;maximumRecords=0</screen>\r
+                       <para>You can see what the response looks like by hitting the same URL in your Web browser: \r
+                       <ulink url="http://dev.gapines.org/opac/extras/sru?version=1.1&amp;operation=searchRetrieve&amp;query=hemingway&amp;maximumRecords=0">\r
+                       http://dev.gapines.org/opac/extras/sru?version=1.1&amp;operation=searchRetrieve&amp;query=hemingway&amp;maximumRecords=0</ulink>\r
+                       CQL queries</para>\r
+                        <para>Evergreen supports some CQL index-sets for advanced queries such as a subset of Dublin Core (DC) elements. Those DC elements that are \r
+                       supported map to Evergreen default indexes as follows:</para>\r
+                       <informaltable xml:id="dc_elements">\r
+                       \r
+                       <tgroup cols="2">\r
+                               <thead>\r
+                                       <row>\r
+                                               <entry>DC element </entry>\r
+                                               <entry>Evergreen index</entry>\r
+                                               \r
+                                       </row>\r
+                               </thead>\r
+                               <tbody>\r
+                                       <row>\r
+                                               <entry>title</entry>\r
+                                               <entry>title</entry>                            \r
+                                       </row>\r
+                                       <row>\r
+                                               <entry>creator </entry>\r
+                                               <entry>author</entry>                           \r
+                                       </row>\r
+                                       <row>\r
+                                               <entry>contributor</entry>\r
+                                               <entry>author</entry>                           \r
+                                       </row>\r
+                                       <row>\r
+                                               <entry>publisher</entry>\r
+                                               <entry>keyword</entry>                          \r
+                                       </row>\r
+                                       <row>\r
+                                               <entry>subject</entry>\r
+                                               <entry>subject</entry>                          \r
+                                       </row>\r
+                                       <row>\r
+                                               <entry>identifier</entry>\r
+                                               <entry>keyword</entry>                          \r
+                                       </row>\r
+                                       <row>\r
+                                               <entry>type</entry>\r
+                                               <entry>none</entry>                             \r
+                                       </row>\r
+                                       <row>\r
+                                               <entry>format</entry>\r
+                                               <entry>none</entry>                             \r
+                                       </row>\r
+                                       <row>\r
+                                               <entry>language</entry>\r
+                                               <entry>lang</entry>                             \r
+                                       </row>\r
+                               </tbody>\r
+                       </tgroup>\r
+                       </informaltable>\r
+                       <para>Here are a few examples of SRU searches against some of these indexes:</para>\r
+                       <itemizedlist>\r
+                               <listitem>dc.title all <quote>complete dinosaur</quote></listitem>\r
+                               <listitem>dc.subject all <quote>britain france</quote></listitem>\r
+                               <listitem>dc.title exact <quote>The Empire Strikes Back</quote></listitem>\r
+                               <listitem>dc.author=king and dc.title=zone</listitem>\r
+                       </itemizedlist>\r
+\r
+       </section>\r
+       <section xml:id="Z3950serversupport">\r
+               <info>\r
+                   <title>Setting up Z39.50 server support</title>\r
+               </info> \r
+\r
+                       <note><para>You must have Evergreen's SRU server running before you can enable Z39.50 server support.</para></note>                      \r
+                       <para>This support uses an Z39.50-to-SRU translator service supplied by the <emphasis>Net::Z3950::Simple2ZOOM</emphasis> Perl module to enable Evergreen to act as a Z39.50 server. \r
+                       You could run the Z39.50 server on a different machine. It just needs to be able to connect to the Evergreen SRU server.</para>\r
+                       <procedure>\r
+                               <title>Setting up the Z39.50 server</title>\r
+                               <step>\r
+                                       <para>Install a recent version of yaz (the Makefile.install should have installed a suitable version).</para></step>\r
+                               <step>\r
+                                       <para>Install Net::Z3950::Simple2ZOOM (sudo cpan Net::Z3950::Simple2ZOOM)</para></step>\r
+                               <step>\r
+                                       <para>Create a Simple2ZOOM configuration file. Something like the following is a good start, and is based on the Simple2ZOOM documentation example. \r
+                                       We'll name the file dgo.conf for our example:</para>    \r
+                                       <screen>\r
+                                       &lt;client&gt;\r
+                                               &lt;database name="gapines"&gt;\r
+                                                       &lt;zurl&gt;http://dev.gapines.org/opac/extras/sru&lt;/zurl&gt;\r
+                                                       &lt;option name="sru"&gt;get&lt;/option&gt;\r
+                                                       &lt;charset&gt;marc-8&lt;/charset&gt;\r
+                                                       &lt;search&gt;\r
+                                                               &lt;querytype&gt;cql&lt;/querytype&gt;\r
+                                                               &lt;map use="4"&gt;&lt;index&gt;eg.title&lt;/index&gt;&lt;/map&gt;\r
+                                                               &lt;map use="7"&gt;&lt;index&gt;eg.keyword&lt;/index&gt;&lt;/map&gt;\r
+                                                               &lt;map use="8"&gt;&lt;index&gt;eg.keyword&lt;/index&gt;&lt;/map&gt;\r
+                                                               &lt;map use="21"&gt;&lt;index&gt;eg.subject&lt;/index&gt;&lt;/map&gt;\r
+                                                               &lt;map use="1003"&gt;&lt;index&gt;eg.author&lt;/index&gt;&lt;/map&gt;\r
+                                                               &lt;map use="1018"&gt;&lt;index&gt;eg.publisher&lt;/index&gt;&lt;/map&gt;\r
+                                                               &lt;map use="1035"&gt;&lt;index&gt;eg.keyword&lt;/index&gt;&lt;/map&gt;\r
+                                                               &lt;map use="1016"&gt;&lt;index&gt;eg.keyword&lt;/index&gt;&lt;/map&gt;\r
+                                                       &lt;/search&gt;\r
+                                               &lt;/database&gt;\r
+                                       &lt;/client&gt;\r
+                                       </screen>\r
+                                       <para>You can have multiple &lt;database&gt; sections in a single file, each pointing to a different scope of your consortium. The name attribute on \r
+                                       the &lt;database&gt; element is used in your Z39.50 connection string to name the database. The &lt;zurl&gt; element must point to \r
+                                       <emphasis>http://hostname/opac/extras/sru</emphasis>. As of Evergreen 1.6, you can append an optional organization unit shortname for search \r
+                                       scoping purposes, and you can also append <emphasis>/holdings</emphasis> if you want to expose the holdings for any returned records. So your zurl \r
+                                       could be <emphasis>http://dev.gapines.org/opac/extras/sru/BR1/holdings</emphasis> to limit the search scope to BR1 and its children, and \r
+                                       to expose its holdings.</para>\r
+                               </step>\r
+                               <step>\r
+                                       <para>Run simple2ZOOM as a daemon, specifying the configuration files and one or more listener addresses that the Z39.50 server will be accessible on. \r
+                                       If you do not specify a port, it will automatically run on port 9999. In the following example, we tell it to listen both to localhost on port 2210, \r
+                                       and on dev.gapines.org on port 210:</para>\r
+                                       <screen>\r
+                                        &lt;yazgfs&gt;\r
+                                               &lt;server id="server1"&gt;\r
+                                                       &lt;retrievalinfo&gt;\r
+                                                               &lt;retrieval syntax="xml"/&gt;\r
+                                                               &lt;retrieval syntax="marc21"&gt;\r
+                                                               &lt;backend syntax="xml"&gt;\r
+                                                                       &lt;marc inputformat="xml" outputformat="marc" inputcharset="utf-8" outputcharset="marc-8"/&gt;\r
+                                                               &lt;/backend&gt;\r
+                                                               &lt;/retrieval&gt;\r
+                                                       &lt;/retrievalinfo&gt;\r
+                                               &lt;/server&gt;\r
+                                       &lt;/yazgfs&gt;\r
+                                       </screen>\r
+                               </step>\r
+                               <step>\r
+                                       <para>Run simple2ZOOM as a daemon, specifying the configuration files and one or more listener addresses that the Z39.50 server will be accessible on. \r
+                                       If you do not specify a port, it will automatically run on port 9999. In the following example, we tell it to listen both to localhost on port 2210, \r
+                                       and on dev.gapines.org on port 210:</para>\r
+                                       <command>simple2zoom -c dgo.conf -- -f xml2marc-yaz.cfg localhost:2210 dev.gapines.org:210</command>\r
+                               </step>                         \r
+                       </procedure>\r
+                       <para>To test the Z39.50 server, we can use yaz-client again:</para>]\r
+                       <screen>\r
+                       yaz-client\r
+                       Z&gt; open localhost:2210/gapines\r
+                       Connecting...OK.\r
+                       Sent initrequest.\r
+                       Connection accepted by v3 target.\r
+                       ID     : 81/81\r
+                       Name   : Simple2ZOOM Universal Gateway/GFS/YAZ\r
+                       Version: 1.03/1.128/3.0.34\r
+                       Options: search present delSet triggerResourceCtrl scan sort namedResultSets\r
+                       Elapsed: 0.010718\r
+                       Z&gt; format marcxml\r
+                       Z&gt; find <quote>dc.title=zone and dc.author=king</quote>\r
+                       Sent searchRequest.\r
+                       Received SearchResponse.\r
+                       Search was a success.\r
+                       Number of hits: 0, setno 4\r
+                       records returned: 0\r
+                       Elapsed: 0.611432\r
+                       Z&gt; find <quote>dead zone</quote>\r
+                       Sent searchRequest.\r
+                       Received SearchResponse.\r
+                       Search was a success.\r
+                       Number of hits: 4, setno 5\r
+                       records returned: 0\r
+                       Elapsed: 1.555461\r
+                       Z&gt; show 1\r
+                       Sent presentRequest (1+1).\r
+                       Records: 1\r
+                       []Record type: XML\r
+                       &lt;record xmlns:... (rest of record deliberately truncated)\r
+                       </screen>\r
+       </section>\r
+</chapter>\r
+\r
diff --git a/1.6/development/customize_opac.xml b/1.6/development/customize_opac.xml
new file mode 100644 (file)
index 0000000..9178e0a
--- /dev/null
@@ -0,0 +1,272 @@
+<?xml version="1.0" encoding="utf-8"?>\r
+<chapter xml:id="Customizing_OPAC" xmlns="http://docbook.org/ns/docbook" version="5.0" xml:lang="EN"\r
+    xmlns:xi="http://www.w3.org/2001/XInclude" xmlns:xlink="http://www.w3.org/1999/xlink">\r
+       <info>\r
+               <title>Customizing the OPAC</title>\r
+       </info>\r
+       <para>While Evergreen is ready to go out of the box, libraries will want to customize Evergreen with their own color scheme, logos and layout. This chapter will explain how to \r
+       customize Evergreen to meet the needs of your users. For these task some knowledge of html and css is required. Many of these instructions assume a default installation of \r
+       Evergreen using the default file locations.</para>\r
+       <note>\r
+               <para>Be sure to save a backup copy of all files you edit in a location other than /openils/var/web/opac/ as files here could be overwritten when you upgrade your copy \r
+               of Evergreen.</para>\r
+       </note> \r
+       <section xml:id="ColorScheme">\r
+               <title>Change the Color Scheme</title>\r
+               <para>To change the color scheme of the default Evergreen skin, edit <filename>/openils/var/web/opac/theme/default/css/colors.css</filename>. From this one file you can \r
+               change the 4 base color scheme as well as colors of specific elements.     \r
+               </para>                                                  \r
+               <para>You can also create alternate themes for your users.</para>     \r
+               <procedure>\r
+                       <step>\r
+                               <para>Copy the css folder and its contents from the example alternate theme <filename>/openils/var/web/opac/theme/reddish/</filename> \r
+                               to a new folder <filename>/openils/var/web/opac/theme/<emphasis>[your new theme]</emphasis>/</filename>.</para>\r
+                       </step> \r
+                       <step>\r
+                               <para>Edit /openils/var/web/opac/theme/<emphasis>[your new theme]</emphasis>/css/colors.css to use the colors you want.</para>\r
+                       </step>\r
+                       <step>\r
+                               <para>Link to your new style sheet by adding the following to <filename>/openils/var/web/opac/skin/default/xml/common/css_common.xml</filename>.</para>\r
+                               <screen>&lt;link type='text/css'</screen> \r
+                               <screen>rel="alternate stylesheet"</screen>  \r
+                               <screen>title='&amp;opac.style.yourtheme;'</screen>  \r
+                               <screen>href="&lt;!--#echo var='OILS_THEME_BASE'--&gt;/yourtheme/css/colors.css"</screen> \r
+                               <screen>name='Default' csstype='color'/&gt;</screen> \r
+                       </step> \r
+                       <step>\r
+                               <para> Give your new theme a name users can select by adding the following to <filename>/openils/var/web/opac/locale/<emphasis>\r
+                               [your locale]</emphasis>/opac.dtd</filename>.</para>\r
+                               <screen>&lt;!ENTITY opac.style.yourtheme "YourTheme"&gt;</screen>       \r
+                       </step> \r
+               </procedure>    \r
+       </section>\r
+       <section xml:id="customizing_opac_text">\r
+               <title>customizing Opac Text and Labels</title>\r
+               <para>To change text and links used throughout the OPAC, edit the following files:</para>\r
+               <itemizedlist>\r
+                       <listitem><filename>/openils/var/web/opac/locale/<emphasis>[your locale]</emphasis>/lang.dtd</filename></listitem>\r
+                       <listitem><filename>/openils/var/web/opac/locale/<emphasis>[your locale]</emphasis>/opac.dtd</filename></listitem>\r
+               </itemizedlist>\r
+               <tip>\r
+                       <para>A better way to customize OPAC text is to create custom <emphasis>dtd</emphasis> files for your lang and opac customizations and then add a include \r
+                       statement above the default dtd files.</para>\r
+                       <screen>&lt;!DOCTYPE html PUBLIC</screen> \r
+                       <screen>"-//W3C//DTD XHTML 1.0 Transitional//EN"</screen> \r
+                       <screen>"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd" [</screen>\r
+                       <screen>&lt;!--#include virtual="/opac/locale/${locale}/custom_opac.dtd"--&gt;</screen>\r
+                               <screen>&lt;!--#include virtual="/opac/locale/${locale}/opac.dtd"--&gt;</screen>\r
+                       <screen>]&gt;</screen>\r
+                       <para>position is important here. The first/top included <emphasis>dtd</emphasis> files will take precedence over the subsequent dtd includes.</para> \r
+               </tip>\r
+               <para>While it is possible to add text to the xml files itself, it is a good practice to use the DTD file to control the text and refer to the DTD elements in the xml/html code.</para>   \r
+               <para>For example, the footer.xml file has this code to generate a copyright statement:</para>\r
+               <screen>&lt;div id='copyright_text'&gt;</screen>\r
+               <screen>&lt;span&gt;&amp;footer.copyright;&lt;/span&gt;</screen>\r
+               <para>The included <filename>opac.dtd</filename> file in the en-US locale direcotry has this setting for &amp;footer.copyright text:</para>  \r
+               <screen>&lt;!ENTITY footer.copyright "Copyright &#xA9; 2006-2010 Georgia Public Library Service, and others"&gt;</screen>\r
+       </section>\r
+       <section xml:id="Logo_Images">\r
+               <title>Logo Images</title>\r
+               <para>To change the logos used by default to your own logos, replace the following files with images of your own, appropriately sized. </para>\r
+               <itemizedlist>\r
+                       <listitem><filename>/openils/var/web/opac/images/main_logo.jpg</filename></listitem>\r
+                       <listitem><filename>/openils/var/web/opac/images/main_logo.jpg</filename></listitem>\r
+               </itemizedlist>                          \r
+       </section>\r
+       <section xml:id="AddedContent">\r
+               <title>Added Content</title>\r
+               <para>By default Evergreen includes customizable <quote>Added Content</quote> features to enhance the OPAC experience for your user. These features include Amazon book covers \r
+               and Google books searching. These features can be turned off or custimized.</para>\r
+               <simplesect xml:id="bookcovers">\r
+                       <title>Book Covers</title>\r
+                       <para>The default install of Evergreen includes Amazon book covers. The settings for this are controlled by the &lt;added_content&gt; section of \r
+                       <filename>/opneils/conf/opensrf.xml</filename>. Here are the key elements of this configuration:</para> \r
+                       <screen>&lt;module&gt;OpenILS::WWW::AddedContent::Amazon&lt;/module&gt;</screen>\r
+                       <para>This calls the Amazon perl module. If you wish to link to a different book cover service other than Amazon, you must create a new perl module and refer to it here. \r
+                       You will also need to change other settings accordingly. There are some available book cover perl modules available in  \r
+                       <ulink url="http://svn.open-ils.org/trac/ILS/browser/trunk/Open-ILS/src/perlmods/OpenILS/WWW/AddedContent">trunk</ulink></para>\r
+                       <screen>&lt;base_url&gt;http://images.amazon.com/images/P/&lt;/base_url&gt;</screen>\r
+                       <para>Base URL for Amazon added content fetching.  This URL may need to be shortened when new (read: non-image) content fetching \r
+                       capabilities are added.</para>   \r
+                       <screen>&lt;timeout&gt;1&lt;/timeout&gt;</screen>\r
+                       <para>Max number of seconds to wait for an added content request to return data.  Data not returned within the timeout is considered a failure.</para>   \r
+                       <screen>&lt;retry_timeout&gt;600&lt;/retry_timeout&gt;</screen>\r
+                       <para>After added content lookups have been disabled due to too many lookup failures, this is the amount of time to wait before we try again.</para>\r
+                       <screen>&lt;max_errors&gt;15&lt;/max_errors&gt;</screen>\r
+                       <para>Maximum number of consecutive lookup errors a given process can live before added content lookups are disabled for everyone.</para>       \r
+                       <screen>&lt;userid&gt;MY_USER_ID&lt;/userid&gt;</screen>\r
+                       <para>If a userid is required to access the added content.</para>\r
+               </simplesect>\r
+               <simplesect xml:id="googlebookslink">\r
+                       <title>Google Books Link</title>\r
+                       <para>The results page will display a <quote>Browse in Google Books Search</quote> link for items in the results page which have corresponding entries in Google Books. \r
+                       This will link to Google Books content including table of contents and complete versions of the work if it exists in Google Books. Items not in Google Books will not \r
+                       display a link. This feature can be turned off by changing the googleBooksLink variable setting to <quote>false</quote>in the file \r
+                       <filename>/openils/var/web/opac/skin/default/js/result_common.js</filename>. By default, this feature is activated. </para> \r
+               </simplesect>     \r
+       </section>\r
+       <section xml:id="resultspage">\r
+               <title>Customizing the Results Page</title>\r
+               <para>The results page is extremely customizable and allows some built in features to be activated with some simple edits or more advanced customizations can be done by more \r
+               experienced web developers.</para>\r
+               <para>There are several critical files to edit if you wish to  customize the results page:</para>\r
+               <itemizedlist>\r
+                       <listitem><filename>/openils/var/web/opac/skin/default/js/result_common.js</filename> - This file controls the javascript for the top level elements on the results \r
+                       page and should only be edited by experienced web developers except for the <link linkend='googlebookslink'>google books link setting</link> mentioned perviously.</listitem>\r
+                       <listitem><filename>/openils/var/web/opac/skin/default/js/rresult.js</filename> - Has some good controls of results page settings at the top of this file but \r
+                       requires web development skills for editing this file.</listitem>\r
+                       <listitem><filename>/openils/var/web/opac/skin/default/xml/result/rresult_table.xml</filename> - This controls the layout of the items table on results page.</listitem>\r
+               </itemizedlist>          \r
+       </section>\r
+       <section xml:id="deatailspage">\r
+               <title>Customizing the Details Page</title>\r
+               <para>There are many options when customizing the details page in Evergreen. The default settings are effective for most libraries, but it is important to understand the full potential \r
+               of Evergreen when displaying the details of items.</para>               \r
+               <para>Some quick features can be turned on and off by changing variable values in the file <filename>/openils/var/web/opac/skin/default/js/rdedail.js</filename>. \r
+               You will notice the section at the top of this file called <quote>Per-skin configuration settings</quote>. Changing setting in this section can control several features includuing \r
+               limiting results to local only or showing copy location or displaying serial holdings. Form this section you can also enable refworks and set the Refworks host URL.</para>\r
+               <para>Some copy level details settings can be turned on and off from <filename>/openils/var/web/opac/skin/default/js/copy_details.js</filename> including displaying certain fields \r
+               such as due date in the OPAC.</para>\r
+               <para>An important file is the <filename>/openils/var/web/opac/skin/default/xml/rdetail/rdetail_summary.xml</filename> file. This file allows you to control which field to display in \r
+               the details summary of the record. The new <link linkend='googlebookslink'>BibTemnplate</link> feature makes this file even more powerful by allowing you to display any marc fields \r
+               with a variey of formatting options.</para>\r
+               <para>The <filename>/openils/var/web/opac/skin/default/xml/rdetail/rdetail_copyinfo.xml</filename> file allows you to format the display of the copy information.</para>   \r
+       </section>              \r
+       <section xml:id="BibTemplate">\r
+               <title>BibTemplate</title>\r
+               <para>BibTemplate is an Evergreen-custom Dojo module which can be used to retrieve and format XML  data served by the Evergreen unAPI service. unAPI is a protocol for requesting                               known objects in specific formats, and Evergreen uses this to supply data – bibliographic records, metarecords, monograph holdings information, Located URIs, and more to come – \r
+               in many different formats from MARCXML to MODS to custom XML applications.</para> \r
+               <para>Managing the display of information from raw XML can be difficult, and the purpose of BibTemplate  is to make this simpler, as well as move the display closer to the \r
+               client and away from the source data. This is good from a separation-of-responsibilities perspective, and also makes it easier to contain and control local customization.</para>\r
+               <para>BibTemplate supports the foloowing Evergreen metadata formats:</para>\r
+               <itemizedlist>\r
+                       <listitem>MARCXML - datatype='marcxml-full' (default)</listitem>                                        \r
+                       <listitem>MODS 3.3: datatype='mods33'</listitem>\r
+                       <listitem>Dublin Core: datatype='rdf_dc'</listitem>\r
+                       <listitem>FGDC: datatype='fgdc'</listitem>\r
+               </itemizedlist>                 \r
+               <simplesect>\r
+                       <title>HTML API</title>\r
+                       <para>BibTemplate follows the Dojo convention of adding attributes to existing (X)HTML in order to progressively change its behavior. The 1.6.0 HTML API consists of a \r
+                       set of attributes that are added to existing OPAC markup, and fall into two classes:</para>\r
+                       <itemizedlist>\r
+                               <listitem> The slot marker – Elements that denote the location of bibliographic data to insert.</listitem>\r
+                               <listitem>The slot formatter – Elements that specify how the named data should be formatted for display.</listitem>\r
+                       </itemizedlist>          \r
+               </simplesect>\r
+               <simplesect>\r
+                       <title>Slot Marker</title>\r
+                       <para>A slot marker is any displayable HTML element that has a type attribute with a value starting with opac/slot-data. This element will become the container \r
+                       for the formatted data. A slot marker is required in order to retrieve, format and display data using BibTemplate. A slot marker must also have an \r
+                       attribute called query containing a CSS3 selector. This selector is applied to the XML returned by the unAPI service in order to gather the specific XML \r
+                       Nodes that should be considered for formatting.</para>\r
+                       <para>The slot marker can also specify the format of the data to be returned from the unAPI service. This can be specified by adding +{format} to the type \r
+                       attribute, as in opac/slot-data+mods33-full. The default data format is marcxml-uri, which is an augmented MARCXML record containing Located URI information \r
+                       and unAPI links.</para>\r
+                       <para>Example of a slot marker:</para>\r
+                       <screen>&lt;p type='opac/slot-data' query='datafield[tag=245]'&gt;&lt;/p&gt;</screen>\r
+                       <para>Most useful attribute match operators include:</para>\r
+                       <itemizedlist>\r
+                               <listitem> datafield[tag=245] - exact match</listitem>\r
+                               <listitem>datafield[tag^=65] - match start of value</listitem>\r
+                       </itemizedlist> \r
+                       <tip><para>Selectors always narrow, so select broadly and iterate through the NodeList</para></tip>\r
+               </simplesect>\r
+               <simplesect>\r
+                       <title>Slot Formatter</title>\r
+                       <para>A slot formatter is any invisible HTML element which has a type attribute with the value of opac/slot-format. (NOTE: before 1.6.0.4, only &lt;script&gt; \r
+                       elements were supported, though this restriction is now removed to support Internet Explorer.) Only one slot formatter element is allowed in each slot. The text contents \r
+                       of this element are wrapped in a JavaScript function and run for each node returned by the query CSS3 selector specified on the slot marker. This function is passed \r
+                       one argument, called item, which an XML Node captured by the selector. This function should return HTML text. The output for all runs of the slot formatter is \r
+                       concatenated into a single string and used to replace the contents of the slot marker.</para>\r
+                       <para>The slot formatter is optional, and if not supplied BibTemplate will create a simple function which extracts and returns the text content of the XML Nodes \r
+                       specified in the CSS3 selector.</para>\r
+                       <para>Example of a slot formatter:</para>\r
+                       <programlisting>\r
+                               &lt;td class='rdetail_item' id='rdetail_online' type='opac/slot-data' query='volumes volume uris uri' join=", "&gt;\r
+                                       &lt;script type='opac/slot-format'&gt;&lt;![CDATA[\r
+                                               var link = '&lt;a href="' + item.getAttribute('href') + '"&gt;' + item.getAttribute('label') + '&lt;/a&gt;';\r
+                                               if (item.getAttribute('use_restriction'))\r
+                                                       link += ' (Use restriction: ' + item.getAttribute('use_restriction') + ')';\r
+                                                       return link;\r
+                                       ]]&gt;&lt;/script&gt;\r
+                               &lt;/td&gt;\r
+                       </programlisting>\r
+               </simplesect>\r
+               <simplesect>\r
+                       <title>JavaScript API</title>\r
+                       <para>In order for BibTemplate to find the slot markers and invoke the slot formatters JavaScript renderer must be instantiated and called. This must be done \r
+                       for each record that is to contribute to a pages display. The API for this is simple and straight-forward:</para>\r
+                       <para>The slot formatter is optional, and if not supplied BibTemplate will create a simple function which extracts and returns the text content of the XML Nodes \r
+                       specified in the CSS3 selector.</para>\r
+                       <para>Example of a slot formatter:</para>\r
+                       <programlisting>\r
+                               dojo.require('openils.BibTemplate'); // Tell Dojo to load BibTemplate, if it is not already loaded\r
+                               \r
+                               // Create a renderer supplying the record id and the short name of the org unit, if known, and call the render() method                                         \r
+                               new openils.BibTemplate({ record : new CGI().param('r'), org_unit : here.shortname() }).render(); \r
+\r
+                       </programlisting>\r
+                       <para>The argument hash supplied to the new openils.BibTemplate() constructor can have the following properties:</para>\r
+                       <itemizedlist>\r
+                               <listitem>record – The bibliographic record ID.</listitem>\r
+                               <listitem>org_unit – The relevant Organizational Unit, used to restrict holdings scope as on a search result or record detail page.</listitem>\r
+                               <listitem>root – The root element within the web page that BibTemplate should search for slot markers</listitem>\r
+                       </itemizedlist>          \r
+               </simplesect>\r
+               <simplesect>\r
+                       <title>BibTemplate Examples</title>\r
+                       <para>This is all that we had to add to display the contents of an arbitrary MARC field:</para>\r
+                       <programlisting>\r
+                               &lt;tr&gt;\r
+                                       &lt;td&gt;Bibliography note&lt;/td&gt;\r
+                                       &lt;td type='opac/slot-data' query='datafield[tag=504]'&gt;&lt;/td&gt;\r
+                               &lt;/tr&gt;\r
+                       </programlisting>\r
+                       <note><para>If multiple fields match, they are displayed on consecutive lines within the same left-hand cell.</para></note>\r
+                       <para>To display a specific MARC subfield, add that subfield to the query attribute.</para>\r
+                       <para>For example, subfield $a is the only user-oriented subfield in field 586 (Awards Note)</para>\r
+                       <programlisting>\r
+                               &lt;tr&gt;\r
+                                       &lt;td&gt;Awards note&lt;/td&gt;\r
+                                       &lt;td type='opac/slot-data' query='datafield[tag=586] subfield[code=a]'&gt;&lt;/td&gt;\r
+                               &lt;/tr&gt;\r
+                       </programlisting>\r
+                       <para>Hide empty rows by default, and display them only if they have content:</para>\r
+                       <programlisting>\r
+                               &lt;tr class='hide_me' id='tag504'&gt;\r
+                                       &lt;td&gt;Bibliographic note&lt;/td&gt;\r
+                                       &lt;td type='opac/slot-data' query='datafield[tag=504]'&gt;\r
+                                               &lt;script type='opac/slot-format'&gt;&lt;![CDATA[\r
+                                                       dojo.query('#tag504').removeClass('hide_me');\r
+                                                       return '&lt;span&gt;' + dojox.data.dom.textContent(item) +\r
+                                                       '&lt;/span&gt;&lt;br/&gt;';\r
+                                               ]]&gt;&lt;/script&gt;\r
+                                       &lt;/td&gt;&lt;/tr&gt;\r
+                       </programlisting>\r
+                       <itemizedlist>\r
+                               <listitem>&lt;![CDATA[ ... ]]&gt; tells Evergreen Web server to treat the contents as literal <quote>character data</quote> - \r
+                               avoids hilarity of entity substitution</listitem>\r
+                               <listitem>&lt;script type='opac/slot-format'&gt;...&lt;/script&gt;, contained within an 'opac/slot-data' element, receives a variable named item \r
+                               containing the results of the query (a NodeList)</listitem>     \r
+                       </itemizedlist> \r
+                       <para>Suppressing a subfield:</para>\r
+                       <programlisting>\r
+                               &lt;tr class='hide_me' id='tag700'&gt;\r
+                                       &lt;td>Additional authors&lt;/td&gt;\r
+                                       &lt;td type='opac/slot-data' query='datafield[tag=700]'&gt;\r
+                                               &lt;script type='opac/slot-format'&gt;&lt;![CDATA[\r
+                                                       dojo.query('#tag700').removeClass('hide_me');\r
+                                                       var text = '';\r
+                                                       var list = dojo.query('subfield:not([code=4])', item);\r
+                                                       for (var i =0; i &lt; list.length; i++) {\r
+                                                               text += dojox.data.dom.textContent(list[i]) + ' ';\r
+                                                       }\r
+                                                       return '&lt;span&gt;' + text + '&lt;/span&gt;&lt;br/&gt;';\r
+                                               ]]&gt;&lt;/script>\r
+                                       &lt;/td&gt;&lt;/tr&gt;\r
+                       </programlisting>\r
+               </simplesect>\r
+       </section>\r
+</chapter>\r
+\r
diff --git a/1.6/development/development_intro.xml b/1.6/development/development_intro.xml
new file mode 100644 (file)
index 0000000..e227c2d
--- /dev/null
@@ -0,0 +1,3 @@
+<partintro xmlns:xl="http://www.w3.org/1999/xlink" xml:id="development_intro">\r
+<para>This part will allow you to customize the Evergreen OPAC, develop useful SQL queries and help you learn the skills necessary for developing new Evergreen applications. It is intended for experienced Evergreen administrators and Evergreen developers who wish to customize Evergreen or enhance their knowledge of the database structure and code. Some of these chapters are introductory in nature, but others assume some level of web development, programming, or database administration experience.</para>\r
+</partintro>\r
diff --git a/1.6/development/intro.xml b/1.6/development/intro.xml
deleted file mode 100644 (file)
index e69de29..0000000
diff --git a/1.6/development/json.xml b/1.6/development/json.xml
new file mode 100644 (file)
index 0000000..036200d
--- /dev/null
@@ -0,0 +1,1864 @@
+<?xml version="1.0" encoding="utf-8"?>\r
+<chapter xml:id="JSON_Queries" xmlns="http://docbook.org/ns/docbook" version="5.0" xml:lang="EN"\r
+    xmlns:xi="http://www.w3.org/2001/XInclude" xmlns:xlink="http://www.w3.org/1999/xlink">\r
+       <info>\r
+               <title>JSON Queries</title>\r
+       </info>\r
+       <para>The json_query facility provides a way for client applications to query the database over the network. Instead of constructing its own SQL, the application encodes a query in the \r
+       form of a JSON string and passes it to the json_query service. Then the json_query service parses the JSON, constructs and executes the corresponding SQL, and returns the results to \r
+       the client application.</para>\r
+       <para>This arrangement enables the json_query service to act as a gatekeeper, protecting the database from potentially damaging SQL commands. In particular, the generated SQL is \r
+       confined to SELECT statements, which will not change the contents of the database.</para>\r
+\r
+       <para>In addition, the json_query service sometimes uses its knowledge of the database structure to supply column names and join conditions so that the client application doesn't \r
+       have to.</para>\r
+\r
+       <para>Nevertheless, the need to encode a query in a JSON string adds complications, because the client needs to know how to build the right JSON. JSON queries are also somewhat \r
+       limiting -- they can't do all of the things that you can do with raw SQL.</para>\r
+       <simplesect>\r
+               <title>The IDL</title>\r
+\r
+               <para>A JSON query does not refer to tables and columns. Instead, it refers to classes and fields, which the IDL maps to the corresponding database entities.</para>\r
+\r
+               <para>The IDL (Interface Definition Language) is an XML file, typically <filename>/openils/conf/fm_IDL.xml</filename>. It maps each class to a table, view, or subquery, and \r
+               each field to a column. It also includes information about foreign key relationships.</para>\r
+\r
+               <para>(The IDL also defines virtual classes and virtual fields, which don't correspond to database entities. We won't discuss them here, because json_query ignores them.)</para>\r
+\r
+               <para>When it first starts up, json_query loads a relevant subset of the IDL into memory. Thereafter, it consults its copy of the IDL whenever it needs to know about the database \r
+               structure. It uses the IDL to validate the JSON queries, and to translate classes and fields to the corresponding tables and columns. In some cases it uses the IDL to supply information \r
+               that the queries don't provide.\r
+               Definitions</para>\r
+\r
+               <para>You should also be familiar with JSON. However it is worth defining a couple of terms that have other meanings in other contexts:</para>\r
+\r
+               <itemizedlist>\r
+                       <listitem><para>An "object" is a JSON object, i.e. a comma-separated list of name:value pairs, enclosed in curly braces, like this:</para>\r
+                               <screen>{ "a":"frobozz", "b":24, "c":null }</screen>\r
+                       </listitem>\r
+                       <listitem><para>An "array" is a JSON array, i.e. a comma-separated list of values, enclosed in square brackets, like this:</para>\r
+                               <screen>[ "Goober", 629, null, false, "glub" ]</screen>\r
+                       </listitem>     \r
+               </itemizedlist>                                 \r
+       </simplesect>\r
+       <simplesect>\r
+               <title>The Examples</title>\r
+               <para>The test_json_query utility generated the SQL for all of the sample queries in this tutorial. Newlines and indentation were then inserted manually for readability.</para>\r
+               <para>All examples involve the actor.org_unit table, sometimes in combination with a few related tables. The queries themselves are designed to illustrate the syntax, not \r
+               to do anything useful at the application level. For example, it's not meaningful to take the square root of an org_unit id, except to illustrate how to code a function call. \r
+               The examples are like department store mannequins -- they have no brains, they're only for display.</para>\r
+               <para>The simplest kind of query defines nothing but a FROM clause. For example:</para>\r
+               <programlisting>\r
+               {\r
+                       "from":"aou"\r
+               }\r
+               </programlisting>\r
+               <para>In this minimal example we select from only one table. Later we will see how to join multiple tables.</para>\r
+               <para>Since we don't supply a WHERE clause, json_query constructs a default WHERE clause for us, including all the available columns. The resulting SQL looks like this:</para>\r
+               <programlisting>\r
+               SELECT\r
+                   "aou".billing_address AS "billing_address",\r
+                   "aou".holds_address   AS "holds_address",\r
+                   "aou".id              AS "id",\r
+                   "aou".ill_address     AS "ill_address",\r
+                   "aou".mailing_address AS "mailing_address",\r
+                   "aou".name            AS "name",\r
+                   "aou".ou_type         AS "ou_type",\r
+                   "aou".parent_ou       AS "parent_ou",\r
+                   "aou".shortname       AS "shortname",\r
+                   "aou".email           AS "email",\r
+                   "aou".phone           AS "phone",\r
+                   "aou".opac_visible    AS "opac_visible"\r
+               FROM\r
+                       actor.org_unit        AS "aou" ;        \r
+               </programlisting>\r
+        </simplesect>\r
+       <simplesect>\r
+               <title>Default SELECT Clauses</title>\r
+               <para>The default SELECT clause includes every column that the IDL defines it as a non-virtual field for the class in question. If a column is present in the database but \r
+               not defined in the IDL, json_query doesn't know about it. In the case of the example shown above, all the columns are defined in the IDL, so they all show up in the default \r
+               SELECT clause.</para>\r
+               <para>If the FROM clause joins two or more tables, the default SELECT clause includes columns only from the core table, not from any of the joined tables.</para>\r
+               <para>The default SELECT clause has almost the same effect as "SELECT *", but not exactly. If you were to "SELECT * from actor.org_unit_type in psql, the output would \r
+               include all the same columns as in the example above, but not in the same order. A default SELECT clause includes the columns in the order in which the IDL defines them, \r
+               which may be different from the order in which the database defines them.</para>        \r
+               <para>In practice, the sequencing of columns in the SELECT clause is not significant. The result set is returned to the client program in the form of a data structure, which \r
+               the client program can navigate however it chooses.</para>\r
+       </simplesect>   \r
+       <simplesect>\r
+               <title>Other Lessons</title>\r
+               <para>There are other ways to get a default SELECT clause. However, default SELECT clauses are a distraction at this point, because most of the time you'll specify your \r
+               own SELECT clause explicitly, as we will discuss later.</para>\r
+               <para>Let's consider some more important aspects of this simple example -- more important because they apply to more complex queries as well.</para>\r
+               <itemizedlist>\r
+                       <listitem>\r
+                               <para> The entire JSON query is an object. In this simple case the object includes only one entry, for the FROM clause. Typically you'll also have entries \r
+                               for the SELECT clause and the WHERE clause, and possibly for HAVING, ORDER BY, LIMIT, or OFFSET clauses. There is no separate entry for a GROUP BY clause, which you \r
+                               can specify by other means.</para>      \r
+                       </listitem>\r
+                       <listitem>\r
+                               <para>Although all the other entries are optional, you must include an entry for the FROM clause. You cannot, for example, do a SELECT USER the way \r
+                               you can in psql.</para> \r
+                       </listitem>\r
+                       <listitem>\r
+                               <para>Every column is qualified by an alias for the table. This alias is always the class name for the table, as defined in the IDL.</para>     \r
+                       </listitem>\r
+                       <listitem>\r
+                               <para>Every column is aliased with the column name. There is a way to choose a different column alias (not shown here).</para>  \r
+                       </listitem>     \r
+               </itemizedlist>\r
+       </simplesect>\r
+       <simplesect>\r
+               <title>The SELECT Clause</title>\r
+               <para>The following variation also produces a default SELECT clause:</para>\r
+               <programlisting>\r
+               {\r
+                       "from":"aou",\r
+                       "select": {\r
+                       "aou":"*"\r
+                       }\r
+               }\r
+               </programlisting>\r
+               <para>...and so does this one:</para>\r
+               <programlisting>\r
+               {\r
+                       "select": {\r
+                       "aou":null\r
+                       },\r
+                       "from":"aou"\r
+               }\r
+               </programlisting>\r
+               <para>While this syntax may not be terribly useful, it does illustrate the minimal structure of a SELECT clause in a JSON query: an entry in the outermost JSON object, \r
+               with a key of <quote>select</quote>. The value associated with this key is another JSON object, whose keys are class names.</para>\r
+               <para>(These two examples also illustrate another point: unlike SQL, a JSON query doesn't care whether the FROM clause or the SELECT clause comes first.)</para>\r
+               <para>Usually you don't want the default SELECT clause. Here's how to select only some of the columns:</para>\r
+               <programlisting>\r
+               {\r
+                       "from":"aou",\r
+                       "select": {\r
+                       "aou":[ "id", "name" ]\r
+                        }\r
+               }\r
+               </programlisting>\r
+               <para>The value associated with the class name is an array of column names. If you select columns from multiple tables (not shown here), you'll need a separate entry for each table, \r
+               and a separate column list for each entry.</para>\r
+               <para>The previous example results in the following SQL:</para>\r
+               <programlisting>\r
+               SELECT\r
+                       "aou".id       AS "id",\r
+                       "aou".name     AS "name"\r
+               FROM\r
+                       actor.org_unit AS "aou" ;\r
+               </programlisting>\r
+       </simplesect>\r
+       <simplesect>\r
+               <title>Fancier SELECT Clauses</title>\r
+               <para>The previous example featured an array of column names. More generally, it featured an array of field specifications, and one kind of field specification is a column name. \r
+               The other kind is a JSON object, with some combination of the following keys:</para>\r
+               <itemizedlist>\r
+                       <listitem>\r
+                               <para><quote>column</quote> -- the column name (required).</para>       \r
+                       </listitem>\r
+                       <listitem>\r
+                               <para><quote>alias</quote> -- used to define a column alias, which otherwise defaults to the column name.</para>        \r
+                       </listitem>\r
+                       <listitem>\r
+                               <para><quote>aggregate</quote> -- takes a value of true or false. Don't worry about this one yet. It concerns the use of GROUP BY clauses, which we will examine \r
+                               later.</para>   \r
+                       </listitem>\r
+                       <listitem>\r
+                               <para><quote>transform</quote> -- the name of an SQL function to be called.</para>      \r
+                       </listitem>\r
+                               <listitem>\r
+                               <para><quote>result_field</quote> -- used with "transform"; specifies an output column of a function that returns multiple columns at a time.</para>    \r
+                       </listitem>\r
+                       <listitem>\r
+                               <para><quote>params</quote> -- used with "transform"; provides a list of parameters for the function. They may be strings, numbers, or nulls.</para>    \r
+                       </listitem>     \r
+               </itemizedlist> \r
+               <para>This example assigns a different column alias:</para>\r
+               <programlisting>\r
+               {\r
+                       "from":"aou",\r
+                       "select": {\r
+                               "aou": [\r
+                                       "id",\r
+                                       { "column":"name", "alias":"org_name" }\r
+                               ]\r
+                       }\r
+               }\r
+\r
+               SELECT\r
+                       "aou".id AS "id",\r
+                       "aou".name AS "org_name"\r
+               FROM\r
+                       actor.org_unit AS "aou" ;\r
+               </programlisting>\r
+               <para>In this case, changing the column alias doesn't accomplish much. But if we were joining to the actor.org_unit_type table, which also has a "name" column, we could \r
+               use different aliases to distinguish them.</para>\r
+               <para>The following example uses a function to raise a column to upper case:</para>\r
+               <programlisting>\r
+               {\r
+                       "from":"aou",\r
+                       "select": {\r
+                               "aou": [\r
+                                       "id",\r
+                                       { "column":"name", "transform":"upper" }\r
+                               ]\r
+                       }\r
+               }\r
+                               \r
+               SELECT\r
+                       "aou".id           AS "id",\r
+                       upper("aou".name ) AS "name"\r
+               FROM\r
+                       actor.org_unit     AS "aou" ;\r
+               </programlisting>\r
+               <para>Here we take a substring of the name, using the "params" element to pass parameters:</para>\r
+               <programlisting>\r
+               {\r
+                       "from":"aou",\r
+                       "select": {\r
+                               "aou": [\r
+                                       "id", {\r
+                                               "column":"name",\r
+                                               "transform":"substr",\r
+                                               "params":[ 3, 5 ]\r
+                                       }\r
+                               ]\r
+                       }\r
+               }\r
+                               \r
+               SELECT\r
+                       "aou".id AS "id",\r
+                       substr("aou".name,'3','5' ) AS "name"\r
+               FROM\r
+                       actor.org_unit AS "aou" ;\r
+               </programlisting>\r
+               <para>The parameters specified with <quote>params</quote> are inserted after the applicable column (<quote>name</quote> in this case), which is always the first parameter. They are always                     passed as strings, i.e. enclosed in quotes, even if the JSON expresses them as numbers. PostgreSQL will ordinarily coerce them to the right type. However if the function name is overloaded                    to accept different types, PostgreSQL may invoke a function other than the one intended.</para>\r
+               <para>Finally we call a fictitious function "frobozz" that returns multiple columns, where we want only one of them:</para>\r
+               <programlisting>\r
+               {\r
+                       "from":"aou",\r
+                       "select": {\r
+                               "aou": [\r
+                                       "id", {\r
+                                               "column":"name",\r
+                                               "transform":"frobozz",\r
+                                               "result_field":"zamzam"\r
+                                       }\r
+                               ]\r
+                       }\r
+               }\r
+                               \r
+               SELECT\r
+                       "aou".id                        AS "id",\r
+                       (frobozz("aou".name ))."zamzam" AS "name"\r
+               FROM\r
+                       actor.org_unit                  AS "aou" ;\r
+               </programlisting>\r
+               <para>The "frobozz" function doesn't actually exist, but json_query doesn't know that. The query won't fail until json_query tries to execute it in \r
+               the database.</para>\r
+       </simplesect>\r
+       <simplesect>\r
+               <title>Things You Can't Do</title>\r
+               <para>You can do some things in a SELECT clause with raw SQL (with psql, for example) that you can't do with a JSON query. Some of them matter and some of them don't.</para>\r
+               <para>When you do a JOIN, you can't arrange the selected columns in any arbitrary sequence, because all of the columns from a given table must be grouped together. \r
+               This limitation doesn't matter. The results are returned in the form of a data structure, which the client program can navigate however it likes.</para>\r
+               <para>You can't select an arbitrary expression, such as "percentage / 100" or "last_name || ', ' || first_name". Most of the time this limitation doesn't matter either, because \r
+               the client program can do these kinds of manipulations for itself. However, function calls may be a problem. You can't nest them, and you can't pass more than one column value \r
+               to them (and it has to be the first parameter).</para>  \r
+               <para>You can't use a CASE expression. Instead, the client application can do the equivalent branching for itself.</para>\r
+               <para>You can't select a subquery. In raw SQL you can do something like the following:</para>\r
+               <programlisting>\r
+               SELECT\r
+                       id,\r
+                       name,\r
+                       (\r
+                               SELECT name\r
+                               FROM actor.org_unit_type AS aout\r
+                               WHERE aout.id = aou.ou_type\r
+                       ) AS type_name\r
+               FROM\r
+                       actor.org_unit AS aou;\r
+               </programlisting>\r
+               <para>This contrived example is not very realistic. Normally you would use a JOIN in this case, and that's what you should do in a JSON query. Other cases may not be so \r
+               easy to solve.</para>\r
+       </simplesect>\r
+       <simplesect>\r
+               <title>The WHERE Clause</title>\r
+               <para>Most queries need a WHERE clause, as in this simple example:</para>\r
+               <programlisting>\r
+               {\r
+                       "from":"aou",\r
+                       "select": { "aou":[ "id", "name" ] },\r
+                       "where": {\r
+                               "parent_ou":"3"\r
+                       }\r
+               }\r
+               </programlisting>\r
+               <para>Like the SELECT clause, the WHERE clause gets its own entry in the top-level object of a JSON query. The key is <quote>where</quote>, and the associated value is either \r
+               an object (as shown here) or an array (to be discussed a bit later). Each entry in the object is a separate condition.</para>\r
+               <para>In this case, we use a special shortcut for expressing an equality condition. The column name is on the left of the colon, and the value to which we are equating it is on \r
+               the right.</para>\r
+               <para>Here's the resulting SQL:</para>\r
+               <programlisting>\r
+               SELECT\r
+                       "aou".id       AS "id",\r
+                       "aou".name     AS "name"\r
+               FROM\r
+                       actor.org_unit AS "aou"\r
+               WHERE\r
+                       "aou".parent_ou = 3;\r
+               </programlisting>\r
+               <para>Like the SELECT clause, the generated WHERE clause qualifies each column name with the alias of the relevant table.</para>\r
+               <para>If you want to compare a column to NULL, put <quote>null</quote> (without quotation marks) to the right of the colon instead of a literal value. The \r
+               resulting SQL will include <quote>IS NULL</quote> instead of an equals sign.</para>\r
+       </simplesect>\r
+       <simplesect>\r
+               <title>Other Kinds of Comparisons</title>\r
+               <para>Here's the same query (which generates the same SQL) without the special shortcut:</para>\r
+               <programlisting>\r
+               {\r
+                       "from":"aou",\r
+                       "select": { "aou":[ "id", "name" ] },\r
+                       "where": {\r
+                               "parent_ou":{ "=":3 }\r
+                       }\r
+               }\r
+               </programlisting>\r
+               <para>We still have an entry whose key is the column name, but this time the associated value is another JSON object. It must contain exactly one entry, \r
+               with the comparison operator on the left of the colon, and the value to be compared on the right.</para>\r
+               <para>The same syntax works for other kinds of comparison operators. For example:</para>\r
+               <programlisting>\r
+               {\r
+                       "from":"aou",\r
+                       "select": { "aou":[ "id", "name" ] },\r
+                       "where": {\r
+                               "parent_ou":{ ">":3 }\r
+                       }\r
+               }\r
+               </programlisting>\r
+               <para>...turns into:</para>\r
+               <programlisting>\r
+               SELECT\r
+                       "aou".id       AS "id",\r
+                       "aou".name     AS "name"\r
+               FROM\r
+                       actor.org_unit AS "aou"\r
+               WHERE\r
+                       "aou".parent_ou > 3 ;\r
+               </programlisting>\r
+               <para>The condition '<quote>=</quote>:null' turns into IS NULL. Any other operator used with <quote>null</quote> turns into IS NOT NULL.</para>\r
+               <para>You can use most of the comparison operators recognized by PostgreSQL:</para>\r
+               <literallayout> \r
+               =    &lt;&gt;   !=\r
+               &lt;    &gt;    &lt;=   &gt;=\r
+               ~    ~*   !~   !~*\r
+               like      ilike\r
+               similar to\r
+               </literallayout>        \r
+               <para>The only ones you can't use are <quote>is distinct from</quote> and <quote>is not distinct from</quote>.</para>           \r
+       </simplesect>\r
+       <simplesect>\r
+               <title>Custom Comparisons</title>\r
+               <para>Here's a dirty little secret: json_query doesn't really pay much attention to the operator you supply. It merely checks to make sure that the operator doesn't contain \r
+               any semicolons or white space, in order to prevent certain kinds of SQL injection. It also allows "similar to" as a special exception.</para>\r
+               <para>As a result, you can slip an operator of your own devising into the SQL, so long as it doesn't contain any semicolons or white space, and doesn't create invalid syntax. \r
+               Here's a contrived and rather silly example:</para>\r
+               <programlisting>\r
+               {\r
+                       "from":"aou",\r
+                       "select": { "aou":[ "id", "name" ] },\r
+                       "where": {\r
+                               "parent_ou":{ "&lt;2+":3 }\r
+                       }\r
+               }\r
+               </programlisting>\r
+               <para>...which results in the following SQL:</para>\r
+               <programlisting>\r
+               SELECT\r
+                       "aou".id       AS "id",\r
+                       "aou".name     AS "name"\r
+               FROM\r
+                       actor.org_unit AS "aou"\r
+               WHERE\r
+                       "aou".parent_ou &lt;2+ 3;\r
+               </programlisting>\r
+               <para>It's hard to come up with a realistic case where this hack would be useful, but it could happen.</para>\r
+       </simplesect>\r
+\r
+\r
+       <simplesect>\r
+               <title>Comparing One Column to Another</title>\r
+               <para>Here's how to put another column on the right hand side of a comparison:</para>\r
+               \r
+               <programlisting>\r
+               {\r
+                       "from":"aou",\r
+                       "select": { "aou":[ "id", "name" ] },\r
+                       "where": {\r
+                               "id": { "&gt;": { "+aou":"parent_ou" } }\r
+                       }\r
+               };\r
+               </programlisting>\r
+               <para>This syntax is similar to the previous examples, except that instead of comparing to a literal value, we compare to an object. This object has only a single entry, \r
+               whose key is a table alias preceded by a leading plus sign. The associated value is the name of the column.</para>\r
+               <para>Here's the resulting SQL:</para>\r
+               <programlisting>\r
+               SELECT\r
+                       "aou".id AS "id",\r
+                       "aou".name AS "name"\r
+               FROM\r
+                       actor.org_unit AS "aou"\r
+               WHERE\r
+               (\r
+                       "aou".id &gt; (  "aou".parent_ou  )\r
+               );\r
+               </programlisting>\r
+               <para>The table alias must correspond to the appropriate table. Since json_query doesn't validate the choice of alias, it won't detect an invalid alias until it tries to \r
+               execute the query. In this simple example there's only one table to choose from. The choice of alias is more important in a subquery or join.</para>\r
+               <para>The leading plus sign, combined with a table alias, can be used in other situations to designate the table to which a column belongs. We shall defer a discussion of \r
+               this usage to the section on joins.</para>\r
+       </simplesect>           \r
+       <simplesect>\r
+               <title>Testing Boolean Columns</title>\r
+               <para>In SQL, there are several ways to test a boolean column such as actor.org_unit.opac_visible. The most obvious way is to compare it to true or false:</para>\r
+               <programlisting>\r
+               SELECT\r
+                       id\r
+               FROM\r
+                       actor.org_unit\r
+               WHERE\r
+                       opac_visible = true;\r
+               </programlisting>\r
+               <para>In a JSON query this approach doesn't work. If you try it, the "= true" test will turn into IS NULL. Don't do that. Instead, use a leading plus sign, as described in \r
+               the preceding section, to treat the boolean column as a stand-alone condition:</para>\r
+               <programlisting>\r
+               {\r
+                       "from":"aou",\r
+                       "select": { "aou":[ "id" ] },\r
+                       "where": {\r
+                               "+aou":"opac_visible"\r
+                       }\r
+               }\r
+               </programlisting>\r
+\r
+               <para>Result:</para>\r
+               <programlisting>\r
+               SELECT\r
+                       "aou".id AS "id"\r
+               FROM\r
+                       actor.org_unit AS "aou"\r
+               WHERE\r
+                       "aou".opac_visible ;\r
+               </programlisting>\r
+               <para>If you need to test for falsity, then write a test for truth and negate it with the "-not" operator. We will discuss the "-not" operator later, but here's a preview:</para>\r
+               <programlisting>\r
+               {\r
+                       "from":"aou",\r
+                       "select": { "aou":[ "id" ] },\r
+                       "where": {\r
+                               "-not": {\r
+                                       "+aou":"opac_visible"\r
+                               }\r
+                       }\r
+               }                       \r
+\r
+               SELECT\r
+                       "aou".id AS "id"\r
+               FROM\r
+                       actor.org_unit AS "aou"\r
+               WHERE\r
+                       NOT (  "aou".opac_visible  );           \r
+               </programlisting>\r
+               <para>You can also compare a boolean column directly to a more complex condition:</para>\r
+               <programlisting>\r
+               {\r
+                       "from":"aou",\r
+                       "select": { "aou":[ "id" ] },\r
+                       "where": {\r
+                               "opac_visible": {\r
+                               "=": { "parent_ou":{ "&gt;":3 } }\r
+                               }\r
+                        }\r
+               }\r
+               </programlisting>\r
+               <para>Here we compare a boolean column, not to a literal value, but to a boolean expression. The resulting SQL looks a little goofy, but it works:</para>\r
+               <programlisting>\r
+               SELECT\r
+                       "aou".id AS "id"\r
+               FROM\r
+                       actor.org_unit AS "aou"\r
+               WHERE\r
+                       (\r
+                               "aou".opac_visible = ( "aou".parent_ou &gt; 3 )\r
+                       );\r
+               </programlisting>\r
+               <para>In this case we compare the boolean column to a single simple condition. However you can include additional complications -- multiple conditions, IN lists, \r
+               BETWEEN clauses, and other features as described below.</para>\r
+       </simplesect>\r
+       <simplesect>\r
+               <title>Multiple Conditions</title>\r
+               <para>If you need multiple conditions, just add them to the "where" object, separated by commas:</para>\r
+               <programlisting>\r
+               {\r
+                       "from":"aou",\r
+                       "select": { "aou":[ "id", "name" ] },\r
+                       "where": {\r
+                               "parent_ou":{ "&gt;":3 },\r
+                               "id":{ "&lt;&gt;":7 }\r
+                       }\r
+               }\r
+               </programlisting>\r
+               <para>The generated SQL connects the conditions with AND:</para>\r
+               <programlisting>\r
+               SELECT\r
+                       "aou".id       AS "id",\r
+                       "aou".name     AS "name"\r
+               FROM\r
+                       actor.org_unit AS "aou"\r
+               WHERE\r
+                       "aou".parent_ou g 3\r
+                       AND "aou".id &lt;&gt; 7;\r
+               </programlisting>\r
+               <para>Later we will see how to use OR instead of AND.</para>\r
+       </simplesect>\r
+       <simplesect>\r
+               <title>Using Arrays</title>\r
+               <para>Here's a puzzler. Suppose you need two conditions for the same column. How do you code them in the same WHERE clause? For example, suppose you want something like this:</para>\r
+               <programlisting>\r
+               SELECT\r
+                       id,\r
+                       name\r
+               FROM\r
+                       actor.org_unit\r
+               WHERE\r
+                       parent_ou &gt; 3\r
+                       AND parent_ou &lt;&gt; 7;\r
+               </programlisting>\r
+               <para>You might try a WHERE clause like this:</para>\r
+               <programlisting>\r
+               "where": {\r
+                       "parent_ou":{ "&gt;":3 },\r
+                       "parent_ou":{ "&lt;&gt;":7 }\r
+                }\r
+               </programlisting>\r
+               <para>Nope. Won't work. According to JSON rules, two entries in the same object can't have the same key.</para>\r
+               <para>After slapping yourself in the forehead, you try something a little smarter:</para>\r
+               <programlisting>\r
+               "where": {\r
+                       "parent_ou": {\r
+                               "&gt;":3,\r
+                               "&lt;&gt;":7\r
+                       }\r
+               }\r
+               </programlisting>\r
+               <para>Nice try, but that doesn't work either. Maybe it ought to work -- at least it's legal JSON -- but, no.</para>\r
+               <para>Here's what works:</para>\r
+               <programlisting>\r
+               {\r
+                       "from":"aou",\r
+                       "select": { "aou":[ "id", "name" ] },\r
+                       "where": [\r
+                               { "parent_ou":{ "&gt;":3 } },\r
+                               { "parent_ou":{ "&lt;>":7 } }\r
+                       ]\r
+               }\r
+               </programlisting>\r
+               <para>We wrapped the two conditions into two separate JSON objects, and then wrapped those objects together into a JSON array. The resulting SQL looks like this:</para>\r
+               <programlisting>\r
+               SELECT\r
+                       "aou".id       AS "id",\r
+                       "aou".name     AS "name\r
+               FROM\r
+                       actor.org_unit AS "aou"\r
+               WHERE\r
+                       ( "aou".parent_ou &gt; 3 )\r
+               AND\r
+                       ( "aou".parent_ou &lt;&gt; 7 );\r
+               </programlisting>\r
+               <para>That's not quite what we were hoping for, because the extra parentheses are so ugly. But they're harmless. This will do.</para>\r
+               <para>If you're in the mood, you can use arrays to as many parentheses as you like, even if there is only one condition inside:</para>\r
+               <programlisting>\r
+               {\r
+                       "from":"aou",\r
+                       "select": { "aou":[ "id", "name" ] },\r
+                       "where":\r
+                       [[[[[[\r
+                               {\r
+                                       "parent_ou":{ "&gt;":3 }\r
+                                },\r
+                       ]]]]]]\r
+               }       \r
+               </programlisting>\r
+               <para>...yields:</para>\r
+               <programlisting>\r
+               SELECT\r
+                       "aou".id       AS "id",\r
+                       "aou".name     AS "name"\r
+               FROM\r
+                       actor.org_unit AS "aou"\r
+               WHERE\r
+                       ( ( ( ( ( ( "aou".parent_ou &gt; 3 ) ) ) ) ) );\r
+               </programlisting>\r
+       </simplesect>\r
+       <simplesect>\r
+               <title>How to OR</title>\r
+               <para>By default, json_query combines conditions with AND. When you need OR, here's how to do it:</para>                \r
+               <programlisting>\r
+               {\r
+                       "from":"aou",\r
+                       "select": { "aou":[ "id", "name" ] },\r
+                       "where": {\r
+                               "-or": {\r
+                                       "id":2,\r
+                                       "parent_ou":3\r
+                               }\r
+                       }\r
+               }\r
+               </programlisting>\r
+               <para>We use "-or" as the key, with the conditions to be ORed in an associated object. The leading minus sign is there to make sure that the operator isn't confused with a \r
+               column name. Later we'll see some other operators with leading minus signs. In a couple of spots we even use plus signs.</para>\r
+               <para>Here are the results from the above example:</para>\r
+               <programlisting>\r
+               SELECT\r
+                       "aou".id AS "id",\r
+                       "aou".name AS "name"\r
+               FROM\r
+                       actor.org_unit AS "aou"\r
+               WHERE\r
+                       (\r
+                               "aou".id = 2\r
+                               OR "aou".parent_ou = 3\r
+                       );\r
+               </programlisting>\r
+               <para>The conditions paired with "-or" are linked by OR and enclosed in parentheses.</para>\r
+               <para>Here's how to do the same thing using an array, except that it produces an extra layer of parentheses:</para>\r
+               <programlisting>\r
+               {\r
+                   "from":"aou",\r
+                   "select": { "aou":[ "id", "name" ] },\r
+                   "where": {\r
+                       "-or": [\r
+                           { "id":2 },\r
+                           { "parent_ou":3 }\r
+                       ]\r
+                   }\r
+               }\r
+               SELECT\r
+                   "aou".id AS "id",\r
+                   "aou".name AS "name"\r
+               FROM\r
+                   actor.org_unit AS "aou"\r
+               WHERE\r
+                   (\r
+                       ( "aou".id = 2 )\r
+                       OR ( "aou".parent_ou = 3 )\r
+                   );\r
+               </programlisting>\r
+               <para>It's possible, though not very useful, to have only a single condition subject to the "-or" operator. In that case, the condition appears by itself, since there's nothing \r
+               to OR it to. This trick is another way to add an extraneous layer of parentheses.</para>\r
+       </simplesect>\r
+       <simplesect>\r
+               <title>Another way to AND</title>\r
+               <para>You can also use the <quote>-and</quote> operator. It works just like "-or", except that it combines conditions with AND instead of OR. Since AND is the default, we don't usually \r
+               need a separate operator for it, but it's available.</para>\r
+               <para>In rare cases, nothing else will do -- you can't include two conditions in the same list because of the duplicate key problem, but you can't combine them with \r
+               arrays either. In particular, you might need to combine them within an expression that you're comparing to a boolean column (see the subsection above on Testing Boolean Columns).</para>\r
+       </simplesect>\r
+       <simplesect>\r
+               <title>Negation with NOT</title>\r
+               <para>The <quote>-not</quote> operator negates a condition or set of conditions. For example:</para>\r
+               <programlisting>\r
+               {\r
+                       "from":"aou",\r
+                       "select": { "aou":[ "id", "name" ] },\r
+                       "where": {\r
+                               "-not": {\r
+                                       "id":{ "&gt;":2 },\r
+                                       "parent_ou":3\r
+                               }\r
+                       }\r
+               }\r
+                               \r
+               SELECT\r
+                       "aou".id AS "id",\r
+                       "aou".name AS "name"\r
+               FROM\r
+                       actor.org_unit AS "aou"\r
+               WHERE\r
+                       NOT\r
+                       (\r
+                               "aou".id &gt; 2\r
+                               AND "aou".parent_ou = 3\r
+                       );\r
+               </programlisting>\r
+               <para>In this example we merely negate a combination of two comparisons. However the condition to be negated may be as complicated as it needs to be. Anything that can be \r
+               subject to <quote>where</quote> can be subject to <quote>-not</quote>.</para>\r
+               <para>In most cases you can achieve the same result by other means. However the <quote>-not</quote> operator is the only way to represent NOT BETWEEN \r
+               (to be discussed later).</para> \r
+       </simplesect>\r
+       <simplesect>\r
+               <title>EXISTS with Subqueries</title>\r
+               <para>Two other operators carry a leading minus sign: <quote>-exists</quote> and its negation <quote>-not-exists</quote>. These operators apply to subqueries, which have the \r
+               same format as a full query. For example:</para>\r
+               <programlisting>        \r
+               {\r
+                       "from":"aou",\r
+                       "select": { "aou":[ "id", "name" ] },\r
+                       "where": {\r
+                               "-exists": {\r
+                                       "from":"asv",\r
+                                       "select":{ "asv":[ "id" ] },\r
+                                       "where": {\r
+                                               "owner":7\r
+                                       }\r
+                               }\r
+                       }\r
+               }\r
+                               \r
+               SELECT\r
+                       "aou".id AS "id",\r
+                       "aou".name AS "name"\r
+               FROM\r
+                       actor.org_unit AS "aou"\r
+               WHERE\r
+               EXISTS\r
+                       (\r
+                       SELECT "asv".id AS "id"\r
+                       FROM action.survey AS "asv"\r
+                       WHERE "asv".owner = 7\r
+                       );\r
+               </programlisting>\r
+               <para>This kind of subquery is of limited use, because its WHERE clause doesn't have anything to do with the main query. It just shuts down the main query altogether \r
+               if it isn't satisfied.</para>\r
+               <para>More typical is a correlated subquery, whose WHERE clause refers to a row from the main query. For example:</para>\r
+               <programlisting>        \r
+               {\r
+                       "from":"aou",\r
+                       "select": { "aou":[ "id", "name" ] },\r
+                       "where": {\r
+                               "-exists": {\r
+                                       "from":"asv",\r
+                                       "select":{ "asv":[ "id" ] },\r
+                                       "where": {\r
+                                               "owner":{ "=":{ "+aou":"id" }}\r
+                                       }\r
+                               }\r
+                        }\r
+               }       \r
+               </programlisting>\r
+               <para>Note the use of <quote>+aou</quote> to qualify the id column in the inner WHERE clause.</para>\r
+               <programlisting>        \r
+               SELECT\r
+                       "aou".id AS "id",\r
+                       "aou".name AS "name"\r
+               FROM\r
+                       actor.org_unit AS "aou"\r
+               WHERE\r
+                       EXISTS\r
+                       (\r
+                               SELECT  "asv".id AS "id"\r
+                               FROM action.survey AS "asv"\r
+                               WHERE ("asv".owner = (  "aou".id  ))\r
+                       );\r
+               </programlisting>\r
+               <para>This latter example illustrates the syntax, but in practice, it would probably be more natural to use an IN clause with a subquery (to be discussed later).</para>\r
+       </simplesect>\r
+       <simplesect>\r
+               <title>BETWEEN Clauses</title>\r
+               <para>Here's how to express a BETWEEN clause:</para>\r
+               <programlisting>        \r
+               {\r
+                       "from":"aou",\r
+                       "select": { "aou":[ "id" ] },\r
+                       "where": {\r
+                               "parent_ou": { "between":[ 3, 7 ] }\r
+                       }\r
+               }\r
+               </programlisting>\r
+               <para>The value associated with the column name is an object with a single entry, whose key is "between". The corresponding value is an array with exactly two values, defining the \r
+               range to be tested.</para>\r
+               <para>The range bounds must be either numbers or string literals. Although SQL allows them to be null, a null doesn't make sense in this context, because a null never matches \r
+               anything. Consequently json_query doesn't allow them.</para>\r
+               <para>The resulting SQL is just what you would expect:</para>\r
+               <programlisting>        \r
+               SELECT\r
+                       "aou".id AS "id"\r
+               FROM\r
+                       actor.org_unit AS "aou"\r
+               WHERE\r
+                       parent_ou BETWEEN '3' AND '7';\r
+               </programlisting>\r
+       </simplesect>\r
+       <simplesect>\r
+               <title>IN and NOT IN Lists</title>\r
+               <para>There are two ways to code an IN list. One way is simply to include the list of values in an array:</para>\r
+               <programlisting>        \r
+               {\r
+                       "from":"aou",\r
+                       "select": { "aou":[ "id", "name" ] },\r
+                       "where": {\r
+                               "parent_ou": [ 3, 5, 7 ]\r
+                       }\r
+               }\r
+               </programlisting>\r
+               <para>As with a BETWEEN clause, the values in the array must be numbers or string literals. Nulls aren't allowed. Here's the resulting SQL, which again is just what \r
+               you would expect:</para>\r
+               <programlisting>        \r
+               SELECT\r
+                       "aou".id AS "id",\r
+                       "aou".name AS "name"\r
+               FROM\r
+                       actor.org_unit AS "aou"\r
+               WHERE\r
+                       "aou".parent_ou IN (3, 5, 7);\r
+               </programlisting>\r
+               <para>The other way is similar to the syntax shown above for a BETWEEN clause, except that the array may include any non-zero number of values:</para>\r
+               <programlisting>        \r
+               {\r
+                       "from":"aou",\r
+                       "select": { "aou":[ "id", "name" ] },\r
+                       "where": {\r
+                               "parent_ou": { "in": [ 3, 5, 7 ] }\r
+                       }\r
+               }\r
+               </programlisting>\r
+               <para>This version results in the same SQL as the first one.</para>\r
+               <para>For a NOT IN list, you can use the latter format, using the <quote>not in</quote> operator instead of <quote>in</quote>. Alternatively, you can use either format together with \r
+               the <quote>-not</quote> operator.</para>        \r
+       </simplesect>\r
+       <simplesect>\r
+               <title>IN and NOT IN Clauses with Subqueries</title>\r
+               <para>For an IN clause with a subquery, the syntax is similar to the second of the two formats for an IN list (see the previous subsection). The "in" or "not in" operator \r
+               is paired, not with an array of values, but with an object representing the subquery. For example:</para>\r
+               <programlisting>        \r
+               {\r
+                       "from":"aou",\r
+                       "select": { "aou":[ "id", "name" ] },\r
+                       "where": {\r
+                               "id": {\r
+                                       "in": {\r
+                                               "from":"asv",\r
+                                               "select":{ "asv":[ "owner" ] },\r
+                                               "where":{ "name":"Voter Registration" }\r
+                                               }\r
+                                       }\r
+                       }\r
+               }\r
+               </programlisting>\r
+               <para>The results:</para>\r
+               <programlisting>        \r
+               SELECT\r
+                       "aou".id AS "id",\r
+                       "aou".name AS "name"\r
+               FROM\r
+                       actor.org_unit AS "aou"\r
+               WHERE\r
+                       "aou".id IN\r
+                       (\r
+                               SELECT\r
+                                       "asv".owner AS "owner"\r
+                               FROM\r
+                                       action.survey AS "asv"\r
+                               WHERE\r
+                                       "asv".name = 'Voter Registration'\r
+                       );\r
+               </programlisting>\r
+               <para>In SQL the subquery may select multiple columns, but in a JSON query it can select only a single column.</para>\r
+               <para>For a NOT IN clause with a subquery, use the <quote>not in</quote> operator instead of <quote>in</quote>.</para>\r
+       </simplesect>\r
+       <simplesect>\r
+               <title>Comparing to a Function</title>\r
+               <para>Here's how to compare a column to a function call:</para>\r
+               <programlisting>        \r
+               {\r
+                       "from":"aou",\r
+                       "select": { "aou":[ "id", "name" ] },\r
+                       "where": {\r
+                               "id":{ ">":[ "sqrt", 16 ] }\r
+                        }\r
+               }\r
+               </programlisting>\r
+               <para>A comparison operator ("&gt;" in this case) is paired with an array. The first entry in the array must be a string giving the name of the function. The remaining parameters, \r
+               if any, are the parameters. They may be strings, numbers, or nulls. The resulting SQL for this example:</para>\r
+               <programlisting>        \r
+               SELECT\r
+                       "aou".id AS "id",\r
+                       "aou".name AS "name"\r
+               FROM\r
+                       actor.org_unit AS "aou"\r
+               WHERE\r
+                       "aou".id > sqrt( '16' );\r
+               </programlisting>\r
+               <para>All parameters are passed as quoted strings -- even if, as in this case, they are really numbers.</para>\r
+               <para>This syntax is somewhat limited in that the function parameters must be constants (hence the use of a silly example).</para>\r
+       </simplesect>\r
+       <simplesect>\r
+               <title>Putting a Function Call on the Left</title>\r
+               <para>In the discussion of the SELECT clause, we saw how you could transform the value of a selected column by passing it to a function. In the WHERE clause, you can \r
+               use similar syntax to transform the value of a column before comparing it to something else.</para>\r
+               <para>For example:</para>\r
+               <programlisting>        \r
+               {\r
+                       "from":"aou",\r
+                       "select": { "aou":[ "id", "name" ] },\r
+                       "where": {\r
+                               "name": {\r
+                                       "=": {\r
+                                               "transform":"upper",\r
+                                               "value":"CARTER BRANCH"\r
+                                       }\r
+                               }\r
+                       }\r
+               }\r
+               </programlisting>\r
+               <para>The "transform" entry gives the name of the function that we will use on the left side of the comparison. The "value" entry designates the value on the right side \r
+               of the comparison.</para>\r
+               <programlisting>        \r
+               SELECT\r
+                       "aou".id AS "id",\r
+                       "aou".name AS "name"\r
+               FROM\r
+                       actor.org_unit AS "aou"\r
+               WHERE\r
+                       upper("aou".name ) =  'CARTER BRANCH' ;\r
+               </programlisting>\r
+               <para>As in the SELECT clause, you can pass literal values or nulls to the function as additional parameters by using an array tagged as <quote>params</quote>:</para>\r
+               <programlisting>        \r
+               {\r
+                       "from":"aou",\r
+                       "select": { "aou":[ "id", "name" ] },\r
+                       "where": {\r
+                               "name": {\r
+                                       "=": {\r
+                                               "transform":"substr",\r
+                                               "params":[ 1, 6 ],\r
+                                               "value":"CARTER"\r
+                                       }\r
+                               }\r
+                        }\r
+               }\r
+                       \r
+               SELECT\r
+                   "aou".id AS "id",\r
+                   "aou".name AS "name"\r
+               FROM\r
+                   actor.org_unit AS "aou"\r
+               WHERE\r
+                   substr("aou".name,'1','6' ) =  'CARTER' ;\r
+               </programlisting>\r
+               <para>The first parameter is always the column name, qualified by the class name, followed by any additional parameters (which are always enclosed in quotes even if they \r
+               are numeric).</para>\r
+               <para>As in the SELECT clause: if the function returns multiple columns, you can specify the one you want by using a "result_field" entry (not shown here).</para>\r
+               <para>If you leave out the "transform" entry (or misspell it), the column name will appear on the left without any function call. This syntax works, but it's more \r
+               complicated than it needs to be.</para>\r
+       </simplesect>\r
+\r
+       <simplesect>\r
+               <title>Putting Function Calls on Both Sides</title>\r
+               <para>If you want to compare one function call to another, you can use the same syntax shown in the previous subsection -- except that the "value" entry carries an \r
+               array instead of a literal value. For example:</para>\r
+               <programlisting>        \r
+               {\r
+                       "from":"aou",\r
+                       "select": { "aou":[ "id", "name" ] },\r
+                       "where": {\r
+                               "id": {\r
+                                       "&gt;": {\r
+                                               "transform":"factorial",\r
+                                               "value":[ "sqrt", 1000 ]\r
+                                       }\r
+                               }\r
+                       }\r
+               }               \r
+               SELECT\r
+                   "aou".id AS "id",\r
+                   "aou".name AS "name"\r
+               FROM\r
+                   actor.org_unit AS "aou"\r
+               WHERE\r
+                   factorial("aou".id ) &gt;  sqrt( '1000' ) ;\r
+               </programlisting>\r
+               <para>The format for the right side function is similar to what we saw earlier, in the subsection Comparing to a Function. Note that there are two different formats \r
+               for defining function calls:</para>\r
+               <itemizedlist>\r
+                       <listitem>\r
+                               <para>For a function call to the left of the comparison, the function name is tagged as <quote>transform</quote>. The first parameter is always the relevant \r
+                               column name; additional parameters, if any, are in an array tagged as "params". The entry for <quote>result_field</quote>, if present, specifies a subcolumn.</para>    \r
+                       </listitem>\r
+                       <listitem>\r
+                               <para>For a function call to the right of the comparison, the function name is the first entry in an array, together with any parameters. \r
+                               There's no way to specify a subcolumn.</para>   \r
+                       </listitem>\r
+               </itemizedlist> \r
+       </simplesect>\r
+       <simplesect>\r
+               <title>Comparing a Function to a Condition</title>\r
+               <para>So far we have seen two kinds of data for the "value" tag. A string or number translates to a literal value, and an array translates to a function call. \r
+               The third possibility is a JSON object, which translates to a condition. For example:</para>\r
+               <programlisting>        \r
+               {\r
+                       "from":"aou",\r
+                       "select": { "aou":[ "id", "name" ] },\r
+                       "where": {\r
+                               "id": {\r
+                                        "=": {\r
+                                               "value":{ "parent_ou":{ ">":3 } },\r
+                                               "transform":"is_prime"\r
+                                       }\r
+                               }\r
+                        }\r
+               }\r
+               </programlisting>\r
+               <para>The function tagged as <quote>transform</quote> must return boolean, or else json_query will generate invalid SQL. The function used here, <quote>is_prime</quote>, \r
+               is fictitious.</para>\r
+               <programlisting>        \r
+               SELECT\r
+                       "aou".id AS "id",\r
+                       "aou".name AS "name"\r
+               FROM\r
+                       actor.org_unit AS "aou"\r
+               WHERE\r
+               (\r
+                       is_prime("aou".id ) = ( "aou".parent_ou > 3 )\r
+               );\r
+               </programlisting>\r
+               <para>If we left out the <quote>transform</quote> entry, json_query would compare the column on the left (which would to be boolean) to the condition on the right. The results are similar \r
+               to those for a simpler format described earlier (see the subsection Testing Boolean Columns).</para>\r
+               <para>In the example above we compared the boolean to a simple condition. However the expression on the right may include multiple conditions, IN lists, subqueries, \r
+               and whatever other complications are necessary.</para>\r
+       </simplesect>\r
+       <simplesect>\r
+               <title>Things You Can't Do</title>\r
+               <para>The WHERE clause is subject to some of the same limitations as the SELECT clause. However, in the WHERE clause these limitations are more limiting, because \r
+               the client program can't compensate by doing some of the work for itself.</para>\r
+               <para>You can't use arbitrary expressions in a WHERE condition, such as "WHERE id &gt; parent_ou -- 3". In some cases you may be able to contrive a custom operator in order to \r
+               fake such an expression. However this mechanism is neither very general nor very aesthetic.</para>\r
+               <para>To the right of a comparison operator, all function parameters must be literals or null. You can't pass a column value, nor can you nest function calls.</para>\r
+               <para>Likewise you can't include column values or arbitrary expressions in an IN list or a BETWEEN clause.</para>\r
+               <para>You can't include null values in an IN list or a BETWEEN list, not that you should ever want to.</para>\r
+               <para>As noted earlier: you can't use the comparison operators <quote>is distinct from</quote> or <quote>is not distinct from</quote>.</para>\r
+               <para>Also as noted earlier: a subquery in an IN clause cannot select more than one column.</para>\r
+       </simplesect>\r
+       <simplesect>\r
+               <title>JOIN clauses</title>\r
+               <para>Until now, our examples have selected from only one table at a time. As a result, the FROM clause has been very simple -- just a single string containing \r
+               the class name of the relevant table.</para>\r
+               <para>When the FROM clause joins multiple tables, the corresponding JSON naturally gets more complicated.</para>\r
+               <para>SQL provides two ways to define a join. One way is to list both tables in the FROM clause, and put the join conditions in the WHERE clause:</para>\r
+               <programlisting>        \r
+               SELECT\r
+                       aou.id,\r
+                       aout.name\r
+               FROM\r
+                       actor.org_unit aou,\r
+                       actor.org_unit_type aout\r
+               WHERE\r
+                       aout.id = aou.ou_type;\r
+               </programlisting>\r
+               <para>The other way is to use an explicit JOIN clause:</para>\r
+               <programlisting>        \r
+               SELECT\r
+                       aou.id,\r
+                       aout.name\r
+               FROM\r
+                       actor.org_unit aou\r
+                               JOIN actor.org_unit_type aout\r
+                                       ON ( aout.id = aou.ou_type );\r
+               </programlisting>\r
+               <para>JSON queries use only the second of these methods. The following example expresses the same query in JSON:</para>\r
+               <programlisting>        \r
+               {\r
+                       "select": { "aou":[ "id" ], "aout":[ "name" ] },\r
+                       "from": {\r
+                               "aou":"aout"\r
+                       }\r
+               }\r
+               </programlisting>\r
+               <para>First, let's review the SELECT clause. Since it selects rows from two different tables, the data for <quote>select</quote> includes two entries, one for each table.</para>\r
+               <para>As for the FROM clause, it's no longer just a string. It's a JSON object, with exactly one entry. The key of this entry is the class name of the core table, i.e. \r
+               the table named immediately after the FROM keyword. The data associated with this key contains the rest of the information about the join. In this simple example, \r
+               that information consists entirely of a string containing the class name of the other table.</para>\r
+               <para>So where is the join condition?</para>\r
+               <para>It's in the IDL. Upon reading the IDL, json_query knows that actor.org_unit has a foreign key pointing to actor.org_unit_type, and builds a join condition accordingly:</para>\r
+               <programlisting>        \r
+               SELECT\r
+                       "aou".id AS "id",\r
+                       "aout".name AS "name"\r
+               FROM\r
+                       actor.org_unit AS "aou"\r
+                               INNER JOIN actor.org_unit_type AS "aout"\r
+                                       ON ( "aout".id = "aou".ou_type ) ;\r
+               </programlisting>\r
+               <para>In this case the core table is the child table, and the joined table is the parent table. We could just as well have written it the other way around:</para>\r
+               <programlisting>        \r
+               {\r
+                        "select": { "aou":[ "id" ], "aout":[ "name" ] },\r
+                       "from": {\r
+                               "aout":"aou"\r
+                       }\r
+               }\r
+                       \r
+               SELECT\r
+                   "aou".id AS "id",\r
+                   "aout".name AS "name"\r
+               FROM\r
+                   actor.org_unit_type AS "aout"\r
+                       INNER JOIN actor.org_unit AS "aou"\r
+                           ON ( "aou".ou_type = "aout".id ) ;\r
+               </programlisting>\r
+       </simplesect>\r
+       <simplesect>\r
+               <title>Specifying The Join Columns Explicitly</title>\r
+               <para>While it's convenient to let json_query pick the join columns, it doesn't always work.</para>\r
+               <para>For example, the actor.org_unit table has four different address ids, for four different kinds of addresses. Each of them is a foreign key to the actor.org_address table. \r
+               Json_query can't guess which one you want if you don't tell it.</para>\r
+               <para>(Actually it will try to guess. It will pick the first matching link that it finds in the IDL, which may or may not be the one you want.)</para>\r
+               <para>Here's how to define exactly which columns you want for the join:</para>\r
+               <programlisting>        \r
+               {\r
+                       "select": { "aou":[ "id" ], "aoa":[ "street1" ] },\r
+                       "from": {\r
+                               "aou": {\r
+                                       "aoa": {\r
+                                               "fkey":"holds_address",\r
+                                               "field":"id"\r
+                                       }\r
+                               }\r
+                       }\r
+               }\r
+               </programlisting>\r
+               <para>Before, the table we were joining was represented merely by its class name. Now it's represented by an entry in a JSON object. The key of that entry is the \r
+               class name, and the associated data is another layer of JSON object containing the attributes of the join.</para>\r
+               <para>Later we'll encounter other kinds of join attributes. For now, the only attributes that we're looking at are the ones that identify the join columns: \r
+               <quote>fkey</quote> and <quote>field</quote>. The hard part is remembering which is which:</para>\r
+               <itemizedlist>\r
+                       <listitem>\r
+                               <para><quote>fkey</quote> identifies the join column from the left table;</para>        \r
+                       </listitem>\r
+                       <listitem>\r
+                               <para><quote>field</quote> identifies the join column from the right table. </para>     \r
+                       </listitem>\r
+               </itemizedlist> \r
+               <para>When there are only two tables involved, the core table is on the left, and the non-core table is on the right. In more complex queries neither table may be the \r
+               core table.</para>\r
+               <para>Here is the result of the preceding JSON:</para>\r
+               <programlisting>        \r
+               SELECT\r
+                   "aou".id AS "id",\r
+                   "aoa".street1 AS "street1"\r
+               FROM\r
+                   actor.org_unit AS "aou"\r
+                       INNER JOIN actor.org_address AS "aoa"\r
+                           ON ( "aoa".id = "aou".holds_address ) ;\r
+               </programlisting>\r
+               <para>In this example the child table is on the left and the parent table is on the right. We can swap the tables if we swap the join columns as well:</para>\r
+               <programlisting>        \r
+               {\r
+                       "select": { "aou":[ "id" ], "aoa":[ "street1" ] },\r
+                       "from": {\r
+                               "aoa": {\r
+                                       "aou": {\r
+                                               "fkey":"id",\r
+                                               "field":"holds_address"\r
+                                       }\r
+                               }\r
+                       }\r
+               }\r
+                               \r
+               SELECT\r
+                   "aou".id AS "id",\r
+                   "aoa".street1 AS "street1"\r
+               FROM\r
+                   actor.org_address AS "aoa"\r
+                       INNER JOIN actor.org_unit AS "aou"\r
+                           ON ( "aou".holds_address = "aoa".id ) ;\r
+               </programlisting>\r
+               <para>When you specify both of the join columns, json_query assumes that you know what you're doing. It doesn't check the IDL to confirm that the join makes sense. \r
+               The burden is on you to avoid absurdities.</para>\r
+       </simplesect>\r
+       <simplesect>\r
+               <title>Specifying Only One Join Column</title>\r
+               <para>We just saw how to specify both ends of a join. It turns out that there's a shortcut -- most of the time you only need to specify one end. Consider \r
+               the following variation on the previous example:</para>\r
+               <programlisting>        \r
+               {\r
+                       "select": { "aou":[ "id" ], "aoa":[ "street1" ] },\r
+                       "from": {\r
+                               "aoa": {\r
+                                       "aou": {\r
+                                               "field":"holds_address"\r
+                                       }\r
+                               }\r
+                        }\r
+               }\r
+               </programlisting>\r
+               <para>..which results in exactly the same SQL as before.</para>\r
+               <para>Here we specified the join column from the child table, the column that is a foreign key pointing to another table. As long as that linkage is defined in the IDL, \r
+               json_query can look it up and figure out what the corresponding column is in the parent table.</para>\r
+               <para>However this shortcut doesn't work if you specify only the column in the parent table, because it would lead to ambiguities. Suppose we had specified the id \r
+               column of actor.org_address. As noted earlier, there are four different foreign keys from actor.org_unit to actor.org_address, and json_query would have no way to guess \r
+               which one we wanted.</para>\r
+       </simplesect>\r
+       <simplesect>\r
+               <title>Joining to Multiple Tables</title>\r
+               <para>So far we have joined only two tables at a time. What if we need to join one table to two different tables?</para>\r
+               <para>Here's an example:</para>\r
+               <programlisting>        \r
+               {\r
+                       "select": { "aou":[ "id" ], "aout":[ "depth" ], "aoa":[ "street1" ] },\r
+                       "from": {\r
+                               "aou": {\r
+                                       "aout":{},\r
+                                       "aoa": {\r
+                                               "fkey":"holds_address"\r
+                                       }\r
+                               }\r
+                       }\r
+               }\r
+               </programlisting>\r
+               <para>The first join, to actor.org_unit_type, is simple. We could have specified join columns, but we don't have to, because json_query will construct that join on the basis of \r
+               what it finds in the IDL. Having no join attributes to specify, we leave that object empty.</para>\r
+               <para>For the second join, to actor.org_address, we have to specify at least the join column in the child table, as discussed earlier. We could also have specified the join \r
+               column from the parent table, but we don't have to, so we didn't.</para>\r
+               <para>Here is the resulting SQL:</para>\r
+               <programlisting>        \r
+               SELECT\r
+                       "aou".id AS "id",\r
+                       "aout".depth AS "depth",\r
+                       "aoa".street1 AS "street1"\r
+               FROM\r
+                       actor.org_unit AS "aou"\r
+                               INNER JOIN actor.org_unit_type AS "aout"\r
+                                       ON ( "aout".id = "aou".ou_type )\r
+                               INNER JOIN actor.org_address AS "aoa"\r
+                                       ON ( "aoa".id = "aou".holds_address ) ;\r
+               </programlisting>\r
+               <para>Since there can be only one core table, the outermost object in the FROM clause can have only one entry, whose key is the class name of the core table. The next \r
+               level has one entry for every table that's joined to the core table.</para>\r
+       </simplesect>\r
+       <simplesect>\r
+               <title>Nested Joins</title>\r
+               <para>Let's look at that last query again. It joins three tables, and the core table is the one in the middle. Can we make one of the end tables the core table instead?</para>\r
+               <para>Yes, we can:</para>\r
+               <programlisting>        \r
+               {\r
+                       "select": { "aou":[ "id" ], "aout":[ "depth" ], "aoa":[ "street1" ] },\r
+                       "from": {\r
+                               "aoa": {\r
+                                       "aou": {\r
+                                               "field":"holds_address",\r
+                                               "join": {\r
+                                                       "aout":{ "fkey":"ou_type" }\r
+                                               }\r
+                                       }\r
+                               }\r
+                       }\r
+               }\r
+               </programlisting>\r
+               <para>The "join" attribute introduces another level of join. In this case "aou" is the left table for the nested join, and the right table for the original join. \r
+               Here are the results:</para>\r
+               <programlisting>        \r
+               SELECT\r
+                       "aou".id AS "id",\r
+                       "aout".depth AS "depth",\r
+                       "aoa".street1 AS "street1"\r
+               FROM\r
+                       actor.org_address AS "aoa"\r
+                               INNER JOIN actor.org_unit AS "aou"\r
+                                       ON ( "aou".holds_address = "aoa".id )\r
+                                               INNER JOIN actor.org_unit_type AS "aout"\r
+                                                       ON ( "aout".id = "aou".ou_type ) ;\r
+               </programlisting>\r
+       </simplesect>\r
+       <simplesect>\r
+               <title>Outer Joins</title>\r
+               <para>By default, json_query constructs an inner join. If you need an outer join, you can add the join type as an attribute of the join:</para>\r
+               <para>Yes, we can:</para>\r
+               <programlisting>        \r
+               {\r
+                       "select": { "aou":[ "id" ], "aoa":[ "street1" ] },\r
+                       "from": {\r
+                               "aoa": {\r
+                                       "aou": {\r
+                                               "field":"mailing_address",\r
+                                               "type":"left"\r
+                                       }\r
+                               }\r
+                       }\r
+               }\r
+               </programlisting>\r
+               <para>Here is the resulting SQL for this example:</para>\r
+               <programlisting>        \r
+               SELECT\r
+                   "aou".id AS "id",\r
+                   "aoa".street1 AS "street1"\r
+               FROM\r
+                   actor.org_address AS "aoa"\r
+                       LEFT JOIN actor.org_unit AS "aou"\r
+                           ON ( "aou".mailing_address = "aoa".id ) ;\r
+               </programlisting>\r
+       </simplesect>\r
+       <simplesect>\r
+               <title>Referring to Joined Tables in the WHERE Clause</title>\r
+               <para>In the WHERE clause of the generated SQL, every column name is qualified by a table alias, which is always the corresponding class name.</para>\r
+               <para>If a column belongs to the core table, this qualification happens by default. If it belongs to a joined table, the JSON must specify what class name \r
+               to use for an alias. For example:</para>\r
+               <programlisting>        \r
+               {\r
+                       "select": { "aou":[ "id" ], "aout":[ "name" ] },\r
+                       "from": {\r
+                               "aout":"aou"\r
+                       },\r
+                       "where": {\r
+                               "+aou":{ "parent_ou":2 }\r
+                       }\r
+               }\r
+               </programlisting>\r
+               <para>Note the peculiar operator <quote>+aou</quote> -- a plus sign followed by the relevant class name. This operator tells json_query to apply the specified class to the condition that \r
+               follows. The result:</para>\r
+               <programlisting>        \r
+               SELECT\r
+                       "aou".id AS "id",\r
+                       "aout".name AS "name"\r
+               FROM\r
+                       actor.org_unit_type AS "aout"\r
+                       INNER JOIN actor.org_unit AS "aou"\r
+                               ON ( "aou".ou_type = "aout".id )\r
+               WHERE\r
+                       ( "aou".parent_ou = 2 );\r
+               </programlisting>\r
+               <para>The plus-class operator may apply to multiple conditions:</para>\r
+               <programlisting>        \r
+               {\r
+                   "select": { "aou":[ "id" ], "aout":[ "name" ] },\r
+                   "from": {\r
+                       "aout":"aou"\r
+                   },\r
+                   "where": {\r
+                       "+aou":{\r
+                           "parent_ou":2,\r
+                           "id":{ "&lt;":42 }\r
+                       }\r
+                   }\r
+               }\r
+                               \r
+               SELECT\r
+                   "aou".id AS "id",\r
+                   "aout".name AS "name"\r
+               FROM\r
+                   actor.org_unit_type AS "aout"\r
+                       INNER JOIN actor.org_unit AS "aou"\r
+                           ON ( "aou".ou_type = "aout".id )\r
+               WHERE\r
+                   (\r
+                       "aou".parent_ou = 2\r
+                       AND "aou".id &lt; 42\r
+                   );\r
+               </programlisting>\r
+               <para>For these artificial examples, it would have been simpler to swap the tables, so that actor.org_unit is the core table. Then you wouldn't need to go through any \r
+               special gyrations to apply the right table alias. In a more realistic case, however, you might need to apply conditions to both tables. Just swapping the tables \r
+               wouldn't solve the problem.</para>\r
+               <para>You can also use a plus-class operator to compare columns from two different tables:</para>\r
+               <programlisting>        \r
+               {\r
+                   "select": { "aou":[ "id" ], "aout":[ "name" ] },\r
+                   "from": {\r
+                       "aout":"aou"\r
+                   },\r
+                   "where": {\r
+                       "depth": { "&gt;": { "+aou":"parent_ou" } }\r
+                   }\r
+               }\r
+                               \r
+\r
+               SELECT\r
+                   "aou".id AS "id",\r
+                   "aout".name AS "name"\r
+               FROM\r
+                   actor.org_unit_type AS "aout"\r
+                       INNER JOIN actor.org_unit AS "aou"\r
+                           ON ( "aou".ou_type = "aout".id )\r
+               WHERE\r
+                   (\r
+                       "aout".depth &gt; (  "aou".parent_ou  )\r
+                   );\r
+               </programlisting>\r
+               <para>Please don't expect that query to make any sense. It doesn't. But it illustrates the syntax.</para>\r
+       </simplesect>\r
+       <simplesect>\r
+               <title>Join Filters</title>\r
+               <para>While the above approach certainly works, the special syntax needed is goofy and awkward. A somewhat cleaner solution is to include a condition in the JOIN clause:</para>\r
+               \r
+               <programlisting>        \r
+               {\r
+                   "select": { "aou":[ "id" ], "aout":[ "name" ] },\r
+                   "from": {\r
+                       "aout": {\r
+                           "aou": {\r
+                               "filter": {\r
+                                   "parent_ou":2\r
+                               }\r
+                           }\r
+                       }\r
+                   }\r
+               }                       \r
+\r
+               SELECT\r
+                   "aou".id AS "id", "aout".name AS "name"\r
+               FROM\r
+                   actor.org_unit_type AS "aout"\r
+                       INNER JOIN actor.org_unit AS "aou"\r
+                           ON ( "aou".ou_type = "aout".id\r
+                                AND  "aou".parent_ou = 2 ) ;\r
+               </programlisting>\r
+               <para>By default, json_query uses AND to combine the <quote>filter</quote> condition with the original join condition. If you need OR, you can use the <quote>filter_op</quote> attribute to \r
+               say so:</para>\r
+               <programlisting>        \r
+               {\r
+                   "select": { "aou":[ "id" ], "aout":[ "name" ] },\r
+                   "from": {\r
+                       "aout": {\r
+                           "aou": {\r
+                               "filter": {\r
+                                   "parent_ou":2\r
+                               },\r
+                               "filter_op":"or"\r
+                           }\r
+                       }\r
+                   }\r
+               }               \r
+\r
+               SELECT\r
+                   "aou".id AS "id",\r
+                   "aout".name AS "name"\r
+               FROM\r
+                   actor.org_unit_type AS "aout"\r
+                       INNER JOIN actor.org_unit AS "aou"\r
+                           ON ( "aou".ou_type = "aout".id\r
+                                OR  "aou".parent_ou = 2 ) ;\r
+               </programlisting>\r
+               <para>If the data tagged by <quote>filter_op</quote> is anything but <quote>or</quote> (in upper, lower, or mixed case), json_query uses AND instead of OR.</para>\r
+               <para>The condition tagged by <quote>filter</quote> may be much more complicated. In fact it accepts all the same syntax as the WHERE clause.</para>\r
+               <para>Remember, though, that it all gets combined with the the original join condition with an AND, or with an OR if you so specify. If \r
+               you're not careful, the result may be a confusing mixture of AND and OR at the same level.</para>\r
+       </simplesect>\r
+       <simplesect>\r
+               <title>Joining to a Subquery</title>\r
+               <para>In SQL you can put a subquery in a FROM clause, and select from it as if it were a table. A JSON query has no way to do that directly. The IDL, however, \r
+               can define a class as a subquery instead of as a table. When you SELECT from it, json_query inserts the corresponding subquery into the FROM clause. For example:</para>\r
+               <programlisting>        \r
+               {\r
+                   "select":{ "iatc":[ "id", "dest", "copy_status" ] },\r
+                   "from": "iatc"\r
+               }\r
+               </programlisting>\r
+               <para>There's nothing special-looking about this JSON, but json_query expands it as follows:</para>\r
+               <programlisting>        \r
+               SELECT\r
+                   "iatc".id AS "id",\r
+                   "iatc".dest AS "dest",\r
+                   "iatc".copy_status AS "copy_status"\r
+               FROM\r
+                   (\r
+                       SELECT  t.*\r
+                       FROM\r
+                           action.transit_copy t\r
+                               JOIN actor.org_unit AS s\r
+                                   ON (t.source = s.id)\r
+                               JOIN actor.org_unit AS d\r
+                                   ON (t.dest = d.id)\r
+                       WHERE\r
+                           s.parent_ou &lt;&gt; d.parent_ou\r
+                   ) AS "iatc" ;\r
+               </programlisting>\r
+               <para>The <quote>iatc</quote> class is like a view, except that it's defined in the IDL instead of the database. In this case it provides a way to do a join that would otherwise be \r
+               impossible through a JSON query, because it joins the same table in two different ways (see the next subsection).</para>\r
+       </simplesect>\r
+       <simplesect>\r
+               <title>Things You Can't Do</title>\r
+               <para>In a JOIN, as with other SQL constructs, there are some things that you can't do with a JSON query.</para>\r
+               <para>In particular, you can't specify a table alias, because the table alias is always the class name. As a result:</para>\r
+               <itemizedlist>\r
+                       <listitem>\r
+                               <para>You can't join a table to itself. For example, you can't join actor.org_unit to itself in order to select the name of the parent for every org_unit.</para>       \r
+                       </listitem>\r
+                       <listitem>\r
+                               <para>You can't join to the same table in more than one way. For example, you can't join actor.org_unit to actor.org_address through four different foreign \r
+                               keys, to get four kinds of addresses in a single query.</para>  \r
+                       </listitem>\r
+               </itemizedlist> \r
+               <para>The only workaround is to perform the join in a view, or in a subquery defined in the IDL as described in the previous subsection.</para>\r
+               <para>Some other things, while not impossible, require some ingenuity in the use of join filters.</para>\r
+               <para>For example: by default, json_query constructs a join condition using only a single pair of corresponding columns. As long as the database is designed accordingly, \r
+               a single pair of columns will normally suffice. If you ever need to join on more than one pair of columns, you can use join filters for the extras.</para>      \r
+               <para>Likewise, join conditions are normally equalities. In raw SQL it is possible (though rarely useful) to base a join on an inequality, or to use a function call in a join \r
+               condition, or to omit any join condition in order to obtain a Cartesian product. If necessary, you can devise such unconventional joins by combining the normal join \r
+               conditions with join filters.</para>\r
+               <para>For example, here's how to get a Cartesian product:</para>\r
+               <programlisting>        \r
+               {\r
+                   "select": { "aou":[ "id" ], "aout":[ "name" ] },\r
+                   "from": {\r
+                       "aout": {\r
+                           "aou": {\r
+                               "filter": {\r
+                                   "ou_type":{ "&lt;&gt;": { "+aout":"id" } }\r
+                               },\r
+                               "filter_op":"or"\r
+                           }\r
+                       }\r
+                   }\r
+               }\r
+                       \r
+\r
+               SELECT\r
+                   "aou".id AS "id",\r
+                   "aout".name AS "name"\r
+               FROM\r
+                   actor.org_unit_type AS "aout"\r
+                       INNER JOIN actor.org_unit AS "aou"\r
+                           ON\r
+                           (\r
+                               "aou".ou_type = "aout".id\r
+                               OR  ("aou".ou_type &lt;&gt; (  "aout".id  ))\r
+                           ) ;\r
+               </programlisting>\r
+               <para>Yes, it's ugly, but at least you're not likely to do it by accident.</para>\r
+       </simplesect>\r
+       <simplesect>\r
+               <title>Selecting from Functions</title>\r
+               <para>In SQL, you can put a function call in the FROM clause. The function may return multiple columns and multiple rows. Within the query, the function behaves like a table.</para>\r
+               <para>A JSON query can also select from a function:</para>\r
+               <programlisting>        \r
+               {\r
+                   "from": [ "actor.org_unit_ancestors", 5 ]\r
+               }\r
+               </programlisting>\r
+               <para>The data associated with <quote>from</quote> is an array instead of a string or an object. The first element in the array specifies the name of the function. Subsequent elements, \r
+               if any, supply the parameters of the function; they must be literal values or nulls.</para>\r
+               <para>Here is the resulting query:</para>\r
+               <programlisting>        \r
+               SELECT *\r
+               FROM\r
+                   actor.org_unit_ancestors( '5' ) AS "actor.org_unit_ancestors" ;\r
+               </programlisting>\r
+               <para>In a JSON query this format is very limited, largely because the IDL knows nothing about the available functions. You can't join the function to a table or to \r
+               another function. If you try to supply a SELECT list or a WHERE clause, json_query will ignore it. The generated query will always select every column, via a wild card asterisk, \r
+               from every row.</para>\r
+       </simplesect>\r
+       <simplesect>\r
+               <title>The ORDER BY Clause</title>\r
+               <para>In most cases you can encode an ORDER BY clause as either an array or an object. Let's take a simple example and try it both ways. First the array:</para>\r
+               <programlisting>        \r
+               {\r
+                   "select":{ "aou":[ "name" ] },\r
+                   "from": "aou",\r
+                   "order_by": [\r
+                       { "class":"aou", "field":"name" }\r
+                   ]\r
+               }\r
+               </programlisting>\r
+               <para>Now the object:</para>\r
+               <programlisting>        \r
+               {\r
+                   "select":{ "aou":[ "name" ] },\r
+                   "from": "aou",\r
+                   "order_by": {\r
+                       "aou":{ "name":{} }\r
+                   }\r
+               }\r
+               </programlisting>\r
+               <para>The results are identical from either version:</para>\r
+               <programlisting>        \r
+               SELECT\r
+                   "aou".name AS "name"\r
+               FROM\r
+                   actor.org_unit AS "aou"\r
+               ORDER BY\r
+                   "aou".name;\r
+               </programlisting>\r
+               <para>The array format is more verbose, but as we shall see, it is also more flexible. It can do anything the object format can do, plus some things that the object \r
+               format can't do.</para>\r
+       </simplesect>\r
+       <simplesect>\r
+               <title>ORDER BY as an Array</title>\r
+               <para>In the array format, each element of the array is an object defining one of the sort fields. Each such object must include at least two tags:</para>\r
+               <itemizedlist>\r
+                       <listitem>\r
+                               <para>The <quote>class</quote> tag provides the name of the class, which must be either the core class or a joined class.</para>        \r
+                       </listitem>\r
+                       <listitem>\r
+                               <para>The <quote>field</quote> tag provides the field name, corresponding to one of the columns of the class.</para>    \r
+                       </listitem>\r
+               </itemizedlist> \r
+               <para>If you want to sort by multiple fields, just include a separate object for each field.</para>\r
+               <para>If you want to sort a field in descending order, add a <quote>direction</quote> tag:</para>\r
+               <programlisting>        \r
+               {\r
+                   "select":{ "aou":[ "name" ] },\r
+                   "from": "aou",\r
+                   "order_by": [\r
+                       {\r
+                           "class":"aou",\r
+                           "field":"name",\r
+                           "transform":"upper"\r
+                       }\r
+                   ]\r
+               }\r
+                               \r
+\r
+               SELECT\r
+                   "aou".name AS "name"\r
+               FROM\r
+                   actor.org_unit AS "aou"\r
+               ORDER BY\r
+                   upper("aou".name );\r
+               </programlisting>\r
+               <para>If you need additional parameters for the function, you can use the <quote>params</quote> tag to pass them:</para>\r
+               <programlisting>        \r
+               {\r
+                   "select":{ "aou":[ "name" ] },\r
+                   "from": "aou",\r
+                   "order_by": [\r
+                       {\r
+                           "class":"aou",\r
+                           "field":"name",\r
+                           "transform":"substr",\r
+                           "params":[ 1, 8 ]\r
+                       }\r
+                   ]\r
+               }\r
+               </programlisting>\r
+               <para>The additional parameters appear as elements in an array. They may be numbers, strings, or nulls.</para>\r
+               <programlisting>        \r
+               SELECT\r
+                   "aou".name AS "name"\r
+               FROM\r
+                   actor.org_unit AS "aou"\r
+               ORDER BY\r
+                   substr("aou".name,'1','8' );\r
+               </programlisting>\r
+               <para>As we have seen elsewhere, all literal values are passed as quoted strings, even if they are numbers.</para>\r
+               <para>If the function returns multiple columns, you can use the <quote>result_field</quote> tag to indicate which one you want (not shown).</para>\r
+       </simplesect>\r
+\r
+       <simplesect>\r
+               <title>ORDER BY as an Object</title>\r
+               <para>When you encode the ORDER BY clause as an object, the keys of the object are class names. Each class must be either the core class or a joined class. The data for \r
+               each class can be either an array or another layer of object. Here's an example with one of each:</para>\r
+               <programlisting>        \r
+               {\r
+                   "select":{ "aout":"id", "aou":[ "name" ] },\r
+                   "from": { "aou":"aout" },\r
+                   "order_by": {\r
+                       "aout":[ "id" ],\r
+                       "aou":{ "name":{ "direction":"desc" } }\r
+                   }\r
+               }\r
+               </programlisting>\r
+               <para>For the <quote>aout</quote> class, the associated array is simply a list of field names (in this case, just one). Naturally, each field must reside in the class with which \r
+               it is associated.</para>\r
+               <para>However, a list of field names provides no way to specify the direction of sorting, or a transforming function. You can add those details only if the class \r
+               name is paired with an object, as in the example for the "aou" class. The keys for such an object are field names, and the associated tags define other details.</para>\r
+               <para>In this example, we use the <quote>direction"</quote> tag to specify that the name field be sorted in descending order. This tag works the same way here as described earlier. \r
+               If the associated string starts with "D" or "d", the sort will be descending; otherwise it will be ascending.</para>\r
+               <para>Here is the resulting SQL:</para>\r
+               <programlisting>        \r
+               SELECT\r
+                   "aou".name AS "name"\r
+               FROM\r
+                   actor.org_unit AS "aou"\r
+                       INNER JOIN actor.org_unit_type AS "aout"\r
+                           ON ( "aout".id = "aou".ou_type )\r
+               ORDER BY\r
+                   "aout".id,\r
+                   "aou".name DESC;\r
+               </programlisting>\r
+               <programlisting>\r
+               {\r
+                   "select":{ "aou":[ "name", "id" ] },\r
+                   "from": "aou",\r
+                   "order_by": {\r
+                       "aou":{\r
+                           "name":{ "transform":"substr", "params":[ 1, 8 ] }\r
+                       }\r
+                   }\r
+               }                       \r
+\r
+               SELECT\r
+                   "aou".name AS "name",\r
+                   "aou".id AS "id"\r
+               FROM\r
+                   actor.org_unit AS "aou"\r
+               ORDER BY\r
+                   substr("aou".name,'1','8' );\r
+               </programlisting>\r
+       </simplesect>\r
+       <simplesect>\r
+               <title>Things You Can't Do</title>\r
+               <para>If you encode the ORDER BY clause as an object, you may encounter a couple of restrictions.</para>\r
+               <para>Because the key of such an object is the class name, all the fields from a given class must be grouped together. You can't sort by a column from one table, followed by \r
+               a column from another table, followed by a column from the first table. If you need such a sort, you must encode the ORDER BY clause in the array format, which suffers \r
+               from no such restrictions.</para>\r
+               <para>For similar reasons, with an ORDER BY clause encoded as an object, you can't reference the same column more than once. Although such a sort may seem perverse, \r
+               there are situations where it can be useful, provided that the column is passed to a transforming function.</para>\r
+               <para>For example, you might want a case-insensitive sort, except that for any given letter you want lower case to sort first. For example, you want <quote>diBona</quote> to sort \r
+               before <quote>Dibona</quote>. Here's a way to do that, coding the ORDER BY clause as an array:</para>\r
+               <programlisting>        \r
+               {\r
+                   "select":{ "au":[ "family_name", "id" ] },\r
+                   "from": "au",\r
+                   "order_by": [\r
+                       { "class":"au", "field":"family_name", "transform":"upper" },\r
+                       { "class":"au", "field":"family_name" }\r
+                   ]\r
+               }\r
+               SELECT\r
+                       "au".family_name AS "family_name",\r
+                       "au".id AS "id"\r
+               FROM\r
+                       actor.usr AS "au"\r
+               ORDER BY\r
+                       upper("au".family_name ),\r
+                       "au".family_name;\r
+               </programlisting>\r
+               <para>Such a sort is not possible where the ORDER BY clause is coded as an object.</para>\r
+       </simplesect>\r
+       <simplesect>\r
+               <title>The GROUP BY Clause</title>\r
+               <para>A JSON query has no separate construct to define a GROUP BY clause. Instead, the necessary information is distributed across the SELECT clause. However, \r
+               the way it works is a bit backwards from what you might expect, so pay attention.</para>\r
+               <para>Here's an example:</para>\r
+               <programlisting>        \r
+               {\r
+                   "select": {\r
+                       "aou": [\r
+                           { "column":"parent_ou" },\r
+                           { "column":"name", "transform":"max", "aggregate":true }\r
+                       ]\r
+                   },\r
+                   "from": "aou"\r
+               }\r
+               </programlisting>\r
+               <para>The <quote>transform</quote> tag is there just to give us an excuse to do a GROUP BY. What's important to notice is the <quote>aggregate</quote> tag.</para>\r
+               <para>Here's the resulting SQL:</para>\r
+               <programlisting>        \r
+               SELECT\r
+                   "aou".parent_ou AS "parent_ou",\r
+                   max("aou".name ) AS "name"\r
+               FROM\r
+                   actor.org_unit AS "aou"\r
+               GROUP BY\r
+                   1;\r
+               </programlisting>\r
+               <para>The GROUP BY clause references fields from the SELECT clause by numerical reference, instead of by repeating them. Notice that the field it references, \r
+               parent_ou, is the one that doesn't carry the <quote>aggregate</quote> tag in the JSON.</para>\r
+               <para>Let's state that more generally. The GROUP BY clause includes only the fields that do not carry the <quote>aggregate</quote> tag (or that carry it with a value of false).</para>\r
+               <para>However, that logic applies only when some field somewhere does carry the <quote>aggregate</quote> tag, with a value of true. If there is no <quote>aggregate</quote> tag, or \r
+               it appears only with a value of false, then there is no GROUP BY clause.</para>\r
+               <para>If you really want to include every field in the GROUP BY clause, don't use <quote>aggregate</quote>. Use the <quote>distinct</quote> tag, as described in the next section.</para>\r
+       </simplesect>\r
+       <simplesect>\r
+               <title>The DISTINCT Clause</title>\r
+               <para>JSON queries don't generate DISTINCT clauses. However, they can generate GROUP BY clauses that include every item from the SELECT clause. The effect is the same as \r
+               applying DISTINCT to the entire SELECT clause.</para>\r
+               <para>For example:</para>\r
+               <programlisting>        \r
+               {\r
+                   "select": {\r
+                       "aou": [\r
+                           "parent_ou",\r
+                           "ou_type"\r
+                       ]\r
+                   },\r
+                   "from":"aou",\r
+                   "distinct":"true"\r
+               }\r
+               </programlisting>\r
+               <para>Note the <quote>distinct</quote> entry at the top level of the query object, with a value of <quote>true</quote>.</para>\r
+               <programlisting>        \r
+               SELECT\r
+                   "aou".parent_ou AS "parent_ou",\r
+                   "aou".ou_type AS "ou_type"\r
+               FROM\r
+                   actor.org_unit AS "aou"\r
+               GROUP BY\r
+                   1, 2;\r
+               </programlisting>\r
+               <para>The generated GROUP BY clause references every column in the SELECT clause by number.</para>\r
+       </simplesect>\r
+       <simplesect>\r
+               <title>The HAVING Clause</title>\r
+               <para>For a HAVING clause, add a <quote>having</quote> entry at the top level of the query object. For the associated data, you can use all the same syntax \r
+               that you can use for a WHERE clause.</para>\r
+               <para>Here's a simple example:</para>\r
+               <programlisting>        \r
+               {\r
+                   "select": {\r
+                       "aou": [\r
+                           "parent_ou", {\r
+                               "column":"id",\r
+                               "transform":"count",\r
+                               "alias":"id_count",\r
+                               "aggregate":"true"\r
+                           }\r
+                       ]\r
+                   },\r
+                   "from":"aou",\r
+                   "having": {\r
+                       "id": {\r
+                           "&gt;" : {\r
+                               "transform":"count",\r
+                               "value":6\r
+                           }\r
+                       }\r
+                   }\r
+               }\r
+               </programlisting>\r
+               <para>We use the <quote>aggregate</quote> tag in the SELECT clause to give us a GROUP BY to go with the HAVING. Results:</para>\r
+               <programlisting>        \r
+               SELECT\r
+                   "aou".parent_ou AS "parent_ou",\r
+                   count("aou".id ) AS "id_count"\r
+               FROM\r
+                   actor.org_unit AS "aou"\r
+               GROUP BY\r
+                   1\r
+               HAVING\r
+                   count("aou".id ) &gt;  6 ;\r
+               </programlisting>\r
+               <para>In raw SQL we could have referred to <quote>count( 1 )</quote>. But since JSON queries cannot encode arbitrary expressions, we applied the count function to a column that \r
+               cannot be null.</para>\r
+       </simplesect>\r
+       <simplesect>\r
+               <title>The LIMIT and OFFSET Clauses</title>\r
+               <para>To add an LIMIT or OFFSET clause, add an entry to the top level of a query object. For example:</para>\r
+               <programlisting>        \r
+               {\r
+                   "select": {\r
+                       "aou": [ "id", "name" ]\r
+                   },\r
+                   "from":"aou",\r
+                   "order_by": { "aou":[ "id" ] },\r
+                   "offset": 7,\r
+                   "limit": 42\r
+               }\r
+               </programlisting>\r
+               <para>The data associated with <quote>offset</quote> and <quote>limit</quote> may be either a number or a string, but if it's a string, it should have a number inside.</para>\r
+               <para>Result:</para>\r
+               <programlisting>        \r
+               SELECT\r
+                  "aou".id AS "id",\r
+                  "aou".name AS "name"\r
+               FROM\r
+                  actor.org_unit AS "aou"\r
+               ORDER BY\r
+                  "aou".id\r
+               LIMIT 42 \r
+               OFFSET 7;\r
+               </programlisting>\r
+       </simplesect>\r
+</chapter>\r
diff --git a/1.6/development/supercat.xml b/1.6/development/supercat.xml
new file mode 100644 (file)
index 0000000..18ed504
--- /dev/null
@@ -0,0 +1,272 @@
+<?xml version="1.0" encoding="utf-8"?>\r
+<chapter xml:id="supercat" xmlns="http://docbook.org/ns/docbook" version="5.0" xml:lang="EN"\r
+    xmlns:xi="http://www.w3.org/2001/XInclude" xmlns:xlink="http://www.w3.org/1999/xlink">\r
+       <info>\r
+               <title>SuperCat</title>\r
+       </info>\r
+       <para>SuperCat allows Evergreen record and information retrieval from a web browser using a based on a number of open web standards and formats. The following record types are supported:</para>\r
+       <itemizedlist>\r
+               <listitem>isbn</listitem>\r
+               <listitem>metarecord</listitem>\r
+               <listitem>record</listitem>\r
+       </itemizedlist>\r
+       <simplesect>\r
+               <title>Return a list of ISBNs for related records</title>\r
+               <para>Similar to the OCLC xISBN service, Evergreen can return a list of related records based on its oISBN algorithm:</para>\r
+               <screen>http://&lt;hostname&gt;/opac/extras/osibn/&lt;ISBN&gt;</screen>\r
+               <para>For example, http://dev.gapines.org/opac/extras/oisbn/0439136350 returns:</para>\r
+               <programlisting>\r
+               &lt;idlist metarecord="302670"&gt;\r
+               &lt;isbn record="250060"&gt;0790783525&lt;/isbn&gt;\r
+               &lt;isbn record="20717"&gt;0736691316&lt;/isbn&gt;\r
+               &lt;isbn record="250045"&gt;0790783517&lt;/isbn&gt;\r
+               &lt;isbn record="199060"&gt;9500421151&lt;/isbn&gt;\r
+               &lt;isbn record="250061"&gt;0790783495&lt;/isbn&gt;\r
+               &lt;isbn record="154477"&gt;0807286028&lt;/isbn&gt;\r
+               &lt;isbn record="227297"&gt;1594130027&lt;/isbn&gt;\r
+               &lt;isbn record="26682"&gt;0786222743&lt;/isbn&gt;\r
+               &lt;isbn record="17179"&gt;0807282316&lt;/isbn&gt;\r
+               &lt;isbn record="34885"&gt;0807282316&lt;/isbn&gt;\r
+               &lt;isbn record="118019"&gt;8478885196&lt;/isbn&gt;\r
+               &lt;isbn record="1231"&gt;0738301477&lt;/isbn&gt;\r
+               &lt;/idlist&gt;\r
+               </programlisting>\r
+        </simplesect>\r
+        <simplesect>\r
+               <title>Return records</title>\r
+               <para>SuperCat can return records and metarecords in many different formats (see <xref linkend='supportedsupercatformats' /></para>\r
+               <screen>http://&lt;hostname&gt;/opac/extras/supercat/retrieve/&lt;format&gt;/&lt;record-type&gt;/&lt;bib-ID&gt;</screen>\r
+               <para>For example, http://dev.gapines.org/opac/extras/supercat/retrieve/mods/record/555 returns:</para>\r
+               <programlisting>\r
+               &lt;mods:modsCollection version="3.0"&gt;\r
+                       &lt;mods:mods xsi:schemaLocation="http://www.loc.gov/mods/ http://www.loc.gov/standards/mods/mods.xsd"&gt;\r
+                               &lt;titleInfo&gt;\r
+                                       &lt;title&gt;More Brer Rabbit stories /&lt;/title&gt;\r
+                               &lt;/titleInfo&gt;\r
+                               &lt;typeOfResource&gt;text&lt;/typeOfResource&gt;\r
+                               &lt;originInfo&gt;\r
+                                       &lt;place&gt;\r
+                                               &lt;code authority="marc"&gt;xx&lt;/c0de&gt;\r
+                                       &lt;/place&gt;\r
+                                       &lt;publisher&gt;Award Publications&lt;/publisher&gt;\r
+                                       &lt;dateIssued&gt;c1982, 1983&lt;/dateIssued&gt;\r
+                                       &lt;dateIssued encoding="marc" point="start"&gt;1983&lt;/dateIssued&gt;\r
+                                       &lt;dateIssued encoding="marc" point="end"&gt;1982&lt;/dateIssued&gt;\r
+                                       &lt;issuance&gt;monographic&lt;/issuance&gt;\r
+                                       &lt;/originInfo&gt;\r
+                               &lt;language authority="iso639-2b"&gt;eng&lt;/language&gt;\r
+                               &lt;physicalDescription&gt;\r
+                                       &lt;form authority="marcform"&gt;print&lt;/form&gt;\r
+                                       &lt;extent&gt;unp. : col. ill.&lt;/extent&gt;\r
+                               &lt;/physicalDescription&gt;\r
+                               &lt;note type="statement of responsibility"&gt;ill. by Rene Cloke.&lt;/note&gt;\r
+                               &lt;subject authority="lcsh"&gt;\r
+                                       &lt;topic&gt;Animals&lt;/topic&gt;\r
+                                       &lt;topic&gt;Fiction&lt;/topic&gt;\r
+                               &lt;/subject&gt;\r
+                               &lt;subject authority="lcsh"&gt;\r
+                                       &lt;topic&gt;Fables&lt;/topic&gt;\r
+                               &lt;/subject&gt;\r
+                               &lt;recordInfo&gt;\r
+                                       &lt;recordContentSource&gt;(BRO)&lt;/recordContentSource&gt;\r
+                                       &lt;recordCreationDate encoding="marc"&gt;930903&lt;/recordCreationDate&gt;\r
+                                       &lt;recordChangeDate encoding="iso8601"&gt;19990703024637.0&lt;/recordChangeDate&gt;\r
+                                       &lt;recordIdentifier&gt;PIN60000007 &lt;/recordIdentifier&gt;\r
+                               &lt;/recordInfo&gt;\r
+                       &lt;/mods:mods&gt;\r
+               &lt;/mods:modsCollection&gt;\r
+               </programlisting>\r
+        </simplesect>\r
+        <simplesect>\r
+               <title>Return a feed of recently edited or created records</title>\r
+               <para>SuperCat can return feeds of recently edited or created authority and bibliographic records:</para>\r
+               <screen>http://&lt;hostname&gt;/opac/extras/feed/freshmeat/&lt;feed-type&gt;/[authority|biblio]/[import|edit]/&lt;limit&gt;/&lt;date&gt;</screen>\r
+               <para>The limit records imported or edited following the supplied date will be returned. If you do not supply a date, then the most recent limit records will be returned.</para>\r
+               <para>If you do not supply a limit, then up to 10 records will be returned.</para>      \r
+               <para>Feed-type can be one of atom, html, htmlholdings, marcxml, mods, mods3, or rss2.</para>           \r
+               <para>For example, http://dev.gapines.org/opac/extras/feed/freshmeat/atom/biblio/import/10/2008-01-01</para>\r
+       </simplesect>\r
+        <simplesect>\r
+               <title>Browse records</title>\r
+               <para>SuperCat can browse records in HTML and XML formats:</para>\r
+               <screen>http://&lt;hostname&gt;/opac/extras/supercat/browse/&lt;format&gt;/call_number/&lt;org_unit&gt;/&lt;call_number&gt;</screen>\r
+               <para>For example, http://dev.gapines.org/opac/extras/browse/xml/call_number/-/GV returns:</para>\r
+               <programlisting>\r
+               &lt;hold:volumes xmlns:hold='http://open-ils.org/spec/holdings/v1'&gt;\r
+                       &lt;hold:volume id="tag:open-ils.org,2008:asset-call_number/130607" lib="FRRLS-FA" label="GUTCHEON BETH"&gt;\r
+                               &lt;act:owning_lib id="tag:open-ils.org,2008:actor-org_unit/111" name="Fayette County Public Library"/&gt;\r
+                               &lt;record xsi:schemaLocation="http://www.loc.gov/MARC21/slim http://www.loc.gov/ \r
+                                       standards/marcxml/schema/MARC21slim.xsd"\r
+                                       id="tag:open-ils.org,2008:biblio-record_entry/21669/FRRLS-FA"&gt;\r
+                                       &lt;leader&gt;09319pam a2200961 a 4500&lt;/leader&gt;\r
+                                       &lt;controlfield tag="001"/&gt;\r
+                                       &lt;controlfield tag="005"&gt;20000302124754.0&lt;/controlfield&gt;\r
+                                       &lt;controlfield tag="008"&gt;990817s2000    nyu           000 1 eng  &lt;/controlfield&gt;\r
+                                       &lt;datafield tag="010" ind1=" " ind2=" "&gt;\r
+                                               &lt;subfield code="a"&gt;   99045936&lt;/subfield&gt;\r
+                                       &lt;/datafield&gt;\r
+                                       ..\r
+                               &lt;/record&gt;\r
+                               &lt;record&gt;\r
+                                       ..\r
+                               &lt;/record&gt;\r
+                       &lt;/hold:volume&gt;\r
+               &lt;/hold:volumes&gt;   \r
+               </programlisting>\r
+       </simplesect>\r
+        <simplesect xml:id="supportedsupercatformats">\r
+               <title>Supported formats</title>\r
+               <para>SuperCat maintains a list of supported formats for records and metarecords:</para>\r
+               <screen>http://&lt;hostname&gt;/opac/extras/supercat/formats/&lt;record-type&gt;</screen>\r
+               <para>For example, http://dev.gapines.org/opac/extras/supercat/formats/record returns:</para>\r
+               <programlisting>\r
+               &lt;formats&gt;\r
+\r
+                 &lt;format&gt;\r
+                   &lt;name&gt;opac&lt;/name&gt;\r
+                   &lt;type&gt;text/html&lt;/type&gt;\r
+                 &lt;/format&gt;\r
+\r
+                 &lt;format&gt;\r
+                   &lt;name&gt;htmlholdings&lt;/name&gt;\r
+                   &lt;type&gt;text/html&lt;/type&gt;\r
+                 &lt;/format&gt;\r
+                \r
+                 &lt;format&gt;\r
+                   &lt;name&gt;html&lt;/name&gt;\r
+                   &lt;type&gt;text/html&lt;/type&gt;\r
+                 &lt;/format&gt;\r
+                \r
+                 &lt;format&gt;\r
+                   &lt;name&gt;htmlholdings-full&lt;/name&gt;\r
+                   &lt;type&gt;text/html&lt;/type&gt;\r
+                 &lt;/format&gt;\r
+                \r
+                 &lt;format&gt;\r
+                   &lt;name&gt;html-full&lt;/name&gt;\r
+                   &lt;type&gt;text/html&lt;/type&gt;\r
+                 &lt;/format&gt;\r
+                \r
+                 &lt;format&gt;\r
+                   &lt;name&gt;marcxml&lt;/name&gt;\r
+                   &lt;type&gt;application/xml&lt;/type&gt;\r
+                   &lt;namespace_uri&gt;http://www.loc.gov/MARC21/slim&lt;/namespace_uri&gt;\r
+                   &lt;docs&gt;http://www.loc.gov/marcxml/&lt;/docs&gt;\r
+                \r
+                   &lt;schema_location&gt;\r
+                   http://www.loc.gov/standards/marcxml/schema/MARC21slim.xsd\r
+                   &lt;/schema_location&gt;\r
+                 &lt;/format&gt;\r
+                \r
+                 &lt;format&gt;\r
+                   &lt;name&gt;marcxml-full&lt;/name&gt;\r
+                   &lt;type&gt;application/xml&lt;/type&gt;\r
+                   &lt;namespace_uri&gt;http://www.loc.gov/MARC21/slim&lt;/namespace_uri&gt;\r
+                   &lt;docs&gt;http://www.loc.gov/marcxml/&lt;/docs&gt;\r
+                \r
+                   &lt;schema_location&gt;\r
+                   http://www.loc.gov/standards/marcxml/schema/MARC21slim.xsd\r
+                   &lt;/schema_location&gt;\r
+                 &lt;/format&gt;\r
+                \r
+                 &lt;format&gt;\r
+                   &lt;name&gt;rss2&lt;/name&gt;\r
+                   &lt;type&gt;application/xml&lt;/type&gt;\r
+                 &lt;/format&gt;\r
+                \r
+                 &lt;format&gt;\r
+                   &lt;name&gt;rss2-full&lt;/name&gt;\r
+                   &lt;type&gt;application/xml&lt;/type&gt;\r
+                 &lt;/format&gt;\r
+                \r
+                 &lt;format&gt;\r
+                   &lt;name&gt;rdf_dc&lt;/name&gt;\r
+                   &lt;type&gt;application/xml&lt;/type&gt;\r
+                   &lt;namespace_uri&gt;http://purl.org/dc/elements/1.1/&lt;/namespace_uri&gt;\r
+                   &lt;schema_location&gt;http://purl.org/dc/elements/1.1/&lt;/schema_location&gt;\r
+                 &lt;/format&gt;\r
+                \r
+                 &lt;format&gt;\r
+                   &lt;name&gt;oai_dc&lt;/name&gt;\r
+                   &lt;type&gt;application/xml&lt;/type&gt;\r
+                   &lt;namespace_uri&gt;http://www.openarchives.org/OAI/2.0/oai_dc/&lt;/namespace_uri&gt;\r
+                   &lt;schema_location&gt;http://www.openarchives.org/OAI/2.0/oai_dc.xsd&lt;/schema_location&gt;\r
+                 &lt;/format&gt;\r
+                \r
+                 &lt;format&gt;\r
+                   &lt;name&gt;srw_dc&lt;/name&gt;\r
+                   &lt;type&gt;application/xml&lt;/type&gt;\r
+                   &lt;namespace_uri&gt;info:srw/schema/1/dc-schema&lt;/namespace_uri&gt;\r
+                \r
+                   &lt;schema_location&gt;\r
+                   http://www.loc.gov/z3950/agency/zing/srw/dc-schema.xsd\r
+                   &lt;/schema_location&gt;\r
+                 &lt;/format&gt;\r
+                \r
+                 &lt;format&gt;\r
+                   &lt;name&gt;mods32&lt;/name&gt;\r
+                   &lt;type&gt;application/xml&lt;/type&gt;\r
+                   &lt;namespace_uri&gt;http://www.loc.gov/mods/v3&lt;/namespace_uri&gt;\r
+                   &lt;docs&gt;http://www.loc.gov/mods/&lt;/docs&gt;\r
+                   &lt;schema_location&gt;http://www.loc.gov/standards/mods/v3/mods-3-2.xsd&lt;/schema_location&gt;\r
+                 &lt;/format&gt;\r
+                \r
+                 &lt;format&gt;\r
+                   &lt;name&gt;mods3&lt;/name&gt;\r
+                   &lt;type&gt;application/xml&lt;/type&gt;\r
+                   &lt;namespace_uri&gt;http://www.loc.gov/mods/v3&lt;/namespace_uri&gt;\r
+                   &lt;docs&gt;http://www.loc.gov/mods/&lt;/docs&gt;\r
+                   &lt;schema_location&gt;http://www.loc.gov/standards/mods/v3/mods-3-1.xsd&lt;/schema_location&gt;\r
+                 &lt;/format&gt;\r
+                \r
+                 &lt;format&gt;\r
+                   &lt;name&gt;mods3-full&lt;/name&gt;\r
+                   &lt;type&gt;application/xml&lt;/type&gt;\r
+                   &lt;namespace_uri&gt;http://www.loc.gov/mods/v3&lt;/namespace_uri&gt;\r
+                   &lt;docs&gt;http://www.loc.gov/mods/&lt;/docs&gt;\r
+                   &lt;schema_location&gt;http://www.loc.gov/standards/mods/v3/mods-3-1.xsd&lt;/schema_location&gt;\r
+                 &lt;/format&gt;\r
+                \r
+                 &lt;format&gt;\r
+                   &lt;name&gt;mods&lt;/name&gt;\r
+                   &lt;type&gt;application/xml&lt;/type&gt;\r
+                   &lt;namespace_uri&gt;http://www.loc.gov/mods/&lt;/namespace_uri&gt;\r
+                   &lt;docs&gt;http://www.loc.gov/mods/&lt;/docs&gt;\r
+                   &lt;schema_location&gt;http://www.loc.gov/standards/mods/mods.xsd&lt;/schema_location&gt;\r
+                 &lt;/format&gt;\r
+                \r
+                 &lt;format&gt;\r
+                   &lt;name&gt;mods-full&lt;/name&gt;\r
+                   &lt;type&gt;application/xml&lt;/type&gt;\r
+                   &lt;namespace_uri&gt;http://www.loc.gov/mods/&lt;/namespace_uri&gt;\r
+                   &lt;docs&gt;http://www.loc.gov/mods/&lt;/docs&gt;\r
+                   &lt;schema_location&gt;http://www.loc.gov/standards/mods/mods.xsd&lt;/schema_location&gt;\r
+                 &lt;/format&gt;\r
+                \r
+                 &lt;format&gt;\r
+                   &lt;name&gt;atom&lt;/name&gt;\r
+                   &lt;type&gt;application/xml&lt;/type&gt;\r
+                   &lt;namespace_uri&gt;http://www.w3.org/2005/Atom&lt;/namespace_uri&gt;\r
+                   &lt;docs&gt;http://www.ietf.org/rfc/rfc4287.txt&lt;/docs&gt;\r
+                 &lt;/format&gt;\r
+                \r
+                 &lt;format&gt;\r
+                   &lt;name&gt;atom-full&lt;/name&gt;\r
+                   &lt;type&gt;application/xml&lt;/type&gt;\r
+                   &lt;namespace_uri&gt;http://www.w3.org/2005/Atom&lt;/namespace_uri&gt;\r
+                   &lt;docs&gt;http://www.ietf.org/rfc/rfc4287.txt&lt;/docs&gt;\r
+                 &lt;/format&gt;\r
+               &lt;/formats&gt;\r
+               </programlisting>\r
+       </simplesect>\r
+       <simplesect>\r
+               <title>Adding new SuperCat formats</title>      \r
+               <para>SuperCat web services are based on the OpenSRF application, <emphasis>open-ils.supercat</emphasis>.</para> \r
+               <para>Developers are able to add new formats by adding the <emphasis>xsl</emphasis> \r
+               stylesheet for the format in the directory<filename class="directory">/openils/var/web/opac/extras/xsl/</filename>, and by adding the feed references to the perl modules \r
+               <filename>openils/lib/perl5/OpenILS/WWW/SuperCat/feed.pm</filename> and <filename>openils/lib/perl5/OpenILS/WWW/SuperCat.pm</filename>. An Evergreen restart is \r
+               required for the new format to be activated.</para>\r
+               <tip><para>Use an existing xsl stylesheet and perm module entry as a guide for your new format.</para></tip>\r
+       </simplesect>\r
+</chapter>\r
+\r
index a9c5d9b..1db9488 100644 (file)
        <chapterinfo>\r
        <title>Release Notes</title>\r
        </chapterinfo>\r
-       <section id="FeatureOverview">\r
-               <title>1.6 Feature Overview</title>\r
-               <para>This release adds functionality, configuration, and usability improvements in the following areas:</para>\r
+       <simplesect>\r
+               <title>1.6.0.6</title>\r
                <simplesect>\r
-                       <title>1.6.0.6</title>\r
-                       <simplesect>\r
-                               <title>Security</title>\r
-                               <itemizedlist>\r
-                                       <listitem>Address a security vulnerability in open-ils.pcrud that allows retrieval of information beyond the bounds of the permissions for \r
-                                       the targeted objects.</listitem>\r
-                               </itemizedlist> \r
-                       </simplesect>\r
-                       <simplesect>\r
-                               <title>Bug fixes</title>\r
-                               <itemizedlist>\r
-                                       <listitem>Remove a call to a non-existent method.</listitem>\r
-                                       <listitem>Add debugging messages to the action-trigger script and server code</listitem>\r
-                               </itemizedlist> \r
-                       </simplesect>\r
+                       <title>Security</title>\r
+                       <itemizedlist>\r
+                               <listitem>Address a security vulnerability in open-ils.pcrud that allows retrieval of information beyond the bounds of the permissions for \r
+                               the targeted objects.</listitem>\r
+                       </itemizedlist> \r
+               </simplesect>\r
+               <simplesect>\r
+                       <title>Bug fixes</title>\r
+                       <itemizedlist>\r
+                               <listitem>Remove a call to a non-existent method.</listitem>\r
+                               <listitem>Add debugging messages to the action-trigger script and server code</listitem>\r
+                       </itemizedlist> \r
+               </simplesect>\r
+       </simplesect>\r
+       <simplesect>\r
+               <title>1.6.0.5</title>\r
+               <simplesect>\r
+                       <title>New features</title>\r
+                       <itemizedlist>\r
+                               <listitem> Patch from James Fournie to add a setting for first-in, first-out (FIFO) holds resolution so that items checked in will be assigned to holds \r
+                               by request date first, rather than proximity.</listitem>\r
+                       </itemizedlist> \r
+               </simplesect>\r
                <simplesect>\r
-                       <title>1.6.0.5</title>\r
-                       <simplesect>\r
-                               <title>New features</title>\r
-                               <itemizedlist>\r
-                                       <listitem> Patch from James Fournie to add a setting for first-in, first-out (FIFO) holds resolution so that items checked in will be assigned to holds \r
-                                       by request date first, rather than proximity.</listitem>\r
-                               </itemizedlist> \r
-                       </simplesect>\r
-                       <simplesect>\r
-                               <title>Bug fixes</title>\r
-                               <itemizedlist>\r
-                                       <listitem>Patch from Dan Wells to enable the bookbag menu to show up in Craftsman skin.</listitem>\r
-                                       <listitem>Patch from Bill Ott to add missing apostrophe in rdetail.js.</listitem>\r
-                                       <listitem>Fix for report editor parameters not consistently showing up.</listitem>\r
-                                       <listitem>Log bib search timeouts.</listitem>\r
-                               </itemizedlist> \r
-                       </simplesect>\r
+                       <title>Bug fixes</title>\r
+                       <itemizedlist>\r
+                               <listitem>Patch from Dan Wells to enable the bookbag menu to show up in Craftsman skin.</listitem>\r
+                               <listitem>Patch from Bill Ott to add missing apostrophe in rdetail.js.</listitem>\r
+                               <listitem>Fix for report editor parameters not consistently showing up.</listitem>\r
+                               <listitem>Log bib search timeouts.</listitem>\r
+                       </itemizedlist> \r
                </simplesect>\r
+       </simplesect>\r
 \r
+       <simplesect>\r
+               <title>1.6.0.4</title>\r
                <simplesect>\r
-                       <title>1.6.0.4</title>\r
-                       <simplesect>\r
-                               <title>New features</title>\r
-                               <itemizedlist>\r
-                                       <listitem>Patch from Dan Wells to add an org-unit setting to restrict renewals when the item in question is needed to fulfill a hold.</listitem>\r
-                               </itemizedlist> \r
-                       </simplesect>\r
-                       <simplesect>\r
-                               <title>Bug fixes</title>\r
-                               <itemizedlist>\r
-                                       <listitem>Patch from Jason Stephenson to allow the EVERYTHING permission in permission.usr_has_perm_at_nd.</listitem>\r
-                                       <listitem>Patch from Warren Layton to remove a debugging alert in the permission creation interface.</listitem>\r
-                                       <listitem>Patch from Warren Layton to sort Z39.50 servers in Z39.50 import interface.</listitem>\r
-                                       <listitem>Patch from Galen Charlton to prevent legacy 852 fields from being exported during bib+holdings export.</listitem>\r
-                                       <listitem>Patch from Galen Charlton to prevent one bad MARC record from spoiling the rest of the export.</listitem>\r
-                                       <listitem>Patch from Galen Charlton to remove empty XML elements and control fields when ingesting a bib record.</listitem>\r
-                                       <listitem> Patch from Galen Charlton. This patch adds additional calls to escape_xml to handle cases where patron or library data could contain \r
-                                       ampersand or other characters that need to be converted to entities. Issue discovered by Bibliomation; patch includes contributions by Ben Ostrowsky.</listitem>\r
-                                       <listitem>Enable display of barcodes in brief circulation interface even when patron has no middle name (problem diagnosed by \r
-                                       Bill Ott).</listitem>                   \r
-                                       <listitem>Correct the calculation of patron bills.</listitem>\r
-                                       <listitem>Fix parsing of colons in search phrases.</listitem>\r
-                                       <listitem>Fix handling of horizontal patron summary setting.</listitem>\r
-                                       <listitem>Various fixes for server administration interfaces.</listitem>\r
-                                       <listitem>Correct date handling in <emphasis>My Account</emphasis> interface.</listitem>\r
-                                       <listitem>Prevent an exception from being thrown when a standing penalty is removed.</listitem>\r
-                                       <listitem>Fix ISSN quicksearch (bug reported by Dan Wells).</listitem>\r
-                                       <listitem>Prevent colons from being incorrectly inserted into titles in search results display.</listitem>\r
-                                       <listitem>Fix survey interface in patron editor to enable it to save results correctly.</listitem>\r
-                                       <listitem>Corrections in in-database circulation: enable check-out and renewal of pre-cataloged items, process non-cataloged items.</listitem>\r
-                                       <listitem>Correct Unicode handling in SRU/Z39.50 server.</listitem>                     \r
-                               </itemizedlist> \r
-                       </simplesect>\r
+                       <title>New features</title>\r
+                       <itemizedlist>\r
+                               <listitem>Patch from Dan Wells to add an org-unit setting to restrict renewals when the item in question is needed to fulfill a hold.</listitem>\r
+                       </itemizedlist> \r
                </simplesect>\r
                <simplesect>\r
-                       <title>1.6.0.3</title>\r
-                       <simplesect>\r
-                               <title>Bug fixes</title>\r
-                               <itemizedlist>\r
-                                       <listitem>Patch from Dan Wells to address a regression in the Reshelving-to-Available method call.</listitem>\r
-                                       <listitem>Patch from Warren Layton of NRCAN to address a regression in date calculation code.</listitem>\r
-                                       <listitem>Fix for offline identification requirement (relaxed to match on-line patron registration).</listitem>\r
-                               </itemizedlist> \r
-                       </simplesect>\r
+                       <title>Bug fixes</title>\r
+                       <itemizedlist>\r
+                               <listitem>Patch from Jason Stephenson to allow the EVERYTHING permission in permission.usr_has_perm_at_nd.</listitem>\r
+                               <listitem>Patch from Warren Layton to remove a debugging alert in the permission creation interface.</listitem>\r
+                               <listitem>Patch from Warren Layton to sort Z39.50 servers in Z39.50 import interface.</listitem>\r
+                               <listitem>Patch from Galen Charlton to prevent legacy 852 fields from being exported during bib+holdings export.</listitem>\r
+                               <listitem>Patch from Galen Charlton to prevent one bad MARC record from spoiling the rest of the export.</listitem>\r
+                               <listitem>Patch from Galen Charlton to remove empty XML elements and control fields when ingesting a bib record.</listitem>\r
+                               <listitem> Patch from Galen Charlton. This patch adds additional calls to escape_xml to handle cases where patron or library data could contain \r
+                               ampersand or other characters that need to be converted to entities. Issue discovered by Bibliomation; patch includes contributions by Ben Ostrowsky.</listitem>\r
+                               <listitem>Enable display of barcodes in brief circulation interface even when patron has no middle name (problem diagnosed by \r
+                               Bill Ott).</listitem>                   \r
+                               <listitem>Correct the calculation of patron bills.</listitem>\r
+                               <listitem>Fix parsing of colons in search phrases.</listitem>\r
+                               <listitem>Fix handling of horizontal patron summary setting.</listitem>\r
+                               <listitem>Various fixes for server administration interfaces.</listitem>\r
+                               <listitem>Correct date handling in <emphasis>My Account</emphasis> interface.</listitem>\r
+                               <listitem>Prevent an exception from being thrown when a standing penalty is removed.</listitem>\r
+                               <listitem>Fix ISSN quicksearch (bug reported by Dan Wells).</listitem>\r
+                               <listitem>Prevent colons from being incorrectly inserted into titles in search results display.</listitem>\r
+                               <listitem>Fix survey interface in patron editor to enable it to save results correctly.</listitem>\r
+                               <listitem>Corrections in in-database circulation: enable check-out and renewal of pre-cataloged items, process non-cataloged items.</listitem>\r
+                               <listitem>Correct Unicode handling in SRU/Z39.50 server.</listitem>                     \r
+                       </itemizedlist> \r
                </simplesect>\r
+       </simplesect>\r
+       <simplesect>\r
+               <title>1.6.0.3</title>\r
                <simplesect>\r
-                       <title>1.6.0.2</title>\r
-                       <simplesect>\r
-                               <title>New features</title>\r
-                               <itemizedlist>\r
-                                       <listitem>Support indexing normalization and search of ratio-like strings.</listitem>\r
-                                       <listitem>Support specific-index searching via the basic search dropdown.</listitem>\r
-                               </itemizedlist> \r
-                       </simplesect>\r
-                       <simplesect>\r
-                               <title>Bug fixes</title>\r
-                               <itemizedlist>\r
-                                       <listitem>Fix for search bug introduced in 1.6.0.1 which primarily effected Z39.50 searches against Evergreen.</listitem>\r
-                                       <listitem>Fix for offline patron blocked list generation (Patch from Joe Atzberger).</listitem>\r
-                                       <listitem>General translation and internationalization improvements.</listitem>\r
-                                       <listitem>Force at least one non-system billing type to exist (Identified by Dan Wells).</listitem>\r
-                               </itemizedlist> \r
-                       </simplesect>\r
+                       <title>Bug fixes</title>\r
+                       <itemizedlist>\r
+                               <listitem>Patch from Dan Wells to address a regression in the Reshelving-to-Available method call.</listitem>\r
+                               <listitem>Patch from Warren Layton of NRCAN to address a regression in date calculation code.</listitem>\r
+                               <listitem>Fix for offline identification requirement (relaxed to match on-line patron registration).</listitem>\r
+                       </itemizedlist> \r
                </simplesect>\r
-                <simplesect>\r
-                       <title>1.6.0.1</title>\r
-                       <simplesect>\r
-                               <title>Bug fixes</title>\r
-                               <itemizedlist>\r
-                                       <listitem>Overdue notice XML normalization and encoding fixes.</listitem>\r
-                                       <listitem>Remove cosmetic issues with Offline Mode.</listitem>\r
-                                       <listitem>Backport compatibility-improved triggers for summary data collection.</listitem>\r
-                                               <itemizedlist>\r
-                                                       <listitem>(fixed super-simple record extract view issues for isbn and issn)</listitem>\r
-                                               </itemizedlist>\r
-                                       </listitem>\r
-                                       <listitem>Interface fixes for Self Check.</listitem>\r
-                                               <itemizedlist>\r
-                                                       <listitem>(prevent login of patrons who are marked as invalid)</listitem>\r
-                                               </itemizedlist>\r
-                                       </listitem>\r
-                                       <listitem>General grid-related interface cleanups.</listitem>\r
-                                               <itemizedlist>\r
-                                                       <listitem>(fixed pixel and alignment issues in table views accessible from admin settings)</listitem>\r
-                                               </itemizedlist>\r
-                                       </listitem>\r
-                                       <listitem>String translation interface fix – translated strings can be removed.</listitem>\r
-                                               <itemizedlist>\r
-                                                       <listitem>(the translation windows now perform removals correctly)</listitem>\r
-                                               </itemizedlist>\r
-                                       </listitem>\r
-                                       <listitem>Command-line data extraction script fixes (Galen Charlton).</listitem>\r
-                                               <itemizedlist>\r
-                                                       <listitem>(improved batch export)</listitem>\r
-                                               </itemizedlist>\r
-                                       </listitem>\r
-                                       <listitem>Fixed billing time stamp calculation.</listitem>\r
-                                               <itemizedlist>\r
-                                                       <listitem>(e.g. a book that circulates for whole days that is technically due at 3pm doesn't accrue fines until after the library is \r
-                                                       closed)</listitem>\r
-                                               </itemizedlist>\r
-                                       </listitem>\r
-                                       <listitem>Fix for searches containing colons but no command tag.</listitem>\r
-                                               <itemizedlist>\r
-                                                       <listitem>(the : is no longer assumed to be an index specification so title searches for <emphasis>Homeward Bound: the Incredible Journey</emphasis> \r
-                                               will return results)</listitem>\r
-                                               </itemizedlist>\r
-                                       </listitem>\r
-                                       <listitem>Fix for Z39.50 searches containing diacritical marks (Dan Scott).</listitem>\r
-                                               <itemizedlist>\r
-                                                       <listitem>(the SRU is now better at detecting incoming encoding)</listitem>\r
-                                               </itemizedlist>\r
-                                       </listitem>\r
-                                       <listitem>Horizontal user summary display fix in the Checkout entry point.</listitem>\r
-                                       <listitem>Return of Shadowed Record styling in the staff client for records with no items or no items at this location (Bill Ott).</listitem>\r
-                                       <listitem>Holdings import fixes (Dan Wells) (see changeset 15353).</listitem>\r
-                                               <itemizedlist>\r
-                                                       <listitem>(Found and fixed the Vandelay bug that manifested based on log in type.)</listitem>\r
-                                               </itemizedlist>\r
-                                       </listitem>\r
-                                       <listitem>Holdings import fixes (Dan Wells) (see changeset 15353).</listitem>\r
-                                       <listitem>Fixed an error that occurred when renewing multiple items at once in Items Out</listitem>                                     \r
-                               </itemizedlist> \r
-                       </simplesect>\r
-                       <simplesect>\r
-                               <title>New features (front end)</title>\r
-                               <itemizedlist>\r
-                                       <listitem>French translation updates.</listitem>\r
-                                       <listitem>Several new translations:\r
-                                               <itemizedlist>\r
-                                                       <listitem>Russian (from Tigran Zargaryan)</listitem>\r
-                                                       <listitem>Czech (forward-ported from 1.4)</listitem>\r
-                                                       <listitem>British English (submitted via Launchpad)</listitem>\r
-                                                       <listitem>Spanish (submitted via Launchpad)</listitem>\r
-                                                       <listitem>Brazilian Portuguese (submitted via Launchpad)</listitem>\r
-                                               </itemizedlist>\r
-                                       </listitem>\r
-                                       <listitem>More places to access Record Buckets in the staff client</listitem>\r
-                                       <listitem>Virtual due date for non-cataloged circulations honors closed dates</listitem>\r
-                                       <listitem>Differentiated messages for inactive vs. non-existent users.</listitem>\r
-                                               <itemizedlist>\r
-                                                       <listitem>(error messages in patron OPAC log in are now different for inactive patrons versus bad log in (typo)/non-existent user)</listitem>\r
-                                               </itemizedlist>\r
-                                       </listitem>\r
-                               </itemizedlist> \r
-                       </simplesect>\r
-                       <simplesect>\r
-                               <title>New features (server/administration)</title>\r
-                               <itemizedlist>  \r
-                                       <listitem>Action/Trigger initiator script\r
-                                               <itemizedlist>\r
-                                                       <listitem>(1.6.0.1 includes the default script to initiate system scheduling for action/trigger events - for use in cron jobs)</listitem>\r
-                                               </itemizedlist> \r
-                                       </listitem>     \r
-                                       <listitem>Improved MFHD (serials) import script\r
-                                               <itemizedlist>\r
-                                                       <listitem>(improved instructions in the read me files and relaxed database constraints)</listitem>\r
-                                               </itemizedlist> \r
-                                       </listitem>                                     \r
-                                       <listitem>SIP2 configurable encoding support.</listitem>\r
-                                       <listitem>SIP1 renew-or-checkout support for some 3M equipment which support older SIP protocols.</listitem>\r
-                                       <listitem>Updated Linux distribution support.</listitem>\r
-                                       <listitem>Automatic update of OpenSRF support files when OpenSRF is upgraded.</listitem>\r
-                               </itemizedlist> \r
-                       </simplesect>\r
+       </simplesect>\r
+       <simplesect>\r
+               <title>1.6.0.2</title>\r
+               <simplesect>\r
+                       <title>New features</title>\r
+                       <itemizedlist>\r
+                               <listitem>Support indexing normalization and search of ratio-like strings.</listitem>\r
+                               <listitem>Support specific-index searching via the basic search dropdown.</listitem>\r
+                       </itemizedlist> \r
+               </simplesect>\r
+               <simplesect>\r
+                       <title>Bug fixes</title>\r
+                       <itemizedlist>\r
+                               <listitem>Fix for search bug introduced in 1.6.0.1 which primarily effected Z39.50 searches against Evergreen.</listitem>\r
+                               <listitem>Fix for offline patron blocked list generation (Patch from Joe Atzberger).</listitem>\r
+                               <listitem>General translation and internationalization improvements.</listitem>\r
+                               <listitem>Force at least one non-system billing type to exist (Identified by Dan Wells).</listitem>\r
+                       </itemizedlist> \r
                </simplesect>\r
-               <simplesect>\r
-                       <title>Features from 1.6.0.0</title>\r
-                       \r
-                       <simplesect>\r
-                               <title>New features (front end)</title>\r
-                               <itemizedlist>\r
-                                       <listitem> Added <quote>insert copy above</quote> (<keycombo><keycap>CTRL</keycap> <keycap>up</keycap></keycombo>) and <quote>insert copy below</quote> (keycombo><keycap>CTRL</keycap> <keycap>down</keycap></keycombo>) functionality in the MARC Editor.</listitem>\r
-                                       <listitem>Summary editing in MARC Format for Holdings Data</listitem>\r
-                                       <listitem> BibTemplate OPAC templating – Any field from any version of a record that Evergreen can deliver, with or without embedded holdings, \r
-                                       is now available for display using a simple template language which is further extended with basic JavaScript.\r
-                                               <itemizedlist>\r
-                                                       <listitem> Template customization is now supported that allows specific data fields to be pulled from the MARC and displayed in the OPAC.</listitem>\r
-                                                       <listitem>Examples would be: added author, alternate title, subject links, and URI data.</listitem>\r
-                                               </itemizedlist>\r
-                                       </listitem>\r
-                                       <listitem> BibTemplate OPAC templating – Any field from any version of a record that Evergreen can deliver, with or without embedded holdings, \r
-                                       is now available for display using a simple template language which is further extended with basic JavaScript.\r
-                                               <itemizedlist>\r
-                                                       <listitem> Template customization is now supported that allows specific data fields to be pulled from the MARC and displayed in the OPAC.</listitem>\r
-                                                       <listitem>Examples would be: added author, alternate title, subject links, and URI data.</listitem>\r
-                                               </itemizedlist>\r
-                                       </listitem>\r
-                                       <listitem>Located URIs – Adding an 856$9 containing the short name of a location will restrict search and display of entirely electronic records \r
-                                       (those with no physical copies) to the location named.\r
-                                               <itemizedlist>\r
-                                                       <listitem>In other words, the ability to restrict record visibility to a specific location or set of \r
-                                                       locations in the same way as copies but without creating dummies.</listitem>\r
-                                                       <listitem>Since there is no physical location, however, this does affect advanced searches wherein the shelving location limiter is used.</listitem>\r
-                                                       <listitem>(improved instructions in the read me files and relaxed database constraints)</listitem>\r
-                                               </itemizedlist> \r
-                                       </listitem>     \r
-                                       <listitem>SRU(search/retrieval via url) and Z39.50 searches can now be scoped to specific locations.\r
-                                               <itemizedlist>\r
-                                                       <listitem>As of Evergreen 1.6, you can append an optional organization unit shortname for search scoping purposes, and you \r
-                                                       can also append /holdings if you want to expose the holdings for any returned records. So your zurl could be \r
-                                                       http://dev.gapines.org/opac/extras/sru/BR1/holdings to limit the search scope to BR1 and its children, and to expose its holdings.</listitem>\r
-                                               </itemizedlist> \r
-                                       </listitem>                                             \r
-                                       <listitem>As a benefit of the URI work, Z39.50 now supports a holdings record format.</listitem>\r
-                                       <listitem>Improvements in Fixed Field handling within the MARC Editor.</listitem>\r
-                                       <listitem>Staff-placed holds for patrons follow patron settings more closely (no longer pull notification preferences from staff settings) – \r
-                                       Patch from Jeff Godin of TADL.</listitem>\r
-                                       <listitem>Improved default configuration for LoC Z39.50 target - added support for required truncation specific to LoC.</listitem>\r
-                                       <listitem>Added a new default indexing definition for “all subjects” which will return more results when subject searching in the OPAC.</listitem>\r
-                                       <listitem>Many new server configuration interfaces for functions such as circulation policies, hold policies, and notifications.</listitem>\r
-                                       <listitem>Added time granularity display to Patron Items Out screen in the Staff Client. “Due time” now displays along with due date.</listitem>\r
-                                       <listitem>Added RefWorks (online bibliographic management program) export capability.</listitem>\r
-                                       <listitem> Zotero compatability improvements (MODS namespacing).\r
-                                               <itemizedlist>\r
-                                                       <listitem>For more information on MODS, see this page.</listitem>\r
-                                               </itemizedlist> \r
-                                       </listitem>\r
-                                       <listitem>Ability to import holdings via the standard Record Importer (Vandelay).</listitem>\r
-                                       <listitem>Google Book Preview support as added-content</listitem>\r
-                                       <listitem>Improvements made to cloned patron search; fixing issues with records not returning due to cloned fields.</listitem>\r
-                                       <listitem>Acquisitions Preview includes a sneak peek at the preliminary work for manual funding management, PO creation, cataloging and receiving processes. \r
-                                       These are functional but are not intended for insertion into current workflows. This feature was specifically included to solicit feedback from \r
-                                       the community on this important feature.</listitem>\r
-                                                               \r
-                               </itemizedlist> \r
-                       </simplesect>\r
-                       <simplesect>\r
-                               <title>New features (server/administration)</title>\r
-          &