Merge branch 'master' into blender-tweaks
This commit is contained in:
		| @@ -9,10 +9,10 @@ return array( | |||||||
|   'names' => array( |   'names' => array( | ||||||
|     'conpherence.pkg.css' => 'e68cf1fa', |     'conpherence.pkg.css' => 'e68cf1fa', | ||||||
|     'conpherence.pkg.js' => '15191c65', |     'conpherence.pkg.js' => '15191c65', | ||||||
|     'core.pkg.css' => '70cc8c80', |     'core.pkg.css' => '84d8ab9a', | ||||||
|     'core.pkg.js' => 'e1f0f7bd', |     'core.pkg.js' => '2058ec09', | ||||||
|     'differential.pkg.css' => '06dc617c', |     'differential.pkg.css' => '06dc617c', | ||||||
|     'differential.pkg.js' => 'c2ca903a', |     'differential.pkg.js' => 'ef19e026', | ||||||
|     'diffusion.pkg.css' => 'a2d17c7d', |     'diffusion.pkg.css' => 'a2d17c7d', | ||||||
|     'diffusion.pkg.js' => '6134c5a1', |     'diffusion.pkg.js' => '6134c5a1', | ||||||
|     'maniphest.pkg.css' => '4845691a', |     'maniphest.pkg.css' => '4845691a', | ||||||
| @@ -111,7 +111,7 @@ return array( | |||||||
|     'rsrc/css/application/tokens/tokens.css' => '3d0f239e', |     'rsrc/css/application/tokens/tokens.css' => '3d0f239e', | ||||||
|     'rsrc/css/application/uiexample/example.css' => '528b19de', |     'rsrc/css/application/uiexample/example.css' => '528b19de', | ||||||
|     'rsrc/css/core/core.css' => '62fa3ace', |     'rsrc/css/core/core.css' => '62fa3ace', | ||||||
|     'rsrc/css/core/remarkup.css' => 'bff43c81', |     'rsrc/css/core/remarkup.css' => 'b182076e', | ||||||
|     'rsrc/css/core/syntax.css' => 'e9c95dd4', |     'rsrc/css/core/syntax.css' => 'e9c95dd4', | ||||||
|     'rsrc/css/core/z-index.css' => '9d8f7c4b', |     'rsrc/css/core/z-index.css' => '9d8f7c4b', | ||||||
|     'rsrc/css/diviner/diviner-shared.css' => '896f1d43', |     'rsrc/css/diviner/diviner-shared.css' => '896f1d43', | ||||||
| @@ -144,7 +144,7 @@ return array( | |||||||
|     'rsrc/css/phui/phui-cms.css' => '504b4b23', |     'rsrc/css/phui/phui-cms.css' => '504b4b23', | ||||||
|     'rsrc/css/phui/phui-comment-form.css' => 'ac68149f', |     'rsrc/css/phui/phui-comment-form.css' => 'ac68149f', | ||||||
|     'rsrc/css/phui/phui-comment-panel.css' => 'f50152ad', |     'rsrc/css/phui/phui-comment-panel.css' => 'f50152ad', | ||||||
|     'rsrc/css/phui/phui-crumbs-view.css' => '6ece3bbb', |     'rsrc/css/phui/phui-crumbs-view.css' => '10728aaa', | ||||||
|     'rsrc/css/phui/phui-curtain-view.css' => '2bdaf026', |     'rsrc/css/phui/phui-curtain-view.css' => '2bdaf026', | ||||||
|     'rsrc/css/phui/phui-document-pro.css' => '8af7ea27', |     'rsrc/css/phui/phui-document-pro.css' => '8af7ea27', | ||||||
|     'rsrc/css/phui/phui-document-summary.css' => '9ca48bdf', |     'rsrc/css/phui/phui-document-summary.css' => '9ca48bdf', | ||||||
| @@ -154,7 +154,7 @@ return array( | |||||||
|     'rsrc/css/phui/phui-form-view.css' => 'b446e8ff', |     'rsrc/css/phui/phui-form-view.css' => 'b446e8ff', | ||||||
|     'rsrc/css/phui/phui-form.css' => '7aaa04e3', |     'rsrc/css/phui/phui-form.css' => '7aaa04e3', | ||||||
|     'rsrc/css/phui/phui-head-thing.css' => 'fd311e5f', |     'rsrc/css/phui/phui-head-thing.css' => 'fd311e5f', | ||||||
|     'rsrc/css/phui/phui-header-view.css' => '31dc6c72', |     'rsrc/css/phui/phui-header-view.css' => 'edeb9252', | ||||||
|     'rsrc/css/phui/phui-hovercard.css' => 'f0592bcf', |     'rsrc/css/phui/phui-hovercard.css' => 'f0592bcf', | ||||||
|     'rsrc/css/phui/phui-icon-set-selector.css' => '87db8fee', |     'rsrc/css/phui/phui-icon-set-selector.css' => '87db8fee', | ||||||
|     'rsrc/css/phui/phui-icon.css' => 'cf24ceec', |     'rsrc/css/phui/phui-icon.css' => 'cf24ceec', | ||||||
| @@ -167,7 +167,7 @@ return array( | |||||||
|     'rsrc/css/phui/phui-object-box.css' => '9cff003c', |     'rsrc/css/phui/phui-object-box.css' => '9cff003c', | ||||||
|     'rsrc/css/phui/phui-pager.css' => 'edcbc226', |     'rsrc/css/phui/phui-pager.css' => 'edcbc226', | ||||||
|     'rsrc/css/phui/phui-pinboard-view.css' => '2495140e', |     'rsrc/css/phui/phui-pinboard-view.css' => '2495140e', | ||||||
|     'rsrc/css/phui/phui-property-list-view.css' => 'de4754d8', |     'rsrc/css/phui/phui-property-list-view.css' => '546a04ae', | ||||||
|     'rsrc/css/phui/phui-remarkup-preview.css' => '54a34863', |     'rsrc/css/phui/phui-remarkup-preview.css' => '54a34863', | ||||||
|     'rsrc/css/phui/phui-segment-bar-view.css' => 'b1d1b892', |     'rsrc/css/phui/phui-segment-bar-view.css' => 'b1d1b892', | ||||||
|     'rsrc/css/phui/phui-spacing.css' => '042804d6', |     'rsrc/css/phui/phui-spacing.css' => '042804d6', | ||||||
| @@ -261,7 +261,7 @@ return array( | |||||||
|     'rsrc/externals/javelin/lib/__tests__/URI.js' => '1e45fda9', |     'rsrc/externals/javelin/lib/__tests__/URI.js' => '1e45fda9', | ||||||
|     'rsrc/externals/javelin/lib/__tests__/behavior.js' => '1ea62783', |     'rsrc/externals/javelin/lib/__tests__/behavior.js' => '1ea62783', | ||||||
|     'rsrc/externals/javelin/lib/behavior.js' => '61cbc29a', |     'rsrc/externals/javelin/lib/behavior.js' => '61cbc29a', | ||||||
|     'rsrc/externals/javelin/lib/control/tokenizer/Tokenizer.js' => '8d3bc1b2', |     'rsrc/externals/javelin/lib/control/tokenizer/Tokenizer.js' => 'bb6e5c16', | ||||||
|     'rsrc/externals/javelin/lib/control/typeahead/Typeahead.js' => '70baed2f', |     'rsrc/externals/javelin/lib/control/typeahead/Typeahead.js' => '70baed2f', | ||||||
|     'rsrc/externals/javelin/lib/control/typeahead/normalizer/TypeaheadNormalizer.js' => '185bbd53', |     'rsrc/externals/javelin/lib/control/typeahead/normalizer/TypeaheadNormalizer.js' => '185bbd53', | ||||||
|     'rsrc/externals/javelin/lib/control/typeahead/source/TypeaheadCompositeSource.js' => '503e17fd', |     'rsrc/externals/javelin/lib/control/typeahead/source/TypeaheadCompositeSource.js' => '503e17fd', | ||||||
| @@ -375,7 +375,7 @@ return array( | |||||||
|     'rsrc/js/application/dashboard/behavior-dashboard-query-panel-select.js' => '453c5375', |     'rsrc/js/application/dashboard/behavior-dashboard-query-panel-select.js' => '453c5375', | ||||||
|     'rsrc/js/application/dashboard/behavior-dashboard-tab-panel.js' => 'd4eecc63', |     'rsrc/js/application/dashboard/behavior-dashboard-tab-panel.js' => 'd4eecc63', | ||||||
|     'rsrc/js/application/diff/DiffChangeset.js' => 'b49b59d6', |     'rsrc/js/application/diff/DiffChangeset.js' => 'b49b59d6', | ||||||
|     'rsrc/js/application/diff/DiffChangesetList.js' => 'e74b7517', |     'rsrc/js/application/diff/DiffChangesetList.js' => 'f0ffe8c3', | ||||||
|     'rsrc/js/application/diff/DiffInline.js' => 'e83d28f3', |     'rsrc/js/application/diff/DiffInline.js' => 'e83d28f3', | ||||||
|     'rsrc/js/application/diff/behavior-preview-link.js' => '051c7832', |     'rsrc/js/application/diff/behavior-preview-link.js' => '051c7832', | ||||||
|     'rsrc/js/application/differential/behavior-comment-preview.js' => '51c5ad07', |     'rsrc/js/application/differential/behavior-comment-preview.js' => '51c5ad07', | ||||||
| @@ -390,10 +390,10 @@ return array( | |||||||
|     'rsrc/js/application/diffusion/behavior-pull-lastmodified.js' => 'f01586dc', |     'rsrc/js/application/diffusion/behavior-pull-lastmodified.js' => 'f01586dc', | ||||||
|     'rsrc/js/application/doorkeeper/behavior-doorkeeper-tag.js' => '1db13e70', |     'rsrc/js/application/doorkeeper/behavior-doorkeeper-tag.js' => '1db13e70', | ||||||
|     'rsrc/js/application/drydock/drydock-live-operation-status.js' => '901935ef', |     'rsrc/js/application/drydock/drydock-live-operation-status.js' => '901935ef', | ||||||
|     'rsrc/js/application/files/behavior-document-engine.js' => 'ee0deff8', |     'rsrc/js/application/files/behavior-document-engine.js' => '3935d8c4', | ||||||
|     'rsrc/js/application/files/behavior-icon-composer.js' => '8499b6ab', |     'rsrc/js/application/files/behavior-icon-composer.js' => '8499b6ab', | ||||||
|     'rsrc/js/application/files/behavior-launch-icon-composer.js' => '48086888', |     'rsrc/js/application/files/behavior-launch-icon-composer.js' => '48086888', | ||||||
|     'rsrc/js/application/harbormaster/behavior-harbormaster-log.js' => '191b4909', |     'rsrc/js/application/harbormaster/behavior-harbormaster-log.js' => '549459b8', | ||||||
|     'rsrc/js/application/herald/HeraldRuleEditor.js' => 'dca75c0e', |     'rsrc/js/application/herald/HeraldRuleEditor.js' => 'dca75c0e', | ||||||
|     'rsrc/js/application/herald/PathTypeahead.js' => '662e9cea', |     'rsrc/js/application/herald/PathTypeahead.js' => '662e9cea', | ||||||
|     'rsrc/js/application/herald/herald-rule-editor.js' => '7ebaeed3', |     'rsrc/js/application/herald/herald-rule-editor.js' => '7ebaeed3', | ||||||
| @@ -602,7 +602,7 @@ return array( | |||||||
|     'javelin-behavior-diffusion-commit-graph' => '75b83cbb', |     'javelin-behavior-diffusion-commit-graph' => '75b83cbb', | ||||||
|     'javelin-behavior-diffusion-locate-file' => '6d3e1947', |     'javelin-behavior-diffusion-locate-file' => '6d3e1947', | ||||||
|     'javelin-behavior-diffusion-pull-lastmodified' => 'f01586dc', |     'javelin-behavior-diffusion-pull-lastmodified' => 'f01586dc', | ||||||
|     'javelin-behavior-document-engine' => 'ee0deff8', |     'javelin-behavior-document-engine' => '3935d8c4', | ||||||
|     'javelin-behavior-doorkeeper-tag' => '1db13e70', |     'javelin-behavior-doorkeeper-tag' => '1db13e70', | ||||||
|     'javelin-behavior-drydock-live-operation-status' => '901935ef', |     'javelin-behavior-drydock-live-operation-status' => '901935ef', | ||||||
|     'javelin-behavior-durable-column' => '2ae077e1', |     'javelin-behavior-durable-column' => '2ae077e1', | ||||||
| @@ -611,7 +611,7 @@ return array( | |||||||
|     'javelin-behavior-event-all-day' => 'b41537c9', |     'javelin-behavior-event-all-day' => 'b41537c9', | ||||||
|     'javelin-behavior-fancy-datepicker' => 'ecf4e799', |     'javelin-behavior-fancy-datepicker' => 'ecf4e799', | ||||||
|     'javelin-behavior-global-drag-and-drop' => '960f6a39', |     'javelin-behavior-global-drag-and-drop' => '960f6a39', | ||||||
|     'javelin-behavior-harbormaster-log' => '191b4909', |     'javelin-behavior-harbormaster-log' => '549459b8', | ||||||
|     'javelin-behavior-herald-rule-editor' => '7ebaeed3', |     'javelin-behavior-herald-rule-editor' => '7ebaeed3', | ||||||
|     'javelin-behavior-high-security-warning' => 'a464fe03', |     'javelin-behavior-high-security-warning' => 'a464fe03', | ||||||
|     'javelin-behavior-history-install' => '7ee2b591', |     'javelin-behavior-history-install' => '7ee2b591', | ||||||
| @@ -712,7 +712,7 @@ return array( | |||||||
|     'javelin-scrollbar' => '9065f639', |     'javelin-scrollbar' => '9065f639', | ||||||
|     'javelin-sound' => '949c0fe5', |     'javelin-sound' => '949c0fe5', | ||||||
|     'javelin-stratcom' => '327f418a', |     'javelin-stratcom' => '327f418a', | ||||||
|     'javelin-tokenizer' => '8d3bc1b2', |     'javelin-tokenizer' => 'bb6e5c16', | ||||||
|     'javelin-typeahead' => '70baed2f', |     'javelin-typeahead' => '70baed2f', | ||||||
|     'javelin-typeahead-composite-source' => '503e17fd', |     'javelin-typeahead-composite-source' => '503e17fd', | ||||||
|     'javelin-typeahead-normalizer' => '185bbd53', |     'javelin-typeahead-normalizer' => '185bbd53', | ||||||
| @@ -754,7 +754,7 @@ return array( | |||||||
|     'phabricator-darkmessage' => 'c48cccdd', |     'phabricator-darkmessage' => 'c48cccdd', | ||||||
|     'phabricator-dashboard-css' => 'fe5b1869', |     'phabricator-dashboard-css' => 'fe5b1869', | ||||||
|     'phabricator-diff-changeset' => 'b49b59d6', |     'phabricator-diff-changeset' => 'b49b59d6', | ||||||
|     'phabricator-diff-changeset-list' => 'e74b7517', |     'phabricator-diff-changeset-list' => 'f0ffe8c3', | ||||||
|     'phabricator-diff-inline' => 'e83d28f3', |     'phabricator-diff-inline' => 'e83d28f3', | ||||||
|     'phabricator-drag-and-drop-file-upload' => '58dea2fa', |     'phabricator-drag-and-drop-file-upload' => '58dea2fa', | ||||||
|     'phabricator-draggable-list' => 'bea6e7f4', |     'phabricator-draggable-list' => 'bea6e7f4', | ||||||
| @@ -774,7 +774,7 @@ return array( | |||||||
|     'phabricator-object-selector-css' => '85ee8ce6', |     'phabricator-object-selector-css' => '85ee8ce6', | ||||||
|     'phabricator-phtize' => 'd254d646', |     'phabricator-phtize' => 'd254d646', | ||||||
|     'phabricator-prefab' => '77b0ae28', |     'phabricator-prefab' => '77b0ae28', | ||||||
|     'phabricator-remarkup-css' => 'bff43c81', |     'phabricator-remarkup-css' => 'b182076e', | ||||||
|     'phabricator-search-results-css' => '505dd8cf', |     'phabricator-search-results-css' => '505dd8cf', | ||||||
|     'phabricator-shaped-request' => '7cbe244b', |     'phabricator-shaped-request' => '7cbe244b', | ||||||
|     'phabricator-slowvote-css' => 'a94b7230', |     'phabricator-slowvote-css' => 'a94b7230', | ||||||
| @@ -813,7 +813,7 @@ return array( | |||||||
|     'phui-cms-css' => '504b4b23', |     'phui-cms-css' => '504b4b23', | ||||||
|     'phui-comment-form-css' => 'ac68149f', |     'phui-comment-form-css' => 'ac68149f', | ||||||
|     'phui-comment-panel-css' => 'f50152ad', |     'phui-comment-panel-css' => 'f50152ad', | ||||||
|     'phui-crumbs-view-css' => '6ece3bbb', |     'phui-crumbs-view-css' => '10728aaa', | ||||||
|     'phui-curtain-view-css' => '2bdaf026', |     'phui-curtain-view-css' => '2bdaf026', | ||||||
|     'phui-document-summary-view-css' => '9ca48bdf', |     'phui-document-summary-view-css' => '9ca48bdf', | ||||||
|     'phui-document-view-css' => '878c2f52', |     'phui-document-view-css' => '878c2f52', | ||||||
| @@ -824,7 +824,7 @@ return array( | |||||||
|     'phui-form-css' => '7aaa04e3', |     'phui-form-css' => '7aaa04e3', | ||||||
|     'phui-form-view-css' => 'b446e8ff', |     'phui-form-view-css' => 'b446e8ff', | ||||||
|     'phui-head-thing-view-css' => 'fd311e5f', |     'phui-head-thing-view-css' => 'fd311e5f', | ||||||
|     'phui-header-view-css' => '31dc6c72', |     'phui-header-view-css' => 'edeb9252', | ||||||
|     'phui-hovercard' => '1bd28176', |     'phui-hovercard' => '1bd28176', | ||||||
|     'phui-hovercard-view-css' => 'f0592bcf', |     'phui-hovercard-view-css' => 'f0592bcf', | ||||||
|     'phui-icon-set-selector-css' => '87db8fee', |     'phui-icon-set-selector-css' => '87db8fee', | ||||||
| @@ -845,7 +845,7 @@ return array( | |||||||
|     'phui-oi-simple-ui-css' => 'a8beebea', |     'phui-oi-simple-ui-css' => 'a8beebea', | ||||||
|     'phui-pager-css' => 'edcbc226', |     'phui-pager-css' => 'edcbc226', | ||||||
|     'phui-pinboard-view-css' => '2495140e', |     'phui-pinboard-view-css' => '2495140e', | ||||||
|     'phui-property-list-view-css' => 'de4754d8', |     'phui-property-list-view-css' => '546a04ae', | ||||||
|     'phui-remarkup-preview-css' => '54a34863', |     'phui-remarkup-preview-css' => '54a34863', | ||||||
|     'phui-segment-bar-view-css' => 'b1d1b892', |     'phui-segment-bar-view-css' => 'b1d1b892', | ||||||
|     'phui-spacing-css' => '042804d6', |     'phui-spacing-css' => '042804d6', | ||||||
| @@ -960,9 +960,6 @@ return array( | |||||||
|     '185bbd53' => array( |     '185bbd53' => array( | ||||||
|       'javelin-install', |       'javelin-install', | ||||||
|     ), |     ), | ||||||
|     '191b4909' => array( |  | ||||||
|       'javelin-behavior', |  | ||||||
|     ), |  | ||||||
|     '1ad0a787' => array( |     '1ad0a787' => array( | ||||||
|       'javelin-install', |       'javelin-install', | ||||||
|       'javelin-reactor', |       'javelin-reactor', | ||||||
| @@ -1083,6 +1080,11 @@ return array( | |||||||
|       'javelin-dom', |       'javelin-dom', | ||||||
|       'javelin-vector', |       'javelin-vector', | ||||||
|     ), |     ), | ||||||
|  |     '3935d8c4' => array( | ||||||
|  |       'javelin-behavior', | ||||||
|  |       'javelin-dom', | ||||||
|  |       'javelin-stratcom', | ||||||
|  |     ), | ||||||
|     '3ab51e2c' => array( |     '3ab51e2c' => array( | ||||||
|       'javelin-behavior', |       'javelin-behavior', | ||||||
|       'javelin-behavior-device', |       'javelin-behavior-device', | ||||||
| @@ -1257,6 +1259,9 @@ return array( | |||||||
|       'javelin-vector', |       'javelin-vector', | ||||||
|       'javelin-typeahead-static-source', |       'javelin-typeahead-static-source', | ||||||
|     ), |     ), | ||||||
|  |     '549459b8' => array( | ||||||
|  |       'javelin-behavior', | ||||||
|  |     ), | ||||||
|     '54b612ba' => array( |     '54b612ba' => array( | ||||||
|       'javelin-color', |       'javelin-color', | ||||||
|       'javelin-install', |       'javelin-install', | ||||||
| @@ -1571,12 +1576,6 @@ return array( | |||||||
|       'javelin-stratcom', |       'javelin-stratcom', | ||||||
|       'javelin-behavior', |       'javelin-behavior', | ||||||
|     ), |     ), | ||||||
|     '8d3bc1b2' => array( |  | ||||||
|       'javelin-dom', |  | ||||||
|       'javelin-util', |  | ||||||
|       'javelin-stratcom', |  | ||||||
|       'javelin-install', |  | ||||||
|     ), |  | ||||||
|     '8d4a8c72' => array( |     '8d4a8c72' => array( | ||||||
|       'javelin-install', |       'javelin-install', | ||||||
|       'javelin-dom', |       'javelin-dom', | ||||||
| @@ -1846,6 +1845,12 @@ return array( | |||||||
|       'javelin-uri', |       'javelin-uri', | ||||||
|       'phabricator-notification', |       'phabricator-notification', | ||||||
|     ), |     ), | ||||||
|  |     'bb6e5c16' => array( | ||||||
|  |       'javelin-dom', | ||||||
|  |       'javelin-util', | ||||||
|  |       'javelin-stratcom', | ||||||
|  |       'javelin-install', | ||||||
|  |     ), | ||||||
|     'bcaccd64' => array( |     'bcaccd64' => array( | ||||||
|       'javelin-behavior', |       'javelin-behavior', | ||||||
|       'javelin-behavior-device', |       'javelin-behavior-device', | ||||||
| @@ -2066,10 +2071,6 @@ return array( | |||||||
|       'javelin-behavior', |       'javelin-behavior', | ||||||
|       'javelin-dom', |       'javelin-dom', | ||||||
|     ), |     ), | ||||||
|     'e74b7517' => array( |  | ||||||
|       'javelin-install', |  | ||||||
|       'phuix-button-view', |  | ||||||
|     ), |  | ||||||
|     'e83d28f3' => array( |     'e83d28f3' => array( | ||||||
|       'javelin-dom', |       'javelin-dom', | ||||||
|     ), |     ), | ||||||
| @@ -2108,11 +2109,6 @@ return array( | |||||||
|       'javelin-behavior', |       'javelin-behavior', | ||||||
|       'javelin-uri', |       'javelin-uri', | ||||||
|     ), |     ), | ||||||
|     'ee0deff8' => array( |  | ||||||
|       'javelin-behavior', |  | ||||||
|       'javelin-dom', |  | ||||||
|       'javelin-stratcom', |  | ||||||
|     ), |  | ||||||
|     'efe49472' => array( |     'efe49472' => array( | ||||||
|       'javelin-install', |       'javelin-install', | ||||||
|       'javelin-util', |       'javelin-util', | ||||||
| @@ -2124,6 +2120,10 @@ return array( | |||||||
|       'javelin-workflow', |       'javelin-workflow', | ||||||
|       'javelin-json', |       'javelin-json', | ||||||
|     ), |     ), | ||||||
|  |     'f0ffe8c3' => array( | ||||||
|  |       'javelin-install', | ||||||
|  |       'phuix-button-view', | ||||||
|  |     ), | ||||||
|     'f1ff5494' => array( |     'f1ff5494' => array( | ||||||
|       'phui-button-css', |       'phui-button-css', | ||||||
|       'phui-button-simple-css', |       'phui-button-simple-css', | ||||||
|   | |||||||
| @@ -0,0 +1,8 @@ | |||||||
|  | CREATE TABLE {$NAMESPACE}_metamta.metamta_mailproperties ( | ||||||
|  |   id INT UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY, | ||||||
|  |   objectPHID VARBINARY(64) NOT NULL, | ||||||
|  |   mailProperties LONGTEXT NOT NULL COLLATE {$COLLATE_TEXT}, | ||||||
|  |   dateCreated INT UNSIGNED NOT NULL, | ||||||
|  |   dateModified INT UNSIGNED NOT NULL, | ||||||
|  |   UNIQUE KEY `key_object` (objectPHID) | ||||||
|  | ) ENGINE=InnoDB, COLLATE {$COLLATE_TEXT}; | ||||||
							
								
								
									
										14
									
								
								resources/sql/autopatches/20180430.repo_identity.sql
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										14
									
								
								resources/sql/autopatches/20180430.repo_identity.sql
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,14 @@ | |||||||
|  | CREATE TABLE {$NAMESPACE}_repository.repository_identity ( | ||||||
|  |   id INT UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY, | ||||||
|  |   phid VARBINARY(64) NOT NULL, | ||||||
|  |   dateCreated INT UNSIGNED NOT NULL, | ||||||
|  |   dateModified INT UNSIGNED NOT NULL, | ||||||
|  |   automaticGuessedUserPHID VARBINARY(64) DEFAULT NULL, | ||||||
|  |   manuallySetUserPHID VARBINARY(64) DEFAULT NULL, | ||||||
|  |   currentEffectiveUserPHID VARBINARY(64) DEFAULT NULL, | ||||||
|  |   identityNameHash BINARY(12) NOT NULL, | ||||||
|  |   identityNameRaw LONGBLOB NOT NULL, | ||||||
|  |   identityNameEncoding VARCHAR(16) DEFAULT NULL COLLATE {$COLLATE_TEXT}, | ||||||
|  |   UNIQUE KEY `key_phid` (phid), | ||||||
|  |   UNIQUE KEY `key_identity` (identityNameHash) | ||||||
|  | ) ENGINE=InnoDB DEFAULT CHARSET={$CHARSET} COLLATE={$COLLATE_TEXT}; | ||||||
							
								
								
									
										26
									
								
								resources/sql/autopatches/20180504.owners.01.mailkey.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										26
									
								
								resources/sql/autopatches/20180504.owners.01.mailkey.php
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,26 @@ | |||||||
|  | <?php | ||||||
|  |  | ||||||
|  | $packages_table = new PhabricatorOwnersPackage(); | ||||||
|  | $packages_conn = $packages_table->establishConnection('w'); | ||||||
|  | $packages_name = $packages_table->getTableName(); | ||||||
|  |  | ||||||
|  | $properties_table = new PhabricatorMetaMTAMailProperties(); | ||||||
|  | $conn = $properties_table->establishConnection('w'); | ||||||
|  |  | ||||||
|  | $iterator = new LiskRawMigrationIterator($packages_conn, $packages_name); | ||||||
|  | foreach ($iterator as $package) { | ||||||
|  |   queryfx( | ||||||
|  |     $conn, | ||||||
|  |     'INSERT IGNORE INTO %T | ||||||
|  |         (objectPHID, mailProperties, dateCreated, dateModified) | ||||||
|  |       VALUES | ||||||
|  |         (%s, %s, %d, %d)', | ||||||
|  |     $properties_table->getTableName(), | ||||||
|  |     $package['phid'], | ||||||
|  |     phutil_json_encode( | ||||||
|  |       array( | ||||||
|  |         'mailKey' => $package['mailKey'], | ||||||
|  |       )), | ||||||
|  |     PhabricatorTime::getNow(), | ||||||
|  |     PhabricatorTime::getNow()); | ||||||
|  | } | ||||||
							
								
								
									
										2
									
								
								resources/sql/autopatches/20180504.owners.02.rmkey.sql
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										2
									
								
								resources/sql/autopatches/20180504.owners.02.rmkey.sql
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,2 @@ | |||||||
|  | ALTER TABLE {$NAMESPACE}_owners.owners_package | ||||||
|  |   DROP mailKey; | ||||||
| @@ -0,0 +1,2 @@ | |||||||
|  | ALTER TABLE {$NAMESPACE}_owners.owners_package | ||||||
|  |   ADD properties LONGTEXT NOT NULL COLLATE {$COLLATE_TEXT}; | ||||||
							
								
								
									
										2
									
								
								resources/sql/autopatches/20180504.owners.04.default.sql
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										2
									
								
								resources/sql/autopatches/20180504.owners.04.default.sql
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,2 @@ | |||||||
|  | UPDATE {$NAMESPACE}_owners.owners_package | ||||||
|  |   SET properties = '{}' WHERE properties = ''; | ||||||
| @@ -0,0 +1,2 @@ | |||||||
|  | ALTER TABLE {$NAMESPACE}_repository.repository_identity | ||||||
|  |   ADD COLUMN authorPHID VARBINARY(64) NOT NULL; | ||||||
							
								
								
									
										19
									
								
								resources/sql/autopatches/20180504.repo_identity.xaction.sql
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										19
									
								
								resources/sql/autopatches/20180504.repo_identity.xaction.sql
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,19 @@ | |||||||
|  | CREATE TABLE {$NAMESPACE}_repository.repository_identitytransaction ( | ||||||
|  |   id INT UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY, | ||||||
|  |   phid VARBINARY(64) NOT NULL, | ||||||
|  |   authorPHID VARBINARY(64) NOT NULL, | ||||||
|  |   objectPHID VARBINARY(64) NOT NULL, | ||||||
|  |   viewPolicy VARBINARY(64) NOT NULL, | ||||||
|  |   editPolicy VARBINARY(64) NOT NULL, | ||||||
|  |   commentPHID VARBINARY(64) DEFAULT NULL, | ||||||
|  |   commentVersion INT UNSIGNED NOT NULL, | ||||||
|  |   transactionType VARCHAR(32) NOT NULL, | ||||||
|  |   oldValue LONGTEXT NOT NULL, | ||||||
|  |   newValue LONGTEXT NOT NULL, | ||||||
|  |   contentSource LONGTEXT NOT NULL, | ||||||
|  |   metadata LONGTEXT NOT NULL, | ||||||
|  |   dateCreated INT UNSIGNED NOT NULL, | ||||||
|  |   dateModified INT UNSIGNED NOT NULL, | ||||||
|  |   UNIQUE KEY `key_phid` (`phid`), | ||||||
|  |   KEY `key_object` (`objectPHID`) | ||||||
|  | ) ENGINE=InnoDB DEFAULT CHARSET={$CHARSET} COLLATE {$COLLATE_TEXT}; | ||||||
| @@ -0,0 +1,3 @@ | |||||||
|  | ALTER TABLE {$NAMESPACE}_repository.repository_commit | ||||||
|  |   ADD COLUMN authorIdentityPHID VARBINARY(64) DEFAULT NULL, | ||||||
|  |   ADD COLUMN committerIdentityPHID VARBINARY(64) DEFAULT NULL; | ||||||
| @@ -0,0 +1,2 @@ | |||||||
|  | ALTER TABLE {$NAMESPACE}_phriction.phriction_document | ||||||
|  |   ADD spacePHID VARBINARY(64) DEFAULT NULL; | ||||||
							
								
								
									
										2
									
								
								resources/sql/autopatches/20180730.project.01.spaces.sql
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										2
									
								
								resources/sql/autopatches/20180730.project.01.spaces.sql
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,2 @@ | |||||||
|  | ALTER TABLE {$NAMESPACE}_project.project | ||||||
|  |   ADD COLUMN spacePHID VARBINARY(64) DEFAULT NULL; | ||||||
| @@ -432,6 +432,7 @@ phutil_register_library_map(array( | |||||||
|     'DifferentialChangesSinceLastUpdateField' => 'applications/differential/customfield/DifferentialChangesSinceLastUpdateField.php', |     'DifferentialChangesSinceLastUpdateField' => 'applications/differential/customfield/DifferentialChangesSinceLastUpdateField.php', | ||||||
|     'DifferentialChangeset' => 'applications/differential/storage/DifferentialChangeset.php', |     'DifferentialChangeset' => 'applications/differential/storage/DifferentialChangeset.php', | ||||||
|     'DifferentialChangesetDetailView' => 'applications/differential/view/DifferentialChangesetDetailView.php', |     'DifferentialChangesetDetailView' => 'applications/differential/view/DifferentialChangesetDetailView.php', | ||||||
|  |     'DifferentialChangesetEngine' => 'applications/differential/engine/DifferentialChangesetEngine.php', | ||||||
|     'DifferentialChangesetFileTreeSideNavBuilder' => 'applications/differential/view/DifferentialChangesetFileTreeSideNavBuilder.php', |     'DifferentialChangesetFileTreeSideNavBuilder' => 'applications/differential/view/DifferentialChangesetFileTreeSideNavBuilder.php', | ||||||
|     'DifferentialChangesetHTMLRenderer' => 'applications/differential/render/DifferentialChangesetHTMLRenderer.php', |     'DifferentialChangesetHTMLRenderer' => 'applications/differential/render/DifferentialChangesetHTMLRenderer.php', | ||||||
|     'DifferentialChangesetListController' => 'applications/differential/controller/DifferentialChangesetListController.php', |     'DifferentialChangesetListController' => 'applications/differential/controller/DifferentialChangesetListController.php', | ||||||
| @@ -814,8 +815,16 @@ phutil_register_library_map(array( | |||||||
|     'DiffusionHistoryTableView' => 'applications/diffusion/view/DiffusionHistoryTableView.php', |     'DiffusionHistoryTableView' => 'applications/diffusion/view/DiffusionHistoryTableView.php', | ||||||
|     'DiffusionHistoryView' => 'applications/diffusion/view/DiffusionHistoryView.php', |     'DiffusionHistoryView' => 'applications/diffusion/view/DiffusionHistoryView.php', | ||||||
|     'DiffusionHovercardEngineExtension' => 'applications/diffusion/engineextension/DiffusionHovercardEngineExtension.php', |     'DiffusionHovercardEngineExtension' => 'applications/diffusion/engineextension/DiffusionHovercardEngineExtension.php', | ||||||
|  |     'DiffusionIdentityAssigneeDatasource' => 'applications/diffusion/typeahead/DiffusionIdentityAssigneeDatasource.php', | ||||||
|  |     'DiffusionIdentityAssigneeEditField' => 'applications/diffusion/editfield/DiffusionIdentityAssigneeEditField.php', | ||||||
|  |     'DiffusionIdentityAssigneeSearchField' => 'applications/diffusion/searchfield/DiffusionIdentityAssigneeSearchField.php', | ||||||
|  |     'DiffusionIdentityEditController' => 'applications/diffusion/controller/DiffusionIdentityEditController.php', | ||||||
|  |     'DiffusionIdentityListController' => 'applications/diffusion/controller/DiffusionIdentityListController.php', | ||||||
|  |     'DiffusionIdentityUnassignedDatasource' => 'applications/diffusion/typeahead/DiffusionIdentityUnassignedDatasource.php', | ||||||
|  |     'DiffusionIdentityViewController' => 'applications/diffusion/controller/DiffusionIdentityViewController.php', | ||||||
|     'DiffusionInlineCommentController' => 'applications/diffusion/controller/DiffusionInlineCommentController.php', |     'DiffusionInlineCommentController' => 'applications/diffusion/controller/DiffusionInlineCommentController.php', | ||||||
|     'DiffusionInlineCommentPreviewController' => 'applications/diffusion/controller/DiffusionInlineCommentPreviewController.php', |     'DiffusionInlineCommentPreviewController' => 'applications/diffusion/controller/DiffusionInlineCommentPreviewController.php', | ||||||
|  |     'DiffusionInternalAncestorsConduitAPIMethod' => 'applications/diffusion/conduit/DiffusionInternalAncestorsConduitAPIMethod.php', | ||||||
|     'DiffusionInternalGitRawDiffQueryConduitAPIMethod' => 'applications/diffusion/conduit/DiffusionInternalGitRawDiffQueryConduitAPIMethod.php', |     'DiffusionInternalGitRawDiffQueryConduitAPIMethod' => 'applications/diffusion/conduit/DiffusionInternalGitRawDiffQueryConduitAPIMethod.php', | ||||||
|     'DiffusionLastModifiedController' => 'applications/diffusion/controller/DiffusionLastModifiedController.php', |     'DiffusionLastModifiedController' => 'applications/diffusion/controller/DiffusionLastModifiedController.php', | ||||||
|     'DiffusionLastModifiedQueryConduitAPIMethod' => 'applications/diffusion/conduit/DiffusionLastModifiedQueryConduitAPIMethod.php', |     'DiffusionLastModifiedQueryConduitAPIMethod' => 'applications/diffusion/conduit/DiffusionLastModifiedQueryConduitAPIMethod.php', | ||||||
| @@ -935,6 +944,8 @@ phutil_register_library_map(array( | |||||||
|     'DiffusionRepositoryEditUpdateController' => 'applications/diffusion/controller/DiffusionRepositoryEditUpdateController.php', |     'DiffusionRepositoryEditUpdateController' => 'applications/diffusion/controller/DiffusionRepositoryEditUpdateController.php', | ||||||
|     'DiffusionRepositoryFunctionDatasource' => 'applications/diffusion/typeahead/DiffusionRepositoryFunctionDatasource.php', |     'DiffusionRepositoryFunctionDatasource' => 'applications/diffusion/typeahead/DiffusionRepositoryFunctionDatasource.php', | ||||||
|     'DiffusionRepositoryHistoryManagementPanel' => 'applications/diffusion/management/DiffusionRepositoryHistoryManagementPanel.php', |     'DiffusionRepositoryHistoryManagementPanel' => 'applications/diffusion/management/DiffusionRepositoryHistoryManagementPanel.php', | ||||||
|  |     'DiffusionRepositoryIdentityEditor' => 'applications/diffusion/editor/DiffusionRepositoryIdentityEditor.php', | ||||||
|  |     'DiffusionRepositoryIdentitySearchEngine' => 'applications/diffusion/query/DiffusionRepositoryIdentitySearchEngine.php', | ||||||
|     'DiffusionRepositoryListController' => 'applications/diffusion/controller/DiffusionRepositoryListController.php', |     'DiffusionRepositoryListController' => 'applications/diffusion/controller/DiffusionRepositoryListController.php', | ||||||
|     'DiffusionRepositoryManageController' => 'applications/diffusion/controller/DiffusionRepositoryManageController.php', |     'DiffusionRepositoryManageController' => 'applications/diffusion/controller/DiffusionRepositoryManageController.php', | ||||||
|     'DiffusionRepositoryManagePanelsController' => 'applications/diffusion/controller/DiffusionRepositoryManagePanelsController.php', |     'DiffusionRepositoryManagePanelsController' => 'applications/diffusion/controller/DiffusionRepositoryManagePanelsController.php', | ||||||
| @@ -1430,6 +1441,7 @@ phutil_register_library_map(array( | |||||||
|     'HeraldActionGroup' => 'applications/herald/action/HeraldActionGroup.php', |     'HeraldActionGroup' => 'applications/herald/action/HeraldActionGroup.php', | ||||||
|     'HeraldActionRecord' => 'applications/herald/storage/HeraldActionRecord.php', |     'HeraldActionRecord' => 'applications/herald/storage/HeraldActionRecord.php', | ||||||
|     'HeraldAdapter' => 'applications/herald/adapter/HeraldAdapter.php', |     'HeraldAdapter' => 'applications/herald/adapter/HeraldAdapter.php', | ||||||
|  |     'HeraldAdapterDatasource' => 'applications/herald/typeahead/HeraldAdapterDatasource.php', | ||||||
|     'HeraldAlwaysField' => 'applications/herald/field/HeraldAlwaysField.php', |     'HeraldAlwaysField' => 'applications/herald/field/HeraldAlwaysField.php', | ||||||
|     'HeraldAnotherRuleField' => 'applications/herald/field/HeraldAnotherRuleField.php', |     'HeraldAnotherRuleField' => 'applications/herald/field/HeraldAnotherRuleField.php', | ||||||
|     'HeraldApplicationActionGroup' => 'applications/herald/action/HeraldApplicationActionGroup.php', |     'HeraldApplicationActionGroup' => 'applications/herald/action/HeraldApplicationActionGroup.php', | ||||||
| @@ -1485,12 +1497,17 @@ phutil_register_library_map(array( | |||||||
|     'HeraldRemarkupFieldValue' => 'applications/herald/value/HeraldRemarkupFieldValue.php', |     'HeraldRemarkupFieldValue' => 'applications/herald/value/HeraldRemarkupFieldValue.php', | ||||||
|     'HeraldRemarkupRule' => 'applications/herald/remarkup/HeraldRemarkupRule.php', |     'HeraldRemarkupRule' => 'applications/herald/remarkup/HeraldRemarkupRule.php', | ||||||
|     'HeraldRule' => 'applications/herald/storage/HeraldRule.php', |     'HeraldRule' => 'applications/herald/storage/HeraldRule.php', | ||||||
|  |     'HeraldRuleAdapter' => 'applications/herald/adapter/HeraldRuleAdapter.php', | ||||||
|  |     'HeraldRuleAdapterField' => 'applications/herald/field/rule/HeraldRuleAdapterField.php', | ||||||
|     'HeraldRuleController' => 'applications/herald/controller/HeraldRuleController.php', |     'HeraldRuleController' => 'applications/herald/controller/HeraldRuleController.php', | ||||||
|     'HeraldRuleDatasource' => 'applications/herald/typeahead/HeraldRuleDatasource.php', |     'HeraldRuleDatasource' => 'applications/herald/typeahead/HeraldRuleDatasource.php', | ||||||
|     'HeraldRuleEditor' => 'applications/herald/editor/HeraldRuleEditor.php', |     'HeraldRuleEditor' => 'applications/herald/editor/HeraldRuleEditor.php', | ||||||
|  |     'HeraldRuleField' => 'applications/herald/field/rule/HeraldRuleField.php', | ||||||
|  |     'HeraldRuleFieldGroup' => 'applications/herald/field/rule/HeraldRuleFieldGroup.php', | ||||||
|     'HeraldRuleListController' => 'applications/herald/controller/HeraldRuleListController.php', |     'HeraldRuleListController' => 'applications/herald/controller/HeraldRuleListController.php', | ||||||
|     'HeraldRulePHIDType' => 'applications/herald/phid/HeraldRulePHIDType.php', |     'HeraldRulePHIDType' => 'applications/herald/phid/HeraldRulePHIDType.php', | ||||||
|     'HeraldRuleQuery' => 'applications/herald/query/HeraldRuleQuery.php', |     'HeraldRuleQuery' => 'applications/herald/query/HeraldRuleQuery.php', | ||||||
|  |     'HeraldRuleReplyHandler' => 'applications/herald/mail/HeraldRuleReplyHandler.php', | ||||||
|     'HeraldRuleSearchEngine' => 'applications/herald/query/HeraldRuleSearchEngine.php', |     'HeraldRuleSearchEngine' => 'applications/herald/query/HeraldRuleSearchEngine.php', | ||||||
|     'HeraldRuleSerializer' => 'applications/herald/editor/HeraldRuleSerializer.php', |     'HeraldRuleSerializer' => 'applications/herald/editor/HeraldRuleSerializer.php', | ||||||
|     'HeraldRuleTestCase' => 'applications/herald/storage/__tests__/HeraldRuleTestCase.php', |     'HeraldRuleTestCase' => 'applications/herald/storage/__tests__/HeraldRuleTestCase.php', | ||||||
| @@ -1498,6 +1515,8 @@ phutil_register_library_map(array( | |||||||
|     'HeraldRuleTransactionComment' => 'applications/herald/storage/HeraldRuleTransactionComment.php', |     'HeraldRuleTransactionComment' => 'applications/herald/storage/HeraldRuleTransactionComment.php', | ||||||
|     'HeraldRuleTranscript' => 'applications/herald/storage/transcript/HeraldRuleTranscript.php', |     'HeraldRuleTranscript' => 'applications/herald/storage/transcript/HeraldRuleTranscript.php', | ||||||
|     'HeraldRuleTypeConfig' => 'applications/herald/config/HeraldRuleTypeConfig.php', |     'HeraldRuleTypeConfig' => 'applications/herald/config/HeraldRuleTypeConfig.php', | ||||||
|  |     'HeraldRuleTypeDatasource' => 'applications/herald/typeahead/HeraldRuleTypeDatasource.php', | ||||||
|  |     'HeraldRuleTypeField' => 'applications/herald/field/rule/HeraldRuleTypeField.php', | ||||||
|     'HeraldRuleViewController' => 'applications/herald/controller/HeraldRuleViewController.php', |     'HeraldRuleViewController' => 'applications/herald/controller/HeraldRuleViewController.php', | ||||||
|     'HeraldSchemaSpec' => 'applications/herald/storage/HeraldSchemaSpec.php', |     'HeraldSchemaSpec' => 'applications/herald/storage/HeraldSchemaSpec.php', | ||||||
|     'HeraldSelectFieldValue' => 'applications/herald/value/HeraldSelectFieldValue.php', |     'HeraldSelectFieldValue' => 'applications/herald/value/HeraldSelectFieldValue.php', | ||||||
| @@ -2521,6 +2540,7 @@ phutil_register_library_map(array( | |||||||
|     'PhabricatorChatLogDAO' => 'applications/chatlog/storage/PhabricatorChatLogDAO.php', |     'PhabricatorChatLogDAO' => 'applications/chatlog/storage/PhabricatorChatLogDAO.php', | ||||||
|     'PhabricatorChatLogEvent' => 'applications/chatlog/storage/PhabricatorChatLogEvent.php', |     'PhabricatorChatLogEvent' => 'applications/chatlog/storage/PhabricatorChatLogEvent.php', | ||||||
|     'PhabricatorChatLogQuery' => 'applications/chatlog/query/PhabricatorChatLogQuery.php', |     'PhabricatorChatLogQuery' => 'applications/chatlog/query/PhabricatorChatLogQuery.php', | ||||||
|  |     'PhabricatorCheckboxesEditField' => 'applications/transactions/editfield/PhabricatorCheckboxesEditField.php', | ||||||
|     'PhabricatorChunkedFileStorageEngine' => 'applications/files/engine/PhabricatorChunkedFileStorageEngine.php', |     'PhabricatorChunkedFileStorageEngine' => 'applications/files/engine/PhabricatorChunkedFileStorageEngine.php', | ||||||
|     'PhabricatorClassConfigType' => 'applications/config/type/PhabricatorClassConfigType.php', |     'PhabricatorClassConfigType' => 'applications/config/type/PhabricatorClassConfigType.php', | ||||||
|     'PhabricatorClusterConfigOptions' => 'applications/config/option/PhabricatorClusterConfigOptions.php', |     'PhabricatorClusterConfigOptions' => 'applications/config/option/PhabricatorClusterConfigOptions.php', | ||||||
| @@ -2863,6 +2883,7 @@ phutil_register_library_map(array( | |||||||
|     'PhabricatorDifferentialExtractWorkflow' => 'applications/differential/management/PhabricatorDifferentialExtractWorkflow.php', |     'PhabricatorDifferentialExtractWorkflow' => 'applications/differential/management/PhabricatorDifferentialExtractWorkflow.php', | ||||||
|     'PhabricatorDifferentialManagementWorkflow' => 'applications/differential/management/PhabricatorDifferentialManagementWorkflow.php', |     'PhabricatorDifferentialManagementWorkflow' => 'applications/differential/management/PhabricatorDifferentialManagementWorkflow.php', | ||||||
|     'PhabricatorDifferentialMigrateHunkWorkflow' => 'applications/differential/management/PhabricatorDifferentialMigrateHunkWorkflow.php', |     'PhabricatorDifferentialMigrateHunkWorkflow' => 'applications/differential/management/PhabricatorDifferentialMigrateHunkWorkflow.php', | ||||||
|  |     'PhabricatorDifferentialRebuildChangesetsWorkflow' => 'applications/differential/management/PhabricatorDifferentialRebuildChangesetsWorkflow.php', | ||||||
|     'PhabricatorDifferentialRevisionTestDataGenerator' => 'applications/differential/lipsum/PhabricatorDifferentialRevisionTestDataGenerator.php', |     'PhabricatorDifferentialRevisionTestDataGenerator' => 'applications/differential/lipsum/PhabricatorDifferentialRevisionTestDataGenerator.php', | ||||||
|     'PhabricatorDiffusionApplication' => 'applications/diffusion/application/PhabricatorDiffusionApplication.php', |     'PhabricatorDiffusionApplication' => 'applications/diffusion/application/PhabricatorDiffusionApplication.php', | ||||||
|     'PhabricatorDiffusionBlameSetting' => 'applications/settings/setting/PhabricatorDiffusionBlameSetting.php', |     'PhabricatorDiffusionBlameSetting' => 'applications/settings/setting/PhabricatorDiffusionBlameSetting.php', | ||||||
| @@ -2877,6 +2898,7 @@ phutil_register_library_map(array( | |||||||
|     'PhabricatorDocumentRef' => 'applications/files/document/PhabricatorDocumentRef.php', |     'PhabricatorDocumentRef' => 'applications/files/document/PhabricatorDocumentRef.php', | ||||||
|     'PhabricatorDocumentRenderingEngine' => 'applications/files/document/render/PhabricatorDocumentRenderingEngine.php', |     'PhabricatorDocumentRenderingEngine' => 'applications/files/document/render/PhabricatorDocumentRenderingEngine.php', | ||||||
|     'PhabricatorDoorkeeperApplication' => 'applications/doorkeeper/application/PhabricatorDoorkeeperApplication.php', |     'PhabricatorDoorkeeperApplication' => 'applications/doorkeeper/application/PhabricatorDoorkeeperApplication.php', | ||||||
|  |     'PhabricatorDoubleExportField' => 'infrastructure/export/field/PhabricatorDoubleExportField.php', | ||||||
|     'PhabricatorDraft' => 'applications/draft/storage/PhabricatorDraft.php', |     'PhabricatorDraft' => 'applications/draft/storage/PhabricatorDraft.php', | ||||||
|     'PhabricatorDraftDAO' => 'applications/draft/storage/PhabricatorDraftDAO.php', |     'PhabricatorDraftDAO' => 'applications/draft/storage/PhabricatorDraftDAO.php', | ||||||
|     'PhabricatorDraftEngine' => 'applications/transactions/draft/PhabricatorDraftEngine.php', |     'PhabricatorDraftEngine' => 'applications/transactions/draft/PhabricatorDraftEngine.php', | ||||||
| @@ -3350,6 +3372,7 @@ phutil_register_library_map(array( | |||||||
|     'PhabricatorMailOutboundRoutingSelfEmailHeraldAction' => 'applications/metamta/herald/PhabricatorMailOutboundRoutingSelfEmailHeraldAction.php', |     'PhabricatorMailOutboundRoutingSelfEmailHeraldAction' => 'applications/metamta/herald/PhabricatorMailOutboundRoutingSelfEmailHeraldAction.php', | ||||||
|     'PhabricatorMailOutboundRoutingSelfNotificationHeraldAction' => 'applications/metamta/herald/PhabricatorMailOutboundRoutingSelfNotificationHeraldAction.php', |     'PhabricatorMailOutboundRoutingSelfNotificationHeraldAction' => 'applications/metamta/herald/PhabricatorMailOutboundRoutingSelfNotificationHeraldAction.php', | ||||||
|     'PhabricatorMailOutboundStatus' => 'applications/metamta/constants/PhabricatorMailOutboundStatus.php', |     'PhabricatorMailOutboundStatus' => 'applications/metamta/constants/PhabricatorMailOutboundStatus.php', | ||||||
|  |     'PhabricatorMailPropertiesDestructionEngineExtension' => 'applications/metamta/engineextension/PhabricatorMailPropertiesDestructionEngineExtension.php', | ||||||
|     'PhabricatorMailReceiver' => 'applications/metamta/receiver/PhabricatorMailReceiver.php', |     'PhabricatorMailReceiver' => 'applications/metamta/receiver/PhabricatorMailReceiver.php', | ||||||
|     'PhabricatorMailReceiverTestCase' => 'applications/metamta/receiver/__tests__/PhabricatorMailReceiverTestCase.php', |     'PhabricatorMailReceiverTestCase' => 'applications/metamta/receiver/__tests__/PhabricatorMailReceiverTestCase.php', | ||||||
|     'PhabricatorMailReplyHandler' => 'applications/metamta/replyhandler/PhabricatorMailReplyHandler.php', |     'PhabricatorMailReplyHandler' => 'applications/metamta/replyhandler/PhabricatorMailReplyHandler.php', | ||||||
| @@ -3407,6 +3430,8 @@ phutil_register_library_map(array( | |||||||
|     'PhabricatorMetaMTAMailHasRecipientEdgeType' => 'applications/metamta/edge/PhabricatorMetaMTAMailHasRecipientEdgeType.php', |     'PhabricatorMetaMTAMailHasRecipientEdgeType' => 'applications/metamta/edge/PhabricatorMetaMTAMailHasRecipientEdgeType.php', | ||||||
|     'PhabricatorMetaMTAMailListController' => 'applications/metamta/controller/PhabricatorMetaMTAMailListController.php', |     'PhabricatorMetaMTAMailListController' => 'applications/metamta/controller/PhabricatorMetaMTAMailListController.php', | ||||||
|     'PhabricatorMetaMTAMailPHIDType' => 'applications/metamta/phid/PhabricatorMetaMTAMailPHIDType.php', |     'PhabricatorMetaMTAMailPHIDType' => 'applications/metamta/phid/PhabricatorMetaMTAMailPHIDType.php', | ||||||
|  |     'PhabricatorMetaMTAMailProperties' => 'applications/metamta/storage/PhabricatorMetaMTAMailProperties.php', | ||||||
|  |     'PhabricatorMetaMTAMailPropertiesQuery' => 'applications/metamta/query/PhabricatorMetaMTAMailPropertiesQuery.php', | ||||||
|     'PhabricatorMetaMTAMailQuery' => 'applications/metamta/query/PhabricatorMetaMTAMailQuery.php', |     'PhabricatorMetaMTAMailQuery' => 'applications/metamta/query/PhabricatorMetaMTAMailQuery.php', | ||||||
|     'PhabricatorMetaMTAMailSearchEngine' => 'applications/metamta/query/PhabricatorMetaMTAMailSearchEngine.php', |     'PhabricatorMetaMTAMailSearchEngine' => 'applications/metamta/query/PhabricatorMetaMTAMailSearchEngine.php', | ||||||
|     'PhabricatorMetaMTAMailSection' => 'applications/metamta/view/PhabricatorMetaMTAMailSection.php', |     'PhabricatorMetaMTAMailSection' => 'applications/metamta/view/PhabricatorMetaMTAMailSection.php', | ||||||
| @@ -3565,6 +3590,7 @@ phutil_register_library_map(array( | |||||||
|     'PhabricatorOwnersPackageFerretEngine' => 'applications/owners/search/PhabricatorOwnersPackageFerretEngine.php', |     'PhabricatorOwnersPackageFerretEngine' => 'applications/owners/search/PhabricatorOwnersPackageFerretEngine.php', | ||||||
|     'PhabricatorOwnersPackageFulltextEngine' => 'applications/owners/search/PhabricatorOwnersPackageFulltextEngine.php', |     'PhabricatorOwnersPackageFulltextEngine' => 'applications/owners/search/PhabricatorOwnersPackageFulltextEngine.php', | ||||||
|     'PhabricatorOwnersPackageFunctionDatasource' => 'applications/owners/typeahead/PhabricatorOwnersPackageFunctionDatasource.php', |     'PhabricatorOwnersPackageFunctionDatasource' => 'applications/owners/typeahead/PhabricatorOwnersPackageFunctionDatasource.php', | ||||||
|  |     'PhabricatorOwnersPackageIgnoredTransaction' => 'applications/owners/xaction/PhabricatorOwnersPackageIgnoredTransaction.php', | ||||||
|     'PhabricatorOwnersPackageNameNgrams' => 'applications/owners/storage/PhabricatorOwnersPackageNameNgrams.php', |     'PhabricatorOwnersPackageNameNgrams' => 'applications/owners/storage/PhabricatorOwnersPackageNameNgrams.php', | ||||||
|     'PhabricatorOwnersPackageNameTransaction' => 'applications/owners/xaction/PhabricatorOwnersPackageNameTransaction.php', |     'PhabricatorOwnersPackageNameTransaction' => 'applications/owners/xaction/PhabricatorOwnersPackageNameTransaction.php', | ||||||
|     'PhabricatorOwnersPackageOwnerDatasource' => 'applications/owners/typeahead/PhabricatorOwnersPackageOwnerDatasource.php', |     'PhabricatorOwnersPackageOwnerDatasource' => 'applications/owners/typeahead/PhabricatorOwnersPackageOwnerDatasource.php', | ||||||
| @@ -3860,6 +3886,7 @@ phutil_register_library_map(array( | |||||||
|     'PhabricatorPolicyRequestExceptionHandler' => 'aphront/handler/PhabricatorPolicyRequestExceptionHandler.php', |     'PhabricatorPolicyRequestExceptionHandler' => 'aphront/handler/PhabricatorPolicyRequestExceptionHandler.php', | ||||||
|     'PhabricatorPolicyRule' => 'applications/policy/rule/PhabricatorPolicyRule.php', |     'PhabricatorPolicyRule' => 'applications/policy/rule/PhabricatorPolicyRule.php', | ||||||
|     'PhabricatorPolicySearchEngineExtension' => 'applications/policy/engineextension/PhabricatorPolicySearchEngineExtension.php', |     'PhabricatorPolicySearchEngineExtension' => 'applications/policy/engineextension/PhabricatorPolicySearchEngineExtension.php', | ||||||
|  |     'PhabricatorPolicyStrengthConstants' => 'applications/policy/constants/PhabricatorPolicyStrengthConstants.php', | ||||||
|     'PhabricatorPolicyTestCase' => 'applications/policy/__tests__/PhabricatorPolicyTestCase.php', |     'PhabricatorPolicyTestCase' => 'applications/policy/__tests__/PhabricatorPolicyTestCase.php', | ||||||
|     'PhabricatorPolicyTestObject' => 'applications/policy/__tests__/PhabricatorPolicyTestObject.php', |     'PhabricatorPolicyTestObject' => 'applications/policy/__tests__/PhabricatorPolicyTestObject.php', | ||||||
|     'PhabricatorPolicyType' => 'applications/policy/constants/PhabricatorPolicyType.php', |     'PhabricatorPolicyType' => 'applications/policy/constants/PhabricatorPolicyType.php', | ||||||
| @@ -4003,7 +4030,9 @@ phutil_register_library_map(array( | |||||||
|     'PhabricatorProjectWorkboardBackgroundTransaction' => 'applications/project/xaction/PhabricatorProjectWorkboardBackgroundTransaction.php', |     'PhabricatorProjectWorkboardBackgroundTransaction' => 'applications/project/xaction/PhabricatorProjectWorkboardBackgroundTransaction.php', | ||||||
|     'PhabricatorProjectWorkboardProfileMenuItem' => 'applications/project/menuitem/PhabricatorProjectWorkboardProfileMenuItem.php', |     'PhabricatorProjectWorkboardProfileMenuItem' => 'applications/project/menuitem/PhabricatorProjectWorkboardProfileMenuItem.php', | ||||||
|     'PhabricatorProjectWorkboardTransaction' => 'applications/project/xaction/PhabricatorProjectWorkboardTransaction.php', |     'PhabricatorProjectWorkboardTransaction' => 'applications/project/xaction/PhabricatorProjectWorkboardTransaction.php', | ||||||
|  |     'PhabricatorProjectsAllPolicyRule' => 'applications/project/policyrule/PhabricatorProjectsAllPolicyRule.php', | ||||||
|     'PhabricatorProjectsAncestorsSearchEngineAttachment' => 'applications/project/engineextension/PhabricatorProjectsAncestorsSearchEngineAttachment.php', |     'PhabricatorProjectsAncestorsSearchEngineAttachment' => 'applications/project/engineextension/PhabricatorProjectsAncestorsSearchEngineAttachment.php', | ||||||
|  |     'PhabricatorProjectsBasePolicyRule' => 'applications/project/policyrule/PhabricatorProjectsBasePolicyRule.php', | ||||||
|     'PhabricatorProjectsCurtainExtension' => 'applications/project/engineextension/PhabricatorProjectsCurtainExtension.php', |     'PhabricatorProjectsCurtainExtension' => 'applications/project/engineextension/PhabricatorProjectsCurtainExtension.php', | ||||||
|     'PhabricatorProjectsEditEngineExtension' => 'applications/project/engineextension/PhabricatorProjectsEditEngineExtension.php', |     'PhabricatorProjectsEditEngineExtension' => 'applications/project/engineextension/PhabricatorProjectsEditEngineExtension.php', | ||||||
|     'PhabricatorProjectsEditField' => 'applications/transactions/editfield/PhabricatorProjectsEditField.php', |     'PhabricatorProjectsEditField' => 'applications/transactions/editfield/PhabricatorProjectsEditField.php', | ||||||
| @@ -4020,6 +4049,7 @@ phutil_register_library_map(array( | |||||||
|     'PhabricatorPygmentSetupCheck' => 'applications/config/check/PhabricatorPygmentSetupCheck.php', |     'PhabricatorPygmentSetupCheck' => 'applications/config/check/PhabricatorPygmentSetupCheck.php', | ||||||
|     'PhabricatorQuery' => 'infrastructure/query/PhabricatorQuery.php', |     'PhabricatorQuery' => 'infrastructure/query/PhabricatorQuery.php', | ||||||
|     'PhabricatorQueryConstraint' => 'infrastructure/query/constraint/PhabricatorQueryConstraint.php', |     'PhabricatorQueryConstraint' => 'infrastructure/query/constraint/PhabricatorQueryConstraint.php', | ||||||
|  |     'PhabricatorQueryIterator' => 'infrastructure/storage/lisk/PhabricatorQueryIterator.php', | ||||||
|     'PhabricatorQueryOrderItem' => 'infrastructure/query/order/PhabricatorQueryOrderItem.php', |     'PhabricatorQueryOrderItem' => 'infrastructure/query/order/PhabricatorQueryOrderItem.php', | ||||||
|     'PhabricatorQueryOrderTestCase' => 'infrastructure/query/order/__tests__/PhabricatorQueryOrderTestCase.php', |     'PhabricatorQueryOrderTestCase' => 'infrastructure/query/order/__tests__/PhabricatorQueryOrderTestCase.php', | ||||||
|     'PhabricatorQueryOrderVector' => 'infrastructure/query/order/PhabricatorQueryOrderVector.php', |     'PhabricatorQueryOrderVector' => 'infrastructure/query/order/PhabricatorQueryOrderVector.php', | ||||||
| @@ -4070,6 +4100,16 @@ phutil_register_library_map(array( | |||||||
|     'PhabricatorRepositoryGitLFSRefQuery' => 'applications/repository/query/PhabricatorRepositoryGitLFSRefQuery.php', |     'PhabricatorRepositoryGitLFSRefQuery' => 'applications/repository/query/PhabricatorRepositoryGitLFSRefQuery.php', | ||||||
|     'PhabricatorRepositoryGraphCache' => 'applications/repository/graphcache/PhabricatorRepositoryGraphCache.php', |     'PhabricatorRepositoryGraphCache' => 'applications/repository/graphcache/PhabricatorRepositoryGraphCache.php', | ||||||
|     'PhabricatorRepositoryGraphStream' => 'applications/repository/daemon/PhabricatorRepositoryGraphStream.php', |     'PhabricatorRepositoryGraphStream' => 'applications/repository/daemon/PhabricatorRepositoryGraphStream.php', | ||||||
|  |     'PhabricatorRepositoryIdentity' => 'applications/repository/storage/PhabricatorRepositoryIdentity.php', | ||||||
|  |     'PhabricatorRepositoryIdentityAssignTransaction' => 'applications/repository/xaction/PhabricatorRepositoryIdentityAssignTransaction.php', | ||||||
|  |     'PhabricatorRepositoryIdentityChangeWorker' => 'applications/repository/worker/PhabricatorRepositoryIdentityChangeWorker.php', | ||||||
|  |     'PhabricatorRepositoryIdentityEditEngine' => 'applications/repository/engine/PhabricatorRepositoryIdentityEditEngine.php', | ||||||
|  |     'PhabricatorRepositoryIdentityFerretEngine' => 'applications/repository/search/PhabricatorRepositoryIdentityFerretEngine.php', | ||||||
|  |     'PhabricatorRepositoryIdentityPHIDType' => 'applications/repository/phid/PhabricatorRepositoryIdentityPHIDType.php', | ||||||
|  |     'PhabricatorRepositoryIdentityQuery' => 'applications/repository/query/PhabricatorRepositoryIdentityQuery.php', | ||||||
|  |     'PhabricatorRepositoryIdentityTransaction' => 'applications/repository/storage/PhabricatorRepositoryIdentityTransaction.php', | ||||||
|  |     'PhabricatorRepositoryIdentityTransactionQuery' => 'applications/repository/query/PhabricatorRepositoryIdentityTransactionQuery.php', | ||||||
|  |     'PhabricatorRepositoryIdentityTransactionType' => 'applications/repository/xaction/PhabricatorRepositoryIdentityTransactionType.php', | ||||||
|     'PhabricatorRepositoryManagementCacheWorkflow' => 'applications/repository/management/PhabricatorRepositoryManagementCacheWorkflow.php', |     'PhabricatorRepositoryManagementCacheWorkflow' => 'applications/repository/management/PhabricatorRepositoryManagementCacheWorkflow.php', | ||||||
|     'PhabricatorRepositoryManagementClusterizeWorkflow' => 'applications/repository/management/PhabricatorRepositoryManagementClusterizeWorkflow.php', |     'PhabricatorRepositoryManagementClusterizeWorkflow' => 'applications/repository/management/PhabricatorRepositoryManagementClusterizeWorkflow.php', | ||||||
|     'PhabricatorRepositoryManagementDiscoverWorkflow' => 'applications/repository/management/PhabricatorRepositoryManagementDiscoverWorkflow.php', |     'PhabricatorRepositoryManagementDiscoverWorkflow' => 'applications/repository/management/PhabricatorRepositoryManagementDiscoverWorkflow.php', | ||||||
| @@ -4084,6 +4124,7 @@ phutil_register_library_map(array( | |||||||
|     'PhabricatorRepositoryManagementMovePathsWorkflow' => 'applications/repository/management/PhabricatorRepositoryManagementMovePathsWorkflow.php', |     'PhabricatorRepositoryManagementMovePathsWorkflow' => 'applications/repository/management/PhabricatorRepositoryManagementMovePathsWorkflow.php', | ||||||
|     'PhabricatorRepositoryManagementParentsWorkflow' => 'applications/repository/management/PhabricatorRepositoryManagementParentsWorkflow.php', |     'PhabricatorRepositoryManagementParentsWorkflow' => 'applications/repository/management/PhabricatorRepositoryManagementParentsWorkflow.php', | ||||||
|     'PhabricatorRepositoryManagementPullWorkflow' => 'applications/repository/management/PhabricatorRepositoryManagementPullWorkflow.php', |     'PhabricatorRepositoryManagementPullWorkflow' => 'applications/repository/management/PhabricatorRepositoryManagementPullWorkflow.php', | ||||||
|  |     'PhabricatorRepositoryManagementRebuildIdentitiesWorkflow' => 'applications/repository/management/PhabricatorRepositoryManagementRebuildIdentitiesWorkflow.php', | ||||||
|     'PhabricatorRepositoryManagementRefsWorkflow' => 'applications/repository/management/PhabricatorRepositoryManagementRefsWorkflow.php', |     'PhabricatorRepositoryManagementRefsWorkflow' => 'applications/repository/management/PhabricatorRepositoryManagementRefsWorkflow.php', | ||||||
|     'PhabricatorRepositoryManagementReparseWorkflow' => 'applications/repository/management/PhabricatorRepositoryManagementReparseWorkflow.php', |     'PhabricatorRepositoryManagementReparseWorkflow' => 'applications/repository/management/PhabricatorRepositoryManagementReparseWorkflow.php', | ||||||
|     'PhabricatorRepositoryManagementThawWorkflow' => 'applications/repository/management/PhabricatorRepositoryManagementThawWorkflow.php', |     'PhabricatorRepositoryManagementThawWorkflow' => 'applications/repository/management/PhabricatorRepositoryManagementThawWorkflow.php', | ||||||
| @@ -4415,6 +4456,7 @@ phutil_register_library_map(array( | |||||||
|     'PhabricatorSystemDAO' => 'applications/system/storage/PhabricatorSystemDAO.php', |     'PhabricatorSystemDAO' => 'applications/system/storage/PhabricatorSystemDAO.php', | ||||||
|     'PhabricatorSystemDestructionGarbageCollector' => 'applications/system/garbagecollector/PhabricatorSystemDestructionGarbageCollector.php', |     'PhabricatorSystemDestructionGarbageCollector' => 'applications/system/garbagecollector/PhabricatorSystemDestructionGarbageCollector.php', | ||||||
|     'PhabricatorSystemDestructionLog' => 'applications/system/storage/PhabricatorSystemDestructionLog.php', |     'PhabricatorSystemDestructionLog' => 'applications/system/storage/PhabricatorSystemDestructionLog.php', | ||||||
|  |     'PhabricatorSystemObjectController' => 'applications/system/controller/PhabricatorSystemObjectController.php', | ||||||
|     'PhabricatorSystemReadOnlyController' => 'applications/system/controller/PhabricatorSystemReadOnlyController.php', |     'PhabricatorSystemReadOnlyController' => 'applications/system/controller/PhabricatorSystemReadOnlyController.php', | ||||||
|     'PhabricatorSystemRemoveDestroyWorkflow' => 'applications/system/management/PhabricatorSystemRemoveDestroyWorkflow.php', |     'PhabricatorSystemRemoveDestroyWorkflow' => 'applications/system/management/PhabricatorSystemRemoveDestroyWorkflow.php', | ||||||
|     'PhabricatorSystemRemoveLogWorkflow' => 'applications/system/management/PhabricatorSystemRemoveLogWorkflow.php', |     'PhabricatorSystemRemoveLogWorkflow' => 'applications/system/management/PhabricatorSystemRemoveLogWorkflow.php', | ||||||
| @@ -4992,6 +5034,7 @@ phutil_register_library_map(array( | |||||||
|     'PhrictionDocumentMoveToTransaction' => 'applications/phriction/xaction/PhrictionDocumentMoveToTransaction.php', |     'PhrictionDocumentMoveToTransaction' => 'applications/phriction/xaction/PhrictionDocumentMoveToTransaction.php', | ||||||
|     'PhrictionDocumentPHIDType' => 'applications/phriction/phid/PhrictionDocumentPHIDType.php', |     'PhrictionDocumentPHIDType' => 'applications/phriction/phid/PhrictionDocumentPHIDType.php', | ||||||
|     'PhrictionDocumentPathHeraldField' => 'applications/phriction/herald/PhrictionDocumentPathHeraldField.php', |     'PhrictionDocumentPathHeraldField' => 'applications/phriction/herald/PhrictionDocumentPathHeraldField.php', | ||||||
|  |     'PhrictionDocumentPolicyCodex' => 'applications/phriction/codex/PhrictionDocumentPolicyCodex.php', | ||||||
|     'PhrictionDocumentQuery' => 'applications/phriction/query/PhrictionDocumentQuery.php', |     'PhrictionDocumentQuery' => 'applications/phriction/query/PhrictionDocumentQuery.php', | ||||||
|     'PhrictionDocumentSearchConduitAPIMethod' => 'applications/phriction/conduit/PhrictionDocumentSearchConduitAPIMethod.php', |     'PhrictionDocumentSearchConduitAPIMethod' => 'applications/phriction/conduit/PhrictionDocumentSearchConduitAPIMethod.php', | ||||||
|     'PhrictionDocumentSearchEngine' => 'applications/phriction/query/PhrictionDocumentSearchEngine.php', |     'PhrictionDocumentSearchEngine' => 'applications/phriction/query/PhrictionDocumentSearchEngine.php', | ||||||
| @@ -5718,6 +5761,7 @@ phutil_register_library_map(array( | |||||||
|       'PhabricatorDestructibleInterface', |       'PhabricatorDestructibleInterface', | ||||||
|     ), |     ), | ||||||
|     'DifferentialChangesetDetailView' => 'AphrontView', |     'DifferentialChangesetDetailView' => 'AphrontView', | ||||||
|  |     'DifferentialChangesetEngine' => 'Phobject', | ||||||
|     'DifferentialChangesetFileTreeSideNavBuilder' => 'Phobject', |     'DifferentialChangesetFileTreeSideNavBuilder' => 'Phobject', | ||||||
|     'DifferentialChangesetHTMLRenderer' => 'DifferentialChangesetRenderer', |     'DifferentialChangesetHTMLRenderer' => 'DifferentialChangesetRenderer', | ||||||
|     'DifferentialChangesetListController' => 'DifferentialController', |     'DifferentialChangesetListController' => 'DifferentialController', | ||||||
| @@ -6138,8 +6182,16 @@ phutil_register_library_map(array( | |||||||
|     'DiffusionHistoryTableView' => 'DiffusionHistoryView', |     'DiffusionHistoryTableView' => 'DiffusionHistoryView', | ||||||
|     'DiffusionHistoryView' => 'DiffusionView', |     'DiffusionHistoryView' => 'DiffusionView', | ||||||
|     'DiffusionHovercardEngineExtension' => 'PhabricatorHovercardEngineExtension', |     'DiffusionHovercardEngineExtension' => 'PhabricatorHovercardEngineExtension', | ||||||
|  |     'DiffusionIdentityAssigneeDatasource' => 'PhabricatorTypeaheadCompositeDatasource', | ||||||
|  |     'DiffusionIdentityAssigneeEditField' => 'PhabricatorTokenizerEditField', | ||||||
|  |     'DiffusionIdentityAssigneeSearchField' => 'PhabricatorSearchTokenizerField', | ||||||
|  |     'DiffusionIdentityEditController' => 'DiffusionController', | ||||||
|  |     'DiffusionIdentityListController' => 'DiffusionController', | ||||||
|  |     'DiffusionIdentityUnassignedDatasource' => 'PhabricatorTypeaheadDatasource', | ||||||
|  |     'DiffusionIdentityViewController' => 'DiffusionController', | ||||||
|     'DiffusionInlineCommentController' => 'PhabricatorInlineCommentController', |     'DiffusionInlineCommentController' => 'PhabricatorInlineCommentController', | ||||||
|     'DiffusionInlineCommentPreviewController' => 'PhabricatorInlineCommentPreviewController', |     'DiffusionInlineCommentPreviewController' => 'PhabricatorInlineCommentPreviewController', | ||||||
|  |     'DiffusionInternalAncestorsConduitAPIMethod' => 'DiffusionQueryConduitAPIMethod', | ||||||
|     'DiffusionInternalGitRawDiffQueryConduitAPIMethod' => 'DiffusionQueryConduitAPIMethod', |     'DiffusionInternalGitRawDiffQueryConduitAPIMethod' => 'DiffusionQueryConduitAPIMethod', | ||||||
|     'DiffusionLastModifiedController' => 'DiffusionController', |     'DiffusionLastModifiedController' => 'DiffusionController', | ||||||
|     'DiffusionLastModifiedQueryConduitAPIMethod' => 'DiffusionQueryConduitAPIMethod', |     'DiffusionLastModifiedQueryConduitAPIMethod' => 'DiffusionQueryConduitAPIMethod', | ||||||
| @@ -6258,6 +6310,8 @@ phutil_register_library_map(array( | |||||||
|     'DiffusionRepositoryEditUpdateController' => 'DiffusionRepositoryManageController', |     'DiffusionRepositoryEditUpdateController' => 'DiffusionRepositoryManageController', | ||||||
|     'DiffusionRepositoryFunctionDatasource' => 'PhabricatorTypeaheadCompositeDatasource', |     'DiffusionRepositoryFunctionDatasource' => 'PhabricatorTypeaheadCompositeDatasource', | ||||||
|     'DiffusionRepositoryHistoryManagementPanel' => 'DiffusionRepositoryManagementPanel', |     'DiffusionRepositoryHistoryManagementPanel' => 'DiffusionRepositoryManagementPanel', | ||||||
|  |     'DiffusionRepositoryIdentityEditor' => 'PhabricatorApplicationTransactionEditor', | ||||||
|  |     'DiffusionRepositoryIdentitySearchEngine' => 'PhabricatorApplicationSearchEngine', | ||||||
|     'DiffusionRepositoryListController' => 'DiffusionController', |     'DiffusionRepositoryListController' => 'DiffusionController', | ||||||
|     'DiffusionRepositoryManageController' => 'DiffusionController', |     'DiffusionRepositoryManageController' => 'DiffusionController', | ||||||
|     'DiffusionRepositoryManagePanelsController' => 'DiffusionRepositoryManageController', |     'DiffusionRepositoryManagePanelsController' => 'DiffusionRepositoryManageController', | ||||||
| @@ -6854,6 +6908,7 @@ phutil_register_library_map(array( | |||||||
|     'HeraldActionGroup' => 'HeraldGroup', |     'HeraldActionGroup' => 'HeraldGroup', | ||||||
|     'HeraldActionRecord' => 'HeraldDAO', |     'HeraldActionRecord' => 'HeraldDAO', | ||||||
|     'HeraldAdapter' => 'Phobject', |     'HeraldAdapter' => 'Phobject', | ||||||
|  |     'HeraldAdapterDatasource' => 'PhabricatorTypeaheadDatasource', | ||||||
|     'HeraldAlwaysField' => 'HeraldField', |     'HeraldAlwaysField' => 'HeraldField', | ||||||
|     'HeraldAnotherRuleField' => 'HeraldField', |     'HeraldAnotherRuleField' => 'HeraldField', | ||||||
|     'HeraldApplicationActionGroup' => 'HeraldActionGroup', |     'HeraldApplicationActionGroup' => 'HeraldActionGroup', | ||||||
| @@ -6922,12 +6977,17 @@ phutil_register_library_map(array( | |||||||
|       'PhabricatorDestructibleInterface', |       'PhabricatorDestructibleInterface', | ||||||
|       'PhabricatorSubscribableInterface', |       'PhabricatorSubscribableInterface', | ||||||
|     ), |     ), | ||||||
|  |     'HeraldRuleAdapter' => 'HeraldAdapter', | ||||||
|  |     'HeraldRuleAdapterField' => 'HeraldRuleField', | ||||||
|     'HeraldRuleController' => 'HeraldController', |     'HeraldRuleController' => 'HeraldController', | ||||||
|     'HeraldRuleDatasource' => 'PhabricatorTypeaheadDatasource', |     'HeraldRuleDatasource' => 'PhabricatorTypeaheadDatasource', | ||||||
|     'HeraldRuleEditor' => 'PhabricatorApplicationTransactionEditor', |     'HeraldRuleEditor' => 'PhabricatorApplicationTransactionEditor', | ||||||
|  |     'HeraldRuleField' => 'HeraldField', | ||||||
|  |     'HeraldRuleFieldGroup' => 'HeraldFieldGroup', | ||||||
|     'HeraldRuleListController' => 'HeraldController', |     'HeraldRuleListController' => 'HeraldController', | ||||||
|     'HeraldRulePHIDType' => 'PhabricatorPHIDType', |     'HeraldRulePHIDType' => 'PhabricatorPHIDType', | ||||||
|     'HeraldRuleQuery' => 'PhabricatorCursorPagedPolicyAwareQuery', |     'HeraldRuleQuery' => 'PhabricatorCursorPagedPolicyAwareQuery', | ||||||
|  |     'HeraldRuleReplyHandler' => 'PhabricatorApplicationTransactionReplyHandler', | ||||||
|     'HeraldRuleSearchEngine' => 'PhabricatorApplicationSearchEngine', |     'HeraldRuleSearchEngine' => 'PhabricatorApplicationSearchEngine', | ||||||
|     'HeraldRuleSerializer' => 'Phobject', |     'HeraldRuleSerializer' => 'Phobject', | ||||||
|     'HeraldRuleTestCase' => 'PhabricatorTestCase', |     'HeraldRuleTestCase' => 'PhabricatorTestCase', | ||||||
| @@ -6935,6 +6995,8 @@ phutil_register_library_map(array( | |||||||
|     'HeraldRuleTransactionComment' => 'PhabricatorApplicationTransactionComment', |     'HeraldRuleTransactionComment' => 'PhabricatorApplicationTransactionComment', | ||||||
|     'HeraldRuleTranscript' => 'Phobject', |     'HeraldRuleTranscript' => 'Phobject', | ||||||
|     'HeraldRuleTypeConfig' => 'Phobject', |     'HeraldRuleTypeConfig' => 'Phobject', | ||||||
|  |     'HeraldRuleTypeDatasource' => 'PhabricatorTypeaheadDatasource', | ||||||
|  |     'HeraldRuleTypeField' => 'HeraldRuleField', | ||||||
|     'HeraldRuleViewController' => 'HeraldController', |     'HeraldRuleViewController' => 'HeraldController', | ||||||
|     'HeraldSchemaSpec' => 'PhabricatorConfigSchemaSpec', |     'HeraldSchemaSpec' => 'PhabricatorConfigSchemaSpec', | ||||||
|     'HeraldSelectFieldValue' => 'HeraldFieldValue', |     'HeraldSelectFieldValue' => 'HeraldFieldValue', | ||||||
| @@ -8129,6 +8191,7 @@ phutil_register_library_map(array( | |||||||
|       'PhabricatorPolicyInterface', |       'PhabricatorPolicyInterface', | ||||||
|     ), |     ), | ||||||
|     'PhabricatorChatLogQuery' => 'PhabricatorCursorPagedPolicyAwareQuery', |     'PhabricatorChatLogQuery' => 'PhabricatorCursorPagedPolicyAwareQuery', | ||||||
|  |     'PhabricatorCheckboxesEditField' => 'PhabricatorEditField', | ||||||
|     'PhabricatorChunkedFileStorageEngine' => 'PhabricatorFileStorageEngine', |     'PhabricatorChunkedFileStorageEngine' => 'PhabricatorFileStorageEngine', | ||||||
|     'PhabricatorClassConfigType' => 'PhabricatorTextConfigType', |     'PhabricatorClassConfigType' => 'PhabricatorTextConfigType', | ||||||
|     'PhabricatorClusterConfigOptions' => 'PhabricatorApplicationConfigOptions', |     'PhabricatorClusterConfigOptions' => 'PhabricatorApplicationConfigOptions', | ||||||
| @@ -8513,6 +8576,7 @@ phutil_register_library_map(array( | |||||||
|     'PhabricatorDifferentialExtractWorkflow' => 'PhabricatorDifferentialManagementWorkflow', |     'PhabricatorDifferentialExtractWorkflow' => 'PhabricatorDifferentialManagementWorkflow', | ||||||
|     'PhabricatorDifferentialManagementWorkflow' => 'PhabricatorManagementWorkflow', |     'PhabricatorDifferentialManagementWorkflow' => 'PhabricatorManagementWorkflow', | ||||||
|     'PhabricatorDifferentialMigrateHunkWorkflow' => 'PhabricatorDifferentialManagementWorkflow', |     'PhabricatorDifferentialMigrateHunkWorkflow' => 'PhabricatorDifferentialManagementWorkflow', | ||||||
|  |     'PhabricatorDifferentialRebuildChangesetsWorkflow' => 'PhabricatorDifferentialManagementWorkflow', | ||||||
|     'PhabricatorDifferentialRevisionTestDataGenerator' => 'PhabricatorTestDataGenerator', |     'PhabricatorDifferentialRevisionTestDataGenerator' => 'PhabricatorTestDataGenerator', | ||||||
|     'PhabricatorDiffusionApplication' => 'PhabricatorApplication', |     'PhabricatorDiffusionApplication' => 'PhabricatorApplication', | ||||||
|     'PhabricatorDiffusionBlameSetting' => 'PhabricatorInternalSetting', |     'PhabricatorDiffusionBlameSetting' => 'PhabricatorInternalSetting', | ||||||
| @@ -8527,6 +8591,7 @@ phutil_register_library_map(array( | |||||||
|     'PhabricatorDocumentRef' => 'Phobject', |     'PhabricatorDocumentRef' => 'Phobject', | ||||||
|     'PhabricatorDocumentRenderingEngine' => 'Phobject', |     'PhabricatorDocumentRenderingEngine' => 'Phobject', | ||||||
|     'PhabricatorDoorkeeperApplication' => 'PhabricatorApplication', |     'PhabricatorDoorkeeperApplication' => 'PhabricatorApplication', | ||||||
|  |     'PhabricatorDoubleExportField' => 'PhabricatorExportField', | ||||||
|     'PhabricatorDraft' => 'PhabricatorDraftDAO', |     'PhabricatorDraft' => 'PhabricatorDraftDAO', | ||||||
|     'PhabricatorDraftDAO' => 'PhabricatorLiskDAO', |     'PhabricatorDraftDAO' => 'PhabricatorLiskDAO', | ||||||
|     'PhabricatorDraftEngine' => 'Phobject', |     'PhabricatorDraftEngine' => 'Phobject', | ||||||
| @@ -9049,6 +9114,7 @@ phutil_register_library_map(array( | |||||||
|     'PhabricatorMailOutboundRoutingSelfEmailHeraldAction' => 'PhabricatorMailOutboundRoutingHeraldAction', |     'PhabricatorMailOutboundRoutingSelfEmailHeraldAction' => 'PhabricatorMailOutboundRoutingHeraldAction', | ||||||
|     'PhabricatorMailOutboundRoutingSelfNotificationHeraldAction' => 'PhabricatorMailOutboundRoutingHeraldAction', |     'PhabricatorMailOutboundRoutingSelfNotificationHeraldAction' => 'PhabricatorMailOutboundRoutingHeraldAction', | ||||||
|     'PhabricatorMailOutboundStatus' => 'Phobject', |     'PhabricatorMailOutboundStatus' => 'Phobject', | ||||||
|  |     'PhabricatorMailPropertiesDestructionEngineExtension' => 'PhabricatorDestructionEngineExtension', | ||||||
|     'PhabricatorMailReceiver' => 'Phobject', |     'PhabricatorMailReceiver' => 'Phobject', | ||||||
|     'PhabricatorMailReceiverTestCase' => 'PhabricatorTestCase', |     'PhabricatorMailReceiverTestCase' => 'PhabricatorTestCase', | ||||||
|     'PhabricatorMailReplyHandler' => 'Phobject', |     'PhabricatorMailReplyHandler' => 'Phobject', | ||||||
| @@ -9117,6 +9183,11 @@ phutil_register_library_map(array( | |||||||
|     'PhabricatorMetaMTAMailHasRecipientEdgeType' => 'PhabricatorEdgeType', |     'PhabricatorMetaMTAMailHasRecipientEdgeType' => 'PhabricatorEdgeType', | ||||||
|     'PhabricatorMetaMTAMailListController' => 'PhabricatorMetaMTAController', |     'PhabricatorMetaMTAMailListController' => 'PhabricatorMetaMTAController', | ||||||
|     'PhabricatorMetaMTAMailPHIDType' => 'PhabricatorPHIDType', |     'PhabricatorMetaMTAMailPHIDType' => 'PhabricatorPHIDType', | ||||||
|  |     'PhabricatorMetaMTAMailProperties' => array( | ||||||
|  |       'PhabricatorMetaMTADAO', | ||||||
|  |       'PhabricatorPolicyInterface', | ||||||
|  |     ), | ||||||
|  |     'PhabricatorMetaMTAMailPropertiesQuery' => 'PhabricatorCursorPagedPolicyAwareQuery', | ||||||
|     'PhabricatorMetaMTAMailQuery' => 'PhabricatorCursorPagedPolicyAwareQuery', |     'PhabricatorMetaMTAMailQuery' => 'PhabricatorCursorPagedPolicyAwareQuery', | ||||||
|     'PhabricatorMetaMTAMailSearchEngine' => 'PhabricatorApplicationSearchEngine', |     'PhabricatorMetaMTAMailSearchEngine' => 'PhabricatorApplicationSearchEngine', | ||||||
|     'PhabricatorMetaMTAMailSection' => 'Phobject', |     'PhabricatorMetaMTAMailSection' => 'Phobject', | ||||||
| @@ -9305,6 +9376,7 @@ phutil_register_library_map(array( | |||||||
|     'PhabricatorOwnersPackageFerretEngine' => 'PhabricatorFerretEngine', |     'PhabricatorOwnersPackageFerretEngine' => 'PhabricatorFerretEngine', | ||||||
|     'PhabricatorOwnersPackageFulltextEngine' => 'PhabricatorFulltextEngine', |     'PhabricatorOwnersPackageFulltextEngine' => 'PhabricatorFulltextEngine', | ||||||
|     'PhabricatorOwnersPackageFunctionDatasource' => 'PhabricatorTypeaheadCompositeDatasource', |     'PhabricatorOwnersPackageFunctionDatasource' => 'PhabricatorTypeaheadCompositeDatasource', | ||||||
|  |     'PhabricatorOwnersPackageIgnoredTransaction' => 'PhabricatorOwnersPackageTransactionType', | ||||||
|     'PhabricatorOwnersPackageNameNgrams' => 'PhabricatorSearchNgrams', |     'PhabricatorOwnersPackageNameNgrams' => 'PhabricatorSearchNgrams', | ||||||
|     'PhabricatorOwnersPackageNameTransaction' => 'PhabricatorOwnersPackageTransactionType', |     'PhabricatorOwnersPackageNameTransaction' => 'PhabricatorOwnersPackageTransactionType', | ||||||
|     'PhabricatorOwnersPackageOwnerDatasource' => 'PhabricatorTypeaheadCompositeDatasource', |     'PhabricatorOwnersPackageOwnerDatasource' => 'PhabricatorTypeaheadCompositeDatasource', | ||||||
| @@ -9655,6 +9727,7 @@ phutil_register_library_map(array( | |||||||
|     'PhabricatorPolicyRequestExceptionHandler' => 'PhabricatorRequestExceptionHandler', |     'PhabricatorPolicyRequestExceptionHandler' => 'PhabricatorRequestExceptionHandler', | ||||||
|     'PhabricatorPolicyRule' => 'Phobject', |     'PhabricatorPolicyRule' => 'Phobject', | ||||||
|     'PhabricatorPolicySearchEngineExtension' => 'PhabricatorSearchEngineExtension', |     'PhabricatorPolicySearchEngineExtension' => 'PhabricatorSearchEngineExtension', | ||||||
|  |     'PhabricatorPolicyStrengthConstants' => 'PhabricatorPolicyConstants', | ||||||
|     'PhabricatorPolicyTestCase' => 'PhabricatorTestCase', |     'PhabricatorPolicyTestCase' => 'PhabricatorTestCase', | ||||||
|     'PhabricatorPolicyTestObject' => array( |     'PhabricatorPolicyTestObject' => array( | ||||||
|       'Phobject', |       'Phobject', | ||||||
| @@ -9690,6 +9763,7 @@ phutil_register_library_map(array( | |||||||
|       'PhabricatorFerretInterface', |       'PhabricatorFerretInterface', | ||||||
|       'PhabricatorConduitResultInterface', |       'PhabricatorConduitResultInterface', | ||||||
|       'PhabricatorColumnProxyInterface', |       'PhabricatorColumnProxyInterface', | ||||||
|  |       'PhabricatorSpacesInterface', | ||||||
|     ), |     ), | ||||||
|     'PhabricatorProjectAddHeraldAction' => 'PhabricatorProjectHeraldAction', |     'PhabricatorProjectAddHeraldAction' => 'PhabricatorProjectHeraldAction', | ||||||
|     'PhabricatorProjectApplication' => 'PhabricatorApplication', |     'PhabricatorProjectApplication' => 'PhabricatorApplication', | ||||||
| @@ -9834,7 +9908,9 @@ phutil_register_library_map(array( | |||||||
|     'PhabricatorProjectWorkboardBackgroundTransaction' => 'PhabricatorProjectTransactionType', |     'PhabricatorProjectWorkboardBackgroundTransaction' => 'PhabricatorProjectTransactionType', | ||||||
|     'PhabricatorProjectWorkboardProfileMenuItem' => 'PhabricatorProfileMenuItem', |     'PhabricatorProjectWorkboardProfileMenuItem' => 'PhabricatorProfileMenuItem', | ||||||
|     'PhabricatorProjectWorkboardTransaction' => 'PhabricatorProjectTransactionType', |     'PhabricatorProjectWorkboardTransaction' => 'PhabricatorProjectTransactionType', | ||||||
|  |     'PhabricatorProjectsAllPolicyRule' => 'PhabricatorProjectsBasePolicyRule', | ||||||
|     'PhabricatorProjectsAncestorsSearchEngineAttachment' => 'PhabricatorSearchEngineAttachment', |     'PhabricatorProjectsAncestorsSearchEngineAttachment' => 'PhabricatorSearchEngineAttachment', | ||||||
|  |     'PhabricatorProjectsBasePolicyRule' => 'PhabricatorPolicyRule', | ||||||
|     'PhabricatorProjectsCurtainExtension' => 'PHUICurtainExtension', |     'PhabricatorProjectsCurtainExtension' => 'PHUICurtainExtension', | ||||||
|     'PhabricatorProjectsEditEngineExtension' => 'PhabricatorEditEngineExtension', |     'PhabricatorProjectsEditEngineExtension' => 'PhabricatorEditEngineExtension', | ||||||
|     'PhabricatorProjectsEditField' => 'PhabricatorTokenizerEditField', |     'PhabricatorProjectsEditField' => 'PhabricatorTokenizerEditField', | ||||||
| @@ -9843,7 +9919,7 @@ phutil_register_library_map(array( | |||||||
|     'PhabricatorProjectsMailEngineExtension' => 'PhabricatorMailEngineExtension', |     'PhabricatorProjectsMailEngineExtension' => 'PhabricatorMailEngineExtension', | ||||||
|     'PhabricatorProjectsMembersSearchEngineAttachment' => 'PhabricatorSearchEngineAttachment', |     'PhabricatorProjectsMembersSearchEngineAttachment' => 'PhabricatorSearchEngineAttachment', | ||||||
|     'PhabricatorProjectsMembershipIndexEngineExtension' => 'PhabricatorIndexEngineExtension', |     'PhabricatorProjectsMembershipIndexEngineExtension' => 'PhabricatorIndexEngineExtension', | ||||||
|     'PhabricatorProjectsPolicyRule' => 'PhabricatorPolicyRule', |     'PhabricatorProjectsPolicyRule' => 'PhabricatorProjectsBasePolicyRule', | ||||||
|     'PhabricatorProjectsSearchEngineAttachment' => 'PhabricatorSearchEngineAttachment', |     'PhabricatorProjectsSearchEngineAttachment' => 'PhabricatorSearchEngineAttachment', | ||||||
|     'PhabricatorProjectsSearchEngineExtension' => 'PhabricatorSearchEngineExtension', |     'PhabricatorProjectsSearchEngineExtension' => 'PhabricatorSearchEngineExtension', | ||||||
|     'PhabricatorProjectsWatchersSearchEngineAttachment' => 'PhabricatorSearchEngineAttachment', |     'PhabricatorProjectsWatchersSearchEngineAttachment' => 'PhabricatorSearchEngineAttachment', | ||||||
| @@ -9851,6 +9927,7 @@ phutil_register_library_map(array( | |||||||
|     'PhabricatorPygmentSetupCheck' => 'PhabricatorSetupCheck', |     'PhabricatorPygmentSetupCheck' => 'PhabricatorSetupCheck', | ||||||
|     'PhabricatorQuery' => 'Phobject', |     'PhabricatorQuery' => 'Phobject', | ||||||
|     'PhabricatorQueryConstraint' => 'Phobject', |     'PhabricatorQueryConstraint' => 'Phobject', | ||||||
|  |     'PhabricatorQueryIterator' => 'PhutilBufferedIterator', | ||||||
|     'PhabricatorQueryOrderItem' => 'Phobject', |     'PhabricatorQueryOrderItem' => 'Phobject', | ||||||
|     'PhabricatorQueryOrderTestCase' => 'PhabricatorTestCase', |     'PhabricatorQueryOrderTestCase' => 'PhabricatorTestCase', | ||||||
|     'PhabricatorQueryOrderVector' => array( |     'PhabricatorQueryOrderVector' => array( | ||||||
| @@ -9944,6 +10021,20 @@ phutil_register_library_map(array( | |||||||
|     'PhabricatorRepositoryGitLFSRefQuery' => 'PhabricatorCursorPagedPolicyAwareQuery', |     'PhabricatorRepositoryGitLFSRefQuery' => 'PhabricatorCursorPagedPolicyAwareQuery', | ||||||
|     'PhabricatorRepositoryGraphCache' => 'Phobject', |     'PhabricatorRepositoryGraphCache' => 'Phobject', | ||||||
|     'PhabricatorRepositoryGraphStream' => 'Phobject', |     'PhabricatorRepositoryGraphStream' => 'Phobject', | ||||||
|  |     'PhabricatorRepositoryIdentity' => array( | ||||||
|  |       'PhabricatorRepositoryDAO', | ||||||
|  |       'PhabricatorPolicyInterface', | ||||||
|  |       'PhabricatorApplicationTransactionInterface', | ||||||
|  |     ), | ||||||
|  |     'PhabricatorRepositoryIdentityAssignTransaction' => 'PhabricatorRepositoryIdentityTransactionType', | ||||||
|  |     'PhabricatorRepositoryIdentityChangeWorker' => 'PhabricatorWorker', | ||||||
|  |     'PhabricatorRepositoryIdentityEditEngine' => 'PhabricatorEditEngine', | ||||||
|  |     'PhabricatorRepositoryIdentityFerretEngine' => 'PhabricatorFerretEngine', | ||||||
|  |     'PhabricatorRepositoryIdentityPHIDType' => 'PhabricatorPHIDType', | ||||||
|  |     'PhabricatorRepositoryIdentityQuery' => 'PhabricatorCursorPagedPolicyAwareQuery', | ||||||
|  |     'PhabricatorRepositoryIdentityTransaction' => 'PhabricatorModularTransaction', | ||||||
|  |     'PhabricatorRepositoryIdentityTransactionQuery' => 'PhabricatorApplicationTransactionQuery', | ||||||
|  |     'PhabricatorRepositoryIdentityTransactionType' => 'PhabricatorModularTransactionType', | ||||||
|     'PhabricatorRepositoryManagementCacheWorkflow' => 'PhabricatorRepositoryManagementWorkflow', |     'PhabricatorRepositoryManagementCacheWorkflow' => 'PhabricatorRepositoryManagementWorkflow', | ||||||
|     'PhabricatorRepositoryManagementClusterizeWorkflow' => 'PhabricatorRepositoryManagementWorkflow', |     'PhabricatorRepositoryManagementClusterizeWorkflow' => 'PhabricatorRepositoryManagementWorkflow', | ||||||
|     'PhabricatorRepositoryManagementDiscoverWorkflow' => 'PhabricatorRepositoryManagementWorkflow', |     'PhabricatorRepositoryManagementDiscoverWorkflow' => 'PhabricatorRepositoryManagementWorkflow', | ||||||
| @@ -9958,6 +10049,7 @@ phutil_register_library_map(array( | |||||||
|     'PhabricatorRepositoryManagementMovePathsWorkflow' => 'PhabricatorRepositoryManagementWorkflow', |     'PhabricatorRepositoryManagementMovePathsWorkflow' => 'PhabricatorRepositoryManagementWorkflow', | ||||||
|     'PhabricatorRepositoryManagementParentsWorkflow' => 'PhabricatorRepositoryManagementWorkflow', |     'PhabricatorRepositoryManagementParentsWorkflow' => 'PhabricatorRepositoryManagementWorkflow', | ||||||
|     'PhabricatorRepositoryManagementPullWorkflow' => 'PhabricatorRepositoryManagementWorkflow', |     'PhabricatorRepositoryManagementPullWorkflow' => 'PhabricatorRepositoryManagementWorkflow', | ||||||
|  |     'PhabricatorRepositoryManagementRebuildIdentitiesWorkflow' => 'PhabricatorRepositoryManagementWorkflow', | ||||||
|     'PhabricatorRepositoryManagementRefsWorkflow' => 'PhabricatorRepositoryManagementWorkflow', |     'PhabricatorRepositoryManagementRefsWorkflow' => 'PhabricatorRepositoryManagementWorkflow', | ||||||
|     'PhabricatorRepositoryManagementReparseWorkflow' => 'PhabricatorRepositoryManagementWorkflow', |     'PhabricatorRepositoryManagementReparseWorkflow' => 'PhabricatorRepositoryManagementWorkflow', | ||||||
|     'PhabricatorRepositoryManagementThawWorkflow' => 'PhabricatorRepositoryManagementWorkflow', |     'PhabricatorRepositoryManagementThawWorkflow' => 'PhabricatorRepositoryManagementWorkflow', | ||||||
| @@ -10328,6 +10420,7 @@ phutil_register_library_map(array( | |||||||
|     'PhabricatorSystemDAO' => 'PhabricatorLiskDAO', |     'PhabricatorSystemDAO' => 'PhabricatorLiskDAO', | ||||||
|     'PhabricatorSystemDestructionGarbageCollector' => 'PhabricatorGarbageCollector', |     'PhabricatorSystemDestructionGarbageCollector' => 'PhabricatorGarbageCollector', | ||||||
|     'PhabricatorSystemDestructionLog' => 'PhabricatorSystemDAO', |     'PhabricatorSystemDestructionLog' => 'PhabricatorSystemDAO', | ||||||
|  |     'PhabricatorSystemObjectController' => 'PhabricatorController', | ||||||
|     'PhabricatorSystemReadOnlyController' => 'PhabricatorController', |     'PhabricatorSystemReadOnlyController' => 'PhabricatorController', | ||||||
|     'PhabricatorSystemRemoveDestroyWorkflow' => 'PhabricatorSystemRemoveWorkflow', |     'PhabricatorSystemRemoveDestroyWorkflow' => 'PhabricatorSystemRemoveWorkflow', | ||||||
|     'PhabricatorSystemRemoveLogWorkflow' => 'PhabricatorSystemRemoveWorkflow', |     'PhabricatorSystemRemoveLogWorkflow' => 'PhabricatorSystemRemoveWorkflow', | ||||||
| @@ -11047,6 +11140,8 @@ phutil_register_library_map(array( | |||||||
|       'PhabricatorProjectInterface', |       'PhabricatorProjectInterface', | ||||||
|       'PhabricatorApplicationTransactionInterface', |       'PhabricatorApplicationTransactionInterface', | ||||||
|       'PhabricatorConduitResultInterface', |       'PhabricatorConduitResultInterface', | ||||||
|  |       'PhabricatorPolicyCodexInterface', | ||||||
|  |       'PhabricatorSpacesInterface', | ||||||
|     ), |     ), | ||||||
|     'PhrictionDocumentAuthorHeraldField' => 'PhrictionDocumentHeraldField', |     'PhrictionDocumentAuthorHeraldField' => 'PhrictionDocumentHeraldField', | ||||||
|     'PhrictionDocumentContentHeraldField' => 'PhrictionDocumentHeraldField', |     'PhrictionDocumentContentHeraldField' => 'PhrictionDocumentHeraldField', | ||||||
| @@ -11063,6 +11158,7 @@ phutil_register_library_map(array( | |||||||
|     'PhrictionDocumentMoveToTransaction' => 'PhrictionDocumentTransactionType', |     'PhrictionDocumentMoveToTransaction' => 'PhrictionDocumentTransactionType', | ||||||
|     'PhrictionDocumentPHIDType' => 'PhabricatorPHIDType', |     'PhrictionDocumentPHIDType' => 'PhabricatorPHIDType', | ||||||
|     'PhrictionDocumentPathHeraldField' => 'PhrictionDocumentHeraldField', |     'PhrictionDocumentPathHeraldField' => 'PhrictionDocumentHeraldField', | ||||||
|  |     'PhrictionDocumentPolicyCodex' => 'PhabricatorPolicyCodex', | ||||||
|     'PhrictionDocumentQuery' => 'PhabricatorCursorPagedPolicyAwareQuery', |     'PhrictionDocumentQuery' => 'PhabricatorCursorPagedPolicyAwareQuery', | ||||||
|     'PhrictionDocumentSearchConduitAPIMethod' => 'PhabricatorSearchEngineAPIMethod', |     'PhrictionDocumentSearchConduitAPIMethod' => 'PhabricatorSearchEngineAPIMethod', | ||||||
|     'PhrictionDocumentSearchEngine' => 'PhabricatorApplicationSearchEngine', |     'PhrictionDocumentSearchEngine' => 'PhabricatorApplicationSearchEngine', | ||||||
|   | |||||||
| @@ -54,7 +54,16 @@ abstract class AphrontResponse extends Phobject { | |||||||
|  |  | ||||||
|  |  | ||||||
|   public function getContentIterator() { |   public function getContentIterator() { | ||||||
|     return array($this->buildResponseString()); |     // By default, make sure responses are truly returning a string, not some | ||||||
|  |     // kind of object that behaves like a string. | ||||||
|  |  | ||||||
|  |     // We're going to remove the execution time limit before dumping the | ||||||
|  |     // response into the sink, and want any rendering that's going to occur | ||||||
|  |     // to happen BEFORE we release the limit. | ||||||
|  |  | ||||||
|  |     return array( | ||||||
|  |       (string)$this->buildResponseString(), | ||||||
|  |     ); | ||||||
|   } |   } | ||||||
|  |  | ||||||
|   public function buildResponseString() { |   public function buildResponseString() { | ||||||
|   | |||||||
| @@ -112,6 +112,23 @@ abstract class AphrontHTTPSink extends Phobject { | |||||||
|       $response->getHTTPResponseMessage()); |       $response->getHTTPResponseMessage()); | ||||||
|     $this->writeHeaders($all_headers); |     $this->writeHeaders($all_headers); | ||||||
|  |  | ||||||
|  |     // Allow clients an unlimited amount of time to download the response. | ||||||
|  |  | ||||||
|  |     // This allows clients to perform a "slow loris" attack, where they | ||||||
|  |     // download a large response very slowly to tie up process slots. However, | ||||||
|  |     // concurrent connection limits and "RequestReadTimeout" already prevent | ||||||
|  |     // this attack. We could add our own minimum download rate here if we want | ||||||
|  |     // to make this easier to configure eventually. | ||||||
|  |  | ||||||
|  |     // For normal page responses, we've fully rendered the page into a string | ||||||
|  |     // already so all that's left is writing it to the client. | ||||||
|  |  | ||||||
|  |     // For unusual responses (like large file downloads) we may still be doing | ||||||
|  |     // some meaningful work, but in theory that work is intrinsic to streaming | ||||||
|  |     // the response. | ||||||
|  |  | ||||||
|  |     set_time_limit(0); | ||||||
|  |  | ||||||
|     $abort = false; |     $abort = false; | ||||||
|     foreach ($data as $block) { |     foreach ($data as $block) { | ||||||
|       if (!$this->isWritable()) { |       if (!$this->isWritable()) { | ||||||
|   | |||||||
| @@ -217,6 +217,7 @@ abstract class PhabricatorAphlictManagementWorkflow | |||||||
|             'index "%s"). You should manually create this directory or '. |             'index "%s"). You should manually create this directory or '. | ||||||
|             'choose a different logfile location. %s', |             'choose a different logfile location. %s', | ||||||
|             $dir, |             $dir, | ||||||
|  |             $index, | ||||||
|             $ex->getMessage())); |             $ex->getMessage())); | ||||||
|       } |       } | ||||||
|     } |     } | ||||||
|   | |||||||
| @@ -53,6 +53,10 @@ final class PhabricatorCommitSearchEngine | |||||||
|       $query->withUnreachable($map['unreachable']); |       $query->withUnreachable($map['unreachable']); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|  |     if ($map['ancestorsOf']) { | ||||||
|  |       $query->withAncestorsOf($map['ancestorsOf']); | ||||||
|  |     } | ||||||
|  |  | ||||||
|     return $query; |     return $query; | ||||||
|   } |   } | ||||||
|  |  | ||||||
| @@ -103,6 +107,13 @@ final class PhabricatorCommitSearchEngine | |||||||
|           pht( |           pht( | ||||||
|             'Find or exclude unreachable commits which are not ancestors of '. |             'Find or exclude unreachable commits which are not ancestors of '. | ||||||
|             'any branch, tag, or ref.')), |             'any branch, tag, or ref.')), | ||||||
|  |       id(new PhabricatorSearchStringListField()) | ||||||
|  |         ->setLabel(pht('Ancestors Of')) | ||||||
|  |         ->setKey('ancestorsOf') | ||||||
|  |         ->setDescription( | ||||||
|  |           pht( | ||||||
|  |             'Find commits which are ancestors of a particular ref, '. | ||||||
|  |             'like "master".')), | ||||||
|     ); |     ); | ||||||
|   } |   } | ||||||
|  |  | ||||||
|   | |||||||
| @@ -70,12 +70,6 @@ final class PhabricatorAuthSSHKey | |||||||
|     return parent::save(); |     return parent::save(); | ||||||
|   } |   } | ||||||
|  |  | ||||||
|   public function getMailKey() { |  | ||||||
|     // NOTE: We don't actually receive mail for these objects. It's OK for |  | ||||||
|     // the mail key to be predictable until we do. |  | ||||||
|     return PhabricatorHash::digestForIndex($this->getPHID()); |  | ||||||
|   } |  | ||||||
|  |  | ||||||
|   public function toPublicKey() { |   public function toPublicKey() { | ||||||
|     return PhabricatorAuthSSHPublicKey::newFromStoredKey($this); |     return PhabricatorAuthSSHPublicKey::newFromStoredKey($this); | ||||||
|   } |   } | ||||||
|   | |||||||
| @@ -143,7 +143,7 @@ final class PhabricatorOpcodeCacheSpec extends PhabricatorCacheSpec { | |||||||
|         $this |         $this | ||||||
|           ->newIssue('extension.opcache.devmode') |           ->newIssue('extension.opcache.devmode') | ||||||
|           ->setShortName(pht('OPcache Config')) |           ->setShortName(pht('OPcache Config')) | ||||||
|           ->setName(pht('OPCache Not Configured for Development')) |           ->setName(pht('OPcache Not Configured for Development')) | ||||||
|           ->setSummary($summary) |           ->setSummary($summary) | ||||||
|           ->setMessage($message) |           ->setMessage($message) | ||||||
|           ->addPHPConfig('opcache.validate_timestamps') |           ->addPHPConfig('opcache.validate_timestamps') | ||||||
|   | |||||||
| @@ -211,9 +211,14 @@ final class PhabricatorConduitAPIController | |||||||
|         ->withIsActive(true) |         ->withIsActive(true) | ||||||
|         ->executeOne(); |         ->executeOne(); | ||||||
|       if (!$stored_key) { |       if (!$stored_key) { | ||||||
|  |         $key_summary = id(new PhutilUTF8StringTruncator()) | ||||||
|  |           ->setMaximumBytes(64) | ||||||
|  |           ->truncateString($raw_key); | ||||||
|         return array( |         return array( | ||||||
|           'ERR-INVALID-AUTH', |           'ERR-INVALID-AUTH', | ||||||
|           pht('No user or device is associated with that public key.'), |           pht( | ||||||
|  |             'No user or device is associated with the public key "%s".', | ||||||
|  |             $key_summary), | ||||||
|         ); |         ); | ||||||
|       } |       } | ||||||
|  |  | ||||||
|   | |||||||
| @@ -15,7 +15,7 @@ final class PhabricatorConfigApplication extends PhabricatorApplication { | |||||||
|   } |   } | ||||||
|  |  | ||||||
|   public function getTitleGlyph() { |   public function getTitleGlyph() { | ||||||
|     return "\xE2\x98\xBA"; |     return "\xE2\x9C\xA8"; | ||||||
|   } |   } | ||||||
|  |  | ||||||
|   public function getApplicationGroup() { |   public function getApplicationGroup() { | ||||||
|   | |||||||
| @@ -231,7 +231,6 @@ final class PhabricatorConfigEditController | |||||||
|     $box_header[] = $key; |     $box_header[] = $key; | ||||||
|  |  | ||||||
|     $crumbs = $this->buildApplicationCrumbs(); |     $crumbs = $this->buildApplicationCrumbs(); | ||||||
|     $crumbs->addTextCrumb(pht('Config'), $this->getApplicationURI()); |  | ||||||
|     if ($group) { |     if ($group) { | ||||||
|       $crumbs->addTextCrumb($group->getName(), $group_uri); |       $crumbs->addTextCrumb($group->getName(), $group_uri); | ||||||
|     } |     } | ||||||
|   | |||||||
| @@ -30,7 +30,7 @@ final class PhabricatorConfigGroupController | |||||||
|     $view = $this->buildConfigBoxView($box_header, $list); |     $view = $this->buildConfigBoxView($box_header, $list); | ||||||
|  |  | ||||||
|     $crumbs = $this->buildApplicationCrumbs() |     $crumbs = $this->buildApplicationCrumbs() | ||||||
|       ->addTextCrumb($group_name, $this->getApplicationURI($group_uri)) |       ->addTextCrumb($group_name, $group_uri) | ||||||
|       ->addTextCrumb($options->getName()) |       ->addTextCrumb($options->getName()) | ||||||
|       ->setBorder(true); |       ->setBorder(true); | ||||||
|  |  | ||||||
|   | |||||||
| @@ -445,7 +445,7 @@ final class PhabricatorSetupIssueView extends AphrontView { | |||||||
|         'p', |         'p', | ||||||
|         array(), |         array(), | ||||||
|         pht( |         pht( | ||||||
|           'You can find more information about configuring OPCache in '. |           'You can find more information about configuring OPcache in '. | ||||||
|           'the %s.', |           'the %s.', | ||||||
|           phutil_tag( |           phutil_tag( | ||||||
|             'a', |             'a', | ||||||
| @@ -453,7 +453,7 @@ final class PhabricatorSetupIssueView extends AphrontView { | |||||||
|               'href' => 'http://php.net/manual/opcache.configuration.php', |               'href' => 'http://php.net/manual/opcache.configuration.php', | ||||||
|               'target' => '_blank', |               'target' => '_blank', | ||||||
|             ), |             ), | ||||||
|             pht('PHP OPCache Documentation')))); |             pht('PHP OPcache Documentation')))); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     $info[] = phutil_tag( |     $info[] = phutil_tag( | ||||||
|   | |||||||
| @@ -72,7 +72,7 @@ final class PhabricatorDaemonBulkJobMonitorController | |||||||
|           ->appendParagraph( |           ->appendParagraph( | ||||||
|             pht( |             pht( | ||||||
|               'This job is waiting for confirmation before work begins.')) |               'This job is waiting for confirmation before work begins.')) | ||||||
|           ->addCancelButotn($job->getManageURI(), pht('Details')); |           ->addCancelButton($job->getManageURI(), pht('Details')); | ||||||
|       } |       } | ||||||
|     } |     } | ||||||
|  |  | ||||||
|   | |||||||
| @@ -44,13 +44,16 @@ abstract class PhabricatorDashboardProfileController | |||||||
|   } |   } | ||||||
|  |  | ||||||
|   protected function buildApplicationCrumbs() { |   protected function buildApplicationCrumbs() { | ||||||
|     $dashboard = $this->getDashboard(); |  | ||||||
|     $id = $dashboard->getID(); |  | ||||||
|     $dashboard_uri = $this->getApplicationURI("/view/{$id}/"); |  | ||||||
|  |  | ||||||
|     $crumbs = parent::buildApplicationCrumbs(); |     $crumbs = parent::buildApplicationCrumbs(); | ||||||
|     $crumbs->addTextCrumb($dashboard->getName(), $dashboard_uri); |  | ||||||
|     $crumbs->setBorder(true); |     $crumbs->setBorder(true); | ||||||
|  |  | ||||||
|  |     $dashboard = $this->getDashboard(); | ||||||
|  |     if ($dashboard) { | ||||||
|  |       $id = $dashboard->getID(); | ||||||
|  |       $dashboard_uri = $this->getApplicationURI("/view/{$id}/"); | ||||||
|  |       $crumbs->addTextCrumb($dashboard->getName(), $dashboard_uri); | ||||||
|  |     } | ||||||
|  |  | ||||||
|     return $crumbs; |     return $crumbs; | ||||||
|   } |   } | ||||||
|  |  | ||||||
|   | |||||||
| @@ -20,13 +20,22 @@ final class PhabricatorDashboardPanelDatasource | |||||||
|     return $this->filterResultsAgainstTokens($results); |     return $this->filterResultsAgainstTokens($results); | ||||||
|   } |   } | ||||||
|  |  | ||||||
|  |  | ||||||
|   protected function renderSpecialTokens(array $values) { |   protected function renderSpecialTokens(array $values) { | ||||||
|     return $this->renderTokensFromResults($this->buildResults(), $values); |     return $this->renderTokensFromResults($this->buildResults(), $values); | ||||||
|   } |   } | ||||||
|  |  | ||||||
|   public function buildResults() { |   public function buildResults() { | ||||||
|     $query = id(new PhabricatorDashboardPanelQuery()); |     $query = new PhabricatorDashboardPanelQuery(); | ||||||
|  |  | ||||||
|  |     $raw_query = $this->getRawQuery(); | ||||||
|  |     if (preg_match('/^[wW]\d+\z/', $raw_query)) { | ||||||
|  |       $id = trim($raw_query, 'wW'); | ||||||
|  |       $id = (int)$id; | ||||||
|  |       $query->withIDs(array($id)); | ||||||
|  |     } else { | ||||||
|  |       $query->withNameNgrams($raw_query); | ||||||
|  |     } | ||||||
|  |  | ||||||
|     $panels = $this->executeQuery($query); |     $panels = $this->executeQuery($query); | ||||||
|  |  | ||||||
|     $results = array(); |     $results = array(); | ||||||
|   | |||||||
| @@ -43,7 +43,10 @@ final class PhabricatorDifferentialApplication extends PhabricatorApplication { | |||||||
|  |  | ||||||
|   public function getRoutes() { |   public function getRoutes() { | ||||||
|     return array( |     return array( | ||||||
|       '/D(?P<id>[1-9]\d*)' => 'DifferentialRevisionViewController', |       '/D(?P<id>[1-9]\d*)' => array( | ||||||
|  |         '' => 'DifferentialRevisionViewController', | ||||||
|  |         '/(?P<filter>new)/' => 'DifferentialRevisionViewController', | ||||||
|  |       ), | ||||||
|       '/differential/' => array( |       '/differential/' => array( | ||||||
|         '(?:query/(?P<queryKey>[^/]+)/)?' |         '(?:query/(?P<queryKey>[^/]+)/)?' | ||||||
|           => 'DifferentialRevisionListController', |           => 'DifferentialRevisionListController', | ||||||
|   | |||||||
| @@ -76,6 +76,16 @@ abstract class DifferentialController extends PhabricatorController { | |||||||
|         $repository_phid, |         $repository_phid, | ||||||
|         $changeset_path); |         $changeset_path); | ||||||
|  |  | ||||||
|  |       // If this particular changeset is generated code and the package does | ||||||
|  |       // not match generated code, remove it from the list. | ||||||
|  |       if ($changeset->isGeneratedChangeset()) { | ||||||
|  |         foreach ($packages as $key => $package) { | ||||||
|  |           if ($package->getMustMatchUngeneratedPaths()) { | ||||||
|  |             unset($packages[$key]); | ||||||
|  |           } | ||||||
|  |         } | ||||||
|  |       } | ||||||
|  |  | ||||||
|       $this->pathPackageMap[$changeset_path] = $packages; |       $this->pathPackageMap[$changeset_path] = $packages; | ||||||
|       foreach ($packages as $package) { |       foreach ($packages as $package) { | ||||||
|         $this->packageChangesetMap[$package->getPHID()][] = $changeset; |         $this->packageChangesetMap[$package->getPHID()][] = $changeset; | ||||||
|   | |||||||
| @@ -1,22 +1,45 @@ | |||||||
| <?php | <?php | ||||||
|  |  | ||||||
| final class DifferentialRevisionViewController extends DifferentialController { | final class DifferentialRevisionViewController | ||||||
|  |   extends DifferentialController { | ||||||
|  |  | ||||||
|   private $revisionID; |   private $revisionID; | ||||||
|   private $veryLargeDiff; |   private $changesetCount; | ||||||
|  |   private $hiddenChangesets; | ||||||
|  |   private $warnings = array(); | ||||||
|  |  | ||||||
|   public function shouldAllowPublic() { |   public function shouldAllowPublic() { | ||||||
|     return true; |     return true; | ||||||
|   } |   } | ||||||
|  |  | ||||||
|  |   public function isLargeDiff() { | ||||||
|  |     return ($this->getChangesetCount() > $this->getLargeDiffLimit()); | ||||||
|  |   } | ||||||
|  |  | ||||||
|   public function isVeryLargeDiff() { |   public function isVeryLargeDiff() { | ||||||
|     return $this->veryLargeDiff; |     return ($this->getChangesetCount() > $this->getVeryLargeDiffLimit()); | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   public function getLargeDiffLimit() { | ||||||
|  |     return 100; | ||||||
|   } |   } | ||||||
|  |  | ||||||
|   public function getVeryLargeDiffLimit() { |   public function getVeryLargeDiffLimit() { | ||||||
|     return 1000; |     return 1000; | ||||||
|   } |   } | ||||||
|  |  | ||||||
|  |   public function getChangesetCount() { | ||||||
|  |     if ($this->changesetCount === null) { | ||||||
|  |       throw new PhutilInvalidStateException('setChangesetCount'); | ||||||
|  |     } | ||||||
|  |     return $this->changesetCount; | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   public function setChangesetCount($count) { | ||||||
|  |     $this->changesetCount = $count; | ||||||
|  |     return $this; | ||||||
|  |   } | ||||||
|  |  | ||||||
|   public function handleRequest(AphrontRequest $request) { |   public function handleRequest(AphrontRequest $request) { | ||||||
|     $viewer = $this->getViewer(); |     $viewer = $this->getViewer(); | ||||||
|     $this->revisionID = $request->getURIData('id'); |     $this->revisionID = $request->getURIData('id'); | ||||||
| @@ -46,9 +69,17 @@ final class DifferentialRevisionViewController extends DifferentialController { | |||||||
|  |  | ||||||
|     $revision->attachActiveDiff(last($diffs)); |     $revision->attachActiveDiff(last($diffs)); | ||||||
|  |  | ||||||
|     $diff_vs = $request->getInt('vs'); |     $diff_vs = $this->getOldDiffID($revision, $diffs); | ||||||
|     $target_id = $request->getInt('id'); |     if ($diff_vs instanceof AphrontResponse) { | ||||||
|     $target = idx($diffs, $target_id, end($diffs)); |       return $diff_vs; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     $target_id = $this->getNewDiffID($revision, $diffs); | ||||||
|  |     if ($target_id instanceof AphrontResponse) { | ||||||
|  |       return $target_id; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     $target = $diffs[$target_id]; | ||||||
|  |  | ||||||
|     $target_manual = $target; |     $target_manual = $target; | ||||||
|     if (!$target_id) { |     if (!$target_id) { | ||||||
| @@ -59,10 +90,6 @@ final class DifferentialRevisionViewController extends DifferentialController { | |||||||
|       } |       } | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     if (empty($diffs[$diff_vs])) { |  | ||||||
|       $diff_vs = null; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     $repository = null; |     $repository = null; | ||||||
|     $repository_phid = $target->getRepositoryPHID(); |     $repository_phid = $target->getRepositoryPHID(); | ||||||
|     if ($repository_phid) { |     if ($repository_phid) { | ||||||
| @@ -82,9 +109,7 @@ final class DifferentialRevisionViewController extends DifferentialController { | |||||||
|         idx($diffs, $diff_vs), |         idx($diffs, $diff_vs), | ||||||
|         $repository); |         $repository); | ||||||
|  |  | ||||||
|     if (count($rendering_references) > $this->getVeryLargeDiffLimit()) { |     $this->setChangesetCount(count($rendering_references)); | ||||||
|       $this->veryLargeDiff = true; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     if ($request->getExists('download')) { |     if ($request->getExists('download')) { | ||||||
|       return $this->buildRawDiffResponse( |       return $this->buildRawDiffResponse( | ||||||
| @@ -150,33 +175,54 @@ final class DifferentialRevisionViewController extends DifferentialController { | |||||||
|     } |     } | ||||||
|  |  | ||||||
|     $handles = $this->loadViewerHandles($object_phids); |     $handles = $this->loadViewerHandles($object_phids); | ||||||
|  |     $warnings = $this->warnings; | ||||||
|  |  | ||||||
|     $request_uri = $request->getRequestURI(); |     $request_uri = $request->getRequestURI(); | ||||||
|  |  | ||||||
|     $limit = 100; |  | ||||||
|     $large = $request->getStr('large'); |     $large = $request->getStr('large'); | ||||||
|     if (count($changesets) > $limit && !$large) { |  | ||||||
|       $count = count($changesets); |  | ||||||
|       $warning = new PHUIInfoView(); |  | ||||||
|       $warning->setTitle(pht('Large Diff')); |  | ||||||
|       $warning->setSeverity(PHUIInfoView::SEVERITY_WARNING); |  | ||||||
|       $warning->appendChild(hsprintf( |  | ||||||
|         '%s <strong>%s</strong>', |  | ||||||
|         pht( |  | ||||||
|           'This diff is large and affects %s files. '. |  | ||||||
|           'You may load each file individually or ', |  | ||||||
|           new PhutilNumber($count)), |  | ||||||
|         phutil_tag( |  | ||||||
|           'a', |  | ||||||
|           array( |  | ||||||
|             'class' => 'button button-grey', |  | ||||||
|             'href' => $request_uri |  | ||||||
|               ->alter('large', 'true') |  | ||||||
|               ->setFragment('toc'), |  | ||||||
|           ), |  | ||||||
|           pht('Show All Files Inline')))); |  | ||||||
|       $warning = $warning->render(); |  | ||||||
|  |  | ||||||
|  |     $large_warning = | ||||||
|  |       ($this->isLargeDiff()) && | ||||||
|  |       (!$this->isVeryLargeDiff()) && | ||||||
|  |       (!$large); | ||||||
|  |  | ||||||
|  |     if ($large_warning) { | ||||||
|  |       $count = $this->getChangesetCount(); | ||||||
|  |  | ||||||
|  |       $expand_uri = $request_uri | ||||||
|  |         ->alter('large', 'true') | ||||||
|  |         ->setFragment('toc'); | ||||||
|  |  | ||||||
|  |       $message = array( | ||||||
|  |         pht( | ||||||
|  |           'This large diff affects %s files. Files without inline '. | ||||||
|  |           'comments have been collapsed.', | ||||||
|  |           new PhutilNumber($count)), | ||||||
|  |         ' ', | ||||||
|  |         phutil_tag( | ||||||
|  |           'strong', | ||||||
|  |           array(), | ||||||
|  |           phutil_tag( | ||||||
|  |             'a', | ||||||
|  |             array( | ||||||
|  |               'href' => $expand_uri, | ||||||
|  |             ), | ||||||
|  |             pht('Expand All Files'))), | ||||||
|  |       ); | ||||||
|  |  | ||||||
|  |       $warnings[] = id(new PHUIInfoView()) | ||||||
|  |         ->setTitle(pht('Large Diff')) | ||||||
|  |         ->setSeverity(PHUIInfoView::SEVERITY_WARNING) | ||||||
|  |         ->appendChild($message); | ||||||
|  |  | ||||||
|  |       $folded_changesets = $changesets; | ||||||
|  |     } else { | ||||||
|  |       $folded_changesets = array(); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     // Don't hide or fold changesets which have inline comments. | ||||||
|  |     $hidden_changesets = $this->hiddenChangesets; | ||||||
|  |     if ($hidden_changesets || $folded_changesets) { | ||||||
|       $old = array_select_keys($changesets, $old_ids); |       $old = array_select_keys($changesets, $old_ids); | ||||||
|       $new = array_select_keys($changesets, $new_ids); |       $new = array_select_keys($changesets, $new_ids); | ||||||
|  |  | ||||||
| @@ -191,16 +237,47 @@ final class DifferentialRevisionViewController extends DifferentialController { | |||||||
|         $new, |         $new, | ||||||
|         $revision); |         $revision); | ||||||
|  |  | ||||||
|       $visible_changesets = array(); |  | ||||||
|       foreach ($inlines as $inline) { |       foreach ($inlines as $inline) { | ||||||
|         $changeset_id = $inline->getChangesetID(); |         $changeset_id = $inline->getChangesetID(); | ||||||
|         if (isset($changesets[$changeset_id])) { |         if (!isset($changesets[$changeset_id])) { | ||||||
|           $visible_changesets[$changeset_id] = $changesets[$changeset_id]; |           continue; | ||||||
|         } |         } | ||||||
|  |  | ||||||
|  |         unset($hidden_changesets[$changeset_id]); | ||||||
|  |         unset($folded_changesets[$changeset_id]); | ||||||
|       } |       } | ||||||
|     } else { |     } | ||||||
|       $warning = null; |  | ||||||
|       $visible_changesets = $changesets; |     // If we would hide only one changeset, don't hide anything. The notice | ||||||
|  |     // we'd render about it is about the same size as the changeset. | ||||||
|  |     if (count($hidden_changesets) < 2) { | ||||||
|  |       $hidden_changesets = array(); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     // Update the set of hidden changesets, since we may have just un-hidden | ||||||
|  |     // some of them. | ||||||
|  |     if ($hidden_changesets) { | ||||||
|  |       $warnings[] = id(new PHUIInfoView()) | ||||||
|  |         ->setTitle(pht('Showing Only Differences')) | ||||||
|  |         ->setSeverity(PHUIInfoView::SEVERITY_NOTICE) | ||||||
|  |         ->appendChild( | ||||||
|  |           pht( | ||||||
|  |             'This revision modifies %s more files that are hidden because '. | ||||||
|  |             'they were not modified between selected diffs and they have no '. | ||||||
|  |             'inline comments.', | ||||||
|  |             phutil_count($hidden_changesets))); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     // Compute the unfolded changesets. By default, everything is unfolded. | ||||||
|  |     $unfolded_changesets = $changesets; | ||||||
|  |     foreach ($folded_changesets as $changeset_id => $changeset) { | ||||||
|  |       unset($unfolded_changesets[$changeset_id]); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     // Throw away any hidden changesets. | ||||||
|  |     foreach ($hidden_changesets as $changeset_id => $changeset) { | ||||||
|  |       unset($changesets[$changeset_id]); | ||||||
|  |       unset($unfolded_changesets[$changeset_id]); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     $commit_hashes = mpull($diffs, 'getSourceControlBaseRevision'); |     $commit_hashes = mpull($diffs, 'getSourceControlBaseRevision'); | ||||||
| @@ -236,7 +313,7 @@ final class DifferentialRevisionViewController extends DifferentialController { | |||||||
|     if ($repository) { |     if ($repository) { | ||||||
|       $symbol_indexes = $this->buildSymbolIndexes( |       $symbol_indexes = $this->buildSymbolIndexes( | ||||||
|         $repository, |         $repository, | ||||||
|         $visible_changesets); |         $unfolded_changesets); | ||||||
|     } else { |     } else { | ||||||
|       $symbol_indexes = array(); |       $symbol_indexes = array(); | ||||||
|     } |     } | ||||||
| @@ -297,7 +374,7 @@ final class DifferentialRevisionViewController extends DifferentialController { | |||||||
|     } else { |     } else { | ||||||
|       $changeset_view = id(new DifferentialChangesetListView()) |       $changeset_view = id(new DifferentialChangesetListView()) | ||||||
|         ->setChangesets($changesets) |         ->setChangesets($changesets) | ||||||
|         ->setVisibleChangesets($visible_changesets) |         ->setVisibleChangesets($unfolded_changesets) | ||||||
|         ->setStandaloneURI('/differential/changeset/') |         ->setStandaloneURI('/differential/changeset/') | ||||||
|         ->setRawFileURIs( |         ->setRawFileURIs( | ||||||
|           '/differential/changeset/?view=old', |           '/differential/changeset/?view=old', | ||||||
| @@ -357,23 +434,33 @@ final class DifferentialRevisionViewController extends DifferentialController { | |||||||
|       $other_view = $this->renderOtherRevisions($other_revisions); |       $other_view = $this->renderOtherRevisions($other_revisions); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     $this->buildPackageMaps($changesets); |  | ||||||
|  |  | ||||||
|     if ($this->isVeryLargeDiff()) { |     if ($this->isVeryLargeDiff()) { | ||||||
|       $toc_view = null; |       $toc_view = null; | ||||||
|  |  | ||||||
|  |       // When rendering a "very large" diff, we skip computation of owners | ||||||
|  |       // that own no files because it is significantly expensive and not very | ||||||
|  |       // valuable. | ||||||
|  |       foreach ($revision->getReviewers() as $reviewer) { | ||||||
|  |         // Give each reviewer a dummy nonempty value so the UI does not render | ||||||
|  |         // the "(Owns No Changed Paths)" note. If that behavior becomes more | ||||||
|  |         // sophisticated in the future, this behavior might also need to. | ||||||
|  |         $reviewer->attachChangesets($changesets); | ||||||
|  |       } | ||||||
|     } else { |     } else { | ||||||
|  |       $this->buildPackageMaps($changesets); | ||||||
|  |  | ||||||
|       $toc_view = $this->buildTableOfContents( |       $toc_view = $this->buildTableOfContents( | ||||||
|         $changesets, |         $changesets, | ||||||
|         $visible_changesets, |         $unfolded_changesets, | ||||||
|         $target->loadCoverageMap($viewer)); |         $target->loadCoverageMap($viewer)); | ||||||
|     } |  | ||||||
|  |  | ||||||
|     // Attach changesets to each reviewer so we can show which Owners package |       // Attach changesets to each reviewer so we can show which Owners package | ||||||
|     // reviewers own no files. |       // reviewers own no files. | ||||||
|     foreach ($revision->getReviewers() as $reviewer) { |       foreach ($revision->getReviewers() as $reviewer) { | ||||||
|       $reviewer_phid = $reviewer->getReviewerPHID(); |         $reviewer_phid = $reviewer->getReviewerPHID(); | ||||||
|       $reviewer_changesets = $this->getPackageChangesets($reviewer_phid); |         $reviewer_changesets = $this->getPackageChangesets($reviewer_phid); | ||||||
|       $reviewer->attachChangesets($reviewer_changesets); |         $reviewer->attachChangesets($reviewer_changesets); | ||||||
|  |       } | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     $tab_group = id(new PHUITabGroupView()); |     $tab_group = id(new PHUITabGroupView()); | ||||||
| @@ -479,7 +566,7 @@ final class DifferentialRevisionViewController extends DifferentialController { | |||||||
|  |  | ||||||
|       $footer[] = array( |       $footer[] = array( | ||||||
|         $anchor, |         $anchor, | ||||||
|         $warning, |         $warnings, | ||||||
|         $tab_view, |         $tab_view, | ||||||
|         $changeset_view, |         $changeset_view, | ||||||
|       ); |       ); | ||||||
| @@ -766,6 +853,7 @@ final class DifferentialRevisionViewController extends DifferentialController { | |||||||
|     DifferentialDiff $target, |     DifferentialDiff $target, | ||||||
|     DifferentialDiff $diff_vs = null, |     DifferentialDiff $diff_vs = null, | ||||||
|     PhabricatorRepository $repository = null) { |     PhabricatorRepository $repository = null) { | ||||||
|  |     $viewer = $this->getViewer(); | ||||||
|  |  | ||||||
|     $load_diffs = array($target); |     $load_diffs = array($target); | ||||||
|     if ($diff_vs) { |     if ($diff_vs) { | ||||||
| @@ -773,7 +861,7 @@ final class DifferentialRevisionViewController extends DifferentialController { | |||||||
|     } |     } | ||||||
|  |  | ||||||
|     $raw_changesets = id(new DifferentialChangesetQuery()) |     $raw_changesets = id(new DifferentialChangesetQuery()) | ||||||
|       ->setViewer($this->getRequest()->getUser()) |       ->setViewer($viewer) | ||||||
|       ->withDiffs($load_diffs) |       ->withDiffs($load_diffs) | ||||||
|       ->execute(); |       ->execute(); | ||||||
|     $changeset_groups = mgroup($raw_changesets, 'getDiffID'); |     $changeset_groups = mgroup($raw_changesets, 'getDiffID'); | ||||||
| @@ -781,17 +869,19 @@ final class DifferentialRevisionViewController extends DifferentialController { | |||||||
|     $changesets = idx($changeset_groups, $target->getID(), array()); |     $changesets = idx($changeset_groups, $target->getID(), array()); | ||||||
|     $changesets = mpull($changesets, null, 'getID'); |     $changesets = mpull($changesets, null, 'getID'); | ||||||
|  |  | ||||||
|     $refs          = array(); |     $refs = array(); | ||||||
|     $vs_map        = array(); |     $vs_map = array(); | ||||||
|     $vs_changesets = array(); |     $vs_changesets = array(); | ||||||
|  |     $must_compare = array(); | ||||||
|     if ($diff_vs) { |     if ($diff_vs) { | ||||||
|       $vs_id                  = $diff_vs->getID(); |       $vs_id = $diff_vs->getID(); | ||||||
|       $vs_changesets_path_map = array(); |       $vs_changesets_path_map = array(); | ||||||
|       foreach (idx($changeset_groups, $vs_id, array()) as $changeset) { |       foreach (idx($changeset_groups, $vs_id, array()) as $changeset) { | ||||||
|         $path = $changeset->getAbsoluteRepositoryPath($repository, $diff_vs); |         $path = $changeset->getAbsoluteRepositoryPath($repository, $diff_vs); | ||||||
|         $vs_changesets_path_map[$path] = $changeset; |         $vs_changesets_path_map[$path] = $changeset; | ||||||
|         $vs_changesets[$changeset->getID()] = $changeset; |         $vs_changesets[$changeset->getID()] = $changeset; | ||||||
|       } |       } | ||||||
|  |  | ||||||
|       foreach ($changesets as $key => $changeset) { |       foreach ($changesets as $key => $changeset) { | ||||||
|         $path = $changeset->getAbsoluteRepositoryPath($repository, $target); |         $path = $changeset->getAbsoluteRepositoryPath($repository, $target); | ||||||
|         if (isset($vs_changesets_path_map[$path])) { |         if (isset($vs_changesets_path_map[$path])) { | ||||||
| @@ -800,15 +890,20 @@ final class DifferentialRevisionViewController extends DifferentialController { | |||||||
|           $refs[$changeset->getID()] = |           $refs[$changeset->getID()] = | ||||||
|             $changeset->getID().'/'.$vs_changesets_path_map[$path]->getID(); |             $changeset->getID().'/'.$vs_changesets_path_map[$path]->getID(); | ||||||
|           unset($vs_changesets_path_map[$path]); |           unset($vs_changesets_path_map[$path]); | ||||||
|  |  | ||||||
|  |           $must_compare[] = $changeset->getID(); | ||||||
|  |  | ||||||
|         } else { |         } else { | ||||||
|           $refs[$changeset->getID()] = $changeset->getID(); |           $refs[$changeset->getID()] = $changeset->getID(); | ||||||
|         } |         } | ||||||
|       } |       } | ||||||
|  |  | ||||||
|       foreach ($vs_changesets_path_map as $path => $changeset) { |       foreach ($vs_changesets_path_map as $path => $changeset) { | ||||||
|         $changesets[$changeset->getID()] = $changeset; |         $changesets[$changeset->getID()] = $changeset; | ||||||
|         $vs_map[$changeset->getID()]     = -1; |         $vs_map[$changeset->getID()] = -1; | ||||||
|         $refs[$changeset->getID()]       = $changeset->getID().'/-1'; |         $refs[$changeset->getID()] = $changeset->getID().'/-1'; | ||||||
|       } |       } | ||||||
|  |  | ||||||
|     } else { |     } else { | ||||||
|       foreach ($changesets as $changeset) { |       foreach ($changesets as $changeset) { | ||||||
|         $refs[$changeset->getID()] = $changeset->getID(); |         $refs[$changeset->getID()] = $changeset->getID(); | ||||||
| @@ -817,13 +912,25 @@ final class DifferentialRevisionViewController extends DifferentialController { | |||||||
|  |  | ||||||
|     $changesets = msort($changesets, 'getSortKey'); |     $changesets = msort($changesets, 'getSortKey'); | ||||||
|  |  | ||||||
|  |     // See T13137. When displaying the diff between two updates, hide any | ||||||
|  |     // changesets which haven't actually changed. | ||||||
|  |     $this->hiddenChangesets = array(); | ||||||
|  |     foreach ($must_compare as $changeset_id) { | ||||||
|  |       $changeset = $changesets[$changeset_id]; | ||||||
|  |       $vs_changeset = $vs_changesets[$vs_map[$changeset_id]]; | ||||||
|  |  | ||||||
|  |       if ($changeset->hasSameEffectAs($vs_changeset)) { | ||||||
|  |         $this->hiddenChangesets[$changeset_id] = $changesets[$changeset_id]; | ||||||
|  |       } | ||||||
|  |     } | ||||||
|  |  | ||||||
|     return array($changesets, $vs_map, $vs_changesets, $refs); |     return array($changesets, $vs_map, $vs_changesets, $refs); | ||||||
|   } |   } | ||||||
|  |  | ||||||
|   private function buildSymbolIndexes( |   private function buildSymbolIndexes( | ||||||
|     PhabricatorRepository $repository, |     PhabricatorRepository $repository, | ||||||
|     array $visible_changesets) { |     array $unfolded_changesets) { | ||||||
|     assert_instances_of($visible_changesets, 'DifferentialChangeset'); |     assert_instances_of($unfolded_changesets, 'DifferentialChangeset'); | ||||||
|  |  | ||||||
|     $engine = PhabricatorSyntaxHighlighter::newEngine(); |     $engine = PhabricatorSyntaxHighlighter::newEngine(); | ||||||
|  |  | ||||||
| @@ -848,7 +955,7 @@ final class DifferentialRevisionViewController extends DifferentialController { | |||||||
|       $sources); |       $sources); | ||||||
|  |  | ||||||
|     $indexed_langs = array_fill_keys($langs, true); |     $indexed_langs = array_fill_keys($langs, true); | ||||||
|     foreach ($visible_changesets as $key => $changeset) { |     foreach ($unfolded_changesets as $key => $changeset) { | ||||||
|       $lang = $engine->getLanguageFromFilename($changeset->getFilename()); |       $lang = $engine->getLanguageFromFilename($changeset->getFilename()); | ||||||
|       if (empty($indexed_langs) || isset($indexed_langs[$lang])) { |       if (empty($indexed_langs) || isset($indexed_langs[$lang])) { | ||||||
|         $symbol_indexes[$key] = array( |         $symbol_indexes[$key] = array( | ||||||
| @@ -1210,4 +1317,133 @@ final class DifferentialRevisionViewController extends DifferentialController { | |||||||
|       ->setShowViewAll(true); |       ->setShowViewAll(true); | ||||||
|   } |   } | ||||||
|  |  | ||||||
|  |   private function getOldDiffID(DifferentialRevision $revision, array $diffs) { | ||||||
|  |     assert_instances_of($diffs, 'DifferentialDiff'); | ||||||
|  |     $request = $this->getRequest(); | ||||||
|  |  | ||||||
|  |     $diffs = mpull($diffs, null, 'getID'); | ||||||
|  |  | ||||||
|  |     $is_new = ($request->getURIData('filter') === 'new'); | ||||||
|  |     $old_id = $request->getInt('vs'); | ||||||
|  |  | ||||||
|  |     // This is ambiguous, so just 404 rather than trying to figure out what | ||||||
|  |     // the user expects. | ||||||
|  |     if ($is_new && $old_id) { | ||||||
|  |       return new Aphront404Response(); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     if ($is_new) { | ||||||
|  |       $viewer = $this->getViewer(); | ||||||
|  |  | ||||||
|  |       $xactions = id(new DifferentialTransactionQuery()) | ||||||
|  |         ->setViewer($viewer) | ||||||
|  |         ->withObjectPHIDs(array($revision->getPHID())) | ||||||
|  |         ->withAuthorPHIDs(array($viewer->getPHID())) | ||||||
|  |         ->setOrder('newest') | ||||||
|  |         ->setLimit(1) | ||||||
|  |         ->execute(); | ||||||
|  |  | ||||||
|  |       if (!$xactions) { | ||||||
|  |         $this->warnings[] = id(new PHUIInfoView()) | ||||||
|  |           ->setTitle(pht('No Actions')) | ||||||
|  |           ->setSeverity(PHUIInfoView::SEVERITY_WARNING) | ||||||
|  |           ->appendChild( | ||||||
|  |             pht( | ||||||
|  |               'Showing all changes because you have never taken an '. | ||||||
|  |               'action on this revision.')); | ||||||
|  |       } else { | ||||||
|  |         $xaction = head($xactions); | ||||||
|  |  | ||||||
|  |         // Find the transactions which updated this revision. We want to | ||||||
|  |         // figure out which diff was active when you last took an action. | ||||||
|  |         $updates = id(new DifferentialTransactionQuery()) | ||||||
|  |           ->setViewer($viewer) | ||||||
|  |           ->withObjectPHIDs(array($revision->getPHID())) | ||||||
|  |           ->withTransactionTypes( | ||||||
|  |             array( | ||||||
|  |               DifferentialRevisionUpdateTransaction::TRANSACTIONTYPE, | ||||||
|  |             )) | ||||||
|  |           ->setOrder('oldest') | ||||||
|  |           ->execute(); | ||||||
|  |  | ||||||
|  |         // Sort the diffs into two buckets: those older than your last action | ||||||
|  |         // and those newer than your last action. | ||||||
|  |         $older = array(); | ||||||
|  |         $newer = array(); | ||||||
|  |         foreach ($updates as $update) { | ||||||
|  |           // If you updated the revision with "arc diff", try to count that | ||||||
|  |           // update as "before your last action". | ||||||
|  |           if ($update->getDateCreated() <= $xaction->getDateCreated()) { | ||||||
|  |             $older[] = $update->getNewValue(); | ||||||
|  |           } else { | ||||||
|  |             $newer[] = $update->getNewValue(); | ||||||
|  |           } | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         if (!$newer) { | ||||||
|  |           $this->warnings[] = id(new PHUIInfoView()) | ||||||
|  |             ->setTitle(pht('No Recent Updates')) | ||||||
|  |             ->setSeverity(PHUIInfoView::SEVERITY_WARNING) | ||||||
|  |             ->appendChild( | ||||||
|  |               pht( | ||||||
|  |                 'Showing all changes because the diff for this revision '. | ||||||
|  |                 'has not been updated since your last action.')); | ||||||
|  |         } else { | ||||||
|  |           $older = array_fuse($older); | ||||||
|  |  | ||||||
|  |           // Find the most recent diff from before the last action. | ||||||
|  |           $old = null; | ||||||
|  |           foreach ($diffs as $diff) { | ||||||
|  |             if (!isset($older[$diff->getPHID()])) { | ||||||
|  |               break; | ||||||
|  |             } | ||||||
|  |  | ||||||
|  |             $old = $diff; | ||||||
|  |           } | ||||||
|  |  | ||||||
|  |           // It's possible we may not find such a diff: transactions may have | ||||||
|  |           // been removed from the database, for example. If we miss, just | ||||||
|  |           // fail into some reasonable state since 404'ing would be perplexing. | ||||||
|  |           if ($old) { | ||||||
|  |             $this->warnings[] = id(new PHUIInfoView()) | ||||||
|  |               ->setTitle(pht('New Changes Shown')) | ||||||
|  |               ->setSeverity(PHUIInfoView::SEVERITY_NOTICE) | ||||||
|  |               ->appendChild( | ||||||
|  |                 pht( | ||||||
|  |                   'Showing changes since the last action you took on this '. | ||||||
|  |                   'revision.')); | ||||||
|  |  | ||||||
|  |             $old_id = $old->getID(); | ||||||
|  |           } | ||||||
|  |         } | ||||||
|  |       } | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     if (isset($diffs[$old_id])) { | ||||||
|  |       return $old_id; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     return null; | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   private function getNewDiffID(DifferentialRevision $revision, array $diffs) { | ||||||
|  |     assert_instances_of($diffs, 'DifferentialDiff'); | ||||||
|  |     $request = $this->getRequest(); | ||||||
|  |  | ||||||
|  |     $diffs = mpull($diffs, null, 'getID'); | ||||||
|  |  | ||||||
|  |     $is_new = ($request->getURIData('filter') === 'new'); | ||||||
|  |     $new_id = $request->getInt('id'); | ||||||
|  |  | ||||||
|  |     if ($is_new && $new_id) { | ||||||
|  |       return new Aphront404Response(); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     if (isset($diffs[$new_id])) { | ||||||
|  |       return $new_id; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     return (int)last_key($diffs); | ||||||
|  |   } | ||||||
|  |  | ||||||
| } | } | ||||||
|   | |||||||
| @@ -33,7 +33,7 @@ final class DifferentialRevisionDependedOnByRevisionEdgeType | |||||||
|     $add_edges) { |     $add_edges) { | ||||||
|  |  | ||||||
|     return pht( |     return pht( | ||||||
|       '%s added %s dependent revision(s): %s.', |       '%s added %s child revision(s): %s.', | ||||||
|       $actor, |       $actor, | ||||||
|       $add_count, |       $add_count, | ||||||
|       $add_edges); |       $add_edges); | ||||||
| @@ -45,7 +45,7 @@ final class DifferentialRevisionDependedOnByRevisionEdgeType | |||||||
|     $rem_edges) { |     $rem_edges) { | ||||||
|  |  | ||||||
|     return pht( |     return pht( | ||||||
|       '%s removed %s dependent revision(s): %s.', |       '%s removed %s child revision(s): %s.', | ||||||
|       $actor, |       $actor, | ||||||
|       $rem_count, |       $rem_count, | ||||||
|       $rem_edges); |       $rem_edges); | ||||||
| @@ -60,7 +60,7 @@ final class DifferentialRevisionDependedOnByRevisionEdgeType | |||||||
|     $rem_edges) { |     $rem_edges) { | ||||||
|  |  | ||||||
|     return pht( |     return pht( | ||||||
|       '%s edited dependent revision(s), added %s: %s; removed %s: %s.', |       '%s edited child revision(s), added %s: %s; removed %s: %s.', | ||||||
|       $actor, |       $actor, | ||||||
|       $add_count, |       $add_count, | ||||||
|       $add_edges, |       $add_edges, | ||||||
| @@ -75,7 +75,7 @@ final class DifferentialRevisionDependedOnByRevisionEdgeType | |||||||
|     $add_edges) { |     $add_edges) { | ||||||
|  |  | ||||||
|     return pht( |     return pht( | ||||||
|       '%s added %s dependent revision(s) for %s: %s.', |       '%s added %s child revision(s) for %s: %s.', | ||||||
|       $actor, |       $actor, | ||||||
|       $add_count, |       $add_count, | ||||||
|       $object, |       $object, | ||||||
| @@ -89,7 +89,7 @@ final class DifferentialRevisionDependedOnByRevisionEdgeType | |||||||
|     $rem_edges) { |     $rem_edges) { | ||||||
|  |  | ||||||
|     return pht( |     return pht( | ||||||
|       '%s removed %s dependent revision(s) for %s: %s.', |       '%s removed %s child revision(s) for %s: %s.', | ||||||
|       $actor, |       $actor, | ||||||
|       $rem_count, |       $rem_count, | ||||||
|       $object, |       $object, | ||||||
| @@ -106,7 +106,7 @@ final class DifferentialRevisionDependedOnByRevisionEdgeType | |||||||
|     $rem_edges) { |     $rem_edges) { | ||||||
|  |  | ||||||
|     return pht( |     return pht( | ||||||
|       '%s edited dependent revision(s) for %s, added %s: %s; removed %s: %s.', |       '%s edited child revision(s) for %s, added %s: %s; removed %s: %s.', | ||||||
|       $actor, |       $actor, | ||||||
|       $object, |       $object, | ||||||
|       $add_count, |       $add_count, | ||||||
|   | |||||||
| @@ -36,7 +36,7 @@ final class DifferentialRevisionDependsOnRevisionEdgeType | |||||||
|     $add_edges) { |     $add_edges) { | ||||||
|  |  | ||||||
|     return pht( |     return pht( | ||||||
|       '%s added %s dependencie(s): %s.', |       '%s added %s parent revision(s): %s.', | ||||||
|       $actor, |       $actor, | ||||||
|       $add_count, |       $add_count, | ||||||
|       $add_edges); |       $add_edges); | ||||||
| @@ -48,7 +48,7 @@ final class DifferentialRevisionDependsOnRevisionEdgeType | |||||||
|     $rem_edges) { |     $rem_edges) { | ||||||
|  |  | ||||||
|     return pht( |     return pht( | ||||||
|       '%s removed %s dependencie(s): %s.', |       '%s removed %s parent revision(s): %s.', | ||||||
|       $actor, |       $actor, | ||||||
|       $rem_count, |       $rem_count, | ||||||
|       $rem_edges); |       $rem_edges); | ||||||
| @@ -63,7 +63,7 @@ final class DifferentialRevisionDependsOnRevisionEdgeType | |||||||
|     $rem_edges) { |     $rem_edges) { | ||||||
|  |  | ||||||
|     return pht( |     return pht( | ||||||
|       '%s edited dependencie(s), added %s: %s; removed %s: %s.', |       '%s edited parent revision(s), added %s: %s; removed %s: %s.', | ||||||
|       $actor, |       $actor, | ||||||
|       $add_count, |       $add_count, | ||||||
|       $add_edges, |       $add_edges, | ||||||
| @@ -78,7 +78,7 @@ final class DifferentialRevisionDependsOnRevisionEdgeType | |||||||
|     $add_edges) { |     $add_edges) { | ||||||
|  |  | ||||||
|     return pht( |     return pht( | ||||||
|       '%s added %s dependencie(s) for %s: %s.', |       '%s added %s parent revision(s) for %s: %s.', | ||||||
|       $actor, |       $actor, | ||||||
|       $add_count, |       $add_count, | ||||||
|       $object, |       $object, | ||||||
| @@ -92,7 +92,7 @@ final class DifferentialRevisionDependsOnRevisionEdgeType | |||||||
|     $rem_edges) { |     $rem_edges) { | ||||||
|  |  | ||||||
|     return pht( |     return pht( | ||||||
|       '%s removed %s dependencie(s) for %s: %s.', |       '%s removed %s parent revision(s) for %s: %s.', | ||||||
|       $actor, |       $actor, | ||||||
|       $rem_count, |       $rem_count, | ||||||
|       $object, |       $object, | ||||||
| @@ -109,7 +109,7 @@ final class DifferentialRevisionDependsOnRevisionEdgeType | |||||||
|     $rem_edges) { |     $rem_edges) { | ||||||
|  |  | ||||||
|     return pht( |     return pht( | ||||||
|       '%s edited dependencie(s) for %s, added %s: %s; removed %s: %s.', |       '%s edited parent revision(s) for %s, added %s: %s; removed %s: %s.', | ||||||
|       $actor, |       $actor, | ||||||
|       $object, |       $object, | ||||||
|       $add_count, |       $add_count, | ||||||
|   | |||||||
| @@ -7,11 +7,13 @@ final class DifferentialTransactionEditor | |||||||
|   private $isCloseByCommit; |   private $isCloseByCommit; | ||||||
|   private $repositoryPHIDOverride = false; |   private $repositoryPHIDOverride = false; | ||||||
|   private $didExpandInlineState = false; |   private $didExpandInlineState = false; | ||||||
|   private $affectedPaths; |  | ||||||
|   private $firstBroadcast = false; |   private $firstBroadcast = false; | ||||||
|   private $wasBroadcasting; |   private $wasBroadcasting; | ||||||
|   private $isDraftDemotion; |   private $isDraftDemotion; | ||||||
|  |  | ||||||
|  |   private $ownersDiff; | ||||||
|  |   private $ownersChangesets; | ||||||
|  |  | ||||||
|   public function getEditorApplicationClass() { |   public function getEditorApplicationClass() { | ||||||
|     return 'PhabricatorDifferentialApplication'; |     return 'PhabricatorDifferentialApplication'; | ||||||
|   } |   } | ||||||
| @@ -590,12 +592,13 @@ final class DifferentialTransactionEditor | |||||||
|   } |   } | ||||||
|  |  | ||||||
|   protected function buildMailTemplate(PhabricatorLiskDAO $object) { |   protected function buildMailTemplate(PhabricatorLiskDAO $object) { | ||||||
|     $id = $object->getID(); |     $monogram = $object->getMonogram(); | ||||||
|     $title = $object->getTitle(); |     $title = $object->getTitle(); | ||||||
|     $subject = "D{$id}: {$title}"; |  | ||||||
|  |  | ||||||
|     return id(new PhabricatorMetaMTAMail()) |     return id(new PhabricatorMetaMTAMail()) | ||||||
|       ->setSubject($subject); |       ->setSubject(pht('%s: %s', $monogram, $title)) | ||||||
|  |       ->setMustEncryptSubject(pht('%s: Revision Updated', $monogram)) | ||||||
|  |       ->setMustEncryptURI($object->getURI()); | ||||||
|   } |   } | ||||||
|  |  | ||||||
|   protected function getTransactionsForMail( |   protected function getTransactionsForMail( | ||||||
| @@ -621,7 +624,9 @@ final class DifferentialTransactionEditor | |||||||
|     $body = new PhabricatorMetaMTAMailBody(); |     $body = new PhabricatorMetaMTAMailBody(); | ||||||
|     $body->setViewer($this->requireActor()); |     $body->setViewer($this->requireActor()); | ||||||
|  |  | ||||||
|     $revision_uri = PhabricatorEnv::getProductionURI('/D'.$object->getID()); |     $revision_uri = $object->getURI(); | ||||||
|  |     $revision_uri = PhabricatorEnv::getProductionURI($revision_uri); | ||||||
|  |     $new_uri = $revision_uri.'/new/'; | ||||||
|  |  | ||||||
|     $this->addHeadersAndCommentsToMailBody( |     $this->addHeadersAndCommentsToMailBody( | ||||||
|       $body, |       $body, | ||||||
| @@ -642,19 +647,6 @@ final class DifferentialTransactionEditor | |||||||
|       $this->appendInlineCommentsForMail($object, $inlines, $body); |       $this->appendInlineCommentsForMail($object, $inlines, $body); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     $changed_uri = $this->getChangedPriorToCommitURI(); |  | ||||||
|     if ($changed_uri) { |  | ||||||
|       $body->addLinkSection( |  | ||||||
|         pht('CHANGED PRIOR TO COMMIT'), |  | ||||||
|         $changed_uri); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     $this->addCustomFieldsToMailBody($body, $object, $xactions); |  | ||||||
|  |  | ||||||
|     $body->addLinkSection( |  | ||||||
|       pht('REVISION DETAIL'), |  | ||||||
|       $revision_uri); |  | ||||||
|  |  | ||||||
|     $update_xaction = null; |     $update_xaction = null; | ||||||
|     foreach ($xactions as $xaction) { |     foreach ($xactions as $xaction) { | ||||||
|       switch ($xaction->getTransactionType()) { |       switch ($xaction->getTransactionType()) { | ||||||
| @@ -666,7 +658,28 @@ final class DifferentialTransactionEditor | |||||||
|  |  | ||||||
|     if ($update_xaction) { |     if ($update_xaction) { | ||||||
|       $diff = $this->requireDiff($update_xaction->getNewValue(), true); |       $diff = $this->requireDiff($update_xaction->getNewValue(), true); | ||||||
|  |     } else { | ||||||
|  |       $diff = null; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     $changed_uri = $this->getChangedPriorToCommitURI(); | ||||||
|  |     if ($changed_uri) { | ||||||
|  |       $body->addLinkSection( | ||||||
|  |         pht('CHANGED PRIOR TO COMMIT'), | ||||||
|  |         $changed_uri); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     $this->addCustomFieldsToMailBody($body, $object, $xactions); | ||||||
|  |  | ||||||
|  |     if (!$this->getIsNewObject()) { | ||||||
|  |       $body->addLinkSection(pht('CHANGES SINCE LAST ACTION'), $new_uri); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     $body->addLinkSection( | ||||||
|  |       pht('REVISION DETAIL'), | ||||||
|  |       $revision_uri); | ||||||
|  |  | ||||||
|  |     if ($update_xaction) { | ||||||
|       $body->addTextSection( |       $body->addTextSection( | ||||||
|         pht('AFFECTED FILES'), |         pht('AFFECTED FILES'), | ||||||
|         $this->renderAffectedFilesForMail($diff)); |         $this->renderAffectedFilesForMail($diff)); | ||||||
| @@ -680,8 +693,13 @@ final class DifferentialTransactionEditor | |||||||
|       if ($config_inline || $config_attach) { |       if ($config_inline || $config_attach) { | ||||||
|         $body_limit = PhabricatorEnv::getEnvConfig('metamta.email-body-limit'); |         $body_limit = PhabricatorEnv::getEnvConfig('metamta.email-body-limit'); | ||||||
|  |  | ||||||
|         $patch = $this->buildPatchForMail($diff); |         try { | ||||||
|         if ($config_inline) { |           $patch = $this->buildPatchForMail($diff, $body_limit); | ||||||
|  |         } catch (ArcanistDiffByteSizeException $ex) { | ||||||
|  |           $patch = null; | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         if (($patch !== null) && $config_inline) { | ||||||
|           $lines = substr_count($patch, "\n"); |           $lines = substr_count($patch, "\n"); | ||||||
|           $bytes = strlen($patch); |           $bytes = strlen($patch); | ||||||
|  |  | ||||||
| @@ -704,7 +722,7 @@ final class DifferentialTransactionEditor | |||||||
|           } |           } | ||||||
|         } |         } | ||||||
|  |  | ||||||
|         if ($config_attach) { |         if (($patch !== null) && $config_attach) { | ||||||
|           // See T12033, T11767, and PHI55. This is a crude fix to stop the |           // See T12033, T11767, and PHI55. This is a crude fix to stop the | ||||||
|           // major concrete problems that lackluster email size limits cause. |           // major concrete problems that lackluster email size limits cause. | ||||||
|           if (strlen($patch) < $body_limit) { |           if (strlen($patch) < $body_limit) { | ||||||
| @@ -968,13 +986,20 @@ final class DifferentialTransactionEditor | |||||||
|       return array(); |       return array(); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     if (!$this->affectedPaths) { |     $diff = $this->ownersDiff; | ||||||
|  |     $changesets = $this->ownersChangesets; | ||||||
|  |  | ||||||
|  |     $this->ownersDiff = null; | ||||||
|  |     $this->ownersChangesets = null; | ||||||
|  |  | ||||||
|  |     if (!$changesets) { | ||||||
|       return array(); |       return array(); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     $packages = PhabricatorOwnersPackage::loadAffectedPackages( |     $packages = PhabricatorOwnersPackage::loadAffectedPackagesForChangesets( | ||||||
|       $repository, |       $repository, | ||||||
|       $this->affectedPaths); |       $diff, | ||||||
|  |       $changesets); | ||||||
|     if (!$packages) { |     if (!$packages) { | ||||||
|       return array(); |       return array(); | ||||||
|     } |     } | ||||||
| @@ -1255,9 +1280,12 @@ final class DifferentialTransactionEditor | |||||||
|       $paths[] = $path_prefix.'/'.$changeset->getFilename(); |       $paths[] = $path_prefix.'/'.$changeset->getFilename(); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     // Save the affected paths; we'll use them later to query Owners. This |     // If this change affected paths, save the changesets so we can apply | ||||||
|     // uses the un-expanded paths. |     // Owners rules to them later. | ||||||
|     $this->affectedPaths = $paths; |     if ($paths) { | ||||||
|  |       $this->ownersDiff = $diff; | ||||||
|  |       $this->ownersChangesets = $changesets; | ||||||
|  |     } | ||||||
|  |  | ||||||
|     // Mark this as also touching all parent paths, so you can see all pending |     // Mark this as also touching all parent paths, so you can see all pending | ||||||
|     // changes to any file within a directory. |     // changes to any file within a directory. | ||||||
| @@ -1399,13 +1427,14 @@ final class DifferentialTransactionEditor | |||||||
|       array('style' => 'font-family: monospace;'), $patch); |       array('style' => 'font-family: monospace;'), $patch); | ||||||
|   } |   } | ||||||
|  |  | ||||||
|   private function buildPatchForMail(DifferentialDiff $diff) { |   private function buildPatchForMail(DifferentialDiff $diff, $byte_limit) { | ||||||
|     $format = PhabricatorEnv::getEnvConfig('metamta.differential.patch-format'); |     $format = PhabricatorEnv::getEnvConfig('metamta.differential.patch-format'); | ||||||
|  |  | ||||||
|     return id(new DifferentialRawDiffRenderer()) |     return id(new DifferentialRawDiffRenderer()) | ||||||
|       ->setViewer($this->getActor()) |       ->setViewer($this->getActor()) | ||||||
|       ->setFormat($format) |       ->setFormat($format) | ||||||
|       ->setChangesets($diff->getChangesets()) |       ->setChangesets($diff->getChangesets()) | ||||||
|  |       ->setByteLimit($byte_limit) | ||||||
|       ->buildPatch(); |       ->buildPatch(); | ||||||
|   } |   } | ||||||
|  |  | ||||||
|   | |||||||
| @@ -0,0 +1,248 @@ | |||||||
|  | <?php | ||||||
|  |  | ||||||
|  | final class DifferentialChangesetEngine extends Phobject { | ||||||
|  |  | ||||||
|  |   public function rebuildChangesets(array $changesets) { | ||||||
|  |     assert_instances_of($changesets, 'DifferentialChangeset'); | ||||||
|  |  | ||||||
|  |     foreach ($changesets as $changeset) { | ||||||
|  |       $this->detectGeneratedCode($changeset); | ||||||
|  |       $this->computeHashes($changeset); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     $this->detectCopiedCode($changesets); | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |  | ||||||
|  | /* -(  Generated Code  )----------------------------------------------------- */ | ||||||
|  |  | ||||||
|  |  | ||||||
|  |   private function detectGeneratedCode(DifferentialChangeset $changeset) { | ||||||
|  |     $is_generated_trusted = $this->isTrustedGeneratedCode($changeset); | ||||||
|  |     if ($is_generated_trusted) { | ||||||
|  |       $changeset->setTrustedChangesetAttribute( | ||||||
|  |         DifferentialChangeset::ATTRIBUTE_GENERATED, | ||||||
|  |         $is_generated_trusted); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     $is_generated_untrusted = $this->isUntrustedGeneratedCode($changeset); | ||||||
|  |     if ($is_generated_untrusted) { | ||||||
|  |       $changeset->setUntrustedChangesetAttribute( | ||||||
|  |         DifferentialChangeset::ATTRIBUTE_GENERATED, | ||||||
|  |         $is_generated_untrusted); | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   private function isTrustedGeneratedCode(DifferentialChangeset $changeset) { | ||||||
|  |  | ||||||
|  |     $filename = $changeset->getFilename(); | ||||||
|  |  | ||||||
|  |     $paths = PhabricatorEnv::getEnvConfig('differential.generated-paths'); | ||||||
|  |     foreach ($paths as $regexp) { | ||||||
|  |       if (preg_match($regexp, $filename)) { | ||||||
|  |         return true; | ||||||
|  |       } | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     return false; | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   private function isUntrustedGeneratedCode(DifferentialChangeset $changeset) { | ||||||
|  |  | ||||||
|  |     if ($changeset->getHunks()) { | ||||||
|  |       $new_data = $changeset->makeNewFile(); | ||||||
|  |       if (strpos($new_data, '@'.'generated') !== false) { | ||||||
|  |         return true; | ||||||
|  |       } | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     return false; | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |  | ||||||
|  | /* -(  Content Hashes  )----------------------------------------------------- */ | ||||||
|  |  | ||||||
|  |  | ||||||
|  |   private function computeHashes(DifferentialChangeset $changeset) { | ||||||
|  |  | ||||||
|  |     $effect_key = DifferentialChangeset::METADATA_EFFECT_HASH; | ||||||
|  |  | ||||||
|  |     $effect_hash = $this->newEffectHash($changeset); | ||||||
|  |     if ($effect_hash !== null) { | ||||||
|  |       $changeset->setChangesetMetadata($effect_key, $effect_hash); | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   private function newEffectHash(DifferentialChangeset $changeset) { | ||||||
|  |  | ||||||
|  |     if ($changeset->getHunks()) { | ||||||
|  |       $new_data = $changeset->makeNewFile(); | ||||||
|  |       return PhabricatorHash::digestForIndex($new_data); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     return null; | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |  | ||||||
|  | /* -(  Copied Code  )-------------------------------------------------------- */ | ||||||
|  |  | ||||||
|  |  | ||||||
|  |   private function detectCopiedCode(array $changesets) { | ||||||
|  |     $min_width = 30; | ||||||
|  |     $min_lines = 3; | ||||||
|  |  | ||||||
|  |     $map = array(); | ||||||
|  |     $files = array(); | ||||||
|  |     $types = array(); | ||||||
|  |     foreach ($changesets as $changeset) { | ||||||
|  |       $file = $changeset->getFilename(); | ||||||
|  |       foreach ($changeset->getHunks() as $hunk) { | ||||||
|  |         $lines = $hunk->getStructuredOldFile(); | ||||||
|  |         foreach ($lines as $line => $info) { | ||||||
|  |           $type = $info['type']; | ||||||
|  |           if ($type == '\\') { | ||||||
|  |             continue; | ||||||
|  |           } | ||||||
|  |           $types[$file][$line] = $type; | ||||||
|  |  | ||||||
|  |           $text = $info['text']; | ||||||
|  |           $text = trim($text); | ||||||
|  |           $files[$file][$line] = $text; | ||||||
|  |  | ||||||
|  |           if (strlen($text) >= $min_width) { | ||||||
|  |             $map[$text][] = array($file, $line); | ||||||
|  |           } | ||||||
|  |         } | ||||||
|  |       } | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     foreach ($changesets as $changeset) { | ||||||
|  |       $copies = array(); | ||||||
|  |       foreach ($changeset->getHunks() as $hunk) { | ||||||
|  |         $added = $hunk->getStructuredNewFile(); | ||||||
|  |         $atype = array(); | ||||||
|  |  | ||||||
|  |         foreach ($added as $line => $info) { | ||||||
|  |           $atype[$line] = $info['type']; | ||||||
|  |           $added[$line] = trim($info['text']); | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         $skip_lines = 0; | ||||||
|  |         foreach ($added as $line => $code) { | ||||||
|  |           if ($skip_lines) { | ||||||
|  |             // We're skipping lines that we already processed because we | ||||||
|  |             // extended a block above them downward to include them. | ||||||
|  |             $skip_lines--; | ||||||
|  |             continue; | ||||||
|  |           } | ||||||
|  |  | ||||||
|  |           if ($atype[$line] !== '+') { | ||||||
|  |             // This line hasn't been changed in the new file, so don't try | ||||||
|  |             // to figure out where it came from. | ||||||
|  |             continue; | ||||||
|  |           } | ||||||
|  |  | ||||||
|  |           if (empty($map[$code])) { | ||||||
|  |             // This line was too short to trigger copy/move detection. | ||||||
|  |             continue; | ||||||
|  |           } | ||||||
|  |  | ||||||
|  |           if (count($map[$code]) > 16) { | ||||||
|  |             // If there are a large number of identical lines in this diff, | ||||||
|  |             // don't try to figure out where this block came from: the analysis | ||||||
|  |             // is O(N^2), since we need to compare every line against every | ||||||
|  |             // other line. Even if we arrive at a result, it is unlikely to be | ||||||
|  |             // meaningful. See T5041. | ||||||
|  |             continue; | ||||||
|  |           } | ||||||
|  |  | ||||||
|  |           $best_length = 0; | ||||||
|  |  | ||||||
|  |           // Explore all candidates. | ||||||
|  |           foreach ($map[$code] as $val) { | ||||||
|  |             list($file, $orig_line) = $val; | ||||||
|  |             $length = 1; | ||||||
|  |  | ||||||
|  |             // Search backward and forward to find all of the adjacent lines | ||||||
|  |             // which match. | ||||||
|  |             foreach (array(-1, 1) as $direction) { | ||||||
|  |               $offset = $direction; | ||||||
|  |               while (true) { | ||||||
|  |                 if (isset($copies[$line + $offset])) { | ||||||
|  |                   // If we run into a block above us which we've already | ||||||
|  |                   // attributed to a move or copy from elsewhere, stop | ||||||
|  |                   // looking. | ||||||
|  |                   break; | ||||||
|  |                 } | ||||||
|  |  | ||||||
|  |                 if (!isset($added[$line + $offset])) { | ||||||
|  |                   // If we've run off the beginning or end of the new file, | ||||||
|  |                   // stop looking. | ||||||
|  |                   break; | ||||||
|  |                 } | ||||||
|  |  | ||||||
|  |                 if (!isset($files[$file][$orig_line + $offset])) { | ||||||
|  |                   // If we've run off the beginning or end of the original | ||||||
|  |                   // file, we also stop looking. | ||||||
|  |                   break; | ||||||
|  |                 } | ||||||
|  |  | ||||||
|  |                 $old = $files[$file][$orig_line + $offset]; | ||||||
|  |                 $new = $added[$line + $offset]; | ||||||
|  |                 if ($old !== $new) { | ||||||
|  |                   // If the old line doesn't match the new line, stop | ||||||
|  |                   // looking. | ||||||
|  |                   break; | ||||||
|  |                 } | ||||||
|  |  | ||||||
|  |                 $length++; | ||||||
|  |                 $offset += $direction; | ||||||
|  |               } | ||||||
|  |             } | ||||||
|  |  | ||||||
|  |             if ($length < $best_length) { | ||||||
|  |               // If we already know of a better source (more matching lines) | ||||||
|  |               // for this move/copy, stick with that one. We prefer long | ||||||
|  |               // copies/moves which match a lot of context over short ones. | ||||||
|  |               continue; | ||||||
|  |             } | ||||||
|  |  | ||||||
|  |             if ($length == $best_length) { | ||||||
|  |               if (idx($types[$file], $orig_line) != '-') { | ||||||
|  |                 // If we already know of an equally good source (same number | ||||||
|  |                 // of matching lines) and this isn't a move, stick with the | ||||||
|  |                 // other one. We prefer moves over copies. | ||||||
|  |                 continue; | ||||||
|  |               } | ||||||
|  |             } | ||||||
|  |  | ||||||
|  |             $best_length = $length; | ||||||
|  |             // ($offset - 1) contains number of forward matching lines. | ||||||
|  |             $best_offset = $offset - 1; | ||||||
|  |             $best_file = $file; | ||||||
|  |             $best_line = $orig_line; | ||||||
|  |           } | ||||||
|  |  | ||||||
|  |           $file = ($best_file == $changeset->getFilename() ? '' : $best_file); | ||||||
|  |           for ($i = $best_length; $i--; ) { | ||||||
|  |             $type = idx($types[$best_file], $best_line + $best_offset - $i); | ||||||
|  |             $copies[$line + $best_offset - $i] = ($best_length < $min_lines | ||||||
|  |               ? array() // Ignore short blocks. | ||||||
|  |               : array($file, $best_line + $best_offset - $i, $type)); | ||||||
|  |           } | ||||||
|  |  | ||||||
|  |           $skip_lines = $best_offset; | ||||||
|  |         } | ||||||
|  |       } | ||||||
|  |  | ||||||
|  |       $copies = array_filter($copies); | ||||||
|  |       if ($copies) { | ||||||
|  |         $metadata = $changeset->getMetadata(); | ||||||
|  |         $metadata['copy:lines'] = $copies; | ||||||
|  |         $changeset->setMetadata($metadata); | ||||||
|  |       } | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |   } | ||||||
|  |  | ||||||
|  | } | ||||||
| @@ -120,9 +120,10 @@ final class HeraldDifferentialRevisionAdapter | |||||||
|  |  | ||||||
|       $repository = $this->loadRepository(); |       $repository = $this->loadRepository(); | ||||||
|       if ($repository) { |       if ($repository) { | ||||||
|         $packages = PhabricatorOwnersPackage::loadAffectedPackages( |         $packages = PhabricatorOwnersPackage::loadAffectedPackagesForChangesets( | ||||||
|           $repository, |           $repository, | ||||||
|           $this->loadAffectedPaths()); |           $this->getDiff(), | ||||||
|  |           $this->loadChangesets()); | ||||||
|         $this->affectedPackages = $packages; |         $this->affectedPackages = $packages; | ||||||
|       } |       } | ||||||
|     } |     } | ||||||
|   | |||||||
| @@ -48,10 +48,12 @@ final class PhabricatorDifferentialRevisionTestDataGenerator | |||||||
|  |  | ||||||
|   public function generateDiff($author) { |   public function generateDiff($author) { | ||||||
|     $paste_generator = new PhabricatorPasteTestDataGenerator(); |     $paste_generator = new PhabricatorPasteTestDataGenerator(); | ||||||
|     $languages = $paste_generator->supportedLanguages; |     $languages = $paste_generator->getSupportedLanguages(); | ||||||
|     $lang = array_rand($languages); |     $language = array_rand($languages); | ||||||
|     $code = $paste_generator->generateContent($lang); |     $spec = $languages[$language]; | ||||||
|     $altcode = $paste_generator->generateContent($lang); |  | ||||||
|  |     $code = $paste_generator->generateContent($spec); | ||||||
|  |     $altcode = $paste_generator->generateContent($spec); | ||||||
|     $newcode = $this->randomlyModify($code, $altcode); |     $newcode = $this->randomlyModify($code, $altcode); | ||||||
|     $diff = id(new PhabricatorDifferenceEngine()) |     $diff = id(new PhabricatorDifferenceEngine()) | ||||||
|       ->generateRawDiffFromFileContent($code, $newcode); |       ->generateRawDiffFromFileContent($code, $newcode); | ||||||
|   | |||||||
| @@ -0,0 +1,96 @@ | |||||||
|  | <?php | ||||||
|  |  | ||||||
|  | final class PhabricatorDifferentialRebuildChangesetsWorkflow | ||||||
|  |   extends PhabricatorDifferentialManagementWorkflow { | ||||||
|  |  | ||||||
|  |   protected function didConstruct() { | ||||||
|  |     $this | ||||||
|  |       ->setName('rebuild-changesets') | ||||||
|  |       ->setExamples('**rebuild-changesets** --revision __revision__') | ||||||
|  |       ->setSynopsis(pht('Rebuild changesets for a revision.')) | ||||||
|  |       ->setArguments( | ||||||
|  |         array( | ||||||
|  |           array( | ||||||
|  |             'name' => 'revision', | ||||||
|  |             'param' => 'revision', | ||||||
|  |             'help' => pht('Revision to rebuild changesets for.'), | ||||||
|  |           ), | ||||||
|  |         )); | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   public function execute(PhutilArgumentParser $args) { | ||||||
|  |     $viewer = $this->getViewer(); | ||||||
|  |  | ||||||
|  |     $revision_identifier = $args->getArg('revision'); | ||||||
|  |     if (!$revision_identifier) { | ||||||
|  |       throw new PhutilArgumentUsageException( | ||||||
|  |         pht('Specify a revision to rebuild changesets for with "--revision".')); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     $revision = id(new PhabricatorObjectQuery()) | ||||||
|  |       ->setViewer($viewer) | ||||||
|  |       ->withNames(array($revision_identifier)) | ||||||
|  |       ->executeOne(); | ||||||
|  |     if ($revision) { | ||||||
|  |       if (!($revision instanceof DifferentialRevision)) { | ||||||
|  |         throw new PhutilArgumentUsageException( | ||||||
|  |           pht( | ||||||
|  |             'Object "%s" specified by "--revision" must be a Differential '. | ||||||
|  |             'revision.')); | ||||||
|  |       } | ||||||
|  |     } else { | ||||||
|  |       $revision = id(new DifferentialRevisionQuery()) | ||||||
|  |         ->setViewer($viewer) | ||||||
|  |         ->withIDs(array($revision_identifier)) | ||||||
|  |         ->executeOne(); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     if (!$revision) { | ||||||
|  |       throw new PhutilArgumentUsageException( | ||||||
|  |         pht( | ||||||
|  |           'No revision "%s" exists.', | ||||||
|  |           $revision_identifier)); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     $diffs = id(new DifferentialDiffQuery()) | ||||||
|  |       ->setViewer($viewer) | ||||||
|  |       ->withRevisionIDs(array($revision->getID())) | ||||||
|  |       ->execute(); | ||||||
|  |  | ||||||
|  |     $changesets = id(new DifferentialChangesetQuery()) | ||||||
|  |       ->setViewer($viewer) | ||||||
|  |       ->withDiffs($diffs) | ||||||
|  |       ->needHunks(true) | ||||||
|  |       ->execute(); | ||||||
|  |  | ||||||
|  |     $changeset_groups = mgroup($changesets, 'getDiffID'); | ||||||
|  |  | ||||||
|  |     foreach ($changeset_groups as $diff_id => $changesets) { | ||||||
|  |       echo tsprintf( | ||||||
|  |         "%s\n", | ||||||
|  |         pht( | ||||||
|  |           'Rebuilding %s changeset(s) for diff ID %d.', | ||||||
|  |           phutil_count($changesets), | ||||||
|  |           $diff_id)); | ||||||
|  |  | ||||||
|  |       foreach ($changesets as $changeset) { | ||||||
|  |         echo tsprintf( | ||||||
|  |           "    %s\n", | ||||||
|  |           $changeset->getFilename()); | ||||||
|  |       } | ||||||
|  |  | ||||||
|  |       id(new DifferentialChangesetEngine()) | ||||||
|  |         ->rebuildChangesets($changesets); | ||||||
|  |  | ||||||
|  |       foreach ($changesets as $changeset) { | ||||||
|  |         $changeset->save(); | ||||||
|  |       } | ||||||
|  |  | ||||||
|  |       echo tsprintf( | ||||||
|  |         "%s\n", | ||||||
|  |         pht('Done.')); | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |  | ||||||
|  | } | ||||||
| @@ -542,6 +542,12 @@ final class DifferentialChangesetParser extends Phobject { | |||||||
|     PhutilEventEngine::dispatchEvent($event); |     PhutilEventEngine::dispatchEvent($event); | ||||||
|  |  | ||||||
|     $generated = $event->getValue('is_generated'); |     $generated = $event->getValue('is_generated'); | ||||||
|  |  | ||||||
|  |     $attribute = $this->changeset->isGeneratedChangeset(); | ||||||
|  |     if ($attribute) { | ||||||
|  |       $generated = true; | ||||||
|  |     } | ||||||
|  |  | ||||||
|     $this->specialAttributes[self::ATTR_GENERATED] = $generated; |     $this->specialAttributes[self::ATTR_GENERATED] = $generated; | ||||||
|   } |   } | ||||||
|  |  | ||||||
| @@ -1413,167 +1419,6 @@ final class DifferentialChangesetParser extends Phobject { | |||||||
|     return sprintf('%d%%', 100 * ($covered / ($covered + $not_covered))); |     return sprintf('%d%%', 100 * ($covered / ($covered + $not_covered))); | ||||||
|   } |   } | ||||||
|  |  | ||||||
|   public function detectCopiedCode( |  | ||||||
|     array $changesets, |  | ||||||
|     $min_width = 30, |  | ||||||
|     $min_lines = 3) { |  | ||||||
|  |  | ||||||
|     assert_instances_of($changesets, 'DifferentialChangeset'); |  | ||||||
|  |  | ||||||
|     $map = array(); |  | ||||||
|     $files = array(); |  | ||||||
|     $types = array(); |  | ||||||
|     foreach ($changesets as $changeset) { |  | ||||||
|       $file = $changeset->getFilename(); |  | ||||||
|       foreach ($changeset->getHunks() as $hunk) { |  | ||||||
|         $lines = $hunk->getStructuredOldFile(); |  | ||||||
|         foreach ($lines as $line => $info) { |  | ||||||
|           $type = $info['type']; |  | ||||||
|           if ($type == '\\') { |  | ||||||
|             continue; |  | ||||||
|           } |  | ||||||
|           $types[$file][$line] = $type; |  | ||||||
|  |  | ||||||
|           $text = $info['text']; |  | ||||||
|           $text = trim($text); |  | ||||||
|           $files[$file][$line] = $text; |  | ||||||
|  |  | ||||||
|           if (strlen($text) >= $min_width) { |  | ||||||
|             $map[$text][] = array($file, $line); |  | ||||||
|           } |  | ||||||
|         } |  | ||||||
|       } |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     foreach ($changesets as $changeset) { |  | ||||||
|       $copies = array(); |  | ||||||
|       foreach ($changeset->getHunks() as $hunk) { |  | ||||||
|         $added = $hunk->getStructuredNewFile(); |  | ||||||
|         $atype = array(); |  | ||||||
|  |  | ||||||
|         foreach ($added as $line => $info) { |  | ||||||
|           $atype[$line] = $info['type']; |  | ||||||
|           $added[$line] = trim($info['text']); |  | ||||||
|         } |  | ||||||
|  |  | ||||||
|         $skip_lines = 0; |  | ||||||
|         foreach ($added as $line => $code) { |  | ||||||
|           if ($skip_lines) { |  | ||||||
|             // We're skipping lines that we already processed because we |  | ||||||
|             // extended a block above them downward to include them. |  | ||||||
|             $skip_lines--; |  | ||||||
|             continue; |  | ||||||
|           } |  | ||||||
|  |  | ||||||
|           if ($atype[$line] !== '+') { |  | ||||||
|             // This line hasn't been changed in the new file, so don't try |  | ||||||
|             // to figure out where it came from. |  | ||||||
|             continue; |  | ||||||
|           } |  | ||||||
|  |  | ||||||
|           if (empty($map[$code])) { |  | ||||||
|             // This line was too short to trigger copy/move detection. |  | ||||||
|             continue; |  | ||||||
|           } |  | ||||||
|  |  | ||||||
|           if (count($map[$code]) > 16) { |  | ||||||
|             // If there are a large number of identical lines in this diff, |  | ||||||
|             // don't try to figure out where this block came from: the analysis |  | ||||||
|             // is O(N^2), since we need to compare every line against every |  | ||||||
|             // other line. Even if we arrive at a result, it is unlikely to be |  | ||||||
|             // meaningful. See T5041. |  | ||||||
|             continue; |  | ||||||
|           } |  | ||||||
|  |  | ||||||
|           $best_length = 0; |  | ||||||
|  |  | ||||||
|           // Explore all candidates. |  | ||||||
|           foreach ($map[$code] as $val) { |  | ||||||
|             list($file, $orig_line) = $val; |  | ||||||
|             $length = 1; |  | ||||||
|  |  | ||||||
|             // Search backward and forward to find all of the adjacent lines |  | ||||||
|             // which match. |  | ||||||
|             foreach (array(-1, 1) as $direction) { |  | ||||||
|               $offset = $direction; |  | ||||||
|               while (true) { |  | ||||||
|                 if (isset($copies[$line + $offset])) { |  | ||||||
|                   // If we run into a block above us which we've already |  | ||||||
|                   // attributed to a move or copy from elsewhere, stop |  | ||||||
|                   // looking. |  | ||||||
|                   break; |  | ||||||
|                 } |  | ||||||
|  |  | ||||||
|                 if (!isset($added[$line + $offset])) { |  | ||||||
|                   // If we've run off the beginning or end of the new file, |  | ||||||
|                   // stop looking. |  | ||||||
|                   break; |  | ||||||
|                 } |  | ||||||
|  |  | ||||||
|                 if (!isset($files[$file][$orig_line + $offset])) { |  | ||||||
|                   // If we've run off the beginning or end of the original |  | ||||||
|                   // file, we also stop looking. |  | ||||||
|                   break; |  | ||||||
|                 } |  | ||||||
|  |  | ||||||
|                 $old = $files[$file][$orig_line + $offset]; |  | ||||||
|                 $new = $added[$line + $offset]; |  | ||||||
|                 if ($old !== $new) { |  | ||||||
|                   // If the old line doesn't match the new line, stop |  | ||||||
|                   // looking. |  | ||||||
|                   break; |  | ||||||
|                 } |  | ||||||
|  |  | ||||||
|                 $length++; |  | ||||||
|                 $offset += $direction; |  | ||||||
|               } |  | ||||||
|             } |  | ||||||
|  |  | ||||||
|             if ($length < $best_length) { |  | ||||||
|               // If we already know of a better source (more matching lines) |  | ||||||
|               // for this move/copy, stick with that one. We prefer long |  | ||||||
|               // copies/moves which match a lot of context over short ones. |  | ||||||
|               continue; |  | ||||||
|             } |  | ||||||
|  |  | ||||||
|             if ($length == $best_length) { |  | ||||||
|               if (idx($types[$file], $orig_line) != '-') { |  | ||||||
|                 // If we already know of an equally good source (same number |  | ||||||
|                 // of matching lines) and this isn't a move, stick with the |  | ||||||
|                 // other one. We prefer moves over copies. |  | ||||||
|                 continue; |  | ||||||
|               } |  | ||||||
|             } |  | ||||||
|  |  | ||||||
|             $best_length = $length; |  | ||||||
|             // ($offset - 1) contains number of forward matching lines. |  | ||||||
|             $best_offset = $offset - 1; |  | ||||||
|             $best_file = $file; |  | ||||||
|             $best_line = $orig_line; |  | ||||||
|           } |  | ||||||
|  |  | ||||||
|           $file = ($best_file == $changeset->getFilename() ? '' : $best_file); |  | ||||||
|           for ($i = $best_length; $i--; ) { |  | ||||||
|             $type = idx($types[$best_file], $best_line + $best_offset - $i); |  | ||||||
|             $copies[$line + $best_offset - $i] = ($best_length < $min_lines |  | ||||||
|               ? array() // Ignore short blocks. |  | ||||||
|               : array($file, $best_line + $best_offset - $i, $type)); |  | ||||||
|           } |  | ||||||
|  |  | ||||||
|           $skip_lines = $best_offset; |  | ||||||
|         } |  | ||||||
|       } |  | ||||||
|  |  | ||||||
|       $copies = array_filter($copies); |  | ||||||
|       if ($copies) { |  | ||||||
|         $metadata = $changeset->getMetadata(); |  | ||||||
|         $metadata['copy:lines'] = $copies; |  | ||||||
|         $changeset->setMetadata($metadata); |  | ||||||
|       } |  | ||||||
|     } |  | ||||||
|     return $changesets; |  | ||||||
|   } |  | ||||||
|  |  | ||||||
|   /** |   /** | ||||||
|    * Build maps from lines comments appear on to actual lines. |    * Build maps from lines comments appear on to actual lines. | ||||||
|    */ |    */ | ||||||
|   | |||||||
| @@ -68,7 +68,11 @@ final class DifferentialChangesetOneUpRenderer | |||||||
|           $cells = array(); |           $cells = array(); | ||||||
|           if ($is_old) { |           if ($is_old) { | ||||||
|             if ($p['htype']) { |             if ($p['htype']) { | ||||||
|               $class = 'left old'; |               if (empty($p['oline'])) { | ||||||
|  |                 $class = 'left old old-full'; | ||||||
|  |               } else { | ||||||
|  |                 $class = 'left old'; | ||||||
|  |               } | ||||||
|               $aural = $aural_minus; |               $aural = $aural_minus; | ||||||
|             } else { |             } else { | ||||||
|               $class = 'left'; |               $class = 'left'; | ||||||
| @@ -106,7 +110,11 @@ final class DifferentialChangesetOneUpRenderer | |||||||
|             $cells[] = $no_coverage; |             $cells[] = $no_coverage; | ||||||
|           } else { |           } else { | ||||||
|             if ($p['htype']) { |             if ($p['htype']) { | ||||||
|               $class = 'right new'; |               if (empty($p['oline'])) { | ||||||
|  |                 $class = 'right new new-full'; | ||||||
|  |               } else { | ||||||
|  |                 $class = 'right new'; | ||||||
|  |               } | ||||||
|               $cells[] = phutil_tag('th', array('class' => $class)); |               $cells[] = phutil_tag('th', array('class' => $class)); | ||||||
|               $aural = $aural_plus; |               $aural = $aural_plus; | ||||||
|             } else { |             } else { | ||||||
|   | |||||||
| @@ -5,6 +5,7 @@ final class DifferentialRawDiffRenderer extends Phobject { | |||||||
|   private $changesets; |   private $changesets; | ||||||
|   private $format = 'unified'; |   private $format = 'unified'; | ||||||
|   private $viewer; |   private $viewer; | ||||||
|  |   private $byteLimit; | ||||||
|  |  | ||||||
|   public function setFormat($format) { |   public function setFormat($format) { | ||||||
|     $this->format = $format; |     $this->format = $format; | ||||||
| @@ -35,6 +36,15 @@ final class DifferentialRawDiffRenderer extends Phobject { | |||||||
|     return $this->viewer; |     return $this->viewer; | ||||||
|   } |   } | ||||||
|  |  | ||||||
|  |   public function setByteLimit($byte_limit) { | ||||||
|  |     $this->byteLimit = $byte_limit; | ||||||
|  |     return $this; | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   public function getByteLimit() { | ||||||
|  |     return $this->byteLimit; | ||||||
|  |   } | ||||||
|  |  | ||||||
|   public function buildPatch() { |   public function buildPatch() { | ||||||
|     $diff = new DifferentialDiff(); |     $diff = new DifferentialDiff(); | ||||||
|     $diff->attachChangesets($this->getChangesets()); |     $diff->attachChangesets($this->getChangesets()); | ||||||
| @@ -52,15 +62,18 @@ final class DifferentialRawDiffRenderer extends Phobject { | |||||||
|     $bundle = ArcanistBundle::newFromChanges($changes); |     $bundle = ArcanistBundle::newFromChanges($changes); | ||||||
|     $bundle->setLoadFileDataCallback(array($loader, 'loadFileData')); |     $bundle->setLoadFileDataCallback(array($loader, 'loadFileData')); | ||||||
|  |  | ||||||
|  |     $byte_limit = $this->getByteLimit(); | ||||||
|  |     if ($byte_limit) { | ||||||
|  |       $bundle->setByteLimit($byte_limit); | ||||||
|  |     } | ||||||
|  |  | ||||||
|     $format = $this->getFormat(); |     $format = $this->getFormat(); | ||||||
|     switch ($format) { |     switch ($format) { | ||||||
|       case 'git': |       case 'git': | ||||||
|         return $bundle->toGitPatch(); |         return $bundle->toGitPatch(); | ||||||
|         break; |  | ||||||
|       case 'unified': |       case 'unified': | ||||||
|       default: |       default: | ||||||
|         return $bundle->toUnifiedDiff(); |         return $bundle->toUnifiedDiff(); | ||||||
|         break; |  | ||||||
|     } |     } | ||||||
|   } |   } | ||||||
| } | } | ||||||
|   | |||||||
| @@ -12,7 +12,7 @@ final class DifferentialChangeset | |||||||
|   protected $awayPaths; |   protected $awayPaths; | ||||||
|   protected $changeType; |   protected $changeType; | ||||||
|   protected $fileType; |   protected $fileType; | ||||||
|   protected $metadata; |   protected $metadata = array(); | ||||||
|   protected $oldProperties; |   protected $oldProperties; | ||||||
|   protected $newProperties; |   protected $newProperties; | ||||||
|   protected $addLines; |   protected $addLines; | ||||||
| @@ -24,6 +24,12 @@ final class DifferentialChangeset | |||||||
|  |  | ||||||
|   const TABLE_CACHE = 'differential_changeset_parse_cache'; |   const TABLE_CACHE = 'differential_changeset_parse_cache'; | ||||||
|  |  | ||||||
|  |   const METADATA_TRUSTED_ATTRIBUTES = 'attributes.trusted'; | ||||||
|  |   const METADATA_UNTRUSTED_ATTRIBUTES = 'attributes.untrusted'; | ||||||
|  |   const METADATA_EFFECT_HASH = 'hash.effect'; | ||||||
|  |  | ||||||
|  |   const ATTRIBUTE_GENERATED = 'generated'; | ||||||
|  |  | ||||||
|   protected function getConfiguration() { |   protected function getConfiguration() { | ||||||
|     return array( |     return array( | ||||||
|       self::CONFIG_SERIALIZATION => array( |       self::CONFIG_SERIALIZATION => array( | ||||||
| @@ -138,6 +144,48 @@ final class DifferentialChangeset | |||||||
|     return $ret; |     return $ret; | ||||||
|   } |   } | ||||||
|  |  | ||||||
|  |   /** | ||||||
|  |    * Test if this changeset and some other changeset put the affected file in | ||||||
|  |    * the same state. | ||||||
|  |    * | ||||||
|  |    * @param DifferentialChangeset Changeset to compare against. | ||||||
|  |    * @return bool True if the two changesets have the same effect. | ||||||
|  |    */ | ||||||
|  |   public function hasSameEffectAs(DifferentialChangeset $other) { | ||||||
|  |     if ($this->getFilename() !== $other->getFilename()) { | ||||||
|  |       return false; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     $hash_key = self::METADATA_EFFECT_HASH; | ||||||
|  |  | ||||||
|  |     $u_hash = $this->getChangesetMetadata($hash_key); | ||||||
|  |     if ($u_hash === null) { | ||||||
|  |       return false; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     $v_hash = $other->getChangesetMetadata($hash_key); | ||||||
|  |     if ($v_hash === null) { | ||||||
|  |       return false; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     if ($u_hash !== $v_hash) { | ||||||
|  |       return false; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     // Make sure the final states for the file properties (like the "+x" | ||||||
|  |     // executable bit) match one another. | ||||||
|  |     $u_props = $this->getNewProperties(); | ||||||
|  |     $v_props = $other->getNewProperties(); | ||||||
|  |     ksort($u_props); | ||||||
|  |     ksort($v_props); | ||||||
|  |  | ||||||
|  |     if ($u_props !== $v_props) { | ||||||
|  |       return false; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     return true; | ||||||
|  |   } | ||||||
|  |  | ||||||
|   public function getSortKey() { |   public function getSortKey() { | ||||||
|     $sort_key = $this->getFilename(); |     $sort_key = $this->getFilename(); | ||||||
|     // Sort files with ".h" in them first, so headers (.h, .hpp) come before |     // Sort files with ".h" in them first, so headers (.h, .hpp) come before | ||||||
| @@ -266,6 +314,90 @@ final class DifferentialChangeset | |||||||
|     return null; |     return null; | ||||||
|   } |   } | ||||||
|  |  | ||||||
|  |   public function setChangesetMetadata($key, $value) { | ||||||
|  |     if (!is_array($this->metadata)) { | ||||||
|  |       $this->metadata = array(); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     $this->metadata[$key] = $value; | ||||||
|  |  | ||||||
|  |     return $this; | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   public function getChangesetMetadata($key, $default = null) { | ||||||
|  |     if (!is_array($this->metadata)) { | ||||||
|  |       return $default; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     return idx($this->metadata, $key, $default); | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   private function setInternalChangesetAttribute($trusted, $key, $value) { | ||||||
|  |     if ($trusted) { | ||||||
|  |       $meta_key = self::METADATA_TRUSTED_ATTRIBUTES; | ||||||
|  |     } else { | ||||||
|  |       $meta_key = self::METADATA_UNTRUSTED_ATTRIBUTES; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     $attributes = $this->getChangesetMetadata($meta_key, array()); | ||||||
|  |     $attributes[$key] = $value; | ||||||
|  |     $this->setChangesetMetadata($meta_key, $attributes); | ||||||
|  |  | ||||||
|  |     return $this; | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   private function getInternalChangesetAttributes($trusted) { | ||||||
|  |     if ($trusted) { | ||||||
|  |       $meta_key = self::METADATA_TRUSTED_ATTRIBUTES; | ||||||
|  |     } else { | ||||||
|  |       $meta_key = self::METADATA_UNTRUSTED_ATTRIBUTES; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     return $this->getChangesetMetadata($meta_key, array()); | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   public function setTrustedChangesetAttribute($key, $value) { | ||||||
|  |     return $this->setInternalChangesetAttribute(true, $key, $value); | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   public function getTrustedChangesetAttributes() { | ||||||
|  |     return $this->getInternalChangesetAttributes(true); | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   public function getTrustedChangesetAttribute($key, $default = null) { | ||||||
|  |     $map = $this->getTrustedChangesetAttributes(); | ||||||
|  |     return idx($map, $key, $default); | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   public function setUntrustedChangesetAttribute($key, $value) { | ||||||
|  |     return $this->setInternalChangesetAttribute(false, $key, $value); | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   public function getUntrustedChangesetAttributes() { | ||||||
|  |     return $this->getInternalChangesetAttributes(false); | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   public function getUntrustedChangesetAttribute($key, $default = null) { | ||||||
|  |     $map = $this->getUntrustedChangesetAttributes(); | ||||||
|  |     return idx($map, $key, $default); | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   public function getChangesetAttributes() { | ||||||
|  |     // Prefer trusted values over untrusted values when both exist. | ||||||
|  |     return | ||||||
|  |       $this->getTrustedChangesetAttributes() + | ||||||
|  |       $this->getUntrustedChangesetAttributes(); | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   public function getChangesetAttribute($key, $default = null) { | ||||||
|  |     $map = $this->getChangesetAttributes(); | ||||||
|  |     return idx($map, $key, $default); | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   public function isGeneratedChangeset() { | ||||||
|  |     return $this->getChangesetAttribute(self::ATTRIBUTE_GENERATED); | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |  | ||||||
| /* -(  PhabricatorPolicyInterface  )----------------------------------------- */ | /* -(  PhabricatorPolicyInterface  )----------------------------------------- */ | ||||||
|  |  | ||||||
|   | |||||||
| @@ -230,17 +230,14 @@ final class DifferentialDiff | |||||||
|     } |     } | ||||||
|     $diff->setLineCount($lines); |     $diff->setLineCount($lines); | ||||||
|  |  | ||||||
|     $parser = new DifferentialChangesetParser(); |     $changesets = $diff->getChangesets(); | ||||||
|     $changesets = $parser->detectCopiedCode( |  | ||||||
|       $diff->getChangesets(), |     id(new DifferentialChangesetEngine()) | ||||||
|       $min_width = 30, |       ->rebuildChangesets($changesets); | ||||||
|       $min_lines = 3); |  | ||||||
|     $diff->attachChangesets($changesets); |  | ||||||
|  |  | ||||||
|     return $diff; |     return $diff; | ||||||
|   } |   } | ||||||
|  |  | ||||||
|  |  | ||||||
|   public function getDiffDict() { |   public function getDiffDict() { | ||||||
|     $dict = array( |     $dict = array( | ||||||
|       'id' => $this->getID(), |       'id' => $this->getID(), | ||||||
| @@ -821,5 +818,4 @@ final class DifferentialDiff | |||||||
|     ); |     ); | ||||||
|   } |   } | ||||||
|  |  | ||||||
|  |  | ||||||
| } | } | ||||||
|   | |||||||
| @@ -1148,6 +1148,10 @@ final class DifferentialRevision extends DifferentialDAO | |||||||
|         ->setKey('summary') |         ->setKey('summary') | ||||||
|         ->setType('string') |         ->setType('string') | ||||||
|         ->setDescription(pht('Revision summary.')), |         ->setDescription(pht('Revision summary.')), | ||||||
|  |       id(new PhabricatorConduitSearchFieldSpecification()) | ||||||
|  |         ->setKey('testPlan') | ||||||
|  |         ->setType('string') | ||||||
|  |         ->setDescription(pht('Revision test plan.')), | ||||||
|     ); |     ); | ||||||
|   } |   } | ||||||
|  |  | ||||||
| @@ -1167,6 +1171,7 @@ final class DifferentialRevision extends DifferentialDAO | |||||||
|       'repositoryPHID' => $this->getRepositoryPHID(), |       'repositoryPHID' => $this->getRepositoryPHID(), | ||||||
|       'diffPHID' => $this->getActiveDiffPHID(), |       'diffPHID' => $this->getActiveDiffPHID(), | ||||||
|       'summary' => $this->getSummary(), |       'summary' => $this->getSummary(), | ||||||
|  |       'testPlan' => $this->getTestPlan(), | ||||||
|     ); |     ); | ||||||
|   } |   } | ||||||
|  |  | ||||||
|   | |||||||
| @@ -307,7 +307,7 @@ final class DifferentialRevisionUpdateHistoryView extends AphrontView { | |||||||
|     $content = phabricator_form( |     $content = phabricator_form( | ||||||
|       $this->getUser(), |       $this->getUser(), | ||||||
|       array( |       array( | ||||||
|         'action' => '#toc', |         'action' => '/D'.$revision_id.'#toc', | ||||||
|       ), |       ), | ||||||
|       array( |       array( | ||||||
|         $table, |         $table, | ||||||
|   | |||||||
| @@ -86,4 +86,12 @@ final class DifferentialRevisionAbandonTransaction | |||||||
|       $this->renderObject()); |       $this->renderObject()); | ||||||
|   } |   } | ||||||
|  |  | ||||||
|  |   public function getTransactionTypeForConduit($xaction) { | ||||||
|  |     return 'abandon'; | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   public function getFieldValuesForConduit($object, $data) { | ||||||
|  |     return array(); | ||||||
|  |   } | ||||||
|  |  | ||||||
| } | } | ||||||
|   | |||||||
| @@ -7,7 +7,7 @@ final class DifferentialRevisionAcceptTransaction | |||||||
|   const ACTIONKEY = 'accept'; |   const ACTIONKEY = 'accept'; | ||||||
|  |  | ||||||
|   protected function getRevisionActionLabel() { |   protected function getRevisionActionLabel() { | ||||||
|     return pht("Accept Revision \xE2\x9C\x94"); |     return pht('Accept Revision'); | ||||||
|   } |   } | ||||||
|  |  | ||||||
|   protected function getRevisionActionDescription( |   protected function getRevisionActionDescription( | ||||||
| @@ -234,4 +234,12 @@ final class DifferentialRevisionAcceptTransaction | |||||||
|       $this->renderObject()); |       $this->renderObject()); | ||||||
|   } |   } | ||||||
|  |  | ||||||
|  |   public function getTransactionTypeForConduit($xaction) { | ||||||
|  |     return 'accept'; | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   public function getFieldValuesForConduit($object, $data) { | ||||||
|  |     return array(); | ||||||
|  |   } | ||||||
|  |  | ||||||
| } | } | ||||||
|   | |||||||
| @@ -154,5 +154,4 @@ final class DifferentialRevisionCloseTransaction | |||||||
|     ); |     ); | ||||||
|   } |   } | ||||||
|  |  | ||||||
|  |  | ||||||
| } | } | ||||||
|   | |||||||
| @@ -87,4 +87,12 @@ final class DifferentialRevisionCommandeerTransaction | |||||||
|       $this->renderObject()); |       $this->renderObject()); | ||||||
|   } |   } | ||||||
|  |  | ||||||
|  |   public function getTransactionTypeForConduit($xaction) { | ||||||
|  |     return 'commandeer'; | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   public function getFieldValuesForConduit($object, $data) { | ||||||
|  |     return array(); | ||||||
|  |   } | ||||||
|  |  | ||||||
| } | } | ||||||
|   | |||||||
| @@ -119,4 +119,12 @@ final class DifferentialRevisionPlanChangesTransaction | |||||||
|     return (bool)$this->getMetadataValue('draft.demote'); |     return (bool)$this->getMetadataValue('draft.demote'); | ||||||
|   } |   } | ||||||
|  |  | ||||||
|  |   public function getTransactionTypeForConduit($xaction) { | ||||||
|  |     return 'plan-changes'; | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   public function getFieldValuesForConduit($object, $data) { | ||||||
|  |     return array(); | ||||||
|  |   } | ||||||
|  |  | ||||||
| } | } | ||||||
|   | |||||||
| @@ -85,4 +85,12 @@ final class DifferentialRevisionReclaimTransaction | |||||||
|       $this->renderObject()); |       $this->renderObject()); | ||||||
|   } |   } | ||||||
|  |  | ||||||
|  |   public function getTransactionTypeForConduit($xaction) { | ||||||
|  |     return 'reclaim'; | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   public function getFieldValuesForConduit($object, $data) { | ||||||
|  |     return array(); | ||||||
|  |   } | ||||||
|  |  | ||||||
| } | } | ||||||
|   | |||||||
| @@ -7,7 +7,7 @@ final class DifferentialRevisionRejectTransaction | |||||||
|   const ACTIONKEY = 'reject'; |   const ACTIONKEY = 'reject'; | ||||||
|  |  | ||||||
|   protected function getRevisionActionLabel() { |   protected function getRevisionActionLabel() { | ||||||
|     return pht("Request Changes \xE2\x9C\x98"); |     return pht('Request Changes'); | ||||||
|   } |   } | ||||||
|  |  | ||||||
|   protected function getRevisionActionDescription( |   protected function getRevisionActionDescription( | ||||||
| @@ -99,4 +99,12 @@ final class DifferentialRevisionRejectTransaction | |||||||
|       $this->renderObject()); |       $this->renderObject()); | ||||||
|   } |   } | ||||||
|  |  | ||||||
|  |   public function getTransactionTypeForConduit($xaction) { | ||||||
|  |     return 'request-changes'; | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   public function getFieldValuesForConduit($object, $data) { | ||||||
|  |     return array(); | ||||||
|  |   } | ||||||
|  |  | ||||||
| } | } | ||||||
|   | |||||||
| @@ -72,4 +72,12 @@ final class DifferentialRevisionReopenTransaction | |||||||
|       $this->renderObject()); |       $this->renderObject()); | ||||||
|   } |   } | ||||||
|  |  | ||||||
|  |   public function getTransactionTypeForConduit($xaction) { | ||||||
|  |     return 'reopen'; | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   public function getFieldValuesForConduit($object, $data) { | ||||||
|  |     return array(); | ||||||
|  |   } | ||||||
|  |  | ||||||
| } | } | ||||||
|   | |||||||
| @@ -84,4 +84,12 @@ final class DifferentialRevisionRequestReviewTransaction | |||||||
|       $this->renderObject()); |       $this->renderObject()); | ||||||
|   } |   } | ||||||
|  |  | ||||||
|  |   public function getTransactionTypeForConduit($xaction) { | ||||||
|  |     return 'request-review'; | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   public function getFieldValuesForConduit($object, $data) { | ||||||
|  |     return array(); | ||||||
|  |   } | ||||||
|  |  | ||||||
| } | } | ||||||
|   | |||||||
| @@ -93,4 +93,12 @@ final class DifferentialRevisionResignTransaction | |||||||
|       $this->renderObject()); |       $this->renderObject()); | ||||||
|   } |   } | ||||||
|  |  | ||||||
|  |   public function getTransactionTypeForConduit($xaction) { | ||||||
|  |     return 'resign'; | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   public function getFieldValuesForConduit($object, $data) { | ||||||
|  |     return array(); | ||||||
|  |   } | ||||||
|  |  | ||||||
| } | } | ||||||
|   | |||||||
| @@ -124,6 +124,15 @@ final class PhabricatorDiffusionApplication extends PhabricatorApplication { | |||||||
|         '(?P<repositoryCallsign>[A-Z]+)' => $repository_routes, |         '(?P<repositoryCallsign>[A-Z]+)' => $repository_routes, | ||||||
|         '(?P<repositoryID>[1-9]\d*)' => $repository_routes, |         '(?P<repositoryID>[1-9]\d*)' => $repository_routes, | ||||||
|  |  | ||||||
|  |         'identity/' => array( | ||||||
|  |           $this->getQueryRoutePattern() => | ||||||
|  |             'DiffusionIdentityListController', | ||||||
|  |           $this->getEditRoutePattern('edit/') => | ||||||
|  |             'DiffusionIdentityEditController', | ||||||
|  |           'view/(?P<id>[^/]+)/' => | ||||||
|  |             'DiffusionIdentityViewController', | ||||||
|  |         ), | ||||||
|  |  | ||||||
|         'inline/' => array( |         'inline/' => array( | ||||||
|           'edit/(?P<phid>[^/]+)/' => 'DiffusionInlineCommentController', |           'edit/(?P<phid>[^/]+)/' => 'DiffusionInlineCommentController', | ||||||
|           'preview/(?P<phid>[^/]+)/' |           'preview/(?P<phid>[^/]+)/' | ||||||
|   | |||||||
| @@ -30,18 +30,31 @@ final class DiffusionBranchQueryConduitAPIMethod | |||||||
|  |  | ||||||
|     $contains = $request->getValue('contains'); |     $contains = $request->getValue('contains'); | ||||||
|     if (strlen($contains)) { |     if (strlen($contains)) { | ||||||
|  |  | ||||||
|  |       // See PHI720. If the standard "branch" field is provided, use it | ||||||
|  |       // as the "pattern" argument to "git branch ..." to let callers test | ||||||
|  |       // for reachability from a particular branch head. | ||||||
|  |       $pattern = $request->getValue('branch'); | ||||||
|  |       if (strlen($pattern)) { | ||||||
|  |         $pattern_argv = array($pattern); | ||||||
|  |       } else { | ||||||
|  |         $pattern_argv = array(); | ||||||
|  |       } | ||||||
|  |  | ||||||
|       // NOTE: We can't use DiffusionLowLevelGitRefQuery here because |       // NOTE: We can't use DiffusionLowLevelGitRefQuery here because | ||||||
|       // `git for-each-ref` does not support `--contains`. |       // `git for-each-ref` does not support `--contains`. | ||||||
|       if ($repository->isWorkingCopyBare()) { |       if ($repository->isWorkingCopyBare()) { | ||||||
|         list($stdout) = $repository->execxLocalCommand( |         list($stdout) = $repository->execxLocalCommand( | ||||||
|           'branch --verbose --no-abbrev --contains %s --', |           'branch --verbose --no-abbrev --contains %s -- %Ls', | ||||||
|           $contains); |           $contains, | ||||||
|  |           $pattern_argv); | ||||||
|         $ref_map = DiffusionGitBranch::parseLocalBranchOutput( |         $ref_map = DiffusionGitBranch::parseLocalBranchOutput( | ||||||
|           $stdout); |           $stdout); | ||||||
|       } else { |       } else { | ||||||
|         list($stdout) = $repository->execxLocalCommand( |         list($stdout) = $repository->execxLocalCommand( | ||||||
|           'branch -r --verbose --no-abbrev --contains %s --', |           'branch -r --verbose --no-abbrev --contains %s -- %Ls', | ||||||
|           $contains); |           $contains, | ||||||
|  |           $pattern_argv); | ||||||
|         $ref_map = DiffusionGitBranch::parseRemoteBranchOutput( |         $ref_map = DiffusionGitBranch::parseRemoteBranchOutput( | ||||||
|           $stdout, |           $stdout, | ||||||
|           DiffusionGitBranch::DEFAULT_GIT_REMOTE); |           DiffusionGitBranch::DEFAULT_GIT_REMOTE); | ||||||
|   | |||||||
| @@ -0,0 +1,51 @@ | |||||||
|  | <?php | ||||||
|  |  | ||||||
|  | final class DiffusionInternalAncestorsConduitAPIMethod | ||||||
|  |   extends DiffusionQueryConduitAPIMethod { | ||||||
|  |  | ||||||
|  |   public function isInternalAPI() { | ||||||
|  |     return true; | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   public function getAPIMethodName() { | ||||||
|  |     return 'diffusion.internal.ancestors'; | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   public function getMethodDescription() { | ||||||
|  |     return pht('Internal method for filtering ref ancestors.'); | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   protected function defineReturnType() { | ||||||
|  |     return 'list<string>'; | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   protected function defineCustomParamTypes() { | ||||||
|  |     return array( | ||||||
|  |       'ref' => 'required string', | ||||||
|  |       'commits' => 'required list<string>', | ||||||
|  |     ); | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   protected function getResult(ConduitAPIRequest $request) { | ||||||
|  |     $drequest = $this->getDiffusionRequest(); | ||||||
|  |     $repository = $drequest->getRepository(); | ||||||
|  |  | ||||||
|  |     $commits = $request->getValue('commits'); | ||||||
|  |     $ref = $request->getValue('ref'); | ||||||
|  |  | ||||||
|  |     $graph = new PhabricatorGitGraphStream($repository, $ref); | ||||||
|  |  | ||||||
|  |     $keep = array(); | ||||||
|  |     foreach ($commits as $identifier) { | ||||||
|  |       try { | ||||||
|  |         $graph->getCommitDate($identifier); | ||||||
|  |         $keep[] = $identifier; | ||||||
|  |       } catch (Exception $ex) { | ||||||
|  |         // Not an ancestor. | ||||||
|  |       } | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     return $keep; | ||||||
|  |   } | ||||||
|  |  | ||||||
|  | } | ||||||
| @@ -22,6 +22,7 @@ final class DiffusionBranchTableController extends DiffusionController { | |||||||
|     $params = array( |     $params = array( | ||||||
|       'offset' => $pager->getOffset(), |       'offset' => $pager->getOffset(), | ||||||
|       'limit' => $pager->getPageSize() + 1, |       'limit' => $pager->getPageSize() + 1, | ||||||
|  |       'branch' => null, | ||||||
|     ); |     ); | ||||||
|  |  | ||||||
|     $contains = $drequest->getSymbolicCommit(); |     $contains = $drequest->getSymbolicCommit(); | ||||||
|   | |||||||
| @@ -22,6 +22,7 @@ final class DiffusionCommitBranchesController extends DiffusionController { | |||||||
|         array( |         array( | ||||||
|           'contains' => $drequest->getCommit(), |           'contains' => $drequest->getCommit(), | ||||||
|           'limit' => $branch_limit + 1, |           'limit' => $branch_limit + 1, | ||||||
|  |           'branch' => null, | ||||||
|         ))); |         ))); | ||||||
|  |  | ||||||
|     $has_more_branches = (count($branches) > $branch_limit); |     $has_more_branches = (count($branches) > $branch_limit); | ||||||
|   | |||||||
| @@ -0,0 +1,12 @@ | |||||||
|  | <?php | ||||||
|  |  | ||||||
|  | final class DiffusionIdentityEditController | ||||||
|  |   extends DiffusionController { | ||||||
|  |  | ||||||
|  |   public function handleRequest(AphrontRequest $request) { | ||||||
|  |     return id(new PhabricatorRepositoryIdentityEditEngine()) | ||||||
|  |       ->setController($this) | ||||||
|  |       ->buildResponse(); | ||||||
|  |   } | ||||||
|  |  | ||||||
|  | } | ||||||
| @@ -0,0 +1,22 @@ | |||||||
|  | <?php | ||||||
|  |  | ||||||
|  | final class DiffusionIdentityListController | ||||||
|  |   extends DiffusionController { | ||||||
|  |  | ||||||
|  |   public function handleRequest(AphrontRequest $request) { | ||||||
|  |     return id(new DiffusionRepositoryIdentitySearchEngine()) | ||||||
|  |       ->setController($this) | ||||||
|  |       ->buildResponse(); | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   protected function buildApplicationCrumbs() { | ||||||
|  |     $crumbs = parent::buildApplicationCrumbs(); | ||||||
|  |  | ||||||
|  |     id(new PhabricatorRepositoryIdentityEditEngine()) | ||||||
|  |       ->setViewer($this->getViewer()) | ||||||
|  |       ->addActionToCrumbs($crumbs); | ||||||
|  |  | ||||||
|  |     return $crumbs; | ||||||
|  |   } | ||||||
|  |  | ||||||
|  | } | ||||||
| @@ -0,0 +1,135 @@ | |||||||
|  | <?php | ||||||
|  |  | ||||||
|  | final class DiffusionIdentityViewController | ||||||
|  |   extends DiffusionController { | ||||||
|  |  | ||||||
|  |   public function handleRequest(AphrontRequest $request) { | ||||||
|  |     $viewer = $request->getViewer(); | ||||||
|  |  | ||||||
|  |     $id = $request->getURIData('id'); | ||||||
|  |     $identity = id(new PhabricatorRepositoryIdentityQuery()) | ||||||
|  |       ->setViewer($viewer) | ||||||
|  |       ->withIDs(array($id)) | ||||||
|  |       ->executeOne(); | ||||||
|  |     if (!$identity) { | ||||||
|  |       return new Aphront404Response(); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     $title = pht('Identity %d', $identity->getID()); | ||||||
|  |  | ||||||
|  |     $curtain = $this->buildCurtain($identity); | ||||||
|  |  | ||||||
|  |     $header = id(new PHUIHeaderView()) | ||||||
|  |       ->setUser($viewer) | ||||||
|  |       ->setHeader($identity->getIdentityShortName()) | ||||||
|  |       ->setHeaderIcon('fa-globe') | ||||||
|  |       ->setPolicyObject($identity); | ||||||
|  |  | ||||||
|  |     $crumbs = $this->buildApplicationCrumbs(); | ||||||
|  |     $crumbs->addTextCrumb($identity->getID()); | ||||||
|  |     $crumbs->setBorder(true); | ||||||
|  |  | ||||||
|  |     $timeline = $this->buildTransactionTimeline( | ||||||
|  |       $identity, | ||||||
|  |       new PhabricatorRepositoryIdentityTransactionQuery()); | ||||||
|  |     $timeline->setShouldTerminate(true); | ||||||
|  |  | ||||||
|  |     $properties = $this->buildPropertyList($identity); | ||||||
|  |  | ||||||
|  |     $view = id(new PHUITwoColumnView()) | ||||||
|  |       ->setHeader($header) | ||||||
|  |       ->setCurtain($curtain) | ||||||
|  |       ->setMainColumn(array( | ||||||
|  |           $properties, | ||||||
|  |           $timeline, | ||||||
|  |         )); | ||||||
|  |  | ||||||
|  |     return $this->newPage() | ||||||
|  |       ->setTitle($title) | ||||||
|  |       ->setCrumbs($crumbs) | ||||||
|  |       ->appendChild( | ||||||
|  |         array( | ||||||
|  |           $view, | ||||||
|  |       )); | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   private function buildCurtain(PhabricatorRepositoryIdentity $identity) { | ||||||
|  |     $viewer = $this->getViewer(); | ||||||
|  |  | ||||||
|  |     $can_edit = PhabricatorPolicyFilter::hasCapability( | ||||||
|  |       $viewer, | ||||||
|  |       $identity, | ||||||
|  |       PhabricatorPolicyCapability::CAN_EDIT); | ||||||
|  |  | ||||||
|  |     $id = $identity->getID(); | ||||||
|  |     $edit_uri = $this->getApplicationURI("identity/edit/{$id}/"); | ||||||
|  |  | ||||||
|  |     $curtain = $this->newCurtainView($identity); | ||||||
|  |  | ||||||
|  |     $curtain->addAction( | ||||||
|  |       id(new PhabricatorActionView()) | ||||||
|  |         ->setIcon('fa-pencil') | ||||||
|  |         ->setName(pht('Edit Identity')) | ||||||
|  |         ->setHref($edit_uri) | ||||||
|  |         ->setWorkflow(!$can_edit) | ||||||
|  |         ->setDisabled(!$can_edit)); | ||||||
|  |  | ||||||
|  |     return $curtain; | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   private function buildPropertyList( | ||||||
|  |     PhabricatorRepositoryIdentity $identity) { | ||||||
|  |  | ||||||
|  |     $viewer = $this->getViewer(); | ||||||
|  |  | ||||||
|  |     $properties = id(new PHUIPropertyListView()) | ||||||
|  |       ->setUser($viewer); | ||||||
|  |  | ||||||
|  |     $effective_phid = $identity->getCurrentEffectiveUserPHID(); | ||||||
|  |     $automatic_phid = $identity->getAutomaticGuessedUserPHID(); | ||||||
|  |     $manual_phid = $identity->getManuallySetUserPHID(); | ||||||
|  |  | ||||||
|  |     if ($effective_phid) { | ||||||
|  |       $tag = id(new PHUITagView()) | ||||||
|  |         ->setType(PHUITagView::TYPE_SHADE) | ||||||
|  |         ->setColor('green') | ||||||
|  |         ->setIcon('fa-check') | ||||||
|  |         ->setName('Assigned'); | ||||||
|  |     } else { | ||||||
|  |       $tag = id(new PHUITagView()) | ||||||
|  |         ->setType(PHUITagView::TYPE_SHADE) | ||||||
|  |         ->setColor('indigo') | ||||||
|  |         ->setIcon('fa-bomb') | ||||||
|  |         ->setName('Unassigned'); | ||||||
|  |     } | ||||||
|  |     $properties->addProperty( | ||||||
|  |       pht('Effective User'), | ||||||
|  |       $this->buildPropertyValue($effective_phid)); | ||||||
|  |     $properties->addProperty( | ||||||
|  |       pht('Automatically Detected User'), | ||||||
|  |       $this->buildPropertyValue($automatic_phid)); | ||||||
|  |     $properties->addProperty( | ||||||
|  |       pht('Manually Set User'), | ||||||
|  |       $this->buildPropertyValue($manual_phid)); | ||||||
|  |  | ||||||
|  |     $header = id(new PHUIHeaderView()) | ||||||
|  |       ->setHeader(array(pht('Identity Assignments'), $tag)); | ||||||
|  |  | ||||||
|  |     return id(new PHUIObjectBoxView()) | ||||||
|  |       ->setHeader($header) | ||||||
|  |       ->setBackground(PHUIObjectBoxView::BLUE_PROPERTY) | ||||||
|  |       ->addPropertyList($properties); | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   private function buildPropertyValue($value) { | ||||||
|  |     $viewer = $this->getViewer(); | ||||||
|  |  | ||||||
|  |     if ($value == DiffusionIdentityUnassignedDatasource::FUNCTION_TOKEN) { | ||||||
|  |       return phutil_tag('em', array(), pht('Explicitly Unassigned')); | ||||||
|  |     } else if (!$value) { | ||||||
|  |       return null; | ||||||
|  |     } else { | ||||||
|  |       return $viewer->renderHandle($value); | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  | } | ||||||
| @@ -69,7 +69,7 @@ final class DiffusionDocumentRenderingEngine | |||||||
|     return; |     return; | ||||||
|   } |   } | ||||||
|  |  | ||||||
|   protected function willRenderRef(PhabricatorDocumentRef $ref) { |   protected function willStageRef(PhabricatorDocumentRef $ref) { | ||||||
|     $drequest = $this->getDiffusionRequest(); |     $drequest = $this->getDiffusionRequest(); | ||||||
|  |  | ||||||
|     $blame_uri = (string)$drequest->generateURI( |     $blame_uri = (string)$drequest->generateURI( | ||||||
| @@ -78,9 +78,13 @@ final class DiffusionDocumentRenderingEngine | |||||||
|         'stable' => true, |         'stable' => true, | ||||||
|       )); |       )); | ||||||
|  |  | ||||||
|     $ref |     $ref->setBlameURI($blame_uri); | ||||||
|       ->setSymbolMetadata($this->getSymbolMetadata()) |   } | ||||||
|       ->setBlameURI($blame_uri); |  | ||||||
|  |   protected function willRenderRef(PhabricatorDocumentRef $ref) { | ||||||
|  |     $drequest = $this->getDiffusionRequest(); | ||||||
|  |  | ||||||
|  |     $ref->setSymbolMetadata($this->getSymbolMetadata()); | ||||||
|  |  | ||||||
|     $coverage = $drequest->loadCoverage(); |     $coverage = $drequest->loadCoverage(); | ||||||
|     if (strlen($coverage)) { |     if (strlen($coverage)) { | ||||||
|   | |||||||
| @@ -0,0 +1,22 @@ | |||||||
|  | <?php | ||||||
|  |  | ||||||
|  | final class DiffusionIdentityAssigneeEditField | ||||||
|  |   extends PhabricatorTokenizerEditField { | ||||||
|  |  | ||||||
|  |   protected function newDatasource() { | ||||||
|  |     return new DiffusionIdentityAssigneeDatasource(); | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   protected function newHTTPParameterType() { | ||||||
|  |     return new AphrontUserListHTTPParameterType(); | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   protected function newConduitParameterType() { | ||||||
|  |     if ($this->getIsSingleValue()) { | ||||||
|  |       return new ConduitUserParameterType(); | ||||||
|  |     } else { | ||||||
|  |       return new ConduitUserListParameterType(); | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  |  | ||||||
|  | } | ||||||
| @@ -0,0 +1,26 @@ | |||||||
|  | <?php | ||||||
|  |  | ||||||
|  | final class DiffusionRepositoryIdentityEditor | ||||||
|  |   extends PhabricatorApplicationTransactionEditor { | ||||||
|  |  | ||||||
|  |   public function getEditorObjectsDescription() { | ||||||
|  |     return pht('Repository Identity'); | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   public function getCreateObjectTitle($author, $object) { | ||||||
|  |     return pht('%s created this identity.', $author); | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   public function getCreateObjectTitleForFeed($author, $object) { | ||||||
|  |     return pht('%s created %s.', $author, $object); | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   protected function supportsSearch() { | ||||||
|  |     return true; | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   public function getEditorApplicationClass() { | ||||||
|  |     return 'PhabricatorDiffusionApplication'; | ||||||
|  |   } | ||||||
|  |  | ||||||
|  | } | ||||||
| @@ -37,6 +37,7 @@ final class DiffusionCommitHookEngine extends Phobject { | |||||||
|   private $rejectDetails; |   private $rejectDetails; | ||||||
|   private $emailPHIDs = array(); |   private $emailPHIDs = array(); | ||||||
|   private $changesets = array(); |   private $changesets = array(); | ||||||
|  |   private $changesetsSize = 0; | ||||||
|  |  | ||||||
|  |  | ||||||
| /* -(  Config  )------------------------------------------------------------- */ | /* -(  Config  )------------------------------------------------------------- */ | ||||||
| @@ -1121,11 +1122,22 @@ final class DiffusionCommitHookEngine extends Phobject { | |||||||
|       return; |       return; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|  |     // See T13142. Don't cache more than 64MB of changesets. For normal small | ||||||
|  |     // pushes, caching everything here can let us hit the cache from Herald if | ||||||
|  |     // we need to run content rules, which speeds things up a bit. For large | ||||||
|  |     // pushes, we may not be able to hold everything in memory. | ||||||
|  |     $cache_limit = 1024 * 1024 * 64; | ||||||
|  |  | ||||||
|     foreach ($content_updates as $update) { |     foreach ($content_updates as $update) { | ||||||
|       $identifier = $update->getRefNew(); |       $identifier = $update->getRefNew(); | ||||||
|       try { |       try { | ||||||
|         $changesets = $this->loadChangesetsForCommit($identifier); |         $info = $this->loadChangesetsForCommit($identifier); | ||||||
|         $this->changesets[$identifier] = $changesets; |         list($changesets, $size) = $info; | ||||||
|  |  | ||||||
|  |         if ($this->changesetsSize + $size <= $cache_limit) { | ||||||
|  |           $this->changesets[$identifier] = $changesets; | ||||||
|  |           $this->changesetsSize += $size; | ||||||
|  |         } | ||||||
|       } catch (Exception $ex) { |       } catch (Exception $ex) { | ||||||
|         $this->changesets[$identifier] = $ex; |         $this->changesets[$identifier] = $ex; | ||||||
|  |  | ||||||
| @@ -1200,14 +1212,18 @@ final class DiffusionCommitHookEngine extends Phobject { | |||||||
|  |  | ||||||
|     if (!strlen($raw_diff)) { |     if (!strlen($raw_diff)) { | ||||||
|       // If the commit is actually empty, just return no changesets. |       // If the commit is actually empty, just return no changesets. | ||||||
|       return array(); |       return array(array(), 0); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     $parser = new ArcanistDiffParser(); |     $parser = new ArcanistDiffParser(); | ||||||
|     $changes = $parser->parseDiff($raw_diff); |     $changes = $parser->parseDiff($raw_diff); | ||||||
|     $diff = DifferentialDiff::newEphemeralFromRawChanges( |     $diff = DifferentialDiff::newEphemeralFromRawChanges( | ||||||
|       $changes); |       $changes); | ||||||
|     return $diff->getChangesets(); |  | ||||||
|  |     $changesets = $diff->getChangesets(); | ||||||
|  |     $size = strlen($raw_diff); | ||||||
|  |  | ||||||
|  |     return array($changesets, $size); | ||||||
|   } |   } | ||||||
|  |  | ||||||
|   public function getChangesetsForCommit($identifier) { |   public function getChangesetsForCommit($identifier) { | ||||||
| @@ -1221,7 +1237,9 @@ final class DiffusionCommitHookEngine extends Phobject { | |||||||
|       return $cached; |       return $cached; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     return $this->loadChangesetsForCommit($identifier); |     $info = $this->loadChangesetsForCommit($identifier); | ||||||
|  |     list($changesets, $size) = $info; | ||||||
|  |     return $changesets; | ||||||
|   } |   } | ||||||
|  |  | ||||||
|   public function loadCommitRefForCommit($identifier) { |   public function loadCommitRefForCommit($identifier) { | ||||||
|   | |||||||
| @@ -207,7 +207,7 @@ final class HeraldCommitAdapter | |||||||
|   } |   } | ||||||
|  |  | ||||||
|   public static function getEnormousByteLimit() { |   public static function getEnormousByteLimit() { | ||||||
|     return 1024 * 1024 * 1024; // 1GB |     return 256 * 1024 * 1024; // 256MB. See T13142 and T13143. | ||||||
|   } |   } | ||||||
|  |  | ||||||
|   public static function getEnormousTimeLimit() { |   public static function getEnormousTimeLimit() { | ||||||
|   | |||||||
| @@ -33,7 +33,24 @@ final class DiffusionSubversionWireProtocol extends Phobject { | |||||||
|  |  | ||||||
|     $messages = array(); |     $messages = array(); | ||||||
|     while (true) { |     while (true) { | ||||||
|       if ($this->state == 'item') { |       if ($this->state == 'space') { | ||||||
|  |         // Consume zero or more extra spaces after matching an item. The | ||||||
|  |         // protocol requires at least one space, but allows more than one. | ||||||
|  |  | ||||||
|  |         $matches = null; | ||||||
|  |         if (!preg_match('/^(\s*)\S/', $this->buffer, $matches)) { | ||||||
|  |          // Wait for more data. | ||||||
|  |           break; | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         // We have zero or more spaces and then some other character, so throw | ||||||
|  |         // the spaces away and continue parsing frames. | ||||||
|  |         if (strlen($matches[1])) { | ||||||
|  |           $this->buffer = substr($this->buffer, strlen($matches[1])); | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         $this->state = 'item'; | ||||||
|  |       } else if ($this->state == 'item') { | ||||||
|         $match = null; |         $match = null; | ||||||
|         $result = null; |         $result = null; | ||||||
|         $buf = $this->buffer; |         $buf = $this->buffer; | ||||||
| @@ -69,6 +86,12 @@ final class DiffusionSubversionWireProtocol extends Phobject { | |||||||
|             ); |             ); | ||||||
|             $this->raw = ''; |             $this->raw = ''; | ||||||
|           } |           } | ||||||
|  |  | ||||||
|  |           // Consume any extra whitespace after an item. If we're in the | ||||||
|  |           // "bytes" state, we aren't looking for whitespace. | ||||||
|  |           if ($this->state == 'item') { | ||||||
|  |             $this->state = 'space'; | ||||||
|  |           } | ||||||
|         } else { |         } else { | ||||||
|           // No matches yet, wait for more data. |           // No matches yet, wait for more data. | ||||||
|           break; |           break; | ||||||
| @@ -90,7 +113,7 @@ final class DiffusionSubversionWireProtocol extends Phobject { | |||||||
|           // Strip off the terminal space. |           // Strip off the terminal space. | ||||||
|           $this->pushItem(substr($this->byteBuffer, 0, -1), 'string'); |           $this->pushItem(substr($this->byteBuffer, 0, -1), 'string'); | ||||||
|           $this->byteBuffer = ''; |           $this->byteBuffer = ''; | ||||||
|           $this->state = 'item'; |           $this->state = 'space'; | ||||||
|         } |         } | ||||||
|       } else { |       } else { | ||||||
|         throw new Exception(pht("Invalid state '%s'!", $this->state)); |         throw new Exception(pht("Invalid state '%s'!", $this->state)); | ||||||
|   | |||||||
| @@ -59,6 +59,50 @@ final class DiffusionSubversionWireProtocolTestCase | |||||||
|           ), |           ), | ||||||
|         ), |         ), | ||||||
|       )); |       )); | ||||||
|  |  | ||||||
|  |     // This is testing that multiple spaces are parsed correctly. See T13140 | ||||||
|  |     // for discussion. | ||||||
|  |     $this->assertSameSubversionMessages( | ||||||
|  |       '( get-file true  false ) ', | ||||||
|  |       //               ^-- Note extra space! | ||||||
|  |       array( | ||||||
|  |         array( | ||||||
|  |           array( | ||||||
|  |             'type' => 'word', | ||||||
|  |             'value' => 'get-file', | ||||||
|  |           ), | ||||||
|  |           array( | ||||||
|  |             'type' => 'word', | ||||||
|  |             'value' => 'true', | ||||||
|  |           ), | ||||||
|  |           array( | ||||||
|  |             'type' => 'word', | ||||||
|  |             'value' => 'false', | ||||||
|  |           ), | ||||||
|  |         ), | ||||||
|  |       ), | ||||||
|  |       '( get-file true false ) '); | ||||||
|  |  | ||||||
|  |     $this->assertSameSubversionMessages( | ||||||
|  |       '( duck 5:quack    moo  ) ', | ||||||
|  |       array( | ||||||
|  |         array( | ||||||
|  |           array( | ||||||
|  |             'type' => 'word', | ||||||
|  |             'value' => 'duck', | ||||||
|  |           ), | ||||||
|  |           array( | ||||||
|  |             'type' => 'string', | ||||||
|  |             'value' => 'quack', | ||||||
|  |           ), | ||||||
|  |           array( | ||||||
|  |             'type' => 'word', | ||||||
|  |             'value' => 'moo', | ||||||
|  |           ), | ||||||
|  |         ), | ||||||
|  |       ), | ||||||
|  |       '( duck 5:quack moo ) '); | ||||||
|  |  | ||||||
|   } |   } | ||||||
|  |  | ||||||
|   public function testSubversionWireProtocolPartialFrame() { |   public function testSubversionWireProtocolPartialFrame() { | ||||||
| @@ -86,7 +130,11 @@ final class DiffusionSubversionWireProtocolTestCase | |||||||
|       ipull($msg2, 'structure')); |       ipull($msg2, 'structure')); | ||||||
|   } |   } | ||||||
|  |  | ||||||
|   private function assertSameSubversionMessages($string, array $structs) { |   private function assertSameSubversionMessages( | ||||||
|  |     $string, | ||||||
|  |     array $structs, | ||||||
|  |     $serial_string = null) { | ||||||
|  |  | ||||||
|     $proto = new DiffusionSubversionWireProtocol(); |     $proto = new DiffusionSubversionWireProtocol(); | ||||||
|  |  | ||||||
|     // Verify that the wire message parses into the structs. |     // Verify that the wire message parses into the structs. | ||||||
| @@ -100,6 +148,13 @@ final class DiffusionSubversionWireProtocolTestCase | |||||||
|       $serial[] = $proto->serializeStruct($struct); |       $serial[] = $proto->serializeStruct($struct); | ||||||
|     } |     } | ||||||
|     $serial = implode('', $serial); |     $serial = implode('', $serial); | ||||||
|     $this->assertEqual($string, $serial, 'serialize<'.$string.'>'); |  | ||||||
|  |     if ($serial_string === null) { | ||||||
|  |       $expect_serial = $string; | ||||||
|  |     } else { | ||||||
|  |       $expect_serial = $serial_string; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     $this->assertEqual($expect_serial, $serial, 'serialize<'.$string.'>'); | ||||||
|   } |   } | ||||||
| } | } | ||||||
|   | |||||||
| @@ -22,9 +22,14 @@ final class DiffusionCommitQuery | |||||||
|   private $epochMin; |   private $epochMin; | ||||||
|   private $epochMax; |   private $epochMax; | ||||||
|   private $importing; |   private $importing; | ||||||
|  |   private $ancestorsOf; | ||||||
|  |  | ||||||
|   private $needCommitData; |   private $needCommitData; | ||||||
|   private $needDrafts; |   private $needDrafts; | ||||||
|  |   private $needIdentities; | ||||||
|  |  | ||||||
|  |   private $mustFilterRefs = false; | ||||||
|  |   private $refRepository; | ||||||
|  |  | ||||||
|   public function withIDs(array $ids) { |   public function withIDs(array $ids) { | ||||||
|     $this->ids = $ids; |     $this->ids = $ids; | ||||||
| @@ -92,7 +97,7 @@ final class DiffusionCommitQuery | |||||||
|   } |   } | ||||||
|  |  | ||||||
|   public function withRepositoryIDs(array $repository_ids) { |   public function withRepositoryIDs(array $repository_ids) { | ||||||
|     $this->repositoryIDs = $repository_ids; |     $this->repositoryIDs = array_unique($repository_ids); | ||||||
|     return $this; |     return $this; | ||||||
|   } |   } | ||||||
|  |  | ||||||
| @@ -106,6 +111,11 @@ final class DiffusionCommitQuery | |||||||
|     return $this; |     return $this; | ||||||
|   } |   } | ||||||
|  |  | ||||||
|  |   public function needIdentities($need) { | ||||||
|  |     $this->needIdentities = $need; | ||||||
|  |     return $this; | ||||||
|  |   } | ||||||
|  |  | ||||||
|   public function needAuditRequests($need) { |   public function needAuditRequests($need) { | ||||||
|     $this->needAuditRequests = $need; |     $this->needAuditRequests = $need; | ||||||
|     return $this; |     return $this; | ||||||
| @@ -152,6 +162,11 @@ final class DiffusionCommitQuery | |||||||
|     return $this; |     return $this; | ||||||
|   } |   } | ||||||
|  |  | ||||||
|  |   public function withAncestorsOf(array $refs) { | ||||||
|  |     $this->ancestorsOf = $refs; | ||||||
|  |     return $this; | ||||||
|  |   } | ||||||
|  |  | ||||||
|   public function getIdentifierMap() { |   public function getIdentifierMap() { | ||||||
|     if ($this->identifierMap === null) { |     if ($this->identifierMap === null) { | ||||||
|       throw new Exception( |       throw new Exception( | ||||||
| @@ -307,6 +322,54 @@ final class DiffusionCommitQuery | |||||||
|   protected function didFilterPage(array $commits) { |   protected function didFilterPage(array $commits) { | ||||||
|     $viewer = $this->getViewer(); |     $viewer = $this->getViewer(); | ||||||
|  |  | ||||||
|  |     if ($this->mustFilterRefs) { | ||||||
|  |       // If this flag is set, the query has an "Ancestors Of" constraint and | ||||||
|  |       // at least one of the constraining refs had too many ancestors for us | ||||||
|  |       // to apply the constraint with a big "commitIdentifier IN (%Ls)" clause. | ||||||
|  |       // We're going to filter each page and hope we get a full result set | ||||||
|  |       // before the query overheats. | ||||||
|  |  | ||||||
|  |       $ancestor_list = mpull($commits, 'getCommitIdentifier'); | ||||||
|  |       $ancestor_list = array_values($ancestor_list); | ||||||
|  |  | ||||||
|  |       foreach ($this->ancestorsOf as $ref) { | ||||||
|  |         try { | ||||||
|  |           $ancestor_list = DiffusionQuery::callConduitWithDiffusionRequest( | ||||||
|  |             $viewer, | ||||||
|  |             DiffusionRequest::newFromDictionary( | ||||||
|  |               array( | ||||||
|  |                 'repository' => $this->refRepository, | ||||||
|  |                 'user' => $viewer, | ||||||
|  |               )), | ||||||
|  |             'diffusion.internal.ancestors', | ||||||
|  |             array( | ||||||
|  |               'ref' => $ref, | ||||||
|  |               'commits' => $ancestor_list, | ||||||
|  |             )); | ||||||
|  |         } catch (ConduitClientException $ex) { | ||||||
|  |           throw new PhabricatorSearchConstraintException( | ||||||
|  |             $ex->getMessage()); | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         if (!$ancestor_list) { | ||||||
|  |           break; | ||||||
|  |         } | ||||||
|  |       } | ||||||
|  |  | ||||||
|  |       $ancestor_list = array_fuse($ancestor_list); | ||||||
|  |       foreach ($commits as $key => $commit) { | ||||||
|  |         $identifier = $commit->getCommitIdentifier(); | ||||||
|  |         if (!isset($ancestor_list[$identifier])) { | ||||||
|  |           $this->didRejectResult($commit); | ||||||
|  |           unset($commits[$key]); | ||||||
|  |         } | ||||||
|  |       } | ||||||
|  |  | ||||||
|  |       if (!$commits) { | ||||||
|  |         return $commits; | ||||||
|  |       } | ||||||
|  |     } | ||||||
|  |  | ||||||
|     if ($this->needCommitData) { |     if ($this->needCommitData) { | ||||||
|       $data = id(new PhabricatorRepositoryCommitData())->loadAllWhere( |       $data = id(new PhabricatorRepositoryCommitData())->loadAllWhere( | ||||||
|         'commitID in (%Ld)', |         'commitID in (%Ld)', | ||||||
| @@ -336,6 +399,24 @@ final class DiffusionCommitQuery | |||||||
|       } |       } | ||||||
|     } |     } | ||||||
|  |  | ||||||
|  |     if ($this->needIdentities) { | ||||||
|  |       $identity_phids = array_merge( | ||||||
|  |         mpull($commits, 'getAuthorIdentityPHID'), | ||||||
|  |         mpull($commits, 'getCommitterIdentityPHID')); | ||||||
|  |  | ||||||
|  |       $data = id(new PhabricatorRepositoryIdentityQuery()) | ||||||
|  |         ->withPHIDs($identity_phids) | ||||||
|  |         ->setViewer($this->getViewer()) | ||||||
|  |         ->execute(); | ||||||
|  |       $data = mpull($data, null, 'getPHID'); | ||||||
|  |  | ||||||
|  |       foreach ($commits as $commit) { | ||||||
|  |         $author_identity = idx($data, $commit->getAuthorIdentityPHID()); | ||||||
|  |         $committer_identity = idx($data, $commit->getCommitterIdentityPHID()); | ||||||
|  |         $commit->attachIdentities($author_identity, $committer_identity); | ||||||
|  |       } | ||||||
|  |     } | ||||||
|  |  | ||||||
|     if ($this->needDrafts) { |     if ($this->needDrafts) { | ||||||
|       PhabricatorDraftEngine::attachDrafts( |       PhabricatorDraftEngine::attachDrafts( | ||||||
|         $viewer, |         $viewer, | ||||||
| @@ -364,6 +445,95 @@ final class DiffusionCommitQuery | |||||||
|       $this->withRepositoryIDs($repository_ids); |       $this->withRepositoryIDs($repository_ids); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|  |     if ($this->ancestorsOf !== null) { | ||||||
|  |       if (count($this->repositoryIDs) !== 1) { | ||||||
|  |         throw new PhabricatorSearchConstraintException( | ||||||
|  |           pht( | ||||||
|  |             'To search for commits which are ancestors of particular refs, '. | ||||||
|  |             'you must constrain the search to exactly one repository.')); | ||||||
|  |       } | ||||||
|  |  | ||||||
|  |       $repository_id = head($this->repositoryIDs); | ||||||
|  |       $history_limit = $this->getRawResultLimit() * 32; | ||||||
|  |       $viewer = $this->getViewer(); | ||||||
|  |  | ||||||
|  |       $repository = id(new PhabricatorRepositoryQuery()) | ||||||
|  |         ->setViewer($viewer) | ||||||
|  |         ->withIDs(array($repository_id)) | ||||||
|  |         ->executeOne(); | ||||||
|  |  | ||||||
|  |       if (!$repository) { | ||||||
|  |         throw new PhabricatorEmptyQueryException(); | ||||||
|  |       } | ||||||
|  |  | ||||||
|  |       if ($repository->isSVN()) { | ||||||
|  |         throw new PhabricatorSearchConstraintException( | ||||||
|  |           pht( | ||||||
|  |             'Subversion does not support searching for ancestors of '. | ||||||
|  |             'a particular ref. This operation is not meaningful in '. | ||||||
|  |             'Subversion.')); | ||||||
|  |       } | ||||||
|  |  | ||||||
|  |       if ($repository->isHg()) { | ||||||
|  |         throw new PhabricatorSearchConstraintException( | ||||||
|  |           pht( | ||||||
|  |             'Mercurial does not currently support searching for ancestors of '. | ||||||
|  |             'a particular ref.')); | ||||||
|  |       } | ||||||
|  |  | ||||||
|  |       $can_constrain = true; | ||||||
|  |       $history_identifiers = array(); | ||||||
|  |       foreach ($this->ancestorsOf as $key => $ref) { | ||||||
|  |         try { | ||||||
|  |           $raw_history = DiffusionQuery::callConduitWithDiffusionRequest( | ||||||
|  |             $viewer, | ||||||
|  |             DiffusionRequest::newFromDictionary( | ||||||
|  |               array( | ||||||
|  |                 'repository' => $repository, | ||||||
|  |                 'user' => $viewer, | ||||||
|  |               )), | ||||||
|  |             'diffusion.historyquery', | ||||||
|  |             array( | ||||||
|  |               'commit' => $ref, | ||||||
|  |               'limit' => $history_limit, | ||||||
|  |             )); | ||||||
|  |         } catch (ConduitClientException $ex) { | ||||||
|  |           throw new PhabricatorSearchConstraintException( | ||||||
|  |             $ex->getMessage()); | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         $ref_identifiers = array(); | ||||||
|  |         foreach ($raw_history['pathChanges'] as $change) { | ||||||
|  |           $ref_identifiers[] = $change['commitIdentifier']; | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         // If this ref had fewer total commits than the limit, we're safe to | ||||||
|  |         // apply the constraint as a large `IN (...)` query for a list of | ||||||
|  |         // commit identifiers. This is efficient. | ||||||
|  |         if ($history_limit) { | ||||||
|  |           if (count($ref_identifiers) >= $history_limit) { | ||||||
|  |             $can_constrain = false; | ||||||
|  |             break; | ||||||
|  |           } | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         $history_identifiers += array_fuse($ref_identifiers); | ||||||
|  |       } | ||||||
|  |  | ||||||
|  |       // If all refs had a small number of ancestors, we can just put the | ||||||
|  |       // constraint into the query here and we're done. Otherwise, we need | ||||||
|  |       // to filter each page after it comes out of the MySQL layer. | ||||||
|  |       if ($can_constrain) { | ||||||
|  |         $where[] = qsprintf( | ||||||
|  |           $conn, | ||||||
|  |           'commit.commitIdentifier IN (%Ls)', | ||||||
|  |           $history_identifiers); | ||||||
|  |       } else { | ||||||
|  |         $this->mustFilterRefs = true; | ||||||
|  |         $this->refRepository = $repository; | ||||||
|  |       } | ||||||
|  |     } | ||||||
|  |  | ||||||
|     if ($this->ids !== null) { |     if ($this->ids !== null) { | ||||||
|       $where[] = qsprintf( |       $where[] = qsprintf( | ||||||
|         $conn, |         $conn, | ||||||
|   | |||||||
| @@ -0,0 +1,108 @@ | |||||||
|  | <?php | ||||||
|  |  | ||||||
|  | final class DiffusionRepositoryIdentitySearchEngine | ||||||
|  |   extends PhabricatorApplicationSearchEngine { | ||||||
|  |  | ||||||
|  |   public function getResultTypeDescription() { | ||||||
|  |     return pht('Repository Identities'); | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   public function getApplicationClassName() { | ||||||
|  |     return 'PhabricatorDiffusionApplication'; | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   public function newQuery() { | ||||||
|  |     return new PhabricatorRepositoryIdentityQuery(); | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   protected function buildCustomSearchFields() { | ||||||
|  |     return array( | ||||||
|  |       id(new DiffusionIdentityAssigneeSearchField()) | ||||||
|  |         ->setLabel(pht('Assigned To')) | ||||||
|  |         ->setKey('assignee') | ||||||
|  |         ->setDescription(pht('Search for identities by assignee.')), | ||||||
|  |       id(new PhabricatorSearchTextField()) | ||||||
|  |         ->setLabel(pht('Identity Contains')) | ||||||
|  |         ->setKey('match') | ||||||
|  |         ->setDescription(pht('Search for identities by substring.')), | ||||||
|  |       id(new PhabricatorSearchThreeStateField()) | ||||||
|  |         ->setLabel(pht('Is Assigned')) | ||||||
|  |         ->setKey('hasEffectivePHID') | ||||||
|  |         ->setOptions( | ||||||
|  |           pht('(Show All)'), | ||||||
|  |           pht('Show Only Assigned Identities'), | ||||||
|  |           pht('Show Only Unassigned Identities')), | ||||||
|  |     ); | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   protected function buildQueryFromParameters(array $map) { | ||||||
|  |     $query = $this->newQuery(); | ||||||
|  |  | ||||||
|  |     if ($map['hasEffectivePHID'] !== null) { | ||||||
|  |       $query->withHasEffectivePHID($map['hasEffectivePHID']); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     if ($map['match'] !== null) { | ||||||
|  |       $query->withIdentityNameLike($map['match']); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     if ($map['assignee']) { | ||||||
|  |       $query->withAssigneePHIDs($map['assignee']); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     return $query; | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   protected function getURI($path) { | ||||||
|  |     return '/diffusion/identity/'.$path; | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   protected function getBuiltinQueryNames() { | ||||||
|  |     $names = array( | ||||||
|  |       'all' => pht('All Identities'), | ||||||
|  |     ); | ||||||
|  |  | ||||||
|  |     return $names; | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   public function buildSavedQueryFromBuiltin($query_key) { | ||||||
|  |  | ||||||
|  |     $query = $this->newSavedQuery(); | ||||||
|  |     $query->setQueryKey($query_key); | ||||||
|  |  | ||||||
|  |     switch ($query_key) { | ||||||
|  |       case 'all': | ||||||
|  |         return $query; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     return parent::buildSavedQueryFromBuiltin($query_key); | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   protected function renderResultList( | ||||||
|  |     array $identities, | ||||||
|  |     PhabricatorSavedQuery $query, | ||||||
|  |     array $handles) { | ||||||
|  |     assert_instances_of($identities, 'PhabricatorRepositoryIdentity'); | ||||||
|  |  | ||||||
|  |     $viewer = $this->requireViewer(); | ||||||
|  |  | ||||||
|  |     $list = new PHUIObjectItemListView(); | ||||||
|  |     $list->setUser($viewer); | ||||||
|  |     foreach ($identities as $identity) { | ||||||
|  |       $item = id(new PHUIObjectItemView()) | ||||||
|  |         ->setObjectName(pht('Identity %d', $identity->getID())) | ||||||
|  |         ->setHeader($identity->getIdentityShortName()) | ||||||
|  |         ->setHref($identity->getURI()) | ||||||
|  |         ->setObject($identity); | ||||||
|  |  | ||||||
|  |       $list->addItem($item); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     $result = new PhabricatorApplicationSearchResultView(); | ||||||
|  |     $result->setObjectList($list); | ||||||
|  |     $result->setNoDataString(pht('No Identities found.')); | ||||||
|  |  | ||||||
|  |     return $result; | ||||||
|  |   } | ||||||
|  |  | ||||||
|  | } | ||||||
| @@ -0,0 +1,22 @@ | |||||||
|  | <?php | ||||||
|  |  | ||||||
|  | final class DiffusionIdentityAssigneeSearchField | ||||||
|  |   extends PhabricatorSearchTokenizerField { | ||||||
|  |  | ||||||
|  |   protected function getDefaultValue() { | ||||||
|  |     return array(); | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   protected function getValueFromRequest(AphrontRequest $request, $key) { | ||||||
|  |     return $this->getUsersFromRequest($request, $key); | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   protected function newDatasource() { | ||||||
|  |     return new DiffusionIdentityAssigneeDatasource(); | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   protected function newConduitParameterType() { | ||||||
|  |     return new ConduitUserListParameterType(); | ||||||
|  |   } | ||||||
|  |  | ||||||
|  | } | ||||||
| @@ -0,0 +1,21 @@ | |||||||
|  | <?php | ||||||
|  |  | ||||||
|  | final class DiffusionIdentityAssigneeDatasource | ||||||
|  |   extends PhabricatorTypeaheadCompositeDatasource { | ||||||
|  |  | ||||||
|  |   public function getBrowseTitle() { | ||||||
|  |     return pht('Browse Assignee'); | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   public function getPlaceholderText() { | ||||||
|  |     return pht('Type a username or function...'); | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   public function getComponentDatasources() { | ||||||
|  |     return array( | ||||||
|  |       new PhabricatorPeopleDatasource(), | ||||||
|  |       new DiffusionIdentityUnassignedDatasource(), | ||||||
|  |     ); | ||||||
|  |   } | ||||||
|  |  | ||||||
|  | } | ||||||
| @@ -0,0 +1,77 @@ | |||||||
|  | <?php | ||||||
|  |  | ||||||
|  | final class DiffusionIdentityUnassignedDatasource | ||||||
|  |   extends PhabricatorTypeaheadDatasource { | ||||||
|  |  | ||||||
|  |   const FUNCTION_TOKEN = 'unassigned()'; | ||||||
|  |  | ||||||
|  |   public function getBrowseTitle() { | ||||||
|  |     return pht('Browse Explicitly Unassigned'); | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   public function getPlaceholderText() { | ||||||
|  |     return pht('Type "unassigned"...'); | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   public function getDatasourceApplicationClass() { | ||||||
|  |     return 'PhabricatorDiffusionApplication'; | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   public function getDatasourceFunctions() { | ||||||
|  |     return array( | ||||||
|  |       'unassigned' => array( | ||||||
|  |         'name' => pht('Explicitly Unassigned'), | ||||||
|  |         'summary' => pht('Find results which are not assigned.'), | ||||||
|  |         'description' => pht( | ||||||
|  |           "This function includes results which have been explicitly ". | ||||||
|  |           "unassigned. Use a query like this to find explicitly ". | ||||||
|  |           "unassigned results:\n\n%s\n\n". | ||||||
|  |           "If you combine this function with other functions, the query will ". | ||||||
|  |           "return results which match the other selectors //or// have no ". | ||||||
|  |           "assignee. For example, this query will find results which are ". | ||||||
|  |           "assigned to `alincoln`, and will also find results which have been ". | ||||||
|  |           "unassigned:\n\n%s", | ||||||
|  |           '> unassigned()', | ||||||
|  |           '> alincoln, unassigned()'), | ||||||
|  |       ), | ||||||
|  |     ); | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   public function loadResults() { | ||||||
|  |     $results = array( | ||||||
|  |       $this->buildUnassignedResult(), | ||||||
|  |     ); | ||||||
|  |     return $this->filterResultsAgainstTokens($results); | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   protected function evaluateFunction($function, array $argv_list) { | ||||||
|  |     $results = array(); | ||||||
|  |  | ||||||
|  |     foreach ($argv_list as $argv) { | ||||||
|  |       $results[] = self::FUNCTION_TOKEN; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     return $results; | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   public function renderFunctionTokens($function, array $argv_list) { | ||||||
|  |     $results = array(); | ||||||
|  |     foreach ($argv_list as $argv) { | ||||||
|  |       $results[] = PhabricatorTypeaheadTokenView::newFromTypeaheadResult( | ||||||
|  |         $this->buildUnassignedResult()); | ||||||
|  |     } | ||||||
|  |     return $results; | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   private function buildUnassignedResult() { | ||||||
|  |     $name = pht('Unassigned'); | ||||||
|  |     return $this->newFunctionResult() | ||||||
|  |       ->setName($name.' unassigned') | ||||||
|  |       ->setDisplayName($name) | ||||||
|  |       ->setIcon('fa-ban') | ||||||
|  |       ->setPHID('unassigned()') | ||||||
|  |       ->setUnique(true) | ||||||
|  |       ->addAttribute(pht('Select results with no owner.')); | ||||||
|  |   } | ||||||
|  |  | ||||||
|  | } | ||||||
| @@ -14,6 +14,8 @@ final class DiffusionPushLogListView extends AphrontView { | |||||||
|     $logs = $this->logs; |     $logs = $this->logs; | ||||||
|     $viewer = $this->getViewer(); |     $viewer = $this->getViewer(); | ||||||
|  |  | ||||||
|  |     $reject_herald = PhabricatorRepositoryPushLog::REJECT_HERALD; | ||||||
|  |  | ||||||
|     $handle_phids = array(); |     $handle_phids = array(); | ||||||
|     foreach ($logs as $log) { |     foreach ($logs as $log) { | ||||||
|       $handle_phids[] = $log->getPusherPHID(); |       $handle_phids[] = $log->getPusherPHID(); | ||||||
| @@ -21,9 +23,13 @@ final class DiffusionPushLogListView extends AphrontView { | |||||||
|       if ($device_phid) { |       if ($device_phid) { | ||||||
|         $handle_phids[] = $device_phid; |         $handle_phids[] = $device_phid; | ||||||
|       } |       } | ||||||
|  |  | ||||||
|  |       if ($log->getPushEvent()->getRejectCode() == $reject_herald) { | ||||||
|  |         $handle_phids[] = $log->getPushEvent()->getRejectDetails(); | ||||||
|  |       } | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     $handles = $viewer->loadHandles($handle_phids); |     $viewer->loadHandles($handle_phids); | ||||||
|  |  | ||||||
|     // Only administrators can view remote addresses. |     // Only administrators can view remote addresses. | ||||||
|     $remotes_visible = $viewer->getIsAdmin(); |     $remotes_visible = $viewer->getIsAdmin(); | ||||||
| @@ -74,10 +80,17 @@ final class DiffusionPushLogListView extends AphrontView { | |||||||
|         $flag_names); |         $flag_names); | ||||||
|  |  | ||||||
|       $reject_code = $log->getPushEvent()->getRejectCode(); |       $reject_code = $log->getPushEvent()->getRejectCode(); | ||||||
|       $reject_label = idx( |  | ||||||
|         $reject_map, |       if ($reject_code == $reject_herald) { | ||||||
|         $reject_code, |         $rule_phid = $log->getPushEvent()->getRejectDetails(); | ||||||
|         pht('Unknown ("%s")', $reject_code)); |         $handle = $viewer->renderHandle($rule_phid); | ||||||
|  |         $reject_label = pht('Blocked: %s', $handle); | ||||||
|  |       } else { | ||||||
|  |         $reject_label = idx( | ||||||
|  |           $reject_map, | ||||||
|  |           $reject_code, | ||||||
|  |           pht('Unknown ("%s")', $reject_code)); | ||||||
|  |       } | ||||||
|  |  | ||||||
|       $rows[] = array( |       $rows[] = array( | ||||||
|         phutil_tag( |         phutil_tag( | ||||||
|   | |||||||
| @@ -88,12 +88,29 @@ final class PhabricatorFileDataController extends PhabricatorFileController { | |||||||
|       $response->setMimeType($file->getViewableMimeType()); |       $response->setMimeType($file->getViewableMimeType()); | ||||||
|     } else { |     } else { | ||||||
|       $is_post = $request->isHTTPPost(); |       $is_post = $request->isHTTPPost(); | ||||||
|  |       $is_public = !$viewer->isLoggedIn(); | ||||||
|  |  | ||||||
|       // NOTE: Require POST to download files from the primary domain. If the |       // NOTE: Require POST to download files from the primary domain. If the | ||||||
|       // request is not a POST request but arrives on the primary domain, we |       // request is not a POST request but arrives on the primary domain, we | ||||||
|       // render a confirmation dialog. For discussion, see T13094. |       // render a confirmation dialog. For discussion, see T13094. | ||||||
|  |  | ||||||
|       $is_safe = ($is_alternate_domain || $is_lfs || $is_post); |       // There are two exceptions to this rule: | ||||||
|  |  | ||||||
|  |       // Git LFS requests can download with GET. This is safe (Git LFS won't | ||||||
|  |       // execute files it downloads) and necessary to support Git LFS. | ||||||
|  |  | ||||||
|  |       // Requests with no credentials may also download with GET. This | ||||||
|  |       // primarily supports downloading files with `arc download` or other | ||||||
|  |       // API clients. This is only "mostly" safe: if you aren't logged in, you | ||||||
|  |       // are likely immune to XSS and CSRF. However, an attacker may still be | ||||||
|  |       // able to set cookies on this domain (for example, to fixate your | ||||||
|  |       // session). For now, we accept these risks because users running | ||||||
|  |       // Phabricator in this mode are knowingly accepting a security risk | ||||||
|  |       // against setup advice, and there's significant value in having | ||||||
|  |       // API development against test and production installs work the same | ||||||
|  |       // way. | ||||||
|  |  | ||||||
|  |       $is_safe = ($is_alternate_domain || $is_post || $is_lfs || $is_public); | ||||||
|       if (!$is_safe) { |       if (!$is_safe) { | ||||||
|         return $this->newDialog() |         return $this->newDialog() | ||||||
|           ->setSubmitURI($file->getDownloadURI()) |           ->setSubmitURI($file->getDownloadURI()) | ||||||
|   | |||||||
| @@ -7,6 +7,7 @@ abstract class PhabricatorDocumentEngine | |||||||
|   private $highlightedLines = array(); |   private $highlightedLines = array(); | ||||||
|   private $encodingConfiguration; |   private $encodingConfiguration; | ||||||
|   private $highlightingConfiguration; |   private $highlightingConfiguration; | ||||||
|  |   private $blameConfiguration = true; | ||||||
|  |  | ||||||
|   final public function setViewer(PhabricatorUser $viewer) { |   final public function setViewer(PhabricatorUser $viewer) { | ||||||
|     $this->viewer = $viewer; |     $this->viewer = $viewer; | ||||||
| @@ -60,6 +61,19 @@ abstract class PhabricatorDocumentEngine | |||||||
|     return $this->highlightingConfiguration; |     return $this->highlightingConfiguration; | ||||||
|   } |   } | ||||||
|  |  | ||||||
|  |   final public function setBlameConfiguration($blame_configuration) { | ||||||
|  |     $this->blameConfiguration = $blame_configuration; | ||||||
|  |     return $this; | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   final public function getBlameConfiguration() { | ||||||
|  |     return $this->blameConfiguration; | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   final protected function getBlameEnabled() { | ||||||
|  |     return $this->blameConfiguration; | ||||||
|  |   } | ||||||
|  |  | ||||||
|   public function shouldRenderAsync(PhabricatorDocumentRef $ref) { |   public function shouldRenderAsync(PhabricatorDocumentRef $ref) { | ||||||
|     return false; |     return false; | ||||||
|   } |   } | ||||||
|   | |||||||
| @@ -119,11 +119,23 @@ final class PhabricatorDocumentRef | |||||||
|     } |     } | ||||||
|  |  | ||||||
|     $snippet = $this->getSnippet(); |     $snippet = $this->getSnippet(); | ||||||
|     if (!preg_match('/^\s*[{[]/', $snippet)) { |  | ||||||
|  |     // If the file is longer than the snippet, we don't detect the content | ||||||
|  |     // as JSON. We could use some kind of heuristic here if we wanted, but | ||||||
|  |     // see PHI749 for a false positive. | ||||||
|  |     if (strlen($snippet) < $this->getByteLength()) { | ||||||
|       return false; |       return false; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     return phutil_is_utf8($snippet); |     // If the snippet is the whole file, just check if the snippet is valid | ||||||
|  |     // JSON. Note that `phutil_json_decode()` only accepts arrays and objects | ||||||
|  |     // as JSON, so this won't misfire on files with content like "3". | ||||||
|  |     try { | ||||||
|  |       phutil_json_decode($snippet); | ||||||
|  |       return true; | ||||||
|  |     } catch (Exception $ex) { | ||||||
|  |       return false; | ||||||
|  |     } | ||||||
|   } |   } | ||||||
|  |  | ||||||
|   public function getSnippet() { |   public function getSnippet() { | ||||||
|   | |||||||
| @@ -50,7 +50,7 @@ final class PhabricatorSourceDocumentEngine | |||||||
|     } |     } | ||||||
|  |  | ||||||
|     $options = array(); |     $options = array(); | ||||||
|     if ($ref->getBlameURI()) { |     if ($ref->getBlameURI() && $this->getBlameEnabled()) { | ||||||
|       $content = phutil_split_lines($content); |       $content = phutil_split_lines($content); | ||||||
|       $blame = range(1, count($content)); |       $blame = range(1, count($content)); | ||||||
|       $blame = array_fuse($blame); |       $blame = array_fuse($blame); | ||||||
|   | |||||||
| @@ -69,6 +69,9 @@ abstract class PhabricatorDocumentRenderingEngine | |||||||
|       $engine->setHighlightingConfiguration($highlight_setting); |       $engine->setHighlightingConfiguration($highlight_setting); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|  |     $blame_setting = ($request->getStr('blame') !== 'off'); | ||||||
|  |     $engine->setBlameConfiguration($blame_setting); | ||||||
|  |  | ||||||
|     $views = array(); |     $views = array(); | ||||||
|     foreach ($engines as $candidate_key => $candidate_engine) { |     foreach ($engines as $candidate_key => $candidate_engine) { | ||||||
|       $label = $candidate_engine->getViewAsLabel($ref); |       $label = $candidate_engine->getViewAsLabel($ref); | ||||||
| @@ -104,6 +107,8 @@ abstract class PhabricatorDocumentRenderingEngine | |||||||
|       'controlID' => $control_id, |       'controlID' => $control_id, | ||||||
|     ); |     ); | ||||||
|  |  | ||||||
|  |     $this->willStageRef($ref); | ||||||
|  |  | ||||||
|     if ($engine->shouldRenderAsync($ref)) { |     if ($engine->shouldRenderAsync($ref)) { | ||||||
|       $content = $engine->newLoadingContent($ref); |       $content = $engine->newLoadingContent($ref); | ||||||
|       $config['next'] = 'render'; |       $config['next'] = 'render'; | ||||||
| @@ -142,7 +147,11 @@ abstract class PhabricatorDocumentRenderingEngine | |||||||
|         'value' => $highlight_setting, |         'value' => $highlight_setting, | ||||||
|       ), |       ), | ||||||
|       'blame' => array( |       'blame' => array( | ||||||
|  |         'icon' => 'fa-backward', | ||||||
|  |         'hide' => pht('Hide Blame'), | ||||||
|  |         'show' => pht('Show Blame'), | ||||||
|         'uri' => $ref->getBlameURI(), |         'uri' => $ref->getBlameURI(), | ||||||
|  |         'enabled' => $blame_setting, | ||||||
|         'value' => null, |         'value' => null, | ||||||
|       ), |       ), | ||||||
|       'coverage' => array( |       'coverage' => array( | ||||||
| @@ -180,6 +189,7 @@ abstract class PhabricatorDocumentRenderingEngine | |||||||
|   } |   } | ||||||
|  |  | ||||||
|   final public function newRenderResponse(PhabricatorDocumentRef $ref) { |   final public function newRenderResponse(PhabricatorDocumentRef $ref) { | ||||||
|  |     $this->willStageRef($ref); | ||||||
|     $this->willRenderRef($ref); |     $this->willRenderRef($ref); | ||||||
|  |  | ||||||
|     $request = $this->getRequest(); |     $request = $this->getRequest(); | ||||||
| @@ -207,6 +217,9 @@ abstract class PhabricatorDocumentRenderingEngine | |||||||
|       $engine->setHighlightingConfiguration($highlight_setting); |       $engine->setHighlightingConfiguration($highlight_setting); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|  |     $blame_setting = ($request->getStr('blame') !== 'off'); | ||||||
|  |     $engine->setBlameConfiguration($blame_setting); | ||||||
|  |  | ||||||
|     try { |     try { | ||||||
|       $content = $engine->newDocument($ref); |       $content = $engine->newDocument($ref); | ||||||
|     } catch (Exception $ex) { |     } catch (Exception $ex) { | ||||||
| @@ -314,6 +327,10 @@ abstract class PhabricatorDocumentRenderingEngine | |||||||
|     return; |     return; | ||||||
|   } |   } | ||||||
|  |  | ||||||
|  |   protected function willStageRef(PhabricatorDocumentRef $ref) { | ||||||
|  |     return; | ||||||
|  |   } | ||||||
|  |  | ||||||
|   protected function willRenderRef(PhabricatorDocumentRef $ref) { |   protected function willRenderRef(PhabricatorDocumentRef $ref) { | ||||||
|     return; |     return; | ||||||
|   } |   } | ||||||
|   | |||||||
| @@ -247,7 +247,14 @@ final class PhabricatorFaviconRef extends Phobject { | |||||||
|     $src_w = $template['width']; |     $src_w = $template['width']; | ||||||
|     $src_h = $template['height']; |     $src_h = $template['height']; | ||||||
|  |  | ||||||
|     $template_data = $template['file']->loadFileData(); |     try { | ||||||
|  |       $template_data = $template['file']->loadFileData(); | ||||||
|  |     } catch (Exception $ex) { | ||||||
|  |       // In rare cases, we can end up with a corrupted or inaccessible file. | ||||||
|  |       // If we do, just give up: otherwise, it's impossible to get pages to | ||||||
|  |       // generate and not obvious how to fix it. | ||||||
|  |       return null; | ||||||
|  |     } | ||||||
|  |  | ||||||
|     if (!function_exists('imagecreatefromstring')) { |     if (!function_exists('imagecreatefromstring')) { | ||||||
|       return $template_data; |       return $template_data; | ||||||
|   | |||||||
| @@ -85,6 +85,32 @@ final class PhabricatorFilesManagementMigrateWorkflow | |||||||
|     foreach ($iterator as $file) { |     foreach ($iterator as $file) { | ||||||
|       $monogram = $file->getMonogram(); |       $monogram = $file->getMonogram(); | ||||||
|  |  | ||||||
|  |       // See T7148. When we export data for an instance, we copy all the data | ||||||
|  |       // for Files from S3 into the database dump so that the database dump is | ||||||
|  |       // a complete, standalone archive of all the data. In the general case, | ||||||
|  |       // installs may have a similar process using "--copy" to create a more | ||||||
|  |       // complete backup. | ||||||
|  |  | ||||||
|  |       // When doing this, we may run into temporary files which have been | ||||||
|  |       // deleted between the time we took the original dump and the current | ||||||
|  |       // timestamp. These files can't be copied since the data no longer | ||||||
|  |       // exists: the daemons on the live install already deleted it. | ||||||
|  |  | ||||||
|  |       // Simply avoid this whole mess by declining to migrate expired temporary | ||||||
|  |       // files. They're as good as dead anyway. | ||||||
|  |  | ||||||
|  |       $ttl = $file->getTTL(); | ||||||
|  |       if ($ttl) { | ||||||
|  |         if ($ttl < PhabricatorTime::getNow()) { | ||||||
|  |           echo tsprintf( | ||||||
|  |             "%s\n", | ||||||
|  |             pht( | ||||||
|  |               '%s: Skipping expired temporary file.', | ||||||
|  |               $monogram)); | ||||||
|  |           continue; | ||||||
|  |         } | ||||||
|  |       } | ||||||
|  |  | ||||||
|       $engine_key = $file->getStorageEngine(); |       $engine_key = $file->getStorageEngine(); | ||||||
|       $engine = idx($engines, $engine_key); |       $engine = idx($engines, $engine_key); | ||||||
|  |  | ||||||
|   | |||||||
| @@ -260,6 +260,18 @@ final class PhabricatorEmbedFileRemarkupRule | |||||||
|       $autoplay = null; |       $autoplay = null; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|  |     if ($is_video) { | ||||||
|  |       // See T13135. Chrome refuses to play videos with type "video/quicktime", | ||||||
|  |       // even though it may actually be able to play them. The least awful fix | ||||||
|  |       // based on available information is to simply omit the "type" attribute | ||||||
|  |       // from `<source />` tags. This causes Chrome to try to play the video | ||||||
|  |       // and realize it can, and does not appear to produce any bad behavior in | ||||||
|  |       // any other browser. | ||||||
|  |       $mime_type = null; | ||||||
|  |     } else { | ||||||
|  |       $mime_type = $file->getMimeType(); | ||||||
|  |     } | ||||||
|  |  | ||||||
|     return $this->newTag( |     return $this->newTag( | ||||||
|       $tag, |       $tag, | ||||||
|       array( |       array( | ||||||
| @@ -274,7 +286,7 @@ final class PhabricatorEmbedFileRemarkupRule | |||||||
|         'source', |         'source', | ||||||
|         array( |         array( | ||||||
|           'src' => $file->getBestURI(), |           'src' => $file->getBestURI(), | ||||||
|           'type' => $file->getMimeType(), |           'type' => $mime_type, | ||||||
|         ))); |         ))); | ||||||
|   } |   } | ||||||
|  |  | ||||||
|   | |||||||
| @@ -492,12 +492,13 @@ final class PhabricatorFile extends PhabricatorFileDAO | |||||||
|     $this->setStorageFormat($format->getStorageFormatKey()); |     $this->setStorageFormat($format->getStorageFormatKey()); | ||||||
|     $this->setStorageProperties($properties); |     $this->setStorageProperties($properties); | ||||||
|  |  | ||||||
|     list($identifier, $new_handle) = $this->writeToEngine( |     list($identifier, $new_handle, $integrity_hash) = $this->writeToEngine( | ||||||
|       $engine, |       $engine, | ||||||
|       $data, |       $data, | ||||||
|       $params); |       $params); | ||||||
|  |  | ||||||
|     $this->setStorageHandle($new_handle); |     $this->setStorageHandle($new_handle); | ||||||
|  |     $this->setIntegrityHash($integrity_hash); | ||||||
|     $this->save(); |     $this->save(); | ||||||
|  |  | ||||||
|     $this->deleteFileDataIfUnused( |     $this->deleteFileDataIfUnused( | ||||||
|   | |||||||
| @@ -3,6 +3,10 @@ | |||||||
| final class HarbormasterBuildLogDownloadController | final class HarbormasterBuildLogDownloadController | ||||||
|   extends HarbormasterController { |   extends HarbormasterController { | ||||||
|  |  | ||||||
|  |   public function shouldAllowPublic() { | ||||||
|  |     return true; | ||||||
|  |   } | ||||||
|  |  | ||||||
|   public function handleRequest(AphrontRequest $request) { |   public function handleRequest(AphrontRequest $request) { | ||||||
|     $request = $this->getRequest(); |     $request = $this->getRequest(); | ||||||
|     $viewer = $request->getUser(); |     $viewer = $request->getUser(); | ||||||
|   | |||||||
| @@ -3,6 +3,10 @@ | |||||||
| final class HarbormasterBuildLogRenderController | final class HarbormasterBuildLogRenderController | ||||||
|   extends HarbormasterController { |   extends HarbormasterController { | ||||||
|  |  | ||||||
|  |   public function shouldAllowPublic() { | ||||||
|  |     return true; | ||||||
|  |   } | ||||||
|  |  | ||||||
|   public function handleRequest(AphrontRequest $request) { |   public function handleRequest(AphrontRequest $request) { | ||||||
|     $viewer = $this->getViewer(); |     $viewer = $this->getViewer(); | ||||||
|  |  | ||||||
|   | |||||||
| @@ -3,6 +3,10 @@ | |||||||
| final class HarbormasterBuildLogViewController | final class HarbormasterBuildLogViewController | ||||||
|   extends HarbormasterController { |   extends HarbormasterController { | ||||||
|  |  | ||||||
|  |   public function shouldAllowPublic() { | ||||||
|  |     return true; | ||||||
|  |   } | ||||||
|  |  | ||||||
|   public function handleRequest(AphrontRequest $request) { |   public function handleRequest(AphrontRequest $request) { | ||||||
|     $viewer = $this->getViewer(); |     $viewer = $this->getViewer(); | ||||||
|  |  | ||||||
|   | |||||||
| @@ -3,6 +3,10 @@ | |||||||
| final class HarbormasterBuildViewController | final class HarbormasterBuildViewController | ||||||
|   extends HarbormasterController { |   extends HarbormasterController { | ||||||
|  |  | ||||||
|  |   public function shouldAllowPublic() { | ||||||
|  |     return true; | ||||||
|  |   } | ||||||
|  |  | ||||||
|   public function handleRequest(AphrontRequest $request) { |   public function handleRequest(AphrontRequest $request) { | ||||||
|     $request = $this->getRequest(); |     $request = $this->getRequest(); | ||||||
|     $viewer = $request->getUser(); |     $viewer = $request->getUser(); | ||||||
|   | |||||||
| @@ -3,6 +3,10 @@ | |||||||
| final class HarbormasterBuildableViewController | final class HarbormasterBuildableViewController | ||||||
|   extends HarbormasterController { |   extends HarbormasterController { | ||||||
|  |  | ||||||
|  |   public function shouldAllowPublic() { | ||||||
|  |     return true; | ||||||
|  |   } | ||||||
|  |  | ||||||
|   public function handleRequest(AphrontRequest $request) { |   public function handleRequest(AphrontRequest $request) { | ||||||
|     $viewer = $this->getViewer(); |     $viewer = $this->getViewer(); | ||||||
|  |  | ||||||
| @@ -350,6 +354,4 @@ final class HarbormasterBuildableViewController | |||||||
|     return array($lint, $unit); |     return array($lint, $unit); | ||||||
|   } |   } | ||||||
|  |  | ||||||
|  |  | ||||||
|  |  | ||||||
| } | } | ||||||
|   | |||||||
| @@ -3,6 +3,10 @@ | |||||||
| final class HarbormasterLintMessagesController | final class HarbormasterLintMessagesController | ||||||
|   extends HarbormasterController { |   extends HarbormasterController { | ||||||
|  |  | ||||||
|  |   public function shouldAllowPublic() { | ||||||
|  |     return true; | ||||||
|  |   } | ||||||
|  |  | ||||||
|   public function handleRequest(AphrontRequest $request) { |   public function handleRequest(AphrontRequest $request) { | ||||||
|     $viewer = $this->getViewer(); |     $viewer = $this->getViewer(); | ||||||
|  |  | ||||||
|   | |||||||
| @@ -2,6 +2,10 @@ | |||||||
|  |  | ||||||
| final class HarbormasterPlanViewController extends HarbormasterPlanController { | final class HarbormasterPlanViewController extends HarbormasterPlanController { | ||||||
|  |  | ||||||
|  |   public function shouldAllowPublic() { | ||||||
|  |     return true; | ||||||
|  |   } | ||||||
|  |  | ||||||
|   public function handleRequest(AphrontRequest $request) { |   public function handleRequest(AphrontRequest $request) { | ||||||
|     $viewer = $this->getViewer(); |     $viewer = $this->getViewer(); | ||||||
|     $id = $request->getURIData('id'); |     $id = $request->getURIData('id'); | ||||||
|   | |||||||
| @@ -3,6 +3,10 @@ | |||||||
| final class HarbormasterStepViewController | final class HarbormasterStepViewController | ||||||
|   extends HarbormasterPlanController { |   extends HarbormasterPlanController { | ||||||
|  |  | ||||||
|  |   public function shouldAllowPublic() { | ||||||
|  |     return true; | ||||||
|  |   } | ||||||
|  |  | ||||||
|   public function handleRequest(AphrontRequest $request) { |   public function handleRequest(AphrontRequest $request) { | ||||||
|     $viewer = $this->getViewer(); |     $viewer = $this->getViewer(); | ||||||
|     $id = $request->getURIData('id'); |     $id = $request->getURIData('id'); | ||||||
|   | |||||||
| @@ -3,6 +3,10 @@ | |||||||
| final class HarbormasterUnitMessageListController | final class HarbormasterUnitMessageListController | ||||||
|   extends HarbormasterController { |   extends HarbormasterController { | ||||||
|  |  | ||||||
|  |   public function shouldAllowPublic() { | ||||||
|  |     return true; | ||||||
|  |   } | ||||||
|  |  | ||||||
|   public function handleRequest(AphrontRequest $request) { |   public function handleRequest(AphrontRequest $request) { | ||||||
|     $viewer = $this->getViewer(); |     $viewer = $this->getViewer(); | ||||||
|  |  | ||||||
|   | |||||||
| @@ -3,6 +3,10 @@ | |||||||
| final class HarbormasterUnitMessageViewController | final class HarbormasterUnitMessageViewController | ||||||
|   extends HarbormasterController { |   extends HarbormasterController { | ||||||
|  |  | ||||||
|  |   public function shouldAllowPublic() { | ||||||
|  |     return true; | ||||||
|  |   } | ||||||
|  |  | ||||||
|   public function handleRequest(AphrontRequest $request) { |   public function handleRequest(AphrontRequest $request) { | ||||||
|     $viewer = $this->getViewer(); |     $viewer = $this->getViewer(); | ||||||
|  |  | ||||||
|   | |||||||
| @@ -106,6 +106,14 @@ EOTEXT | |||||||
|       ), |       ), | ||||||
|       'meta_data' => array( |       'meta_data' => array( | ||||||
|         'buildTargetPHID' => $build_target->getPHID(), |         'buildTargetPHID' => $build_target->getPHID(), | ||||||
|  |  | ||||||
|  |         // See PHI611. These are undocumented secret magic. | ||||||
|  |         'phabricator:build:id' => (int)$build->getID(), | ||||||
|  |         'phabricator:build:url' => | ||||||
|  |           PhabricatorEnv::getProductionURI($build->getURI()), | ||||||
|  |         'phabricator:buildable:id' => (int)$buildable->getID(), | ||||||
|  |         'phabricator:buildable:url' => | ||||||
|  |           PhabricatorEnv::getProductionURI($buildable->getURI()), | ||||||
|       ), |       ), | ||||||
|     ); |     ); | ||||||
|  |  | ||||||
|   | |||||||
| @@ -643,7 +643,13 @@ final class HarbormasterBuildLog | |||||||
|       $pos += $slice_length; |       $pos += $slice_length; | ||||||
|  |  | ||||||
|       $map_bytes += $slice_length; |       $map_bytes += $slice_length; | ||||||
|       $line_count += count(preg_split("/\r\n|\r|\n/", $slice)) - 1; |  | ||||||
|  |       // Count newlines in the slice. This goofy approach is meaningfully | ||||||
|  |       // faster than "preg_match_all()" or "preg_split()". See PHI766. | ||||||
|  |       $n_rn = substr_count($slice, "\r\n"); | ||||||
|  |       $n_r = substr_count($slice, "\r"); | ||||||
|  |       $n_n = substr_count($slice, "\n"); | ||||||
|  |       $line_count += ($n_rn) + ($n_r - $n_rn) + ($n_n - $n_rn); | ||||||
|  |  | ||||||
|       if ($map_bytes >= ($marker_distance - $max_utf8_width)) { |       if ($map_bytes >= ($marker_distance - $max_utf8_width)) { | ||||||
|         $map[] = array( |         $map[] = array( | ||||||
|   | |||||||
| @@ -46,7 +46,13 @@ final class HarbormasterTargetWorker extends HarbormasterWorker { | |||||||
|     $build = $target->getBuild(); |     $build = $target->getBuild(); | ||||||
|     $viewer = $this->getViewer(); |     $viewer = $this->getViewer(); | ||||||
|  |  | ||||||
|     $target->setDateStarted(time()); |     // If this is the first time we're starting work on this target, mark the | ||||||
|  |     // current time as the start time. If the target yields or waits, we may | ||||||
|  |     // end up here again later, so we don't want to overwrite the start time if | ||||||
|  |     // we already have a value. | ||||||
|  |     if (!$target->getDateStarted()) { | ||||||
|  |       $target->setDateStarted(PhabricatorTime::getNow()); | ||||||
|  |     } | ||||||
|  |  | ||||||
|     try { |     try { | ||||||
|       if ($target->getBuildGeneration() !== $build->getBuildGeneration()) { |       if ($target->getBuildGeneration() !== $build->getBuildGeneration()) { | ||||||
|   | |||||||
							
								
								
									
										74
									
								
								src/applications/herald/adapter/HeraldRuleAdapter.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										74
									
								
								src/applications/herald/adapter/HeraldRuleAdapter.php
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,74 @@ | |||||||
|  | <?php | ||||||
|  |  | ||||||
|  | final class HeraldRuleAdapter extends HeraldAdapter { | ||||||
|  |  | ||||||
|  |   private $rule; | ||||||
|  |  | ||||||
|  |   protected function newObject() { | ||||||
|  |     return new HeraldRule(); | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   public function getAdapterApplicationClass() { | ||||||
|  |     return 'PhabricatorHeraldApplication'; | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   public function getAdapterContentDescription() { | ||||||
|  |     return pht('React to Herald rules being created or updated.'); | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   public function isTestAdapterForObject($object) { | ||||||
|  |     return ($object instanceof HeraldRule); | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   public function getAdapterTestDescription() { | ||||||
|  |     return pht( | ||||||
|  |       'Test rules which run when another Herald rule is created or '. | ||||||
|  |       'updated.'); | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   protected function initializeNewAdapter() { | ||||||
|  |     $this->rule = $this->newObject(); | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   public function supportsApplicationEmail() { | ||||||
|  |     return true; | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   public function supportsRuleType($rule_type) { | ||||||
|  |     switch ($rule_type) { | ||||||
|  |       case HeraldRuleTypeConfig::RULE_TYPE_GLOBAL: | ||||||
|  |       case HeraldRuleTypeConfig::RULE_TYPE_PERSONAL: | ||||||
|  |         return true; | ||||||
|  |       case HeraldRuleTypeConfig::RULE_TYPE_OBJECT: | ||||||
|  |       default: | ||||||
|  |         return false; | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   public function setRule(HeraldRule $rule) { | ||||||
|  |     $this->rule = $rule; | ||||||
|  |     return $this; | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   public function getRule() { | ||||||
|  |     return $this->rule; | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   public function setObject($object) { | ||||||
|  |     $this->rule = $object; | ||||||
|  |     return $this; | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   public function getObject() { | ||||||
|  |     return $this->rule; | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   public function getAdapterContentName() { | ||||||
|  |     return pht('Herald Rules'); | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   public function getHeraldName() { | ||||||
|  |     return $this->getRule()->getMonogram(); | ||||||
|  |   } | ||||||
|  |  | ||||||
|  | } | ||||||
| @@ -38,8 +38,10 @@ final class HeraldTestConsoleController extends HeraldController { | |||||||
|  |  | ||||||
|     $object = $this->getTestObject(); |     $object = $this->getTestObject(); | ||||||
|     $adapter = $this->getTestAdapter(); |     $adapter = $this->getTestAdapter(); | ||||||
|  |     $source = $this->newContentSource($object); | ||||||
|  |  | ||||||
|     $adapter |     $adapter | ||||||
|  |       ->setContentSource($source) | ||||||
|       ->setIsNewObject(false) |       ->setIsNewObject(false) | ||||||
|       ->setActingAsPHID($viewer->getPHID()) |       ->setActingAsPHID($viewer->getPHID()) | ||||||
|       ->setViewer($viewer); |       ->setViewer($viewer); | ||||||
| @@ -218,4 +220,29 @@ final class HeraldTestConsoleController extends HeraldController { | |||||||
|       ->appendChild($view); |       ->appendChild($view); | ||||||
|   } |   } | ||||||
|  |  | ||||||
|  |   private function newContentSource($object) { | ||||||
|  |     $viewer = $this->getViewer(); | ||||||
|  |  | ||||||
|  |     // Try using the content source associated with the most recent transaction | ||||||
|  |     // on the object. | ||||||
|  |  | ||||||
|  |     $query = PhabricatorApplicationTransactionQuery::newQueryForObject($object); | ||||||
|  |  | ||||||
|  |     $xaction = $query | ||||||
|  |       ->setViewer($viewer) | ||||||
|  |       ->withObjectPHIDs(array($object->getPHID())) | ||||||
|  |       ->setLimit(1) | ||||||
|  |       ->setOrder('newest') | ||||||
|  |       ->executeOne(); | ||||||
|  |     if ($xaction) { | ||||||
|  |       return $xaction->getContentSource(); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     // If we couldn't find a transaction (which should be rare), fall back to | ||||||
|  |     // building a new content source from the test console request itself. | ||||||
|  |  | ||||||
|  |     $request = $this->getRequest(); | ||||||
|  |     return PhabricatorContentSource::newFromRequest($request); | ||||||
|  |   } | ||||||
|  |  | ||||||
| } | } | ||||||
|   | |||||||
| @@ -87,4 +87,68 @@ final class HeraldRuleEditor | |||||||
|     return; |     return; | ||||||
|   } |   } | ||||||
|  |  | ||||||
|  |   protected function shouldApplyHeraldRules( | ||||||
|  |     PhabricatorLiskDAO $object, | ||||||
|  |     array $xactions) { | ||||||
|  |     return true; | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   protected function buildHeraldAdapter( | ||||||
|  |     PhabricatorLiskDAO $object, | ||||||
|  |     array $xactions) { | ||||||
|  |     return id(new HeraldRuleAdapter()) | ||||||
|  |       ->setRule($object); | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   protected function shouldSendMail( | ||||||
|  |     PhabricatorLiskDAO $object, | ||||||
|  |     array $xactions) { | ||||||
|  |     return true; | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   protected function getMailTo(PhabricatorLiskDAO $object) { | ||||||
|  |     $phids = array(); | ||||||
|  |  | ||||||
|  |     $phids[] = $this->getActingAsPHID(); | ||||||
|  |  | ||||||
|  |     if ($object->isPersonalRule()) { | ||||||
|  |       $phids[] = $object->getAuthorPHID(); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     return $phids; | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   protected function buildReplyHandler(PhabricatorLiskDAO $object) { | ||||||
|  |     return id(new HeraldRuleReplyHandler()) | ||||||
|  |       ->setMailReceiver($object); | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   protected function buildMailTemplate(PhabricatorLiskDAO $object) { | ||||||
|  |     $monogram = $object->getMonogram(); | ||||||
|  |     $name = $object->getName(); | ||||||
|  |  | ||||||
|  |     $subject = pht('%s: %s', $monogram, $name); | ||||||
|  |  | ||||||
|  |     return id(new PhabricatorMetaMTAMail()) | ||||||
|  |       ->setSubject($subject); | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   protected function getMailSubjectPrefix() { | ||||||
|  |     return pht('[Herald]'); | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |  | ||||||
|  |   protected function buildMailBody( | ||||||
|  |     PhabricatorLiskDAO $object, | ||||||
|  |     array $xactions) { | ||||||
|  |  | ||||||
|  |     $body = parent::buildMailBody($object, $xactions); | ||||||
|  |  | ||||||
|  |     $body->addLinkSection( | ||||||
|  |       pht('RULE DETAIL'), | ||||||
|  |       PhabricatorEnv::getProductionURI($object->getURI())); | ||||||
|  |  | ||||||
|  |     return $body; | ||||||
|  |   } | ||||||
|  |  | ||||||
| } | } | ||||||
|   | |||||||
Some files were not shown because too many files have changed in this diff Show More
		Reference in New Issue
	
	Block a user