Page MenuHomePhabricator

D16541.id39826.diff
No OneTemporary

D16541.id39826.diff

diff --git a/resources/celerity/map.php b/resources/celerity/map.php
--- a/resources/celerity/map.php
+++ b/resources/celerity/map.php
@@ -146,6 +146,7 @@
'rsrc/css/phui/phui-image-mask.css' => 'a8498f9c',
'rsrc/css/phui/phui-info-panel.css' => '27ea50a1',
'rsrc/css/phui/phui-info-view.css' => '28efab79',
+ 'rsrc/css/phui/phui-invisible-character-view.css' => '6993d9f0',
'rsrc/css/phui/phui-list.css' => '9da2aa00',
'rsrc/css/phui/phui-object-box.css' => '6b487c57',
'rsrc/css/phui/phui-object-item-list-view.css' => '87278fa0',
@@ -922,6 +923,7 @@
'phui-info-panel-css' => '27ea50a1',
'phui-info-view-css' => '28efab79',
'phui-inline-comment-view-css' => '5953c28e',
+ 'phui-invisible-character-view-css' => '6993d9f0',
'phui-list-view-css' => '9da2aa00',
'phui-object-box-css' => '6b487c57',
'phui-object-item-list-view-css' => '87278fa0',
diff --git a/src/__phutil_library_map__.php b/src/__phutil_library_map__.php
--- a/src/__phutil_library_map__.php
+++ b/src/__phutil_library_map__.php
@@ -1652,6 +1652,8 @@
'PHUIInfoPanelExample' => 'applications/uiexample/examples/PHUIInfoPanelExample.php',
'PHUIInfoPanelView' => 'view/phui/PHUIInfoPanelView.php',
'PHUIInfoView' => 'view/form/PHUIInfoView.php',
+ 'PHUIInvisibleCharacterTestCase' => 'view/phui/__tests__/PHUIInvisibleCharacterTestCase.php',
+ 'PHUIInvisibleCharacterView' => 'view/phui/PHUIInvisibleCharacterView.php',
'PHUIListExample' => 'applications/uiexample/examples/PHUIListExample.php',
'PHUIListItemView' => 'view/phui/PHUIListItemView.php',
'PHUIListView' => 'view/phui/PHUIListView.php',
@@ -6320,6 +6322,8 @@
'PHUIInfoPanelExample' => 'PhabricatorUIExample',
'PHUIInfoPanelView' => 'AphrontView',
'PHUIInfoView' => 'AphrontView',
+ 'PHUIInvisibleCharacterTestCase' => 'PhabricatorTestCase',
+ 'PHUIInvisibleCharacterView' => 'AphrontView',
'PHUIListExample' => 'PhabricatorUIExample',
'PHUIListItemView' => 'AphrontTagView',
'PHUIListView' => 'AphrontTagView',
diff --git a/src/applications/auth/controller/PhabricatorAuthRegisterController.php b/src/applications/auth/controller/PhabricatorAuthRegisterController.php
--- a/src/applications/auth/controller/PhabricatorAuthRegisterController.php
+++ b/src/applications/auth/controller/PhabricatorAuthRegisterController.php
@@ -89,13 +89,14 @@
// user expectation and it's not clear the cases it enables are valuable.
// See discussion in T3472.
if (!PhabricatorUserEmail::isAllowedAddress($default_email)) {
+ $debug_email = new PHUIInvisibleCharacterView($default_email);
return $this->renderError(
array(
pht(
'The account you are attempting to register with has an invalid '.
'email address (%s). This Phabricator install only allows '.
'registration with specific email addresses:',
- $default_email),
+ $debug_email),
phutil_tag('br'),
phutil_tag('br'),
PhabricatorUserEmail::describeAllowedAddresses(),
diff --git a/src/applications/phurl/editor/PhabricatorPhurlURLEditor.php b/src/applications/phurl/editor/PhabricatorPhurlURLEditor.php
--- a/src/applications/phurl/editor/PhabricatorPhurlURLEditor.php
+++ b/src/applications/phurl/editor/PhabricatorPhurlURLEditor.php
@@ -123,18 +123,23 @@
foreach ($xactions as $xaction) {
if ($xaction->getOldValue() != $xaction->getNewValue()) {
$new_alias = $xaction->getNewValue();
+ $debug_alias = new PHUIInvisibleCharacterView($new_alias);
if (!preg_match('/[a-zA-Z]/', $new_alias)) {
$errors[] = new PhabricatorApplicationTransactionValidationError(
$type,
pht('Invalid Alias'),
- pht('The alias must contain at least one letter.'),
+ pht('The alias you provided (%s) must contain at least one '.
+ 'letter.',
+ $debug_alias),
$xaction);
}
if (preg_match('/[^a-z0-9]/i', $new_alias)) {
$errors[] = new PhabricatorApplicationTransactionValidationError(
$type,
pht('Invalid Alias'),
- pht('The alias may only contain letters and numbers.'),
+ pht('The alias you provided (%s) may only contain letters and '.
+ 'numbers.',
+ $debug_alias),
$xaction);
}
}
diff --git a/src/view/phui/PHUIInvisibleCharacterView.php b/src/view/phui/PHUIInvisibleCharacterView.php
new file mode 100644
--- /dev/null
+++ b/src/view/phui/PHUIInvisibleCharacterView.php
@@ -0,0 +1,94 @@
+<?php
+
+/**
+ * API for replacing whitespace characters and some control characters with
+ * their printable representations. This is useful for debugging and
+ * displaying more helpful error messages to users.
+ *
+ */
+final class PHUIInvisibleCharacterView extends AphrontView {
+
+ private $inputText;
+ private $plainText = false;
+
+ // This is a list of the common invisible characters that are
+ // actually typeable. Other invisible characters will simply
+ // be displayed as their hex representations.
+ private static $invisibleChars = array(
+ "\x00" => 'NULL',
+ "\t" => 'TAB',
+ "\n" => 'NEWLINE',
+ "\x20" => 'SPACE',
+ );
+
+ public function __construct($input_text) {
+ $this->inputText = $input_text;
+ }
+
+ public function setPlainText($plain_text) {
+ $this->plainText = $plain_text;
+ return $this;
+ }
+
+ public function getStringParts() {
+ $input_text = $this->inputText;
+ $text_array = phutil_utf8v($input_text);
+
+ for ($ii = 0; $ii < count($text_array); $ii++) {
+ $char = $text_array[$ii];
+ $char_hex = bin2hex($char);
+ if (array_key_exists($char, self::$invisibleChars)) {
+ $text_array[$ii] = array(
+ 'special' => true,
+ 'value' => '<'.self::$invisibleChars[$char].'>',
+ );
+ } else if (ord($char) < 32) {
+ $text_array[$ii] = array(
+ 'special' => true,
+ 'value' => '<0x'.$char_hex.'>',
+ );
+ } else {
+ $text_array[$ii] = array(
+ 'special' => false,
+ 'value' => $char,
+ );
+ }
+ }
+ return $text_array;
+ }
+
+ private function renderHtmlArray() {
+ $html_array = array();
+ $parts = $this->getStringParts();
+ foreach ($parts as $part) {
+ if ($part['special']) {
+ $html_array[] = phutil_tag(
+ 'span',
+ array('class' => 'invisible-special'),
+ $part['value']);
+ } else {
+ $html_array[] = $part['value'];
+ }
+ }
+ return $html_array;
+ }
+
+ private function renderPlainText() {
+ $parts = $this->getStringParts();
+ $res = '';
+ foreach ($parts as $part) {
+ $res .= $part['value'];
+ }
+ return $res;
+ }
+
+ public function render() {
+ require_celerity_resource('phui-invisible-character-view-css');
+ if ($this->plainText) {
+ return $this->renderPlainText();
+ } else {
+ return $this->renderHtmlArray();
+ }
+ }
+
+}
diff --git a/src/view/phui/__tests__/PHUIInvisibleCharacterTestCase.php b/src/view/phui/__tests__/PHUIInvisibleCharacterTestCase.php
new file mode 100644
--- /dev/null
+++ b/src/view/phui/__tests__/PHUIInvisibleCharacterTestCase.php
@@ -0,0 +1,52 @@
+<?php
+
+final class PHUIInvisibleCharacterTestCase extends PhabricatorTestCase {
+
+ public function testEmptyString() {
+ $view = new PHUIInvisibleCharacterView('');
+ $res = $view->render();
+ $this->assertEqual($res, array());
+ }
+
+ public function testEmptyPlainText() {
+ $view = (new PHUIInvisibleCharacterView(''))
+ ->setPlainText(true);
+ $res = $view->render();
+ $this->assertEqual($res, '');
+ }
+
+ public function testWithNamedChars() {
+ $test_input = "\x00\n\t ";
+ $view = (new PHUIInvisibleCharacterView($test_input))
+ ->setPlainText(true);
+ $res = $view->render();
+ $this->assertEqual($res, '<NULL><NEWLINE><TAB><SPACE>');
+ }
+
+ public function testWithHexChars() {
+ $test_input = "abc\x01";
+ $view = (new PHUIInvisibleCharacterView($test_input))
+ ->setPlainText(true);
+ $res = $view->render();
+ $this->assertEqual($res, 'abc<0x01>');
+ }
+
+ public function testWithNamedAsHex() {
+ $test_input = "\x00\x0a\x09\x20";
+ $view = (new PHUIInvisibleCharacterView($test_input))
+ ->setPlainText(true);
+ $res = $view->render();
+ $this->assertEqual($res, '<NULL><NEWLINE><TAB><SPACE>');
+ }
+
+ public function testHtmlDecoration() {
+ $test_input = "a\x00\n\t ";
+ $view = new PHUIInvisibleCharacterView($test_input);
+ $res = $view->render();
+ $this->assertFalse($res[0] instanceof PhutilSafeHTML);
+ $this->assertTrue($res[1] instanceof PhutilSafeHTML);
+ $this->assertTrue($res[2] instanceof PhutilSafeHTML);
+ $this->assertTrue($res[3] instanceof PhutilSafeHTML);
+ $this->assertTrue($res[4] instanceof PhutilSafeHTML);
+ }
+}
diff --git a/webroot/rsrc/css/phui/phui-invisible-character-view.css b/webroot/rsrc/css/phui/phui-invisible-character-view.css
new file mode 100644
--- /dev/null
+++ b/webroot/rsrc/css/phui/phui-invisible-character-view.css
@@ -0,0 +1,12 @@
+/**
+ * @provides phui-invisible-character-view-css
+ */
+
+.invisible-special {
+ font-family: monospace;
+ color: #000;
+ background: rgba({$alphablue},0.1);
+ padding: 1px 4px;
+ border-radius: 3px;
+ white-space: pre-wrap;
+}

File Metadata

Mime Type
text/plain
Expires
Mon, Oct 28, 2:50 AM (3 w, 11 h ago)
Storage Engine
blob
Storage Format
Encrypted (AES-256-CBC)
Storage Handle
6713159
Default Alt Text
D16541.id39826.diff (9 KB)

Event Timeline