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', @@ -6319,6 +6321,8 @@ 'PHUIInfoPanelExample' => 'PhabricatorUIExample', 'PHUIInfoPanelView' => 'AphrontView', 'PHUIInfoView' => 'AphrontView', + 'PHUIInvisibleCharacterTestCase' => 'PhabricatorTestCase', + 'PHUIInvisibleCharacterView' => 'AphrontView', 'PHUIListExample' => 'PhabricatorUIExample', 'PHUIListItemView' => 'AphrontTagView', 'PHUIListView' => 'AphrontTagView', 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,93 @@ + '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() { + 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 @@ +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, ''); + } + + 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, ''); + } + + 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); + } +}