+New service definition element, xslt, that allows an embedded stylesheet
+to be defined. This can be referred to from pz:xslt as an alternative to
+external files.
+
New pz:sortmap:field setting for specifying hints on how to make
a target natively sort on a field. This is used for command=show in
conjunction with sort.
</varlistentry>
<varlistentry>
+ <term id="servicexslt" xreflabel="xslt">xslt</term>
+ <listitem>
+ <para>
+ Defines a XSLT stylesheet. The <literal>xslt</literal>
+ element takes exactly one attribute <literal>id</literal>
+ which names the stylesheet. This can be referred to in target
+ settings <xref linkend="pzxslt"/>.
+ </para>
+ <para>
+ The content of the xslt element is the embedded stylesheet XML
+ </para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
<term id="icuchain" xreflabel="icu_chain">icu_chain</term>
<listitem>
<para>
</varlistentry>
<varlistentry>
- <term>pz:xslt</term>
+ <term id="pzxslt" xreflabel="pz:xslt">pz:xslt</term>
<listitem>
<para>
- Is a comma separated list of of files that specifies
+ Is a comma separated list of of stylesheet names that specifies
how to convert incoming records to the internal representation.
</para>
<para>
+ For each name, the embedded stylesheets (XSL) that comes with the
+ service definition are consulted first and takes precedence over
+ external files; see <xref linkend="servicexslt"/>
+ of service definition).
+ If the name does not match an embedded stylesheet it is
+ considered a filename.
+ </para>
+ <para>
The suffix of each file specifies the kind of tranformation.
Suffix "<literal>.xsl</literal>" makes an XSL transform. Suffix
"<literal>.mmap</literal>" will use the MMAP transform (described below).
record.c record.h \
relevance.c relevance.h \
sel_thread.c sel_thread.h \
+ service_xslt.c service_xslt.h \
session.c session.h \
settings.c settings.h \
termlists.c termlists.h
}
normalize_record_t normalize_cache_get(normalize_cache_t nc,
- struct conf_config *conf,
+ struct conf_service *service,
const char *spec)
{
normalize_record_t nt;
nt = ci->nt;
else
{
- nt = normalize_record_create(conf, spec);
+ nt = normalize_record_create(service, spec);
if (nt)
{
ci = nmem_malloc(nc->nmem, sizeof(*ci));
normalize_cache_t normalize_cache_create(void);
normalize_record_t normalize_cache_get(normalize_cache_t nc,
- struct conf_config *conf,
+ struct conf_service *service,
const char *spec);
void normalize_cache_destroy(normalize_cache_t nc);
#include "normalize_record.h"
#include "pazpar2_config.h"
-
+#include "service_xslt.h"
#include "marcmap.h"
#include <libxslt/xslt.h>
#include <libxslt/transform.h>
struct normalize_step {
struct normalize_step *next;
- xsltStylesheet *stylesheet;
+ xsltStylesheet *stylesheet; /* created by normalize_record */
+ xsltStylesheet *stylesheet2; /* external stylesheet (service) */
struct marcmap *marcmap;
};
NMEM nmem;
};
-normalize_record_t normalize_record_create(struct conf_config *conf,
+normalize_record_t normalize_record_create(struct conf_service *service,
const char *spec)
{
NMEM nmem = nmem_create();
int i, num;
int no_errors = 0;
char **stylesheets;
+ struct conf_config *conf = service->server->config;
nt->nmem = nmem;
*m = nmem_malloc(nt->nmem, sizeof(**m));
(*m)->marcmap = NULL;
(*m)->stylesheet = NULL;
-
- // XSLT
- if (!strcmp(&stylesheets[i][strlen(stylesheets[i])-4], ".xsl"))
+
+ (*m)->stylesheet2 = service_xslt_get(service, stylesheets[i]);
+ if ((*m)->stylesheet2)
+ ;
+ else if (!strcmp(&stylesheets[i][strlen(stylesheets[i])-4], ".xsl"))
{
if (!((*m)->stylesheet =
xsltParseStylesheetFile((xmlChar *) wrbuf_cstr(fname))))
no_errors++;
}
}
- // marcmap
else if (!strcmp(&stylesheets[i][strlen(stylesheets[i])-5], ".mmap"))
{
if (!((*m)->marcmap = marcmap_load(wrbuf_cstr(fname), nt->nmem)))
xmlDoc *ndoc;
if (m->stylesheet)
ndoc = xsltApplyStylesheet(m->stylesheet, *doc, parms);
+ else if (m->stylesheet2)
+ ndoc = xsltApplyStylesheet(m->stylesheet2, *doc, parms);
else if (m->marcmap)
ndoc = marcmap_apply(m->marcmap, *doc);
else
#define NORMALIZE_RECORD_H
typedef struct normalize_record_s *normalize_record_t;
-struct conf_config;
+struct conf_service;
-normalize_record_t normalize_record_create(struct conf_config *conf,
+normalize_record_t normalize_record_create(struct conf_service *service,
const char *spec);
void normalize_record_destroy(normalize_record_t nt);
#include "ppmutex.h"
#include "incref.h"
#include "pazpar2_config.h"
+#include "service_xslt.h"
#include "settings.h"
#include "eventl.h"
#include "http.h"
database_hosts_t database_hosts;
};
+struct service_xslt
+{
+ char *id;
+ xsltStylesheetPtr xsp;
+ struct service_xslt *next;
+};
+
static void conf_metadata_assign(NMEM nmem,
struct conf_metadata * metadata,
const char *name,
service->nmem = nmem;
service->next = 0;
service->databases = 0;
+ service->xslt_list = 0;
service->server = server;
service->session_timeout = 60; /* default session timeout */
service->z3950_session_timeout = 180;
{
if (!pazpar2_decref(&service->ref_count, service->mutex))
{
+ service_xslt_destroy(service);
pp2_charset_fact_destroy(service->charsets);
yaz_mutex_destroy(&service->mutex);
nmem_destroy(service->nmem);
if (parse_metadata(service, n, &md_node, &sk_node))
return 0;
}
+ else if (!strcmp((const char *) n->name, (const char *) "xslt"))
+ {
+ if (service_xslt_config(service, n))
+ return 0;
+ }
else
{
yaz_log(YLOG_FATAL, "Bad element: %s", n->name);
/* duplicated from conf_server */
pp2_charset_fact_t charsets;
+ struct service_xslt *xslt_list;
+
struct database *databases;
struct conf_server *server;
};
--- /dev/null
+/* This file is part of Pazpar2.
+ Copyright (C) 2006-2011 Index Data
+
+Pazpar2 is free software; you can redistribute it and/or modify it under
+the terms of the GNU General Public License as published by the Free
+Software Foundation; either version 2, or (at your option) any later
+version.
+
+Pazpar2 is distributed in the hope that it will be useful, but WITHOUT ANY
+WARRANTY; without even the implied warranty of MERCHANTABILITY or
+FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+
+*/
+
+#if HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <string.h>
+#include <assert.h>
+
+#include <libxml/parser.h>
+#include <libxml/tree.h>
+
+#include <yaz/yaz-util.h>
+#include <yaz/nmem.h>
+#include <yaz/snprintf.h>
+#include <yaz/tpath.h>
+#include <yaz/xml_include.h>
+
+#include "service_xslt.h"
+#include "pazpar2_config.h"
+
+struct service_xslt
+{
+ char *id;
+ xsltStylesheetPtr xsp;
+ struct service_xslt *next;
+};
+
+xsltStylesheetPtr service_xslt_get(struct conf_service *service,
+ const char *id)
+{
+ struct service_xslt *sx;
+ for (sx = service->xslt_list; sx; sx = sx->next)
+ if (!strcmp(id, sx->id))
+ return sx->xsp;
+ return 0;
+}
+
+void service_xslt_destroy(struct conf_service *service)
+{
+ struct service_xslt *sx = service->xslt_list;
+ for (; sx; sx = sx->next)
+ xsltFreeStylesheet(sx->xsp);
+}
+
+int service_xslt_config(struct conf_service *service, xmlNode *n)
+{
+ xmlDoc *xsp_doc;
+ xmlNode *root = n->children;
+ struct service_xslt *sx;
+ const char *id = 0;
+ struct _xmlAttr *attr;
+ for (attr = n->properties; attr; attr = attr->next)
+ if (!strcmp((const char *) attr->name, "id"))
+ id = (const char *) attr->children->content;
+ else
+ {
+ yaz_log(YLOG_FATAL, "Invalid attribute %s for xslt element",
+ (const char *) n->name);
+ return -1;
+ }
+ if (!id)
+ {
+ yaz_log(YLOG_FATAL, "Missing attribute id for xslt element");
+ return 0;
+ }
+ while (root && root->type != XML_ELEMENT_NODE)
+ root = root->next;
+ if (!root)
+ {
+ yaz_log(YLOG_FATAL, "Missing content for xslt element");
+ return -1;
+ }
+ for (sx = service->xslt_list; sx; sx = sx->next)
+ if (!strcmp(sx->id, id))
+ {
+ yaz_log(YLOG_FATAL, "Multiple xslt with id=%s", id);
+ return -1;
+ }
+
+ sx = nmem_malloc(service->nmem, sizeof(*sx));
+ sx->id = nmem_strdup(service->nmem, id);
+ sx->next = service->xslt_list;
+ service->xslt_list = sx;
+
+ xsp_doc = xmlNewDoc(BAD_CAST "1.0");
+ xmlDocSetRootElement(xsp_doc, xmlCopyNode(root, 1));
+ sx->xsp = xsltParseStylesheetDoc(xsp_doc);
+ if (!sx->xsp)
+ {
+ xmlFreeDoc(xsp_doc);
+ yaz_log(YLOG_FATAL, "Failed to parse XSLT");
+ return -1;
+ }
+ return 0;
+}
+
+/*
+ * Local variables:
+ * c-basic-offset: 4
+ * c-file-style: "Stroustrup"
+ * indent-tabs-mode: nil
+ * End:
+ * vim: shiftwidth=4 tabstop=8 expandtab
+ */
+
--- /dev/null
+/* This file is part of Pazpar2.
+ Copyright (C) 2006-2011 Index Data
+
+Pazpar2 is free software; you can redistribute it and/or modify it under
+the terms of the GNU General Public License as published by the Free
+Software Foundation; either version 2, or (at your option) any later
+version.
+
+Pazpar2 is distributed in the hope that it will be useful, but WITHOUT ANY
+WARRANTY; without even the implied warranty of MERCHANTABILITY or
+FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+
+*/
+
+#ifndef SERVICE_XSLT_H
+#define SERVICE_XSLT_H
+
+#include <libxslt/xsltutils.h>
+#include <libxslt/transform.h>
+
+struct conf_service;
+
+xsltStylesheetPtr service_xslt_get(struct conf_service *service,
+ const char *id);
+
+void service_xslt_destroy(struct conf_service *service);
+
+int service_xslt_config(struct conf_service *service, xmlNode *n);
+
+#endif
+
+/*
+ * Local variables:
+ * c-basic-offset: 4
+ * c-file-style: "Stroustrup"
+ * indent-tabs-mode: nil
+ * End:
+ * vim: shiftwidth=4 tabstop=8 expandtab
+ */
+
}
}
sdb->map = normalize_cache_get(se->normalize_cache,
- se->service->server->config, s);
+ se->service, s);
if (!sdb->map)
return -1;
}
<!-- Result normalization settings -->
<set name="pz:nativesyntax" value="iso2709"/>
- <set name="pz:xslt" value="marc21_test.xsl"/>
+ <set name="pz:xslt" value="myxslt"/>
<set name="pz:apdulog" value="1"/>
<set name="pz:maxrecs" value="3" />
<metadata name="author" brief="yes" termlist="yes" merge="longest" rank="2"/>
<metadata name="subject" merge="unique" termlist="yes" rank="3"/>
<metadata name="id"/>
+
+ <xslt id="myxslt">
+<xsl:stylesheet
+ version="1.0"
+ xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
+ xmlns:pz="http://www.indexdata.com/pazpar2/1.0"
+ xmlns:marc="http://www.loc.gov/MARC21/slim">
+
+ <xsl:param name="test"/>
+
+ <xsl:output indent="yes" method="xml" version="1.0" encoding="UTF-8"/>
+
+<!-- Extract metadata from MARC21/USMARC
+ http://www.loc.gov/marc/bibliographic/ecbdhome.html
+-->
+
+ <xsl:template match="marc:record">
+ <xsl:variable name="title_medium" select="marc:datafield[@tag='245']/marc:subfield[@code='h']"/>
+ <xsl:variable name="journal_title" select="marc:datafield[@tag='773']/marc:subfield[@code='t']"/>
+ <xsl:variable name="electronic_location_url" select="marc:datafield[@tag='856']/marc:subfield[@code='u']"/>
+ <xsl:variable name="fulltext_a" select="marc:datafield[@tag='900']/marc:subfield[@code='a']"/>
+ <xsl:variable name="fulltext_b" select="marc:datafield[@tag='900']/marc:subfield[@code='b']"/>
+ <xsl:variable name="medium">
+ <xsl:choose>
+ <xsl:when test="$title_medium">
+ <xsl:value-of select="substring-after(substring-before($title_medium,']'),'[')"/>
+ </xsl:when>
+ <xsl:when test="$fulltext_a">
+ <xsl:text>electronic resource</xsl:text>
+ </xsl:when>
+ <xsl:when test="$fulltext_b">
+ <xsl:text>electronic resource</xsl:text>
+ </xsl:when>
+ <xsl:when test="$electronic_location_url">
+ <xsl:text>electronic resource</xsl:text>
+ </xsl:when>
+ <xsl:when test="$journal_title">
+ <xsl:text>article</xsl:text>
+ </xsl:when>
+ <xsl:otherwise>
+ <xsl:text>book</xsl:text>
+ </xsl:otherwise>
+ </xsl:choose>
+ </xsl:variable>
+
+ <pz:record>
+ <xsl:attribute name="mergekey">
+ <xsl:text>title </xsl:text>
+ <xsl:value-of select="marc:datafield[@tag='245']/marc:subfield[@code='a']"/>
+ <xsl:text> author </xsl:text>
+ <xsl:value-of select="marc:datafield[@tag='100']/marc:subfield[@code='a']"/>
+ <xsl:text> medium </xsl:text>
+ <xsl:value-of select="$medium"/>
+ </xsl:attribute>
+
+ <pz:metadata type="test-usersetting-2">
+ test-usersetting-2 data:
+ <xsl:value-of select="$test"/>
+ </pz:metadata>
+
+ <xsl:for-each select="marc:controlfield[@tag='001']">
+ <pz:metadata type="id">
+ <xsl:value-of select="."/>
+ </pz:metadata>
+ </xsl:for-each>
+
+ <xsl:for-each select="marc:datafield[@tag='010']">
+ <pz:metadata type="lccn">
+ <xsl:value-of select="marc:subfield[@code='a']"/>
+ </pz:metadata>
+ </xsl:for-each>
+
+ <xsl:for-each select="marc:datafield[@tag='020']">
+ <pz:metadata type="isbn">
+ <xsl:value-of select="marc:subfield[@code='a']"/>
+ </pz:metadata>
+ </xsl:for-each>
+
+ <xsl:for-each select="marc:datafield[@tag='022']">
+ <pz:metadata type="issn">
+ <xsl:value-of select="marc:subfield[@code='a']"/>
+ </pz:metadata>
+ </xsl:for-each>
+
+ <xsl:for-each select="marc:datafield[@tag='027']">
+ <pz:metadata type="tech-rep-nr">
+ <xsl:value-of select="marc:subfield[@code='a']"/>
+ </pz:metadata>
+ </xsl:for-each>
+
+ <xsl:for-each select="marc:datafield[@tag='100']">
+ <pz:metadata type="author">
+ <xsl:value-of select="marc:subfield[@code='a']"/>
+ </pz:metadata>
+ <pz:metadata type="author-title">
+ <xsl:value-of select="marc:subfield[@code='c']"/>
+ </pz:metadata>
+ <pz:metadata type="author-date">
+ <xsl:value-of select="marc:subfield[@code='d']"/>
+ </pz:metadata>
+ </xsl:for-each>
+
+ <xsl:for-each select="marc:datafield[@tag='110']">
+ <pz:metadata type="corporate-name">
+ <xsl:value-of select="marc:subfield[@code='a']"/>
+ </pz:metadata>
+ <pz:metadata type="corporate-location">
+ <xsl:value-of select="marc:subfield[@code='c']"/>
+ </pz:metadata>
+ <pz:metadata type="corporate-date">
+ <xsl:value-of select="marc:subfield[@code='d']"/>
+ </pz:metadata>
+ </xsl:for-each>
+
+ <xsl:for-each select="marc:datafield[@tag='111']">
+ <pz:metadata type="meeting-name">
+ <xsl:value-of select="marc:subfield[@code='a']"/>
+ </pz:metadata>
+ <pz:metadata type="meeting-location">
+ <xsl:value-of select="marc:subfield[@code='c']"/>
+ </pz:metadata>
+ <pz:metadata type="meeting-date">
+ <xsl:value-of select="marc:subfield[@code='d']"/>
+ </pz:metadata>
+ </xsl:for-each>
+
+ <xsl:for-each select="marc:datafield[@tag='260']">
+ <pz:metadata type="date">
+ <xsl:value-of select="marc:subfield[@code='c']"/>
+ </pz:metadata>
+ </xsl:for-each>
+
+ <xsl:for-each select="marc:datafield[@tag='245']">
+ <pz:metadata type="title">
+ <xsl:value-of select="marc:subfield[@code='a']"/>
+ </pz:metadata>
+ <pz:metadata type="title-remainder">
+ <xsl:value-of select="marc:subfield[@code='b']"/>
+ </pz:metadata>
+ <pz:metadata type="title-responsibility">
+ <xsl:value-of select="marc:subfield[@code='c']"/>
+ </pz:metadata>
+ <pz:metadata type="title-dates">
+ <xsl:value-of select="marc:subfield[@code='f']"/>
+ </pz:metadata>
+ <pz:metadata type="title-medium">
+ <xsl:value-of select="marc:subfield[@code='h']"/>
+ </pz:metadata>
+ </xsl:for-each>
+
+ <xsl:for-each select="marc:datafield[@tag='250']">
+ <pz:metadata type="edition">
+ <xsl:value-of select="marc:subfield[@code='a']"/>
+ </pz:metadata>
+ </xsl:for-each>
+
+ <xsl:for-each select="marc:datafield[@tag='260']">
+ <pz:metadata type="publication-place">
+ <xsl:value-of select="marc:subfield[@code='a']"/>
+ </pz:metadata>
+ <pz:metadata type="publication-name">
+ <xsl:value-of select="marc:subfield[@code='b']"/>
+ </pz:metadata>
+ <pz:metadata type="publication-date">
+ <xsl:value-of select="marc:subfield[@code='c']"/>
+ </pz:metadata>
+ </xsl:for-each>
+
+ <xsl:for-each select="marc:datafield[@tag='300']">
+ <pz:metadata type="physical-extent">
+ <xsl:value-of select="marc:subfield[@code='a']"/>
+ </pz:metadata>
+ <pz:metadata type="physical-format">
+ <xsl:value-of select="marc:subfield[@code='b']"/>
+ </pz:metadata>
+ <pz:metadata type="physical-dimensions">
+ <xsl:value-of select="marc:subfield[@code='c']"/>
+ </pz:metadata>
+ <pz:metadata type="physical-accomp">
+ <xsl:value-of select="marc:subfield[@code='e']"/>
+ </pz:metadata>
+ <pz:metadata type="physical-unittype">
+ <xsl:value-of select="marc:subfield[@code='f']"/>
+ </pz:metadata>
+ <pz:metadata type="physical-unitsize">
+ <xsl:value-of select="marc:subfield[@code='g']"/>
+ </pz:metadata>
+ <pz:metadata type="physical-specified">
+ <xsl:value-of select="marc:subfield[@code='3']"/>
+ </pz:metadata>
+ </xsl:for-each>
+
+ <xsl:for-each select="marc:datafield[@tag='440']">
+ <pz:metadata type="series-title">
+ <xsl:value-of select="marc:subfield[@code='a']"/>
+ </pz:metadata>
+ </xsl:for-each>
+
+ <xsl:for-each select="marc:datafield[@tag >= 500 and @tag <= 599]
+ [@tag != '506' and @tag != '530' and
+ @tag != '540' and @tag != '546'
+ and @tag != '522']">
+ <!-- The tag attribute below will be preserved -->
+ <pz:metadata type="description" tag="{@tag}">
+ <xsl:value-of select="*/text()"/>
+ </pz:metadata>
+ </xsl:for-each>
+
+ <xsl:for-each select="marc:datafield[@tag='650' or @tag='653']">
+ <pz:metadata type="subject">
+ <xsl:value-of select="marc:subfield[@code='a']"/>
+ </pz:metadata>
+ </xsl:for-each>
+
+ <xsl:for-each select="marc:datafield[@tag='856']">
+ <pz:metadata type="electronic-url">
+ <xsl:value-of select="marc:subfield[@code='u']"/>
+ </pz:metadata>
+ <pz:metadata type="electronic-text">
+ <xsl:value-of select="marc:subfield[@code='y']"/>
+ </pz:metadata>
+ <pz:metadata type="electronic-note">
+ <xsl:value-of select="marc:subfield[@code='z']"/>
+ </pz:metadata>
+ </xsl:for-each>
+
+ <xsl:for-each select="marc:datafield[@tag='773']">
+ <pz:metadata type="citation">
+ <xsl:for-each select="*">
+ <xsl:value-of select="normalize-space(.)"/>
+ <xsl:text> </xsl:text>
+ </xsl:for-each>
+ </pz:metadata>
+ </xsl:for-each>
+
+ <pz:metadata type="medium">
+ <xsl:value-of select="$medium"/>
+ </pz:metadata>
+
+ <xsl:if test="$fulltext_a">
+ <pz:metadata type="fulltext">
+ <xsl:value-of select="$fulltext_a"/>
+ </pz:metadata>
+ </xsl:if>
+
+ <xsl:if test="$fulltext_b">
+ <pz:metadata type="fulltext">
+ <xsl:value-of select="$fulltext_b"/>
+ </pz:metadata>
+ </xsl:if>
+ </pz:record>
+ </xsl:template>
+
+</xsl:stylesheet>
+ </xslt>
</service>
"$(OBJDIR)\ppmutex.obj" \
"$(OBJDIR)\incref.obj" \
"$(OBJDIR)\sel_thread.obj" \
+ "$(OBJDIR)\service_xslt.obj" \
"$(OBJDIR)\connection.obj" \
"$(OBJDIR)\facet_limit.obj"