NAME
    RT-Extension-InlineHelp - InlineHelp

DESCRIPTION
    This extension supplies the ability to add inline help to RT web pages.

RT VERSION
    Works with RT 6.0

INSTALLATION
    perl Makefile.PL
    make
    make install
        May need root permissions

    make initdb
        Only run this the first time you install this module.

        If you run this twice, you may end up with duplicate data in your
        database.

        If you are upgrading this module, check for upgrading instructions
        in case changes need to be made to your database.

    Edit your /opt/rt6/etc/RT_SiteConfig.pm
        Add this line:

            Plugin('RT::Extension::InlineHelp');

        To show InlineHelp by default:

            Set($ShowInlineHelp, 1);

    Clear your mason cache
            rm -rf /opt/rt6/var/mason_data/obj

    Restart your webserver

OVERVIEW
    This extension adds help icons to various elements on pages throughout
    the application. When the user hovers over the help icon, a popup dialog
    will display useful information related to that element.

    Custom fields and custom roles in RT already have input help that show
    "Entry hints" as tooltips. This extension replaces those elements with
    the version provided by InlineHelp, so the icons are the same size and
    the display is a popup with a title instead of a tooltip. The content
    still comes from the entry hint, so your existing configurations will
    continue to work.

  How it works
    Help content is managed as a collection of articles in
    specially-designated classes. If a class has the "Inline Help" custom
    field set to "yes", then the articles in that class will be used when
    finding help topics. A second custom field called "Locale" identifies
    the language used by articles in that class. In these classes, the
    article names map to parts of the RT interface to determine where to
    show the article content as help.

  Sync vs Async
    There are basically two modes of operation for the InlineHelp:
    synchronous and asynchronous.

    In synchronous mode, all of the help content is either retrieved or
    supplied directly on the server side when the initial page is rendered.
    This means that the help content itself is delivered to the browser.

    In asynchronous mode, only the help topic is supplied when the page is
    rendered. When the user hovers over the help icon, the help content is
    dynamically retrieved from the server and displayed in the popup dialog.
    See "Async" for more details.

    Both modes can be used interchangeably on the same page.

USAGE
    InlineHelp can be used at render time on the server. For example, in a
    Mason template, you might use the PopupHelp template to annotate a form
    field:

        <div class="form-row">
          <div class="label col-3">
            <span>Ticket Id</span>
            <& /Elements/PopupHelp, Title => 'My Topic' &>:
          </div>
          <div class="value col-9">
            <input class="form-control" type="text" name="ticketId" />
          </div>
        </div>

    InlineHelp can also be used at runtime on the client. For example, you
    can add the same help topic to every HTML element matching a certain
    query. The following would associate a help topic to a specific button:

        <script>
        jQuery(document).ready(function() {
            addPopupHelpItems( { "selector": "button#save-form", "title": "My Topic" } )
        })
        </script>

REFERENCE
    There are four primary ways to use the Inline Help:

    *   "Target Selector"

    *   "Mason Templates"

    *   "HTML Attributes"

    *   "JavaScript"

  Target Selector
    With this approach, you don't need to write any Mason/HTML/JS code and
    you can add new help topics by adding or updating articles via the RT
    web UI. Articles in the Inline Help class will have a custom field
    called Target applied to them. To add inline help to some element in RT,
    specify the HTML elements of that element in the Target field, using
    jQuery selector syntax. jQuery is very flexible, so you can use many
    elements in the DOM to target elements including ids, classes, and even
    text. Once you get the selector to match, the content of that article
    will show up in the web UI on that location.

   Examples
    Below are some examples of the Target value you would use to add help
    text to different parts of RT.

    "Search" in the main top menu
        Target: #search

    Ticket display page, Status label
        Target: div.form-row.status div.label

    RT at a glance, Unowned tickets portlet
        Target: .titlebox .titlebox-title span.left
        a[href^="/Search/Results.html"]:contains("newest unowned tickets")

  Mason Templates
    Add a /Elements/PopupHelp component anywhere in a Mason template:

        <& /Elements/PopupHelp, Title => "My Topic" &>

    This will render an empty HTML span element

        <span data-help="My Topic"
              data-content="The article contents"
              data-action="replace"
              style="display: none;"
        ></span>

    which will be picked up and processed on page load by the default
    "helpification" rule when all of the accumulated rules are executed when
    renderPopupHelpItems is called (for example, in the Elements/Footer
    component in the page footer).

    If no valid help article named My Help Topic is found, (see "OVERVIEW")
    or the ShowInlineHelp setting/user-preference is false, the <span> will
    be suppressed altogether.

    Because the help content has already been retrieved and sent to the
    client, it will already be in the DOM and there should be virtually no
    delay when displaying the help popup following a user hover.

   Example
    To add help to a form field, a Mason template might create a help tag
    directly:

        <div class="form-row">
          <div class="label col-3">
            <span>Ticket Id</span>
            <& /Elements/PopupHelp, Title => 'My Topic' &>:
          </div>
          <div class="value col-9">
            <input class="form-control" type="text" name="ticketId" />
          </div>
        </div>

    or might create help tags dynamically based on a Custom Field called
    Category:

        % while (my $ticket = $tickets->Next) {
        %   my $ctgy = $ticket->FirstCustomFieldValue("Category")
        <h1><% $ticket->Subject %></h1><& /Elements/PopupHelp, Title => $ctgy &>
        % }

  HTML Attributes
    Add data-help="My Topic" and (optionally) data-content="The help
    content" attributes to any HTML elements.

    *   data-help Required. The name of the help topic. If data-content is
        omitted, content will come from an article with this Name. See
        "Async".

    *   data-content Optional. The help content. If omitted, asynchronous
        mode will be used to dynamically retrieve the help content. See
        "Async".

    *   data-action Optional. The action to use when adding the help icon to
        the DOM. Defaults to "append". See "Help Selector Rules" section for
        more details.

   Example
    A Mason template might add the data-help attribute to an element along
    with some static help content that includes custom HTML

        <button data-help="Save Widget"
                data-content="Saves the Widget to RT"
                data-action="after">Save</button>

    Or we could omit the data-content altogether to have RT return the help
    content from the matching "List Sprockets" article when the user hovers
    over the help icon

        <button data-help="List Sprockets" data-action="after">List</button>

  JavaScript
    Call addPopupHelpItems to add one or more rules to the list of help
    topics on a page that should be decorated with help icons.

    The addPopupHelpItems function populates the pagePopupHelpItems array
    with a list of declarative rules that define elements in the DOM that
    should have associated help icons. If a rule's selector key matches one
    or more elements, its action key will determine where a help icon should
    be added to the DOM with help content corresponding to the content key
    or from a valid help article with the same name as the title key.

    Any rules thus added will be picked up and processed on page load when
    all of the accumulated rules are executed when renderPopupHelpItems is
    called (for example, in the Elements/Footer component in the page
    footer).

    This includes the default rule

        { selector: "[data-help]" }

    which matches anything with a data-help attribute and therefore powers
    the "HTML Attributes" method.

    This method of using JavaScript allows for tremendous flexibly
    annotating the DOM with help items, even after it has been
    rendered--perhaps by other templates altogether, making it attractive as
    a mechanism for users to annotate aspects of RT--however it has been
    installed for them, including any and all extensions--simply by
    inspecting what is rendered to the browser and writing the appropriate
    rules. Importantly, these rules can usually be added to one place (e.g.
    in a page callback somewhere) so they do not need to overlay virtually
    every template in RT just to add help icons throughout.

   Help Selector Rules
    A help selector rule is a JavaScript object with the following keys:

    *   selector - *String*

        Required. Defines which DOM elements should receive a help icon. Can
        match 0, 1, or many elements. Selectors matching 0 elements have no
        impact on the DOM.

        *   *String* A JQuery selector string that defines the matching DOM
            elements

    *   title - *String*

        Optional. The help topic(s) that should be associated with the
        element(s) matching the selector

        *   *String* The name of the help topic that should be matched
            against the article Name. If the selector matches exactly one
            element, this will be its help topic. If more than one element
            are matched, they will all get this same help topic.

    *   content - *String*

        Optional. The help content to be displayed in the popup when the
        user hovers over the help icon.

        If missing, asynchronous mode is automatically triggered (see
        "Async")

        *   *String* The help content. May contain HTML. Will be applied for
            all elements matched by selector.

    *   action - *String*

        Optional. The action that should be taken with each help icon that
        results from the application of selector. Responsible for actually
        adding the help icons to the DOM. This controls, for example, where
        the icon should be rendered relative to the matching DOM element.

        If missing, "append" is the default.

        *   *String*

            *   *before* The help icon will be prepended to the DOM *before*
                the element(s) matched by selector

            *   *after* Default. The help icon will be appended to the DOM
                *after* the element(s) matched by selector

            *   *append* The help icon will be appended to the end of the
                DOM element(s) matched by selector

            *   *prepend* The help icon will be prepended to the beginning
                of the DOM element(s) matched by selector

            *   *replace* The help icon will be inserted into the DOM *in
                place of* the element(s) matched by selector. This action is
                used, for example, by the /Elements/PopupHelp Mason
                component.

   Examples
    Add a help topic named "My Topic" to the DOM element with an id of
    "ticket-id"

        addPopupHelpItems(
            {
                selector: "#ticket-id",
                title: "My Topic"
            }
        )

    Add a help topic named "Phone" and custom HTML content to the DOM
    element with an id of "phone-nbr"

        addPopupHelpItems(
            {
                selector: "#phone-nbr",
                title: "Phone",
                content: "The customer phone number. This <i>MUST</i> include the country code."
            }
        )

    Add more than one rule at a time

        addPopupHelpItems(
            { selector: "#ticket-status", title: "Status Topic" },
            { selector: "#ticket-queue",  title: "Queue Topic"  }
        )

    Add a help topic named "A Note on Submitting Forms" to every <button>
    element of type submit.

        addPopupHelpItems( { selector: "button[type='submit']", title: "A Note on Submitting Forms" } )

    Prepend help topics to all form radio buttons

        addPopupHelpItems(
            {
                selector: "form input[type='radio']",
                title:    "Radio Button Help",
                content:  "You can only select one at a time",
                action:   "prepend"
            }
        )

  Programmatic API
    The following functions are part of, and used by, InlineHelp. You can
    also call them directly from your code.

   HTML::Mason::Commands::GetInlineHelpClass( locales )
    Given a list of locales, find the best article class that has been
    associated with the "Inline Help" custom field. Locales are searched in
    order. The first Class with an "Inline Help" custom field and matching
    "Locale" custom field will be returned.

   HTML::Mason::Commands::GetInlineHelpArticleTitle( class_id, article_name )
    Returns the value of the Display Name Custom Field of an Article of the
    given Class.

   HTML::Mason::Commands::GetInlineHelpArticleContent( class_id, article_name )
    Returns the raw, unscrubbed and unescaped Content of an Article of the
    given Class.

  Async
    In asynchronous mode, only the help topic is supplied when the page is
    rendered. Only when the user hovers over the help icon is the help
    content dynamically retrieved from the server with a second AJAX request
    to which will attempt to fetch the given help article contents. The
    contents are returned directly as an HTML fragment--that is, they are
    not wrapped in a <html> tag, for example.

    The AJAX call will be a request to /Helpers/HelpTopic?key=MyTopic which
    will return the raw contents of the MyTopic Article, which may contain
    HTML. It will not be sanitized. If no valid MyTopic help article exists
    (see "OVERVIEW"),

        <div class="text-danger">No help was found for 'MyTopic'.</div>

    will be returned instead.

    The /Helpers/HelpTopic component does not consider the ShowInlineHelp
    setting/user-preference. However, if ShowInlineHelp is not set, the help
    icon would generally not have been rendered anyway, so the AJAX call
    would never have been made.

    Asynchronous mode does have the benefit of reducing the number of
    database calls that need to be made to retrieve help article content on
    page request, but the user may experience a slight lag when the help
    icon is hovered over and the AJAX request is being made. This will need
    to be evaluated on a case-by-case basis. On a heavily used RT system,
    the performance of pages with many help topics may benefit from using
    asynchronous mode more generously.

NAMING
    Since InlineHelp uses the help topic as the key to find a corresponding
    article, it helps to have a somewhat predictable naming convention for
    help topics.

  RT objects
    In general, help topics for built-in RT functionality will be prefixed
    by "RT-"

    *   RT-{The Name}

    *   RT-{Context}-{The Name}

    *   RT-{Path/To/Page}-{The Name}

    *   RT-MainMenu-{}-{}-...

    *   RT-PageMenu-{}-{}-...

  User-defined objects
    When you wish to dynamically create help topics based on the name of an
    object that the end users create, the following naming conventions can
    serve as a guide

    *   User-Dashboard-{The Name}

    *   System-Dashboard-{The Name}

    *   CustomRole-{The Name}

    *   SystemRole-{The Name}

    *   CustomField-{The Name}

    *   User-SavedSearch-{The Name}

    *   {Group Name}-SavedSearch-{The Name}

    *   System-SavedSearch-{The Name}

DESIGN CONSIDERATIONS
    Choosing synchronous vs asynchronous mode involves several tradeoffs
    already discussed in "Async".

    In synchronous mode, there are also tradeoffs in choosing whether to
    provide content directly via the data-content attribute or the content
    property of a JavaScript help rule. It is often convenient to provide
    the help directly, especially if it has to be constructed in order to do
    so. However, this makes it much more difficult for end users to edit or
    customize the help content (since it now lives in code instead of an
    article). It also makes it more difficult to support multiple locales.

MISC
  Change Icon Size
    By default the icon size is inherited from its parent, to customize it,
    you can add css rules on Admin Theme page like:

        span.popup-help {
            font-size: larger;
        }

        span.popup-help {
            font-size: smaller;
        }

        span.popup-help {
            font-size: 12px;
        }

INTERNATIONALIZATION
    InlineHelp works with multiple languages by using articles in classes.
    Each class should have a different value for its Locale custom field.
    All of the articles in that class should be in that language.

  Adding a new language
    *   To add a new language, create a new class with the settigns below.
        You can use any name for the class. If you plan to have several
        languages, you'll likely want to have consistent naming for your
        classes.

    *   Set the "Inline Help" custom field to "yes".

    *   Set "Locale" to the language you want.

    *   Add articles to your new Class

AUTHOR
    Best Practical Solutions, LLC <modules@bestpractical.com>

BUGS
    All bugs should be reported via email to
        bug-RT-Extension-InlineHelp@rt.cpan.org
    or via the web at
        http://rt.cpan.org/Public/Dist/Display.html?Name=RT-Extension-InlineHelp
LICENSE AND COPYRIGHT
    This software is Copyright (c) 2025 by Best Practical Solutions, LLC

    This is free software, licensed under:

      The GNU General Public License, Version 2, June 1991