Page MenuHomePhabricator

Add "namespace" support for combobox and tokens input
Closed, ResolvedPublic

Description

This patch adds support for a "namespace" attribute for the combobox and tokens input.

Rationale:. The #forminput parser function already has this feature, but it works only in page view mode. This feature ensure that users don't have to manually add a specific namespace to their input in the form edit mode.

E.g. namespace="Tag" will ensure that "Tag:" will be prefixed to (each) user input which doesn't already include a namespace already (somewhat smart behaviour).

Sadly, I couldn't find a place to put this more generically, so I had to fall back to including this into the forminputs itself.

It would also be possible to add this to text/text with autocomplete, if there's need for it.

From a72dc33723c0ab77942e85f71d01a32a2bf39957 Mon Sep 17 00:00:00 2001
From: Fannon <heimlersimon@gmail.com>
Date: Thu, 12 May 2016 12:01:11 +0200
Subject: [PATCH] first working version, support for namespace attribute in
 tokens and combobox forminput

---
 includes/forminputs/SF_ComboBoxInput.php |  3 +++
 includes/forminputs/SF_TokensInput.php   |  3 +++
 libs/ext.sf.select2.combobox.js          | 10 +++++++++-
 libs/ext.sf.select2.tokens.js            | 10 +++++++++-
 4 files changed, 24 insertions(+), 2 deletions(-)

diff --git a/includes/forminputs/SF_ComboBoxInput.php b/includes/forminputs/SF_ComboBoxInput.php
index 799766f..9a1e8d5 100644
--- a/includes/forminputs/SF_ComboBoxInput.php
+++ b/includes/forminputs/SF_ComboBoxInput.php
@@ -106,6 +106,9 @@ class SFComboBoxInput extends SFFormInput {
 		if ( !is_null( $remoteDataType ) ) {
 			$inputAttrs['autocompletedatatype'] = $remoteDataType;
 		}
+		if ( array_key_exists( 'namespace', $other_args ) ) {
+			$inputAttrs['data-namespace'] = $other_args['namespace'];
+		}
 
 		$inputText = Html::rawElement( 'input', $inputAttrs);
 
diff --git a/includes/forminputs/SF_TokensInput.php b/includes/forminputs/SF_TokensInput.php
index a5afbf8..2d2792a 100644
--- a/includes/forminputs/SF_TokensInput.php
+++ b/includes/forminputs/SF_TokensInput.php
@@ -150,6 +150,9 @@ class SFTokensInput extends SFFormInput {
 		if ( array_key_exists( 'max values', $other_args ) ) {
 			$inputAttrs['maxvalues'] = $other_args['max values'];
 		}
+		if ( array_key_exists( 'namespace', $other_args ) ) {
+			$inputAttrs['data-namespace'] = $other_args['namespace'];
+		}
 		$text = "\n\t" . Html::input( $input_name, $cur_value, 'text', $inputAttrs ) . "\n";
 
 		$spanClass = 'inputSpan';
diff --git a/libs/ext.sf.select2.combobox.js b/libs/ext.sf.select2.combobox.js
index f300440..1320c0c 100644
--- a/libs/ext.sf.select2.combobox.js
+++ b/libs/ext.sf.select2.combobox.js
@@ -247,8 +247,16 @@
 	combobox_proto.onChange = function() {
 		var self = this;
 		var data = $(this).select2( "data" );
+		var namespace = $(this).attr( "data-namespace" );
 		if (data !== null) {
-			$(this).val( data.text );
+			var val = data.text;
+			
+			if (namespace) {
+				if (val.split(':').length === 1) {
+					val = namespace + ':' + val;
+				}
+			}
+			$(this).val( val );
 		} else {
 			$(this).val( '' );
 		}
diff --git a/libs/ext.sf.select2.tokens.js b/libs/ext.sf.select2.tokens.js
index 03302da..e5fbe36 100644
--- a/libs/ext.sf.select2.tokens.js
+++ b/libs/ext.sf.select2.tokens.js
@@ -275,10 +275,18 @@
 		var tokens = new sf.select2.tokens();
 		var delim = tokens.getDelimiter( $(this) );
 
+		var namespace = $(this).attr( "data-namespace" );
+
 		if (data !== null) {
 			var tokens_value = "";
 			data.forEach( function( token ) {
-				tokens_value += token.text.trim() + delim + " ";
+				var val = token.text.trim();
+				if (namespace) {
+					if (val.split(':').length === 1) {
+						val = namespace + ':' + val;
+					}
+				}
+				tokens_value += val + delim + " ";
 			});
 			$(this).val( tokens_value );
 		} else {
-- 
2.7.0.windows.1

Event Timeline

This is pretty clever! Having the JavaScript make the change, so that users can see the namespace being added within the UI, reduces the element of surprise.

However, I think the check should be smarter - it should explicitly look for "namespace:", not just ":", because colons can show up in page titles.

Could you please associate at least one project with this task to allow others to find this task when searching in the corresponding project(s)? Thanks!

Yes, that's a possibility, too.

I can think of three approaches

  • Explicitly check for <namespace>: and if it doesn't exist, prepend it (your proposal
  • Check for : only, to see if there's a namespace at all. If there is already a namespace (even when its a different one) no prepend or replacement will take place.
  • If there's already a namespace, replace it with the given one. If there's none, add it.

What do you think?

I'd say definitely the first one - it's difficult to know for sure if "Blah:" at the beginning of a page name is a namespace or not. (Unless "Blah:" is the very thing you're looking for.)

Ok, here's a new patch with your proposed behavior:

diff --git a/0001-first-working-version-support-for-namespace-attribut.patch b/0001-first-working-version-support-for-namespace-attribut.patch
new file mode 100644
index 0000000..829f8c6
--- /dev/null
+++ b/0001-first-working-version-support-for-namespace-attribut.patch
@@ -0,0 +1,90 @@
+From a72dc33723c0ab77942e85f71d01a32a2bf39957 Mon Sep 17 00:00:00 2001
+From: Fannon <heimlersimon@gmail.com>
+Date: Thu, 12 May 2016 12:01:11 +0200
+Subject: [PATCH 1/2] first working version, support for namespace attribute in
+ tokens and combobox forminput
+
+---
+ includes/forminputs/SF_ComboBoxInput.php |  3 +++
+ includes/forminputs/SF_TokensInput.php   |  3 +++
+ libs/ext.sf.select2.combobox.js          | 10 +++++++++-
+ libs/ext.sf.select2.tokens.js            | 10 +++++++++-
+ 4 files changed, 24 insertions(+), 2 deletions(-)
+
+diff --git a/includes/forminputs/SF_ComboBoxInput.php b/includes/forminputs/SF_ComboBoxInput.php
+index 799766f..9a1e8d5 100644
+--- a/includes/forminputs/SF_ComboBoxInput.php
++++ b/includes/forminputs/SF_ComboBoxInput.php
+@@ -106,6 +106,9 @@ class SFComboBoxInput extends SFFormInput {
+ 		if ( !is_null( $remoteDataType ) ) {
+ 			$inputAttrs['autocompletedatatype'] = $remoteDataType;
+ 		}
++		if ( array_key_exists( 'namespace', $other_args ) ) {
++			$inputAttrs['data-namespace'] = $other_args['namespace'];
++		}
+ 
+ 		$inputText = Html::rawElement( 'input', $inputAttrs);
+ 
+diff --git a/includes/forminputs/SF_TokensInput.php b/includes/forminputs/SF_TokensInput.php
+index a5afbf8..2d2792a 100644
+--- a/includes/forminputs/SF_TokensInput.php
++++ b/includes/forminputs/SF_TokensInput.php
+@@ -150,6 +150,9 @@ class SFTokensInput extends SFFormInput {
+ 		if ( array_key_exists( 'max values', $other_args ) ) {
+ 			$inputAttrs['maxvalues'] = $other_args['max values'];
+ 		}
++		if ( array_key_exists( 'namespace', $other_args ) ) {
++			$inputAttrs['data-namespace'] = $other_args['namespace'];
++		}
+ 		$text = "\n\t" . Html::input( $input_name, $cur_value, 'text', $inputAttrs ) . "\n";
+ 
+ 		$spanClass = 'inputSpan';
+diff --git a/libs/ext.sf.select2.combobox.js b/libs/ext.sf.select2.combobox.js
+index f300440..1320c0c 100644
+--- a/libs/ext.sf.select2.combobox.js
++++ b/libs/ext.sf.select2.combobox.js
+@@ -247,8 +247,16 @@
+ 	combobox_proto.onChange = function() {
+ 		var self = this;
+ 		var data = $(this).select2( "data" );
++		var namespace = $(this).attr( "data-namespace" );
+ 		if (data !== null) {
+-			$(this).val( data.text );
++			var val = data.text;
++			
++			if (namespace) {
++				if (val.split(':').length === 1) {
++					val = namespace + ':' + val;
++				}
++			}
++			$(this).val( val );
+ 		} else {
+ 			$(this).val( '' );
+ 		}
+diff --git a/libs/ext.sf.select2.tokens.js b/libs/ext.sf.select2.tokens.js
+index 03302da..e5fbe36 100644
+--- a/libs/ext.sf.select2.tokens.js
++++ b/libs/ext.sf.select2.tokens.js
+@@ -275,10 +275,18 @@
+ 		var tokens = new sf.select2.tokens();
+ 		var delim = tokens.getDelimiter( $(this) );
+ 
++		var namespace = $(this).attr( "data-namespace" );
++
+ 		if (data !== null) {
+ 			var tokens_value = "";
+ 			data.forEach( function( token ) {
+-				tokens_value += token.text.trim() + delim + " ";
++				var val = token.text.trim();
++				if (namespace) {
++					if (val.split(':').length === 1) {
++						val = namespace + ':' + val;
++					}
++				}
++				tokens_value += val + delim + " ";
+ 			});
+ 			$(this).val( tokens_value );
+ 		} else {
+-- 
+2.7.0.windows.1
+
diff --git a/includes/forminputs/SF_ComboBoxInput.php b/includes/forminputs/SF_ComboBoxInput.php
index 799766f..9a1e8d5 100644
--- a/includes/forminputs/SF_ComboBoxInput.php
+++ b/includes/forminputs/SF_ComboBoxInput.php
@@ -106,6 +106,9 @@ class SFComboBoxInput extends SFFormInput {
 		if ( !is_null( $remoteDataType ) ) {
 			$inputAttrs['autocompletedatatype'] = $remoteDataType;
 		}
+		if ( array_key_exists( 'namespace', $other_args ) ) {
+			$inputAttrs['data-namespace'] = $other_args['namespace'];
+		}
 
 		$inputText = Html::rawElement( 'input', $inputAttrs);
 
diff --git a/includes/forminputs/SF_TokensInput.php b/includes/forminputs/SF_TokensInput.php
index a5afbf8..2d2792a 100644
--- a/includes/forminputs/SF_TokensInput.php
+++ b/includes/forminputs/SF_TokensInput.php
@@ -150,6 +150,9 @@ class SFTokensInput extends SFFormInput {
 		if ( array_key_exists( 'max values', $other_args ) ) {
 			$inputAttrs['maxvalues'] = $other_args['max values'];
 		}
+		if ( array_key_exists( 'namespace', $other_args ) ) {
+			$inputAttrs['data-namespace'] = $other_args['namespace'];
+		}
 		$text = "\n\t" . Html::input( $input_name, $cur_value, 'text', $inputAttrs ) . "\n";
 
 		$spanClass = 'inputSpan';
diff --git a/libs/ext.sf.select2.combobox.js b/libs/ext.sf.select2.combobox.js
index f300440..ad67682 100644
--- a/libs/ext.sf.select2.combobox.js
+++ b/libs/ext.sf.select2.combobox.js
@@ -247,8 +247,16 @@
 	combobox_proto.onChange = function() {
 		var self = this;
 		var data = $(this).select2( "data" );
+		var namespace = $(this).attr( "data-namespace" );
 		if (data !== null) {
-			$(this).val( data.text );
+			var val = data.text;
+			
+			if (namespace) {
+				if (val.indexOf(namespace + ':') === -1) {
+					val = namespace + ':' + val;
+				}
+			}
+			$(this).val( val );
 		} else {
 			$(this).val( '' );
 		}
diff --git a/libs/ext.sf.select2.tokens.js b/libs/ext.sf.select2.tokens.js
index 03302da..ff84634 100644
--- a/libs/ext.sf.select2.tokens.js
+++ b/libs/ext.sf.select2.tokens.js
@@ -275,10 +275,18 @@
 		var tokens = new sf.select2.tokens();
 		var delim = tokens.getDelimiter( $(this) );
 
+		var namespace = $(this).attr( "data-namespace" );
+
 		if (data !== null) {
 			var tokens_value = "";
 			data.forEach( function( token ) {
-				tokens_value += token.text.trim() + delim + " ";
+				var val = token.text.trim();
+				if (namespace) {
+					if (val.indexOf(namespace + ':') === -1) {
+						val = namespace + ':' + val;
+					}
+				}
+				tokens_value += val + delim + " ";
 			});
 			$(this).val( tokens_value );
 		} else {

Sorry, I've committed the last patch itself

Thanks! I just checked this in, in slightly modified form.

Hello Yaron,

today I've detected a bug in my namespace implementation which cause issues with values that are coming from autocomplete. This patch will only apply the namespace logic on user entered content:

Index: libs/ext.sf.select2.combobox.js
IDEA additional info:
Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP
<+>UTF-8
===================================================================
--- libs/ext.sf.select2.combobox.js	(revision 3cc57e7e50f9c81afa46cb9f6b3bb100b9dc50e9)
+++ libs/ext.sf.select2.combobox.js	(revision )
@@ -248,10 +248,11 @@
 		var self = this;
 		var data = $(this).select2( "data" );
 		var namespace = $(this).attr( "data-namespace" );
+
 		if (data !== null) {
 			var val = data.text;
 			
-			if (namespace) {
+			if (namespace && data.id === data.text) {
 				if (val.indexOf(namespace + ':') === -1) {
 					val = namespace + ':' + val;
 				}
Index: libs/ext.sf.select2.tokens.js
IDEA additional info:
Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP
<+>UTF-8
===================================================================
--- libs/ext.sf.select2.tokens.js	(revision 3cc57e7e50f9c81afa46cb9f6b3bb100b9dc50e9)
+++ libs/ext.sf.select2.tokens.js	(revision )
@@ -281,7 +281,7 @@
 			var tokens_value = "";
 			data.forEach( function( token ) {
 				var val = token.text.trim();
-				if (namespace) {
+				if (namespace && data.id === data.text) {
 					if (val.indexOf(namespace + ':') === -1) {
 						val = namespace + ':' + val;
 					}

Hello Yaron,

today I've looked into the master branch of SF, and this minor patch ist still missing - now applied to your coding standards:

diff
diff --git a/libs/ext.sf.select2.combobox.js b/libs/ext.sf.select2.combobox.js
index da59bb4..882f078 100644
--- a/libs/ext.sf.select2.combobox.js
+++ b/libs/ext.sf.select2.combobox.js
@@ -251,7 +251,7 @@
 
 		if (data !== null) {
  			var val = data.text;
- 			if ( namespace ) {
+ 			if ( namespace && data.id === data.text) {
  				if ( val.indexOf( namespace + ':' ) !== 0 ) {
  					val = namespace + ':' + val;
  				}
diff --git a/libs/ext.sf.select2.tokens.js b/libs/ext.sf.select2.tokens.js
index e12643e..e6388df 100644
--- a/libs/ext.sf.select2.tokens.js
+++ b/libs/ext.sf.select2.tokens.js
@@ -280,7 +280,7 @@
 			var tokens_value = "";
 			data.forEach( function( token ) {
  				var val = token.text.trim();
- 				if ( namespace ) {
+ 				if ( namespace && data.id === data.text ) {
  					if (val.indexOf( namespace + ':' ) !== 0 ) {
  						val = namespace + ':' + val;
  					}

This is just a bugfix, that ensures that only user entered text will get the namespace prepended. Otherwise pre-existing or autocompleted items will be processed, too.

Okay, that sounds good. I just checked in your fix - thank you!