@recaptime-dev's working patches + fork for Phorge, a community fork of Phabricator. (Upstream dev and stable branches are at upstream/main and upstream/stable respectively.) hq.recaptime.dev/wiki/Phorge
phorge phabricator
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

Added initial class for displaying invisible chars

Summary:
Fixes T11586. First pass at a class for displaying invisible characters. Still need to:
- Write a couple unit tests
- Add some styling to the .invisible-special spans
- Actually start using the class when displaying form errors to users

Currently this makes the string `"\nab\x00c\x01d\te\nf"` look like:

{F1812711}

Test Plan:
Unit tests all pass and run in <1ms:

{F1812998}

Reviewers: epriestley, #blessed_reviewers, chad

Reviewed By: epriestley, #blessed_reviewers

Subscribers: Korvin, epriestley, yelirekim

Maniphest Tasks: T11586

Differential Revision: https://secure.phabricator.com/D16541

Josh Cox 2588b4fa ff64c4e0

+173 -3
+2
resources/celerity/map.php
··· 146 146 'rsrc/css/phui/phui-image-mask.css' => 'a8498f9c', 147 147 'rsrc/css/phui/phui-info-panel.css' => '27ea50a1', 148 148 'rsrc/css/phui/phui-info-view.css' => '28efab79', 149 + 'rsrc/css/phui/phui-invisible-character-view.css' => '6993d9f0', 149 150 'rsrc/css/phui/phui-list.css' => '9da2aa00', 150 151 'rsrc/css/phui/phui-object-box.css' => '6b487c57', 151 152 'rsrc/css/phui/phui-object-item-list-view.css' => '87278fa0', ··· 922 923 'phui-info-panel-css' => '27ea50a1', 923 924 'phui-info-view-css' => '28efab79', 924 925 'phui-inline-comment-view-css' => '5953c28e', 926 + 'phui-invisible-character-view-css' => '6993d9f0', 925 927 'phui-list-view-css' => '9da2aa00', 926 928 'phui-object-box-css' => '6b487c57', 927 929 'phui-object-item-list-view-css' => '87278fa0',
+4
src/__phutil_library_map__.php
··· 1652 1652 'PHUIInfoPanelExample' => 'applications/uiexample/examples/PHUIInfoPanelExample.php', 1653 1653 'PHUIInfoPanelView' => 'view/phui/PHUIInfoPanelView.php', 1654 1654 'PHUIInfoView' => 'view/form/PHUIInfoView.php', 1655 + 'PHUIInvisibleCharacterTestCase' => 'view/phui/__tests__/PHUIInvisibleCharacterTestCase.php', 1656 + 'PHUIInvisibleCharacterView' => 'view/phui/PHUIInvisibleCharacterView.php', 1655 1657 'PHUIListExample' => 'applications/uiexample/examples/PHUIListExample.php', 1656 1658 'PHUIListItemView' => 'view/phui/PHUIListItemView.php', 1657 1659 'PHUIListView' => 'view/phui/PHUIListView.php', ··· 6320 6322 'PHUIInfoPanelExample' => 'PhabricatorUIExample', 6321 6323 'PHUIInfoPanelView' => 'AphrontView', 6322 6324 'PHUIInfoView' => 'AphrontView', 6325 + 'PHUIInvisibleCharacterTestCase' => 'PhabricatorTestCase', 6326 + 'PHUIInvisibleCharacterView' => 'AphrontView', 6323 6327 'PHUIListExample' => 'PhabricatorUIExample', 6324 6328 'PHUIListItemView' => 'AphrontTagView', 6325 6329 'PHUIListView' => 'AphrontTagView',
+2 -1
src/applications/auth/controller/PhabricatorAuthRegisterController.php
··· 89 89 // user expectation and it's not clear the cases it enables are valuable. 90 90 // See discussion in T3472. 91 91 if (!PhabricatorUserEmail::isAllowedAddress($default_email)) { 92 + $debug_email = new PHUIInvisibleCharacterView($default_email); 92 93 return $this->renderError( 93 94 array( 94 95 pht( 95 96 'The account you are attempting to register with has an invalid '. 96 97 'email address (%s). This Phabricator install only allows '. 97 98 'registration with specific email addresses:', 98 - $default_email), 99 + $debug_email), 99 100 phutil_tag('br'), 100 101 phutil_tag('br'), 101 102 PhabricatorUserEmail::describeAllowedAddresses(),
+7 -2
src/applications/phurl/editor/PhabricatorPhurlURLEditor.php
··· 123 123 foreach ($xactions as $xaction) { 124 124 if ($xaction->getOldValue() != $xaction->getNewValue()) { 125 125 $new_alias = $xaction->getNewValue(); 126 + $debug_alias = new PHUIInvisibleCharacterView($new_alias); 126 127 if (!preg_match('/[a-zA-Z]/', $new_alias)) { 127 128 $errors[] = new PhabricatorApplicationTransactionValidationError( 128 129 $type, 129 130 pht('Invalid Alias'), 130 - pht('The alias must contain at least one letter.'), 131 + pht('The alias you provided (%s) must contain at least one '. 132 + 'letter.', 133 + $debug_alias), 131 134 $xaction); 132 135 } 133 136 if (preg_match('/[^a-z0-9]/i', $new_alias)) { 134 137 $errors[] = new PhabricatorApplicationTransactionValidationError( 135 138 $type, 136 139 pht('Invalid Alias'), 137 - pht('The alias may only contain letters and numbers.'), 140 + pht('The alias you provided (%s) may only contain letters and '. 141 + 'numbers.', 142 + $debug_alias), 138 143 $xaction); 139 144 } 140 145 }
+94
src/view/phui/PHUIInvisibleCharacterView.php
··· 1 + <?php 2 + 3 + /** 4 + * API for replacing whitespace characters and some control characters with 5 + * their printable representations. This is useful for debugging and 6 + * displaying more helpful error messages to users. 7 + * 8 + */ 9 + final class PHUIInvisibleCharacterView extends AphrontView { 10 + 11 + private $inputText; 12 + private $plainText = false; 13 + 14 + // This is a list of the common invisible characters that are 15 + // actually typeable. Other invisible characters will simply 16 + // be displayed as their hex representations. 17 + private static $invisibleChars = array( 18 + "\x00" => 'NULL', 19 + "\t" => 'TAB', 20 + "\n" => 'NEWLINE', 21 + "\x20" => 'SPACE', 22 + ); 23 + 24 + public function __construct($input_text) { 25 + $this->inputText = $input_text; 26 + } 27 + 28 + public function setPlainText($plain_text) { 29 + $this->plainText = $plain_text; 30 + return $this; 31 + } 32 + 33 + public function getStringParts() { 34 + $input_text = $this->inputText; 35 + $text_array = phutil_utf8v($input_text); 36 + 37 + for ($ii = 0; $ii < count($text_array); $ii++) { 38 + $char = $text_array[$ii]; 39 + $char_hex = bin2hex($char); 40 + if (array_key_exists($char, self::$invisibleChars)) { 41 + $text_array[$ii] = array( 42 + 'special' => true, 43 + 'value' => '<'.self::$invisibleChars[$char].'>', 44 + ); 45 + } else if (ord($char) < 32) { 46 + $text_array[$ii] = array( 47 + 'special' => true, 48 + 'value' => '<0x'.$char_hex.'>', 49 + ); 50 + } else { 51 + $text_array[$ii] = array( 52 + 'special' => false, 53 + 'value' => $char, 54 + ); 55 + } 56 + } 57 + return $text_array; 58 + } 59 + 60 + private function renderHtmlArray() { 61 + $html_array = array(); 62 + $parts = $this->getStringParts(); 63 + foreach ($parts as $part) { 64 + if ($part['special']) { 65 + $html_array[] = phutil_tag( 66 + 'span', 67 + array('class' => 'invisible-special'), 68 + $part['value']); 69 + } else { 70 + $html_array[] = $part['value']; 71 + } 72 + } 73 + return $html_array; 74 + } 75 + 76 + private function renderPlainText() { 77 + $parts = $this->getStringParts(); 78 + $res = ''; 79 + foreach ($parts as $part) { 80 + $res .= $part['value']; 81 + } 82 + return $res; 83 + } 84 + 85 + public function render() { 86 + require_celerity_resource('phui-invisible-character-view-css'); 87 + if ($this->plainText) { 88 + return $this->renderPlainText(); 89 + } else { 90 + return $this->renderHtmlArray(); 91 + } 92 + } 93 + 94 + }
+52
src/view/phui/__tests__/PHUIInvisibleCharacterTestCase.php
··· 1 + <?php 2 + 3 + final class PHUIInvisibleCharacterTestCase extends PhabricatorTestCase { 4 + 5 + public function testEmptyString() { 6 + $view = new PHUIInvisibleCharacterView(''); 7 + $res = $view->render(); 8 + $this->assertEqual($res, array()); 9 + } 10 + 11 + public function testEmptyPlainText() { 12 + $view = (new PHUIInvisibleCharacterView('')) 13 + ->setPlainText(true); 14 + $res = $view->render(); 15 + $this->assertEqual($res, ''); 16 + } 17 + 18 + public function testWithNamedChars() { 19 + $test_input = "\x00\n\t "; 20 + $view = (new PHUIInvisibleCharacterView($test_input)) 21 + ->setPlainText(true); 22 + $res = $view->render(); 23 + $this->assertEqual($res, '<NULL><NEWLINE><TAB><SPACE>'); 24 + } 25 + 26 + public function testWithHexChars() { 27 + $test_input = "abc\x01"; 28 + $view = (new PHUIInvisibleCharacterView($test_input)) 29 + ->setPlainText(true); 30 + $res = $view->render(); 31 + $this->assertEqual($res, 'abc<0x01>'); 32 + } 33 + 34 + public function testWithNamedAsHex() { 35 + $test_input = "\x00\x0a\x09\x20"; 36 + $view = (new PHUIInvisibleCharacterView($test_input)) 37 + ->setPlainText(true); 38 + $res = $view->render(); 39 + $this->assertEqual($res, '<NULL><NEWLINE><TAB><SPACE>'); 40 + } 41 + 42 + public function testHtmlDecoration() { 43 + $test_input = "a\x00\n\t "; 44 + $view = new PHUIInvisibleCharacterView($test_input); 45 + $res = $view->render(); 46 + $this->assertFalse($res[0] instanceof PhutilSafeHTML); 47 + $this->assertTrue($res[1] instanceof PhutilSafeHTML); 48 + $this->assertTrue($res[2] instanceof PhutilSafeHTML); 49 + $this->assertTrue($res[3] instanceof PhutilSafeHTML); 50 + $this->assertTrue($res[4] instanceof PhutilSafeHTML); 51 + } 52 + }
+12
webroot/rsrc/css/phui/phui-invisible-character-view.css
··· 1 + /** 2 + * @provides phui-invisible-character-view-css 3 + */ 4 + 5 + .invisible-special { 6 + font-family: monospace; 7 + color: #000; 8 + background: rgba({$alphablue},0.1); 9 + padding: 1px 4px; 10 + border-radius: 3px; 11 + white-space: pre-wrap; 12 + }