Loading TOC...
Information Studio Developer's Guide (PDF)

Information Studio Developer's Guide — Chapter 6

Creating Custom Collectors and Transforms

Information Studio is shipped with built-in collectors and transforms. These collectors and transforms are implemented as plugins, which are located in the <marklogic-dir>/Assets/plugins/marklogic/appservices directory and described in Selecting a Collector and Transforming Content During Ingestion.

Information Studio provides a plugin framework that enables you to create custom collectors and transforms to meet your own special needs. The main topics in this chapter are:

The infodev and plugin APIs

The infodev and plugin APIs provide functions that allow you to create custom collector and transform plugins. Both APIs are documented in detail in the MarkLogic XQuery and XSLT Function Reference.

Information Studio Plugin Framework

Information Studio provides a graphical interface for configuring and running collectors and transforms. The Plugin Framework described in this chapter provides a set of tools for creating your own custom plugins for use by Information Studio. The general Plugin Framework is described in the System Plugin Framework chapter in the Application Developer's Guide. This section includes the following parts:

Plugin Directory

Collector and Transform plugins are located in the following directory:

<marklogic-dir>/Assets/plugins/marklogic/appservices 

For example, the Filesystem Directory collector, described in Using the Filesystem Directory Collector, is stored in the following directory:

Assets/plugins/marklogic/appservices/collector-filescan

Each plugin directory contains a manifest.xml file that describes the plugin capabilities, a lib/ subdirectory containing one or more library module files that implement the plugin capabilities, and an assets/ subdirectory containing one or more one or more asset file, such as HTML, JS, CSS, XSLT, and image files. Sub-directories of the assets/ directory are ignored.

For example, the collector-filescan directory contains the following files:

manifest.xml
lib/collector-filescan.xqy

Modules referenced by your plugin can be located in either the plugin lib/ directory, the <marklogic-dir>/Modules directory, or in your modules database, if your code is stored in the database instead of the filesystem.

An error in a plugin in the <marklogic-dir>/Assets/plugins/marklogic/appservices directory will cause that plugin to be ignored. Always test your plugins on a non-production server before installing them on a production server.

Upgrading 4.2 Custom Plugins to MarkLogic 8

The directory where Information Studio looks for its application plugins has changed. In 4.2, the plugins were in the system plugin directory. In MarkLogic 8, they are under the directory shown in Plugin Directory.

If you have plugins created in MarkLogic Server 4.2, you must move them from the old directory:

<marklogic-dir>/Plugins

to the new directory:

<marklogic-dir>/Assets/plugins/marklogic/appservices

Collector or Transform Plugin Module

Every collector or transform application plugin contains a manifest of capabilities and a plugin module that implements the functions defined by the manifest. The available collector capabilities are described in Collector Capabilities and Function Signatures. The available transform capabilities are described in Transform Capabilities and Function Signatures.

A function pointer takes the form:

xdmp:function(xs:QName("prefix:functionName"))

A collector capability name takes the form:

"http://marklogic.com/appservices/infostudio/collector/capability"

A transform capability name takes the form:

"http://marklogic.com/appservices/infostudio/transformer/capability"

A capability must have a name argument that identifies the capability and either:

  • an ns and local-name element that identifies the module namespace and name of the function that implements the capability, or
  • an asset-reference element that identifies an asset in the assets/ directory that is used to implement the capability.

A capability that makes use of a function implemented by a library module in either the plugin lib/ directory or the MarkLogic/modules directory is identified by a module namespace and function name. For example, a collector capability, named model, that makes use of the mycollector:model function in the lib/mycollector.xqy module may be defined as:

<capability name=
  "http://marklogic.com/appservices/infostudio/collector/model">
  <ns>http://marklogic.com/extension/plugin/mycollector</ns> 
  <local-name>model</local-name>
</capability>

A capability that makes use of an asset, such as an HTML, JS, CSS, XSLT, or image file, located in the plugin's assets/ subdirectory, is identified by an asset-reference element. For example, a collector capability, named picture, that makes use of the assets/picture.jpg image may be defined as:

<capability name=
  "http://marklogic.com/appservices/infostudio/collector/picture">
  <asset-reference>picture.jpg</asset-reference>
</capability>

Do not specify the assets/ prefix when identifying an asset.

Example: Basic Capabilities Manifest

The following is the manifest of a very basic collector, named mycollector:

?xml version="1.0" encoding="UTF-8"?>
<plugin xmlns="http://marklogic.com/extension/plugin">
  <name>mycollector</name> 
  <id>collector-mycollector</id> 
  <version>0.1</version> 
  <provider-name>MarkLogic</provider-name> 
  <description>This is my collector</description>
  <module>
    <ns>http://marklogic.com/extension/plugin/mycollector</ns> 
    <ns-prefix>mycollector</ns-prefix>
    <path>lib/mycollector.xqy</path>
  </module>
  <capabilities>
    <capability name=
      "http://marklogic.com/appservices/infostudio/collector/model">
      <ns>http://marklogic.com/extension/plugin/mycollector</ns> 
      <local-name>model</local-name>
    </capability>
    <capability name=
      "http://marklogic.com/appservices/infostudio/collector/start">
      <ns>http://marklogic.com/extension/plugin/mycollector</ns>
      <local-name>start</local-name>
    </capability>
    <capability name=
      "http://marklogic.com/appservices/infostudio/collector/config-view">
      <ns>http://marklogic.com/extension/plugin/mycollector</ns>
      <local-name>view</local-name>
    </capability>
    <capability name=
      "http://marklogic.com/appservices/infostudio/collector/cancel">
      <ns>http://marklogic.com/extension/plugin/mycollector</ns>
      <local-name>cancel</local-name>
    </capability>
    <capability name="http://marklogic.com/appservices/string">
      <ns>http://marklogic.com/extension/plugin/mycollector</ns>
      <local-name>string</local-name>
    </capability>
  </capabilities>
</plugin>

In MarkLogic 8, the plugin capabilities are automatically registered when MarkLogic Server is started. It is no longer necessary to call the plugin:register function as in earlier releases.

Creating Custom Collectors

This section describes how to create custom collectors. For example, you might want to write a collector that unzips zip files and ingests the contents into the database or a collector that ingests RSS feeds. Another type of collector might recognize a particular user and set the ingestion policy specifically for that user or extract files from one database and ingest them into another database.

The main topics in this section are:

Types of Collectors

There are two basic types of collectors:

  • One-shot
  • Long-running

An example of a one-shot collector is the Filesystem Directory collector, which is started, completes a specific ingestion operation, and then automatically stops. An example of a long-running collector is the Browser Drop-Box collector, which once started, continues to 'listen' for input until it is explicitly stopped by a user.

The collector type impacts how you implement the collector, as described in Collector Interaction with Information Studio, as well as how the collector behaves in the event of a MarkLogic Server restart, as described in Collector Type and MarkLogic Server Restart.

Collector Capabilities and Function Signatures

At a minimum, a collector must do the following:

  • Define a data model that specifies the data to be passed into the start function.
  • Define a start function that initiates the load.
  • Define a string function that specifies all of the labels needed for display.
  • Call the infodev:ingest function to ingest the files into the database.

If the collector is long-running it must also define a function that returns a plugin:listener-view element.

The following table describes all of the available collector capabilities and the function signatures used by plugins to implement the capabilities.

CapabilityDescription
model

The model for the data to be passed into the plugin:start function. Currently, model is not used by Information Studio. However future versions will require this capability, so it is recommended that you implement a model to ensure forward compatibility of your plugin.

Function signature:

plugin:model(
) as element(plugin:plugin-model)
start

Starts the plugin.

Function signature:

plugin:start(
   $model as element(),
   $ticket-id as xs:string,
   $policy-deltas as element(info:options)?
) as empty-sequence() 
config-view

The view to display the collector configuration window in the Information Studio Interface. The strings displayed are defined in the plugin:string function.

Note that the $model parameter is optional, so the plugin needs to either return a view for a user-defined model passed from the Information Studio Interface to the plugin:config-view function or return a view for the generic model defined in the plugin:model function, when no model is passed in from the Information Studio Interface.

Function signature:

plugin:config-view(
   $model as element(plugin:plugin-model)?,
   $lang as xs:string,
   $submit-here as xs:string
) as element(plugin:config-view)
listener-view

The listener view for long-running collectors.

Function signature:

plugin:listener-view(
   $upload-here as xs:string
) as element(plugin:listener-view)
handle-post

Handles the ingestion of newly posted documents into the listener-view of a long-running collector.

Function signature:

plugin:handle-post(
   $document as node(),
   $source-location as xs:string,
   $tid as xs:string?,
   $policy-deltas as element(info:options)?,
   $properties as element()*
) as xs:string+
cancel

Sets the ticket status to ‘cancelled'.

Function signature:

plugin:cancel(
   $ticket-id as xs:string
) as empty-sequence()
complete

Sets the ticket status to ‘completed'.

Function signature:

plugin:complete(
   $ticket-id as xs:string
) as empty-sequence()
abort

Sets the ticket status to ‘aborted'.

Function signature:

plugin:abort(
   $ticket-id as xs:string
) as empty-sequence()
validate

Validates the data in the model.

Function signature:

plugin:validate(
   $model as element(plugin:plugin-model)
) as element(plugin:report)*
string

Defines the labels displayed by the collector in the Information Studio Interface.

Function signature:

plugin:string(
   $key as xs:string, 
   $model as element(plugin:plugin-model)?, 
   $lang as xs:string) 
as xs:string?

Collector Interaction with Information Studio

The interaction between Information Studio and a collector is different depending on whether the collector is one-shot or long-running. The following sections describe the interaction between Information Studio and each type of collector.

One-Shot Collectors

The interaction between Information Studio and a one-shot collector is as follows:

  1. When the user creates a flow, a new flow document is created in the App-Services database.
  2. When the user selects a collector for the flow in the Information Studio Interface, Information Studio renders the collector's view defined in the plugin:config-view function as an HTML iframe in the Information Studio Interface.
  3. The iframe describes the elements to be defined in the data model. When the user finishes filling in the iframe fields and clicks the Done button, the flow stored in the App-Services database is updated with the completed data model.
  4. Information Studio displays the completed collector configuration in the Information Studio Interface using the labels in the plugin:string function.
  5. The collector is in the quiescent state until the user clicks Start Loading in the Information Studio Interface. At this point, Information Studio asks for the listener-view capability in the plugin. In this case, the collector does not have a listener-view capability, so it is identified as one-shot in the ticket.
  6. Information Studio updates the ticket in the App-Services database with the collector type and start time and sets its status to ‘active.'
  7. The plugin:start function reads the ticket, data model, and any policy deltas from the flow stored in the App-Services database and calls the infodev:ingest function for each document to ingest into the database.
  8. When Information Studio has finished ingesting the documents, it updates the ticket with the ‘completed' status.
Long-Running Collectors

The interaction between Information Studio and a long-running collector is as follows:

  1. When the user creates a flow, a new flow document is created in the App-Services database.
  2. When the user selects a collector for the flow in the Information Studio Interface, Information Studio displays the collector configuration in the Information Studio Interface using the labels in the plugin:string function.
  3. The collector is in the quiescent state until the user clicks Start Loading in the Information Studio Interface. At that point, Information Studio asks for the listener-view capability in the plugin. In this case, the collector has a listener-view capability, so Information Studio creates a ticket that identifies the plugin as long-running.
  4. Information Studio updates the ticket in the App-Services database with the collector type and start time and sets its status to ‘active.'
  5. Information Studio activates the listener-view defined in the plugin:listener-view function in the Information Studio Interface.

    The Browser Drop-Box collector implements the listener-view as an UploadApplet, but anything that can post multi-part can be used to implement the listener-view.

  6. From this point, as long as the ticket status is ‘active', any documents put into listener are passed to a handle-post function that calls the infodev:ingest function to ingest the document into the database. If multiple documents are put into the listener, the listener ensures that one document at a time is passed to the handle-post function.
  7. The listener-view stays active until the user clicks the Stop Loading, as which time Information Studio terminates the listener-view and sets the ticket status to ‘completed.'

Collector Type and MarkLogic Server Restart

There are special considerations when designing a collector that impact its behavior in the event that MarkLogic Server shuts down and restarts before a collector has completed its ingestion operation.

Whether or not a collector is long-running has implications should MarkLogic Server restart before the collector has completed its ingestion operation. When the collector plugin is called by the user, a ticket is created containing the server start time and an annotation that notes whether the collector is long-running or not long-running. Should MarkLogic Server restart before a collection operation has completed, a trigger on the Database Online event for the App-Services database checks all active tickets. If a ticket is annotated as long-running, no action is taken, so the ticket remains active and the ingestion operation is resumed. If a ticket is not annotated as long-running and if the server start time is later than the start time recorded in the ticket, then the ticket status is set to 'aborted'.

An Example Collector

This section walks through a simple custom collector that inserts a single, fixed document that is specified in the plugin. The collector loads a 'document' with the following content into a database and URI specified by the user:

<root attribute="value">
    <child>content</child>
    <namespaced xmlns="http://marklogic.com">content</namespaced>
</root>
  1. Place the code for the sample collector in the following files under the marklogic home directory:
    Assets/plugins/marklogic/appservices/collector-test/manifest.xml
    Assets/plugins/marklogic/appservices/collector-test/lib/collector-testdoc.xqy
  2. Create a directory named, collector-test, under MarkLogic/Assets/plugins/marklogic/appservices.
  3. Save the following code as the manifest.xml file in the collector-test directory:
    <?xml version="1.0" encoding="UTF-8"?>
    <plugin xmlns="http://marklogic.com/extension/plugin">
      <name>Sample Collector</name> 
      <id>collector-test</id> 
      <version>0.1</version> 
      <provider-name>MarkLogic</provider-name> 
        <description>This is a sample collector plugin</description>
      <module>
        <ns>http://marklogic.com/extension/plugin/test</ns> 
        <ns-prefix>test</ns-prefix>
        <path>lib/collector-test.xqy</path>
      </module>
      <capabilities>
        <capability name="http://marklogic.com/appservices/infostudio/collector/model"> 
          <ns>http://marklogic.com/extension/plugin/test</ns> 
          <local-name>model</local-name>
        </capability>
        <capability name="http://marklogic.com/appservices/infostudio/collector/start">
          <ns>http://marklogic.com/extension/plugin/test</ns>
          <local-name>start</local-name>
        </capability>
        <capability name="http://marklogic.com/appservices/infostudio/collector/config-view">
          <ns>http://marklogic.com/extension/plugin/test</ns>
          <local-name>view</local-name>
        </capability>
        <capability name="http://marklogic.com/appservices/infostudio/collector/cancel">
          <ns>http://marklogic.com/extension/plugin/test</ns>
          <local-name>cancel</local-name>
        </capability>
        <capability name="http://marklogic.com/appservices/infostudio/collector/validate">
          <ns>http://marklogic.com/extension/plugin/test</ns>
          <local-name>validate</local-name>
        </capability>
        <capability name="http://marklogic.com/appservices/string">
          <ns>http://marklogic.com/extension/plugin/test</ns>
          <local-name>string</local-name>
        </capability>
      </capabilities>
    </plugin>
  4. Create a lib directory under collector-test and save the following code as the collector-test.xqy file.
    xquery version "1.0-ml";
    (: Copyright 2002-2012 MarkLogic Corporation.  All Rights Reserved. :)
    module namespace test = "http://marklogic.com/extension/plugin/test";
    import module namespace plugin =
        "http://marklogic.com/extension/plugin"
          at "/MarkLogic/plugin/plugin.xqy";
    import module namespace info=
        "http://marklogic.com/appservices/infostudio" 
          at "/MarkLogic/appservices/infostudio/info.xqy";
    import module namespace infodev=
        "http://marklogic.com/appservices/infostudio/dev" 
          at "/MarkLogic/appservices/infostudio/infodev.xqy";
    declare namespace ml="http://marklogic.com/appservices/mlogic";
    declare namespace lbl="http://marklogic.com/xqutils/labels";
    declare default function namespace
        "http://www.w3.org/2005/xpath-functions";
    (:~ Implement the data model to be used by config-view in the
        Information Studio Interface. :)
    declare function test:model() 
    as element(plugin:plugin-model) 
    {
        <plugin:plugin-model>
           <plugin:data>
              <dir>/test/document.xml</dir>
           </plugin:data>
        </plugin:plugin-model>
    };
    (:~ Implement the start function that starts the plugin. All 
        collector start functions accept the same parameters: the 
        plugin model, the ticket ID, and optional policy deltas. :)
    declare function test:start(
        $model as element(plugin:plugin-model),
        $ticket-id as xs:string,
        $policy-deltas as element(info:options)?
    ) as empty-sequence()
    {
    (: The "document" to be ingested :)
        let $doc :=
          <root attribute="value">
            <child>content</child>
            <namespaced xmlns="http://marklogic.com">content</namespaced>
          </root>
        let $path := $model/plugin:data/path
        let $_ := infodev:ticket-set-total-documents($ticket-id, 1)
    (: Ingest the document into the database and log the ingest event
       to the ticket's progress file in the App-Services database. :)
        let $ingestion :=
          try {
            infodev:ingest($doc, $path, $ticket-id, $policy-deltas),
            infodev:log-progress(
               $ticket-id, 
               <info:annotation>test doc inserted</info:annotation>,
               1)
          } catch($e) {
              infodev:handle-error($ticket-id, $path, $e)
            }
    (: When ingestion has completed, reset the ticket status. :)
        let $_ := infodev:ticket-set-status($ticket-id, "completed")
      return ()
    };
    (:~ Implement the view function to display the collector popup
        configuration window in the Information Studio Interface. The
        strings displayed are defined in the testdoc:string function. 
        The "Done" button comes from the Application Services mlogic tag
        library. 
        Note the value of the input type, "{$model/plugin:data/*:path}".
        This is because path is not in the default namespace. :)
    declare function test:view(
        $model as element(plugin:plugin-model)?, 
        $lang as xs:string, 
        $submit-here as xs:string)
    as element(plugin:config-view)
    {
        <config-view xmlns="http://marklogic.com/extension/plugin">
          <html xmlns="http://www.w3.org/1999/xhtml">
            <head>
              <title>iframe plugin configuration</title>
            </head>
            <body>
              <form action="{$submit-here}" method="post">
                 <label for="path">
                   {test:string("path-label", $model, $lang)}
                 </label>
                 <input type="text" name="path" id="path" 
                  value="{$model/plugin:data/*:path}"/>
                 <br/>
                 <ml:submit label="Done"/>
              </form>
            </body>
          </html>
        </config-view>
    };
    (:~ Implement a function to cancel an active ticket :) 
    declare function test:cancel(
        $ticket-id as xs:string) 
    as empty-sequence()
    {
        infodev:ticket-set-status($ticket-id, "cancelled")
    };
    (:~ Implement a function to validate the plugin model. Return an 
        empty sequence if a path is specified in the plugin model.
        Report an error if the path is empty. :)
    declare function test:validate(
        $model as element(plugin:plugin-model)
    ) as element(plugin:report)*
    {
        if (normalize-space(string($model/plugin:data/path)) eq "")
        then 
           <plugin:report id="db">
              Specified path must not be empty
           </plugin:report>
        else ()
    };
    (:~ Implement a function to define all of the labels displayed by 
        the collector. This plugin uses the labels defined in the 
        Application Services label library. :)
    declare function test:string(
        $key as xs:string,
        $model as element(plugin:plugin-model)?, 
        $lang as xs:string)
    as xs:string?
    {
        let $labels :=
        <lbl:labels xmlns:lbl="http://marklogic.com/xqutils/labels">
          <lbl:label key="name">
            <lbl:value xml:lang="en">
              Load an individual document for testing purposes
            </lbl:value>
          </lbl:label>
          <lbl:label key="description">
            <lbl:value xml:lang="en">
              Insert a fixed document
            </lbl:value>
          </lbl:label>
          <lbl:label key="path-label">
            <lbl:value xml:lang="en">Path:</lbl:value>
          </lbl:label>
        </lbl:labels>
    return $labels/lbl:label[@key eq $key]/lbl:value[@xml:lang eq $lang]/string()
    };

Initializing the Example Collector

To initialize the collector-test plugin, enter the following query:

  1. Open Query Console and enter the following query:
    xquery version "1.0-ml"; 
    import module namespace plugin =
      "http://marklogic.com/extension/plugin" 
        at "/MarkLogic/plugin/plugin.xqy";
    plugin:flush-scope("marklogic.appservices.collector-test"), 
    plugin:install-from-filesystem("marklogic.appservices"), 
    plugin:initialize-scope("marklogic.appservices.collector-test")
  2. Restart MarkLogic Server

Creating Custom Transforms

This section describes how to create custom transforms. Transforms utilize MarkLogic Server Content Processing Framework (CPF) to modify one document at a time as it is loaded into the database. Information Studio automatically configures CPF domains, pipelines, and triggers on the Fab database for each transform. A transformation process must be linear, so branching and conditional operations are not possible.

The main topics in this section are:

Transform Capabilities and Function Signatures

All transforms must do the following:

  • Define a data model that specifies the data to be passed to the compile function.
  • Define a configuration view to display the transform configuration window in the UI.
  • Define a compile function that modifies the data in the model.
  • Define a string function that specifies all of the labels needed for display.

The following table describes all of the available transform capabilities and the function signatures used by plugins to implement the capabilities.

CapabilityDescription
model

The data model for the UI. This represents the data to be passed into the plugin:compile function. Currently, model is not used by Information Studio. However future versions will require this capability, so it is recommended that you implement a model to ensure forward compatibility of your plugin.

Function signature:

plugin:model(
) as element(plugin:plugin-model)
config-view

The view to display the transform configuration window in the UI. The strings displayed are defined in the plugin:string function.

Note that the $model parameter is optional, so the plugin needs to either return a view for a user-defined model passed from the UI to the plugin:config-view function or return a view for the generic model defined in the plugin:model function, when no model is passed in from the UI.

Function signature:

plugin:config-view(
   $model as element(plugin:plugin-model)?,
   $lang as xs:string,
   $submit-here as xs:string
) as element(plugin:config-view)
compile

Transforms the document according to the data model.

Function signature:

plugin:compile(
   $model as element()
) as element(plugin:compile)
string

Defines the labels displayed by the transform in the UI.

Function signature:

plugin:string(
   $key as xs:string, 
   $model as element(plugin:plugin-model)?, 
   $lang as xs:string) 
as xs:string?

T ransform Interaction with Information Studio

The interaction between Information Studio and a transform is as follows:

  1. When the user creates a flow, a new flow document is created in the App-Services database.
  2. When the user selects a transform for the flow in the UI, Information Studio renders the transform's view defined in the plugin:config-view function as an HTML iframe in the UI.
  3. The iframe describes the elements to be defined in the transform's data model. When the user finishes filling in the iframe fields and clicks the Done button, the plugin:compile function in the transform updates the flow stored in the App-Services database with the completed data model.
  4. Information Studio displays the completed transform step in the UI using the labels in the plugin:string function.
  5. From this point, each document ingested by the flow is modified against the transform's data model.

    Transform steps implemented using XSLT do not work on binary documents. Binary documents pass through XSLT steps unchanged. Additionally, you cannot use the <xsl:result-document> XSLT instruction in an Information Studio transformation; if you use it, any result documents are not propagated to your content database.

An Example Transform

This section walks through the implementation of the Schema Validation transform described in Extracting Metadata from Binary Content With the Filter Documents Transform that allows users to set the level at which the XML of loaded documents are to be validated against the schema.

The code for the Schema Validation transform is stored in the following files under the marklogic home directory:

/Assets/plugins/marklogic/appservices/transform-validate-with-xml-schema/\
manifest.xml
/Assets/plugins/marklogic/appservices/transform-validate-with-xml-schema/\
lib/transform-validate-with-xml-schema.xqy
  1. The manifest.xml file in the transform-validate-with-xml-schema directory is as follows:
    <?xml version="1.0" encoding="UTF-8"?>
    <plugin xmlns="http://marklogic.com/extension/plugin">
      <name>Schema Validation</name> 
      <id>transform-validate-with-xml-schema</id> 
      <version>0.1</version> 
      <provider-name>MarkLogic</provider-name> 
      <description>
        This Transform Plugin validates documents against existing schemas
      </description>
      <module>
        <ns>http://marklogic.com/extension/plugin/transform</ns> 
        <ns-prefix>transform</ns-prefix>
        <path>lib/transform-validate-with-xml-schema.xqy</path>
      </module>
      <capabilities>
        <capability name=
          "http://marklogic.com/appservices/infostudio/transformer/model">
          <ns>http://marklogic.com/extension/plugin/transform</ns> 
          <local-name>model</local-name>
        </capability>
        <capability name=
          "http://marklogic.com/appservices/infostudio/transformer/config-view">
          <ns>http://marklogic.com/extension/plugin/transform</ns>
          <local-name>view</local-name>
        </capability>
        <capability name=
          "http://marklogic.com/appservices/infostudio/transformer/compile">
          <ns>http://marklogic.com/extension/plugin/transform</ns>
          <local-name>compile-step</local-name>
        </capability>
        <capability name="http://marklogic.com/appservices/string">
          <ns>http://marklogic.com/extension/plugin/transform</ns>
          <local-name>string</local-name>
        </capability>
      </capabilities>
    </plugin>
  2. The transform-validate-with-xml-schema.xqy file in the transform-validate-with-xml-schema/lib/ directory is as follows:
    xquery version "1.0-ml";
    (: Copyright 2002-2012 MarkLogic Corporation.  All Rights Reserved. :)
    module namespace transform =
      "http://marklogic.com/extension/plugin/transform";
    declare namespace lbl=
      "http://marklogic.com/xqutils/labels";
    declare namespace info =
      "http://marklogic.com/appservices/infostudio";
    import module namespace plugin =
      "http://marklogic.com/extension/plugin"
        at "/MarkLogic/plugin/plugin.xqy";
    import module namespace infodev=
      "http://marklogic.com/appservices/infostudio/dev"
        at "/MarkLogic/appservices/infostudio/infodev.xqy";
    import modul namespace pipe =
      "http://marklogic.com/appservices/pipeline"
        at "/MarkLogic/appservices/infostudio/pipe.xqy";
    declare namespace ml="http://marklogic.com/appservices/mlogic";
    declare namespace xproc="http://www.w3.org/ns/xproc";
    declare namespace html="http://www.w3.org/1999/xhtml";
    declare namespace xsl = "http://www.w3.org/1999/XSL/Transform";
    declare default function namespace
      "http://www.w3.org/2005/xpath-functions";
    (:~ Implement the data model to be used by config-view in the UI. 
        This represents the data to be passed to invoke. :)
    declare function transform:model() 
    as element(plugin:plugin-model)
    {
        <plugin:plugin-model>
            <plugin:data>
                <name/>
                <mode>strict</mode>
            </plugin:data>
        </plugin:plugin-model>
    };
    (:~ Implement the view function to display the transform popup
        configuration window in the UI. The data model defined in the
        transform:model-validate-with-xml-schema function is used to
        display the default setting of ‘strict'. The strings displayed are
        defined in the transform:string-validate-with-xml-schema function.
        The "Done" button comes from the Application Services mlogic tag
        library. :)
    declare function transform:view(
        $model as element(plugin:plugin-model)?,
        $lang as xs:string,
        $submit-here as xs:string)
    as element(plugin:config-view)
    {
      let $m := ($model, transform:model())[1]
      let $is-strict := $m/plugin:data/mode eq "strict"
      return
        <config-view xmlns="http://marklogic.com/extension/plugin">
          <html xmlns="http://www.w3.org/1999/xhtml">
            <link href="/infostudio/static/css/transform-plugin.css"
                  type="text/css" rel="stylesheet"/>
            <body>
              <h3>Schema Validation Transform</h3>
              <form action="{$submit-here}" method="post">
                <div class="name">
                  <label for="name">
                    Transform name: <input name="name" id="name"
                                     value="{$m/plugin:data/*:name}"/>
                  </label>
                </div>
                <hr/>
                <p>Validation mode:
                  <select name="mode">
                    <option>{if ($is-strict) 
                             then attribute selected {"selected"} 
                             else ()}strict</option>
                    <option>{if ($is-strict)
                             then ()
                             else attribute selected {"selected"}}lax
                    </option>
                  </select>
                </p>
                <div id="submit">
                  <ml:submit label="Done"/>
                </div>
                <div class="tips guide">
                  <hr/>
                  <p>
                    <label>Validation mode:</label><br/>
                    Validation mode specifies the initial validation mode:
                  </p>
                  <dl>
                  <dt><em>strict</em></dt>
                  <dd>
                    In strict mode, there must be a schema for the document
                    element and the entire document must be valid.
                  </dd>
                  <dt><em>lax</em></dt>
                  <dd>
                    In lax mode, the server will validate elements if it
                    can find schema information about them, but will
                    otherwise silently accept them.
                  </dd>
                  </dl>
                  <p>
                    <label>Note:</label><br/>
                    Validation is performed using schemas from the
                    'Schemas' database.
                  </p>
                </div>
              </form>
            </body>
          </html>
        </config-view>
    };
    (:~ Implement a compile function to validate the document against the
        schema using the validation level specified in the data model. :)
    declare function transform:compile-step(
        $model as element()
    ) as element(plugin:compile)
    {
        let $xproc := <xproc:validate-with-xml-schema validation=
                         "{$model/plugin:data/mode}"/>
        return
           <plugin:compile>
             <plugin:xslt>{pipe:compile-step($xproc)}</plugin:xslt>
           </plugin:compile>
    };
    (:~ Implement a function to define all of the labels displayed by 
        the transform. This plugin uses the labels defined in the 
        Application Services label library. :)
    declare function transform:string(
        $key as xs:string,
        $model as element(plugin:plugin-model)?,
        $lang as xs:string)
    as xs:string?
    {
        let $labels :=
        <lbl:labels xmlns:lbl="http://marklogic.com/xqutils/labels">
            <lbl:label key="name">
                <lbl:value xml:lang="en">Schema Validation</lbl:value>
            </lbl:label>
            <lbl:label key="description">
                <lbl:value xml:lang="en">{ 
                    if($model)
                    then $model/plugin:data/*:name/string()
                    else "Validate a document against existing schemas." }
                </lbl:value>
            </lbl:label>
            <lbl:label key="validation">
                <lbl:value xml:lang="en">Validation Type</lbl:value>
            </lbl:label>
            <lbl:label key="validation-strict">
                <lbl:value xml:lang="en">Strict</lbl:value>
            </lbl:label>
            <lbl:label key="validation-lax">
                <lbl:value xml:lang="en">Lax</lbl:value>
            </lbl:label>
        </lbl:labels>
        return $labels/lbl:label[@key eq $key]
                /lbl:value[@xml:lang eq $lang]/string()
    };

To initialize the transform-validate-with-xml-schema transform, enter the following query:

  1. Open Query Console and enter the following query:
    xquery version "1.0-ml"; 
    import module namespace plugin =
      "http://marklogic.com/extension/plugin" 
        at "/MarkLogic/plugin/plugin.xqy";
    plugin:flush-scope("marklogic.appservices.transform-validate-with-xml-schema"), 
    plugin:install-from-filesystem("marklogic.appservices"), 
    plugin:initialize-scope("marklogic.appservices.transform-validate-with-xml-schema")
  2. Restart MarkLogic Server

« Previous chapter
Powered by MarkLogic Server 7.0-4.1 and rundmc | Terms of Use | Privacy Policy