[Rt-commit] rt branch, 5.0/help-basics, updated. rt-5.0.1-459-g0d951322d1

Steve Burr steve at bestpractical.com
Fri Jun 18 16:31:15 EDT 2021


The branch, 5.0/help-basics has been updated
       via  0d951322d1a68a46c777460506a0585faed904a8 (commit)
       via  489141b5b284c9d78002b15e85a46a042ddaa440 (commit)
       via  fbf9edfb291759acc7b59b03a81696d56c17487b (commit)
       via  549a25a091133999ebd72261a283358ac9334152 (commit)
       via  873f5b34f867d235e17e4e1cc2ef13c3304401d3 (commit)
       via  7a983db4572f6cc18e96b7019b6f2b64f911bada (commit)
       via  5e2b9517a3ce3b356a2ac4aedd8639fad0f78cd5 (commit)
      from  0aa629e2c2defe55570a1e98a242fb25e71a79ea (commit)

Summary of changes:
 devel/docs/help-system.pod    | 75 +++++++++++++++++++--------------
 lib/RT/Interface/Web.pm       | 25 ++++++++++-
 share/html/Elements/PopupHelp |  4 +-
 share/static/js/popup-help.js | 96 ++++++++++++++++++++++++++++---------------
 4 files changed, 135 insertions(+), 65 deletions(-)

- Log -----------------------------------------------------------------
commit 5e2b9517a3ce3b356a2ac4aedd8639fad0f78cd5
Author: Steven Burr <steve at bestpractical.com>
Date:   Fri Jun 18 13:41:15 2021 -0400

    Allow multiple rules as individual args or array

diff --git a/share/static/js/popup-help.js b/share/static/js/popup-help.js
index 2d35130110..adfe1bccd5 100644
--- a/share/static/js/popup-help.js
+++ b/share/static/js/popup-help.js
@@ -5,7 +5,7 @@ var pagePopupHelpItems = [
 
 // add one or more items to the list of help entries to process for the page
 function addPopupHelpItems() {
-	const args = [].slice.call(arguments)
+	const args = [].slice.call(arguments).reduce(function(acc,val) { return acc.concat(val) }, [] )
 	pagePopupHelpItems = pagePopupHelpItems || []
 	pagePopupHelpItems = pagePopupHelpItems.concat(args)
 }

commit 7a983db4572f6cc18e96b7019b6f2b64f911bada
Author: Steven Burr <steve at bestpractical.com>
Date:   Fri Jun 18 14:01:13 2021 -0400

    Refactor popupHelpAjax function for reuse

diff --git a/share/static/js/popup-help.js b/share/static/js/popup-help.js
index adfe1bccd5..7f929511a6 100644
--- a/share/static/js/popup-help.js
+++ b/share/static/js/popup-help.js
@@ -98,8 +98,11 @@ function applyPopupHelpAction( entry, $els ) {
 	}
 }
 
-// render all the help icons and popover-ify them
-function renderPopupHelpItems( list ) {
+// Dynamically load the help topic corresponding to a DOM element using AJAX
+// Should be called with the DOM element as the 'this' context of the function,
+// making it directly compatible with the 'content' property of the popper.js
+// popover() method, which is its primary purpose
+const popupHelpAjax = function() {
 	const isDefined = function(x) { return typeof x !== "undefined" }
 	const buildUrl = function(key) { return RT.Config.WebHomePath + "/Helpers/HelpTopic?key=" + encodeURIComponent(key) }
 	const boolVal = function(str) {
@@ -111,9 +114,38 @@ function renderPopupHelpItems( list ) {
 		}
 	}
 
+	const $el = jQuery(this)
+	const title = $el.data("help") || $el.data("title") || $el.data("originalTitle")
+	var content = $el.data("content")
+	if (content) {
+		return content
+	} else {
+		const isAsync = isDefined($el.data("async")) ? boolVal($el.data("async")) : true
+		if (isAsync) {
+			const tmpId = "tmp-id-" + jQuery.now()
+			jQuery.ajax({
+				url: buildUrl(title), dataType: "html",
+				dataType: "html",
+				success: function(response, statusText, xhr) {
+					jQuery("#" + tmpId).html(xhr.responseText)
+				},
+				error: function(e) {
+					jQuery("#" + tmpId).html("<div class='text-danger'>Error loading help for '" + title + "': " + e)
+				}
+			})
+			return "<div id='" + tmpId + "'>Loading...</div>"
+		} else {
+			return "<div class='text-danger'>No help content available for '" + title + "'.</div>"
+		}
+	}
+}
+
+// render all the help icons and popover-ify them
+function renderPopupHelpItems( list ) {
 	list = list || pagePopupHelpItems
 	if (list && Array.isArray(list) && list.length) {
 		list.forEach(function(entry) {
+			console.log("processing entry:", entry)
 			const $els = applySelectorQueryOrFunc(entry.selector)
 			if ( $els ) {
 				applyPopupHelpAction( entry, $els )
@@ -122,32 +154,7 @@ function renderPopupHelpItems( list ) {
         jQuery('[data-toggle="popover"]').popover({
 			trigger: 'focus',
 			html: true,
-			content: function() {
-				const $el = jQuery(this)
-				const title = $el.data("help") || $el.data("title") || $el.data("originalTitle")
-				var content = $el.data("content")
-				if (content) {
-					return content
-				} else {
-					const isAsync = isDefined($el.data("async")) ? boolVal($el.data("async")) : true
-					if (isAsync) {
-						const tmpId = "tmp-id-" + jQuery.now()
-						jQuery.ajax({
-							url: buildUrl(title), dataType: "html",
-							dataType: "html",
-							success: function(response, statusText, xhr) {
-								jQuery("#" + tmpId).html(xhr.responseText)
-							},
-							error: function(e) {
-								jQuery("#" + tmpId).html("<div class='text-danger'>Error loading help for '" + title + "': " + e)
-							}
-						})
-						return "<div id='" + tmpId + "'>Loading...</div>"
-					} else {
-						return "<div class='text-danger'>No help content available for '" + title + "'.</div>"
-					}
-				}
-			}
+			content: popupHelpAjax
 		})
 	}
 }

commit 873f5b34f867d235e17e4e1cc2ef13c3304401d3
Author: Steven Burr <steve at bestpractical.com>
Date:   Fri Jun 18 14:30:58 2021 -0400

    Ignore disabled help articles

diff --git a/lib/RT/Interface/Web.pm b/lib/RT/Interface/Web.pm
index 4501914c08..7cb9f515d9 100644
--- a/lib/RT/Interface/Web.pm
+++ b/lib/RT/Interface/Web.pm
@@ -5238,7 +5238,7 @@ sub GetHelpArticleContent {
 
     # find the article of the given class
     my $Article = RT::Article->new( RT->SystemUser );
-    my ($ret, $msg) = $Article->LoadByCols( Name => $article_name, Class => $class_id );
+    my ($ret, $msg) = $Article->LoadByCols( Name => $article_name, Class => $class_id, Disabled => 0 );
     if ( $Article and $Article->Id ) {
         RT::Logger->debug("Found help article id: " . $Article->Id);
         my $class = $Article->ClassObj;

commit 549a25a091133999ebd72261a283358ac9334152
Author: Steven Burr <steve at bestpractical.com>
Date:   Fri Jun 18 16:05:52 2021 -0400

    Use Article "Display Text" Custom Field for popup dialog title

diff --git a/devel/docs/help-system.pod b/devel/docs/help-system.pod
index 3837504f67..6e574088ba 100644
--- a/devel/docs/help-system.pod
+++ b/devel/docs/help-system.pod
@@ -64,7 +64,7 @@ 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" } ] )
+        addPopupHelpItems( { "selector": "button#save-form", "title": "My Topic" } )
     })
     </script>
 
@@ -144,8 +144,14 @@ attributes to any HTML elements.
 =over
 
 =item * C<data-help>
-Required. The name of the help topic. Used as the title of the popup dialog. If C<data-content>
+Required. The name of the help topic. If C<data-content>
 is omitted, content will come from an Article with this Name.
+Used as the title of the popup dialog if C<data-title> is not supplied or if in
+asynchronous mode. See L</Async>.
+
+=item * C<data-title>
+Optional. The title to use for the popup dialog box. If omitted, C<data-help> will
+be used.
 
 =item * C<data-content>
 Optional. The help content. If omitted, asynchronous mode will be used to dynamically retrieve
@@ -316,39 +322,39 @@ Any additional arguments that should be passed to the C<action> function.
 
 Add a help topic named C<"My Topic"> to the DOM element with an id of C<"ticket-id">
 
-    addPopupHelpItems([
+    addPopupHelpItems(
         {
             selector: "#ticket-id",
             title: "My Topic"
         }
-    ])
+    )
 
 Add a help topic named C<"Phone"> and custom HTML content to the DOM element with an id of C<"phone-nbr">
 
-    addPopupHelpItems([
+    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([
+    addPopupHelpItems(
         { selector: "#ticket-status", title: "Status Topic" },
         { selector: "#ticket-queue",  title: "Queue Topic"  }
-    ])
+    )
 
 Add a help topic named C<"A Note on Submitting Forms"> to every C<E<lt>buttonE<gt>> element
 of type C<submit>.
 
-    addPopupHelpItems([{ selector: "button[type='submit']", title: "A Note on Submitting Forms" }])
+    addPopupHelpItems( { selector: "button[type='submit']", title: "A Note on Submitting Forms" } )
 
 Find every C<E<lt>divE<gt>> element with a C<"heading"> class, and add a help topic named 
 C<"One"> to the first one, C<"Two"> to the second one, and C<"Three"> to the third one.
 
-    addPopupHelpItems([{ selector: "div.heading", title: [ "One", "Two", "Three" ]}])
+    addPopupHelpItems( { selector: "div.heading", title: [ "One", "Two", "Three" ]} )
 
 Use a custom C<selector> function to match divs that have ids starting with C<"ACME-"> but only when 
 not working locally in developer mode. Determine the article title from the matching ids by stripping
@@ -366,23 +372,23 @@ off the C<"ACME-"> portion
         return $el.id.replace("ACME-", "")
     }
 
-    addPopupHelpItems([
+    addPopupHelpItems(
         {
             selector: acmeDivs,
             title:    makeTitle
         }
-    ])
+    )
 
 Prepend help topics to all form radio buttons
 
-    addPopupHelpItems([
+    addPopupHelpItems(
         {
             selector: "form input[type='radio']",
             topic:    "Radio Button Help",
             content:  "You can only select one at a time",
             action:   "prepend"
         }
-    ])
+    )
 
 Provide help for every field in each section on a ticket display page, but place each
 help icon in a line at the top of its respective section. Use asynchronous mode for
@@ -394,12 +400,12 @@ help content, using the field text as the help topic.
         $a.append( buildPopupHelpHtml( fieldName ) )
     }
 
-    addPopupHelpItems([
+    addPopupHelpItems(
         {
             selector: ".titlebox .card-body .form-row .label",
             action:   sectionInsert
         }
-    ])
+    )
 
 =head2 Programmatic API
 
diff --git a/lib/RT/Interface/Web.pm b/lib/RT/Interface/Web.pm
index 7cb9f515d9..9205f8c83d 100644
--- a/lib/RT/Interface/Web.pm
+++ b/lib/RT/Interface/Web.pm
@@ -5225,6 +5225,29 @@ sub GetSystemHelpClass {
     return;
 }
 
+=head3 GetHelpArticleTitle class_id, article_name
+
+Returns the value of the C<"Display Name"> Custom Field of an Article of the given Class.
+Often, the class_id will come from GetSystemHelpClass, but it does not have to.
+
+=cut
+
+sub GetHelpArticleTitle {
+    my $class_id = shift || return '';      # required
+    my $article_name = shift || return '';  # required
+
+    # find the article of the given class
+    my $Article = RT::Article->new( RT->SystemUser );
+    my ($ret, $msg) = $Article->LoadByCols( Name => $article_name, Class => $class_id, Disabled => 0 );
+    if ( $Article and $Article->Id ) {
+        return $Article->FirstCustomFieldValue('Display Name') || '';
+    }
+
+    # no match was found
+    RT::Logger->debug("No help article found for '$article_name'");
+    return '';
+}
+
 =head3 GetHelpArticleContent class_id, article_name
 
 Returns the raw, unscrubbed and unescaped Content of an Article of the given Class.
diff --git a/share/html/Elements/PopupHelp b/share/html/Elements/PopupHelp
index 7eae04d716..0f801e42bb 100644
--- a/share/html/Elements/PopupHelp
+++ b/share/html/Elements/PopupHelp
@@ -51,16 +51,18 @@ $key => ''
 my $has_help;
 my $help_class;
 my $help_content;
+my $help_title;
 if ($key) {
     my $lh = $session{'CurrentUser'}->LanguageHandle;
     my @locs = ( $lh->language_tag(), $lh->fallback_languages() );
     my $help_class = GetSystemHelpClass( \@locs );
     if ($help_class && $help_class->Id) {
+        $help_title = GetHelpArticleTitle( $help_class->id, $key ) || $key;
         $help_content = GetHelpArticleContent( $help_class->Id, $key );
     }
     $has_help = $help_content;
 }
 </%init>
 % if (RT->Config->Get('ShowInlineHelp', $session{CurrentUser}) && $has_help) {
-<span data-help="<% $key %>" data-content="<% $help_content %>" data-action="replace" style="display: none;"/>
+<span data-help="<% $help_title %>" data-content="<% $help_content %>" data-action="replace" style="display: none;"/>
 % }
diff --git a/share/static/js/popup-help.js b/share/static/js/popup-help.js
index 7f929511a6..0212c43e0b 100644
--- a/share/static/js/popup-help.js
+++ b/share/static/js/popup-help.js
@@ -63,7 +63,7 @@ function helpify($els, item={}, options={}) {
 	$els.each(function(index) {
 		const $el = jQuery($els.get(index))
 		const action = $el.data("action") || item.action || options.action
-		const title = $el.data("help") || $el.data("title") || item.title
+		const title = $el.data("title") || item.title || $el.data("help")
 		const content = $el.data("content") || item.content
 		switch(action) {
 			case "prepend":
@@ -115,7 +115,7 @@ const popupHelpAjax = function() {
 	}
 
 	const $el = jQuery(this)
-	const title = $el.data("help") || $el.data("title") || $el.data("originalTitle")
+	const key = $el.data("help") || $el.data("title") || $el.data("originalTitle")
 	var content = $el.data("content")
 	if (content) {
 		return content
@@ -124,18 +124,18 @@ const popupHelpAjax = function() {
 		if (isAsync) {
 			const tmpId = "tmp-id-" + jQuery.now()
 			jQuery.ajax({
-				url: buildUrl(title), dataType: "html",
+				url: buildUrl(key), dataType: "html",
 				dataType: "html",
 				success: function(response, statusText, xhr) {
 					jQuery("#" + tmpId).html(xhr.responseText)
 				},
 				error: function(e) {
-					jQuery("#" + tmpId).html("<div class='text-danger'>Error loading help for '" + title + "': " + e)
+					jQuery("#" + tmpId).html("<div class='text-danger'>Error loading help for '" + key + "': " + e)
 				}
 			})
 			return "<div id='" + tmpId + "'>Loading...</div>"
 		} else {
-			return "<div class='text-danger'>No help content available for '" + title + "'.</div>"
+			return "<div class='text-danger'>No help content available for '" + key + "'.</div>"
 		}
 	}
 }

commit fbf9edfb291759acc7b59b03a81696d56c17487b
Author: Steven Burr <steve at bestpractical.com>
Date:   Fri Jun 18 16:15:47 2021 -0400

    Update docs for action function signature

diff --git a/devel/docs/help-system.pod b/devel/docs/help-system.pod
index 6e574088ba..0570d26fe6 100644
--- a/devel/docs/help-system.pod
+++ b/devel/docs/help-system.pod
@@ -251,9 +251,9 @@ An array of help topic names. They will be applied in order corresponding to the
 returned by the C<selector>
 
 =item * I<Function>
-A JavaScript function that will be called for each element matched by the C<selector> that
+A JavaScript function that will be called with the elements matched by the C<selector> that
 should return the help topic for that element. That is, the function signagure is 
-C<function( $el ) { ... }>
+C<function( $els ) { ... }>
 
 =back
 
@@ -368,8 +368,8 @@ off the C<"ACME-"> portion
         }
     }
 
-    var makeTitle = function( $el ) {
-        return $el.id.replace("ACME-", "")
+    var makeTitle = function( el ) {
+        return el.id.replace("ACME-", "")
     }
 
     addPopupHelpItems(
@@ -394,10 +394,13 @@ Provide help for every field in each section on a ticket display page, but place
 help icon in a line at the top of its respective section. Use asynchronous mode for
 help content, using the field text as the help topic.
 
-    var sectionInsert = function( $el, rule, options ) {
-        const $a = $el.closest(".titlebox").find(".titlebox-title.card-header a")
-        const fieldName = $el.text().replace(":", "")
-        $a.append( buildPopupHelpHtml( fieldName ) )
+    var sectionInsert = function( $els, rule, options ) {
+        $els.each(function(i,el) {
+            const $el = jQuery(el)
+            const $a = $el.closest(".titlebox").find(".titlebox-title.card-header a")
+            const fieldName = $el.text().replace(":", "")
+            $a.append( buildPopupHelpHtml( fieldName ) )
+        })
     }
 
     addPopupHelpItems(

commit 489141b5b284c9d78002b15e85a46a042ddaa440
Author: Steven Burr <steve at bestpractical.com>
Date:   Fri Jun 18 16:26:42 2021 -0400

    Add 'before' and 'after' actions

diff --git a/devel/docs/help-system.pod b/devel/docs/help-system.pod
index 0570d26fe6..995edc79ef 100644
--- a/devel/docs/help-system.pod
+++ b/devel/docs/help-system.pod
@@ -170,12 +170,12 @@ with some static help content that includes custom HTML
 
     <button data-help="Save Widget"
             data-content='Saves the <font color="red">Widget</font> to RT'
-            data-action="append">Save</button>
+            data-action="after">Save</button>
 
 Or we could omit the C<data-content> altogether to have RT return the help content from the
 matching C<"List Sprockets"> Article when the user clicks the help icon
 
-    <button data-help="List Sprockets" data-action="append">List</button>
+    <button data-help="List Sprockets" data-action="after">List</button>
 
 =head2 JavaScript
 
@@ -280,7 +280,7 @@ Optional. The action that should be taken with each help icon that results from
 of C<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, C<"replace"> is the default.
+If missing, C<"after"> is the default.
 
 =over
 
@@ -290,15 +290,21 @@ are supported:
 
 =over
 
+=item * I<before>
+The help icon will be prepended to the DOM I<before> the element(s) matched by C<selector>
+
+=item * I<after>
+Default. The help icon will be appended to the DOM I<after> the element(s) matched by C<selector>
+
 =item * I<append>
-The help icon will be appended to the DOM I<after> the element(s) matched by C<selector>
+The help icon will be appended to the end of the DOM element(s) matched by C<selector>
 
 =item * I<prepend>
-The help icon will be prepended to the DOM I<before> the element(s) matched by C<selector>
+The help icon will be prepended to the beginning of the DOM element(s) matched by C<selector>
 
 =item * I<replace>
 The help icon will be inserted into the DOM I<in place of> the element(s) matched by C<selector>.
-This is the default behavior. It is used, for example, by the C</Elements/PopupHelp> Mason component.
+This action is used, for example, by the C</Elements/PopupHelp> Mason component.
 
 =item * I<offset>
 The help icon will be offset from the element(s) matched by C<selector> by the amounts 
diff --git a/share/static/js/popup-help.js b/share/static/js/popup-help.js
index 0212c43e0b..01466d8dca 100644
--- a/share/static/js/popup-help.js
+++ b/share/static/js/popup-help.js
@@ -24,6 +24,8 @@ function getPopupHelpAction( entry={} ) {
 	entry.action = entry.action || "append"
 	if ( typeof(entry.action) === "string" ) {
 		const funcMap = {
+			"before": beforePopupHelp,
+			"after": afterPopupHelp,
 			"append": appendPopupHelp,
 			"prepend": prependPopupHelp,
 			"offset": offsetPopupHelp,
@@ -39,6 +41,16 @@ function getPopupHelpActionArgs( entry={}, $els ) {
 	return entry.actionArgs ? [ $els, entry, entry.actionArgs ] : [ $els, entry ]
 }
 
+function beforePopupHelp( $els, item={}, options={} ) {
+	item.action = options.action = "before"
+	return helpify( $els, item, options )
+}
+
+function afterPopupHelp( $els, item={}, options={} ) {
+	item.action = options.action = "after"
+	return helpify( $els, item, options )
+}
+
 function appendPopupHelp( $els, item={}, options={} ) {
 	item.action = options.action = "append"
 	return helpify( $els, item, options )
@@ -66,8 +78,11 @@ function helpify($els, item={}, options={}) {
 		const title = $el.data("title") || item.title || $el.data("help")
 		const content = $el.data("content") || item.content
 		switch(action) {
+			case "before":
+				$el.before( buildPopupHelpHtml( title, content ) )
+				break
 			case "prepend":
-				$el.parent().prepend( buildPopupHelpHtml( title, content ) )
+				$el.prepend( buildPopupHelpHtml( title, content ) )
 				break
 			case "offset":
 				$el.append( buildPopupHelpHtml( title, content ) ).offset( options )
@@ -75,7 +90,10 @@ function helpify($els, item={}, options={}) {
 			case "replace":
 				$el.replaceWith( buildPopupHelpHtml( title, content ) )
 				break
-			case "append":  // intentionally fallthrough, as 'append' is the default
+			case "append":
+				$el.append( buildPopupHelpHtml( title, content ) )
+				break
+			case "after":  // intentionally fallthrough, as 'after' is the default
 			default:
 				$el.parent().append( buildPopupHelpHtml( title, content ) )
 		}

commit 0d951322d1a68a46c777460506a0585faed904a8
Author: Steven Burr <steve at bestpractical.com>
Date:   Fri Jun 18 16:29:43 2021 -0400

    Gracefully handle unknown actions

diff --git a/share/static/js/popup-help.js b/share/static/js/popup-help.js
index 01466d8dca..c41d8494ab 100644
--- a/share/static/js/popup-help.js
+++ b/share/static/js/popup-help.js
@@ -31,7 +31,12 @@ function getPopupHelpAction( entry={} ) {
 			"offset": offsetPopupHelp,
 			"replace": replacePopupHelp
 		}
-		return funcMap[entry.action]
+		if (funcMap.hasOwnProperty(entry.action)) {
+			return funcMap[entry.action]
+		} else {
+			console.error("Unknown action '" + entry.action + "' using 'after' instead")
+			return funcMap.after
+		}
 	} else if ( typeof(entry.action) === "function" ) {
 		return entry.action
 	}

-----------------------------------------------------------------------


More information about the rt-commit mailing list