@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.

Substantially support character encodings and "Highlight As" in changesets

Summary: Ref T5179. Ref T4045. Ref T832. We can now write non-utf8 hunks into the database, so try to do more reasonable things with them in the UI.

Test Plan: (See screenshots...)

Reviewers: btrahan

Reviewed By: btrahan

Subscribers: epriestley

Maniphest Tasks: T832, T4045, T5179

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

+619 -299
+34 -33
resources/celerity/map.php
··· 11 11 'core.pkg.js' => '07b01d4f', 12 12 'darkconsole.pkg.js' => 'ca8671ce', 13 13 'differential.pkg.css' => '4a93db37', 14 - 'differential.pkg.js' => 'eca39a2c', 14 + 'differential.pkg.js' => '2b128f3a', 15 15 'diffusion.pkg.css' => '471bc9eb', 16 16 'diffusion.pkg.js' => '077e3ad0', 17 17 'maniphest.pkg.css' => 'f88a8402', ··· 358 358 'rsrc/js/application/dashboard/behavior-dashboard-move-panels.js' => 'fa187a68', 359 359 'rsrc/js/application/dashboard/behavior-dashboard-query-panel-select.js' => '3be3eef5', 360 360 'rsrc/js/application/dashboard/behavior-dashboard-tab-panel.js' => 'aa077691', 361 - 'rsrc/js/application/differential/ChangesetViewManager.js' => 'db09a523', 361 + 'rsrc/js/application/differential/ChangesetViewManager.js' => '1f304ef8', 362 362 'rsrc/js/application/differential/DifferentialInlineCommentEditor.js' => 'f2441746', 363 363 'rsrc/js/application/differential/behavior-add-reviewers-and-ccs.js' => '533a187b', 364 364 'rsrc/js/application/differential/behavior-comment-jump.js' => '71755c79', 365 365 'rsrc/js/application/differential/behavior-comment-preview.js' => '127f2018', 366 366 'rsrc/js/application/differential/behavior-diff-radios.js' => 'e1ff79b1', 367 - 'rsrc/js/application/differential/behavior-dropdown-menus.js' => '64a79839', 367 + 'rsrc/js/application/differential/behavior-dropdown-menus.js' => '710f209e', 368 368 'rsrc/js/application/differential/behavior-edit-inline-comments.js' => '00861799', 369 369 'rsrc/js/application/differential/behavior-keyboard-nav.js' => '173ce7e7', 370 370 'rsrc/js/application/differential/behavior-populate.js' => 'bdb3e4d0', ··· 509 509 'aphront-two-column-view-css' => '16ab3ad2', 510 510 'aphront-typeahead-control-css' => 'a989b5b3', 511 511 'auth-css' => '1e655982', 512 - 'changeset-view-manager' => 'db09a523', 512 + 'changeset-view-manager' => '1f304ef8', 513 513 'config-options-css' => '7fedf08b', 514 514 'conpherence-menu-css' => 'e1e0fdf1', 515 515 'conpherence-message-pane-css' => '11a393ca', ··· 564 564 'javelin-behavior-differential-add-reviewers-and-ccs' => '533a187b', 565 565 'javelin-behavior-differential-comment-jump' => '71755c79', 566 566 'javelin-behavior-differential-diff-radios' => 'e1ff79b1', 567 - 'javelin-behavior-differential-dropdown-menus' => '64a79839', 567 + 'javelin-behavior-differential-dropdown-menus' => '710f209e', 568 568 'javelin-behavior-differential-edit-inline-comments' => '00861799', 569 569 'javelin-behavior-differential-feedback-preview' => '127f2018', 570 570 'javelin-behavior-differential-keyboard-navigation' => '173ce7e7', ··· 1001 1001 5 => 'phabricator-drag-and-drop-file-upload', 1002 1002 6 => 'phabricator-draggable-list', 1003 1003 ), 1004 + '1f304ef8' => 1005 + array( 1006 + 0 => 'javelin-dom', 1007 + 1 => 'javelin-util', 1008 + 2 => 'javelin-stratcom', 1009 + 3 => 'javelin-install', 1010 + 4 => 'javelin-workflow', 1011 + 5 => 'javelin-router', 1012 + 6 => 'javelin-behavior-device', 1013 + 7 => 'javelin-vector', 1014 + ), 1004 1015 '2290aeef' => 1005 1016 array( 1006 1017 0 => 'javelin-install', ··· 1273 1284 2 => 'javelin-util', 1274 1285 3 => 'phabricator-shaped-request', 1275 1286 ), 1276 - '7319e029' => 1277 - array( 1278 - 0 => 'javelin-behavior', 1279 - 1 => 'javelin-dom', 1280 - ), 1281 1287 '62e18640' => 1282 1288 array( 1283 1289 0 => 'javelin-install', ··· 1291 1297 1 => 'javelin-dom', 1292 1298 2 => 'javelin-fx', 1293 1299 ), 1294 - '64a79839' => 1295 - array( 1296 - 0 => 'javelin-behavior', 1297 - 1 => 'javelin-dom', 1298 - 2 => 'javelin-util', 1299 - 3 => 'javelin-stratcom', 1300 - 4 => 'phuix-dropdown-menu', 1301 - 5 => 'phuix-action-list-view', 1302 - 6 => 'phuix-action-view', 1303 - 7 => 'phabricator-phtize', 1304 - 8 => 'changeset-view-manager', 1305 - ), 1306 1300 '64ef2fd2' => 1307 1301 array( 1308 1302 0 => 'javelin-behavior', ··· 1344 1338 1 => 'javelin-dom', 1345 1339 2 => 'javelin-util', 1346 1340 ), 1341 + '710f209e' => 1342 + array( 1343 + 0 => 'javelin-behavior', 1344 + 1 => 'javelin-dom', 1345 + 2 => 'javelin-util', 1346 + 3 => 'javelin-stratcom', 1347 + 4 => 'javelin-workflow', 1348 + 5 => 'phuix-dropdown-menu', 1349 + 6 => 'phuix-action-list-view', 1350 + 7 => 'phuix-action-view', 1351 + 8 => 'phabricator-phtize', 1352 + 9 => 'changeset-view-manager', 1353 + ), 1347 1354 '71755c79' => 1348 1355 array( 1349 1356 0 => 'javelin-behavior', 1350 1357 1 => 'javelin-stratcom', 1351 1358 2 => 'javelin-dom', 1359 + ), 1360 + '7319e029' => 1361 + array( 1362 + 0 => 'javelin-behavior', 1363 + 1 => 'javelin-dom', 1352 1364 ), 1353 1365 '76f4ebed' => 1354 1366 array( ··· 1915 1927 0 => 'javelin-install', 1916 1928 1 => 'javelin-util', 1917 1929 2 => 'javelin-stratcom', 1918 - ), 1919 - 'db09a523' => 1920 - array( 1921 - 0 => 'javelin-dom', 1922 - 1 => 'javelin-util', 1923 - 2 => 'javelin-stratcom', 1924 - 3 => 'javelin-install', 1925 - 4 => 'javelin-workflow', 1926 - 5 => 'javelin-router', 1927 - 6 => 'javelin-behavior-device', 1928 - 7 => 'javelin-vector', 1929 1930 ), 1930 1931 'dd7e8ef5' => 1931 1932 array(
+4
src/__phutil_library_map__.php
··· 2258 2258 'PhabricatorSystemRemoveDestroyWorkflow' => 'applications/system/management/PhabricatorSystemRemoveDestroyWorkflow.php', 2259 2259 'PhabricatorSystemRemoveLogWorkflow' => 'applications/system/management/PhabricatorSystemRemoveLogWorkflow.php', 2260 2260 'PhabricatorSystemRemoveWorkflow' => 'applications/system/management/PhabricatorSystemRemoveWorkflow.php', 2261 + 'PhabricatorSystemSelectEncodingController' => 'applications/system/controller/PhabricatorSystemSelectEncodingController.php', 2262 + 'PhabricatorSystemSelectHighlightController' => 'applications/system/controller/PhabricatorSystemSelectHighlightController.php', 2261 2263 'PhabricatorTaskmasterDaemon' => 'infrastructure/daemon/workers/PhabricatorTaskmasterDaemon.php', 2262 2264 'PhabricatorTestCase' => 'infrastructure/testing/PhabricatorTestCase.php', 2263 2265 'PhabricatorTestController' => 'applications/base/controller/__tests__/PhabricatorTestController.php', ··· 5134 5136 'PhabricatorSystemRemoveDestroyWorkflow' => 'PhabricatorSystemRemoveWorkflow', 5135 5137 'PhabricatorSystemRemoveLogWorkflow' => 'PhabricatorSystemRemoveWorkflow', 5136 5138 'PhabricatorSystemRemoveWorkflow' => 'PhabricatorManagementWorkflow', 5139 + 'PhabricatorSystemSelectEncodingController' => 'PhabricatorController', 5140 + 'PhabricatorSystemSelectHighlightController' => 'PhabricatorController', 5137 5141 'PhabricatorTaskmasterDaemon' => 'PhabricatorDaemon', 5138 5142 'PhabricatorTestCase' => 'ArcanistPhutilTestCase', 5139 5143 'PhabricatorTestController' => 'PhabricatorController',
+2
src/applications/differential/controller/DifferentialChangesetViewController.php
··· 155 155 $parser->setRightSideCommentMapping($right_source, $right_new); 156 156 $parser->setLeftSideCommentMapping($left_source, $left_new); 157 157 $parser->setWhitespaceMode($request->getStr('whitespace')); 158 + $parser->setCharacterEncoding($request->getStr('encoding')); 159 + $parser->setHighlightAs($request->getStr('highlight')); 158 160 159 161 if ($request->getStr('renderer') == '1up') { 160 162 $parser->setRenderer(new DifferentialChangesetOneUpRenderer());
+65 -10
src/applications/differential/parser/DifferentialChangesetParser.php
··· 41 41 private $highlightErrors; 42 42 private $disableCache; 43 43 private $renderer; 44 + private $characterEncoding; 45 + private $highlightAs; 46 + 47 + public function setHighlightAs($highlight_as) { 48 + $this->highlightAs = $highlight_as; 49 + return $this; 50 + } 51 + 52 + public function getHighlightAs() { 53 + return $this->highlightAs; 54 + } 55 + 56 + public function setCharacterEncoding($character_encoding) { 57 + $this->characterEncoding = $character_encoding; 58 + return $this; 59 + } 60 + 61 + public function getCharacterEncoding() { 62 + return $this->characterEncoding; 63 + } 44 64 45 65 public function setRenderer($renderer) { 46 66 $this->renderer = $renderer; ··· 422 442 } 423 443 424 444 private function getHighlightFuture($corpus) { 445 + $language = $this->highlightAs; 446 + 447 + if (!$language) { 448 + $language = $this->highlightEngine->getLanguageFromFilename( 449 + $this->filename); 450 + } 451 + 425 452 return $this->highlightEngine->getHighlightFuture( 426 - $this->highlightEngine->getLanguageFromFilename($this->filename), 453 + $language, 427 454 $corpus); 428 455 } 429 456 ··· 452 479 453 480 $skip_cache = ($whitespace_mode != self::WHITESPACE_IGNORE_ALL); 454 481 if ($this->disableCache) { 482 + $skip_cache = true; 483 + } 484 + 485 + if ($this->characterEncoding) { 486 + $skip_cache = true; 487 + } 488 + 489 + if ($this->highlightAs) { 455 490 $skip_cache = true; 456 491 } 457 492 ··· 677 712 // requests. 678 713 $this->isTopLevel = (($range_start === null) && ($range_len === null)); 679 714 $this->highlightEngine = PhabricatorSyntaxHighlighter::newEngine(); 715 + 716 + $encoding = null; 717 + if ($this->characterEncoding) { 718 + // We are forcing this changeset to be interpreted with a specific 719 + // character encoding, so force all the hunks into that encoding and 720 + // propagate it to the renderer. 721 + $encoding = $this->characterEncoding; 722 + foreach ($this->changeset->getHunks() as $hunk) { 723 + $hunk->forceEncoding($this->characterEncoding); 724 + } 725 + } else { 726 + // We're just using the default, so tell the renderer what that is 727 + // (by reading the encoding from the first hunk). 728 + foreach ($this->changeset->getHunks() as $hunk) { 729 + $encoding = $hunk->getDataEncoding(); 730 + break; 731 + } 732 + } 733 + 680 734 $this->tryCacheStuff(); 681 735 $render_pch = $this->shouldRenderPropertyChangeHeader($this->changeset); 682 736 ··· 700 754 ->setMarkupEngine($this->markupEngine) 701 755 ->setHandles($this->handles) 702 756 ->setOldLines($this->old) 703 - ->setNewLines($this->new); 757 + ->setNewLines($this->new) 758 + ->setOriginalCharacterEncoding($encoding); 704 759 705 760 if ($this->user) { 706 761 $renderer->setUser($this->user); ··· 1013 1068 private function isCommentVisibleOnRenderedDiff( 1014 1069 PhabricatorInlineCommentInterface $comment) { 1015 1070 1016 - $changeset_id = $comment->getChangesetID(); 1017 - $is_new = $comment->getIsNewFile(); 1071 + $changeset_id = $comment->getChangesetID(); 1072 + $is_new = $comment->getIsNewFile(); 1018 1073 1019 - if ($changeset_id == $this->rightSideChangesetID && 1074 + if ($changeset_id == $this->rightSideChangesetID && 1020 1075 $is_new == $this->rightSideAttachesToNewFile) { 1021 - return true; 1022 - } 1076 + return true; 1077 + } 1023 1078 1024 - if ($changeset_id == $this->leftSideChangesetID && 1079 + if ($changeset_id == $this->leftSideChangesetID && 1025 1080 $is_new == $this->leftSideAttachesToNewFile) { 1026 - return true; 1027 - } 1081 + return true; 1082 + } 1028 1083 1029 1084 return false; 1030 1085 }
+258 -231
src/applications/differential/render/DifferentialChangesetHTMLRenderer.php
··· 9 9 $change = $changeset->getChangeType(); 10 10 $file = $changeset->getFileType(); 11 11 12 - $message = null; 13 - if ($change == DifferentialChangeType::TYPE_CHANGE && 14 - $file == DifferentialChangeType::FILE_TEXT) { 15 - if ($force) { 16 - // We have to force something to render because there were no changes 17 - // of other kinds. 18 - $message = pht('This file was not modified.'); 19 - } else { 20 - // Default case of changes to a text file, no metadata. 21 - return null; 22 - } 23 - } else { 24 - $none = hsprintf(''); 25 - switch ($change) { 12 + $messages = array(); 13 + $none = hsprintf(''); 14 + switch ($change) { 26 15 27 - case DifferentialChangeType::TYPE_ADD: 28 - switch ($file) { 29 - case DifferentialChangeType::FILE_TEXT: 30 - $message = pht('This file was <strong>added</strong>.', $none); 31 - break; 32 - case DifferentialChangeType::FILE_IMAGE: 33 - $message = pht('This image was <strong>added</strong>.', $none); 34 - break; 35 - case DifferentialChangeType::FILE_DIRECTORY: 36 - $message = pht( 37 - 'This directory was <strong>added</strong>.', 38 - $none); 39 - break; 40 - case DifferentialChangeType::FILE_BINARY: 41 - $message = pht( 42 - 'This binary file was <strong>added</strong>.', 43 - $none); 44 - break; 45 - case DifferentialChangeType::FILE_SYMLINK: 46 - $message = pht('This symlink was <strong>added</strong>.', $none); 47 - break; 48 - case DifferentialChangeType::FILE_SUBMODULE: 49 - $message = pht( 50 - 'This submodule was <strong>added</strong>.', 51 - $none); 52 - break; 53 - } 54 - break; 16 + case DifferentialChangeType::TYPE_ADD: 17 + switch ($file) { 18 + case DifferentialChangeType::FILE_TEXT: 19 + $messages[] = pht( 20 + 'This file was <strong>added</strong>.', 21 + $none); 22 + break; 23 + case DifferentialChangeType::FILE_IMAGE: 24 + $messages[] = pht( 25 + 'This image was <strong>added</strong>.', 26 + $none); 27 + break; 28 + case DifferentialChangeType::FILE_DIRECTORY: 29 + $messages[] = pht( 30 + 'This directory was <strong>added</strong>.', 31 + $none); 32 + break; 33 + case DifferentialChangeType::FILE_BINARY: 34 + $messages[] = pht( 35 + 'This binary file was <strong>added</strong>.', 36 + $none); 37 + break; 38 + case DifferentialChangeType::FILE_SYMLINK: 39 + $messages[] = pht( 40 + 'This symlink was <strong>added</strong>.', 41 + $none); 42 + break; 43 + case DifferentialChangeType::FILE_SUBMODULE: 44 + $messages[] = pht( 45 + 'This submodule was <strong>added</strong>.', 46 + $none); 47 + break; 48 + } 49 + break; 50 + 51 + case DifferentialChangeType::TYPE_DELETE: 52 + switch ($file) { 53 + case DifferentialChangeType::FILE_TEXT: 54 + $messages[] = pht( 55 + 'This file was <strong>deleted</strong>.', 56 + $none); 57 + break; 58 + case DifferentialChangeType::FILE_IMAGE: 59 + $messages[] = pht( 60 + 'This image was <strong>deleted</strong>.', 61 + $none); 62 + break; 63 + case DifferentialChangeType::FILE_DIRECTORY: 64 + $messages[] = pht( 65 + 'This directory was <strong>deleted</strong>.', 66 + $none); 67 + break; 68 + case DifferentialChangeType::FILE_BINARY: 69 + $messages[] = pht( 70 + 'This binary file was <strong>deleted</strong>.', 71 + $none); 72 + break; 73 + case DifferentialChangeType::FILE_SYMLINK: 74 + $messages[] = pht( 75 + 'This symlink was <strong>deleted</strong>.', 76 + $none); 77 + break; 78 + case DifferentialChangeType::FILE_SUBMODULE: 79 + $messages[] = pht( 80 + 'This submodule was <strong>deleted</strong>.', 81 + $none); 82 + break; 83 + } 84 + break; 55 85 56 - case DifferentialChangeType::TYPE_DELETE: 57 - switch ($file) { 58 - case DifferentialChangeType::FILE_TEXT: 59 - $message = pht('This file was <strong>deleted</strong>.', $none); 60 - break; 61 - case DifferentialChangeType::FILE_IMAGE: 62 - $message = pht('This image was <strong>deleted</strong>.', $none); 63 - break; 64 - case DifferentialChangeType::FILE_DIRECTORY: 65 - $message = pht( 66 - 'This directory was <strong>deleted</strong>.', 67 - $none); 68 - break; 69 - case DifferentialChangeType::FILE_BINARY: 70 - $message = pht( 71 - 'This binary file was <strong>deleted</strong>.', 72 - $none); 73 - break; 74 - case DifferentialChangeType::FILE_SYMLINK: 75 - $message = pht( 76 - 'This symlink was <strong>deleted</strong>.', 77 - $none); 78 - break; 79 - case DifferentialChangeType::FILE_SUBMODULE: 80 - $message = pht( 81 - 'This submodule was <strong>deleted</strong>.', 82 - $none); 83 - break; 84 - } 85 - break; 86 + case DifferentialChangeType::TYPE_MOVE_HERE: 87 + $from = phutil_tag('strong', array(), $changeset->getOldFile()); 88 + switch ($file) { 89 + case DifferentialChangeType::FILE_TEXT: 90 + $messages[] = pht('This file was moved from %s.', $from); 91 + break; 92 + case DifferentialChangeType::FILE_IMAGE: 93 + $messages[] = pht('This image was moved from %s.', $from); 94 + break; 95 + case DifferentialChangeType::FILE_DIRECTORY: 96 + $messages[] = pht('This directory was moved from %s.', $from); 97 + break; 98 + case DifferentialChangeType::FILE_BINARY: 99 + $messages[] = pht('This binary file was moved from %s.', $from); 100 + break; 101 + case DifferentialChangeType::FILE_SYMLINK: 102 + $messages[] = pht('This symlink was moved from %s.', $from); 103 + break; 104 + case DifferentialChangeType::FILE_SUBMODULE: 105 + $messages[] = pht('This submodule was moved from %s.', $from); 106 + break; 107 + } 108 + break; 86 109 87 - case DifferentialChangeType::TYPE_MOVE_HERE: 88 - $from = phutil_tag('strong', array(), $changeset->getOldFile()); 89 - switch ($file) { 90 - case DifferentialChangeType::FILE_TEXT: 91 - $message = pht('This file was moved from %s.', $from); 92 - break; 93 - case DifferentialChangeType::FILE_IMAGE: 94 - $message = pht('This image was moved from %s.', $from); 95 - break; 96 - case DifferentialChangeType::FILE_DIRECTORY: 97 - $message = pht('This directory was moved from %s.', $from); 98 - break; 99 - case DifferentialChangeType::FILE_BINARY: 100 - $message = pht('This binary file was moved from %s.', $from); 101 - break; 102 - case DifferentialChangeType::FILE_SYMLINK: 103 - $message = pht('This symlink was moved from %s.', $from); 104 - break; 105 - case DifferentialChangeType::FILE_SUBMODULE: 106 - $message = pht('This submodule was moved from %s.', $from); 107 - break; 108 - } 109 - break; 110 + case DifferentialChangeType::TYPE_COPY_HERE: 111 + $from = phutil_tag('strong', array(), $changeset->getOldFile()); 112 + switch ($file) { 113 + case DifferentialChangeType::FILE_TEXT: 114 + $messages[] = pht('This file was copied from %s.', $from); 115 + break; 116 + case DifferentialChangeType::FILE_IMAGE: 117 + $messages[] = pht('This image was copied from %s.', $from); 118 + break; 119 + case DifferentialChangeType::FILE_DIRECTORY: 120 + $messages[] = pht('This directory was copied from %s.', $from); 121 + break; 122 + case DifferentialChangeType::FILE_BINARY: 123 + $messages[] = pht('This binary file was copied from %s.', $from); 124 + break; 125 + case DifferentialChangeType::FILE_SYMLINK: 126 + $messages[] = pht('This symlink was copied from %s.', $from); 127 + break; 128 + case DifferentialChangeType::FILE_SUBMODULE: 129 + $messages[] = pht('This submodule was copied from %s.', $from); 130 + break; 131 + } 132 + break; 110 133 111 - case DifferentialChangeType::TYPE_COPY_HERE: 112 - $from = phutil_tag('strong', array(), $changeset->getOldFile()); 113 - switch ($file) { 114 - case DifferentialChangeType::FILE_TEXT: 115 - $message = pht('This file was copied from %s.', $from); 116 - break; 117 - case DifferentialChangeType::FILE_IMAGE: 118 - $message = pht('This image was copied from %s.', $from); 119 - break; 120 - case DifferentialChangeType::FILE_DIRECTORY: 121 - $message = pht('This directory was copied from %s.', $from); 122 - break; 123 - case DifferentialChangeType::FILE_BINARY: 124 - $message = pht('This binary file was copied from %s.', $from); 125 - break; 126 - case DifferentialChangeType::FILE_SYMLINK: 127 - $message = pht('This symlink was copied from %s.', $from); 128 - break; 129 - case DifferentialChangeType::FILE_SUBMODULE: 130 - $message = pht('This submodule was copied from %s.', $from); 131 - break; 132 - } 133 - break; 134 + case DifferentialChangeType::TYPE_MOVE_AWAY: 135 + $paths = phutil_tag( 136 + 'strong', 137 + array(), 138 + implode(', ', $changeset->getAwayPaths())); 139 + switch ($file) { 140 + case DifferentialChangeType::FILE_TEXT: 141 + $messages[] = pht('This file was moved to %s.', $paths); 142 + break; 143 + case DifferentialChangeType::FILE_IMAGE: 144 + $messages[] = pht('This image was moved to %s.', $paths); 145 + break; 146 + case DifferentialChangeType::FILE_DIRECTORY: 147 + $messages[] = pht('This directory was moved to %s.', $paths); 148 + break; 149 + case DifferentialChangeType::FILE_BINARY: 150 + $messages[] = pht('This binary file was moved to %s.', $paths); 151 + break; 152 + case DifferentialChangeType::FILE_SYMLINK: 153 + $messages[] = pht('This symlink was moved to %s.', $paths); 154 + break; 155 + case DifferentialChangeType::FILE_SUBMODULE: 156 + $messages[] = pht('This submodule was moved to %s.', $paths); 157 + break; 158 + } 159 + break; 134 160 135 - case DifferentialChangeType::TYPE_MOVE_AWAY: 136 - $paths = phutil_tag( 137 - 'strong', 138 - array(), 139 - implode(', ', $changeset->getAwayPaths())); 140 - switch ($file) { 141 - case DifferentialChangeType::FILE_TEXT: 142 - $message = pht('This file was moved to %s.', $paths); 143 - break; 144 - case DifferentialChangeType::FILE_IMAGE: 145 - $message = pht('This image was moved to %s.', $paths); 146 - break; 147 - case DifferentialChangeType::FILE_DIRECTORY: 148 - $message = pht('This directory was moved to %s.', $paths); 149 - break; 150 - case DifferentialChangeType::FILE_BINARY: 151 - $message = pht('This binary file was moved to %s.', $paths); 152 - break; 153 - case DifferentialChangeType::FILE_SYMLINK: 154 - $message = pht('This symlink was moved to %s.', $paths); 155 - break; 156 - case DifferentialChangeType::FILE_SUBMODULE: 157 - $message = pht('This submodule was moved to %s.', $paths); 158 - break; 159 - } 160 - break; 161 + case DifferentialChangeType::TYPE_COPY_AWAY: 162 + $paths = phutil_tag( 163 + 'strong', 164 + array(), 165 + implode(', ', $changeset->getAwayPaths())); 166 + switch ($file) { 167 + case DifferentialChangeType::FILE_TEXT: 168 + $messages[] = pht('This file was copied to %s.', $paths); 169 + break; 170 + case DifferentialChangeType::FILE_IMAGE: 171 + $messages[] = pht('This image was copied to %s.', $paths); 172 + break; 173 + case DifferentialChangeType::FILE_DIRECTORY: 174 + $messages[] = pht('This directory was copied to %s.', $paths); 175 + break; 176 + case DifferentialChangeType::FILE_BINARY: 177 + $messages[] = pht('This binary file was copied to %s.', $paths); 178 + break; 179 + case DifferentialChangeType::FILE_SYMLINK: 180 + $messages[] = pht('This symlink was copied to %s.', $paths); 181 + break; 182 + case DifferentialChangeType::FILE_SUBMODULE: 183 + $messages[] = pht('This submodule was copied to %s.', $paths); 184 + break; 185 + } 186 + break; 161 187 162 - case DifferentialChangeType::TYPE_COPY_AWAY: 163 - $paths = phutil_tag( 164 - 'strong', 165 - array(), 166 - implode(', ', $changeset->getAwayPaths())); 167 - switch ($file) { 168 - case DifferentialChangeType::FILE_TEXT: 169 - $message = pht('This file was copied to %s.', $paths); 170 - break; 171 - case DifferentialChangeType::FILE_IMAGE: 172 - $message = pht('This image was copied to %s.', $paths); 173 - break; 174 - case DifferentialChangeType::FILE_DIRECTORY: 175 - $message = pht('This directory was copied to %s.', $paths); 176 - break; 177 - case DifferentialChangeType::FILE_BINARY: 178 - $message = pht('This binary file was copied to %s.', $paths); 179 - break; 180 - case DifferentialChangeType::FILE_SYMLINK: 181 - $message = pht('This symlink was copied to %s.', $paths); 182 - break; 183 - case DifferentialChangeType::FILE_SUBMODULE: 184 - $message = pht('This submodule was copied to %s.', $paths); 185 - break; 186 - } 187 - break; 188 + case DifferentialChangeType::TYPE_MULTICOPY: 189 + $paths = phutil_tag( 190 + 'strong', 191 + array(), 192 + implode(', ', $changeset->getAwayPaths())); 193 + switch ($file) { 194 + case DifferentialChangeType::FILE_TEXT: 195 + $messages[] = pht( 196 + 'This file was deleted after being copied to %s.', 197 + $paths); 198 + break; 199 + case DifferentialChangeType::FILE_IMAGE: 200 + $messages[] = pht( 201 + 'This image was deleted after being copied to %s.', 202 + $paths); 203 + break; 204 + case DifferentialChangeType::FILE_DIRECTORY: 205 + $messages[] = pht( 206 + 'This directory was deleted after being copied to %s.', 207 + $paths); 208 + break; 209 + case DifferentialChangeType::FILE_BINARY: 210 + $messages[] = pht( 211 + 'This binary file was deleted after being copied to %s.', 212 + $paths); 213 + break; 214 + case DifferentialChangeType::FILE_SYMLINK: 215 + $messages[] = pht( 216 + 'This symlink was deleted after being copied to %s.', 217 + $paths); 218 + break; 219 + case DifferentialChangeType::FILE_SUBMODULE: 220 + $messages[] = pht( 221 + 'This submodule was deleted after being copied to %s.', 222 + $paths); 223 + break; 224 + } 225 + break; 188 226 189 - case DifferentialChangeType::TYPE_MULTICOPY: 190 - $paths = phutil_tag( 191 - 'strong', 192 - array(), 193 - implode(', ', $changeset->getAwayPaths())); 194 - switch ($file) { 195 - case DifferentialChangeType::FILE_TEXT: 196 - $message = pht( 197 - 'This file was deleted after being copied to %s.', 198 - $paths); 199 - break; 200 - case DifferentialChangeType::FILE_IMAGE: 201 - $message = pht( 202 - 'This image was deleted after being copied to %s.', 203 - $paths); 204 - break; 205 - case DifferentialChangeType::FILE_DIRECTORY: 206 - $message = pht( 207 - 'This directory was deleted after being copied to %s.', 208 - $paths); 209 - break; 210 - case DifferentialChangeType::FILE_BINARY: 211 - $message = pht( 212 - 'This binary file was deleted after being copied to %s.', 213 - $paths); 214 - break; 215 - case DifferentialChangeType::FILE_SYMLINK: 216 - $message = pht( 217 - 'This symlink was deleted after being copied to %s.', 218 - $paths); 219 - break; 220 - case DifferentialChangeType::FILE_SUBMODULE: 221 - $message = pht( 222 - 'This submodule was deleted after being copied to %s.', 223 - $paths); 224 - break; 225 - } 226 - break; 227 + default: 228 + switch ($file) { 229 + case DifferentialChangeType::FILE_TEXT: 230 + // This is the default case, so we only render this header if 231 + // forced to since it's not very useful. 232 + if ($force) { 233 + $messages[] = pht('This file was not modified.'); 234 + } 235 + break; 236 + case DifferentialChangeType::FILE_IMAGE: 237 + $messages[] = pht('This is an image.'); 238 + break; 239 + case DifferentialChangeType::FILE_DIRECTORY: 240 + $messages[] = pht('This is a directory.'); 241 + break; 242 + case DifferentialChangeType::FILE_BINARY: 243 + $messages[] = pht('This is a binary file.'); 244 + break; 245 + case DifferentialChangeType::FILE_SYMLINK: 246 + $messages[] = pht('This is a symlink.'); 247 + break; 248 + case DifferentialChangeType::FILE_SUBMODULE: 249 + $messages[] = pht('This is a submodule.'); 250 + break; 251 + } 252 + break; 253 + } 227 254 228 - default: 229 - switch ($file) { 230 - case DifferentialChangeType::FILE_TEXT: 231 - $message = pht('This is a file.'); 232 - break; 233 - case DifferentialChangeType::FILE_IMAGE: 234 - $message = pht('This is an image.'); 235 - break; 236 - case DifferentialChangeType::FILE_DIRECTORY: 237 - $message = pht('This is a directory.'); 238 - break; 239 - case DifferentialChangeType::FILE_BINARY: 240 - $message = pht('This is a binary file.'); 241 - break; 242 - case DifferentialChangeType::FILE_SYMLINK: 243 - $message = pht('This is a symlink.'); 244 - break; 245 - case DifferentialChangeType::FILE_SUBMODULE: 246 - $message = pht('This is a submodule.'); 247 - break; 248 - } 249 - break; 255 + $encoding = $this->getOriginalCharacterEncoding(); 256 + if ($encoding != 'utf8' && ($file == DifferentialChangeType::FILE_TEXT)) { 257 + if ($encoding) { 258 + $messages[] = pht( 259 + 'This file was converted from %s for display.', 260 + phutil_tag('strong', array(), $encoding)); 261 + } else { 262 + $messages[] = pht( 263 + 'This file uses an unknown character encoding.'); 250 264 } 251 265 } 252 266 253 - return phutil_tag_div('differential-meta-notice', $message); 267 + if (!$messages) { 268 + return null; 269 + } 270 + 271 + foreach ($messages as $key => $message) { 272 + $messages[$key] = phutil_tag('li', array(), $message); 273 + } 274 + 275 + return phutil_tag( 276 + 'ul', 277 + array( 278 + 'class' => 'differential-meta-notice', 279 + ), 280 + $messages); 254 281 } 255 282 256 283 protected function renderPropertyChangeHeader() {
+10
src/applications/differential/render/DifferentialChangesetRenderer.php
··· 28 28 private $gaps; 29 29 private $mask; 30 30 private $depths; 31 + private $originalCharacterEncoding; 32 + 33 + public function setOriginalCharacterEncoding($original_character_encoding) { 34 + $this->originalCharacterEncoding = $original_character_encoding; 35 + return $this; 36 + } 37 + 38 + public function getOriginalCharacterEncoding() { 39 + return $this->originalCharacterEncoding; 40 + } 31 41 32 42 public function setDepths($depths) { 33 43 $this->depths = $depths;
+9
src/applications/differential/storage/DifferentialHunkLegacy.php
··· 8 8 return 'differential_hunk'; 9 9 } 10 10 11 + public function getDataEncoding() { 12 + return 'utf8'; 13 + } 14 + 15 + public function forceEncoding($encoding) { 16 + // Not supported, these are always utf8. 17 + return $this; 18 + } 19 + 11 20 }
+7 -1
src/applications/differential/storage/DifferentialHunkModern.php
··· 14 14 protected $data; 15 15 16 16 private $rawData; 17 + private $forcedEncoding; 17 18 18 19 public function getTableName() { 19 20 return 'differential_hunk_modern'; ··· 41 42 public function getChanges() { 42 43 return $this->getUTF8StringFromStorage( 43 44 $this->getRawData(), 44 - $this->getDataEncoding()); 45 + nonempty($this->forcedEncoding, $this->getDataEncoding())); 46 + } 47 + 48 + public function forceEncoding($encoding) { 49 + $this->forcedEncoding = $encoding; 50 + return $this; 45 51 } 46 52 47 53 public function save() {
+5
src/applications/differential/view/DifferentialChangesetListView.php
··· 127 127 'Show Raw File (Left)' => pht('Show Raw File (Left)'), 128 128 'Show Raw File (Right)' => pht('Show Raw File (Right)'), 129 129 'Configure Editor' => pht('Configure Editor'), 130 + 'Load Changes' => pht('Load Changes'), 131 + 'View Side-by-Side' => pht('View Side-by-Side'), 132 + 'View Unified' => pht('View Unified (Barely Works!)'), 133 + 'Change Text Encoding...' => pht('Change Text Encoding...'), 134 + 'Highlight As...' => pht('Highlight As...'), 130 135 ), 131 136 )); 132 137
+3
src/applications/diffusion/controller/DiffusionDiffController.php
··· 68 68 array( 69 69 'action' => 'rendering-ref'))); 70 70 71 + $parser->setCharacterEncoding($request->getStr('encoding')); 72 + $parser->setHighlightAs($request->getStr('highlight')); 73 + 71 74 $coverage = $drequest->loadCoverage(); 72 75 if ($coverage) { 73 76 $parser->setCoverage($coverage);
+4
src/applications/system/application/PhabricatorApplicationSystem.php
··· 15 15 '/status/' => 'PhabricatorStatusController', 16 16 '/debug/' => 'PhabricatorDebugController', 17 17 '/robots.txt' => 'PhabricatorRobotsController', 18 + '/services/' => array( 19 + 'encoding/' => 'PhabricatorSystemSelectEncodingController', 20 + 'highlight/' => 'PhabricatorSystemSelectHighlightController', 21 + ), 18 22 ); 19 23 } 20 24
+55
src/applications/system/controller/PhabricatorSystemSelectEncodingController.php
··· 1 + <?php 2 + 3 + final class PhabricatorSystemSelectEncodingController 4 + extends PhabricatorController { 5 + 6 + public function shouldRequireLogin() { 7 + return false; 8 + } 9 + 10 + public function processRequest() { 11 + $request = $this->getRequest(); 12 + 13 + if (!function_exists('mb_list_encodings')) { 14 + return $this->newDialog() 15 + ->setTitle(pht('No Encoding Support')) 16 + ->appendParagraph( 17 + pht( 18 + 'This system does not have the "mbstring" extension installed, '. 19 + 'so character encodings are not supported. Install "mbstring" to '. 20 + 'enable support.')) 21 + ->addCancelButton('/'); 22 + } 23 + 24 + if ($request->isFormPost()) { 25 + $result = array('encoding' => $request->getStr('encoding')); 26 + return id(new AphrontAjaxResponse())->setContent($result); 27 + } 28 + 29 + $encodings = mb_list_encodings(); 30 + $encodings = array_fuse($encodings); 31 + asort($encodings); 32 + unset($encodings['pass']); 33 + unset($encodings['auto']); 34 + 35 + $encodings = array( 36 + '' => pht('(Use Default)'), 37 + ) + $encodings; 38 + 39 + $form = id(new AphrontFormView()) 40 + ->setUser($this->getRequest()->getUser()) 41 + ->appendRemarkupInstructions(pht('Choose a text encoding to use.')) 42 + ->appendChild( 43 + id(new AphrontFormSelectControl()) 44 + ->setLabel(pht('Encoding')) 45 + ->setName('encoding') 46 + ->setValue($request->getStr('encoding')) 47 + ->setOptions($encodings)); 48 + 49 + return $this->newDialog() 50 + ->setTitle(pht('Select Character Encoding')) 51 + ->appendChild($form->buildLayoutView()) 52 + ->addSubmitButton(pht('Choose Encoding')) 53 + ->addCancelButton('/'); 54 + } 55 + }
+38
src/applications/system/controller/PhabricatorSystemSelectHighlightController.php
··· 1 + <?php 2 + 3 + final class PhabricatorSystemSelectHighlightController 4 + extends PhabricatorController { 5 + 6 + public function shouldRequireLogin() { 7 + return false; 8 + } 9 + 10 + public function processRequest() { 11 + $request = $this->getRequest(); 12 + 13 + if ($request->isFormPost()) { 14 + $result = array('highlight' => $request->getStr('highlight')); 15 + return id(new AphrontAjaxResponse())->setContent($result); 16 + } 17 + 18 + $languages = array( 19 + '' => pht('(Use Default)'), 20 + ) + PhabricatorEnv::getEnvConfig('pygments.dropdown-choices'); 21 + 22 + $form = id(new AphrontFormView()) 23 + ->setUser($this->getRequest()->getUser()) 24 + ->appendRemarkupInstructions(pht('Choose a syntax highlighting to use.')) 25 + ->appendChild( 26 + id(new AphrontFormSelectControl()) 27 + ->setLabel(pht('Highlighting')) 28 + ->setName('highlight') 29 + ->setValue($request->getStr('highlight')) 30 + ->setOptions($languages)); 31 + 32 + return $this->newDialog() 33 + ->setTitle(pht('Select Syntax Highlighting')) 34 + ->appendChild($form->buildLayoutView()) 35 + ->addSubmitButton(pht('Choose Highlighting')) 36 + ->addCancelButton('/'); 37 + } 38 + }
+23
src/infrastructure/storage/lisk/PhabricatorLiskDAO.php
··· 183 183 if ($encoding == 'utf8') { 184 184 return $string; 185 185 } 186 + 187 + if (function_exists('mb_detect_encoding')) { 188 + if (strlen($encoding)) { 189 + $try_encodings = array( 190 + $encoding, 191 + ); 192 + } else { 193 + // TODO: This is pretty much a guess, and probably needs to be 194 + // configurable in the long run. 195 + $try_encodings = array( 196 + 'JIS', 197 + 'EUC-JP', 198 + 'SJIS', 199 + 'ISO-8859-1', 200 + ); 201 + } 202 + 203 + $guess = mb_detect_encoding($string, $try_encodings); 204 + if ($guess) { 205 + return mb_convert_encoding($string, 'UTF-8', $guess); 206 + } 207 + } 208 + 186 209 return phutil_utf8ize($string); 187 210 } 188 211
+32 -3
webroot/rsrc/js/application/differential/ChangesetViewManager.js
··· 22 22 this._whitespace = data.whitespace; 23 23 this._renderer = data.renderer; 24 24 this._highlight = data.highlight; 25 + this._encoding = data.encoding; 25 26 }, 26 27 27 28 members: { ··· 35 36 _whitespace: null, 36 37 _renderer: null, 37 38 _highlight: null, 39 + _encoding: null, 38 40 39 41 40 42 /** ··· 120 122 121 123 var params = { 122 124 ref: this._ref, 123 - whitespace: this._whitespace, 124 - renderer: this._getRenderer() 125 + whitespace: this._whitespace || '', 126 + renderer: this.getRenderer() || '', 127 + highlight: this._highlight || '', 128 + encoding: this._encoding || '' 125 129 }; 126 130 127 131 var workflow = new JX.Workflow(this._renderURI, params) ··· 160 164 return JX.Router.getInstance().getRoutableByKey(this._getRoutableKey()); 161 165 }, 162 166 167 + setRenderer: function(renderer) { 168 + this._renderer = renderer; 169 + return this; 170 + }, 163 171 164 - _getRenderer: function() { 172 + getRenderer: function() { 173 + if (this._renderer !== null) { 174 + return this._renderer; 175 + } 176 + 165 177 // TODO: This is a big pile of TODOs. 166 178 167 179 // NOTE: If you load the page at one device resolution and then resize to ··· 176 188 return renderer; 177 189 }, 178 190 191 + setEncoding: function(encoding) { 192 + this._encoding = encoding; 193 + return this; 194 + }, 195 + 196 + getEncoding: function() { 197 + return this._encoding; 198 + }, 199 + 200 + setHighlight: function(highlight) { 201 + this._highlight = highlight; 202 + return this; 203 + }, 204 + 205 + getHighlight: function() { 206 + return this._highlight; 207 + }, 179 208 180 209 _getNodeData: function() { 181 210 return JX.Stratcom.getData(this._node);
+70 -21
webroot/rsrc/js/application/differential/behavior-dropdown-menus.js
··· 4 4 * javelin-dom 5 5 * javelin-util 6 6 * javelin-stratcom 7 + * javelin-workflow 7 8 * phuix-dropdown-menu 8 9 * phuix-action-list-view 9 10 * phuix-action-view ··· 48 49 var buildmenu = function(e) { 49 50 var button = e.getNode('differential-view-options'); 50 51 var data = JX.Stratcom.getData(button); 51 - 52 52 if (data.menu) { 53 53 return; 54 54 } 55 55 56 56 e.prevent(); 57 57 58 + var changeset = JX.DOM.findAbove( 59 + button, 60 + 'div', 61 + 'differential-changeset'); 62 + 63 + var view = JX.ChangesetViewManager.getForNode(changeset); 58 64 var menu = new JX.PHUIXDropdownMenu(button); 59 65 var list = new JX.PHUIXActionListView(); 60 66 ··· 103 109 104 110 var up_item = new JX.PHUIXActionView() 105 111 .setHandler(function(e) { 106 - var changeset = JX.DOM.findAbove( 107 - button, 108 - 'div', 109 - 'differential-changeset'); 110 - 111 - var view = JX.ChangesetViewManager.getForNode(changeset); 112 + if (view.isLoaded()) { 113 + var renderer = view.getRenderer(); 114 + if (renderer == '1up') { 115 + renderer = '2up'; 116 + } else { 117 + renderer = '1up'; 118 + } 119 + view.setRenderer(renderer); 120 + } 112 121 view.reload(); 113 122 114 123 e.prevent(); ··· 116 125 }); 117 126 list.addItem(up_item); 118 127 128 + var encoding_item = new JX.PHUIXActionView() 129 + .setIcon('fa-font') 130 + .setName(pht('Change Text Encoding...')) 131 + .setHandler(function(e) { 132 + var params = { 133 + encoding: view.getEncoding() 134 + }; 135 + 136 + new JX.Workflow('/services/encoding/', params) 137 + .setHandler(function(r) { 138 + view.setEncoding(r.encoding); 139 + view.reload(); 140 + }) 141 + .start(); 142 + 143 + e.prevent(); 144 + menu.close(); 145 + }); 146 + list.addItem(encoding_item); 147 + 148 + var highlight_item = new JX.PHUIXActionView() 149 + .setIcon('fa-sun-o') 150 + .setName(pht('Highlight As...')) 151 + .setHandler(function(e) { 152 + var params = { 153 + highlight: view.getHighlight() 154 + }; 155 + 156 + new JX.Workflow('/services/highlight/', params) 157 + .setHandler(function(r) { 158 + view.setHighlight(r.highlight); 159 + view.reload(); 160 + }) 161 + .start(); 162 + 163 + e.prevent(); 164 + menu.close(); 165 + }); 166 + list.addItem(highlight_item); 167 + 119 168 add_link('fa-arrow-left', pht('Show Raw File (Left)'), data.leftURI); 120 169 add_link('fa-arrow-right', pht('Show Raw File (Right)'), data.rightURI); 121 170 add_link('fa-pencil', pht('Open in Editor'), data.editor, true); 122 171 add_link('fa-wrench', pht('Configure Editor'), data.editorConfigure); 123 - 124 172 125 173 menu.setContent(list.getNode()); 126 174 127 175 menu.listen('open', function() { 128 - var changeset = JX.DOM.findAbove( 129 - button, 130 - 'div', 131 - 'differential-changeset'); 132 - 133 - var view = JX.ChangesetViewManager.getForNode(changeset); 134 - 135 176 // When the user opens the menu, check if there are any "Show More" 136 177 // links in the changeset body. If there aren't, disable the "Show 137 178 // Entire File" menu item since it won't change anything. ··· 155 196 .setHandler(function(e) { e.prevent(); }); 156 197 } 157 198 158 - // TODO: This is temporary and just makes testing easier. It will do 159 - // some mojo soon. 199 + encoding_item.setDisabled(!view.isLoaded()); 200 + highlight_item.setDisabled(!view.isLoaded()); 201 + 160 202 if (view.isLoaded()) { 161 - up_item 162 - .setIcon('fa-refresh') 163 - .setName('Reload'); 203 + if (view.getRenderer() == '2up') { 204 + up_item 205 + .setIcon('fa-list-alt') 206 + .setName(pht('View Unified')); 207 + } else { 208 + up_item 209 + .setIcon('fa-files-o') 210 + .setName(pht('View Side-by-Side')); 211 + } 164 212 } else { 165 213 up_item 166 214 .setIcon('fa-refresh') 167 - .setName('Load'); 215 + .setName(pht('Load Changes')); 168 216 } 169 217 170 218 visible_item ··· 198 246 } 199 247 200 248 }); 249 + 201 250 data.menu = menu; 202 251 menu.open(); 203 252 };