ResolveExternalLinksComponent

Introduction

ResolveExternalLinksComponent is a custom Sandcastle build component that allows you to configure ID-to-URL mappings so that you can specify hyperlinks in your MAML topics and code comments using IDs instead of actual URLs. It's also extensible and supports mappings from third-party sources, such as a proprietary web service or a database. There's even a built-in database provider that resolves IDs to hyperlinks stored in an ODBC, OLE-DB, SQL Server or Oracle database, for example.

Getting Started

As of DocProject 1.10.1 RC this component does not have to be downloaded separately; it will be added automatically to new DocProjects and DocSites. For other tools or command-line use, download the latest release: ResolveExternalLinksComponent 1.0.

Follow the instructions in the Configuration section below to configure the component. For use with other automation tools, refer to their documentation to find out how to apply the example configurations.

Dependencies

The code is based on the Hosted Visual Studio C# Item Template from the Sandcastle Build Component Templates 1.2 release, which means that the latest release of DocProject must be installed to build the source code but it does not have to be installed to use the compiled assembly with Sandcastle on the command-line or in other Sandcastle automation tools.

To build the source code the same dependencies are required as with the Hosted template; they are DocProject, BuilderAssemblerLibrary and various Framework assemblies. Refer to Sandcastle Build Component Templates for more information.

Editor

When the component is used with tools such as DocProject or Sandcastle Help File Builder it can display a graphical user interface (GUI) for editing its underlying XML configuration:

ResolveExternalLinksComponent editor with a DatabaseUrlMappingProvider selected
Figure 1: ResolveExternalLinksComponent editor with a DatabaseUrlMappingProvider selected

General Tab
Provides a user interface (UI) for configuring component-level attributes.

Namespaces Tab
Provides an editable list of XML namespaces that are used by the parser when applying the XPaths defined on the General page.

When using this component in conceptual builds, for example, the MAML namespace must be added so that <link> elements can be resolved. (See Example Configuration below.)

Mappings Tab
Provides a UI for adding, editing and deleting URL mapping providers.

Mapping providers can also provide their own editor to be hosted by this UI. For example, the DatabaseUrlMappingProvider provides an editor that allows you to specify the database Connection, Query and Parameters.

DocProject Features

When the editor is used in DocProject it will also populate the Topic ID drop-down list with all of the APIs that are available. Note that if a reflection.xml file does not exist from a previous help build then the list will remain empty.

Also in DocProject when you click the ellipses button (...) to browse for a file, the initial directory that the dialog uses will be the project's Help\Settings folder.

In the DocProject Properties window there are more properties that can be edited by expanding the component in a build component stack:

ResolveExternalLinksComponent Properties
Figure 2: ResolveExternalLinksComponent Properties

Usage

Once the component has been added to a stack you can then begin to use new attributes for <see> elements in your code comments and new attributes for <link> elements in MAML topics to reference the configured mappings.

Comments: <see xref="id"/> and <seealso xref="id"/>
MAML: <link xref="id"/>

The xref attribute requires a valid ID. If a matching ID is found in the configured mappings then the corresponding href will be used. If a matching ID is not found then the component will issue a warning and replace the reference with text only.

If a matching ID is found but no href is specified, and if no base URL is defined in scope (see Combining with a base URL below), then the component will issue a warning and replace the reference with text only.

Comments: <see vref="url"/> and <seealso vref="url"/>
MAML: <link vref="url"/>

The vref attribute uses a relative URL as an ID. If a matching ID is found in the configured mappings then the corresponding href will be used; otherwise, the ID itself will be used as the value for href.

If a matching ID is found but no href is specified then the ID itself will be used as the value for href.

Combining with a base URL
After an href has been resolved the component will attempt to combine it with the most derived base URL that is in scope.

When defined, the base URL on the <mappings> element will be combined with the href to form a complete URL. But if there's no base then the global base URL (defined on the <component> element) will be combined with the href; otherwise, the href itself will be used as the entire URL.

Examples

The following example uses the new attribute types on <see> elements in the remarks section of XML documentation comments.

<remarks>
  <p>

    See my <see xref="home"/> page.

  </p><p>

    <see xref="contact">Contact me.</see>

  </p><p>

    <see vref="/example.htm">See an example.</see>

  </p>
</remarks>

The following example uses the new attribute types on <link> elements in the content of a section area in a MAML topic.

<section>
  <title>Example links</title>
  <content>
    <para>

      See my <link xref="home"/> page.

    </para><para>

      <link xref="contact">Contact me.</link>

    </para><para>

      <link vref="/example.htm">See an example.</link>

    </para>
  </content>
</section>

With the above usage and the configuration from the XmlUrlMappingProvider example below, the following links will be created:

See my <a href="http://davesexton.com/" target="_parent">home</a> page.
<a href="http://davesexton.com/Contact/Default.aspx" target="_parent">Contact me.</a>
<a href="http://microsoft.com/example.htm" target="_blank">See an example.</a>

Configuration

To use the component you must add it to a build component stack. The process may differ depending upon the presentation style that you're using, but normally the component must be inserted after the CopyFromIndexComponent that imports XML comments and before the main TransformComponent, which usually follows immediately after.

Instructions for adding the component depend on the tool that you're using to automate Sandcastle:

Example Configuration

The following is an example XML configuration element for this component as it might appear in a sandcastle.config file. It also illustrates a few different types of mapping providers:

<component type="DaveSexton.Sandcastle.ResolveExternalLinksComponent"
           assembly="C:\...\DaveSexton.Sandcastle.dll"
           target="_top" base="http://microsoft.com" enabled="true">

  <!-- XmlUrlMappingProvider (default) -->
  <mappings>
    <url id="home" href="http://davesexton.com" />
    <url id="contact" href="http://davesexton.com/Contact" />
  </mappings>

  <!-- FileUrlMappingProvider -->
  <mappings file="..\..\Help\Settings\links.xml">
    Get the <url/> elements from a settings file.
    This inner XML is completely ignored.
  </mappings>

  <!-- A custom UrlMappingProvider -->
  <mappings type="SomeNamespace.CustomUrlMappingProvider" assembly="C:\...\MyAssembly.dll">
    <custom>This is configuration for the custom provider.  
            Any inner XML will do, but a custom tag is being used as an example.</custom>
  </mappings>
</component>

The following is an example XML configuration element for this component as it might appear in a conceptual.config file.

Note: The mappings elements from the example above could apply to this example as well.
<component type="DaveSexton.Sandcastle.ResolveExternalLinksComponent"
           assembly="%DocProjectPath%\bin\DaveSexton.Sandcastle.dll"
           replaceSeeAlsoLinks="False" seeXPath="//ddue:link[@xref|@vref]"
           target="_top" base="http://microsoft.com" enabled="true">
  
  <seeLinkXml>
    <externalLink xmlns="http://ddue.schemas.microsoft.com/authoring/2003/5">
      <linkText>{2}</linkText>
      <linkUri>{0}</linkUri>
    </externalLink>
  </seeLinkXml>
  
  <context prefix="ddue" name="http://ddue.schemas.microsoft.com/authoring/2003/5" />
  
  <!-- Reference an external file that contains URL mappings -->
  <mappings target="_blank" file="..\..\Help\Settings\links.xml" />

</component>

Schema

This section describes the schema that is supported by the component, regardless of the mapping providers being used.

<component> Element
The <component> element supports three optional attributes and two attributes that are required by Sandcastle: <mappings> Element
The <mappings> element supports six optional attributes:

Mapping Providers

A mapping provider parses the inner XML of a <mappings> element and returns a set of ID-to-URL mappings. The built-in providers allow you to easily configure mappings as a collection of <url/> elements or rows in the result set of a database query. You can also create a custom mappings provider by deriving a class from DaveSexton.Sandcastle.UrlMappingProvider.

XmlUrlMappingProvider

This provider parses the child <url/> elements within a <mappings/> element. It is the default provider used when no provider attribute is specified on the <mappings/> element.
Example Configuration
The following is an example XML configuration element for this component using only XmlUrlMappingProvider:

Note: This example configuration only applies to reference build component stacks. Additional markup is required to use it in conceptual stacks (see the Example Configuration above.)
<component type="DaveSexton.Sandcastle.ResolveExternalLinksComponent"
           assembly="C:\...\DaveSexton.Sandcastle.dll"
           base="http://microsoft.com" target="_parent">

  <mappings base="http://davesexton.com">
    <url id="home" />
    <url id="contact" href="/Contact/Default.aspx" />
  </mappings>

  <mappings target="_blank">
    <url id="example.htm" />
  </mappings>

  <mappings target="_blank" base="http://davesexton.com"
            topic="M:SomeNamespace.SomeType.SomeMethod(System.String)">

    <!-- Override home and contact for a specific topic only.
         Note that no information is inherited from the other mappings. -->

    <url id="home" href="/subsite/Default.aspx" />
    <url id="contact" href="/Contact/Default.aspx" /> <!-- href is the same but the target will be different -->
  </mappings>

</component>
Schema
This section describes the schema that is supported by XmlUrlMappingProvider.

<url> Element
The <url> element supports two attributes:

DatabaseUrlMappingProvider

This provider allows you to define ID-to-URL mappings in a database instead of the inner XML of a mappings element.

In scenarios where you already have URLs stored in a database or when you have a large number of URLs that must be shared between multiple tools, whether related to documentation or not, this provider can be used to author documentation without having to hard-code any URLs, only unique identifiers.

A custom editor that allows you to configure the provider is automatically embedded into the component's editor when the provider is used. With it you can easily specify the data provider, connection string, query, parameters and other settings all in one place.
Example T-SQL Script
The following T-SQL example creates a table in SQL Server that stores ID-to-URL mappings, grouped by topic IDs (an empty value for TopicID indicates no topic association).

This is just an example though. Any schema is supported as long as a query is configured that provides two columns: ID and HREF. The column names are not important, however, since the provider allows you to configure the result set columns that map to the ID and HREF values.

CREATE TABLE [dbo].[UrlMappings] (
  [TopicID] [nvarchar](150) NOT NULL,
  [ID] [nvarchar](300) NOT NULL,
  [HREF] [nvarchar](4000) NULL,
CONSTRAINT [PK_UrlMappings] PRIMARY KEY CLUSTERED
(
  [TopicID] ASC,
  [ID] ASC
))
Example Configuration
The following is an example XML configuration element for this component using only DatabaseUrlMappingProvider:

Note: This example configuration only applies to reference build component stacks. Additional markup is required to use it in conceptual stacks (see the Example Configuration above.)
<component type="DaveSexton.Sandcastle.ResolveExternalLinksComponent"
           assembly="C:\...\DaveSexton.Sandcastle.dll">
  <mappings type="DaveSexton.Sandcastle.DatabaseUrlMappingProvider"
            topic="M:SomeNamespace.SomeType.DocumentedMethod(System.String)">
    <connection provider="System.Data.SqlClient">
      Data Source=DEV1\SQLEXPRESS;Initial Catalog=Docs;Integrated Security=True
    </connection>
    <query>
      SELECT ID, HREF FROM UrlMappings WHERE TopicID = @TopicID;
    </query>
    <parameters>
      <parameter name="@TopicID" source="TopicID" />
    </parameters>
  </mappings>
</component>

Parameters can have hard-coded values or they can be mapped to a specific source. In the example above the value of the <mappings> element's topic attribute will be used as the value for the @TopicID parameter.
Schema
This section describes the schema that is supported by DatabaseUrlMappingProvider.

<connection> Element
The inner text of the <connection> element specifies the connection string that will be used to connect to the database.

One optional attribute is supported: <query> Element
The inner text of the <query> element specifies the command text that will be executed. Parameterized textual queries and stored procedures are supported, but only the first result set is used.

Four optional attributes are supported: <parameters> and <parameter> Elements
The <parameters> element contains zero or more <parameter> elements and supports no attributes.

The <parameter> element supports three attributes: