1
0
Fork 0
mirror of https://we.phorge.it/source/phorge.git synced 2024-11-30 02:32:42 +01:00

Merge branch 'master' into redesign-2015

This commit is contained in:
epriestley 2015-05-28 11:56:49 -07:00
commit 3699253e49
127 changed files with 3234 additions and 2575 deletions

View file

@ -1,6 +1,5 @@
{ {
"project.name" : "phabricator", "phabricator.uri": "https://secure.phabricator.com/",
"phabricator.uri" : "https://secure.phabricator.com/", "unit.engine": "PhutilUnitTestEngine",
"unit.engine" : "PhutilUnitTestEngine", "load": ["src/"]
"load" : ["src/"]
} }

View file

@ -74,6 +74,7 @@
"type": "xhpast", "type": "xhpast",
"include": "(\\.php$)", "include": "(\\.php$)",
"severity": { "severity": {
"16": "advice",
"34": "error" "34": "error"
}, },
"xhpast.blacklisted.function": { "xhpast.blacklisted.function": {

View file

@ -7,11 +7,11 @@
*/ */
return array( return array(
'names' => array( 'names' => array(
'core.pkg.css' => '55901d68', 'core.pkg.css' => 'f85a5ce0',
'core.pkg.js' => 'e4f47dfd', 'core.pkg.js' => 'fbf1d615',
'darkconsole.pkg.js' => 'e7393ebb', 'darkconsole.pkg.js' => 'e7393ebb',
'differential.pkg.css' => 'bb338e4b', 'differential.pkg.css' => '30602b8c',
'differential.pkg.js' => '63a77807', 'differential.pkg.js' => '8c98ce21',
'diffusion.pkg.css' => '385e85b3', 'diffusion.pkg.css' => '385e85b3',
'diffusion.pkg.js' => '0115b37c', 'diffusion.pkg.js' => '0115b37c',
'maniphest.pkg.css' => '4845691a', 'maniphest.pkg.css' => '4845691a',
@ -26,7 +26,7 @@ return array(
'rsrc/css/aphront/pager-view.css' => '2e3539af', 'rsrc/css/aphront/pager-view.css' => '2e3539af',
'rsrc/css/aphront/panel-view.css' => '8427b78d', 'rsrc/css/aphront/panel-view.css' => '8427b78d',
'rsrc/css/aphront/phabricator-nav-view.css' => '0ecd30a1', 'rsrc/css/aphront/phabricator-nav-view.css' => '0ecd30a1',
'rsrc/css/aphront/table-view.css' => 'fb17602c', 'rsrc/css/aphront/table-view.css' => '15fedf7a',
'rsrc/css/aphront/tokenizer.css' => '04875312', 'rsrc/css/aphront/tokenizer.css' => '04875312',
'rsrc/css/aphront/tooltip.css' => '7672b60f', 'rsrc/css/aphront/tooltip.css' => '7672b60f',
'rsrc/css/aphront/two-column.css' => '16ab3ad2', 'rsrc/css/aphront/two-column.css' => '16ab3ad2',
@ -60,7 +60,7 @@ return array(
'rsrc/css/application/differential/add-comment.css' => 'c47f8c40', 'rsrc/css/application/differential/add-comment.css' => 'c47f8c40',
'rsrc/css/application/differential/changeset-view.css' => 'e19cfd6e', 'rsrc/css/application/differential/changeset-view.css' => 'e19cfd6e',
'rsrc/css/application/differential/core.css' => '7ac3cabc', 'rsrc/css/application/differential/core.css' => '7ac3cabc',
'rsrc/css/application/differential/phui-inline-comment.css' => '2174771a', 'rsrc/css/application/differential/phui-inline-comment.css' => 'aa16f165',
'rsrc/css/application/differential/results-table.css' => '181aa9d9', 'rsrc/css/application/differential/results-table.css' => '181aa9d9',
'rsrc/css/application/differential/revision-comment.css' => '14b8565a', 'rsrc/css/application/differential/revision-comment.css' => '14b8565a',
'rsrc/css/application/differential/revision-history.css' => '0e8eb855', 'rsrc/css/application/differential/revision-history.css' => '0e8eb855',
@ -111,7 +111,7 @@ return array(
'rsrc/css/core/core.css' => 'bbc7187b', 'rsrc/css/core/core.css' => 'bbc7187b',
'rsrc/css/core/remarkup.css' => 'ea91b3ee', 'rsrc/css/core/remarkup.css' => 'ea91b3ee',
'rsrc/css/core/syntax.css' => '6b7b24d9', 'rsrc/css/core/syntax.css' => '6b7b24d9',
'rsrc/css/core/z-index.css' => '8c8c40aa', 'rsrc/css/core/z-index.css' => '63689f49',
'rsrc/css/diviner/diviner-shared.css' => '38813222', 'rsrc/css/diviner/diviner-shared.css' => '38813222',
'rsrc/css/font/font-awesome.css' => 'e2e712fe', 'rsrc/css/font/font-awesome.css' => 'e2e712fe',
'rsrc/css/font/font-source-sans-pro.css' => '8906c07b', 'rsrc/css/font/font-source-sans-pro.css' => '8906c07b',
@ -120,7 +120,7 @@ return array(
'rsrc/css/layout/phabricator-hovercard-view.css' => '0d665853', 'rsrc/css/layout/phabricator-hovercard-view.css' => '0d665853',
'rsrc/css/layout/phabricator-side-menu-view.css' => 'a440478a', 'rsrc/css/layout/phabricator-side-menu-view.css' => 'a440478a',
'rsrc/css/layout/phabricator-source-code-view.css' => '2ceee894', 'rsrc/css/layout/phabricator-source-code-view.css' => '2ceee894',
'rsrc/css/phui/calendar/phui-calendar-day.css' => 'c0cf782a', 'rsrc/css/phui/calendar/phui-calendar-day.css' => 'd1cf6f93',
'rsrc/css/phui/calendar/phui-calendar-list.css' => 'c1c7f338', 'rsrc/css/phui/calendar/phui-calendar-list.css' => 'c1c7f338',
'rsrc/css/phui/calendar/phui-calendar-month.css' => '476be7e0', 'rsrc/css/phui/calendar/phui-calendar-month.css' => '476be7e0',
'rsrc/css/phui/calendar/phui-calendar.css' => 'ccabe893', 'rsrc/css/phui/calendar/phui-calendar.css' => 'ccabe893',
@ -132,7 +132,7 @@ return array(
'rsrc/css/phui/phui-document.css' => '8be7a5e3', 'rsrc/css/phui/phui-document.css' => '8be7a5e3',
'rsrc/css/phui/phui-feed-story.css' => 'e5682f4c', 'rsrc/css/phui/phui-feed-story.css' => 'e5682f4c',
'rsrc/css/phui/phui-fontkit.css' => 'b664ac96', 'rsrc/css/phui/phui-fontkit.css' => 'b664ac96',
'rsrc/css/phui/phui-form-view.css' => '87263b05', 'rsrc/css/phui/phui-form-view.css' => 'a0e8f168',
'rsrc/css/phui/phui-form.css' => 'f535f938', 'rsrc/css/phui/phui-form.css' => 'f535f938',
'rsrc/css/phui/phui-header-view.css' => 'd7612da3', 'rsrc/css/phui/phui-header-view.css' => 'd7612da3',
'rsrc/css/phui/phui-icon.css' => '88ba9081', 'rsrc/css/phui/phui-icon.css' => '88ba9081',
@ -325,9 +325,10 @@ return array(
'rsrc/js/application/aphlict/behavior-aphlict-listen.js' => 'b1a59974', 'rsrc/js/application/aphlict/behavior-aphlict-listen.js' => 'b1a59974',
'rsrc/js/application/aphlict/behavior-aphlict-status.js' => 'ea681761', 'rsrc/js/application/aphlict/behavior-aphlict-status.js' => 'ea681761',
'rsrc/js/application/auth/behavior-persona-login.js' => '9414ff18', 'rsrc/js/application/auth/behavior-persona-login.js' => '9414ff18',
'rsrc/js/application/calendar/event-all-day.js' => 'ca5fa62a', 'rsrc/js/application/calendar/behavior-day-view.js' => '5c46cff2',
'rsrc/js/application/config/behavior-reorder-fields.js' => '14a827de', 'rsrc/js/application/calendar/behavior-event-all-day.js' => '38dcf3c8',
'rsrc/js/application/conpherence/ConpherenceThreadManager.js' => '10246726', 'rsrc/js/application/config/behavior-reorder-fields.js' => 'b6993408',
'rsrc/js/application/conpherence/ConpherenceThreadManager.js' => '01774ab2',
'rsrc/js/application/conpherence/behavior-drag-and-drop-photo.js' => 'cf86d16a', 'rsrc/js/application/conpherence/behavior-drag-and-drop-photo.js' => 'cf86d16a',
'rsrc/js/application/conpherence/behavior-durable-column.js' => '16c695bf', 'rsrc/js/application/conpherence/behavior-durable-column.js' => '16c695bf',
'rsrc/js/application/conpherence/behavior-menu.js' => 'c0348cac', 'rsrc/js/application/conpherence/behavior-menu.js' => 'c0348cac',
@ -346,7 +347,7 @@ return array(
'rsrc/js/application/differential/behavior-comment-preview.js' => 'b064af76', 'rsrc/js/application/differential/behavior-comment-preview.js' => 'b064af76',
'rsrc/js/application/differential/behavior-diff-radios.js' => 'e1ff79b1', 'rsrc/js/application/differential/behavior-diff-radios.js' => 'e1ff79b1',
'rsrc/js/application/differential/behavior-dropdown-menus.js' => '2035b9cb', 'rsrc/js/application/differential/behavior-dropdown-menus.js' => '2035b9cb',
'rsrc/js/application/differential/behavior-edit-inline-comments.js' => 'e723c323', 'rsrc/js/application/differential/behavior-edit-inline-comments.js' => '037b59eb',
'rsrc/js/application/differential/behavior-keyboard-nav.js' => '2c426492', 'rsrc/js/application/differential/behavior-keyboard-nav.js' => '2c426492',
'rsrc/js/application/differential/behavior-populate.js' => '8694b1df', 'rsrc/js/application/differential/behavior-populate.js' => '8694b1df',
'rsrc/js/application/differential/behavior-show-field-details.js' => 'bba9eedf', 'rsrc/js/application/differential/behavior-show-field-details.js' => 'bba9eedf',
@ -363,7 +364,7 @@ return array(
'rsrc/js/application/doorkeeper/behavior-doorkeeper-tag.js' => 'e5822781', 'rsrc/js/application/doorkeeper/behavior-doorkeeper-tag.js' => 'e5822781',
'rsrc/js/application/files/behavior-icon-composer.js' => '8ef9ab58', 'rsrc/js/application/files/behavior-icon-composer.js' => '8ef9ab58',
'rsrc/js/application/files/behavior-launch-icon-composer.js' => '48086888', 'rsrc/js/application/files/behavior-launch-icon-composer.js' => '48086888',
'rsrc/js/application/herald/HeraldRuleEditor.js' => '9229e764', 'rsrc/js/application/herald/HeraldRuleEditor.js' => '271ffdd7',
'rsrc/js/application/herald/PathTypeahead.js' => 'f7fc67ec', 'rsrc/js/application/herald/PathTypeahead.js' => 'f7fc67ec',
'rsrc/js/application/herald/herald-rule-editor.js' => '7ebaeed3', 'rsrc/js/application/herald/herald-rule-editor.js' => '7ebaeed3',
'rsrc/js/application/maniphest/behavior-batch-editor.js' => 'f5d1233b', 'rsrc/js/application/maniphest/behavior-batch-editor.js' => 'f5d1233b',
@ -436,7 +437,7 @@ return array(
'rsrc/js/core/behavior-device.js' => 'a205cf28', 'rsrc/js/core/behavior-device.js' => 'a205cf28',
'rsrc/js/core/behavior-drag-and-drop-textarea.js' => '6d49590e', 'rsrc/js/core/behavior-drag-and-drop-textarea.js' => '6d49590e',
'rsrc/js/core/behavior-error-log.js' => '6882e80a', 'rsrc/js/core/behavior-error-log.js' => '6882e80a',
'rsrc/js/core/behavior-fancy-datepicker.js' => '5c0f680f', 'rsrc/js/core/behavior-fancy-datepicker.js' => '510b5809',
'rsrc/js/core/behavior-file-tree.js' => '88236f00', 'rsrc/js/core/behavior-file-tree.js' => '88236f00',
'rsrc/js/core/behavior-form.js' => '5c54cbf3', 'rsrc/js/core/behavior-form.js' => '5c54cbf3',
'rsrc/js/core/behavior-gesture.js' => '3ab51e2c', 'rsrc/js/core/behavior-gesture.js' => '3ab51e2c',
@ -482,7 +483,7 @@ return array(
'aphront-multi-column-view-css' => 'fd18389d', 'aphront-multi-column-view-css' => 'fd18389d',
'aphront-pager-view-css' => '2e3539af', 'aphront-pager-view-css' => '2e3539af',
'aphront-panel-view-css' => '8427b78d', 'aphront-panel-view-css' => '8427b78d',
'aphront-table-view-css' => 'fb17602c', 'aphront-table-view-css' => '15fedf7a',
'aphront-tokenizer-control-css' => '04875312', 'aphront-tokenizer-control-css' => '04875312',
'aphront-tooltip-css' => '7672b60f', 'aphront-tooltip-css' => '7672b60f',
'aphront-two-column-view-css' => '16ab3ad2', 'aphront-two-column-view-css' => '16ab3ad2',
@ -497,7 +498,7 @@ return array(
'conpherence-menu-css' => 'f9f1d143', 'conpherence-menu-css' => 'f9f1d143',
'conpherence-message-pane-css' => '7cbf4cbb', 'conpherence-message-pane-css' => '7cbf4cbb',
'conpherence-notification-css' => '919974b6', 'conpherence-notification-css' => '919974b6',
'conpherence-thread-manager' => '10246726', 'conpherence-thread-manager' => '01774ab2',
'conpherence-transaction-css' => '42a457f6', 'conpherence-transaction-css' => '42a457f6',
'conpherence-update-css' => '1099a660', 'conpherence-update-css' => '1099a660',
'conpherence-widget-pane-css' => '77096740', 'conpherence-widget-pane-css' => '77096740',
@ -519,7 +520,7 @@ return array(
'global-drag-and-drop-css' => '697324ad', 'global-drag-and-drop-css' => '697324ad',
'harbormaster-css' => '49d64eb4', 'harbormaster-css' => '49d64eb4',
'herald-css' => '826075fa', 'herald-css' => '826075fa',
'herald-rule-editor' => '9229e764', 'herald-rule-editor' => '271ffdd7',
'herald-test-css' => '778b008e', 'herald-test-css' => '778b008e',
'inline-comment-summary-css' => 'eb5f8e8c', 'inline-comment-summary-css' => 'eb5f8e8c',
'javelin-aphlict' => '5359e785', 'javelin-aphlict' => '5359e785',
@ -535,7 +536,7 @@ return array(
'javelin-behavior-audio-source' => '59b251eb', 'javelin-behavior-audio-source' => '59b251eb',
'javelin-behavior-audit-preview' => 'd835b03a', 'javelin-behavior-audit-preview' => 'd835b03a',
'javelin-behavior-choose-control' => '6153c708', 'javelin-behavior-choose-control' => '6153c708',
'javelin-behavior-config-reorder-fields' => '14a827de', 'javelin-behavior-config-reorder-fields' => 'b6993408',
'javelin-behavior-conpherence-drag-and-drop-photo' => 'cf86d16a', 'javelin-behavior-conpherence-drag-and-drop-photo' => 'cf86d16a',
'javelin-behavior-conpherence-menu' => 'c0348cac', 'javelin-behavior-conpherence-menu' => 'c0348cac',
'javelin-behavior-conpherence-pontificate' => '21ba5861', 'javelin-behavior-conpherence-pontificate' => '21ba5861',
@ -546,12 +547,13 @@ return array(
'javelin-behavior-dashboard-move-panels' => '82439934', 'javelin-behavior-dashboard-move-panels' => '82439934',
'javelin-behavior-dashboard-query-panel-select' => '453c5375', 'javelin-behavior-dashboard-query-panel-select' => '453c5375',
'javelin-behavior-dashboard-tab-panel' => 'd4eecc63', 'javelin-behavior-dashboard-tab-panel' => 'd4eecc63',
'javelin-behavior-day-view' => '5c46cff2',
'javelin-behavior-device' => 'a205cf28', 'javelin-behavior-device' => 'a205cf28',
'javelin-behavior-differential-add-reviewers-and-ccs' => 'e10f8e18', 'javelin-behavior-differential-add-reviewers-and-ccs' => 'e10f8e18',
'javelin-behavior-differential-comment-jump' => '4fdb476d', 'javelin-behavior-differential-comment-jump' => '4fdb476d',
'javelin-behavior-differential-diff-radios' => 'e1ff79b1', 'javelin-behavior-differential-diff-radios' => 'e1ff79b1',
'javelin-behavior-differential-dropdown-menus' => '2035b9cb', 'javelin-behavior-differential-dropdown-menus' => '2035b9cb',
'javelin-behavior-differential-edit-inline-comments' => 'e723c323', 'javelin-behavior-differential-edit-inline-comments' => '037b59eb',
'javelin-behavior-differential-feedback-preview' => 'b064af76', 'javelin-behavior-differential-feedback-preview' => 'b064af76',
'javelin-behavior-differential-keyboard-navigation' => '2c426492', 'javelin-behavior-differential-keyboard-navigation' => '2c426492',
'javelin-behavior-differential-populate' => '8694b1df', 'javelin-behavior-differential-populate' => '8694b1df',
@ -566,8 +568,8 @@ return array(
'javelin-behavior-doorkeeper-tag' => 'e5822781', 'javelin-behavior-doorkeeper-tag' => 'e5822781',
'javelin-behavior-durable-column' => '16c695bf', 'javelin-behavior-durable-column' => '16c695bf',
'javelin-behavior-error-log' => '6882e80a', 'javelin-behavior-error-log' => '6882e80a',
'javelin-behavior-event-all-day' => 'ca5fa62a', 'javelin-behavior-event-all-day' => '38dcf3c8',
'javelin-behavior-fancy-datepicker' => '5c0f680f', 'javelin-behavior-fancy-datepicker' => '510b5809',
'javelin-behavior-global-drag-and-drop' => 'c8e57404', 'javelin-behavior-global-drag-and-drop' => 'c8e57404',
'javelin-behavior-herald-rule-editor' => '7ebaeed3', 'javelin-behavior-herald-rule-editor' => '7ebaeed3',
'javelin-behavior-high-security-warning' => 'a464fe03', 'javelin-behavior-high-security-warning' => 'a464fe03',
@ -743,7 +745,7 @@ return array(
'phabricator-uiexample-reactor-select' => 'a155550f', 'phabricator-uiexample-reactor-select' => 'a155550f',
'phabricator-uiexample-reactor-sendclass' => '1def2711', 'phabricator-uiexample-reactor-sendclass' => '1def2711',
'phabricator-uiexample-reactor-sendproperties' => 'b1f0ccee', 'phabricator-uiexample-reactor-sendproperties' => 'b1f0ccee',
'phabricator-zindex-css' => '8c8c40aa', 'phabricator-zindex-css' => '63689f49',
'phame-css' => '88bd4705', 'phame-css' => '88bd4705',
'pholio-css' => '95174bdd', 'pholio-css' => '95174bdd',
'pholio-edit-css' => '3ad9d1ee', 'pholio-edit-css' => '3ad9d1ee',
@ -757,7 +759,7 @@ return array(
'phui-box-css' => 'a5bb366d', 'phui-box-css' => 'a5bb366d',
'phui-button-css' => 'b995182d', 'phui-button-css' => 'b995182d',
'phui-calendar-css' => 'ccabe893', 'phui-calendar-css' => 'ccabe893',
'phui-calendar-day-css' => 'c0cf782a', 'phui-calendar-day-css' => 'd1cf6f93',
'phui-calendar-list-css' => 'c1c7f338', 'phui-calendar-list-css' => 'c1c7f338',
'phui-calendar-month-css' => '476be7e0', 'phui-calendar-month-css' => '476be7e0',
'phui-crumbs-view-css' => '3840dc4c', 'phui-crumbs-view-css' => '3840dc4c',
@ -766,13 +768,13 @@ return array(
'phui-font-icon-base-css' => '3dad2ae3', 'phui-font-icon-base-css' => '3dad2ae3',
'phui-fontkit-css' => 'b664ac96', 'phui-fontkit-css' => 'b664ac96',
'phui-form-css' => 'f535f938', 'phui-form-css' => 'f535f938',
'phui-form-view-css' => '87263b05', 'phui-form-view-css' => 'a0e8f168',
'phui-header-view-css' => 'd7612da3', 'phui-header-view-css' => 'd7612da3',
'phui-icon-view-css' => '88ba9081', 'phui-icon-view-css' => '88ba9081',
'phui-image-mask-css' => '5a8b09c8', 'phui-image-mask-css' => '5a8b09c8',
'phui-info-panel-css' => '27ea50a1', 'phui-info-panel-css' => '27ea50a1',
'phui-info-view-css' => '33e54618', 'phui-info-view-css' => '33e54618',
'phui-inline-comment-view-css' => '2174771a', 'phui-inline-comment-view-css' => 'aa16f165',
'phui-list-view-css' => 'e448b6ba', 'phui-list-view-css' => 'e448b6ba',
'phui-object-box-css' => '8eacbeed', 'phui-object-box-css' => '8eacbeed',
'phui-object-item-list-view-css' => '24ed8d94', 'phui-object-item-list-view-css' => '24ed8d94',
@ -815,9 +817,28 @@ return array(
'unhandled-exception-css' => '4c96257a', 'unhandled-exception-css' => '4c96257a',
), ),
'requires' => array( 'requires' => array(
'01774ab2' => array(
'javelin-dom',
'javelin-util',
'javelin-stratcom',
'javelin-install',
'javelin-aphlict',
'javelin-workflow',
'javelin-router',
'javelin-behavior-device',
'javelin-vector',
),
'029a133d' => array( '029a133d' => array(
'aphront-dialog-view-css', 'aphront-dialog-view-css',
), ),
'037b59eb' => array(
'javelin-behavior',
'javelin-stratcom',
'javelin-dom',
'javelin-util',
'javelin-vector',
'differential-inline-comment-editor',
),
'048330fa' => array( '048330fa' => array(
'javelin-behavior', 'javelin-behavior',
'javelin-typeahead-ondemand-source', 'javelin-typeahead-ondemand-source',
@ -874,17 +895,6 @@ return array(
'javelin-install', 'javelin-install',
'javelin-util', 'javelin-util',
), ),
10246726 => array(
'javelin-dom',
'javelin-util',
'javelin-stratcom',
'javelin-install',
'javelin-aphlict',
'javelin-workflow',
'javelin-router',
'javelin-behavior-device',
'javelin-vector',
),
'13c739ea' => array( '13c739ea' => array(
'javelin-behavior', 'javelin-behavior',
'javelin-stratcom', 'javelin-stratcom',
@ -899,13 +909,6 @@ return array(
'javelin-dom', 'javelin-dom',
'javelin-history', 'javelin-history',
), ),
'14a827de' => array(
'javelin-behavior',
'javelin-stratcom',
'javelin-dom',
'javelin-json',
'phabricator-draggable-list',
),
'14ac66f5' => array( '14ac66f5' => array(
'javelin-install', 'javelin-install',
'javelin-dom', 'javelin-dom',
@ -982,6 +985,15 @@ return array(
'phabricator-drag-and-drop-file-upload', 'phabricator-drag-and-drop-file-upload',
'phabricator-draggable-list', 'phabricator-draggable-list',
), ),
'271ffdd7' => array(
'multirow-row-manager',
'javelin-install',
'javelin-util',
'javelin-dom',
'javelin-stratcom',
'javelin-json',
'phabricator-prefab',
),
'2818f5ce' => array( '2818f5ce' => array(
'javelin-install', 'javelin-install',
'javelin-util', 'javelin-util',
@ -1147,6 +1159,13 @@ return array(
'javelin-typeahead-source', 'javelin-typeahead-source',
'javelin-util', 'javelin-util',
), ),
'510b5809' => array(
'javelin-behavior',
'javelin-util',
'javelin-dom',
'javelin-stratcom',
'javelin-vector',
),
'519705ea' => array( '519705ea' => array(
'javelin-install', 'javelin-install',
'javelin-dom', 'javelin-dom',
@ -1224,13 +1243,6 @@ return array(
'javelin-uri', 'javelin-uri',
'javelin-routable', 'javelin-routable',
), ),
'5c0f680f' => array(
'javelin-behavior',
'javelin-util',
'javelin-dom',
'javelin-stratcom',
'javelin-vector',
),
'5c54cbf3' => array( '5c54cbf3' => array(
'javelin-behavior', 'javelin-behavior',
'javelin-stratcom', 'javelin-stratcom',
@ -1505,15 +1517,6 @@ return array(
'javelin-dom', 'javelin-dom',
'javelin-stratcom', 'javelin-stratcom',
), ),
'9229e764' => array(
'multirow-row-manager',
'javelin-install',
'javelin-util',
'javelin-dom',
'javelin-stratcom',
'javelin-json',
'phabricator-prefab',
),
93568464 => array( 93568464 => array(
'javelin-behavior', 'javelin-behavior',
'javelin-dom', 'javelin-dom',
@ -1706,6 +1709,13 @@ return array(
'javelin-dom', 'javelin-dom',
'javelin-util', 'javelin-util',
), ),
'b6993408' => array(
'javelin-behavior',
'javelin-stratcom',
'javelin-dom',
'javelin-json',
'phabricator-draggable-list',
),
'ba4fa35c' => array( 'ba4fa35c' => array(
'javelin-behavior', 'javelin-behavior',
'javelin-dom', 'javelin-dom',
@ -1913,14 +1923,6 @@ return array(
'e6e25838' => array( 'e6e25838' => array(
'javelin-install', 'javelin-install',
), ),
'e723c323' => array(
'javelin-behavior',
'javelin-stratcom',
'javelin-dom',
'javelin-util',
'javelin-vector',
'differential-inline-comment-editor',
),
'e9581f08' => array( 'e9581f08' => array(
'javelin-behavior', 'javelin-behavior',
'javelin-stratcom', 'javelin-stratcom',

View file

@ -0,0 +1,2 @@
ALTER TABLE {$NAMESPACE}_releeph.releeph_project
MODIFY arcanistProjectID int(10) unsigned NULL;

View file

@ -0,0 +1,7 @@
CREATE TABLE {$NAMESPACE}_differential.differential_hiddencomment (
id INT UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY,
userPHID VARBINARY(64) NOT NULL,
commentID INT UNSIGNED NOT NULL,
UNIQUE KEY `key_user` (userPHID, commentID),
KEY `key_comment` (commentID)
) ENGINE=InnoDB, COLLATE {$COLLATE_TEXT};

View file

@ -0,0 +1,2 @@
ALTER TABLE {$NAMESPACE}_owners.owners_package
ADD mailKey binary(20) NOT NULL;

View file

@ -0,0 +1,18 @@
<?php
$table = new PhabricatorOwnersPackage();
$conn_w = $table->establishConnection('w');
$iterator = new LiskMigrationIterator($table);
foreach ($iterator as $package) {
$id = $package->getID();
echo pht('Adding mail key for package %d...', $id);
echo "\n";
queryfx(
$conn_w,
'UPDATE %T SET mailKey = %s WHERE id = %d',
$table->getTableName(),
Filesystem::readRandomCharacters(20),
$id);
}

View file

@ -0,0 +1,19 @@
CREATE TABLE {$NAMESPACE}_owners.owners_packagetransaction (
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) COLLATE {$COLLATE_TEXT} NOT NULL,
oldValue LONGTEXT COLLATE {$COLLATE_TEXT} NOT NULL,
newValue LONGTEXT COLLATE {$COLLATE_TEXT} NOT NULL,
contentSource LONGTEXT COLLATE {$COLLATE_TEXT} NOT NULL,
metadata LONGTEXT COLLATE {$COLLATE_TEXT} NOT NULL,
dateCreated INT UNSIGNED NOT NULL,
dateModified INT UNSIGNED NOT NULL,
UNIQUE KEY `key_phid` (`phid`),
KEY `key_object` (`objectPHID`)
) ENGINE=InnoDB, COLLATE {$COLLATE_TEXT};

View file

@ -374,6 +374,7 @@ phutil_register_library_map(array(
'DifferentialGetWorkingCopy' => 'applications/differential/DifferentialGetWorkingCopy.php', 'DifferentialGetWorkingCopy' => 'applications/differential/DifferentialGetWorkingCopy.php',
'DifferentialGitHubLandingStrategy' => 'applications/differential/landing/DifferentialGitHubLandingStrategy.php', 'DifferentialGitHubLandingStrategy' => 'applications/differential/landing/DifferentialGitHubLandingStrategy.php',
'DifferentialGitSVNIDField' => 'applications/differential/customfield/DifferentialGitSVNIDField.php', 'DifferentialGitSVNIDField' => 'applications/differential/customfield/DifferentialGitSVNIDField.php',
'DifferentialHiddenComment' => 'applications/differential/storage/DifferentialHiddenComment.php',
'DifferentialHostField' => 'applications/differential/customfield/DifferentialHostField.php', 'DifferentialHostField' => 'applications/differential/customfield/DifferentialHostField.php',
'DifferentialHostedGitLandingStrategy' => 'applications/differential/landing/DifferentialHostedGitLandingStrategy.php', 'DifferentialHostedGitLandingStrategy' => 'applications/differential/landing/DifferentialHostedGitLandingStrategy.php',
'DifferentialHostedMercurialLandingStrategy' => 'applications/differential/landing/DifferentialHostedMercurialLandingStrategy.php', 'DifferentialHostedMercurialLandingStrategy' => 'applications/differential/landing/DifferentialHostedMercurialLandingStrategy.php',
@ -461,7 +462,6 @@ phutil_register_library_map(array(
'DifferentialUpdateRevisionConduitAPIMethod' => 'applications/differential/conduit/DifferentialUpdateRevisionConduitAPIMethod.php', 'DifferentialUpdateRevisionConduitAPIMethod' => 'applications/differential/conduit/DifferentialUpdateRevisionConduitAPIMethod.php',
'DifferentialUpdateUnitResultsConduitAPIMethod' => 'applications/differential/conduit/DifferentialUpdateUnitResultsConduitAPIMethod.php', 'DifferentialUpdateUnitResultsConduitAPIMethod' => 'applications/differential/conduit/DifferentialUpdateUnitResultsConduitAPIMethod.php',
'DifferentialViewPolicyField' => 'applications/differential/customfield/DifferentialViewPolicyField.php', 'DifferentialViewPolicyField' => 'applications/differential/customfield/DifferentialViewPolicyField.php',
'DiffusionArcanistProjectDatasource' => 'applications/diffusion/typeahead/DiffusionArcanistProjectDatasource.php',
'DiffusionAuditorDatasource' => 'applications/diffusion/typeahead/DiffusionAuditorDatasource.php', 'DiffusionAuditorDatasource' => 'applications/diffusion/typeahead/DiffusionAuditorDatasource.php',
'DiffusionBranchQueryConduitAPIMethod' => 'applications/diffusion/conduit/DiffusionBranchQueryConduitAPIMethod.php', 'DiffusionBranchQueryConduitAPIMethod' => 'applications/diffusion/conduit/DiffusionBranchQueryConduitAPIMethod.php',
'DiffusionBranchTableController' => 'applications/diffusion/controller/DiffusionBranchTableController.php', 'DiffusionBranchTableController' => 'applications/diffusion/controller/DiffusionBranchTableController.php',
@ -596,6 +596,7 @@ phutil_register_library_map(array(
'DiffusionRepositoryEditEncodingController' => 'applications/diffusion/controller/DiffusionRepositoryEditEncodingController.php', 'DiffusionRepositoryEditEncodingController' => 'applications/diffusion/controller/DiffusionRepositoryEditEncodingController.php',
'DiffusionRepositoryEditHostingController' => 'applications/diffusion/controller/DiffusionRepositoryEditHostingController.php', 'DiffusionRepositoryEditHostingController' => 'applications/diffusion/controller/DiffusionRepositoryEditHostingController.php',
'DiffusionRepositoryEditMainController' => 'applications/diffusion/controller/DiffusionRepositoryEditMainController.php', 'DiffusionRepositoryEditMainController' => 'applications/diffusion/controller/DiffusionRepositoryEditMainController.php',
'DiffusionRepositoryEditStagingController' => 'applications/diffusion/controller/DiffusionRepositoryEditStagingController.php',
'DiffusionRepositoryEditStorageController' => 'applications/diffusion/controller/DiffusionRepositoryEditStorageController.php', 'DiffusionRepositoryEditStorageController' => 'applications/diffusion/controller/DiffusionRepositoryEditStorageController.php',
'DiffusionRepositoryEditSubversionController' => 'applications/diffusion/controller/DiffusionRepositoryEditSubversionController.php', 'DiffusionRepositoryEditSubversionController' => 'applications/diffusion/controller/DiffusionRepositoryEditSubversionController.php',
'DiffusionRepositoryEditUpdateController' => 'applications/diffusion/controller/DiffusionRepositoryEditUpdateController.php', 'DiffusionRepositoryEditUpdateController' => 'applications/diffusion/controller/DiffusionRepositoryEditUpdateController.php',
@ -1176,6 +1177,7 @@ phutil_register_library_map(array(
'PHUIDiffInlineCommentUndoView' => 'infrastructure/diff/view/PHUIDiffInlineCommentUndoView.php', 'PHUIDiffInlineCommentUndoView' => 'infrastructure/diff/view/PHUIDiffInlineCommentUndoView.php',
'PHUIDiffInlineCommentView' => 'infrastructure/diff/view/PHUIDiffInlineCommentView.php', 'PHUIDiffInlineCommentView' => 'infrastructure/diff/view/PHUIDiffInlineCommentView.php',
'PHUIDiffOneUpInlineCommentRowScaffold' => 'infrastructure/diff/view/PHUIDiffOneUpInlineCommentRowScaffold.php', 'PHUIDiffOneUpInlineCommentRowScaffold' => 'infrastructure/diff/view/PHUIDiffOneUpInlineCommentRowScaffold.php',
'PHUIDiffRevealIconView' => 'infrastructure/diff/view/PHUIDiffRevealIconView.php',
'PHUIDiffTwoUpInlineCommentRowScaffold' => 'infrastructure/diff/view/PHUIDiffTwoUpInlineCommentRowScaffold.php', 'PHUIDiffTwoUpInlineCommentRowScaffold' => 'infrastructure/diff/view/PHUIDiffTwoUpInlineCommentRowScaffold.php',
'PHUIDocumentExample' => 'applications/uiexample/examples/PHUIDocumentExample.php', 'PHUIDocumentExample' => 'applications/uiexample/examples/PHUIDocumentExample.php',
'PHUIDocumentView' => 'view/phui/PHUIDocumentView.php', 'PHUIDocumentView' => 'view/phui/PHUIDocumentView.php',
@ -1226,10 +1228,6 @@ phutil_register_library_map(array(
'PHUITypeaheadExample' => 'applications/uiexample/examples/PHUITypeaheadExample.php', 'PHUITypeaheadExample' => 'applications/uiexample/examples/PHUITypeaheadExample.php',
'PHUIWorkboardView' => 'view/phui/PHUIWorkboardView.php', 'PHUIWorkboardView' => 'view/phui/PHUIWorkboardView.php',
'PHUIWorkpanelView' => 'view/phui/PHUIWorkpanelView.php', 'PHUIWorkpanelView' => 'view/phui/PHUIWorkpanelView.php',
'PackageCreateMail' => 'applications/owners/mail/PackageCreateMail.php',
'PackageDeleteMail' => 'applications/owners/mail/PackageDeleteMail.php',
'PackageMail' => 'applications/owners/mail/PackageMail.php',
'PackageModifyMail' => 'applications/owners/mail/PackageModifyMail.php',
'PassphraseAbstractKey' => 'applications/passphrase/keys/PassphraseAbstractKey.php', 'PassphraseAbstractKey' => 'applications/passphrase/keys/PassphraseAbstractKey.php',
'PassphraseConduitAPIMethod' => 'applications/passphrase/conduit/PassphraseConduitAPIMethod.php', 'PassphraseConduitAPIMethod' => 'applications/passphrase/conduit/PassphraseConduitAPIMethod.php',
'PassphraseController' => 'applications/passphrase/controller/PassphraseController.php', 'PassphraseController' => 'applications/passphrase/controller/PassphraseController.php',
@ -1496,6 +1494,7 @@ phutil_register_library_map(array(
'PhabricatorCalendarEvent' => 'applications/calendar/storage/PhabricatorCalendarEvent.php', 'PhabricatorCalendarEvent' => 'applications/calendar/storage/PhabricatorCalendarEvent.php',
'PhabricatorCalendarEventCancelController' => 'applications/calendar/controller/PhabricatorCalendarEventCancelController.php', 'PhabricatorCalendarEventCancelController' => 'applications/calendar/controller/PhabricatorCalendarEventCancelController.php',
'PhabricatorCalendarEventCommentController' => 'applications/calendar/controller/PhabricatorCalendarEventCommentController.php', 'PhabricatorCalendarEventCommentController' => 'applications/calendar/controller/PhabricatorCalendarEventCommentController.php',
'PhabricatorCalendarEventDragController' => 'applications/calendar/controller/PhabricatorCalendarEventDragController.php',
'PhabricatorCalendarEventEditController' => 'applications/calendar/controller/PhabricatorCalendarEventEditController.php', 'PhabricatorCalendarEventEditController' => 'applications/calendar/controller/PhabricatorCalendarEventEditController.php',
'PhabricatorCalendarEventEditIconController' => 'applications/calendar/controller/PhabricatorCalendarEventEditIconController.php', 'PhabricatorCalendarEventEditIconController' => 'applications/calendar/controller/PhabricatorCalendarEventEditIconController.php',
'PhabricatorCalendarEventEditor' => 'applications/calendar/editor/PhabricatorCalendarEventEditor.php', 'PhabricatorCalendarEventEditor' => 'applications/calendar/editor/PhabricatorCalendarEventEditor.php',
@ -1538,6 +1537,8 @@ phutil_register_library_map(array(
'PhabricatorClusterConfigOptions' => 'applications/config/option/PhabricatorClusterConfigOptions.php', 'PhabricatorClusterConfigOptions' => 'applications/config/option/PhabricatorClusterConfigOptions.php',
'PhabricatorCommitBranchesField' => 'applications/repository/customfield/PhabricatorCommitBranchesField.php', 'PhabricatorCommitBranchesField' => 'applications/repository/customfield/PhabricatorCommitBranchesField.php',
'PhabricatorCommitCustomField' => 'applications/repository/customfield/PhabricatorCommitCustomField.php', 'PhabricatorCommitCustomField' => 'applications/repository/customfield/PhabricatorCommitCustomField.php',
'PhabricatorCommitMergedCommitsField' => 'applications/repository/customfield/PhabricatorCommitMergedCommitsField.php',
'PhabricatorCommitRepositoryField' => 'applications/repository/customfield/PhabricatorCommitRepositoryField.php',
'PhabricatorCommitSearchEngine' => 'applications/audit/query/PhabricatorCommitSearchEngine.php', 'PhabricatorCommitSearchEngine' => 'applications/audit/query/PhabricatorCommitSearchEngine.php',
'PhabricatorCommitTagsField' => 'applications/repository/customfield/PhabricatorCommitTagsField.php', 'PhabricatorCommitTagsField' => 'applications/repository/customfield/PhabricatorCommitTagsField.php',
'PhabricatorCommonPasswords' => 'applications/auth/constants/PhabricatorCommonPasswords.php', 'PhabricatorCommonPasswords' => 'applications/auth/constants/PhabricatorCommonPasswords.php',
@ -2160,19 +2161,21 @@ phutil_register_library_map(array(
'PhabricatorOwnersConfigOptions' => 'applications/owners/config/PhabricatorOwnersConfigOptions.php', 'PhabricatorOwnersConfigOptions' => 'applications/owners/config/PhabricatorOwnersConfigOptions.php',
'PhabricatorOwnersController' => 'applications/owners/controller/PhabricatorOwnersController.php', 'PhabricatorOwnersController' => 'applications/owners/controller/PhabricatorOwnersController.php',
'PhabricatorOwnersDAO' => 'applications/owners/storage/PhabricatorOwnersDAO.php', 'PhabricatorOwnersDAO' => 'applications/owners/storage/PhabricatorOwnersDAO.php',
'PhabricatorOwnersDeleteController' => 'applications/owners/controller/PhabricatorOwnersDeleteController.php',
'PhabricatorOwnersDetailController' => 'applications/owners/controller/PhabricatorOwnersDetailController.php', 'PhabricatorOwnersDetailController' => 'applications/owners/controller/PhabricatorOwnersDetailController.php',
'PhabricatorOwnersEditController' => 'applications/owners/controller/PhabricatorOwnersEditController.php', 'PhabricatorOwnersEditController' => 'applications/owners/controller/PhabricatorOwnersEditController.php',
'PhabricatorOwnersListController' => 'applications/owners/controller/PhabricatorOwnersListController.php', 'PhabricatorOwnersListController' => 'applications/owners/controller/PhabricatorOwnersListController.php',
'PhabricatorOwnersOwner' => 'applications/owners/storage/PhabricatorOwnersOwner.php', 'PhabricatorOwnersOwner' => 'applications/owners/storage/PhabricatorOwnersOwner.php',
'PhabricatorOwnersPackage' => 'applications/owners/storage/PhabricatorOwnersPackage.php', 'PhabricatorOwnersPackage' => 'applications/owners/storage/PhabricatorOwnersPackage.php',
'PhabricatorOwnersPackageDatasource' => 'applications/owners/typeahead/PhabricatorOwnersPackageDatasource.php', 'PhabricatorOwnersPackageDatasource' => 'applications/owners/typeahead/PhabricatorOwnersPackageDatasource.php',
'PhabricatorOwnersPackageEditor' => 'applications/owners/editor/PhabricatorOwnersPackageEditor.php',
'PhabricatorOwnersPackagePHIDType' => 'applications/owners/phid/PhabricatorOwnersPackagePHIDType.php', 'PhabricatorOwnersPackagePHIDType' => 'applications/owners/phid/PhabricatorOwnersPackagePHIDType.php',
'PhabricatorOwnersPackagePathValidator' => 'applications/repository/worker/commitchangeparser/PhabricatorOwnersPackagePathValidator.php',
'PhabricatorOwnersPackageQuery' => 'applications/owners/query/PhabricatorOwnersPackageQuery.php', 'PhabricatorOwnersPackageQuery' => 'applications/owners/query/PhabricatorOwnersPackageQuery.php',
'PhabricatorOwnersPackageSearchEngine' => 'applications/owners/query/PhabricatorOwnersPackageSearchEngine.php',
'PhabricatorOwnersPackageTestCase' => 'applications/owners/storage/__tests__/PhabricatorOwnersPackageTestCase.php', 'PhabricatorOwnersPackageTestCase' => 'applications/owners/storage/__tests__/PhabricatorOwnersPackageTestCase.php',
'PhabricatorOwnersPackageTransaction' => 'applications/owners/storage/PhabricatorOwnersPackageTransaction.php',
'PhabricatorOwnersPackageTransactionEditor' => 'applications/owners/editor/PhabricatorOwnersPackageTransactionEditor.php',
'PhabricatorOwnersPackageTransactionQuery' => 'applications/owners/query/PhabricatorOwnersPackageTransactionQuery.php',
'PhabricatorOwnersPath' => 'applications/owners/storage/PhabricatorOwnersPath.php', 'PhabricatorOwnersPath' => 'applications/owners/storage/PhabricatorOwnersPath.php',
'PhabricatorOwnersPathsController' => 'applications/owners/controller/PhabricatorOwnersPathsController.php',
'PhabricatorPHDConfigOptions' => 'applications/config/option/PhabricatorPHDConfigOptions.php', 'PhabricatorPHDConfigOptions' => 'applications/config/option/PhabricatorPHDConfigOptions.php',
'PhabricatorPHID' => 'applications/phid/storage/PhabricatorPHID.php', 'PhabricatorPHID' => 'applications/phid/storage/PhabricatorPHID.php',
'PhabricatorPHIDConstants' => 'applications/phid/PhabricatorPHIDConstants.php', 'PhabricatorPHIDConstants' => 'applications/phid/PhabricatorPHIDConstants.php',
@ -2383,8 +2386,6 @@ phutil_register_library_map(array(
'PhabricatorRepositoriesSetupCheck' => 'applications/config/check/PhabricatorRepositoriesSetupCheck.php', 'PhabricatorRepositoriesSetupCheck' => 'applications/config/check/PhabricatorRepositoriesSetupCheck.php',
'PhabricatorRepository' => 'applications/repository/storage/PhabricatorRepository.php', 'PhabricatorRepository' => 'applications/repository/storage/PhabricatorRepository.php',
'PhabricatorRepositoryArcanistProject' => 'applications/repository/storage/PhabricatorRepositoryArcanistProject.php', 'PhabricatorRepositoryArcanistProject' => 'applications/repository/storage/PhabricatorRepositoryArcanistProject.php',
'PhabricatorRepositoryArcanistProjectDeleteController' => 'applications/repository/controller/PhabricatorRepositoryArcanistProjectDeleteController.php',
'PhabricatorRepositoryArcanistProjectEditController' => 'applications/repository/controller/PhabricatorRepositoryArcanistProjectEditController.php',
'PhabricatorRepositoryArcanistProjectPHIDType' => 'applications/repository/phid/PhabricatorRepositoryArcanistProjectPHIDType.php', 'PhabricatorRepositoryArcanistProjectPHIDType' => 'applications/repository/phid/PhabricatorRepositoryArcanistProjectPHIDType.php',
'PhabricatorRepositoryArcanistProjectQuery' => 'applications/repository/query/PhabricatorRepositoryArcanistProjectQuery.php', 'PhabricatorRepositoryArcanistProjectQuery' => 'applications/repository/query/PhabricatorRepositoryArcanistProjectQuery.php',
'PhabricatorRepositoryAuditRequest' => 'applications/repository/storage/PhabricatorRepositoryAuditRequest.php', 'PhabricatorRepositoryAuditRequest' => 'applications/repository/storage/PhabricatorRepositoryAuditRequest.php',
@ -3146,7 +3147,6 @@ phutil_register_library_map(array(
'ReleephProductTransactionQuery' => 'applications/releeph/query/ReleephProductTransactionQuery.php', 'ReleephProductTransactionQuery' => 'applications/releeph/query/ReleephProductTransactionQuery.php',
'ReleephProductViewController' => 'applications/releeph/controller/product/ReleephProductViewController.php', 'ReleephProductViewController' => 'applications/releeph/controller/product/ReleephProductViewController.php',
'ReleephProject' => 'applications/releeph/storage/ReleephProject.php', 'ReleephProject' => 'applications/releeph/storage/ReleephProject.php',
'ReleephProjectInfoConduitAPIMethod' => 'applications/releeph/conduit/ReleephProjectInfoConduitAPIMethod.php',
'ReleephQueryBranchesConduitAPIMethod' => 'applications/releeph/conduit/ReleephQueryBranchesConduitAPIMethod.php', 'ReleephQueryBranchesConduitAPIMethod' => 'applications/releeph/conduit/ReleephQueryBranchesConduitAPIMethod.php',
'ReleephQueryProductsConduitAPIMethod' => 'applications/releeph/conduit/ReleephQueryProductsConduitAPIMethod.php', 'ReleephQueryProductsConduitAPIMethod' => 'applications/releeph/conduit/ReleephQueryProductsConduitAPIMethod.php',
'ReleephQueryRequestsConduitAPIMethod' => 'applications/releeph/conduit/ReleephQueryRequestsConduitAPIMethod.php', 'ReleephQueryRequestsConduitAPIMethod' => 'applications/releeph/conduit/ReleephQueryRequestsConduitAPIMethod.php',
@ -3614,6 +3614,7 @@ phutil_register_library_map(array(
'DifferentialGetRevisionConduitAPIMethod' => 'DifferentialConduitAPIMethod', 'DifferentialGetRevisionConduitAPIMethod' => 'DifferentialConduitAPIMethod',
'DifferentialGitHubLandingStrategy' => 'DifferentialLandingStrategy', 'DifferentialGitHubLandingStrategy' => 'DifferentialLandingStrategy',
'DifferentialGitSVNIDField' => 'DifferentialCustomField', 'DifferentialGitSVNIDField' => 'DifferentialCustomField',
'DifferentialHiddenComment' => 'DifferentialDAO',
'DifferentialHostField' => 'DifferentialCustomField', 'DifferentialHostField' => 'DifferentialCustomField',
'DifferentialHostedGitLandingStrategy' => 'DifferentialLandingStrategy', 'DifferentialHostedGitLandingStrategy' => 'DifferentialLandingStrategy',
'DifferentialHostedMercurialLandingStrategy' => 'DifferentialLandingStrategy', 'DifferentialHostedMercurialLandingStrategy' => 'DifferentialLandingStrategy',
@ -3706,7 +3707,6 @@ phutil_register_library_map(array(
'DifferentialUpdateRevisionConduitAPIMethod' => 'DifferentialConduitAPIMethod', 'DifferentialUpdateRevisionConduitAPIMethod' => 'DifferentialConduitAPIMethod',
'DifferentialUpdateUnitResultsConduitAPIMethod' => 'DifferentialConduitAPIMethod', 'DifferentialUpdateUnitResultsConduitAPIMethod' => 'DifferentialConduitAPIMethod',
'DifferentialViewPolicyField' => 'DifferentialCoreCustomField', 'DifferentialViewPolicyField' => 'DifferentialCoreCustomField',
'DiffusionArcanistProjectDatasource' => 'PhabricatorTypeaheadDatasource',
'DiffusionAuditorDatasource' => 'PhabricatorTypeaheadCompositeDatasource', 'DiffusionAuditorDatasource' => 'PhabricatorTypeaheadCompositeDatasource',
'DiffusionBranchQueryConduitAPIMethod' => 'DiffusionQueryConduitAPIMethod', 'DiffusionBranchQueryConduitAPIMethod' => 'DiffusionQueryConduitAPIMethod',
'DiffusionBranchTableController' => 'DiffusionController', 'DiffusionBranchTableController' => 'DiffusionController',
@ -3831,6 +3831,7 @@ phutil_register_library_map(array(
'DiffusionRepositoryEditEncodingController' => 'DiffusionRepositoryEditController', 'DiffusionRepositoryEditEncodingController' => 'DiffusionRepositoryEditController',
'DiffusionRepositoryEditHostingController' => 'DiffusionRepositoryEditController', 'DiffusionRepositoryEditHostingController' => 'DiffusionRepositoryEditController',
'DiffusionRepositoryEditMainController' => 'DiffusionRepositoryEditController', 'DiffusionRepositoryEditMainController' => 'DiffusionRepositoryEditController',
'DiffusionRepositoryEditStagingController' => 'DiffusionRepositoryEditController',
'DiffusionRepositoryEditStorageController' => 'DiffusionRepositoryEditController', 'DiffusionRepositoryEditStorageController' => 'DiffusionRepositoryEditController',
'DiffusionRepositoryEditSubversionController' => 'DiffusionRepositoryEditController', 'DiffusionRepositoryEditSubversionController' => 'DiffusionRepositoryEditController',
'DiffusionRepositoryEditUpdateController' => 'DiffusionRepositoryEditController', 'DiffusionRepositoryEditUpdateController' => 'DiffusionRepositoryEditController',
@ -4502,6 +4503,7 @@ phutil_register_library_map(array(
'PHUIDiffInlineCommentUndoView' => 'PHUIDiffInlineCommentView', 'PHUIDiffInlineCommentUndoView' => 'PHUIDiffInlineCommentView',
'PHUIDiffInlineCommentView' => 'AphrontView', 'PHUIDiffInlineCommentView' => 'AphrontView',
'PHUIDiffOneUpInlineCommentRowScaffold' => 'PHUIDiffInlineCommentRowScaffold', 'PHUIDiffOneUpInlineCommentRowScaffold' => 'PHUIDiffInlineCommentRowScaffold',
'PHUIDiffRevealIconView' => 'AphrontView',
'PHUIDiffTwoUpInlineCommentRowScaffold' => 'PHUIDiffInlineCommentRowScaffold', 'PHUIDiffTwoUpInlineCommentRowScaffold' => 'PHUIDiffInlineCommentRowScaffold',
'PHUIDocumentExample' => 'PhabricatorUIExample', 'PHUIDocumentExample' => 'PhabricatorUIExample',
'PHUIDocumentView' => 'AphrontTagView', 'PHUIDocumentView' => 'AphrontTagView',
@ -4552,10 +4554,6 @@ phutil_register_library_map(array(
'PHUITypeaheadExample' => 'PhabricatorUIExample', 'PHUITypeaheadExample' => 'PhabricatorUIExample',
'PHUIWorkboardView' => 'AphrontTagView', 'PHUIWorkboardView' => 'AphrontTagView',
'PHUIWorkpanelView' => 'AphrontTagView', 'PHUIWorkpanelView' => 'AphrontTagView',
'PackageCreateMail' => 'PackageMail',
'PackageDeleteMail' => 'PackageMail',
'PackageMail' => 'PhabricatorMail',
'PackageModifyMail' => 'PackageMail',
'PassphraseAbstractKey' => 'Phobject', 'PassphraseAbstractKey' => 'Phobject',
'PassphraseConduitAPIMethod' => 'ConduitAPIMethod', 'PassphraseConduitAPIMethod' => 'ConduitAPIMethod',
'PassphraseController' => 'PhabricatorController', 'PassphraseController' => 'PhabricatorController',
@ -4849,6 +4847,7 @@ phutil_register_library_map(array(
), ),
'PhabricatorCalendarEventCancelController' => 'PhabricatorCalendarController', 'PhabricatorCalendarEventCancelController' => 'PhabricatorCalendarController',
'PhabricatorCalendarEventCommentController' => 'PhabricatorCalendarController', 'PhabricatorCalendarEventCommentController' => 'PhabricatorCalendarController',
'PhabricatorCalendarEventDragController' => 'PhabricatorCalendarController',
'PhabricatorCalendarEventEditController' => 'PhabricatorCalendarController', 'PhabricatorCalendarEventEditController' => 'PhabricatorCalendarController',
'PhabricatorCalendarEventEditIconController' => 'PhabricatorCalendarController', 'PhabricatorCalendarEventEditIconController' => 'PhabricatorCalendarController',
'PhabricatorCalendarEventEditor' => 'PhabricatorApplicationTransactionEditor', 'PhabricatorCalendarEventEditor' => 'PhabricatorApplicationTransactionEditor',
@ -4900,6 +4899,8 @@ phutil_register_library_map(array(
'PhabricatorClusterConfigOptions' => 'PhabricatorApplicationConfigOptions', 'PhabricatorClusterConfigOptions' => 'PhabricatorApplicationConfigOptions',
'PhabricatorCommitBranchesField' => 'PhabricatorCommitCustomField', 'PhabricatorCommitBranchesField' => 'PhabricatorCommitCustomField',
'PhabricatorCommitCustomField' => 'PhabricatorCustomField', 'PhabricatorCommitCustomField' => 'PhabricatorCustomField',
'PhabricatorCommitMergedCommitsField' => 'PhabricatorCommitCustomField',
'PhabricatorCommitRepositoryField' => 'PhabricatorCommitCustomField',
'PhabricatorCommitSearchEngine' => 'PhabricatorApplicationSearchEngine', 'PhabricatorCommitSearchEngine' => 'PhabricatorApplicationSearchEngine',
'PhabricatorCommitTagsField' => 'PhabricatorCommitCustomField', 'PhabricatorCommitTagsField' => 'PhabricatorCommitCustomField',
'PhabricatorCommonPasswords' => 'Phobject', 'PhabricatorCommonPasswords' => 'Phobject',
@ -5560,7 +5561,6 @@ phutil_register_library_map(array(
'PhabricatorOwnersConfigOptions' => 'PhabricatorApplicationConfigOptions', 'PhabricatorOwnersConfigOptions' => 'PhabricatorApplicationConfigOptions',
'PhabricatorOwnersController' => 'PhabricatorController', 'PhabricatorOwnersController' => 'PhabricatorController',
'PhabricatorOwnersDAO' => 'PhabricatorLiskDAO', 'PhabricatorOwnersDAO' => 'PhabricatorLiskDAO',
'PhabricatorOwnersDeleteController' => 'PhabricatorOwnersController',
'PhabricatorOwnersDetailController' => 'PhabricatorOwnersController', 'PhabricatorOwnersDetailController' => 'PhabricatorOwnersController',
'PhabricatorOwnersEditController' => 'PhabricatorOwnersController', 'PhabricatorOwnersEditController' => 'PhabricatorOwnersController',
'PhabricatorOwnersListController' => 'PhabricatorOwnersController', 'PhabricatorOwnersListController' => 'PhabricatorOwnersController',
@ -5568,13 +5568,18 @@ phutil_register_library_map(array(
'PhabricatorOwnersPackage' => array( 'PhabricatorOwnersPackage' => array(
'PhabricatorOwnersDAO', 'PhabricatorOwnersDAO',
'PhabricatorPolicyInterface', 'PhabricatorPolicyInterface',
'PhabricatorApplicationTransactionInterface',
), ),
'PhabricatorOwnersPackageDatasource' => 'PhabricatorTypeaheadDatasource', 'PhabricatorOwnersPackageDatasource' => 'PhabricatorTypeaheadDatasource',
'PhabricatorOwnersPackageEditor' => 'PhabricatorEditor',
'PhabricatorOwnersPackagePHIDType' => 'PhabricatorPHIDType', 'PhabricatorOwnersPackagePHIDType' => 'PhabricatorPHIDType',
'PhabricatorOwnersPackageQuery' => 'PhabricatorCursorPagedPolicyAwareQuery', 'PhabricatorOwnersPackageQuery' => 'PhabricatorCursorPagedPolicyAwareQuery',
'PhabricatorOwnersPackageSearchEngine' => 'PhabricatorApplicationSearchEngine',
'PhabricatorOwnersPackageTestCase' => 'PhabricatorTestCase', 'PhabricatorOwnersPackageTestCase' => 'PhabricatorTestCase',
'PhabricatorOwnersPackageTransaction' => 'PhabricatorApplicationTransaction',
'PhabricatorOwnersPackageTransactionEditor' => 'PhabricatorApplicationTransactionEditor',
'PhabricatorOwnersPackageTransactionQuery' => 'PhabricatorApplicationTransactionQuery',
'PhabricatorOwnersPath' => 'PhabricatorOwnersDAO', 'PhabricatorOwnersPath' => 'PhabricatorOwnersDAO',
'PhabricatorOwnersPathsController' => 'PhabricatorOwnersController',
'PhabricatorPHDConfigOptions' => 'PhabricatorApplicationConfigOptions', 'PhabricatorPHDConfigOptions' => 'PhabricatorApplicationConfigOptions',
'PhabricatorPHPASTApplication' => 'PhabricatorApplication', 'PhabricatorPHPASTApplication' => 'PhabricatorApplication',
'PhabricatorPHPConfigSetupCheck' => 'PhabricatorSetupCheck', 'PhabricatorPHPConfigSetupCheck' => 'PhabricatorSetupCheck',
@ -5825,8 +5830,6 @@ phutil_register_library_map(array(
'PhabricatorPolicyInterface', 'PhabricatorPolicyInterface',
'PhabricatorDestructibleInterface', 'PhabricatorDestructibleInterface',
), ),
'PhabricatorRepositoryArcanistProjectDeleteController' => 'PhabricatorRepositoryController',
'PhabricatorRepositoryArcanistProjectEditController' => 'PhabricatorRepositoryController',
'PhabricatorRepositoryArcanistProjectPHIDType' => 'PhabricatorPHIDType', 'PhabricatorRepositoryArcanistProjectPHIDType' => 'PhabricatorPHIDType',
'PhabricatorRepositoryArcanistProjectQuery' => 'PhabricatorCursorPagedPolicyAwareQuery', 'PhabricatorRepositoryArcanistProjectQuery' => 'PhabricatorCursorPagedPolicyAwareQuery',
'PhabricatorRepositoryAuditRequest' => array( 'PhabricatorRepositoryAuditRequest' => array(
@ -6724,7 +6727,6 @@ phutil_register_library_map(array(
'PhabricatorApplicationTransactionInterface', 'PhabricatorApplicationTransactionInterface',
'PhabricatorPolicyInterface', 'PhabricatorPolicyInterface',
), ),
'ReleephProjectInfoConduitAPIMethod' => 'ReleephConduitAPIMethod',
'ReleephQueryBranchesConduitAPIMethod' => 'ReleephConduitAPIMethod', 'ReleephQueryBranchesConduitAPIMethod' => 'ReleephConduitAPIMethod',
'ReleephQueryProductsConduitAPIMethod' => 'ReleephConduitAPIMethod', 'ReleephQueryProductsConduitAPIMethod' => 'ReleephConduitAPIMethod',
'ReleephQueryRequestsConduitAPIMethod' => 'ReleephConduitAPIMethod', 'ReleephQueryRequestsConduitAPIMethod' => 'ReleephConduitAPIMethod',

View file

@ -7,8 +7,12 @@ final class ArcanistProjectInfoConduitAPIMethod
return 'arcanist.projectinfo'; return 'arcanist.projectinfo';
} }
public function getMethodStatus() {
return self::METHOD_STATUS_DEPRECATED;
}
public function getMethodDescription() { public function getMethodDescription() {
return pht('Get information about Arcanist projects.'); return pht('Arcanist projects are deprecated.');
} }
protected function defineParamTypes() { protected function defineParamTypes() {

View file

@ -23,6 +23,14 @@ final class PhabricatorAuditInlineComment
return $this->proxy; return $this->proxy;
} }
public function supportsHiding() {
return false;
}
public function isHidden() {
return false;
}
public function getTransactionCommentForSave() { public function getTransactionCommentForSave() {
$content_source = PhabricatorContentSource::newForSource( $content_source = PhabricatorContentSource::newForSource(
PhabricatorContentSource::SOURCE_LEGACY, PhabricatorContentSource::SOURCE_LEGACY,

View file

@ -54,6 +54,8 @@ final class PhabricatorCalendarApplication extends PhabricatorApplication {
=> 'PhabricatorCalendarEventEditController', => 'PhabricatorCalendarEventEditController',
'edit/(?P<id>[1-9]\d*)/' 'edit/(?P<id>[1-9]\d*)/'
=> 'PhabricatorCalendarEventEditController', => 'PhabricatorCalendarEventEditController',
'drag/(?P<id>[1-9]\d*)/'
=> 'PhabricatorCalendarEventDragController',
'cancel/(?P<id>[1-9]\d*)/' 'cancel/(?P<id>[1-9]\d*)/'
=> 'PhabricatorCalendarEventCancelController', => 'PhabricatorCalendarEventCancelController',
'(?P<action>join|decline|accept)/(?P<id>[1-9]\d*)/' '(?P<action>join|decline|accept)/(?P<id>[1-9]\d*)/'

View file

@ -5,11 +5,23 @@ abstract class PhabricatorCalendarController extends PhabricatorController {
protected function buildApplicationCrumbs() { protected function buildApplicationCrumbs() {
$crumbs = parent::buildApplicationCrumbs(); $crumbs = parent::buildApplicationCrumbs();
$actions = id(new PhabricatorActionListView())
->setUser($this->getViewer())
->addAction(
id(new PhabricatorActionView())
->setName(pht('Create Private Event'))
->setHref('/calendar/event/create/?mode=private'))
->addAction(
id(new PhabricatorActionView())
->setName(pht('Create Public Event'))
->setHref('/calendar/event/create/?mode=public'));
$crumbs->addAction( $crumbs->addAction(
id(new PHUIListItemView()) id(new PHUIListItemView())
->setName(pht('Create Event')) ->setName(pht('Create Event'))
->setHref($this->getApplicationURI().'event/create/') ->setHref($this->getApplicationURI().'event/create/')
->setIcon('fa-plus-square')); ->setIcon('fa-plus-square')
->setDropdownMenu($actions));
return $crumbs; return $crumbs;
} }

View file

@ -0,0 +1,66 @@
<?php
final class PhabricatorCalendarEventDragController
extends PhabricatorCalendarController {
public function handleRequest(AphrontRequest $request) {
$viewer = $request->getViewer();
$event = id(new PhabricatorCalendarEventQuery())
->setViewer($viewer)
->withIDs(array($request->getURIData('id')))
->requireCapabilities(
array(
PhabricatorPolicyCapability::CAN_VIEW,
PhabricatorPolicyCapability::CAN_EDIT,
))
->executeOne();
if (!$event) {
return new Aphront404Response();
}
if (!$request->validateCSRF()) {
return new Aphront400Response();
}
if ($event->getIsAllDay()) {
return new Aphront400Response();
}
$xactions = array();
$duration = $event->getDateTo() - $event->getDateFrom();
$start = $request->getInt('start');
$start_value = id(AphrontFormDateControlValue::newFromEpoch(
$viewer,
$start));
$end = $start + $duration;
$end_value = id(AphrontFormDateControlValue::newFromEpoch(
$viewer,
$end));
$xactions[] = id(new PhabricatorCalendarEventTransaction())
->setTransactionType(
PhabricatorCalendarEventTransaction::TYPE_START_DATE)
->setNewValue($start_value);
$xactions[] = id(new PhabricatorCalendarEventTransaction())
->setTransactionType(
PhabricatorCalendarEventTransaction::TYPE_END_DATE)
->setNewValue($end_value);
$editor = id(new PhabricatorCalendarEventEditor())
->setActor($viewer)
->setContinueOnMissingFields(true)
->setContentSourceFromRequest($request)
->setContinueOnNoEffect(true);
$xactions = $editor->applyTransactions($event, $xactions);
return id(new AphrontReloadResponse());
}
}

View file

@ -14,8 +14,8 @@ final class PhabricatorCalendarEventEditController
} }
public function handleRequest(AphrontRequest $request) { public function handleRequest(AphrontRequest $request) {
$user = $request->getUser(); $viewer = $request->getViewer();
$user_phid = $user->getPHID(); $user_phid = $viewer->getPHID();
$error_name = true; $error_name = true;
$error_start_date = true; $error_start_date = true;
$error_end_date = true; $error_end_date = true;
@ -25,9 +25,44 @@ final class PhabricatorCalendarEventEditController
$start_date_id = celerity_generate_unique_node_id(); $start_date_id = celerity_generate_unique_node_id();
$end_date_id = null; $end_date_id = null;
$next_workflow = $request->getStr('next');
$uri_query = $request->getStr('query');
if ($this->isCreate()) { if ($this->isCreate()) {
$event = PhabricatorCalendarEvent::initializeNewCalendarEvent($user); $mode = $request->getStr('mode');
list($start_value, $end_value) = $this->getDefaultTimeValues($user); $event = PhabricatorCalendarEvent::initializeNewCalendarEvent(
$viewer,
$mode);
$create_start_year = $request->getInt('year');
$create_start_month = $request->getInt('month');
$create_start_day = $request->getInt('day');
$create_start_time = $request->getStr('time');
if ($create_start_year) {
$start = AphrontFormDateControlValue::newFromParts(
$viewer,
$create_start_year,
$create_start_month,
$create_start_day,
$create_start_time);
if (!$start->isValid()) {
return new Aphront400Response();
}
$start_value = AphrontFormDateControlValue::newFromEpoch(
$viewer,
$start->getEpoch());
$end = clone $start_value->getDateTime();
$end->modify('+1 hour');
$end_value = AphrontFormDateControlValue::newFromEpoch(
$viewer,
$end->format('U'));
} else {
list($start_value, $end_value) = $this->getDefaultTimeValues($viewer);
}
$submit_label = pht('Create'); $submit_label = pht('Create');
$page_title = pht('Create Event'); $page_title = pht('Create Event');
@ -38,7 +73,7 @@ final class PhabricatorCalendarEventEditController
$end_date_id = celerity_generate_unique_node_id(); $end_date_id = celerity_generate_unique_node_id();
} else { } else {
$event = id(new PhabricatorCalendarEventQuery()) $event = id(new PhabricatorCalendarEventQuery())
->setViewer($user) ->setViewer($viewer)
->withIDs(array($this->id)) ->withIDs(array($this->id))
->requireCapabilities( ->requireCapabilities(
array( array(
@ -51,10 +86,10 @@ final class PhabricatorCalendarEventEditController
} }
$end_value = AphrontFormDateControlValue::newFromEpoch( $end_value = AphrontFormDateControlValue::newFromEpoch(
$user, $viewer,
$event->getDateTo()); $event->getDateTo());
$start_value = AphrontFormDateControlValue::newFromEpoch( $start_value = AphrontFormDateControlValue::newFromEpoch(
$user, $viewer,
$event->getDateFrom()); $event->getDateFrom());
$submit_label = pht('Update'); $submit_label = pht('Update');
@ -81,7 +116,7 @@ final class PhabricatorCalendarEventEditController
$icon = $event->getIcon(); $icon = $event->getIcon();
$current_policies = id(new PhabricatorPolicyQuery()) $current_policies = id(new PhabricatorPolicyQuery())
->setViewer($user) ->setViewer($viewer)
->setObject($event) ->setObject($event)
->execute(); ->execute();
@ -106,9 +141,9 @@ final class PhabricatorCalendarEventEditController
$new_invitees = $this->getNewInviteeList($invitees, $event); $new_invitees = $this->getNewInviteeList($invitees, $event);
$status_attending = PhabricatorCalendarEventInvitee::STATUS_ATTENDING; $status_attending = PhabricatorCalendarEventInvitee::STATUS_ATTENDING;
if ($this->isCreate()) { if ($this->isCreate()) {
$status = idx($new_invitees, $user->getPHID()); $status = idx($new_invitees, $viewer->getPHID());
if ($status) { if ($status) {
$new_invitees[$user->getPHID()] = $status_attending; $new_invitees[$viewer->getPHID()] = $status_attending;
} }
} }
@ -161,14 +196,29 @@ final class PhabricatorCalendarEventEditController
->setNewValue($request->getStr('editPolicy')); ->setNewValue($request->getStr('editPolicy'));
$editor = id(new PhabricatorCalendarEventEditor()) $editor = id(new PhabricatorCalendarEventEditor())
->setActor($user) ->setActor($viewer)
->setContentSourceFromRequest($request) ->setContentSourceFromRequest($request)
->setContinueOnNoEffect(true); ->setContinueOnNoEffect(true);
try { try {
$xactions = $editor->applyTransactions($event, $xactions); $xactions = $editor->applyTransactions($event, $xactions);
$response = id(new AphrontRedirectResponse()); $response = id(new AphrontRedirectResponse());
return $response->setURI('/E'.$event->getID()); switch ($next_workflow) {
case 'day':
if (!$uri_query) {
$uri_query = 'month';
}
$year = $start_value->getDateTime()->format('Y');
$month = $start_value->getDateTime()->format('m');
$day = $start_value->getDateTime()->format('d');
$response->setURI(
'/calendar/query/'.$uri_query.'/'.$year.'/'.$month.'/'.$day.'/');
break;
default:
$response->setURI('/E'.$event->getID());
break;
}
return $response;
} catch (PhabricatorApplicationTransactionValidationException $ex) { } catch (PhabricatorApplicationTransactionValidationException $ex) {
$validation_exception = $ex; $validation_exception = $ex;
$error_name = $ex->getShortMessage( $error_name = $ex->getShortMessage(
@ -204,7 +254,7 @@ final class PhabricatorCalendarEventEditController
$all_day_id); $all_day_id);
$start_control = id(new AphrontFormDateControl()) $start_control = id(new AphrontFormDateControl())
->setUser($user) ->setUser($viewer)
->setName('start') ->setName('start')
->setLabel(pht('Start')) ->setLabel(pht('Start'))
->setError($error_start_date) ->setError($error_start_date)
@ -214,7 +264,7 @@ final class PhabricatorCalendarEventEditController
->setEndDateID($end_date_id); ->setEndDateID($end_date_id);
$end_control = id(new AphrontFormDateControl()) $end_control = id(new AphrontFormDateControl())
->setUser($user) ->setUser($viewer)
->setName('end') ->setName('end')
->setLabel(pht('End')) ->setLabel(pht('End'))
->setError($error_end_date) ->setError($error_end_date)
@ -228,13 +278,13 @@ final class PhabricatorCalendarEventEditController
->setValue($description); ->setValue($description);
$view_policies = id(new AphrontFormPolicyControl()) $view_policies = id(new AphrontFormPolicyControl())
->setUser($user) ->setUser($viewer)
->setCapability(PhabricatorPolicyCapability::CAN_VIEW) ->setCapability(PhabricatorPolicyCapability::CAN_VIEW)
->setPolicyObject($event) ->setPolicyObject($event)
->setPolicies($current_policies) ->setPolicies($current_policies)
->setName('viewPolicy'); ->setName('viewPolicy');
$edit_policies = id(new AphrontFormPolicyControl()) $edit_policies = id(new AphrontFormPolicyControl())
->setUser($user) ->setUser($viewer)
->setCapability(PhabricatorPolicyCapability::CAN_EDIT) ->setCapability(PhabricatorPolicyCapability::CAN_EDIT)
->setPolicyObject($event) ->setPolicyObject($event)
->setPolicies($current_policies) ->setPolicies($current_policies)
@ -244,14 +294,14 @@ final class PhabricatorCalendarEventEditController
->setLabel(pht('Subscribers')) ->setLabel(pht('Subscribers'))
->setName('subscribers') ->setName('subscribers')
->setValue($subscribers) ->setValue($subscribers)
->setUser($user) ->setUser($viewer)
->setDatasource(new PhabricatorMetaMTAMailableDatasource()); ->setDatasource(new PhabricatorMetaMTAMailableDatasource());
$invitees = id(new AphrontFormTokenizerControl()) $invitees = id(new AphrontFormTokenizerControl())
->setLabel(pht('Invitees')) ->setLabel(pht('Invitees'))
->setName('invitees') ->setName('invitees')
->setValue($invitees) ->setValue($invitees)
->setUser($user) ->setUser($viewer)
->setDatasource(new PhabricatorMetaMTAMailableDatasource()); ->setDatasource(new PhabricatorMetaMTAMailableDatasource());
if ($this->isCreate()) { if ($this->isCreate()) {
@ -269,7 +319,9 @@ final class PhabricatorCalendarEventEditController
->setValue($icon); ->setValue($icon);
$form = id(new AphrontFormView()) $form = id(new AphrontFormView())
->setUser($user) ->addHiddenInput('next', $next_workflow)
->addHiddenInput('query', $uri_query)
->setUser($viewer)
->appendChild($name) ->appendChild($name)
->appendChild($all_day_checkbox) ->appendChild($all_day_checkbox)
->appendChild($start_control) ->appendChild($start_control)
@ -351,19 +403,19 @@ final class PhabricatorCalendarEventEditController
return $new; return $new;
} }
private function getDefaultTimeValues($user) { private function getDefaultTimeValues($viewer) {
$start = new DateTime('@'.time()); $start = new DateTime('@'.time());
$start->setTimeZone($user->getTimeZone()); $start->setTimeZone($viewer->getTimeZone());
$start->setTime($start->format('H'), 0, 0); $start->setTime($start->format('H'), 0, 0);
$start->modify('+1 hour'); $start->modify('+1 hour');
$end = id(clone $start)->modify('+1 hour'); $end = id(clone $start)->modify('+1 hour');
$start_value = AphrontFormDateControlValue::newFromEpoch( $start_value = AphrontFormDateControlValue::newFromEpoch(
$user, $viewer,
$start->format('U')); $start->format('U'));
$end_value = AphrontFormDateControlValue::newFromEpoch( $end_value = AphrontFormDateControlValue::newFromEpoch(
$user, $viewer,
$end->format('U')); $end->format('U'));
return array($start_value, $end_value); return array($start_value, $end_value);

View file

@ -180,6 +180,21 @@ final class PhabricatorCalendarEventQuery
protected function willFilterPage(array $events) { protected function willFilterPage(array $events) {
$range_start = $this->rangeBegin;
$range_end = $this->rangeEnd;
foreach ($events as $key => $event) {
$event_start = $event->getDateFrom();
$event_end = $event->getDateTo();
if ($range_start && $event_end < $range_start) {
unset($events[$key]);
}
if ($range_end && $event_start > $range_end) {
unset($events[$key]);
}
}
$phids = array(); $phids = array();
foreach ($events as $event) { foreach ($events as $event) {
@ -197,6 +212,8 @@ final class PhabricatorCalendarEventQuery
$event->attachInvitees($event_invitees); $event->attachInvitees($event_invitees);
} }
$events = msort($events, 'getDateFrom');
return $events; return $events;
} }

View file

@ -390,12 +390,13 @@ final class PhabricatorCalendarEventSearchEngine
list($start_year, $start_month, $start_day) = list($start_year, $start_month, $start_day) =
$this->getDisplayYearAndMonthAndDay($query); $this->getDisplayYearAndMonthAndDay($query);
$day_view = new PHUICalendarDayView( $day_view = id(new PHUICalendarDayView(
$this->getDateFrom($query), $this->getDateFrom($query),
$this->getDateTo($query), $this->getDateTo($query),
$start_year, $start_year,
$start_month, $start_month,
$start_day); $start_day))
->setQuery($query->getQueryKey());
$day_view->setUser($viewer); $day_view->setUser($viewer);
@ -408,7 +409,13 @@ final class PhabricatorCalendarEventSearchEngine
$viewer_is_invited = $status->getIsUserInvited($viewer->getPHID()); $viewer_is_invited = $status->getIsUserInvited($viewer->getPHID());
$can_edit = PhabricatorPolicyFilter::hasCapability(
$viewer,
$status,
PhabricatorPolicyCapability::CAN_EDIT);
$event = new AphrontCalendarEventView(); $event = new AphrontCalendarEventView();
$event->setCanEdit($can_edit);
$event->setEventID($status->getID()); $event->setEventID($status->getID());
$event->setEpochRange($status->getDateFrom(), $status->getDateTo()); $event->setEpochRange($status->getDateFrom(), $status->getDateTo());
$event->setIsAllDay($status->getIsAllDay()); $event->setIsAllDay($status->getIsAllDay());

View file

@ -28,18 +28,26 @@ final class PhabricatorCalendarEvent extends PhabricatorCalendarDAO
private $invitees = self::ATTACHABLE; private $invitees = self::ATTACHABLE;
private $appliedViewer; private $appliedViewer;
public static function initializeNewCalendarEvent(PhabricatorUser $actor) { public static function initializeNewCalendarEvent(
PhabricatorUser $actor,
$mode) {
$app = id(new PhabricatorApplicationQuery()) $app = id(new PhabricatorApplicationQuery())
->setViewer($actor) ->setViewer($actor)
->withClasses(array('PhabricatorCalendarApplication')) ->withClasses(array('PhabricatorCalendarApplication'))
->executeOne(); ->executeOne();
if ($mode == 'public') {
$view_policy = PhabricatorPolicies::getMostOpenPolicy();
} else {
$view_policy = $actor->getPHID();
}
return id(new PhabricatorCalendarEvent()) return id(new PhabricatorCalendarEvent())
->setUserPHID($actor->getPHID()) ->setUserPHID($actor->getPHID())
->setIsCancelled(0) ->setIsCancelled(0)
->setIsAllDay(0) ->setIsAllDay(0)
->setIcon(self::DEFAULT_ICON) ->setIcon(self::DEFAULT_ICON)
->setViewPolicy($actor->getPHID()) ->setViewPolicy($view_policy)
->setEditPolicy($actor->getPHID()) ->setEditPolicy($actor->getPHID())
->attachInvitees(array()) ->attachInvitees(array())
->applyViewerTimezone($actor); ->applyViewerTimezone($actor);

View file

@ -12,6 +12,7 @@ final class AphrontCalendarEventView extends AphrontView {
private $uri; private $uri;
private $isAllDay; private $isAllDay;
private $icon; private $icon;
private $canEdit;
public function setURI($uri) { public function setURI($uri) {
$this->uri = $uri; $this->uri = $uri;
@ -97,6 +98,14 @@ final class AphrontCalendarEventView extends AphrontView {
return $this->icon; return $this->icon;
} }
public function setCanEdit($can_edit) {
$this->canEdit = $can_edit;
return $this;
}
public function getCanEdit() {
return $this->canEdit;
}
public function getMultiDay() { public function getMultiDay() {
$nextday = strtotime('12:00 AM Tomorrow', $this->getEpochStart()); $nextday = strtotime('12:00 AM Tomorrow', $this->getEpochStart());

View file

@ -98,7 +98,7 @@ final class PhabricatorConduitLogController
array($call->getMethod(), $client), array($call->getMethod(), $client),
$status, $status,
$call->getError(), $call->getError(),
pht('%d us', number_format($call->getDuration())), pht('%s us', new PhutilNumber($call->getDuration())),
phabricator_datetime($call->getDateCreated(), $viewer), phabricator_datetime($call->getDateCreated(), $viewer),
); );
} }

View file

@ -198,7 +198,7 @@ final class DarkConsoleServicesPlugin extends DarkConsolePlugin {
$summary[] = array( $summary[] = array(
$type, $type,
number_format($counts[$type]), number_format($counts[$type]),
pht('%d us', number_format((int)(1000000 * $totals[$type]))), pht('%s us', new PhutilNumber((int)(1000000 * $totals[$type]))),
sprintf('%.1f%%', 100 * $totals[$type] / $page_total), sprintf('%.1f%%', 100 * $totals[$type] / $page_total),
); );
} }
@ -258,10 +258,12 @@ final class DarkConsoleServicesPlugin extends DarkConsolePlugin {
break; break;
} }
$offset = ($row['begin'] - $data['start']);
$rows[] = array( $rows[] = array(
$row['type'], $row['type'],
pht('+%d ms', number_format(1000 * ($row['begin'] - $data['start']))), pht('+%s ms', new PhutilNumber(1000 * $offset)),
pht('%d us', number_format(1000000 * $row['duration'])), pht('%s us', new PhutilNumber(1000000 * $row['duration'])),
$info, $info,
$analysis, $analysis,
); );

View file

@ -50,7 +50,7 @@ final class PhabricatorDaemonConsoleController
$rows[] = array( $rows[] = array(
$class, $class,
number_format($info['n']), number_format($info['n']),
pht('%d us', number_format((int)($info['duration'] / $info['n']))), pht('%s us', new PhutilNumber((int)($info['duration'] / $info['n']))),
); );
} }

View file

@ -141,7 +141,7 @@ final class PhabricatorWorkerTaskDetailController
$expires); $expires);
if ($task->isArchived()) { if ($task->isArchived()) {
$duration = pht('%d us', number_format($task->getDuration())); $duration = pht('%s us', new PhutilNumber($task->getDuration()));
} else { } else {
$duration = phutil_tag('em', array(), pht('Not Completed')); $duration = phutil_tag('em', array(), pht('Not Completed'));
} }

View file

@ -191,6 +191,7 @@ final class DifferentialChangesetViewController extends DifferentialController {
if ($revision) { if ($revision) {
$query = id(new DifferentialInlineCommentQuery()) $query = id(new DifferentialInlineCommentQuery())
->setViewer($viewer) ->setViewer($viewer)
->needHidden(true)
->withRevisionPHIDs(array($revision->getPHID())); ->withRevisionPHIDs(array($revision->getPHID()));
$inlines = $query->execute(); $inlines = $query->execute();
$inlines = $query->adjustInlinesForChangesets( $inlines = $query->adjustInlinesForChangesets(

View file

@ -42,6 +42,7 @@ final class DifferentialInlineCommentEditController
->setViewer($this->getViewer()) ->setViewer($this->getViewer())
->withIDs(array($id)) ->withIDs(array($id))
->withDeletedDrafts(true) ->withDeletedDrafts(true)
->needHidden(true)
->executeOne(); ->executeOne();
} }
@ -50,6 +51,7 @@ final class DifferentialInlineCommentEditController
->setViewer($this->getViewer()) ->setViewer($this->getViewer())
->withPHIDs(array($phid)) ->withPHIDs(array($phid))
->withDeletedDrafts(true) ->withDeletedDrafts(true)
->needHidden(true)
->executeOne(); ->executeOne();
} }
@ -152,4 +154,38 @@ final class DifferentialInlineCommentEditController
return $this->loadRevision()->getAuthorPHID(); return $this->loadRevision()->getAuthorPHID();
} }
protected function hideComments(array $ids) {
$viewer = $this->getViewer();
$table = new DifferentialHiddenComment();
$conn_w = $table->establishConnection('w');
$sql = array();
foreach ($ids as $id) {
$sql[] = qsprintf(
$conn_w,
'(%s, %d)',
$viewer->getPHID(),
$id);
}
queryfx(
$conn_w,
'INSERT IGNORE INTO %T (userPHID, commentID) VALUES %Q',
$table->getTableName(),
implode(', ', $sql));
}
protected function showComments(array $ids) {
$viewer = $this->getViewer();
$table = new DifferentialHiddenComment();
$conn_w = $table->establishConnection('w');
queryfx(
$conn_w,
'DELETE FROM %T WHERE userPHID = %s AND commentID IN (%Ld)',
$table->getTableName(),
$viewer->getPHID(),
$ids);
}
} }

View file

@ -19,6 +19,7 @@ extends PhabricatorInlineCommentPreviewController {
->withDrafts(true) ->withDrafts(true)
->withAuthorPHIDs(array($viewer->getPHID())) ->withAuthorPHIDs(array($viewer->getPHID()))
->withRevisionPHIDs(array($revision->getPHID())) ->withRevisionPHIDs(array($revision->getPHID()))
->needHidden(true)
->execute(); ->execute();
} }

View file

@ -175,6 +175,7 @@ final class DifferentialRevisionViewController extends DifferentialController {
$query = id(new DifferentialInlineCommentQuery()) $query = id(new DifferentialInlineCommentQuery())
->setViewer($user) ->setViewer($user)
->needHidden(true)
->withRevisionPHIDs(array($revision->getPHID())); ->withRevisionPHIDs(array($revision->getPHID()));
$inlines = $query->execute(); $inlines = $query->execute();
$inlines = $query->adjustInlinesForChangesets( $inlines = $query->adjustInlinesForChangesets(

View file

@ -47,6 +47,11 @@ final class DifferentialGitHubLandingStrategy
DifferentialRevision $revision, DifferentialRevision $revision,
PhabricatorRepository $repository) { PhabricatorRepository $repository) {
// TODO: This temporarily disables this action, because it doesn't work
// and is confusing to users. If you want to use it, comment out this line
// for now and we'll provide real support eventually.
return;
$vcs = $repository->getVersionControlSystem(); $vcs = $repository->getVersionControlSystem();
if ($vcs !== PhabricatorRepositoryType::REPOSITORY_TYPE_GIT) { if ($vcs !== PhabricatorRepositoryType::REPOSITORY_TYPE_GIT) {
return; return;

View file

@ -906,11 +906,10 @@ final class DifferentialChangesetParser {
$shield = $renderer->renderShield( $shield = $renderer->renderShield(
pht('This file was completely deleted.')); pht('This file was completely deleted.'));
} else if ($this->changeset->getAffectedLineCount() > 2500) { } else if ($this->changeset->getAffectedLineCount() > 2500) {
$lines = number_format($this->changeset->getAffectedLineCount());
$shield = $renderer->renderShield( $shield = $renderer->renderShield(
pht( pht(
'This file has a very large number of changes (%s lines).', 'This file has a very large number of changes (%s lines).',
$lines)); new PhutilNumber($this->changeset->getAffectedLineCount())));
} }
} }

View file

@ -15,6 +15,7 @@ final class DifferentialInlineCommentQuery
private $authorPHIDs; private $authorPHIDs;
private $revisionPHIDs; private $revisionPHIDs;
private $deletedDrafts; private $deletedDrafts;
private $needHidden;
public function setViewer(PhabricatorUser $viewer) { public function setViewer(PhabricatorUser $viewer) {
$this->viewer = $viewer; $this->viewer = $viewer;
@ -55,6 +56,11 @@ final class DifferentialInlineCommentQuery
return $this; return $this;
} }
public function needHidden($need) {
$this->needHidden = $need;
return $this;
}
public function execute() { public function execute() {
$table = new DifferentialTransactionComment(); $table = new DifferentialTransactionComment();
$conn_r = $table->establishConnection('r'); $conn_r = $table->establishConnection('r');
@ -68,6 +74,26 @@ final class DifferentialInlineCommentQuery
$comments = $table->loadAllFromArray($data); $comments = $table->loadAllFromArray($data);
if ($this->needHidden) {
$viewer_phid = $this->getViewer()->getPHID();
if ($viewer_phid && $comments) {
$hidden = queryfx_all(
$conn_r,
'SELECT commentID FROM %T WHERE userPHID = %s
AND commentID IN (%Ls)',
id(new DifferentialHiddenComment())->getTableName(),
$viewer_phid,
mpull($comments, 'getID'));
$hidden = array_fuse(ipull($hidden, 'commentID'));
} else {
$hidden = array();
}
foreach ($comments as $inline) {
$inline->attachIsHidden(isset($hidden[$inline->getID()]));
}
}
foreach ($comments as $key => $value) { foreach ($comments as $key => $value) {
$comments[$key] = DifferentialInlineComment::newFromModernComment( $comments[$key] = DifferentialInlineComment::newFromModernComment(
$value); $value);

View file

@ -41,8 +41,10 @@ final class DifferentialChangesetOneUpRenderer
$column_width = 4; $column_width = 4;
$hidden = new PHUIDiffRevealIconView();
$out = array(); $out = array();
foreach ($primitives as $p) { foreach ($primitives as $k => $p) {
$type = $p['type']; $type = $p['type'];
switch ($type) { switch ($type) {
case 'old': case 'old':
@ -51,6 +53,27 @@ final class DifferentialChangesetOneUpRenderer
case 'new-file': case 'new-file':
$is_old = ($type == 'old' || $type == 'old-file'); $is_old = ($type == 'old' || $type == 'old-file');
$o_hidden = array();
$n_hidden = array();
for ($look = $k + 1; isset($primitives[$look]); $look++) {
$next = $primitives[$look];
switch ($next['type']) {
case 'inline':
$comment = $next['comment'];
if ($comment->isHidden()) {
if ($next['right']) {
$n_hidden[] = $comment;
} else {
$o_hidden[] = $comment;
}
}
break;
default:
break 2;
}
}
$cells = array(); $cells = array();
if ($is_old) { if ($is_old) {
if ($p['htype']) { if ($p['htype']) {
@ -68,7 +91,13 @@ final class DifferentialChangesetOneUpRenderer
} else { } else {
$left_id = null; $left_id = null;
} }
$cells[] = phutil_tag('th', array('id' => $left_id), $p['line']);
$line = $p['line'];
if ($o_hidden) {
$line = array($hidden, $line);
}
$cells[] = phutil_tag('th', array('id' => $left_id), $line);
$cells[] = phutil_tag('th', array()); $cells[] = phutil_tag('th', array());
$cells[] = $no_copy; $cells[] = $no_copy;
@ -85,7 +114,13 @@ final class DifferentialChangesetOneUpRenderer
} else { } else {
$left_id = null; $left_id = null;
} }
$cells[] = phutil_tag('th', array('id' => $left_id), $p['oline']);
$oline = $p['oline'];
if ($o_hidden) {
$oline = array($hidden, $oline);
}
$cells[] = phutil_tag('th', array('id' => $left_id), $oline);
} }
if ($type == 'new-file') { if ($type == 'new-file') {
@ -97,8 +132,13 @@ final class DifferentialChangesetOneUpRenderer
} else { } else {
$right_id = null; $right_id = null;
} }
$cells[] = phutil_tag('th', array('id' => $right_id), $p['line']);
$line = $p['line'];
if ($n_hidden) {
$line = array($hidden, $line);
}
$cells[] = phutil_tag('th', array('id' => $right_id), $line);
$cells[] = $no_copy; $cells[] = $no_copy;
$cells[] = phutil_tag('td', array('class' => $class), $p['render']); $cells[] = phutil_tag('td', array('class' => $class), $p['render']);

View file

@ -69,6 +69,8 @@ final class DifferentialChangesetTwoUpRenderer
$depths = $this->getDepths(); $depths = $this->getDepths();
$mask = $this->getMask(); $mask = $this->getMask();
$hidden = new PHUIDiffRevealIconView();
for ($ii = $range_start; $ii < $range_start + $range_len; $ii++) { for ($ii = $range_start; $ii < $range_start + $range_len; $ii++) {
if (empty($mask[$ii])) { if (empty($mask[$ii])) {
// If we aren't going to show this line, we've just entered a gap. // If we aren't going to show this line, we've just entered a gap.
@ -235,6 +237,69 @@ final class DifferentialChangesetTwoUpRenderer
$n_id = null; $n_id = null;
} }
$old_comments = $this->getOldComments();
$new_comments = $this->getNewComments();
$scaffolds = array();
$o_hidden = array();
$n_hidden = array();
if ($o_num && isset($old_comments[$o_num])) {
foreach ($old_comments[$o_num] as $comment) {
$inline = $this->buildInlineComment(
$comment,
$on_right = false);
$scaffold = $this->getRowScaffoldForInline($inline);
if ($comment->isHidden()) {
$o_hidden[] = $comment;
}
if ($n_num && isset($new_comments[$n_num])) {
foreach ($new_comments[$n_num] as $key => $new_comment) {
if ($comment->isCompatible($new_comment)) {
$companion = $this->buildInlineComment(
$new_comment,
$on_right = true);
if ($new_comment->isHidden()) {
$n_hidden = $new_comment;
}
$scaffold->addInlineView($companion);
unset($new_comments[$n_num][$key]);
break;
}
}
}
$scaffolds[] = $scaffold;
}
}
if ($n_num && isset($new_comments[$n_num])) {
foreach ($new_comments[$n_num] as $comment) {
$inline = $this->buildInlineComment(
$comment,
$on_right = true);
if ($comment->isHidden()) {
$n_hidden[] = $comment;
}
$scaffolds[] = $this->getRowScaffoldForInline($inline);
}
}
if ($o_hidden) {
$o_num = array($hidden, $o_num);
}
if ($n_hidden) {
$n_num = array($hidden, $n_num);
}
// NOTE: This is a unicode zero-width space, which we use as a hint when // NOTE: This is a unicode zero-width space, which we use as a hint when
// intercepting 'copy' events to make sure sensible text ends up on the // intercepting 'copy' events to make sure sensible text ends up on the
// clipboard. See the 'phabricator-oncopy' behavior. // clipboard. See the 'phabricator-oncopy' behavior.
@ -259,40 +324,8 @@ final class DifferentialChangesetTwoUpRenderer
$html[] = $context_not_available; $html[] = $context_not_available;
} }
$old_comments = $this->getOldComments(); foreach ($scaffolds as $scaffold) {
$new_comments = $this->getNewComments(); $html[] = $scaffold;
if ($o_num && isset($old_comments[$o_num])) {
foreach ($old_comments[$o_num] as $comment) {
$inline = $this->buildInlineComment(
$comment,
$on_right = false);
$scaffold = $this->getRowScaffoldForInline($inline);
if ($n_num && isset($new_comments[$n_num])) {
foreach ($new_comments[$n_num] as $key => $new_comment) {
if ($comment->isCompatible($new_comment)) {
$companion = $this->buildInlineComment(
$new_comment,
$on_right = true);
$scaffold->addInlineView($companion);
unset($new_comments[$n_num][$key]);
break;
}
}
}
$html[] = $scaffold;
}
}
if ($n_num && isset($new_comments[$n_num])) {
foreach ($new_comments[$n_num] as $comment) {
$inline = $this->buildInlineComment(
$comment,
$on_right = true);
$html[] = $this->getRowScaffoldForInline($inline);
}
} }
} }

View file

@ -0,0 +1,24 @@
<?php
final class DifferentialHiddenComment
extends DifferentialDAO {
protected $userPHID;
protected $commentID;
protected function getConfiguration() {
return array(
self::CONFIG_TIMESTAMPS => false,
self::CONFIG_KEY_SCHEMA => array(
'key_user' => array(
'columns' => array('userPHID', 'commentID'),
'unique' => true,
),
'key_comment' => array(
'columns' => array('commentID'),
),
),
) + parent::getConfiguration();
}
}

View file

@ -24,6 +24,7 @@ final class DifferentialInlineComment
->setViewPolicy('public') ->setViewPolicy('public')
->setEditPolicy($this->getAuthorPHID()) ->setEditPolicy($this->getAuthorPHID())
->setContentSource($content_source) ->setContentSource($content_source)
->attachIsHidden(false)
->setCommentVersion(1); ->setCommentVersion(1);
return $this->proxy; return $this->proxy;
@ -49,6 +50,20 @@ final class DifferentialInlineComment
return $this; return $this;
} }
public function supportsHiding() {
if ($this->getSyntheticAuthor()) {
return false;
}
return true;
}
public function isHidden() {
if (!$this->supportsHiding()) {
return false;
}
return $this->proxy->getIsHidden();
}
public function getID() { public function getID() {
return $this->proxy->getID(); return $this->proxy->getID();
} }

View file

@ -522,6 +522,7 @@ final class DifferentialRevision extends DifferentialDAO
} }
$query = id(new DifferentialInlineCommentQuery()) $query = id(new DifferentialInlineCommentQuery())
->needHidden(true)
->setViewer($viewer); ->setViewer($viewer);
// NOTE: This is a bit sketchy: this method adjusts the inlines as a // NOTE: This is a bit sketchy: this method adjusts the inlines as a

View file

@ -13,6 +13,7 @@ final class DifferentialTransactionComment
protected $replyToCommentPHID; protected $replyToCommentPHID;
private $replyToComment = self::ATTACHABLE; private $replyToComment = self::ATTACHABLE;
private $isHidden = self::ATTACHABLE;
public function getApplicationTransactionObject() { public function getApplicationTransactionObject() {
return new DifferentialTransaction(); return new DifferentialTransaction();
@ -99,4 +100,13 @@ final class DifferentialTransactionComment
return $inline_groups; return $inline_groups;
} }
public function getIsHidden() {
return $this->assertAttached($this->isHidden);
}
public function attachIsHidden($hidden) {
$this->isHidden = $hidden;
return $this;
}
} }

View file

@ -232,8 +232,9 @@ final class DifferentialChangesetListView extends AphrontView {
if ($this->inlineURI) { if ($this->inlineURI) {
Javelin::initBehavior('differential-edit-inline-comments', array( Javelin::initBehavior('differential-edit-inline-comments', array(
'uri' => $this->inlineURI, 'uri' => $this->inlineURI,
'stage' => 'differential-review-stage', 'stage' => 'differential-review-stage',
'revealIcon' => hsprintf('%s', new PHUIDiffRevealIconView()),
)); ));
} }

View file

@ -101,6 +101,7 @@ final class PhabricatorDiffusionApplication extends PhabricatorApplication {
'(?P<serve>serve)/' => 'DiffusionRepositoryEditHostingController', '(?P<serve>serve)/' => 'DiffusionRepositoryEditHostingController',
'update/' => 'DiffusionRepositoryEditUpdateController', 'update/' => 'DiffusionRepositoryEditUpdateController',
'symbol/' => 'DiffusionRepositorySymbolsController', 'symbol/' => 'DiffusionRepositorySymbolsController',
'staging/' => 'DiffusionRepositoryEditStagingController',
), ),
'pathtree/(?P<dblob>.*)' => 'DiffusionPathTreeController', 'pathtree/(?P<dblob>.*)' => 'DiffusionPathTreeController',
'mirror/' => array( 'mirror/' => array(

View file

@ -20,6 +20,22 @@ final class PhabricatorDiffusionConfigOptions
} }
public function getOptions() { public function getOptions() {
$custom_field_type = 'custom:PhabricatorCustomFieldConfigOptionType';
$fields = array(
new PhabricatorCommitRepositoryField(),
new PhabricatorCommitBranchesField(),
new PhabricatorCommitTagsField(),
new PhabricatorCommitMergedCommitsField(),
);
$default_fields = array();
foreach ($fields as $field) {
$default_fields[$field->getFieldKey()] = array(
'disabled' => $field->shouldDisableByDefault(),
);
}
return array( return array(
$this->newOption( $this->newOption(
'metamta.diffusion.subject-prefix', 'metamta.diffusion.subject-prefix',
@ -124,6 +140,12 @@ final class PhabricatorDiffusionConfigOptions
'from web traffic (for example, if you use different SSH and '. 'from web traffic (for example, if you use different SSH and '.
'web load balancers), you can set the SSH hostname here. This '. 'web load balancers), you can set the SSH hostname here. This '.
'is an advanced option.')), 'is an advanced option.')),
$this->newOption('diffusion.fields', $custom_field_type, $default_fields)
->setCustomData(
id(new PhabricatorRepositoryCommit())
->getCustomFieldBaseClass())
->setDescription(
pht('Select and reorder Diffusion fields.')),
); );
} }

View file

@ -240,10 +240,10 @@ final class DiffusionCommitController extends DiffusionController {
$change_panel = new PHUIObjectBoxView(); $change_panel = new PHUIObjectBoxView();
$header = new PHUIHeaderView(); $header = new PHUIHeaderView();
$header->setHeader(pht('Changes (%d', number_format($count))); $header->setHeader(pht('Changes (%s)', new PhutilNumber($count)));
$change_panel->setID('toc'); $change_panel->setID('toc');
if ($count > self::CHANGES_LIMIT && !$show_all_details) {
if ($count > self::CHANGES_LIMIT && !$show_all_details) {
$icon = id(new PHUIIconView()) $icon = id(new PHUIIconView())
->setIconFont('fa-files-o'); ->setIconFont('fa-files-o');

View file

@ -30,6 +30,7 @@ final class DiffusionRepositoryEditMainController
$has_branches = ($is_git || $is_hg); $has_branches = ($is_git || $is_hg);
$has_local = $repository->usesLocalWorkingCopy(); $has_local = $repository->usesLocalWorkingCopy();
$supports_staging = $repository->supportsStaging();
$crumbs = $this->buildApplicationCrumbs($is_main = true); $crumbs = $this->buildApplicationCrumbs($is_main = true);
@ -92,6 +93,13 @@ final class DiffusionRepositoryEditMainController
$this->buildStorageActions($repository)); $this->buildStorageActions($repository));
} }
$staging_properties = null;
if ($supports_staging) {
$staging_properties = $this->buildStagingProperties(
$repository,
$this->buildStagingActions($repository));
}
$actions_properties = $this->buildActionsProperties( $actions_properties = $this->buildActionsProperties(
$repository, $repository,
$this->buildActionsActions($repository)); $this->buildActionsActions($repository));
@ -157,6 +165,12 @@ final class DiffusionRepositoryEditMainController
->addPropertyList($storage_properties); ->addPropertyList($storage_properties);
} }
if ($staging_properties) {
$boxes[] = id(new PHUIObjectBoxView())
->setHeaderText(pht('Staging'))
->addPropertyList($staging_properties);
}
$boxes[] = id(new PHUIObjectBoxView()) $boxes[] = id(new PHUIObjectBoxView())
->setHeaderText(pht('Text Encoding')) ->setHeaderText(pht('Text Encoding'))
->addPropertyList($encoding_properties); ->addPropertyList($encoding_properties);
@ -609,6 +623,45 @@ final class DiffusionRepositoryEditMainController
return $view; return $view;
} }
private function buildStagingActions(PhabricatorRepository $repository) {
$viewer = $this->getViewer();
$view = id(new PhabricatorActionListView())
->setObjectURI($this->getRequest()->getRequestURI())
->setUser($viewer);
$edit = id(new PhabricatorActionView())
->setIcon('fa-pencil')
->setName(pht('Edit Staging'))
->setHref(
$this->getRepositoryControllerURI($repository, 'edit/staging/'));
$view->addAction($edit);
return $view;
}
private function buildStagingProperties(
PhabricatorRepository $repository,
PhabricatorActionListView $actions) {
$viewer = $this->getViewer();
$view = id(new PHUIPropertyListView())
->setUser($viewer)
->setActionList($actions);
$staging_uri = $repository->getStagingURI();
if (!$staging_uri) {
$staging_uri = phutil_tag('em', array(), pht('No Staging Area'));
}
$view->addProperty(
pht('Staging Area'),
$staging_uri);
return $view;
}
private function buildHostingActions(PhabricatorRepository $repository) { private function buildHostingActions(PhabricatorRepository $repository) {
$user = $this->getRequest()->getUser(); $user = $this->getRequest()->getUser();

View file

@ -0,0 +1,92 @@
<?php
final class DiffusionRepositoryEditStagingController
extends DiffusionRepositoryEditController {
protected function processDiffusionRequest(AphrontRequest $request) {
$user = $request->getUser();
$drequest = $this->diffusionRequest;
$repository = $drequest->getRepository();
$repository = id(new PhabricatorRepositoryQuery())
->setViewer($user)
->requireCapabilities(
array(
PhabricatorPolicyCapability::CAN_VIEW,
PhabricatorPolicyCapability::CAN_EDIT,
))
->withIDs(array($repository->getID()))
->executeOne();
if (!$repository) {
return new Aphront404Response();
}
if (!$repository->supportsStaging()) {
return new Aphront404Response();
}
$edit_uri = $this->getRepositoryControllerURI($repository, 'edit/');
$v_area = $repository->getHumanReadableDetail('staging-uri');
if ($request->isFormPost()) {
$v_area = $request->getStr('area');
$xactions = array();
$template = id(new PhabricatorRepositoryTransaction());
$type_encoding = PhabricatorRepositoryTransaction::TYPE_STAGING_URI;
$xactions[] = id(clone $template)
->setTransactionType($type_encoding)
->setNewValue($v_area);
id(new PhabricatorRepositoryEditor())
->setContinueOnNoEffect(true)
->setContentSourceFromRequest($request)
->setActor($user)
->applyTransactions($repository, $xactions);
return id(new AphrontRedirectResponse())->setURI($edit_uri);
}
$crumbs = $this->buildApplicationCrumbs();
$crumbs->addTextCrumb(pht('Edit Staging'));
$title = pht('Edit %s', $repository->getName());
$form = id(new AphrontFormView())
->setUser($user)
->appendRemarkupInstructions(
pht(
"To make it easier to run integration tests and builds on code ".
"under review, you can configure a **Staging Area**. When `arc` ".
"creates a diff, it will push a copy of the changes to the ".
"configured staging area with a corresponding tag.".
"\n\n".
"IMPORTANT: This feature is new, experimental, and not supported. ".
"Use it at your own risk."))
->appendChild(
id(new AphrontFormTextControl())
->setLabel(pht('Staging Area URI'))
->setName('area')
->setValue($v_area))
->appendChild(
id(new AphrontFormSubmitControl())
->setValue(pht('Save'))
->addCancelButton($edit_uri));
$object_box = id(new PHUIObjectBoxView())
->setHeaderText($title)
->setForm($form);
return $this->buildApplicationPage(
array(
$crumbs,
$object_box,
),
array(
'title' => $title,
));
}
}

View file

@ -1,39 +0,0 @@
<?php
final class DiffusionArcanistProjectDatasource
extends PhabricatorTypeaheadDatasource {
public function isBrowsable() {
// TODO: We should probably make this browsable, or maybe remove it.
return false;
}
public function getBrowseTitle() {
return pht('Browse Arcanist Projects');
}
public function getPlaceholderText() {
return pht('Type an arcanist project name...');
}
public function getDatasourceApplicationClass() {
return 'PhabricatorDiffusionApplication';
}
public function loadResults() {
$viewer = $this->getViewer();
$raw_query = $this->getRawQuery();
$results = array();
$arcprojs = id(new PhabricatorRepositoryArcanistProject())->loadAll();
foreach ($arcprojs as $proj) {
$results[] = id(new PhabricatorTypeaheadResult())
->setName($proj->getName())
->setPHID($proj->getPHID());
}
return $results;
}
}

View file

@ -42,7 +42,6 @@ abstract class HeraldAdapter {
const FIELD_APPLICATION_EMAIL = 'applicaton-email'; const FIELD_APPLICATION_EMAIL = 'applicaton-email';
const FIELD_TASK_PRIORITY = 'taskpriority'; const FIELD_TASK_PRIORITY = 'taskpriority';
const FIELD_TASK_STATUS = 'taskstatus'; const FIELD_TASK_STATUS = 'taskstatus';
const FIELD_ARCANIST_PROJECT = 'arcanist-project';
const FIELD_PUSHER_IS_COMMITTER = 'pusher-is-committer'; const FIELD_PUSHER_IS_COMMITTER = 'pusher-is-committer';
const FIELD_PATH = 'path'; const FIELD_PATH = 'path';
@ -100,7 +99,6 @@ abstract class HeraldAdapter {
const VALUE_BUILD_PLAN = 'buildplan'; const VALUE_BUILD_PLAN = 'buildplan';
const VALUE_TASK_PRIORITY = 'taskpriority'; const VALUE_TASK_PRIORITY = 'taskpriority';
const VALUE_TASK_STATUS = 'taskstatus'; const VALUE_TASK_STATUS = 'taskstatus';
const VALUE_ARCANIST_PROJECT = 'arcanistprojects';
const VALUE_LEGAL_DOCUMENTS = 'legaldocuments'; const VALUE_LEGAL_DOCUMENTS = 'legaldocuments';
const VALUE_APPLICATION_EMAIL = 'applicationemail'; const VALUE_APPLICATION_EMAIL = 'applicationemail';
@ -385,7 +383,6 @@ abstract class HeraldAdapter {
self::FIELD_APPLICATION_EMAIL => pht('Receiving email address'), self::FIELD_APPLICATION_EMAIL => pht('Receiving email address'),
self::FIELD_TASK_PRIORITY => pht('Task priority'), self::FIELD_TASK_PRIORITY => pht('Task priority'),
self::FIELD_TASK_STATUS => pht('Task status'), self::FIELD_TASK_STATUS => pht('Task status'),
self::FIELD_ARCANIST_PROJECT => pht('Arcanist Project'),
self::FIELD_PUSHER_IS_COMMITTER => pht('Pusher same as committer'), self::FIELD_PUSHER_IS_COMMITTER => pht('Pusher same as committer'),
self::FIELD_PATH => pht('Path'), self::FIELD_PATH => pht('Path'),
) + $this->getCustomFieldNameMap(); ) + $this->getCustomFieldNameMap();
@ -441,7 +438,6 @@ abstract class HeraldAdapter {
case self::FIELD_PUSHER: case self::FIELD_PUSHER:
case self::FIELD_TASK_PRIORITY: case self::FIELD_TASK_PRIORITY:
case self::FIELD_TASK_STATUS: case self::FIELD_TASK_STATUS:
case self::FIELD_ARCANIST_PROJECT:
return array( return array(
self::CONDITION_IS_ANY, self::CONDITION_IS_ANY,
self::CONDITION_IS_NOT_ANY, self::CONDITION_IS_NOT_ANY,
@ -946,8 +942,6 @@ abstract class HeraldAdapter {
return self::VALUE_TASK_PRIORITY; return self::VALUE_TASK_PRIORITY;
case self::FIELD_TASK_STATUS: case self::FIELD_TASK_STATUS:
return self::VALUE_TASK_STATUS; return self::VALUE_TASK_STATUS;
case self::FIELD_ARCANIST_PROJECT:
return self::VALUE_ARCANIST_PROJECT;
default: default:
return self::VALUE_USER; return self::VALUE_USER;
} }
@ -1203,7 +1197,15 @@ abstract class HeraldAdapter {
$rule_global = HeraldRuleTypeConfig::RULE_TYPE_GLOBAL; $rule_global = HeraldRuleTypeConfig::RULE_TYPE_GLOBAL;
$action_type = $action->getAction(); $action_type = $action->getAction();
$action_name = idx($this->getActionNameMap($rule_global), $action_type);
$default = $this->isHeraldCustomKey($action_type)
? pht('(Unknown Custom Action "%s") equals', $action_type)
: pht('(Unknown Action "%s") equals', $action_type);
$action_name = idx(
$this->getActionNameMap($rule_global),
$action_type,
$default);
$target = $this->renderActionTargetAsText($action, $handles); $target = $this->renderActionTargetAsText($action, $handles);
@ -1525,7 +1527,9 @@ abstract class HeraldAdapter {
$supported = $this->getActions($rule_type); $supported = $this->getActions($rule_type);
$supported = array_fuse($supported); $supported = array_fuse($supported);
if (empty($supported[$action])) { if (empty($supported[$action])) {
throw new Exception( return new HeraldApplyTranscript(
$effect,
false,
pht( pht(
'Adapter "%s" does not support action "%s" for rule type "%s".', 'Adapter "%s" does not support action "%s" for rule type "%s".',
get_class($this), get_class($this),
@ -1548,7 +1552,9 @@ abstract class HeraldAdapter {
$result = $this->handleCustomHeraldEffect($effect); $result = $this->handleCustomHeraldEffect($effect);
if (!$result) { if (!$result) {
throw new Exception( return new HeraldApplyTranscript(
$effect,
false,
pht( pht(
'No custom action exists to handle rule action "%s".', 'No custom action exists to handle rule action "%s".',
$action)); $action));

View file

@ -154,7 +154,10 @@ final class HeraldDifferentialDiffAdapter extends HeraldDifferentialAdapter {
pht('Blocked diff.')); pht('Blocked diff.'));
break; break;
default: default:
throw new Exception(pht('No rules to handle action "%s"!', $action)); $result[] = new HeraldApplyTranscript(
$effect,
false,
pht('No rules to handle action "%s"!', $action));
} }
} }

View file

@ -80,7 +80,6 @@ final class HeraldDifferentialRevisionAdapter
self::FIELD_AFFECTED_PACKAGE, self::FIELD_AFFECTED_PACKAGE,
self::FIELD_AFFECTED_PACKAGE_OWNER, self::FIELD_AFFECTED_PACKAGE_OWNER,
self::FIELD_IS_NEW_OBJECT, self::FIELD_IS_NEW_OBJECT,
self::FIELD_ARCANIST_PROJECT,
), ),
parent::getFields()); parent::getFields());
} }
@ -259,8 +258,6 @@ final class HeraldDifferentialRevisionAdapter
$packages = $this->loadAffectedPackages(); $packages = $this->loadAffectedPackages();
return PhabricatorOwnersOwner::loadAffiliatedUserPHIDs( return PhabricatorOwnersOwner::loadAffiliatedUserPHIDs(
mpull($packages, 'getID')); mpull($packages, 'getID'));
case self::FIELD_ARCANIST_PROJECT:
return $this->revision->getArcanistProjectPHID();
} }
return parent::getHeraldField($field); return parent::getHeraldField($field);

View file

@ -320,7 +320,7 @@ final class HeraldRuleController extends HeraldController {
try { try {
$adapter->willSaveAction($rule, $obj); $adapter->willSaveAction($rule, $obj);
} catch (HeraldInvalidActionException $ex) { } catch (HeraldInvalidActionException $ex) {
$errors[] = $ex; $errors[] = $ex->getMessage();
} }
$actions[] = $obj; $actions[] = $obj;
@ -354,7 +354,6 @@ final class HeraldRuleController extends HeraldController {
if ($rule->getConditions()) { if ($rule->getConditions()) {
$serial_conditions = array(); $serial_conditions = array();
foreach ($rule->getConditions() as $condition) { foreach ($rule->getConditions() as $condition) {
$value = $condition->getValue(); $value = $condition->getValue();
switch ($condition->getFieldName()) { switch ($condition->getFieldName()) {
case HeraldAdapter::FIELD_TASK_PRIORITY: case HeraldAdapter::FIELD_TASK_PRIORITY:
@ -394,10 +393,10 @@ final class HeraldRuleController extends HeraldController {
$serial_actions = array( $serial_actions = array(
array('default', ''), array('default', ''),
); );
if ($rule->getActions()) { if ($rule->getActions()) {
$serial_actions = array(); $serial_actions = array();
foreach ($rule->getActions() as $action) { foreach ($rule->getActions() as $action) {
switch ($action->getAction()) { switch ($action->getAction()) {
case HeraldAdapter::ACTION_FLAG: case HeraldAdapter::ACTION_FLAG:
case HeraldAdapter::ACTION_BLOCK: case HeraldAdapter::ACTION_BLOCK:
@ -438,21 +437,39 @@ final class HeraldRuleController extends HeraldController {
// names of, so that saving a rule without touching anything doesn't change // names of, so that saving a rule without touching anything doesn't change
// it. // it.
foreach ($rule->getConditions() as $condition) { foreach ($rule->getConditions() as $condition) {
if (empty($field_map[$condition->getFieldName()])) { $field_name = $condition->getFieldName();
$field_map[$condition->getFieldName()] = pht('<Unknown Field>');
if (empty($field_map[$field_name])) {
$field_map[$field_name] = pht('<Unknown Field "%s">', $field_name);
} }
} }
$actions = $adapter->getActions($rule->getRuleType()); $actions = $adapter->getActions($rule->getRuleType());
$action_map = array_select_keys($all_actions, $actions); $action_map = array_select_keys($all_actions, $actions);
// Populate any actions which exist in the rule but which we don't know the
// names of, so that saving a rule without touching anything doesn't change
// it.
foreach ($rule->getActions() as $action) {
$action_name = $action->getAction();
if (empty($action_map[$action_name])) {
$action_map[$action_name] = pht('<Unknown Action "%s">', $action_name);
}
}
$config_info = array(); $config_info = array();
$config_info['fields'] = $field_map; $config_info['fields'] = $field_map;
$config_info['conditions'] = $all_conditions; $config_info['conditions'] = $all_conditions;
$config_info['actions'] = $action_map; $config_info['actions'] = $action_map;
foreach ($config_info['fields'] as $field => $name) { foreach ($config_info['fields'] as $field => $name) {
$field_conditions = $adapter->getConditionsForField($field); try {
$field_conditions = $adapter->getConditionsForField($field);
} catch (Exception $ex) {
$field_conditions = array(HeraldAdapter::CONDITION_UNCONDITIONALLY);
}
$config_info['conditionMap'][$field] = $field_conditions; $config_info['conditionMap'][$field] = $field_conditions;
} }
@ -468,9 +485,15 @@ final class HeraldRuleController extends HeraldController {
$config_info['rule_type'] = $rule->getRuleType(); $config_info['rule_type'] = $rule->getRuleType();
foreach ($config_info['actions'] as $action => $name) { foreach ($config_info['actions'] as $action => $name) {
$config_info['targets'][$action] = $adapter->getValueTypeForAction( try {
$action, $action_value = $adapter->getValueTypeForAction(
$rule->getRuleType()); $action,
$rule->getRuleType());
} catch (Exception $ex) {
$action_value = array(HeraldAdapter::VALUE_NONE);
}
$config_info['targets'][$action] = $action_value;
} }
$changeflag_options = $changeflag_options =
@ -603,7 +626,6 @@ final class HeraldRuleController extends HeraldController {
'taskpriority' => new ManiphestTaskPriorityDatasource(), 'taskpriority' => new ManiphestTaskPriorityDatasource(),
'taskstatus' => new ManiphestTaskStatusDatasource(), 'taskstatus' => new ManiphestTaskStatusDatasource(),
'buildplan' => new HarbormasterBuildPlanDatasource(), 'buildplan' => new HarbormasterBuildPlanDatasource(),
'arcanistprojects' => new DiffusionArcanistProjectDatasource(),
'package' => new PhabricatorOwnersPackageDatasource(), 'package' => new PhabricatorOwnersPackageDatasource(),
'project' => new PhabricatorProjectDatasource(), 'project' => new PhabricatorProjectDatasource(),
'user' => new PhabricatorPeopleDatasource(), 'user' => new PhabricatorPeopleDatasource(),

View file

@ -380,7 +380,10 @@ final class HeraldTranscriptController extends HeraldController {
$item->setState(PHUIObjectItemView::STATE_FAIL); $item->setState(PHUIObjectItemView::STATE_FAIL);
} }
$rule = idx($action_names, $apply_xscript->getAction(), pht('Unknown')); $rule = idx(
$action_names,
$apply_xscript->getAction(),
pht('Unknown Action "%s"', $apply_xscript->getAction()));
$item->setHeader(pht('%s: %s', $rule, $target)); $item->setHeader(pht('%s: %s', $rule, $target));
$item->addAttribute($apply_xscript->getReason()); $item->addAttribute($apply_xscript->getReason());

View file

@ -272,6 +272,16 @@ final class HeraldEngine {
$result = false; $result = false;
} else { } else {
foreach ($conditions as $condition) { foreach ($conditions as $condition) {
try {
$object->getHeraldField($condition->getFieldName());
} catch (Exception $ex) {
$reason = pht(
'Field "%s" does not exist!',
$condition->getFieldName());
$result = false;
break;
}
$match = $this->doesConditionMatch($rule, $condition, $object); $match = $this->doesConditionMatch($rule, $condition, $object);
if (!$all && $match) { if (!$all && $match) {

View file

@ -125,7 +125,7 @@ final class HeraldTranscriptSearchEngine
} }
$item->addAttribute($handles[$xscript->getObjectPHID()]->renderLink()); $item->addAttribute($handles[$xscript->getObjectPHID()]->renderLink());
$item->addAttribute( $item->addAttribute(
pht('%d ms', number_format((int)(1000 * $xscript->getDuration())))); pht('%s ms', new PhutilNumber((int)(1000 * $xscript->getDuration()))));
$item->addIcon( $item->addIcon(
'none', 'none',
phabricator_datetime($xscript->getTime(), $viewer)); phabricator_datetime($xscript->getTime(), $viewer));

View file

@ -42,12 +42,11 @@ final class PhabricatorOwnersApplication extends PhabricatorApplication {
public function getRoutes() { public function getRoutes() {
return array( return array(
'/owners/' => array( '/owners/' => array(
'' => 'PhabricatorOwnersListController', '(?:query/(?P<queryKey>[^/]+)/)?' => 'PhabricatorOwnersListController',
'view/(?P<view>[^/]+)/' => 'PhabricatorOwnersListController',
'edit/(?P<id>[1-9]\d*)/' => 'PhabricatorOwnersEditController', 'edit/(?P<id>[1-9]\d*)/' => 'PhabricatorOwnersEditController',
'new/' => 'PhabricatorOwnersEditController', 'new/' => 'PhabricatorOwnersEditController',
'package/(?P<id>[1-9]\d*)/' => 'PhabricatorOwnersDetailController', 'package/(?P<id>[1-9]\d*)/' => 'PhabricatorOwnersDetailController',
'delete/(?P<id>[1-9]\d*)/' => 'PhabricatorOwnersDeleteController', 'paths/(?P<id>[1-9]\d*)/' => 'PhabricatorOwnersPathsController',
), ),
); );
} }

View file

@ -1,70 +1,3 @@
<?php <?php
abstract class PhabricatorOwnersController extends PhabricatorController { abstract class PhabricatorOwnersController extends PhabricatorController {}
private $filter;
private function getSideNavFilter() {
return $this->filter;
}
protected function setSideNavFilter($filter) {
$this->filter = $filter;
return $this;
}
public function buildSideNavView() {
$nav = new AphrontSideNavFilterView();
$base_uri = new PhutilURI('/owners/');
$nav->setBaseURI($base_uri);
$nav->addLabel(pht('Packages'));
$this->getExtraPackageViews($nav);
$nav->addFilter('view/owned', pht('Owned'));
$nav->addFilter('view/projects', pht('Projects'));
$nav->addFilter('view/all', pht('All'));
$nav->selectFilter($this->getSideNavFilter(), 'view/owned');
$filter = $nav->getSelectedFilter();
switch ($filter) {
case 'view/owned':
$title = pht('Owned Packages');
break;
case 'view/all':
$title = pht('All Packages');
break;
case 'view/projects':
$title = pht('Projects');
break;
case 'new':
$title = pht('New Package');
break;
default:
$title = pht('Package');
break;
}
return $nav;
}
protected function buildApplicationCrumbs() {
$crumbs = parent::buildApplicationCrumbs();
$crumbs->addAction(
id(new PHUIListItemView())
->setName(pht('Create Package'))
->setHref('/owners/new/')
->setIcon('fa-plus-square'));
return $crumbs;
}
public function buildApplicationMenu() {
return $this->buildSideNavView()->getMenu();
}
protected function getExtraPackageViews(AphrontSideNavFilterView $view) {
return;
}
}

View file

@ -1,44 +0,0 @@
<?php
final class PhabricatorOwnersDeleteController
extends PhabricatorOwnersController {
private $id;
public function willProcessRequest(array $data) {
$this->id = $data['id'];
}
public function processRequest() {
$request = $this->getRequest();
$user = $request->getUser();
$package = id(new PhabricatorOwnersPackage())->load($this->id);
if (!$package) {
return new Aphront404Response();
}
if ($request->isDialogFormPost()) {
id(new PhabricatorOwnersPackageEditor())
->setActor($user)
->setPackage($package)
->delete();
return id(new AphrontRedirectResponse())->setURI('/owners/');
}
$text = pht(
'Are you sure you want to delete the "%s" package? This '.
'operation can not be undone.',
$package->getName());
$dialog = id(new AphrontDialogView())
->setUser($user)
->setTitle(pht('Really delete this package?'))
->appendChild(phutil_tag('p', array(), $text))
->addSubmitButton(pht('Delete'))
->addCancelButton('/owners/package/'.$package->getID().'/')
->setSubmitURI($request->getRequestURI());
return id(new AphrontDialogResponse())->setDialog($dialog);
}
}

View file

@ -3,25 +3,22 @@
final class PhabricatorOwnersDetailController final class PhabricatorOwnersDetailController
extends PhabricatorOwnersController { extends PhabricatorOwnersController {
private $id; public function shouldAllowPublic() {
private $package; return true;
public function willProcessRequest(array $data) {
$this->id = $data['id'];
} }
public function processRequest() { public function handleRequest(AphrontRequest $request) {
$request = $this->getRequest(); $viewer = $this->getViewer();
$user = $request->getUser();
$package = id(new PhabricatorOwnersPackage())->load($this->id); $package = id(new PhabricatorOwnersPackageQuery())
->setViewer($viewer)
->withIDs(array($request->getURIData('id')))
->executeOne();
if (!$package) { if (!$package) {
return new Aphront404Response(); return new Aphront404Response();
} }
$this->package = $package;
$paths = $package->loadPaths(); $paths = $package->loadPaths();
$owners = $package->loadOwners();
$repository_phids = array(); $repository_phids = array();
foreach ($paths as $path) { foreach ($paths as $path) {
@ -30,7 +27,7 @@ final class PhabricatorOwnersDetailController
if ($repository_phids) { if ($repository_phids) {
$repositories = id(new PhabricatorRepositoryQuery()) $repositories = id(new PhabricatorRepositoryQuery())
->setViewer($user) ->setViewer($viewer)
->withPHIDs(array_keys($repository_phids)) ->withPHIDs(array_keys($repository_phids))
->execute(); ->execute();
$repositories = mpull($repositories, null, 'getPHID'); $repositories = mpull($repositories, null, 'getPHID');
@ -38,101 +35,18 @@ final class PhabricatorOwnersDetailController
$repositories = array(); $repositories = array();
} }
$phids = array(); $actions = $this->buildPackageActionView($package);
foreach ($owners as $owner) { $properties = $this->buildPackagePropertyView($package);
$phids[$owner->getUserPHID()] = true; $properties->setActionList($actions);
}
$phids = array_keys($phids);
$handles = $this->loadViewerHandles($phids); $header = id(new PHUIHeaderView())
->setUser($viewer)
->setHeader($package->getName())
->setPolicyObject($package);
$rows = array(); $panel = id(new PHUIObjectBoxView())
->setHeader($header)
$rows[] = array(pht('Name'), $package->getName()); ->addPropertyList($properties);
$rows[] = array(pht('Description'), $package->getDescription());
$primary_owner = null;
$primary_phid = $package->getPrimaryOwnerPHID();
if ($primary_phid && isset($handles[$primary_phid])) {
$primary_owner = phutil_tag(
'strong',
array(),
$handles[$primary_phid]->renderLink());
}
$rows[] = array(pht('Primary Owner'), $primary_owner);
$owner_links = array();
foreach ($owners as $owner) {
$owner_links[] = $handles[$owner->getUserPHID()]->renderLink();
}
$owner_links = phutil_implode_html(phutil_tag('br'), $owner_links);
$rows[] = array(pht('Owners'), $owner_links);
$rows[] = array(
pht('Auditing'),
$package->getAuditingEnabled() ?
pht('Enabled') :
pht('Disabled'),
);
$path_links = array();
foreach ($paths as $path) {
$repo = idx($repositories, $path->getRepositoryPHID());
if (!$repo) {
continue;
}
$href = DiffusionRequest::generateDiffusionURI(
array(
'callsign' => $repo->getCallsign(),
'branch' => $repo->getDefaultBranch(),
'path' => $path->getPath(),
'action' => 'browse',
));
$repo_name = phutil_tag('strong', array(), $repo->getName());
$path_link = phutil_tag(
'a',
array(
'href' => (string)$href,
),
$path->getPath());
$path_links[] = hsprintf(
'%s %s %s',
($path->getExcluded() ? "\xE2\x80\x93" : '+'),
$repo_name,
$path_link);
}
$path_links = phutil_implode_html(phutil_tag('br'), $path_links);
$rows[] = array(pht('Paths'), $path_links);
$table = new AphrontTableView($rows);
$table->setColumnClasses(
array(
'header',
'wide',
));
$panel = new PHUIObjectBoxView();
$header = new PHUIHeaderView();
$header->setHeader(
pht('Package Details for "%s"', $package->getName()));
$header->addActionLink(
id(new PHUIButtonView())
->setTag('a')
->setHref('/owners/delete/'.$package->getID().'/')
->addSigil('workflow')
->setText(pht('Delete Package')));
$header->addActionLink(
id(new PHUIButtonView())
->setTag('a')
->setHref('/owners/edit/'.$package->getID().'/')
->setText(pht('Edit Package')));
$panel->setHeader($header);
$panel->setTable($table);
$key = 'package/'.$package->getID();
$this->setSideNavFilter($key);
$commit_views = array(); $commit_views = array();
@ -151,7 +65,7 @@ final class PhabricatorOwnersDetailController
->execute(); ->execute();
if ($attention_commits) { if ($attention_commits) {
$view = id(new PhabricatorAuditListView()) $view = id(new PhabricatorAuditListView())
->setUser($user) ->setUser($viewer)
->setCommits($attention_commits); ->setCommits($attention_commits);
$commit_views[] = array( $commit_views[] = array(
@ -172,7 +86,7 @@ final class PhabricatorOwnersDetailController
->execute(); ->execute();
$view = id(new PhabricatorAuditListView()) $view = id(new PhabricatorAuditListView())
->setUser($user) ->setUser($viewer)
->setCommits($all_commits) ->setCommits($all_commits)
->setNoDataString(pht('No commits in this package.')); ->setNoDataString(pht('No commits in this package.'));
@ -210,21 +124,165 @@ final class PhabricatorOwnersDetailController
$crumbs = $this->buildApplicationCrumbs(); $crumbs = $this->buildApplicationCrumbs();
$crumbs->addTextCrumb($package->getName()); $crumbs->addTextCrumb($package->getName());
$nav = $this->buildSideNavView(); $timeline = $this->buildTransactionTimeline(
$nav->appendChild($crumbs); $package,
$nav->appendChild($panel); new PhabricatorOwnersPackageTransactionQuery());
$nav->appendChild($commit_panels); $timeline->setShouldTerminate(true);
return $this->buildApplicationPage( return $this->buildApplicationPage(
$nav,
array( array(
'title' => pht('Package %s', $package->getName()), $crumbs,
$panel,
$this->renderPathsTable($paths, $repositories),
$commit_panels,
$timeline,
),
array(
'title' => $package->getName(),
)); ));
} }
protected function getExtraPackageViews(AphrontSideNavFilterView $view) {
$package = $this->package; private function buildPackagePropertyView(PhabricatorOwnersPackage $package) {
$view->addFilter('package/'.$package->getID(), pht('Details')); $viewer = $this->getViewer();
$view = id(new PHUIPropertyListView())
->setUser($viewer);
$primary_phid = $package->getPrimaryOwnerPHID();
if ($primary_phid) {
$primary_owner = $viewer->renderHandle($primary_phid);
} else {
$primary_owner = phutil_tag('em', array(), pht('None'));
}
$view->addProperty(pht('Primary Owner'), $primary_owner);
// TODO: needOwners() this on the Query.
$owners = $package->loadOwners();
if ($owners) {
$owner_list = $viewer->renderHandleList(mpull($owners, 'getUserPHID'));
} else {
$owner_list = phutil_tag('em', array(), pht('None'));
}
$view->addProperty(pht('Owners'), $owner_list);
if ($package->getAuditingEnabled()) {
$auditing = pht('Enabled');
} else {
$auditing = pht('Disabled');
}
$view->addProperty(pht('Auditing'), $auditing);
$description = $package->getDescription();
if (strlen($description)) {
$view->addSectionHeader(pht('Description'));
$view->addTextContent(
$output = PhabricatorMarkupEngine::renderOneObject(
id(new PhabricatorMarkupOneOff())->setContent($description),
'default',
$viewer));
}
return $view;
}
private function buildPackageActionView(PhabricatorOwnersPackage $package) {
$viewer = $this->getViewer();
// TODO: Implement this capability.
$can_edit = true;
$id = $package->getID();
$edit_uri = $this->getApplicationURI("/edit/{$id}/");
$paths_uri = $this->getApplicationURI("/paths/{$id}/");
$view = id(new PhabricatorActionListView())
->setUser($viewer)
->setObject($package)
->addAction(
id(new PhabricatorActionView())
->setName(pht('Edit Package'))
->setIcon('fa-pencil')
->setDisabled(!$can_edit)
->setWorkflow(!$can_edit)
->setHref($edit_uri))
->addAction(
id(new PhabricatorActionView())
->setName(pht('Edit Paths'))
->setIcon('fa-folder-open')
->setDisabled(!$can_edit)
->setWorkflow(!$can_edit)
->setHref($paths_uri));
return $view;
}
private function renderPathsTable(array $paths, array $repositories) {
$viewer = $this->getViewer();
$rows = array();
foreach ($paths as $path) {
$repo = idx($repositories, $path->getRepositoryPHID());
if (!$repo) {
continue;
}
$href = DiffusionRequest::generateDiffusionURI(
array(
'callsign' => $repo->getCallsign(),
'branch' => $repo->getDefaultBranch(),
'path' => $path->getPath(),
'action' => 'browse',
));
$path_link = phutil_tag(
'a',
array(
'href' => (string)$href,
),
$path->getPath());
$rows[] = array(
($path->getExcluded() ? '-' : '+'),
$repo->getName(),
$path_link,
);
}
$info = null;
if (!$paths) {
$info = id(new PHUIInfoView())
->setSeverity(PHUIInfoView::SEVERITY_WARNING)
->setErrors(
array(
pht(
'This package does not contain any paths yet. Use '.
'"Edit Paths" to add some.'),
));
}
$table = id(new AphrontTableView($rows))
->setHeaders(
array(
null,
pht('Repository'),
pht('Path'),
))
->setColumnClasses(
array(
null,
null,
'wide',
));
$box = id(new PHUIObjectBoxView())
->setHeaderText(pht('Paths'))
->appendChild($table);
if ($info) {
$box->setInfoView($info);
}
return $box;
} }
} }

View file

@ -3,180 +3,132 @@
final class PhabricatorOwnersEditController final class PhabricatorOwnersEditController
extends PhabricatorOwnersController { extends PhabricatorOwnersController {
private $id; public function handleRequest(AphrontRequest $request) {
$viewer = $request->getUser();
public function willProcessRequest(array $data) { $id = $request->getURIData('id');
$this->id = idx($data, 'id'); if ($id) {
} $package = id(new PhabricatorOwnersPackageQuery())
->setViewer($viewer)
public function processRequest() { ->withIDs(array($id))
$request = $this->getRequest(); ->requireCapabilities(
$user = $request->getUser(); array(
PhabricatorPolicyCapability::CAN_VIEW,
if ($this->id) { // TODO: Support this capability.
$package = id(new PhabricatorOwnersPackage())->load($this->id); // PhabricatorPolicyCapability::CAN_EDIT,
))
->executeOne();
if (!$package) { if (!$package) {
return new Aphront404Response(); return new Aphront404Response();
} }
$is_new = false;
} else { } else {
$package = new PhabricatorOwnersPackage(); $package = PhabricatorOwnersPackage::initializeNewPackage($viewer);
$package->setPrimaryOwnerPHID($user->getPHID()); $is_new = true;
} }
$e_name = true; $e_name = true;
$e_primary = true; $e_primary = true;
$v_name = $package->getName();
$v_primary = $package->getPrimaryOwnerPHID();
// TODO: Pull these off needOwners() on the Query.
$v_owners = mpull($package->loadOwners(), 'getUserPHID');
$v_auditing = $package->getAuditingEnabled();
$v_description = $package->getDescription();
$errors = array(); $errors = array();
if ($request->isFormPost()) { if ($request->isFormPost()) {
$package->setName($request->getStr('name')); $xactions = array();
$package->setDescription($request->getStr('description'));
$old_auditing_enabled = $package->getAuditingEnabled();
$package->setAuditingEnabled(
($request->getStr('auditing') === 'enabled')
? 1
: 0);
$primary = $request->getArr('primary'); $v_name = $request->getStr('name');
$primary = reset($primary); $v_primary = head($request->getArr('primary'));
$old_primary = $package->getPrimaryOwnerPHID(); $v_owners = $request->getArr('owners');
$package->setPrimaryOwnerPHID($primary); $v_auditing = ($request->getStr('auditing') == 'enabled');
$v_description = $request->getStr('description');
$owners = $request->getArr('owners'); if ($v_primary) {
if ($primary) { $v_owners[] = $v_primary;
array_unshift($owners, $primary); $v_owners = array_unique($v_owners);
} }
$owners = array_unique($owners);
$paths = $request->getArr('path'); $type_name = PhabricatorOwnersPackageTransaction::TYPE_NAME;
$repos = $request->getArr('repo'); $type_primary = PhabricatorOwnersPackageTransaction::TYPE_PRIMARY;
$excludes = $request->getArr('exclude'); $type_owners = PhabricatorOwnersPackageTransaction::TYPE_OWNERS;
$type_auditing = PhabricatorOwnersPackageTransaction::TYPE_AUDITING;
$type_description = PhabricatorOwnersPackageTransaction::TYPE_DESCRIPTION;
$path_refs = array(); $xactions[] = id(new PhabricatorOwnersPackageTransaction())
for ($ii = 0; $ii < count($paths); $ii++) { ->setTransactionType($type_name)
if (empty($paths[$ii]) || empty($repos[$ii])) { ->setNewValue($v_name);
continue;
$xactions[] = id(new PhabricatorOwnersPackageTransaction())
->setTransactionType($type_primary)
->setNewValue($v_primary);
$xactions[] = id(new PhabricatorOwnersPackageTransaction())
->setTransactionType($type_owners)
->setNewValue($v_owners);
$xactions[] = id(new PhabricatorOwnersPackageTransaction())
->setTransactionType($type_auditing)
->setNewValue($v_auditing);
$xactions[] = id(new PhabricatorOwnersPackageTransaction())
->setTransactionType($type_description)
->setNewValue($v_description);
$editor = id(new PhabricatorOwnersPackageTransactionEditor())
->setActor($viewer)
->setContentSourceFromRequest($request)
->setContinueOnNoEffect(true);
try {
$editor->applyTransactions($package, $xactions);
$id = $package->getID();
if ($is_new) {
$next_uri = '/owners/paths/'.$id.'/';
} else {
$next_uri = '/owners/package/'.$id.'/';
} }
$path_refs[] = array(
'repositoryPHID' => $repos[$ii],
'path' => $paths[$ii],
'excluded' => $excludes[$ii],
);
}
if (!strlen($package->getName())) { return id(new AphrontRedirectResponse())->setURI($next_uri);
$e_name = pht('Required'); } catch (AphrontDuplicateKeyQueryException $ex) {
$errors[] = pht('Package name is required.'); $e_name = pht('Duplicate');
} else { $errors[] = pht('Package name must be unique.');
$e_name = null; } catch (PhabricatorApplicationTransactionValidationException $ex) {
} $validation_exception = $ex;
if (!$package->getPrimaryOwnerPHID()) { $e_name = $ex->getShortMessage($type_name);
$e_primary = pht('Required'); $e_primary = $ex->getShortMessage($type_primary);
$errors[] = pht('Package must have a primary owner.');
} else {
$e_primary = null;
}
if (!$path_refs) {
$errors[] = pht('Package must include at least one path.');
}
if (!$errors) {
$package->attachUnsavedOwners($owners);
$package->attachUnsavedPaths($path_refs);
$package->attachOldAuditingEnabled($old_auditing_enabled);
$package->attachOldPrimaryOwnerPHID($old_primary);
try {
id(new PhabricatorOwnersPackageEditor())
->setActor($user)
->setPackage($package)
->save();
return id(new AphrontRedirectResponse())
->setURI('/owners/package/'.$package->getID().'/');
} catch (AphrontDuplicateKeyQueryException $ex) {
$e_name = pht('Duplicate');
$errors[] = pht('Package name must be unique.');
}
}
} else {
$owners = $package->loadOwners();
$owners = mpull($owners, 'getUserPHID');
$paths = $package->loadPaths();
$path_refs = array();
foreach ($paths as $path) {
$path_refs[] = array(
'repositoryPHID' => $path->getRepositoryPHID(),
'path' => $path->getPath(),
'excluded' => $path->getExcluded(),
);
} }
} }
$primary = $package->getPrimaryOwnerPHID(); if ($v_primary) {
if ($primary) { $value_primary_owner = array($v_primary);
$value_primary_owner = array($primary);
} else { } else {
$value_primary_owner = array(); $value_primary_owner = array();
} }
if ($package->getID()) { if ($is_new) {
$title = pht('Edit Package'); $cancel_uri = '/owners/';
$side_nav_filter = 'edit/'.$this->id;
} else {
$title = pht('New Package'); $title = pht('New Package');
$side_nav_filter = 'new'; $button_text = pht('Continue');
} else {
$cancel_uri = '/owners/package/'.$package->getID().'/';
$title = pht('Edit Package');
$button_text = pht('Save Package');
} }
$this->setSideNavFilter($side_nav_filter);
$repos = id(new PhabricatorRepositoryQuery())
->setViewer($user)
->execute();
$default_paths = array();
foreach ($repos as $repo) {
$default_path = $repo->getDetail('default-owners-path');
if ($default_path) {
$default_paths[$repo->getPHID()] = $default_path;
}
}
$repos = mpull($repos, 'getCallsign', 'getPHID');
asort($repos);
$template = new AphrontTypeaheadTemplateView();
$template = $template->render();
Javelin::initBehavior(
'owners-path-editor',
array(
'root' => 'path-editor',
'table' => 'paths',
'add_button' => 'addpath',
'repositories' => $repos,
'input_template' => $template,
'pathRefs' => $path_refs,
'completeURI' => '/diffusion/services/path/complete/',
'validateURI' => '/diffusion/services/path/validate/',
'repositoryDefaultPaths' => $default_paths,
));
require_celerity_resource('owners-path-editor-css');
$cancel_uri = $package->getID()
? '/owners/package/'.$package->getID().'/'
: '/owners/';
$form = id(new AphrontFormView()) $form = id(new AphrontFormView())
->setUser($user) ->setUser($viewer)
->appendChild( ->appendChild(
id(new AphrontFormTextControl()) id(new AphrontFormTextControl())
->setLabel(pht('Name')) ->setLabel(pht('Name'))
->setName('name') ->setName('name')
->setValue($package->getName()) ->setValue($v_name)
->setError($e_name)) ->setError($e_name))
->appendControl( ->appendControl(
id(new AphrontFormTokenizerControl()) id(new AphrontFormTokenizerControl())
@ -191,7 +143,7 @@ final class PhabricatorOwnersEditController
->setDatasource(new PhabricatorProjectOrUserDatasource()) ->setDatasource(new PhabricatorProjectOrUserDatasource())
->setLabel(pht('Owners')) ->setLabel(pht('Owners'))
->setName('owners') ->setName('owners')
->setValue($owners)) ->setValue($v_owners))
->appendChild( ->appendChild(
id(new AphrontFormSelectControl()) id(new AphrontFormSelectControl())
->setName('auditing') ->setName('auditing')
@ -202,47 +154,22 @@ final class PhabricatorOwnersEditController
'this package will be reviewed to make sure an owner '. 'this package will be reviewed to make sure an owner '.
'of the package is involved and the commit message has '. 'of the package is involved and the commit message has '.
'a valid revision, reviewed by, and author.')) 'a valid revision, reviewed by, and author.'))
->setOptions(array( ->setOptions(
'disabled' => pht('Disabled'), array(
'enabled' => pht('Enabled'), 'disabled' => pht('Disabled'),
)) 'enabled' => pht('Enabled'),
->setValue( ))
$package->getAuditingEnabled() ->setValue(($v_auditing ? 'enabled' : 'disabled')))
? 'enabled'
: 'disabled'))
->appendChild( ->appendChild(
id(new PHUIFormInsetView()) id(new PhabricatorRemarkupControl())
->setTitle(pht('Paths')) ->setUser($viewer)
->addDivAttributes(array('id' => 'path-editor'))
->setRightButton(javelin_tag(
'a',
array(
'href' => '#',
'class' => 'button green',
'sigil' => 'addpath',
'mustcapture' => true,
),
pht('Add New Path')))
->setDescription(
pht(
'Specify the files and directories which comprise '.
'this package.'))
->setContent(javelin_tag(
'table',
array(
'class' => 'owners-path-editor-table',
'sigil' => 'paths',
),
'')))
->appendChild(
id(new AphrontFormTextAreaControl())
->setLabel(pht('Description')) ->setLabel(pht('Description'))
->setName('description') ->setName('description')
->setValue($package->getDescription())) ->setValue($v_description))
->appendChild( ->appendChild(
id(new AphrontFormSubmitControl()) id(new AphrontFormSubmitControl())
->addCancelButton($cancel_uri) ->addCancelButton($cancel_uri)
->setValue(pht('Save Package'))); ->setValue($button_text));
$form_box = id(new PHUIObjectBoxView()) $form_box = id(new PHUIObjectBoxView())
->setHeaderText($title) ->setHeaderText($title)
@ -251,29 +178,22 @@ final class PhabricatorOwnersEditController
$crumbs = $this->buildApplicationCrumbs(); $crumbs = $this->buildApplicationCrumbs();
if ($package->getID()) { if ($package->getID()) {
$crumbs->addTextCrumb(pht('Edit %s', $package->getName())); $crumbs->addTextCrumb(
$package->getName(),
$this->getApplicationURI('package/'.$package->getID().'/'));
$crumbs->addTextCrumb(pht('Edit'));
} else { } else {
$crumbs->addTextCrumb(pht('New Package')); $crumbs->addTextCrumb(pht('New Package'));
} }
$nav = $this->buildSideNavView();
$nav->appendChild($crumbs);
$nav->appendChild($form_box);
return $this->buildApplicationPage( return $this->buildApplicationPage(
array( array(
$nav, $crumbs,
$form_box,
), ),
array( array(
'title' => $title, 'title' => $title,
)); ));
} }
protected function getExtraPackageViews(AphrontSideNavFilterView $view) {
if ($this->id) {
$view->addFilter('edit/'.$this->id, pht('Edit'));
} else {
$view->addFilter('new', pht('New'));
}
}
} }

View file

@ -3,340 +3,52 @@
final class PhabricatorOwnersListController final class PhabricatorOwnersListController
extends PhabricatorOwnersController { extends PhabricatorOwnersController {
protected $view; public function shouldAllowPublic() {
return true;
public function willProcessRequest(array $data) {
$this->view = idx($data, 'view', 'owned');
$this->setSideNavFilter('view/'.$this->view);
} }
public function processRequest() { public function handleRequest(AphrontRequest $request) {
$controller = id(new PhabricatorApplicationSearchController())
->setQueryKey($request->getURIData('queryKey'))
->setSearchEngine(new PhabricatorOwnersPackageSearchEngine())
->setNavigation($this->buildSideNavView());
$request = $this->getRequest(); return $this->delegateToController($controller);
$user = $request->getUser();
$package = new PhabricatorOwnersPackage();
$owner = new PhabricatorOwnersOwner();
$path = new PhabricatorOwnersPath();
$repository_phid = '';
if ($request->getStr('repository') != '') {
$repository_phid = id(new PhabricatorRepositoryQuery())
->setViewer($user)
->withCallsigns(array($request->getStr('repository')))
->executeOne()
->getPHID();
}
switch ($this->view) {
case 'search':
$packages = array();
$conn_r = $package->establishConnection('r');
$where = array('1 = 1');
$join = array();
$having = '';
if ($request->getStr('name')) {
$where[] = qsprintf(
$conn_r,
'p.name LIKE %~',
$request->getStr('name'));
}
if ($repository_phid || $request->getStr('path')) {
$join[] = qsprintf(
$conn_r,
'JOIN %T path ON path.packageID = p.id',
$path->getTableName());
if ($repository_phid) {
$where[] = qsprintf(
$conn_r,
'path.repositoryPHID = %s',
$repository_phid);
}
if ($request->getStr('path')) {
$where[] = qsprintf(
$conn_r,
'(path.path LIKE %~ AND NOT path.excluded) OR
%s LIKE CONCAT(REPLACE(path.path, %s, %s), %s)',
$request->getStr('path'),
$request->getStr('path'),
'_',
'\_',
'%');
$having = 'HAVING MAX(path.excluded) = 0';
}
}
if ($request->getArr('owner')) {
$join[] = qsprintf(
$conn_r,
'JOIN %T o ON o.packageID = p.id',
$owner->getTableName());
$where[] = qsprintf(
$conn_r,
'o.userPHID IN (%Ls)',
$request->getArr('owner'));
}
$data = queryfx_all(
$conn_r,
'SELECT p.* FROM %T p %Q WHERE %Q GROUP BY p.id %Q',
$package->getTableName(),
implode(' ', $join),
'('.implode(') AND (', $where).')',
$having);
$packages = $package->loadAllFromArray($data);
$header = pht('Search Results');
$nodata = pht('No packages match your query.');
break;
case 'owned':
$data = queryfx_all(
$package->establishConnection('r'),
'SELECT p.* FROM %T p JOIN %T o ON p.id = o.packageID
WHERE o.userPHID = %s GROUP BY p.id',
$package->getTableName(),
$owner->getTableName(),
$user->getPHID());
$packages = $package->loadAllFromArray($data);
$header = pht('Owned Packages');
$nodata = pht('No owned packages');
break;
case 'projects':
$projects = id(new PhabricatorProjectQuery())
->setViewer($user)
->withMemberPHIDs(array($user->getPHID()))
->withStatus(PhabricatorProjectQuery::STATUS_ANY)
->execute();
$owner_phids = mpull($projects, 'getPHID');
if ($owner_phids) {
$data = queryfx_all(
$package->establishConnection('r'),
'SELECT p.* FROM %T p JOIN %T o ON p.id = o.packageID
WHERE o.userPHID IN (%Ls) GROUP BY p.id',
$package->getTableName(),
$owner->getTableName(),
$owner_phids);
} else {
$data = array();
}
$packages = $package->loadAllFromArray($data);
$header = pht('Project Packages');
$nodata = pht('No owned packages');
break;
case 'all':
$packages = $package->loadAll();
$header = pht('All Packages');
$nodata = pht('There are no defined packages.');
break;
}
$content = $this->renderPackageTable(
$packages,
$header,
$nodata);
$filter = new AphrontListFilterView();
$owner_phids = $request->getArr('owner');
$callsigns = array('' => pht('(Any Repository)'));
$repositories = id(new PhabricatorRepositoryQuery())
->setViewer($user)
->setOrder('callsign')
->execute();
foreach ($repositories as $repository) {
$callsigns[$repository->getCallsign()] =
$repository->getCallsign().': '.$repository->getName();
}
$form = id(new AphrontFormView())
->setUser($user)
->setAction('/owners/view/search/')
->setMethod('GET')
->appendChild(
id(new AphrontFormTextControl())
->setName('name')
->setLabel(pht('Name'))
->setValue($request->getStr('name')))
->appendControl(
id(new AphrontFormTokenizerControl())
->setDatasource(new PhabricatorProjectOrUserDatasource())
->setLimit(1)
->setName('owner')
->setLabel(pht('Owner'))
->setValue($owner_phids))
->appendChild(
id(new AphrontFormSelectControl())
->setName('repository')
->setLabel(pht('Repository'))
->setOptions($callsigns)
->setValue($request->getStr('repository')))
->appendChild(
id(new AphrontFormTextControl())
->setName('path')
->setLabel(pht('Path'))
->setValue($request->getStr('path')))
->appendChild(
id(new AphrontFormSubmitControl())
->setValue(pht('Search for Packages')));
$filter->appendChild($form);
$title = pht('Package Index');
$crumbs = $this->buildApplicationCrumbs();
$crumbs->addTextCrumb($header);
$nav = $this->buildSideNavView();
$nav->appendChild($crumbs);
$nav->appendChild($filter);
$nav->appendChild($content);
return $this->buildApplicationPage(
$nav,
array(
'title' => pht('Package Index'),
));
} }
private function renderPackageTable(array $packages, $header, $nodata) { public function buildSideNavView($for_app = false) {
assert_instances_of($packages, 'PhabricatorOwnersPackage'); $viewer = $this->getViewer();
if ($packages) { $nav = new AphrontSideNavFilterView();
$package_ids = mpull($packages, 'getID'); $nav->setBaseURI(new PhutilURI($this->getApplicationURI()));
$owners = id(new PhabricatorOwnersOwner())->loadAllWhere( if ($for_app) {
'packageID IN (%Ld)', $nav->addFilter('new/', pht('Create Package'));
$package_ids);
$paths = id(new PhabricatorOwnersPath())->loadAllWhere(
'packageID in (%Ld)',
$package_ids);
$phids = array();
foreach ($owners as $owner) {
$phids[$owner->getUserPHID()] = true;
}
$phids = array_keys($phids);
$handles = $this->loadViewerHandles($phids);
$repository_phids = array();
foreach ($paths as $path) {
$repository_phids[$path->getRepositoryPHID()] = true;
}
if ($repository_phids) {
$repositories = id(new PhabricatorRepositoryQuery())
->setViewer($this->getRequest()->getUser())
->withPHIDs(array_keys($repository_phids))
->execute();
} else {
$repositories = array();
}
$repositories = mpull($repositories, null, 'getPHID');
$owners = mgroup($owners, 'getPackageID');
$paths = mgroup($paths, 'getPackageID');
} else {
$handles = array();
$repositories = array();
$owners = array();
$paths = array();
} }
$rows = array(); id(new PhabricatorOwnersPackageSearchEngine())
foreach ($packages as $package) { ->setViewer($viewer)
->addNavigationItems($nav->getMenu());
$pkg_owners = idx($owners, $package->getID(), array()); $nav->selectFilter(null);
foreach ($pkg_owners as $key => $owner) {
$pkg_owners[$key] = $handles[$owner->getUserPHID()]->renderLink();
if ($owner->getUserPHID() == $package->getPrimaryOwnerPHID()) {
$pkg_owners[$key] = phutil_tag('strong', array(), $pkg_owners[$key]);
}
}
$pkg_owners = phutil_implode_html(phutil_tag('br'), $pkg_owners);
$pkg_paths = idx($paths, $package->getID(), array()); return $nav;
foreach ($pkg_paths as $key => $path) {
$repo = idx($repositories, $path->getRepositoryPHID());
if ($repo) {
$href = DiffusionRequest::generateDiffusionURI(
array(
'callsign' => $repo->getCallsign(),
'branch' => $repo->getDefaultBranch(),
'path' => $path->getPath(),
'action' => 'browse',
));
$pkg_paths[$key] = hsprintf(
'%s %s%s',
($path->getExcluded() ? "\xE2\x80\x93" : '+'),
phutil_tag('strong', array(), $repo->getName()),
phutil_tag(
'a',
array(
'href' => (string)$href,
),
$path->getPath()));
} else {
$pkg_paths[$key] = $path->getPath();
}
}
$pkg_paths = phutil_implode_html(phutil_tag('br'), $pkg_paths);
$rows[] = array(
phutil_tag(
'a',
array(
'href' => '/owners/package/'.$package->getID().'/',
),
$package->getName()),
$pkg_owners,
$pkg_paths,
phutil_tag(
'a',
array(
'href' => '/audit/?auditorPHIDs='.$package->getPHID(),
),
pht('Related Commits')),
);
}
$table = new AphrontTableView($rows);
$table->setHeaders(
array(
pht('Name'),
pht('Owners'),
pht('Paths'),
pht('Related Commits'),
));
$table->setColumnClasses(
array(
'pri',
'',
'wide wrap',
'narrow',
));
$panel = new PHUIObjectBoxView();
$panel->setHeaderText($header);
$panel->setTable($table);
return $panel;
} }
protected function getExtraPackageViews(AphrontSideNavFilterView $view) { public function buildApplicationMenu() {
if ($this->view == 'search') { return $this->buildSideNavView(true)->getMenu();
$view->addFilter('view/search', pht('Search Results'));
}
} }
protected function buildApplicationCrumbs() {
$crumbs = parent::buildApplicationCrumbs();
$crumbs->addAction(
id(new PHUIListItemView())
->setName(pht('Create Package'))
->setHref($this->getApplicationURI('new/'))
->setIcon('fa-plus-square'));
return $crumbs;
}
} }

View file

@ -0,0 +1,165 @@
<?php
final class PhabricatorOwnersPathsController
extends PhabricatorOwnersController {
public function handleRequest(AphrontRequest $request) {
$viewer = $request->getUser();
$package = id(new PhabricatorOwnersPackageQuery())
->setViewer($viewer)
->withIDs(array($request->getURIData('id')))
->requireCapabilities(
array(
PhabricatorPolicyCapability::CAN_VIEW,
// TODO: Support this capability.
// PhabricatorPolicyCapability::CAN_EDIT,
))
->executeOne();
if (!$package) {
return new Aphront404Response();
}
if ($request->isFormPost()) {
$paths = $request->getArr('path');
$repos = $request->getArr('repo');
$excludes = $request->getArr('exclude');
$path_refs = array();
foreach ($paths as $key => $path) {
if (!isset($repos[$key])) {
throw new Exception(
pht(
'No repository PHID for path "%s"!',
$key));
}
if (!isset($excludes[$key])) {
throw new Exception(
pht(
'No exclusion value for path "%s"!',
$key));
}
$path_refs[] = array(
'repositoryPHID' => $repos[$key],
'path' => $path,
'excluded' => (int)$excludes[$key],
);
}
$type_paths = PhabricatorOwnersPackageTransaction::TYPE_PATHS;
$xactions = array();
$xactions[] = id(new PhabricatorOwnersPackageTransaction())
->setTransactionType($type_paths)
->setNewValue($path_refs);
$editor = id(new PhabricatorOwnersPackageTransactionEditor())
->setActor($viewer)
->setContentSourceFromRequest($request)
->setContinueOnNoEffect(true)
->setContinueOnMissingFields(true);
$editor->applyTransactions($package, $xactions);
return id(new AphrontRedirectResponse())
->setURI('/owners/package/'.$package->getID().'/');
} else {
$paths = $package->loadPaths();
$path_refs = mpull($paths, 'getRef');
}
$repos = id(new PhabricatorRepositoryQuery())
->setViewer($viewer)
->execute();
$default_paths = array();
foreach ($repos as $repo) {
$default_path = $repo->getDetail('default-owners-path');
if ($default_path) {
$default_paths[$repo->getPHID()] = $default_path;
}
}
$repos = mpull($repos, 'getCallsign', 'getPHID');
asort($repos);
$template = new AphrontTypeaheadTemplateView();
$template = $template->render();
Javelin::initBehavior(
'owners-path-editor',
array(
'root' => 'path-editor',
'table' => 'paths',
'add_button' => 'addpath',
'repositories' => $repos,
'input_template' => $template,
'pathRefs' => $path_refs,
'completeURI' => '/diffusion/services/path/complete/',
'validateURI' => '/diffusion/services/path/validate/',
'repositoryDefaultPaths' => $default_paths,
));
require_celerity_resource('owners-path-editor-css');
$cancel_uri = '/owners/package/'.$package->getID().'/';
$form = id(new AphrontFormView())
->setUser($viewer)
->appendChild(
id(new PHUIFormInsetView())
->setTitle(pht('Paths'))
->addDivAttributes(array('id' => 'path-editor'))
->setRightButton(javelin_tag(
'a',
array(
'href' => '#',
'class' => 'button green',
'sigil' => 'addpath',
'mustcapture' => true,
),
pht('Add New Path')))
->setDescription(
pht(
'Specify the files and directories which comprise '.
'this package.'))
->setContent(javelin_tag(
'table',
array(
'class' => 'owners-path-editor-table',
'sigil' => 'paths',
),
'')))
->appendChild(
id(new AphrontFormSubmitControl())
->addCancelButton($cancel_uri)
->setValue(pht('Save Paths')));
$form_box = id(new PHUIObjectBoxView())
->setHeaderText(pht('Edit Paths'))
->setForm($form);
$crumbs = $this->buildApplicationCrumbs();
$crumbs->addTextCrumb(
$package->getName(),
$this->getApplicationURI('package/'.$package->getID().'/'));
$crumbs->addTextCrumb(pht('Edit Paths'));
return $this->buildApplicationPage(
array(
$crumbs,
$form_box,
),
array(
'title' => array(
$package->getName(),
pht('Edit Paths'),
),
));
}
}

View file

@ -1,198 +0,0 @@
<?php
final class PhabricatorOwnersPackageEditor extends PhabricatorEditor {
private $package;
public function setPackage(PhabricatorOwnersPackage $package) {
$this->package = $package;
return $this;
}
public function getPackage() {
return $this->package;
}
public function save() {
$actor = $this->getActor();
$package = $this->getPackage();
$package->attachActorPHID($actor->getPHID());
if ($package->getID()) {
$is_new = false;
} else {
$is_new = true;
}
$package->openTransaction();
$ret = $package->save();
$add_owners = array();
$remove_owners = array();
$all_owners = array();
if ($package->getUnsavedOwners()) {
$new_owners = array_fill_keys($package->getUnsavedOwners(), true);
$cur_owners = array();
foreach ($package->loadOwners() as $owner) {
if (empty($new_owners[$owner->getUserPHID()])) {
$remove_owners[$owner->getUserPHID()] = true;
$owner->delete();
continue;
}
$cur_owners[$owner->getUserPHID()] = true;
}
$add_owners = array_diff_key($new_owners, $cur_owners);
$all_owners = array_merge(
array($package->getPrimaryOwnerPHID() => true),
$new_owners,
$remove_owners);
foreach ($add_owners as $phid => $ignored) {
$owner = new PhabricatorOwnersOwner();
$owner->setPackageID($package->getID());
$owner->setUserPHID($phid);
$owner->save();
}
$package->attachUnsavedOwners(array());
}
$add_paths = array();
$remove_paths = array();
$touched_repos = array();
if ($package->getUnsavedPaths()) {
$new_paths = igroup(
$package->getUnsavedPaths(),
'repositoryPHID',
'path');
$cur_paths = $package->loadPaths();
foreach ($cur_paths as $key => $path) {
$repository_phid = $path->getRepositoryPHID();
$new_path = head(idx(
idx($new_paths, $repository_phid, array()),
$path->getPath(),
array()));
$excluded = $path->getExcluded();
if ($new_path === false ||
idx($new_path, 'excluded') != $excluded) {
$touched_repos[$repository_phid] = true;
$remove_paths[$repository_phid][$path->getPath()] = $excluded;
$path->delete();
unset($cur_paths[$key]);
}
}
$cur_paths = mgroup($cur_paths, 'getRepositoryPHID', 'getPath');
$repositories = id(new PhabricatorRepositoryQuery())
->setViewer($actor)
->withPHIDs(array_keys($cur_paths))
->execute();
$repositories = mpull($repositories, null, 'getPHID');
foreach ($new_paths as $repository_phid => $paths) {
$repository = idx($repositories, $repository_phid);
if (!$repository) {
continue;
}
foreach ($paths as $path => $dicts) {
$path = ltrim($path, '/');
// build query to validate path
$drequest = DiffusionRequest::newFromDictionary(
array(
'user' => $actor,
'repository' => $repository,
'path' => $path,
));
$results = DiffusionBrowseResultSet::newFromConduit(
DiffusionQuery::callConduitWithDiffusionRequest(
$actor,
$drequest,
'diffusion.browsequery',
array(
'commit' => $drequest->getCommit(),
'path' => $path,
'needValidityOnly' => true,
)));
$valid = $results->isValidResults();
$is_directory = true;
if (!$valid) {
switch ($results->getReasonForEmptyResultSet()) {
case DiffusionBrowseResultSet::REASON_IS_FILE:
$valid = true;
$is_directory = false;
break;
case DiffusionBrowseResultSet::REASON_IS_EMPTY:
$valid = true;
break;
}
}
if ($is_directory && substr($path, -1) != '/') {
$path .= '/';
}
if (substr($path, 0, 1) != '/') {
$path = '/'.$path;
}
if (empty($cur_paths[$repository_phid][$path]) && $valid) {
$touched_repos[$repository_phid] = true;
$excluded = idx(reset($dicts), 'excluded', 0);
$add_paths[$repository_phid][$path] = $excluded;
$obj = new PhabricatorOwnersPath();
$obj->setPackageID($package->getID());
$obj->setRepositoryPHID($repository_phid);
$obj->setPath($path);
$obj->setExcluded($excluded);
$obj->save();
}
}
}
$package->attachUnsavedPaths(array());
}
$package->saveTransaction();
if ($is_new) {
$mail = new PackageCreateMail($package);
} else {
$mail = new PackageModifyMail(
$package,
array_keys($add_owners),
array_keys($remove_owners),
array_keys($all_owners),
array_keys($touched_repos),
$add_paths,
$remove_paths);
}
$mail->setActor($actor);
$mail->send();
return $ret;
}
public function delete() {
$actor = $this->getActor();
$package = $this->getPackage();
$package->attachActorPHID($actor->getPHID());
$mails = id(new PackageDeleteMail($package))
->setActor($actor)
->prepareMails();
$package->openTransaction();
foreach ($package->loadOwners() as $owner) {
$owner->delete();
}
foreach ($package->loadPaths() as $path) {
$path->delete();
}
$ret = $package->delete();
$package->saveTransaction();
foreach ($mails as $mail) {
$mail->saveAndSend();
}
return $ret;
}
}

View file

@ -0,0 +1,290 @@
<?php
final class PhabricatorOwnersPackageTransactionEditor
extends PhabricatorApplicationTransactionEditor {
public function getEditorApplicationClass() {
return 'PhabricatorOwnersApplication';
}
public function getEditorObjectsDescription() {
return pht('Owners Packages');
}
public function getTransactionTypes() {
$types = parent::getTransactionTypes();
$types[] = PhabricatorOwnersPackageTransaction::TYPE_NAME;
$types[] = PhabricatorOwnersPackageTransaction::TYPE_PRIMARY;
$types[] = PhabricatorOwnersPackageTransaction::TYPE_OWNERS;
$types[] = PhabricatorOwnersPackageTransaction::TYPE_AUDITING;
$types[] = PhabricatorOwnersPackageTransaction::TYPE_DESCRIPTION;
$types[] = PhabricatorOwnersPackageTransaction::TYPE_PATHS;
return $types;
}
protected function getCustomTransactionOldValue(
PhabricatorLiskDAO $object,
PhabricatorApplicationTransaction $xaction) {
switch ($xaction->getTransactionType()) {
case PhabricatorOwnersPackageTransaction::TYPE_NAME:
return $object->getName();
case PhabricatorOwnersPackageTransaction::TYPE_PRIMARY:
return $object->getPrimaryOwnerPHID();
case PhabricatorOwnersPackageTransaction::TYPE_OWNERS:
// TODO: needOwners() this on the Query.
$phids = mpull($object->loadOwners(), 'getUserPHID');
$phids = array_values($phids);
return $phids;
case PhabricatorOwnersPackageTransaction::TYPE_AUDITING:
return (int)$object->getAuditingEnabled();
case PhabricatorOwnersPackageTransaction::TYPE_DESCRIPTION:
return $object->getDescription();
case PhabricatorOwnersPackageTransaction::TYPE_PATHS:
// TODO: needPaths() this on the query
$paths = $object->loadPaths();
return mpull($paths, 'getRef');
}
}
protected function getCustomTransactionNewValue(
PhabricatorLiskDAO $object,
PhabricatorApplicationTransaction $xaction) {
switch ($xaction->getTransactionType()) {
case PhabricatorOwnersPackageTransaction::TYPE_NAME:
case PhabricatorOwnersPackageTransaction::TYPE_PRIMARY:
case PhabricatorOwnersPackageTransaction::TYPE_DESCRIPTION:
case PhabricatorOwnersPackageTransaction::TYPE_PATHS:
return $xaction->getNewValue();
case PhabricatorOwnersPackageTransaction::TYPE_AUDITING:
return (int)$xaction->getNewValue();
case PhabricatorOwnersPackageTransaction::TYPE_OWNERS:
$phids = $xaction->getNewValue();
$phids = array_unique($phids);
$phids = array_values($phids);
return $phids;
}
}
protected function transactionHasEffect(
PhabricatorLiskDAO $object,
PhabricatorApplicationTransaction $xaction) {
switch ($xaction->getTransactionType()) {
case PhabricatorOwnersPackageTransaction::TYPE_PATHS:
$old = $xaction->getOldValue();
$new = $xaction->getNewValue();
$diffs = PhabricatorOwnersPath::getTransactionValueChanges($old, $new);
list($rem, $add) = $diffs;
return ($rem || $add);
}
return parent::transactionHasEffect($object, $xaction);
}
protected function applyCustomInternalTransaction(
PhabricatorLiskDAO $object,
PhabricatorApplicationTransaction $xaction) {
switch ($xaction->getTransactionType()) {
case PhabricatorOwnersPackageTransaction::TYPE_NAME:
$object->setName($xaction->getNewValue());
return;
case PhabricatorOwnersPackageTransaction::TYPE_PRIMARY:
$object->setPrimaryOwnerPHID($xaction->getNewValue());
return;
case PhabricatorOwnersPackageTransaction::TYPE_DESCRIPTION:
$object->setDescription($xaction->getNewValue());
return;
case PhabricatorOwnersPackageTransaction::TYPE_AUDITING:
$object->setAuditingEnabled($xaction->getNewValue());
return;
case PhabricatorOwnersPackageTransaction::TYPE_OWNERS:
case PhabricatorOwnersPackageTransaction::TYPE_PATHS:
return;
}
return parent::applyCustomInternalTransaction($object, $xaction);
}
protected function applyCustomExternalTransaction(
PhabricatorLiskDAO $object,
PhabricatorApplicationTransaction $xaction) {
switch ($xaction->getTransactionType()) {
case PhabricatorOwnersPackageTransaction::TYPE_NAME:
case PhabricatorOwnersPackageTransaction::TYPE_PRIMARY:
case PhabricatorOwnersPackageTransaction::TYPE_DESCRIPTION:
case PhabricatorOwnersPackageTransaction::TYPE_AUDITING:
return;
case PhabricatorOwnersPackageTransaction::TYPE_OWNERS:
$old = $xaction->getOldValue();
$new = $xaction->getNewValue();
// TODO: needOwners this
$owners = $object->loadOwners();
$owners = mpull($owners, null, 'getUserPHID');
$rem = array_diff($old, $new);
foreach ($rem as $phid) {
if (isset($owners[$phid])) {
$owners[$phid]->delete();
unset($owners[$phid]);
}
}
$add = array_diff($new, $old);
foreach ($add as $phid) {
$owners[$phid] = id(new PhabricatorOwnersOwner())
->setPackageID($object->getID())
->setUserPHID($phid)
->save();
}
// TODO: Attach owners here
return;
case PhabricatorOwnersPackageTransaction::TYPE_PATHS:
$old = $xaction->getOldValue();
$new = $xaction->getNewValue();
// TODO: needPaths this
$paths = $object->loadPaths();
$diffs = PhabricatorOwnersPath::getTransactionValueChanges($old, $new);
list($rem, $add) = $diffs;
$set = PhabricatorOwnersPath::getSetFromTransactionValue($rem);
foreach ($paths as $path) {
$ref = $path->getRef();
if (PhabricatorOwnersPath::isRefInSet($ref, $set)) {
$path->delete();
}
}
foreach ($add as $ref) {
$path = PhabricatorOwnersPath::newFromRef($ref)
->setPackageID($object->getID())
->save();
}
return;
}
return parent::applyCustomExternalTransaction($object, $xaction);
}
protected function validateTransaction(
PhabricatorLiskDAO $object,
$type,
array $xactions) {
$errors = parent::validateTransaction($object, $type, $xactions);
switch ($type) {
case PhabricatorOwnersPackageTransaction::TYPE_NAME:
$missing = $this->validateIsEmptyTextField(
$object->getName(),
$xactions);
if ($missing) {
$error = new PhabricatorApplicationTransactionValidationError(
$type,
pht('Required'),
pht('Package name is required.'),
nonempty(last($xactions), null));
$error->setIsMissingFieldError(true);
$errors[] = $error;
}
break;
case PhabricatorOwnersPackageTransaction::TYPE_PRIMARY:
$missing = $this->validateIsEmptyTextField(
$object->getPrimaryOwnerPHID(),
$xactions);
if ($missing) {
$error = new PhabricatorApplicationTransactionValidationError(
$type,
pht('Required'),
pht('Packages must have a primary owner.'),
nonempty(last($xactions), null));
$error->setIsMissingFieldError(true);
$errors[] = $error;
}
break;
}
return $errors;
}
protected function extractFilePHIDsFromCustomTransaction(
PhabricatorLiskDAO $object,
PhabricatorApplicationTransaction $xaction) {
switch ($xaction->getTransactionType()) {
case PhabricatorOwnersPackageTransaction::TYPE_DESCRIPTION:
return array($xaction->getNewValue());
}
return parent::extractFilePHIDsFromCustomTransaction($object, $xaction);
}
protected function shouldSendMail(
PhabricatorLiskDAO $object,
array $xactions) {
return true;
}
protected function getMailSubjectPrefix() {
return PhabricatorEnv::getEnvConfig('metamta.package.subject-prefix');
}
protected function getMailTo(PhabricatorLiskDAO $object) {
return array(
$object->getPrimaryOwnerPHID(),
$this->requireActor()->getPHID(),
);
}
protected function getMailCC(PhabricatorLiskDAO $object) {
// TODO: needOwners() this
return mpull($object->loadOwners(), 'getUserPHID');
}
protected function buildReplyHandler(PhabricatorLiskDAO $object) {
return id(new OwnersPackageReplyHandler())
->setMailReceiver($object);
}
protected function buildMailTemplate(PhabricatorLiskDAO $object) {
$id = $object->getID();
$name = $object->getName();
return id(new PhabricatorMetaMTAMail())
->setSubject($name)
->addHeader('Thread-Topic', $object->getPHID());
}
protected function buildMailBody(
PhabricatorLiskDAO $object,
array $xactions) {
$body = parent::buildMailBody($object, $xactions);
$detail_uri = PhabricatorEnv::getProductionURI(
'/owners/package/'.$object->getID().'/');
$body->addLinkSection(
pht('PACKAGE DETAIL'),
$detail_uri);
return $body;
}
}

View file

@ -1,12 +0,0 @@
<?php
final class PackageCreateMail extends PackageMail {
protected function isNewThread() {
return true;
}
protected function getVerb() {
return pht('Created');
}
}

View file

@ -1,13 +0,0 @@
<?php
final class PackageDeleteMail extends PackageMail {
protected function getVerb() {
return pht('Deleted');
}
protected function isNewThread() {
return false;
}
}

View file

@ -1,212 +0,0 @@
<?php
abstract class PackageMail extends PhabricatorMail {
protected $package;
protected $handles;
protected $owners;
protected $paths;
protected $mailTo;
public function __construct(PhabricatorOwnersPackage $package) {
$this->package = $package;
}
abstract protected function getVerb();
abstract protected function isNewThread();
final protected function getPackage() {
return $this->package;
}
final protected function getHandles() {
return $this->handles;
}
final protected function getOwners() {
return $this->owners;
}
final protected function getPaths() {
return $this->paths;
}
final protected function getMailTo() {
return $this->mailTo;
}
final protected function renderPackageTitle() {
return $this->getPackage()->getName();
}
final protected function renderRepoSubSection($repository_phid, $paths) {
$handles = $this->getHandles();
$section = array();
$section[] = ' '.
pht('In repository %s', $handles[$repository_phid]->getName()).
' - '.PhabricatorEnv::getProductionURI($handles[$repository_phid]
->getURI());
foreach ($paths as $path => $excluded) {
$section[] = ' '.
($excluded ? pht('Excluded') : pht('Included')).' '.$path;
}
return implode("\n", $section);
}
protected function needSend() {
return true;
}
protected function loadData() {
$package = $this->getPackage();
$owners = $package->loadOwners();
$this->owners = $owners;
$owner_phids = mpull($owners, 'getUserPHID');
$primary_owner_phid = $package->getPrimaryOwnerPHID();
$mail_to = $owner_phids;
if (!in_array($primary_owner_phid, $owner_phids)) {
$mail_to[] = $primary_owner_phid;
}
$this->mailTo = $mail_to;
$this->paths = array();
$repository_paths = mgroup($package->loadPaths(), 'getRepositoryPHID');
foreach ($repository_paths as $repository_phid => $paths) {
$this->paths[$repository_phid] = mpull($paths, 'getExcluded', 'getPath');
}
$phids = array_merge(
$this->mailTo,
array($package->getActorPHID()),
array_keys($this->paths));
$this->handles = id(new PhabricatorHandleQuery())
->setViewer($this->getActor())
->withPHIDs($phids)
->execute();
}
final protected function renderSummarySection() {
$package = $this->getPackage();
$handles = $this->getHandles();
$section = array();
$section[] = $handles[$package->getActorPHID()]->getName().' '.
strtolower($this->getVerb()).' '.$this->renderPackageTitle().'.';
$section[] = '';
$section[] = pht('PACKAGE DETAIL');
$section[] = ' '.PhabricatorEnv::getProductionURI(
'/owners/package/'.$package->getID().'/');
return implode("\n", $section);
}
protected function renderDescriptionSection() {
return pht('PACKAGE DESCRIPTION')."\n ".
$this->getPackage()->getDescription();
}
protected function renderPrimaryOwnerSection() {
$handles = $this->getHandles();
return pht('PRIMARY OWNER')."\n ".
$handles[$this->getPackage()->getPrimaryOwnerPHID()]->getName();
}
protected function renderOwnersSection() {
$handles = $this->getHandles();
$owners = $this->getOwners();
if (!$owners) {
return null;
}
$owners = mpull($owners, 'getUserPHID');
$owners = array_select_keys($handles, $owners);
$owners = mpull($owners, 'getName');
return pht('OWNERS')."\n ".implode(', ', $owners);
}
protected function renderAuditingEnabledSection() {
return pht('AUDITING ENABLED STATUS')."\n ".
($this->getPackage()->getAuditingEnabled()
? pht('Enabled')
: pht('Disabled'));
}
protected function renderPathsSection() {
$section = array();
$section[] = pht('PATHS');
foreach ($this->paths as $repository_phid => $paths) {
$section[] = $this->renderRepoSubSection($repository_phid, $paths);
}
return implode("\n", $section);
}
final protected function renderBody() {
$body = array();
$body[] = $this->renderSummarySection();
$body[] = $this->renderDescriptionSection();
$body[] = $this->renderPrimaryOwnerSection();
$body[] = $this->renderOwnersSection();
$body[] = $this->renderAuditingEnabledSection();
$body[] = $this->renderPathsSection();
$body = array_filter($body);
return implode("\n\n", $body)."\n";
}
final public function send() {
$mails = $this->prepareMails();
foreach ($mails as $mail) {
$mail->saveAndSend();
}
}
final public function prepareMails() {
if (!$this->needSend()) {
return array();
}
$this->loadData();
$package = $this->getPackage();
$prefix = PhabricatorEnv::getEnvConfig('metamta.package.subject-prefix');
$verb = $this->getVerb();
$threading = $this->getMailThreading();
list($thread_id, $thread_topic) = $threading;
$template = id(new PhabricatorMetaMTAMail())
->setSubject($this->renderPackageTitle())
->setSubjectPrefix($prefix)
->setVarySubjectPrefix("[{$verb}]")
->setFrom($package->getActorPHID())
->setThreadID($thread_id, $this->isNewThread())
->addHeader('Thread-Topic', $thread_topic)
->setRelatedPHID($package->getPHID())
->setIsBulk(true)
->setBody($this->renderBody());
$reply_handler = $this->newReplyHandler();
$mails = $reply_handler->multiplexMail(
$template,
array_select_keys($this->getHandles(), $this->getMailTo()),
array());
return $mails;
}
private function getMailThreading() {
return array(
'package-'.$this->getPackage()->getPHID(),
'Package '.$this->getPackage()->getOriginalName(),
);
}
private function newReplyHandler() {
$reply_handler = new OwnersPackageReplyHandler();
$reply_handler->setMailReceiver($this->getPackage());
return $reply_handler;
}
}

View file

@ -1,160 +0,0 @@
<?php
final class PackageModifyMail extends PackageMail {
protected $addOwners;
protected $removeOwners;
protected $allOwners;
protected $touchedRepos;
protected $addPaths;
protected $removePaths;
public function __construct(
PhabricatorOwnersPackage $package,
$add_owners,
$remove_owners,
$all_owners,
$touched_repos,
$add_paths,
$remove_paths) {
$this->package = $package;
$this->addOwners = $add_owners;
$this->removeOwners = $remove_owners;
$this->allOwners = $all_owners;
$this->touchedRepos = $touched_repos;
$this->addPaths = $add_paths;
$this->removePaths = $remove_paths;
}
protected function getVerb() {
return pht('Modified');
}
protected function isNewThread() {
return false;
}
protected function needSend() {
$package = $this->getPackage();
if ($package->getOldPrimaryOwnerPHID() !== $package->getPrimaryOwnerPHID()
|| $package->getOldAuditingEnabled() != $package->getAuditingEnabled()
|| $this->addOwners
|| $this->removeOwners
|| $this->addPaths
|| $this->removePaths) {
return true;
} else {
return false;
}
}
protected function loadData() {
$this->mailTo = $this->allOwners;
$phids = array_merge(
$this->allOwners,
$this->touchedRepos,
array(
$this->getPackage()->getActorPHID(),
));
$this->handles = id(new PhabricatorHandleQuery())
->setViewer($this->getActor())
->withPHIDs($phids)
->execute();
}
protected function renderDescriptionSection() {
return null;
}
protected function renderPrimaryOwnerSection() {
$package = $this->getPackage();
$handles = $this->getHandles();
$old_primary_owner_phid = $package->getOldPrimaryOwnerPHID();
$primary_owner_phid = $package->getPrimaryOwnerPHID();
if ($old_primary_owner_phid == $primary_owner_phid) {
return null;
}
$section = array();
$section[] = pht('PRIMARY OWNER CHANGE');
$section[] = ' '.pht('Old owner:').' '.
$handles[$old_primary_owner_phid]->getName();
$section[] = ' '.pht('New owner:').' '.
$handles[$primary_owner_phid]->getName();
return implode("\n", $section);
}
protected function renderOwnersSection() {
$section = array();
$add_owners = $this->addOwners;
$remove_owners = $this->removeOwners;
$handles = $this->getHandles();
if ($add_owners) {
$add_owners = array_select_keys($handles, $add_owners);
$add_owners = mpull($add_owners, 'getName');
$section[] = pht('ADDED OWNERS');
$section[] = ' '.implode(', ', $add_owners);
}
if ($remove_owners) {
if ($add_owners) {
$section[] = '';
}
$remove_owners = array_select_keys($handles, $remove_owners);
$remove_owners = mpull($remove_owners, 'getName');
$section[] = pht('REMOVED OWNERS');
$section[] = ' '.implode(', ', $remove_owners);
}
if ($section) {
return implode("\n", $section);
} else {
return null;
}
}
protected function renderAuditingEnabledSection() {
$package = $this->getPackage();
$old_auditing_enabled = $package->getOldAuditingEnabled();
$auditing_enabled = $package->getAuditingEnabled();
if ($old_auditing_enabled == $auditing_enabled) {
return null;
}
$section = array();
$section[] = pht('AUDITING ENABLED STATUS CHANGE');
$section[] = ' '.pht('Old value:').' '.
($old_auditing_enabled ? pht('Enabled') : pht('Disabled'));
$section[] = ' '.pht('New value:').' '.
($auditing_enabled ? pht('Enabled') : pht('Disabled'));
return implode("\n", $section);
}
protected function renderPathsSection() {
$section = array();
if ($this->addPaths) {
$section[] = pht('ADDED PATHS');
foreach ($this->addPaths as $repository_phid => $paths) {
$section[] = $this->renderRepoSubSection($repository_phid, $paths);
}
}
if ($this->removePaths) {
if ($this->addPaths) {
$section[] = '';
}
$section[] = pht('REMOVED PATHS');
foreach ($this->removePaths as $repository_phid => $paths) {
$section[] = $this->renderRepoSubSection($repository_phid, $paths);
}
}
return implode("\n", $section);
}
}

View file

@ -3,8 +3,10 @@
final class PhabricatorOwnersPackageQuery final class PhabricatorOwnersPackageQuery
extends PhabricatorCursorPagedPolicyAwareQuery { extends PhabricatorCursorPagedPolicyAwareQuery {
private $ids;
private $phids; private $phids;
private $ownerPHIDs; private $ownerPHIDs;
private $repositoryPHIDs;
/** /**
* Owners are direct owners, and members of owning projects. * Owners are direct owners, and members of owning projects.
@ -19,6 +21,16 @@ final class PhabricatorOwnersPackageQuery
return $this; return $this;
} }
public function withIDs(array $ids) {
$this->ids = $ids;
return $this;
}
public function withRepositoryPHIDs(array $phids) {
$this->repositoryPHIDs = $phids;
return $this;
}
protected function loadPage() { protected function loadPage() {
$table = new PhabricatorOwnersPackage(); $table = new PhabricatorOwnersPackage();
$conn_r = $table->establishConnection('r'); $conn_r = $table->establishConnection('r');
@ -38,27 +50,48 @@ final class PhabricatorOwnersPackageQuery
protected function buildJoinClause(AphrontDatabaseConnection $conn_r) { protected function buildJoinClause(AphrontDatabaseConnection $conn_r) {
$joins = array(); $joins = array();
if ($this->ownerPHIDs) { if ($this->ownerPHIDs !== null) {
$joins[] = qsprintf( $joins[] = qsprintf(
$conn_r, $conn_r,
'JOIN %T o ON o.packageID = p.id', 'JOIN %T o ON o.packageID = p.id',
id(new PhabricatorOwnersOwner())->getTableName()); id(new PhabricatorOwnersOwner())->getTableName());
} }
if ($this->repositoryPHIDs !== null) {
$joins[] = qsprintf(
$conn_r,
'JOIN %T rpath ON rpath.packageID = p.id',
id(new PhabricatorOwnersPath())->getTableName());
}
return implode(' ', $joins); return implode(' ', $joins);
} }
protected function buildWhereClause(AphrontDatabaseConnection $conn_r) { protected function buildWhereClause(AphrontDatabaseConnection $conn_r) {
$where = array(); $where = array();
if ($this->phids) { if ($this->phids !== null) {
$where[] = qsprintf( $where[] = qsprintf(
$conn_r, $conn_r,
'p.phid IN (%Ls)', 'p.phid IN (%Ls)',
$this->phids); $this->phids);
} }
if ($this->ownerPHIDs) { if ($this->ids !== null) {
$where[] = qsprintf(
$conn_r,
'p.id IN (%Ld)',
$this->ids);
}
if ($this->repositoryPHIDs !== null) {
$where[] = qsprintf(
$conn_r,
'rpath.repositoryPHID IN (%Ls)',
$this->repositoryPHIDs);
}
if ($this->ownerPHIDs !== null) {
$base_phids = $this->ownerPHIDs; $base_phids = $this->ownerPHIDs;
$query = new PhabricatorProjectQuery(); $query = new PhabricatorProjectQuery();

View file

@ -0,0 +1,134 @@
<?php
final class PhabricatorOwnersPackageSearchEngine
extends PhabricatorApplicationSearchEngine {
public function getResultTypeDescription() {
return pht('Owners Packages');
}
public function getApplicationClassName() {
return 'PhabricatorOwnersApplication';
}
public function buildSavedQueryFromRequest(AphrontRequest $request) {
$saved = new PhabricatorSavedQuery();
$saved->setParameter(
'ownerPHIDs',
$this->readUsersFromRequest(
$request,
'owners',
array(
PhabricatorProjectProjectPHIDType::TYPECONST,
)));
$saved->setParameter(
'repositoryPHIDs',
$this->readPHIDsFromRequest(
$request,
'repositories',
array(
PhabricatorRepositoryRepositoryPHIDType::TYPECONST,
)));
return $saved;
}
public function buildQueryFromSavedQuery(PhabricatorSavedQuery $saved) {
$query = id(new PhabricatorOwnersPackageQuery());
$owner_phids = $saved->getParameter('ownerPHIDs', array());
if ($owner_phids) {
$query->withOwnerPHIDs($owner_phids);
}
$repository_phids = $saved->getParameter('repositoryPHIDs', array());
if ($repository_phids) {
$query->withRepositoryPHIDs($repository_phids);
}
return $query;
}
public function buildSearchForm(
AphrontFormView $form,
PhabricatorSavedQuery $saved) {
$owner_phids = $saved->getParameter('ownerPHIDs', array());
$repository_phids = $saved->getParameter('repositoryPHIDs', array());
$form
->appendControl(
id(new AphrontFormTokenizerControl())
->setDatasource(new PhabricatorProjectOrUserDatasource())
->setName('owners')
->setLabel(pht('Owners'))
->setValue($owner_phids))
->appendControl(
id(new AphrontFormTokenizerControl())
->setDatasource(new DiffusionRepositoryDatasource())
->setName('repositories')
->setLabel(pht('Repositories'))
->setValue($repository_phids));
}
protected function getURI($path) {
return '/owners/'.$path;
}
protected function getBuiltinQueryNames() {
$names = array();
if ($this->requireViewer()->isLoggedIn()) {
$names['owned'] = pht('Owned');
}
$names += array(
'all' => pht('All Packages'),
);
return $names;
}
public function buildSavedQueryFromBuiltin($query_key) {
$query = $this->newSavedQuery();
$query->setQueryKey($query_key);
switch ($query_key) {
case 'all':
return $query;
case 'owned':
return $query->setParameter(
'ownerPHIDs',
array($this->requireViewer()->getPHID()));
}
return parent::buildSavedQueryFromBuiltin($query_key);
}
protected function renderResultList(
array $packages,
PhabricatorSavedQuery $query,
array $handles) {
assert_instances_of($packages, 'PhabricatorOwnersPackage');
$viewer = $this->requireViewer();
$list = id(new PHUIObjectItemListView())
->setUser($viewer);
foreach ($packages as $package) {
$id = $package->getID();
$item = id(new PHUIObjectItemView())
->setObject($package)
->setObjectName(pht('Package %d', $id))
->setHeader($package->getName())
->setHref('/owners/package/'.$id.'/');
$list->addItem($item);
}
return $list;
}
}

View file

@ -0,0 +1,10 @@
<?php
final class PhabricatorOwnersPackageTransactionQuery
extends PhabricatorApplicationTransactionQuery {
public function getTemplateApplicationTransaction() {
return new PhabricatorOwnersPackageTransaction();
}
}

View file

@ -1,19 +1,23 @@
<?php <?php
final class PhabricatorOwnersPackage extends PhabricatorOwnersDAO final class PhabricatorOwnersPackage
implements PhabricatorPolicyInterface { extends PhabricatorOwnersDAO
implements
PhabricatorPolicyInterface,
PhabricatorApplicationTransactionInterface {
protected $name; protected $name;
protected $originalName; protected $originalName;
protected $auditingEnabled; protected $auditingEnabled;
protected $description; protected $description;
protected $primaryOwnerPHID; protected $primaryOwnerPHID;
protected $mailKey;
private $unsavedOwners = self::ATTACHABLE; public static function initializeNewPackage(PhabricatorUser $actor) {
private $unsavedPaths = self::ATTACHABLE; return id(new PhabricatorOwnersPackage())
private $actorPHID; ->setAuditingEnabled(0)
private $oldPrimaryOwnerPHID; ->setPrimaryOwnerPHID($actor->getPHID());
private $oldAuditingEnabled; }
public function getCapabilities() { public function getCapabilities() {
return array( return array(
@ -37,13 +41,14 @@ final class PhabricatorOwnersPackage extends PhabricatorOwnersDAO
return array( return array(
// This information is better available from the history table. // This information is better available from the history table.
self::CONFIG_TIMESTAMPS => false, self::CONFIG_TIMESTAMPS => false,
self::CONFIG_AUX_PHID => true, self::CONFIG_AUX_PHID => true,
self::CONFIG_COLUMN_SCHEMA => array( self::CONFIG_COLUMN_SCHEMA => array(
'name' => 'text128', 'name' => 'text128',
'originalName' => 'text255', 'originalName' => 'text255',
'description' => 'text', 'description' => 'text',
'primaryOwnerPHID' => 'phid?', 'primaryOwnerPHID' => 'phid?',
'auditingEnabled' => 'bool', 'auditingEnabled' => 'bool',
'mailKey' => 'bytes20',
), ),
self::CONFIG_KEY_SCHEMA => array( self::CONFIG_KEY_SCHEMA => array(
'key_phid' => null, 'key_phid' => null,
@ -60,52 +65,16 @@ final class PhabricatorOwnersPackage extends PhabricatorOwnersDAO
} }
public function generatePHID() { public function generatePHID() {
return PhabricatorPHID::generateNewPHID('OPKG'); return PhabricatorPHID::generateNewPHID(
PhabricatorOwnersPackagePHIDType::TYPECONST);
} }
public function attachUnsavedOwners(array $owners) { public function save() {
$this->unsavedOwners = $owners; if (!$this->getMailKey()) {
return $this; $this->setMailKey(Filesystem::readRandomCharacters(20));
} }
public function getUnsavedOwners() { return parent::save();
return $this->assertAttached($this->unsavedOwners);
}
public function attachUnsavedPaths(array $paths) {
$this->unsavedPaths = $paths;
return $this;
}
public function getUnsavedPaths() {
return $this->assertAttached($this->unsavedPaths);
}
public function attachActorPHID($actor_phid) {
$this->actorPHID = $actor_phid;
return $this;
}
public function getActorPHID() {
return $this->actorPHID;
}
public function attachOldPrimaryOwnerPHID($old_primary) {
$this->oldPrimaryOwnerPHID = $old_primary;
return $this;
}
public function getOldPrimaryOwnerPHID() {
return $this->oldPrimaryOwnerPHID;
}
public function attachOldAuditingEnabled($auditing_enabled) {
$this->oldAuditingEnabled = $auditing_enabled;
return $this;
}
public function getOldAuditingEnabled() {
return $this->oldAuditingEnabled;
} }
public function setName($name) { public function setName($name) {
@ -143,15 +112,15 @@ final class PhabricatorOwnersPackage extends PhabricatorOwnersDAO
} }
return self::loadPackagesForPaths($repository, $paths); return self::loadPackagesForPaths($repository, $paths);
} }
public static function loadOwningPackages($repository, $path) { public static function loadOwningPackages($repository, $path) {
if (empty($path)) { if (empty($path)) {
return array(); return array();
} }
return self::loadPackagesForPaths($repository, array($path), 1); return self::loadPackagesForPaths($repository, array($path), 1);
} }
private static function loadPackagesForPaths( private static function loadPackagesForPaths(
PhabricatorRepository $repository, PhabricatorRepository $repository,
@ -268,4 +237,27 @@ final class PhabricatorOwnersPackage extends PhabricatorOwnersDAO
} }
return $result; return $result;
} }
/* -( PhabricatorApplicationTransactionInterface )------------------------- */
public function getApplicationTransactionEditor() {
return new PhabricatorOwnersPackageTransactionEditor();
}
public function getApplicationTransactionObject() {
return $this;
}
public function getApplicationTransactionTemplate() {
return new PhabricatorOwnersPackageTransaction();
}
public function willRenderTimeline(
PhabricatorApplicationTransactionView $timeline,
AphrontRequest $request) {
return $timeline;
}
} }

View file

@ -0,0 +1,211 @@
<?php
final class PhabricatorOwnersPackageTransaction
extends PhabricatorApplicationTransaction {
const TYPE_NAME = 'owners.name';
const TYPE_PRIMARY = 'owners.primary';
const TYPE_OWNERS = 'owners.owners';
const TYPE_AUDITING = 'owners.auditing';
const TYPE_DESCRIPTION = 'owners.description';
const TYPE_PATHS = 'owners.paths';
public function getApplicationName() {
return 'owners';
}
public function getApplicationTransactionType() {
return PhabricatorOwnersPackagePHIDType::TYPECONST;
}
public function getRequiredHandlePHIDs() {
$phids = parent::getRequiredHandlePHIDs();
$old = $this->getOldValue();
$new = $this->getNewValue();
switch ($this->getTransactionType()) {
case self::TYPE_PRIMARY:
if ($old) {
$phids[] = $old;
}
if ($new) {
$phids[] = $new;
}
break;
case self::TYPE_OWNERS:
$add = array_diff($new, $old);
foreach ($add as $phid) {
$phids[] = $phid;
}
$rem = array_diff($old, $new);
foreach ($rem as $phid) {
$phids[] = $phid;
}
break;
}
return $phids;
}
public function shouldHide() {
$old = $this->getOldValue();
$new = $this->getNewValue();
switch ($this->getTransactionType()) {
case self::TYPE_DESCRIPTION:
return ($old === null);
}
}
public function getTitle() {
$old = $this->getOldValue();
$new = $this->getNewValue();
$author_phid = $this->getAuthorPHID();
switch ($this->getTransactionType()) {
case self::TYPE_NAME:
if ($old === null) {
return pht(
'%s created this package.',
$this->renderHandleLink($author_phid));
} else {
return pht(
'%s renamed this package from "%s" to "%s".',
$this->renderHandleLink($author_phid),
$old,
$new);
}
case self::TYPE_PRIMARY:
return pht(
'%s changed the primary owner for this package from %s to %s.',
$this->renderHandleLink($author_phid),
$this->renderHandleLink($old),
$this->renderHandleLink($new));
case self::TYPE_OWNERS:
$add = array_diff($new, $old);
$rem = array_diff($old, $new);
if ($add && !$rem) {
return pht(
'%s added %s owner(s): %s.',
$this->renderHandleLink($author_phid),
count($add),
$this->renderHandleList($add));
} else if ($rem && !$add) {
return pht(
'%s removed %s owner(s): %s.',
$this->renderHandleLink($author_phid),
count($rem),
$this->renderHandleList($rem));
} else {
return pht(
'%s changed %s package owner(s), added %s: %s; removed %s: %s.',
$this->renderHandleLink($author_phid),
count($add) + count($rem),
count($add),
$this->renderHandleList($add),
count($rem),
$this->renderHandleList($rem));
}
case self::TYPE_AUDITING:
if ($new) {
return pht(
'%s enabled auditing for this package.',
$this->renderHandleLink($author_phid));
} else {
return pht(
'%s disabled auditing for this package.',
$this->renderHandleLink($author_phid));
}
case self::TYPE_DESCRIPTION:
return pht(
'%s updated the description for this package.',
$this->renderHandleLink($author_phid));
case self::TYPE_PATHS:
// TODO: Flesh this out.
return pht(
'%s updated paths for this package.',
$this->renderHandleLink($author_phid));
}
return parent::getTitle();
}
public function hasChangeDetails() {
switch ($this->getTransactionType()) {
case self::TYPE_DESCRIPTION:
return ($this->getOldValue() !== null);
case self::TYPE_PATHS:
return true;
}
return parent::hasChangeDetails();
}
public function renderChangeDetails(PhabricatorUser $viewer) {
switch ($this->getTransactionType()) {
case self::TYPE_DESCRIPTION:
$old = $this->getOldValue();
$new = $this->getNewValue();
return $this->renderTextCorpusChangeDetails(
$viewer,
$old,
$new);
case self::TYPE_PATHS:
$old = $this->getOldValue();
$new = $this->getNewValue();
$diffs = PhabricatorOwnersPath::getTransactionValueChanges($old, $new);
list($rem, $add) = $diffs;
$rows = array();
foreach ($rem as $ref) {
$rows[] = array(
'class' => 'diff-removed',
'change' => '-',
) + $ref;
}
foreach ($add as $ref) {
$rows[] = array(
'class' => 'diff-added',
'change' => '+',
) + $ref;
}
$rowc = array();
foreach ($rows as $key => $row) {
$rowc[] = $row['class'];
$rows[$key] = array(
$row['change'],
$row['excluded'] ? pht('Exclude') : pht('Include'),
$viewer->renderHandle($row['repositoryPHID']),
$row['path'],
);
}
$table = id(new AphrontTableView($rows))
->setRowClasses($rowc)
->setHeaders(
array(
null,
pht('Type'),
pht('Repository'),
pht('Path'),
))
->setColumnClasses(
array(
null,
null,
null,
'wide',
));
return $table;
}
return parent::renderChangeDetails($viewer);
}
}

View file

@ -22,4 +22,52 @@ final class PhabricatorOwnersPath extends PhabricatorOwnersDAO {
) + parent::getConfiguration(); ) + parent::getConfiguration();
} }
public static function newFromRef(array $ref) {
$path = new PhabricatorOwnersPath();
$path->repositoryPHID = $ref['repositoryPHID'];
$path->path = $ref['path'];
$path->excluded = $ref['excluded'];
return $path;
}
public function getRef() {
return array(
'repositoryPHID' => $this->getRepositoryPHID(),
'path' => $this->getPath(),
'excluded' => (int)$this->getExcluded(),
);
}
public static function getTransactionValueChanges(array $old, array $new) {
return array(
self::getTransactionValueDiff($old, $new),
self::getTransactionValueDiff($new, $old),
);
}
private static function getTransactionValueDiff(array $u, array $v) {
$set = self::getSetFromTransactionValue($v);
foreach ($u as $key => $ref) {
if (self::isRefInSet($ref, $set)) {
unset($u[$key]);
}
}
return $u;
}
public static function getSetFromTransactionValue(array $v) {
$set = array();
foreach ($v as $ref) {
$set[$ref['repositoryPHID']][$ref['path']][$ref['excluded']] = true;
}
return $set;
}
public static function isRefInSet(array $ref, array $set) {
return isset($set[$ref['repositoryPHID']][$ref['path']][$ref['excluded']]);
}
} }

View file

@ -1,100 +0,0 @@
<?php
final class ReleephProjectInfoConduitAPIMethod extends ReleephConduitAPIMethod {
public function getAPIMethodName() {
return 'releeph.projectinfo';
}
public function getMethodDescription() {
return pht(
'Fetch information about all Releeph projects '.
'for a given Arcanist project.');
}
protected function defineParamTypes() {
return array(
'arcProjectName' => 'optional string',
);
}
protected function defineReturnType() {
return 'dict<string, wild>';
}
protected function defineErrorTypes() {
return array(
'ERR_UNKNOWN_ARC' => pht(
"The given Arcanist project name doesn't exist in the ".
"installation of Phabricator you are accessing."),
);
}
protected function execute(ConduitAPIRequest $request) {
$arc_project_name = $request->getValue('arcProjectName');
if ($arc_project_name) {
$arc_project = id(new PhabricatorRepositoryArcanistProject())
->loadOneWhere('name = %s', $arc_project_name);
if (!$arc_project) {
throw id(new ConduitException('ERR_UNKNOWN_ARC'))
->setErrorDescription(
pht(
"Unknown Arcanist project '%s': ".
"are you using the correct Conduit URI?",
$arc_project_name));
}
$releeph_projects = id(new ReleephProject())
->loadAllWhere('arcanistProjectID = %d', $arc_project->getID());
} else {
$releeph_projects = id(new ReleephProject())->loadAll();
}
$releeph_projects = mfilter($releeph_projects, 'getIsActive');
$result = array();
foreach ($releeph_projects as $releeph_project) {
$selector = $releeph_project->getReleephFieldSelector();
$fields = $selector->getFieldSpecifications();
$fields_info = array();
foreach ($fields as $field) {
$field->setReleephProject($releeph_project);
if ($field->isEditable()) {
$key = $field->getKeyForConduit();
$fields_info[$key] = array(
'class' => get_class($field),
'name' => $field->getName(),
'key' => $key,
'arcHelp' => $field->renderHelpForArcanist(),
);
}
}
$releeph_branches = mfilter(
id(new ReleephBranch())
->loadAllWhere('releephProjectID = %d', $releeph_project->getID()),
'getIsActive');
$releeph_branches_struct = array();
foreach ($releeph_branches as $branch) {
$releeph_branches_struct[] = array(
'branchName' => $branch->getName(),
'projectName' => $releeph_project->getName(),
'projectPHID' => $releeph_project->getPHID(),
'branchPHID' => $branch->getPHID(),
);
}
$result[] = array(
'projectName' => $releeph_project->getName(),
'projectPHID' => $releeph_project->getPHID(),
'branches' => $releeph_branches_struct,
'fields' => $fields_info,
);
}
return $result;
}
}

View file

@ -13,10 +13,10 @@ final class ReleephBranchNamePreviewController
$template = ReleephBranchTemplate::getDefaultTemplate(); $template = ReleephBranchTemplate::getDefaultTemplate();
} }
$arc_project_id = $request->getInt('arcProjectID'); $repository_phid = $request->getInt('repositoryPHID');
$fake_commit_handle = $fake_commit_handle =
ReleephBranchTemplate::getFakeCommitHandleFor( ReleephBranchTemplate::getFakeCommitHandleFor(
$arc_project_id, $repository_phid,
$request->getUser()); $request->getUser());
list($name, $errors) = id(new ReleephBranchTemplate()) list($name, $errors) = id(new ReleephBranchTemplate())

View file

@ -6,9 +6,7 @@ final class ReleephProductCreateController extends ReleephProductController {
$request = $this->getRequest(); $request = $this->getRequest();
$name = trim($request->getStr('name')); $name = trim($request->getStr('name'));
$trunk_branch = trim($request->getStr('trunkBranch')); $trunk_branch = trim($request->getStr('trunkBranch'));
$arc_pr_id = $request->getInt('arcPrID'); $repository_phid = $request->getStr('repositoryPHID');
$arc_projects = $this->loadArcProjects();
$e_name = true; $e_name = true;
$e_trunk_branch = true; $e_trunk_branch = true;
@ -27,14 +25,10 @@ final class ReleephProductCreateController extends ReleephProductController {
'You must specify which branch you will be picking from.'); 'You must specify which branch you will be picking from.');
} }
$arc_project = $arc_projects[$arc_pr_id]; $pr_repository = id(new PhabricatorRepositoryQuery())
$pr_repository = null; ->setViewer($request->getUser())
if ($arc_project->getRepositoryID()) { ->withPHIDs(array($repository_phid))
$pr_repository = id(new PhabricatorRepositoryQuery()) ->executeOne();
->setViewer($request->getUser())
->withIDs(array($arc_project->getRepositoryID()))
->executeOne();
}
if (!$errors) { if (!$errors) {
@ -42,7 +36,6 @@ final class ReleephProductCreateController extends ReleephProductController {
->setName($name) ->setName($name)
->setTrunkBranch($trunk_branch) ->setTrunkBranch($trunk_branch)
->setRepositoryPHID($pr_repository->getPHID()) ->setRepositoryPHID($pr_repository->getPHID())
->setArcanistProjectID($arc_project->getID())
->setCreatedByUserPHID($request->getUser()->getPHID()) ->setCreatedByUserPHID($request->getUser()->getPHID())
->setIsActive(1); ->setIsActive(1);
@ -58,7 +51,7 @@ final class ReleephProductCreateController extends ReleephProductController {
} }
} }
$arc_project_options = $this->getArcProjectSelectOptions($arc_projects); $repo_options = $this->getRepositorySelectOptions();
$product_name_input = id(new AphrontFormTextControl()) $product_name_input = id(new AphrontFormTextControl())
->setLabel(pht('Name')) ->setLabel(pht('Name'))
@ -68,32 +61,23 @@ final class ReleephProductCreateController extends ReleephProductController {
->setError($e_name) ->setError($e_name)
->setCaption(pht('A name like "Thrift" but not "Thrift releases".')); ->setCaption(pht('A name like "Thrift" but not "Thrift releases".'));
$arc_project_input = id(new AphrontFormSelectControl()) $repository_input = id(new AphrontFormSelectControl())
->setLabel(pht('Arc Project')) ->setLabel(pht('Repository'))
->setName('arcPrID') ->setName('repositoryPHID')
->setValue($arc_pr_id) ->setValue($repository_phid)
->setCaption(pht( ->setOptions($repo_options);
"If your Arc project isn't listed, associate it with a repository %s.",
phutil_tag(
'a',
array(
'href' => '/repository/',
'target' => '_blank',
),
'here')))
->setOptions($arc_project_options);
$branch_name_preview = id(new ReleephBranchPreviewView()) $branch_name_preview = id(new ReleephBranchPreviewView())
->setLabel(pht('Example Branch')) ->setLabel(pht('Example Branch'))
->addControl('projectName', $product_name_input) ->addControl('projectName', $product_name_input)
->addControl('arcProjectID', $arc_project_input) ->addControl('repositoryPHID', $repository_input)
->addStatic('template', '') ->addStatic('template', '')
->addStatic('isSymbolic', false); ->addStatic('isSymbolic', false);
$form = id(new AphrontFormView()) $form = id(new AphrontFormView())
->setUser($request->getUser()) ->setUser($request->getUser())
->appendChild($product_name_input) ->appendChild($product_name_input)
->appendChild($arc_project_input) ->appendChild($repository_input)
->appendChild( ->appendChild(
id(new AphrontFormTextControl()) id(new AphrontFormTextControl())
->setLabel(pht('Trunk')) ->setLabel(pht('Trunk'))
@ -126,43 +110,23 @@ final class ReleephProductCreateController extends ReleephProductController {
)); ));
} }
private function loadArcProjects() { private function getRepositorySelectOptions() {
$viewer = $this->getRequest()->getUser(); $repos = id(new PhabricatorRepositoryQuery())
->setViewer($this->getRequest()->getUser())
$projects = id(new PhabricatorRepositoryArcanistProjectQuery())
->setViewer($viewer)
->needRepositories(true)
->execute(); ->execute();
$projects = mfilter($projects, 'getRepository'); $repos = msort($repos, 'getName');
$projects = msort($projects, 'getName');
return $projects;
}
private function getArcProjectSelectOptions(array $arc_projects) {
assert_instances_of($arc_projects, 'PhabricatorRepositoryArcanistProject');
$repos = mpull($arc_projects, 'getRepository');
$repos = mpull($repos, null, 'getID'); $repos = mpull($repos, null, 'getID');
$groups = array();
foreach ($arc_projects as $arc_project) {
$id = $arc_project->getID();
$repo_id = $arc_project->getRepository()->getID();
$groups[$repo_id][$id] = $arc_project->getName();
}
$choices = array(); $choices = array();
foreach ($groups as $repo_id => $group) {
$repo_name = $repos[$repo_id]->getName(); foreach ($repos as $repo_id => $repo) {
$callsign = $repos[$repo_id]->getCallsign(); $repo_name = $repo->getName();
$name = "r{$callsign} ({$repo_name})"; $callsign = $repo->getCallsign();
$choices[$name] = $group; $choices[$repo->getPHID()] = "r{$callsign} ({$repo_name})";
} }
ksort($choices); ksort($choices);
return $choices; return $choices;
} }

View file

@ -15,7 +15,6 @@ final class ReleephProductEditController extends ReleephProductController {
$product = id(new ReleephProductQuery()) $product = id(new ReleephProductQuery())
->setViewer($viewer) ->setViewer($viewer)
->withIDs(array($this->productID)) ->withIDs(array($this->productID))
->needArcanistProjects(true)
->requireCapabilities( ->requireCapabilities(
array( array(
PhabricatorPolicyCapability::CAN_VIEW, PhabricatorPolicyCapability::CAN_VIEW,
@ -48,7 +47,7 @@ final class ReleephProductEditController extends ReleephProductController {
$test_paths = $product->getDetail('testPaths', array()); $test_paths = $product->getDetail('testPaths', array());
} }
$arc_project_id = $product->getArcanistProjectID(); $repository_phid = $product->getRepositoryPHID();
if ($request->isFormPost()) { if ($request->isFormPost()) {
$pusher_phids = $request->getArr('pushers'); $pusher_phids = $request->getArr('pushers');
@ -92,8 +91,9 @@ final class ReleephProductEditController extends ReleephProductController {
->setDetail('branchTemplate', $branch_template) ->setDetail('branchTemplate', $branch_template)
->setDetail('testPaths', $test_paths); ->setDetail('testPaths', $test_paths);
$fake_commit_handle = $fake_commit_handle = ReleephBranchTemplate::getFakeCommitHandleFor(
ReleephBranchTemplate::getFakeCommitHandleFor($arc_project_id, $viewer); $repository_phid,
$viewer);
if ($branch_template) { if ($branch_template) {
list($branch_name, $template_errors) = id(new ReleephBranchTemplate()) list($branch_name, $template_errors) = id(new ReleephBranchTemplate())
@ -136,9 +136,9 @@ final class ReleephProductEditController extends ReleephProductController {
$product->getRepository()->getName())) $product->getRepository()->getName()))
->appendChild( ->appendChild(
id(new AphrontFormStaticControl()) id(new AphrontFormStaticControl())
->setLabel(pht('Arc Project')) ->setLabel(pht('Repository'))
->setValue( ->setValue(
$product->getArcanistProject()->getName())) $product->getRepository()->getName()))
->appendChild( ->appendChild(
id(new AphrontFormStaticControl()) id(new AphrontFormStaticControl())
->setLabel(pht('Releeph Project PHID')) ->setLabel(pht('Releeph Project PHID'))
@ -179,7 +179,7 @@ final class ReleephProductEditController extends ReleephProductController {
$branch_template_preview = id(new ReleephBranchPreviewView()) $branch_template_preview = id(new ReleephBranchPreviewView())
->setLabel(pht('Preview')) ->setLabel(pht('Preview'))
->addControl('template', $branch_template_input) ->addControl('template', $branch_template_input)
->addStatic('arcProjectID', $arc_project_id) ->addStatic('repositoryPHID', $repository_phid)
->addStatic('isSymbolic', false) ->addStatic('isSymbolic', false)
->addStatic('projectName', $product->getName()); ->addStatic('projectName', $product->getName());

View file

@ -25,19 +25,18 @@ final class ReleephRequestDifferentialCreateController
} }
$this->revision = $diff_rev; $this->revision = $diff_rev;
$arc_project = id(new PhabricatorRepositoryArcanistProject()) $repository = $this->revision->getRepository();
->loadOneWhere('phid = %s', $this->revision->getArcanistProjectPHID());
$projects = id(new ReleephProject())->loadAllWhere( $projects = id(new ReleephProject())->loadAllWhere(
'arcanistProjectID = %d AND isActive = 1', 'repositoryPHID = %s AND isActive = 1',
$arc_project->getID()); $repository->getPHID());
if (!$projects) { if (!$projects) {
throw new Exception( throw new Exception(
pht( pht(
"%s belongs to the '%s' Arcanist project, ". "%s belongs to the '%s' repository, ".
"which is not part of any Releeph project!", "which is not part of any Releeph project!",
'D'.$this->revision->getID(), 'D'.$this->revision->getID(),
$arc_project->getName())); $repository->getMonogram()));
} }
$branches = id(new ReleephBranch())->loadAllWhere( $branches = id(new ReleephBranch())->loadAllWhere(

View file

@ -8,8 +8,6 @@ final class ReleephProductQuery
private $phids; private $phids;
private $repositoryPHIDs; private $repositoryPHIDs;
private $needArcanistProjects;
const ORDER_ID = 'order-id'; const ORDER_ID = 'order-id';
const ORDER_NAME = 'order-name'; const ORDER_NAME = 'order-name';
@ -47,11 +45,6 @@ final class ReleephProductQuery
return $this; return $this;
} }
public function needArcanistProjects($need) {
$this->needArcanistProjects = $need;
return $this;
}
protected function loadPage() { protected function loadPage() {
$table = new ReleephProject(); $table = new ReleephProject();
$conn_r = $table->establishConnection('r'); $conn_r = $table->establishConnection('r');
@ -90,27 +83,6 @@ final class ReleephProductQuery
return $projects; return $projects;
} }
protected function didFilterPage(array $products) {
if ($this->needArcanistProjects) {
$project_ids = array_filter(mpull($products, 'getArcanistProjectID'));
if ($project_ids) {
$projects = id(new PhabricatorRepositoryArcanistProject())
->loadAllWhere('id IN (%Ld)', $project_ids);
$projects = mpull($projects, null, 'getID');
} else {
$projects = array();
}
foreach ($products as $product) {
$project_id = $product->getArcanistProjectID();
$project = idx($projects, $project_id);
$product->attachArcanistProject($project);
}
}
return $products;
}
protected function buildWhereClause(AphrontDatabaseConnection $conn_r) { protected function buildWhereClause(AphrontDatabaseConnection $conn_r) {
$where = array(); $where = array();

View file

@ -21,8 +21,7 @@ final class ReleephProductSearchEngine
public function buildQueryFromSavedQuery(PhabricatorSavedQuery $saved) { public function buildQueryFromSavedQuery(PhabricatorSavedQuery $saved) {
$query = id(new ReleephProductQuery()) $query = id(new ReleephProductQuery())
->setOrder(ReleephProductQuery::ORDER_NAME) ->setOrder(ReleephProductQuery::ORDER_NAME);
->needArcanistProjects(true);
$active = $saved->getParameter('active'); $active = $saved->getParameter('active');
$value = idx($this->getActiveValues(), $active); $value = idx($this->getActiveValues(), $active);
@ -119,11 +118,6 @@ final class ReleephProductSearchEngine
), ),
'r'.$repo->getCallsign())); 'r'.$repo->getCallsign()));
$arc = $product->getArcanistProject();
if ($arc) {
$item->addAttribute($arc->getName());
}
$list->addItem($item); $list->addItem($item);
} }

View file

@ -35,6 +35,7 @@ final class ReleephProject extends ReleephDAO
'name' => 'text128', 'name' => 'text128',
'trunkBranch' => 'text255', 'trunkBranch' => 'text255',
'isActive' => 'bool', 'isActive' => 'bool',
'arcanistProjectID' => 'id?',
), ),
self::CONFIG_KEY_SCHEMA => array( self::CONFIG_KEY_SCHEMA => array(
'projectName' => array( 'projectName' => array(

View file

@ -24,7 +24,7 @@ final class ReleephBranchPreviewView extends AphrontFormControl {
protected function renderInput() { protected function renderInput() {
static $required_params = array( static $required_params = array(
'arcProjectID', 'repositoryPHID',
'projectName', 'projectName',
'isSymbolic', 'isSymbolic',
'template', 'template',
@ -43,9 +43,9 @@ final class ReleephBranchPreviewView extends AphrontFormControl {
$output_id = celerity_generate_unique_node_id(); $output_id = celerity_generate_unique_node_id();
Javelin::initBehavior('releeph-preview-branch', array( Javelin::initBehavior('releeph-preview-branch', array(
'uri' => '/releeph/branch/preview/', 'uri' => '/releeph/branch/preview/',
'outputID' => $output_id, 'outputID' => $output_id,
'params' => array( 'params' => array(
'static' => $this->statics, 'static' => $this->statics,
'dynamic' => $this->dynamics, 'dynamic' => $this->dynamics,
), ),

View file

@ -20,25 +20,14 @@ final class ReleephBranchTemplate {
} }
public static function getFakeCommitHandleFor( public static function getFakeCommitHandleFor(
$arc_project_id, $repository_phid,
PhabricatorUser $viewer) { PhabricatorUser $viewer) {
$arc_project = id(new PhabricatorRepositoryArcanistProject()) $repository = id(new PhabricatorRepositoryQuery())
->load($arc_project_id); ->setViewer($viewer)
if (!$arc_project) { ->withPHIDs(array($repository_phid))
throw new Exception( ->executeOne();
pht(
"No Arc project found with id '%s'!",
$arc_project_id));
}
$repository = null;
if ($arc_project->getRepositoryID()) {
$repository = id(new PhabricatorRepositoryQuery())
->setViewer($viewer)
->withIDs(array($arc_project->getRepositoryID()))
->executeOne();
}
$fake_handle = 'SOFAKE'; $fake_handle = 'SOFAKE';
if ($repository) { if ($repository) {
$fake_handle = id(new PhabricatorObjectHandle()) $fake_handle = id(new PhabricatorObjectHandle())

View file

@ -30,10 +30,6 @@ final class PhabricatorRepositoriesApplication extends PhabricatorApplication {
return array( return array(
'/repository/' => array( '/repository/' => array(
'' => 'PhabricatorRepositoryListController', '' => 'PhabricatorRepositoryListController',
'project/edit/(?P<id>[1-9]\d*)/'
=> 'PhabricatorRepositoryArcanistProjectEditController',
'project/delete/(?P<id>[1-9]\d*)/'
=> 'PhabricatorRepositoryArcanistProjectDeleteController',
), ),
); );
} }

View file

@ -1,42 +0,0 @@
<?php
final class PhabricatorRepositoryArcanistProjectDeleteController
extends PhabricatorRepositoryController {
private $id;
public function willProcessRequest(array $data) {
$this->id = $data['id'];
}
public function processRequest() {
$arc_project =
id(new PhabricatorRepositoryArcanistProject())->load($this->id);
if (!$arc_project) {
return new Aphront404Response();
}
$request = $this->getRequest();
if ($request->isDialogFormPost()) {
$arc_project->delete();
return id(new AphrontRedirectResponse())->setURI('/repository/');
}
$dialog = new AphrontDialogView();
$dialog
->setUser($request->getUser())
->setTitle(pht('Really delete this arcanist project?'))
->appendChild(
pht(
'Really delete the "%s" arcanist project? '.
'This operation can not be undone.',
$arc_project->getName()))
->setSubmitURI('/repository/project/delete/'.$this->id.'/')
->addSubmitButton(pht('Delete Arcanist Project'))
->addCancelButton('/repository/');
return id(new AphrontDialogResponse())->setDialog($dialog);
}
}

View file

@ -1,85 +0,0 @@
<?php
final class PhabricatorRepositoryArcanistProjectEditController
extends PhabricatorRepositoryController {
private $id;
public function willProcessRequest(array $data) {
$this->id = $data['id'];
}
public function processRequest() {
$request = $this->getRequest();
$user = $request->getUser();
$project = id(new PhabricatorRepositoryArcanistProject())->load($this->id);
if (!$project) {
return new Aphront404Response();
}
$repositories = id(new PhabricatorRepositoryQuery())
->setViewer($user)
->execute();
$repos = array(
0 => 'None',
);
foreach ($repositories as $repository) {
$callsign = $repository->getCallsign();
$name = $repository->getname();
$repos[$repository->getID()] = "r{$callsign} ({$name})";
}
// note "None" will still be first thanks to 'r' prefix
asort($repos);
if ($request->isFormPost()) {
$repo_id = $request->getInt('repository', 0);
if (isset($repos[$repo_id])) {
$project->setRepositoryID($repo_id);
$project->save();
return id(new AphrontRedirectResponse())
->setURI('/repository/');
}
}
$form = id(new AphrontFormView())
->setUser($user)
->appendChild(
id(new AphrontFormStaticControl())
->setLabel(pht('Name'))
->setValue($project->getName()))
->appendChild(
id(new AphrontFormStaticControl())
->setLabel('PHID')
->setValue($project->getPHID()))
->appendChild(
id(new AphrontFormSelectControl())
->setLabel(pht('Repository'))
->setOptions($repos)
->setName('repository')
->setValue($project->getRepositoryID()))
->appendChild(
id(new AphrontFormSubmitControl())
->addCancelButton('/repository/')
->setValue(pht('Save')));
$panel = new PHUIObjectBoxView();
$panel->setHeaderText(pht('Edit Arcanist Project'));
$panel->setForm($form);
$crumbs = $this->buildApplicationCrumbs();
$crumbs->addTextCrumb(pht('Edit Project'));
return $this->buildApplicationPage(
array(
$crumbs,
$panel,
),
array(
'title' => pht('Edit Project'),
));
}
}

View file

@ -85,68 +85,6 @@ final class PhabricatorRepositoryListController
$panel->setHeader($header); $panel->setHeader($header);
$panel->setTable($table); $panel->setTable($table);
$projects = id(new PhabricatorRepositoryArcanistProject())->loadAll();
$rows = array();
foreach ($projects as $project) {
$repo = idx($repos, $project->getRepositoryID());
if ($repo) {
$repo_name = $repo->getName();
} else {
$repo_name = '-';
}
$rows[] = array(
$project->getName(),
$repo_name,
phutil_tag(
'a',
array(
'href' => '/repository/project/edit/'.$project->getID().'/',
'class' => 'button grey small',
),
pht('Edit')),
javelin_tag(
'a',
array(
'href' => '/repository/project/delete/'.$project->getID().'/',
'class' => 'button grey small',
'sigil' => 'workflow',
),
pht('Delete')),
);
}
$project_table = new AphrontTableView($rows);
$project_table->setNoDataString(pht('No Arcanist Projects'));
$project_table->setHeaders(
array(
pht('Project ID'),
pht('Repository'),
'',
'',
));
$project_table->setColumnClasses(
array(
'',
'wide',
'action',
'action',
));
$project_table->setColumnVisibility(
array(
true,
true,
$is_admin,
$is_admin,
));
$project_panel = new PHUIObjectBoxView();
$project_panel->setHeaderText(pht('Arcanist Projects'));
$project_panel->setTable($project_table);
$crumbs = $this->buildApplicationCrumbs(); $crumbs = $this->buildApplicationCrumbs();
$crumbs->addTextCrumb(pht('Repository List')); $crumbs->addTextCrumb(pht('Repository List'));
@ -154,7 +92,6 @@ final class PhabricatorRepositoryListController
array( array(
$crumbs, $crumbs,
$panel, $panel,
$project_panel,
), ),
array( array(
'title' => pht('Repository List'), 'title' => pht('Repository List'),

View file

@ -7,13 +7,22 @@ final class PhabricatorCommitBranchesField
return 'diffusion:branches'; return 'diffusion:branches';
} }
public function shouldAppearInApplicationTransactions() { public function getFieldName() {
return pht('Branches');
}
public function getFieldDescription() {
return pht('Shows branches a commit appears on in email.');
}
public function shouldAppearInTransactionMail() {
return true; return true;
} }
public function buildApplicationTransactionMailBody( public function updateTransactionMailBody(
PhabricatorApplicationTransaction $xaction, PhabricatorMetaMTAMailBody $body,
PhabricatorMetaMTAMailBody $body) { PhabricatorApplicationTransactionEditor $editor,
array $xactions) {
$params = array( $params = array(
'contains' => $this->getObject()->getCommitIdentifier(), 'contains' => $this->getObject()->getCommitIdentifier(),

View file

@ -0,0 +1,74 @@
<?php
final class PhabricatorCommitMergedCommitsField
extends PhabricatorCommitCustomField {
public function getFieldKey() {
return 'diffusion:mergedcommits';
}
public function getFieldName() {
return pht('Merged Commits');
}
public function getFieldDescription() {
return pht('For merge commits, shows merged changes in email.');
}
public function shouldDisableByDefault() {
return true;
}
public function shouldAppearInTransactionMail() {
return true;
}
public function updateTransactionMailBody(
PhabricatorMetaMTAMailBody $body,
PhabricatorApplicationTransactionEditor $editor,
array $xactions) {
// Put all the merged commits info int the mail body if this is a merge
$merges_caption = '';
// TODO: Make this limit configurable after T6030
$limit = 50;
$commit = $this->getObject();
try {
$merges = DiffusionPathChange::newFromConduit(
id(new ConduitCall('diffusion.mergedcommitsquery', array(
'commit' => $commit->getCommitIdentifier(),
'limit' => $limit + 1,
'callsign' => $commit->getRepository()->getCallsign(),
)))
->setUser($this->getViewer())
->execute());
if (count($merges) > $limit) {
$merges = array_slice($merges, 0, $limit);
$merges_caption =
pht("This commit merges more than %d changes. Only the first ".
"%d are shown.\n", $limit, $limit);
}
if ($merges) {
$merge_commits = array();
foreach ($merges as $merge) {
$merge_commits[] = $merge->getAuthorName().
': '.
$merge->getSummary();
}
$body->addTextSection(
pht('MERGED COMMITS'),
$merges_caption.implode("\n", $merge_commits));
}
} catch (ConduitException $ex) {
// Log the exception into the email body
$body->addTextSection(
pht('MERGED COMMITS'),
pht('Error generating merged commits: ').$ex->getMessage());
}
}
}

View file

@ -0,0 +1,38 @@
<?php
final class PhabricatorCommitRepositoryField
extends PhabricatorCommitCustomField {
public function getFieldKey() {
return 'diffusion:repository';
}
public function getFieldName() {
return pht('Repository');
}
public function getFieldDescription() {
return pht('Shows repository in email.');
}
public function shouldDisableByDefault() {
return true;
}
public function shouldAppearInTransactionMail() {
return true;
}
public function updateTransactionMailBody(
PhabricatorMetaMTAMailBody $body,
PhabricatorApplicationTransactionEditor $editor,
array $xactions) {
$repository = $this->getObject()->getRepository();
$body->addTextSection(
pht('REPOSITORY'),
$repository->getMonogram().' '.$repository->getName());
}
}

View file

@ -7,13 +7,22 @@ final class PhabricatorCommitTagsField
return 'diffusion:tags'; return 'diffusion:tags';
} }
public function shouldAppearInApplicationTransactions() { public function getFieldName() {
return pht('Tags');
}
public function getFieldDescription() {
return pht('Shows commit tags in email.');
}
public function shouldAppearInTransactionMail() {
return true; return true;
} }
public function buildApplicationTransactionMailBody( public function updateTransactionMailBody(
PhabricatorApplicationTransaction $xaction, PhabricatorMetaMTAMailBody $body,
PhabricatorMetaMTAMailBody $body) { PhabricatorApplicationTransactionEditor $editor,
array $xactions) {
$params = array( $params = array(
'commit' => $this->getObject()->getCommitIdentifier(), 'commit' => $this->getObject()->getCommitIdentifier(),

View file

@ -43,6 +43,7 @@ final class PhabricatorRepositoryEditor
$types[] = PhabricatorRepositoryTransaction::TYPE_SERVICE; $types[] = PhabricatorRepositoryTransaction::TYPE_SERVICE;
$types[] = PhabricatorRepositoryTransaction::TYPE_SYMBOLS_LANGUAGE; $types[] = PhabricatorRepositoryTransaction::TYPE_SYMBOLS_LANGUAGE;
$types[] = PhabricatorRepositoryTransaction::TYPE_SYMBOLS_SOURCES; $types[] = PhabricatorRepositoryTransaction::TYPE_SYMBOLS_SOURCES;
$types[] = PhabricatorRepositoryTransaction::TYPE_STAGING_URI;
$types[] = PhabricatorTransactions::TYPE_EDGE; $types[] = PhabricatorTransactions::TYPE_EDGE;
$types[] = PhabricatorTransactions::TYPE_VIEW_POLICY; $types[] = PhabricatorTransactions::TYPE_VIEW_POLICY;
@ -104,6 +105,8 @@ final class PhabricatorRepositoryEditor
return $object->getSymbolLanguages(); return $object->getSymbolLanguages();
case PhabricatorRepositoryTransaction::TYPE_SYMBOLS_SOURCES: case PhabricatorRepositoryTransaction::TYPE_SYMBOLS_SOURCES:
return $object->getSymbolSources(); return $object->getSymbolSources();
case PhabricatorRepositoryTransaction::TYPE_STAGING_URI:
return $object->getDetail('staging-uri');
} }
} }
@ -139,6 +142,7 @@ final class PhabricatorRepositoryEditor
case PhabricatorRepositoryTransaction::TYPE_SERVICE: case PhabricatorRepositoryTransaction::TYPE_SERVICE:
case PhabricatorRepositoryTransaction::TYPE_SYMBOLS_LANGUAGE: case PhabricatorRepositoryTransaction::TYPE_SYMBOLS_LANGUAGE:
case PhabricatorRepositoryTransaction::TYPE_SYMBOLS_SOURCES: case PhabricatorRepositoryTransaction::TYPE_SYMBOLS_SOURCES:
case PhabricatorRepositoryTransaction::TYPE_STAGING_URI:
return $xaction->getNewValue(); return $xaction->getNewValue();
case PhabricatorRepositoryTransaction::TYPE_NOTIFY: case PhabricatorRepositoryTransaction::TYPE_NOTIFY:
case PhabricatorRepositoryTransaction::TYPE_AUTOCLOSE: case PhabricatorRepositoryTransaction::TYPE_AUTOCLOSE:
@ -219,6 +223,9 @@ final class PhabricatorRepositoryEditor
case PhabricatorRepositoryTransaction::TYPE_SYMBOLS_SOURCES: case PhabricatorRepositoryTransaction::TYPE_SYMBOLS_SOURCES:
$object->setDetail('symbol-sources', $xaction->getNewValue()); $object->setDetail('symbol-sources', $xaction->getNewValue());
return; return;
case PhabricatorRepositoryTransaction::TYPE_STAGING_URI:
$object->setDetail('staging-uri', $xaction->getNewValue());
return;
case PhabricatorRepositoryTransaction::TYPE_ENCODING: case PhabricatorRepositoryTransaction::TYPE_ENCODING:
// Make sure the encoding is valid by converting to UTF-8. This tests // Make sure the encoding is valid by converting to UTF-8. This tests
// that the user has mbstring installed, and also that they didn't type // that the user has mbstring installed, and also that they didn't type
@ -330,6 +337,7 @@ final class PhabricatorRepositoryEditor
case PhabricatorRepositoryTransaction::TYPE_SERVICE: case PhabricatorRepositoryTransaction::TYPE_SERVICE:
case PhabricatorRepositoryTransaction::TYPE_SYMBOLS_SOURCES: case PhabricatorRepositoryTransaction::TYPE_SYMBOLS_SOURCES:
case PhabricatorRepositoryTransaction::TYPE_SYMBOLS_LANGUAGE: case PhabricatorRepositoryTransaction::TYPE_SYMBOLS_LANGUAGE:
case PhabricatorRepositoryTransaction::TYPE_STAGING_URI:
PhabricatorPolicyFilter::requireCapability( PhabricatorPolicyFilter::requireCapability(
$this->requireActor(), $this->requireActor(),
$object, $object,

View file

@ -241,8 +241,8 @@ final class PhabricatorRepositoryManagementReparseWorkflow
'**NOTE**: This script will queue tasks to reparse the data. Once the '. '**NOTE**: This script will queue tasks to reparse the data. Once the '.
'tasks have been queued, you need to run Taskmaster daemons to '. 'tasks have been queued, you need to run Taskmaster daemons to '.
'execute them.'."\n\n". 'execute them.'."\n\n".
"QUEUEING TASKS (%d Commits):", "QUEUEING TASKS (%s Commits):",
number_format(count($commits)))); new PhutilNumber(count($commits))));
} }
$progress = new PhutilConsoleProgressBar(); $progress = new PhutilConsoleProgressBar();

View file

@ -135,6 +135,12 @@ final class PhabricatorRepository extends PhabricatorRepositoryDAO
'isActive' => $this->isTracked(), 'isActive' => $this->isTracked(),
'isHosted' => $this->isHosted(), 'isHosted' => $this->isHosted(),
'isImporting' => $this->isImporting(), 'isImporting' => $this->isImporting(),
'encoding' => $this->getDetail('encoding'),
'staging' => array(
'supported' => $this->supportsStaging(),
'prefix' => 'phabricator',
'uri' => $this->getStagingURI(),
),
); );
} }
@ -1796,6 +1802,22 @@ final class PhabricatorRepository extends PhabricatorRepositoryDAO
} }
/* -( Staging )-------------------------------------------------------------*/
public function supportsStaging() {
return $this->isGit();
}
public function getStagingURI() {
if (!$this->supportsStaging()) {
return null;
}
return $this->getDetail('staging-uri', null);
}
/* -( PhabricatorApplicationTransactionInterface )------------------------- */ /* -( PhabricatorApplicationTransactionInterface )------------------------- */

View file

@ -349,9 +349,7 @@ final class PhabricatorRepositoryCommit
public function getCustomFieldSpecificationForRole($role) { public function getCustomFieldSpecificationForRole($role) {
// TODO: We could make this configurable eventually, but just use the return PhabricatorEnv::getEnvConfig('diffusion.fields');
// defaults for now.
return array();
} }
public function getCustomFieldBaseClass() { public function getCustomFieldBaseClass() {

View file

@ -27,6 +27,7 @@ final class PhabricatorRepositoryTransaction
const TYPE_SERVICE = 'repo:service'; const TYPE_SERVICE = 'repo:service';
const TYPE_SYMBOLS_SOURCES = 'repo:symbol-source'; const TYPE_SYMBOLS_SOURCES = 'repo:symbol-source';
const TYPE_SYMBOLS_LANGUAGE = 'repo:symbol-language'; const TYPE_SYMBOLS_LANGUAGE = 'repo:symbol-language';
const TYPE_STAGING_URI = 'repo:staging-uri';
// TODO: Clean up these legacy transaction types. // TODO: Clean up these legacy transaction types.
const TYPE_SSH_LOGIN = 'repo:ssh-login'; const TYPE_SSH_LOGIN = 'repo:ssh-login';
@ -412,9 +413,29 @@ final class PhabricatorRepositoryTransaction
case self::TYPE_SYMBOLS_LANGUAGE: case self::TYPE_SYMBOLS_LANGUAGE:
return pht('%s changed indexed languages from %s to %s.', return pht('%s changed indexed languages from %s to %s.',
$this->renderHandleLink($author_phid),
$old ? implode(', ', $old) : pht('Any'),
$new ? implode(', ', $new) : pht('Any'));
case self::TYPE_STAGING_URI:
if (!$old) {
return pht(
'%s set "%s" as the staging area for this repository.',
$this->renderHandleLink($author_phid), $this->renderHandleLink($author_phid),
$old ? implode(', ', $old) : pht('Any'), $new);
$new ? implode(', ', $new) : pht('Any')); } else if (!$new) {
return pht(
'%s removed "%s" as the staging area for this repository.',
$this->renderHandleLink($author_phid),
$old);
} else {
return pht(
'%s changed the staging area for this repository from '.
'"%s" to "%s".',
$this->renderHandleLink($author_phid),
$old,
$new);
}
} }
return parent::getTitle(); return parent::getTitle();

View file

@ -1,108 +0,0 @@
<?php
final class PhabricatorOwnersPackagePathValidator {
/*
* If a file/directory was moved the paths in owners package become stale.
* This method updates the stale paths in the owners packages to their new
* paths.
*/
public static function updateOwnersPackagePaths(
PhabricatorRepositoryCommit $commit,
PhabricatorUser $actor) {
$repository = id(new PhabricatorRepositoryQuery())
->setViewer($actor)
->withIDs(array($commit->getRepositoryID()))
->executeOne();
if (!$repository) {
return;
}
$changes = self::loadDiffusionChangesForCommit(
$repository,
$commit,
$actor);
if (!$changes) {
return;
}
$move_map = array();
foreach ($changes as $change) {
if ($change->getChangeType() == DifferentialChangeType::TYPE_MOVE_HERE) {
$from_path = '/'.$change->getTargetPath();
$to_path = '/'.$change->getPath();
if ($change->getFileType() == DifferentialChangeType::FILE_DIRECTORY) {
$to_path = $to_path.'/';
$from_path = $from_path.'/';
}
$move_map[$from_path] = $to_path;
}
}
if ($move_map) {
self::updateAffectedPackages($repository, $move_map);
}
}
private static function updateAffectedPackages($repository, array $move_map) {
$paths = array_keys($move_map);
if ($paths) {
$packages = PhabricatorOwnersPackage::loadAffectedPackages($repository,
$paths);
foreach ($packages as $package) {
self::updatePackagePaths($package, $move_map);
}
}
}
private static function updatePackagePaths($package, array $move_map) {
$paths = array_keys($move_map);
$pkg_paths = $package->loadPaths();
$new_paths = array();
foreach ($pkg_paths as $pkg_path) {
$path_changed = false;
foreach ($paths as $old_path) {
if (strncmp($pkg_path->getPath(), $old_path, strlen($old_path)) === 0) {
$new_paths[] = array (
'packageID' => $package->getID(),
'repositoryPHID' => $pkg_path->getRepositoryPHID(),
'path' => str_replace($pkg_path->getPath(), $old_path,
$move_map[$old_path]),
);
$path_changed = true;
}
}
if (!$path_changed) {
$new_paths[] = array (
'packageID' => $package->getID(),
'repositoryPHID' => $pkg_path->getRepositoryPHID(),
'path' => $pkg_path->getPath(),
);
}
}
if ($new_paths) {
$package->attachOldPrimaryOwnerPHID($package->getPrimaryOwnerPHID());
$package->attachUnsavedPaths($new_paths);
$package->save(); // save the changes and notify the owners.
}
}
private static function loadDiffusionChangesForCommit(
PhabricatorRepository $repository,
PhabricatorRepositoryCommit $commit,
PhabricatorUser $actor) {
$data = array(
'user' => $actor,
'repository' => $repository,
'commit' => $commit->getCommitIdentifier(),
);
$drequest = DiffusionRequest::newFromDictionary($data);
$change_query =
DiffusionPathChangeQuery::newFromDiffusionRequest($drequest);
return $change_query->loadChanges();
}
}

View file

@ -96,9 +96,6 @@ abstract class PhabricatorRepositoryCommitChangeParserWorker
id(new PhabricatorSearchIndexer()) id(new PhabricatorSearchIndexer())
->queueDocumentForIndexing($commit->getPHID()); ->queueDocumentForIndexing($commit->getPHID());
PhabricatorOwnersPackagePathValidator::updateOwnersPackagePaths(
$commit,
PhabricatorUser::getOmnipotentUser());
if ($this->shouldQueueFollowupTasks()) { if ($this->shouldQueueFollowupTasks()) {
$this->queueTask( $this->queueTask(
'PhabricatorRepositoryCommitOwnersWorker', 'PhabricatorRepositoryCommitOwnersWorker',

View file

@ -49,10 +49,6 @@ Other options include:
See below for details about path resolution, or see See below for details about path resolution, or see
@{article:libphutil Libraries User Guide} for a general introduction to @{article:libphutil Libraries User Guide} for a general introduction to
libphutil libraries. libphutil libraries.
- **project.name**: name an "Arcanist Project" to associate this working
copy (Git, Mercurial) or directory (SVN) with. Previously, this was a
required option, but `arc` can now usually operate without it in Git and
Mercurial. This option was previously called `project_id`.
- **https.cabundle**: specifies the path to an alternate certificate bundle - **https.cabundle**: specifies the path to an alternate certificate bundle
for use when making HTTPS connections. for use when making HTTPS connections.
- **lint.engine**: the name of a subclass of - **lint.engine**: the name of a subclass of

View file

@ -44,12 +44,9 @@ Create a `.arcconfig` file in your project's working copy:
yourproject/ $ $EDITOR .arcconfig yourproject/ $ $EDITOR .arcconfig
yourproject/ $ cat .arcconfig yourproject/ $ cat .arcconfig
{ {
"project.name" : "yourprojectname",
"phabricator.uri" : "https://phabricator.example.com/" "phabricator.uri" : "https://phabricator.example.com/"
} }
Set `project.name` to a string that identifies the project.
Set `phabricator.uri` to the URI for your Phabricator install (where `arc` Set `phabricator.uri` to the URI for your Phabricator install (where `arc`
should send changes to). should send changes to).

View file

@ -83,8 +83,7 @@ Phabricator. For example, you might write this file to
`libcustom/.arcconfig`: `libcustom/.arcconfig`:
{ {
"project.name" : "libcustom", "load": [
"load" : [
"phabricator/src/" "phabricator/src/"
] ]
} }

View file

@ -12,9 +12,6 @@ final class PhabricatorCustomFieldConfigOptionType
$storage_value = $request->getStr('value'); $storage_value = $request->getStr('value');
$in_value = phutil_json_decode($storage_value); $in_value = phutil_json_decode($storage_value);
if (!is_array($in_value)) {
$in_value = array();
}
// When we submit from JS, we submit a list (since maps are not guaranteed // When we submit from JS, we submit a list (since maps are not guaranteed
// to retain order). Convert it into a map for storage (since it's far more // to retain order). Convert it into a map for storage (since it's far more
@ -37,20 +34,16 @@ final class PhabricatorCustomFieldConfigOptionType
$field_spec = PhabricatorEnv::getEnvConfig($option->getKey()); $field_spec = PhabricatorEnv::getEnvConfig($option->getKey());
} }
// Get all of the fields (including disabled fields) by querying for them
// with a faux spec where no fields are disabled.
$faux_spec = $field_spec;
foreach ($faux_spec as $key => $spec) {
unset($faux_spec[$key]['disabled']);
}
// TODO: We might need to build a real object here eventually. // TODO: We might need to build a real object here eventually.
$faux_object = null; $faux_object = null;
$fields = PhabricatorCustomField::buildFieldList( $fields = PhabricatorCustomField::buildFieldList(
$field_base_class, $field_base_class,
$faux_spec, $field_spec,
$faux_object); $faux_object,
array(
'withDisabled' => true,
));
$list_id = celerity_generate_unique_node_id(); $list_id = celerity_generate_unique_node_id();
$input_id = celerity_generate_unique_node_id(); $input_id = celerity_generate_unique_node_id();
@ -66,7 +59,8 @@ final class PhabricatorCustomFieldConfigOptionType
->addAttribute($field->getFieldDescription()) ->addAttribute($field->getFieldDescription())
->setHeader($field->getFieldName()); ->setHeader($field->getFieldName());
$is_disabled = !empty($field_spec[$key]['disabled']); $spec = idx($field_spec, $key, array());
$is_disabled = idx($spec, 'disabled', $field->shouldDisableByDefault());
$disabled_item = clone $item; $disabled_item = clone $item;
$enabled_item = clone $item; $enabled_item = clone $item;
@ -113,7 +107,7 @@ final class PhabricatorCustomFieldConfigOptionType
'id' => $input_id, 'id' => $input_id,
'type' => 'hidden', 'type' => 'hidden',
'name' => 'value', 'name' => 'value',
'value' => json_encode($display_value), 'value' => '',
)); ));
Javelin::initBehavior( Javelin::initBehavior(

View file

@ -105,7 +105,18 @@ abstract class PhabricatorCustomField {
/** /**
* @task apps * @task apps
*/ */
public static function buildFieldList($base_class, array $spec, $object) { public static function buildFieldList(
$base_class,
array $spec,
$object,
array $options = array()) {
PhutilTypeSpec::checkMap(
$options,
array(
'withDisabled' => 'optional bool',
));
$field_objects = id(new PhutilSymbolLoader()) $field_objects = id(new PhutilSymbolLoader())
->setAncestorClass($base_class) ->setAncestorClass($base_class)
->loadObjects(); ->loadObjects();
@ -135,13 +146,16 @@ abstract class PhabricatorCustomField {
$fields = array_select_keys($fields, array_keys($spec)) + $fields; $fields = array_select_keys($fields, array_keys($spec)) + $fields;
foreach ($spec as $key => $config) { if (empty($options['withDisabled'])) {
if (empty($fields[$key])) { foreach ($fields as $key => $field) {
continue; $config = idx($spec, $key, array()) + array(
} 'disabled' => $field->shouldDisableByDefault(),
if (!empty($config['disabled'])) { );
if ($fields[$key]->canDisableField()) {
unset($fields[$key]); if (!empty($config['disabled'])) {
if ($field->canDisableField()) {
unset($fields[$key]);
}
} }
} }
} }
@ -1055,22 +1069,6 @@ abstract class PhabricatorCustomField {
return false; return false;
} }
/**
* TODO: this is only used by Diffusion right now and everything is completely
* faked since Diffusion doesn't use ApplicationTransactions yet. This should
* get fleshed out as we have more use cases.
*
* @task appxaction
*/
public function buildApplicationTransactionMailBody(
PhabricatorApplicationTransaction $xaction,
PhabricatorMetaMTAMailBody $body) {
if ($this->proxy) {
return $this->proxy->buildApplicationTransactionMailBody($xaction, $body);
}
return;
}
/* -( Transaction Mail )--------------------------------------------------- */ /* -( Transaction Mail )--------------------------------------------------- */

View file

@ -15,6 +15,14 @@ abstract class PhabricatorInlineCommentController
abstract protected function saveComment( abstract protected function saveComment(
PhabricatorInlineCommentInterface $inline); PhabricatorInlineCommentInterface $inline);
protected function hideComments(array $ids) {
throw new PhutilMethodNotImplementedException();
}
protected function showComments(array $ids) {
throw new PhutilMethodNotImplementedException();
}
private $changesetID; private $changesetID;
private $isNewFile; private $isNewFile;
private $isOnRight; private $isOnRight;
@ -84,6 +92,22 @@ abstract class PhabricatorInlineCommentController
$op = $this->getOperation(); $op = $this->getOperation();
switch ($op) { switch ($op) {
case 'hide':
case 'show':
if (!$request->validateCSRF()) {
return new Aphront404Response();
}
$ids = $request->getStrList('ids');
if ($ids) {
if ($op == 'hide') {
$this->hideComments($ids);
} else {
$this->showComments($ids);
}
}
return id(new AphrontAjaxResponse())->setContent(array());
case 'done': case 'done':
if (!$request->validateCSRF()) { if (!$request->validateCSRF()) {
return new Aphront404Response(); return new Aphront404Response();

Some files were not shown because too many files have changed in this diff Show more