mirror of
https://we.phorge.it/source/phorge.git
synced 2024-12-04 20:52:43 +01:00
(stable) Promote 2017 Week 1
This commit is contained in:
commit
ea9c0607e1
76 changed files with 2760 additions and 1122 deletions
|
@ -9,11 +9,11 @@ return array(
|
|||
'names' => array(
|
||||
'conpherence.pkg.css' => '0b64e988',
|
||||
'conpherence.pkg.js' => '6249a1cf',
|
||||
'core.pkg.css' => '404132bb',
|
||||
'core.pkg.js' => '28e8cda8',
|
||||
'core.pkg.css' => '9c725fa0',
|
||||
'core.pkg.js' => 'a2ead3fe',
|
||||
'darkconsole.pkg.js' => 'e7393ebb',
|
||||
'differential.pkg.css' => 'a4ba74b5',
|
||||
'differential.pkg.js' => '634399e9',
|
||||
'differential.pkg.css' => 'f69afb45',
|
||||
'differential.pkg.js' => '40b18f35',
|
||||
'diffusion.pkg.css' => '91c5d3a6',
|
||||
'diffusion.pkg.js' => '84c8f8fd',
|
||||
'favicon.ico' => '30672e08',
|
||||
|
@ -59,7 +59,7 @@ return array(
|
|||
'rsrc/css/application/dashboard/dashboard.css' => 'bc6f2127',
|
||||
'rsrc/css/application/diff/inline-comment-summary.css' => '51efda3a',
|
||||
'rsrc/css/application/differential/add-comment.css' => 'c47f8c40',
|
||||
'rsrc/css/application/differential/changeset-view.css' => 'b158cc46',
|
||||
'rsrc/css/application/differential/changeset-view.css' => '11395d9c',
|
||||
'rsrc/css/application/differential/core.css' => '5b7b8ff4',
|
||||
'rsrc/css/application/differential/phui-inline-comment.css' => '5953c28e',
|
||||
'rsrc/css/application/differential/revision-comment.css' => '14b8565a',
|
||||
|
@ -108,7 +108,7 @@ return array(
|
|||
'rsrc/css/application/tokens/tokens.css' => '3d0f239e',
|
||||
'rsrc/css/application/uiexample/example.css' => '528b19de',
|
||||
'rsrc/css/core/core.css' => 'd0801452',
|
||||
'rsrc/css/core/remarkup.css' => '43e704eb',
|
||||
'rsrc/css/core/remarkup.css' => 'aebc1180',
|
||||
'rsrc/css/core/syntax.css' => '769d3498',
|
||||
'rsrc/css/core/z-index.css' => '5e72c4e0',
|
||||
'rsrc/css/diviner/diviner-shared.css' => 'aa3656aa',
|
||||
|
@ -137,7 +137,7 @@ return array(
|
|||
'rsrc/css/phui/phui-button.css' => '43f4912e',
|
||||
'rsrc/css/phui/phui-chart.css' => '6bf6f78e',
|
||||
'rsrc/css/phui/phui-cms.css' => 'be43c8a8',
|
||||
'rsrc/css/phui/phui-comment-form.css' => 'c953b75e',
|
||||
'rsrc/css/phui/phui-comment-form.css' => '48fbd65d',
|
||||
'rsrc/css/phui/phui-comment-panel.css' => 'f50152ad',
|
||||
'rsrc/css/phui/phui-crumbs-view.css' => 'f82868f2',
|
||||
'rsrc/css/phui/phui-curtain-view.css' => '947bf1a4',
|
||||
|
@ -146,7 +146,7 @@ return array(
|
|||
'rsrc/css/phui/phui-document.css' => 'c32e8dec',
|
||||
'rsrc/css/phui/phui-feed-story.css' => '44a9c8e9',
|
||||
'rsrc/css/phui/phui-fontkit.css' => '9cda225e',
|
||||
'rsrc/css/phui/phui-form-view.css' => 'cd79ff6a',
|
||||
'rsrc/css/phui/phui-form-view.css' => '04cc4771',
|
||||
'rsrc/css/phui/phui-form.css' => '2342b0e5',
|
||||
'rsrc/css/phui/phui-head-thing.css' => 'fd311e5f',
|
||||
'rsrc/css/phui/phui-header-view.css' => '6ec8f155',
|
||||
|
@ -396,8 +396,9 @@ return array(
|
|||
'rsrc/js/application/dashboard/behavior-dashboard-move-panels.js' => '019f36c4',
|
||||
'rsrc/js/application/dashboard/behavior-dashboard-query-panel-select.js' => '453c5375',
|
||||
'rsrc/js/application/dashboard/behavior-dashboard-tab-panel.js' => 'd4eecc63',
|
||||
'rsrc/js/application/diff/behavior-preview-link.js' => '051c7832',
|
||||
'rsrc/js/application/differential/ChangesetViewManager.js' => 'a2828756',
|
||||
'rsrc/js/application/differential/DifferentialInlineCommentEditor.js' => '64a5550f',
|
||||
'rsrc/js/application/differential/DifferentialInlineCommentEditor.js' => '2e3f9738',
|
||||
'rsrc/js/application/differential/behavior-add-reviewers-and-ccs.js' => 'e10f8e18',
|
||||
'rsrc/js/application/differential/behavior-comment-jump.js' => '4fdb476d',
|
||||
'rsrc/js/application/differential/behavior-comment-preview.js' => 'b064af76',
|
||||
|
@ -453,7 +454,7 @@ return array(
|
|||
'rsrc/js/application/search/behavior-reorder-profile-menu-items.js' => 'e2e0a072',
|
||||
'rsrc/js/application/search/behavior-reorder-queries.js' => 'e9581f08',
|
||||
'rsrc/js/application/slowvote/behavior-slowvote-embed.js' => '887ad43f',
|
||||
'rsrc/js/application/transactions/behavior-comment-actions.js' => '1be09f3f',
|
||||
'rsrc/js/application/transactions/behavior-comment-actions.js' => 'b52947eb',
|
||||
'rsrc/js/application/transactions/behavior-reorder-configs.js' => 'd7a74243',
|
||||
'rsrc/js/application/transactions/behavior-reorder-fields.js' => 'b59e1e96',
|
||||
'rsrc/js/application/transactions/behavior-show-older-transactions.js' => '94c65b72',
|
||||
|
@ -488,7 +489,7 @@ return array(
|
|||
'rsrc/js/core/ShapedRequest.js' => '7cbe244b',
|
||||
'rsrc/js/core/TextAreaUtils.js' => '320810c8',
|
||||
'rsrc/js/core/Title.js' => '485aaa6c',
|
||||
'rsrc/js/core/ToolTip.js' => '6323f942',
|
||||
'rsrc/js/core/ToolTip.js' => 'b5c62c3b',
|
||||
'rsrc/js/core/behavior-active-nav.js' => 'e379b58e',
|
||||
'rsrc/js/core/behavior-audio-source.js' => '59b251eb',
|
||||
'rsrc/js/core/behavior-autofocus.js' => '7319e029',
|
||||
|
@ -509,7 +510,7 @@ return array(
|
|||
'rsrc/js/core/behavior-hovercard.js' => 'bcaccd64',
|
||||
'rsrc/js/core/behavior-keyboard-pager.js' => 'a8da01f0',
|
||||
'rsrc/js/core/behavior-keyboard-shortcuts.js' => '01fca1f0',
|
||||
'rsrc/js/core/behavior-lightbox-attachments.js' => 'ddcd41cf',
|
||||
'rsrc/js/core/behavior-lightbox-attachments.js' => 'a5c57c24',
|
||||
'rsrc/js/core/behavior-line-linker.js' => '1499a8cb',
|
||||
'rsrc/js/core/behavior-more.js' => 'a80d0378',
|
||||
'rsrc/js/core/behavior-object-selector.js' => 'e0ec7f2f',
|
||||
|
@ -542,7 +543,7 @@ return array(
|
|||
'rsrc/js/phuix/PHUIXActionView.js' => '8cf6d262',
|
||||
'rsrc/js/phuix/PHUIXAutocomplete.js' => '6d86ce8b',
|
||||
'rsrc/js/phuix/PHUIXDropdownMenu.js' => '82e270da',
|
||||
'rsrc/js/phuix/PHUIXFormControl.js' => '301b7812',
|
||||
'rsrc/js/phuix/PHUIXFormControl.js' => 'bbece68d',
|
||||
'rsrc/js/phuix/PHUIXIconView.js' => 'bff6884b',
|
||||
),
|
||||
'symbols' => array(
|
||||
|
@ -573,9 +574,9 @@ return array(
|
|||
'conpherence-thread-manager' => 'c8b5ee6f',
|
||||
'conpherence-transaction-css' => '85129c68',
|
||||
'd3' => 'a11a5ff2',
|
||||
'differential-changeset-view-css' => 'b158cc46',
|
||||
'differential-changeset-view-css' => '11395d9c',
|
||||
'differential-core-view-css' => '5b7b8ff4',
|
||||
'differential-inline-comment-editor' => '64a5550f',
|
||||
'differential-inline-comment-editor' => '2e3f9738',
|
||||
'differential-revision-add-comment-css' => 'c47f8c40',
|
||||
'differential-revision-comment-css' => '14b8565a',
|
||||
'differential-revision-history-css' => '0e8eb855',
|
||||
|
@ -609,7 +610,7 @@ return array(
|
|||
'javelin-behavior-bulk-job-reload' => 'edf8a145',
|
||||
'javelin-behavior-calendar-month-view' => 'fe33e256',
|
||||
'javelin-behavior-choose-control' => '327a00d1',
|
||||
'javelin-behavior-comment-actions' => '1be09f3f',
|
||||
'javelin-behavior-comment-actions' => 'b52947eb',
|
||||
'javelin-behavior-config-reorder-fields' => 'b6993408',
|
||||
'javelin-behavior-conpherence-menu' => '7524fcfa',
|
||||
'javelin-behavior-conpherence-participant-pane' => '8604caa8',
|
||||
|
@ -625,6 +626,7 @@ return array(
|
|||
'javelin-behavior-desktop-notifications-control' => 'edd1ba66',
|
||||
'javelin-behavior-detect-timezone' => '4c193c96',
|
||||
'javelin-behavior-device' => 'bb1dd507',
|
||||
'javelin-behavior-diff-preview-link' => '051c7832',
|
||||
'javelin-behavior-differential-add-reviewers-and-ccs' => 'e10f8e18',
|
||||
'javelin-behavior-differential-comment-jump' => '4fdb476d',
|
||||
'javelin-behavior-differential-diff-radios' => 'e1ff79b1',
|
||||
|
@ -655,7 +657,7 @@ return array(
|
|||
'javelin-behavior-history-install' => '7ee2b591',
|
||||
'javelin-behavior-icon-composer' => '8499b6ab',
|
||||
'javelin-behavior-launch-icon-composer' => '48086888',
|
||||
'javelin-behavior-lightbox-attachments' => 'ddcd41cf',
|
||||
'javelin-behavior-lightbox-attachments' => 'a5c57c24',
|
||||
'javelin-behavior-line-chart' => 'e4232876',
|
||||
'javelin-behavior-load-blame' => '42126667',
|
||||
'javelin-behavior-maniphest-batch-editor' => '782ab6e7',
|
||||
|
@ -806,7 +808,7 @@ return array(
|
|||
'phabricator-object-selector-css' => '85ee8ce6',
|
||||
'phabricator-phtize' => 'd254d646',
|
||||
'phabricator-prefab' => '8d40ae75',
|
||||
'phabricator-remarkup-css' => '43e704eb',
|
||||
'phabricator-remarkup-css' => 'aebc1180',
|
||||
'phabricator-search-results-css' => '64ad079a',
|
||||
'phabricator-shaped-request' => '7cbe244b',
|
||||
'phabricator-slowvote-css' => 'a94b7230',
|
||||
|
@ -814,7 +816,7 @@ return array(
|
|||
'phabricator-standard-page-view' => '894d8a25',
|
||||
'phabricator-textareautils' => '320810c8',
|
||||
'phabricator-title' => '485aaa6c',
|
||||
'phabricator-tooltip' => '6323f942',
|
||||
'phabricator-tooltip' => 'b5c62c3b',
|
||||
'phabricator-ui-example-css' => '528b19de',
|
||||
'phabricator-uiexample-javelin-view' => 'd4a14807',
|
||||
'phabricator-uiexample-reactor-button' => 'd19198c8',
|
||||
|
@ -849,7 +851,7 @@ return array(
|
|||
'phui-calendar-month-css' => '8e10e92c',
|
||||
'phui-chart-css' => '6bf6f78e',
|
||||
'phui-cms-css' => 'be43c8a8',
|
||||
'phui-comment-form-css' => 'c953b75e',
|
||||
'phui-comment-form-css' => '48fbd65d',
|
||||
'phui-comment-panel-css' => 'f50152ad',
|
||||
'phui-crumbs-view-css' => 'f82868f2',
|
||||
'phui-curtain-view-css' => '947bf1a4',
|
||||
|
@ -860,7 +862,7 @@ return array(
|
|||
'phui-font-icon-base-css' => '870a7360',
|
||||
'phui-fontkit-css' => '9cda225e',
|
||||
'phui-form-css' => '2342b0e5',
|
||||
'phui-form-view-css' => 'cd79ff6a',
|
||||
'phui-form-view-css' => '04cc4771',
|
||||
'phui-head-thing-view-css' => 'fd311e5f',
|
||||
'phui-header-view-css' => '6ec8f155',
|
||||
'phui-hovercard' => '1bd28176',
|
||||
|
@ -901,7 +903,7 @@ return array(
|
|||
'phuix-action-view' => '8cf6d262',
|
||||
'phuix-autocomplete' => '6d86ce8b',
|
||||
'phuix-dropdown-menu' => '82e270da',
|
||||
'phuix-form-control-view' => '301b7812',
|
||||
'phuix-form-control-view' => 'bbece68d',
|
||||
'phuix-icon-view' => 'bff6884b',
|
||||
'policy-css' => '957ea14c',
|
||||
'policy-edit-css' => '815c66f7',
|
||||
|
@ -944,6 +946,11 @@ return array(
|
|||
'javelin-dom',
|
||||
'phabricator-keyboard-shortcut',
|
||||
),
|
||||
'051c7832' => array(
|
||||
'javelin-behavior',
|
||||
'javelin-stratcom',
|
||||
'javelin-dom',
|
||||
),
|
||||
'05270951' => array(
|
||||
'javelin-util',
|
||||
'javelin-magical-init',
|
||||
|
@ -1025,6 +1032,9 @@ return array(
|
|||
'javelin-dom',
|
||||
'javelin-typeahead-normalizer',
|
||||
),
|
||||
'11395d9c' => array(
|
||||
'phui-inline-comment-view-css',
|
||||
),
|
||||
'12884df9' => array(
|
||||
'javelin-behavior',
|
||||
'javelin-stratcom',
|
||||
|
@ -1083,15 +1093,6 @@ return array(
|
|||
'javelin-request',
|
||||
'javelin-uri',
|
||||
),
|
||||
'1be09f3f' => array(
|
||||
'javelin-behavior',
|
||||
'javelin-stratcom',
|
||||
'javelin-workflow',
|
||||
'javelin-dom',
|
||||
'phuix-form-control-view',
|
||||
'phuix-icon-view',
|
||||
'javelin-behavior-phabricator-gesture',
|
||||
),
|
||||
'1def2711' => array(
|
||||
'javelin-install',
|
||||
'javelin-dom',
|
||||
|
@ -1156,13 +1157,17 @@ return array(
|
|||
'javelin-install',
|
||||
'javelin-event',
|
||||
),
|
||||
'2e3f9738' => array(
|
||||
'javelin-dom',
|
||||
'javelin-util',
|
||||
'javelin-stratcom',
|
||||
'javelin-install',
|
||||
'javelin-request',
|
||||
'javelin-workflow',
|
||||
),
|
||||
'2ee659ce' => array(
|
||||
'javelin-install',
|
||||
),
|
||||
'301b7812' => array(
|
||||
'javelin-install',
|
||||
'javelin-dom',
|
||||
),
|
||||
'320810c8' => array(
|
||||
'javelin-install',
|
||||
'javelin-dom',
|
||||
|
@ -1413,26 +1418,12 @@ return array(
|
|||
'javelin-install',
|
||||
'javelin-util',
|
||||
),
|
||||
'6323f942' => array(
|
||||
'javelin-install',
|
||||
'javelin-util',
|
||||
'javelin-dom',
|
||||
'javelin-vector',
|
||||
),
|
||||
'635de1ec' => array(
|
||||
'javelin-behavior',
|
||||
'javelin-stratcom',
|
||||
'javelin-workflow',
|
||||
'javelin-dom',
|
||||
),
|
||||
'64a5550f' => array(
|
||||
'javelin-dom',
|
||||
'javelin-util',
|
||||
'javelin-stratcom',
|
||||
'javelin-install',
|
||||
'javelin-request',
|
||||
'javelin-workflow',
|
||||
),
|
||||
'680ea2c8' => array(
|
||||
'javelin-install',
|
||||
'javelin-dom',
|
||||
|
@ -1768,6 +1759,15 @@ return array(
|
|||
'javelin-uri',
|
||||
'phabricator-notification',
|
||||
),
|
||||
'a5c57c24' => array(
|
||||
'javelin-behavior',
|
||||
'javelin-stratcom',
|
||||
'javelin-dom',
|
||||
'javelin-mask',
|
||||
'javelin-util',
|
||||
'phuix-icon-view',
|
||||
'phabricator-busy',
|
||||
),
|
||||
'a6f7a73b' => array(
|
||||
'javelin-behavior',
|
||||
'javelin-stratcom',
|
||||
|
@ -1846,9 +1846,6 @@ return array(
|
|||
'javelin-util',
|
||||
'phabricator-shaped-request',
|
||||
),
|
||||
'b158cc46' => array(
|
||||
'phui-inline-comment-view-css',
|
||||
),
|
||||
'b1f0ccee' => array(
|
||||
'javelin-install',
|
||||
'javelin-dom',
|
||||
|
@ -1880,6 +1877,15 @@ return array(
|
|||
'javelin-typeahead-preloaded-source',
|
||||
'javelin-util',
|
||||
),
|
||||
'b52947eb' => array(
|
||||
'javelin-behavior',
|
||||
'javelin-stratcom',
|
||||
'javelin-workflow',
|
||||
'javelin-dom',
|
||||
'phuix-form-control-view',
|
||||
'phuix-icon-view',
|
||||
'javelin-behavior-phabricator-gesture',
|
||||
),
|
||||
'b59e1e96' => array(
|
||||
'javelin-behavior',
|
||||
'javelin-stratcom',
|
||||
|
@ -1891,6 +1897,12 @@ return array(
|
|||
'javelin-install',
|
||||
'javelin-dom',
|
||||
),
|
||||
'b5c62c3b' => array(
|
||||
'javelin-install',
|
||||
'javelin-util',
|
||||
'javelin-dom',
|
||||
'javelin-vector',
|
||||
),
|
||||
'b5d57730' => array(
|
||||
'javelin-install',
|
||||
'javelin-stratcom',
|
||||
|
@ -1916,6 +1928,10 @@ return array(
|
|||
'javelin-vector',
|
||||
'javelin-install',
|
||||
),
|
||||
'bbece68d' => array(
|
||||
'javelin-install',
|
||||
'javelin-dom',
|
||||
),
|
||||
'bcaccd64' => array(
|
||||
'javelin-behavior',
|
||||
'javelin-behavior-device',
|
||||
|
@ -2060,15 +2076,6 @@ return array(
|
|||
'javelin-util',
|
||||
'phabricator-shaped-request',
|
||||
),
|
||||
'ddcd41cf' => array(
|
||||
'javelin-behavior',
|
||||
'javelin-stratcom',
|
||||
'javelin-dom',
|
||||
'javelin-mask',
|
||||
'javelin-util',
|
||||
'phuix-icon-view',
|
||||
'phabricator-busy',
|
||||
),
|
||||
'de2e896f' => array(
|
||||
'javelin-behavior',
|
||||
'javelin-dom',
|
||||
|
|
|
@ -348,7 +348,6 @@ phutil_register_library_map(array(
|
|||
'DarkConsoleXHProfPluginAPI' => 'applications/console/plugin/xhprof/DarkConsoleXHProfPluginAPI.php',
|
||||
'DifferentialAction' => 'applications/differential/constants/DifferentialAction.php',
|
||||
'DifferentialActionEmailCommand' => 'applications/differential/command/DifferentialActionEmailCommand.php',
|
||||
'DifferentialAddCommentView' => 'applications/differential/view/DifferentialAddCommentView.php',
|
||||
'DifferentialAdjustmentMapTestCase' => 'applications/differential/storage/__tests__/DifferentialAdjustmentMapTestCase.php',
|
||||
'DifferentialAffectedPath' => 'applications/differential/storage/DifferentialAffectedPath.php',
|
||||
'DifferentialAsanaRepresentationField' => 'applications/differential/customfield/DifferentialAsanaRepresentationField.php',
|
||||
|
@ -380,10 +379,9 @@ phutil_register_library_map(array(
|
|||
'DifferentialChangesetTwoUpTestRenderer' => 'applications/differential/render/DifferentialChangesetTwoUpTestRenderer.php',
|
||||
'DifferentialChangesetViewController' => 'applications/differential/controller/DifferentialChangesetViewController.php',
|
||||
'DifferentialCloseConduitAPIMethod' => 'applications/differential/conduit/DifferentialCloseConduitAPIMethod.php',
|
||||
'DifferentialCommentPreviewController' => 'applications/differential/controller/DifferentialCommentPreviewController.php',
|
||||
'DifferentialCommentSaveController' => 'applications/differential/controller/DifferentialCommentSaveController.php',
|
||||
'DifferentialCommitMessageCustomField' => 'applications/differential/field/DifferentialCommitMessageCustomField.php',
|
||||
'DifferentialCommitMessageField' => 'applications/differential/field/DifferentialCommitMessageField.php',
|
||||
'DifferentialCommitMessageFieldTestCase' => 'applications/differential/field/__tests__/DifferentialCommitMessageFieldTestCase.php',
|
||||
'DifferentialCommitMessageParser' => 'applications/differential/parser/DifferentialCommitMessageParser.php',
|
||||
'DifferentialCommitMessageParserTestCase' => 'applications/differential/parser/__tests__/DifferentialCommitMessageParserTestCase.php',
|
||||
'DifferentialCommitsField' => 'applications/differential/customfield/DifferentialCommitsField.php',
|
||||
|
@ -459,7 +457,6 @@ phutil_register_library_map(array(
|
|||
'DifferentialInlineComment' => 'applications/differential/storage/DifferentialInlineComment.php',
|
||||
'DifferentialInlineCommentEditController' => 'applications/differential/controller/DifferentialInlineCommentEditController.php',
|
||||
'DifferentialInlineCommentMailView' => 'applications/differential/mail/DifferentialInlineCommentMailView.php',
|
||||
'DifferentialInlineCommentPreviewController' => 'applications/differential/controller/DifferentialInlineCommentPreviewController.php',
|
||||
'DifferentialInlineCommentQuery' => 'applications/differential/query/DifferentialInlineCommentQuery.php',
|
||||
'DifferentialJIRAIssuesCommitMessageField' => 'applications/differential/field/DifferentialJIRAIssuesCommitMessageField.php',
|
||||
'DifferentialJIRAIssuesField' => 'applications/differential/customfield/DifferentialJIRAIssuesField.php',
|
||||
|
@ -507,10 +504,15 @@ phutil_register_library_map(array(
|
|||
'DifferentialReviewersHeraldAction' => 'applications/differential/herald/DifferentialReviewersHeraldAction.php',
|
||||
'DifferentialReviewersView' => 'applications/differential/view/DifferentialReviewersView.php',
|
||||
'DifferentialRevision' => 'applications/differential/storage/DifferentialRevision.php',
|
||||
'DifferentialRevisionAbandonTransaction' => 'applications/differential/xaction/DifferentialRevisionAbandonTransaction.php',
|
||||
'DifferentialRevisionAcceptTransaction' => 'applications/differential/xaction/DifferentialRevisionAcceptTransaction.php',
|
||||
'DifferentialRevisionActionTransaction' => 'applications/differential/xaction/DifferentialRevisionActionTransaction.php',
|
||||
'DifferentialRevisionAffectedFilesHeraldField' => 'applications/differential/herald/DifferentialRevisionAffectedFilesHeraldField.php',
|
||||
'DifferentialRevisionAuthorHeraldField' => 'applications/differential/herald/DifferentialRevisionAuthorHeraldField.php',
|
||||
'DifferentialRevisionAuthorProjectsHeraldField' => 'applications/differential/herald/DifferentialRevisionAuthorProjectsHeraldField.php',
|
||||
'DifferentialRevisionCloseDetailsController' => 'applications/differential/controller/DifferentialRevisionCloseDetailsController.php',
|
||||
'DifferentialRevisionCloseTransaction' => 'applications/differential/xaction/DifferentialRevisionCloseTransaction.php',
|
||||
'DifferentialRevisionCommandeerTransaction' => 'applications/differential/xaction/DifferentialRevisionCommandeerTransaction.php',
|
||||
'DifferentialRevisionContentAddedHeraldField' => 'applications/differential/herald/DifferentialRevisionContentAddedHeraldField.php',
|
||||
'DifferentialRevisionContentHeraldField' => 'applications/differential/herald/DifferentialRevisionContentHeraldField.php',
|
||||
'DifferentialRevisionContentRemovedHeraldField' => 'applications/differential/herald/DifferentialRevisionContentRemovedHeraldField.php',
|
||||
|
@ -540,14 +542,21 @@ phutil_register_library_map(array(
|
|||
'DifferentialRevisionPHIDType' => 'applications/differential/phid/DifferentialRevisionPHIDType.php',
|
||||
'DifferentialRevisionPackageHeraldField' => 'applications/differential/herald/DifferentialRevisionPackageHeraldField.php',
|
||||
'DifferentialRevisionPackageOwnerHeraldField' => 'applications/differential/herald/DifferentialRevisionPackageOwnerHeraldField.php',
|
||||
'DifferentialRevisionPlanChangesTransaction' => 'applications/differential/xaction/DifferentialRevisionPlanChangesTransaction.php',
|
||||
'DifferentialRevisionQuery' => 'applications/differential/query/DifferentialRevisionQuery.php',
|
||||
'DifferentialRevisionReclaimTransaction' => 'applications/differential/xaction/DifferentialRevisionReclaimTransaction.php',
|
||||
'DifferentialRevisionRejectTransaction' => 'applications/differential/xaction/DifferentialRevisionRejectTransaction.php',
|
||||
'DifferentialRevisionRelationship' => 'applications/differential/relationships/DifferentialRevisionRelationship.php',
|
||||
'DifferentialRevisionRelationshipSource' => 'applications/search/relationship/DifferentialRevisionRelationshipSource.php',
|
||||
'DifferentialRevisionReopenTransaction' => 'applications/differential/xaction/DifferentialRevisionReopenTransaction.php',
|
||||
'DifferentialRevisionRepositoryHeraldField' => 'applications/differential/herald/DifferentialRevisionRepositoryHeraldField.php',
|
||||
'DifferentialRevisionRepositoryProjectsHeraldField' => 'applications/differential/herald/DifferentialRevisionRepositoryProjectsHeraldField.php',
|
||||
'DifferentialRevisionRepositoryTransaction' => 'applications/differential/xaction/DifferentialRevisionRepositoryTransaction.php',
|
||||
'DifferentialRevisionRequestReviewTransaction' => 'applications/differential/xaction/DifferentialRevisionRequestReviewTransaction.php',
|
||||
'DifferentialRevisionRequiredActionResultBucket' => 'applications/differential/query/DifferentialRevisionRequiredActionResultBucket.php',
|
||||
'DifferentialRevisionResignTransaction' => 'applications/differential/xaction/DifferentialRevisionResignTransaction.php',
|
||||
'DifferentialRevisionResultBucket' => 'applications/differential/query/DifferentialRevisionResultBucket.php',
|
||||
'DifferentialRevisionReviewTransaction' => 'applications/differential/xaction/DifferentialRevisionReviewTransaction.php',
|
||||
'DifferentialRevisionReviewersHeraldField' => 'applications/differential/herald/DifferentialRevisionReviewersHeraldField.php',
|
||||
'DifferentialRevisionReviewersTransaction' => 'applications/differential/xaction/DifferentialRevisionReviewersTransaction.php',
|
||||
'DifferentialRevisionSearchConduitAPIMethod' => 'applications/differential/conduit/DifferentialRevisionSearchConduitAPIMethod.php',
|
||||
|
@ -1631,6 +1640,7 @@ phutil_register_library_map(array(
|
|||
'PHUIDiffGraphViewTestCase' => 'infrastructure/diff/view/__tests__/PHUIDiffGraphViewTestCase.php',
|
||||
'PHUIDiffInlineCommentDetailView' => 'infrastructure/diff/view/PHUIDiffInlineCommentDetailView.php',
|
||||
'PHUIDiffInlineCommentEditView' => 'infrastructure/diff/view/PHUIDiffInlineCommentEditView.php',
|
||||
'PHUIDiffInlineCommentPreviewListView' => 'infrastructure/diff/view/PHUIDiffInlineCommentPreviewListView.php',
|
||||
'PHUIDiffInlineCommentRowScaffold' => 'infrastructure/diff/view/PHUIDiffInlineCommentRowScaffold.php',
|
||||
'PHUIDiffInlineCommentTableScaffold' => 'infrastructure/diff/view/PHUIDiffInlineCommentTableScaffold.php',
|
||||
'PHUIDiffInlineCommentUndoView' => 'infrastructure/diff/view/PHUIDiffInlineCommentUndoView.php',
|
||||
|
@ -1842,6 +1852,7 @@ phutil_register_library_map(array(
|
|||
'PhabricatorApplicationsApplication' => 'applications/meta/application/PhabricatorApplicationsApplication.php',
|
||||
'PhabricatorApplicationsController' => 'applications/meta/controller/PhabricatorApplicationsController.php',
|
||||
'PhabricatorApplicationsListController' => 'applications/meta/controller/PhabricatorApplicationsListController.php',
|
||||
'PhabricatorApplyEditField' => 'applications/transactions/editfield/PhabricatorApplyEditField.php',
|
||||
'PhabricatorAsanaAuthProvider' => 'applications/auth/provider/PhabricatorAsanaAuthProvider.php',
|
||||
'PhabricatorAsanaConfigOptions' => 'applications/doorkeeper/option/PhabricatorAsanaConfigOptions.php',
|
||||
'PhabricatorAsanaSubtaskHasObjectEdgeType' => 'applications/doorkeeper/edge/PhabricatorAsanaSubtaskHasObjectEdgeType.php',
|
||||
|
@ -2544,6 +2555,7 @@ phutil_register_library_map(array(
|
|||
'PhabricatorEditEngineAPIMethod' => 'applications/transactions/editengine/PhabricatorEditEngineAPIMethod.php',
|
||||
'PhabricatorEditEngineColumnsCommentAction' => 'applications/transactions/commentaction/PhabricatorEditEngineColumnsCommentAction.php',
|
||||
'PhabricatorEditEngineCommentAction' => 'applications/transactions/commentaction/PhabricatorEditEngineCommentAction.php',
|
||||
'PhabricatorEditEngineCommentActionGroup' => 'applications/transactions/commentaction/PhabricatorEditEngineCommentActionGroup.php',
|
||||
'PhabricatorEditEngineConfiguration' => 'applications/transactions/storage/PhabricatorEditEngineConfiguration.php',
|
||||
'PhabricatorEditEngineConfigurationDefaultCreateController' => 'applications/transactions/controller/PhabricatorEditEngineConfigurationDefaultCreateController.php',
|
||||
'PhabricatorEditEngineConfigurationDefaultsController' => 'applications/transactions/controller/PhabricatorEditEngineConfigurationDefaultsController.php',
|
||||
|
@ -2570,10 +2582,12 @@ phutil_register_library_map(array(
|
|||
'PhabricatorEditEngineExtensionModule' => 'applications/transactions/engineextension/PhabricatorEditEngineExtensionModule.php',
|
||||
'PhabricatorEditEngineListController' => 'applications/transactions/controller/PhabricatorEditEngineListController.php',
|
||||
'PhabricatorEditEnginePointsCommentAction' => 'applications/transactions/commentaction/PhabricatorEditEnginePointsCommentAction.php',
|
||||
'PhabricatorEditEngineProfileMenuItem' => 'applications/search/menuitem/PhabricatorEditEngineProfileMenuItem.php',
|
||||
'PhabricatorEditEngineQuery' => 'applications/transactions/query/PhabricatorEditEngineQuery.php',
|
||||
'PhabricatorEditEngineSearchEngine' => 'applications/transactions/query/PhabricatorEditEngineSearchEngine.php',
|
||||
'PhabricatorEditEngineSelectCommentAction' => 'applications/transactions/commentaction/PhabricatorEditEngineSelectCommentAction.php',
|
||||
'PhabricatorEditEngineSettingsPanel' => 'applications/settings/panel/PhabricatorEditEngineSettingsPanel.php',
|
||||
'PhabricatorEditEngineStaticCommentAction' => 'applications/transactions/commentaction/PhabricatorEditEngineStaticCommentAction.php',
|
||||
'PhabricatorEditEngineTokenizerCommentAction' => 'applications/transactions/commentaction/PhabricatorEditEngineTokenizerCommentAction.php',
|
||||
'PhabricatorEditField' => 'applications/transactions/editfield/PhabricatorEditField.php',
|
||||
'PhabricatorEditPage' => 'applications/transactions/editengine/PhabricatorEditPage.php',
|
||||
|
@ -4982,7 +4996,6 @@ phutil_register_library_map(array(
|
|||
'DarkConsoleXHProfPluginAPI' => 'Phobject',
|
||||
'DifferentialAction' => 'Phobject',
|
||||
'DifferentialActionEmailCommand' => 'MetaMTAEmailTransactionCommand',
|
||||
'DifferentialAddCommentView' => 'AphrontView',
|
||||
'DifferentialAdjustmentMapTestCase' => 'PhutilTestCase',
|
||||
'DifferentialAffectedPath' => 'DifferentialDAO',
|
||||
'DifferentialAsanaRepresentationField' => 'DifferentialCustomField',
|
||||
|
@ -5017,10 +5030,9 @@ phutil_register_library_map(array(
|
|||
'DifferentialChangesetTwoUpTestRenderer' => 'DifferentialChangesetTestRenderer',
|
||||
'DifferentialChangesetViewController' => 'DifferentialController',
|
||||
'DifferentialCloseConduitAPIMethod' => 'DifferentialConduitAPIMethod',
|
||||
'DifferentialCommentPreviewController' => 'DifferentialController',
|
||||
'DifferentialCommentSaveController' => 'DifferentialController',
|
||||
'DifferentialCommitMessageCustomField' => 'DifferentialCommitMessageField',
|
||||
'DifferentialCommitMessageField' => 'Phobject',
|
||||
'DifferentialCommitMessageFieldTestCase' => 'PhabricatorTestCase',
|
||||
'DifferentialCommitMessageParser' => 'Phobject',
|
||||
'DifferentialCommitMessageParserTestCase' => 'PhabricatorTestCase',
|
||||
'DifferentialCommitsField' => 'DifferentialCustomField',
|
||||
|
@ -5047,6 +5059,7 @@ phutil_register_library_map(array(
|
|||
'DifferentialDiff' => array(
|
||||
'DifferentialDAO',
|
||||
'PhabricatorPolicyInterface',
|
||||
'PhabricatorExtendedPolicyInterface',
|
||||
'HarbormasterBuildableInterface',
|
||||
'HarbormasterCircleCIBuildableInterface',
|
||||
'PhabricatorApplicationTransactionInterface',
|
||||
|
@ -5109,7 +5122,6 @@ phutil_register_library_map(array(
|
|||
),
|
||||
'DifferentialInlineCommentEditController' => 'PhabricatorInlineCommentController',
|
||||
'DifferentialInlineCommentMailView' => 'DifferentialMailView',
|
||||
'DifferentialInlineCommentPreviewController' => 'PhabricatorInlineCommentPreviewController',
|
||||
'DifferentialInlineCommentQuery' => 'PhabricatorOffsetPagedQuery',
|
||||
'DifferentialJIRAIssuesCommitMessageField' => 'DifferentialCommitMessageCustomField',
|
||||
'DifferentialJIRAIssuesField' => 'DifferentialStoredCustomField',
|
||||
|
@ -5173,10 +5185,15 @@ phutil_register_library_map(array(
|
|||
'PhabricatorFulltextInterface',
|
||||
'PhabricatorConduitResultInterface',
|
||||
),
|
||||
'DifferentialRevisionAbandonTransaction' => 'DifferentialRevisionActionTransaction',
|
||||
'DifferentialRevisionAcceptTransaction' => 'DifferentialRevisionReviewTransaction',
|
||||
'DifferentialRevisionActionTransaction' => 'DifferentialRevisionTransactionType',
|
||||
'DifferentialRevisionAffectedFilesHeraldField' => 'DifferentialRevisionHeraldField',
|
||||
'DifferentialRevisionAuthorHeraldField' => 'DifferentialRevisionHeraldField',
|
||||
'DifferentialRevisionAuthorProjectsHeraldField' => 'DifferentialRevisionHeraldField',
|
||||
'DifferentialRevisionCloseDetailsController' => 'DifferentialController',
|
||||
'DifferentialRevisionCloseTransaction' => 'DifferentialRevisionActionTransaction',
|
||||
'DifferentialRevisionCommandeerTransaction' => 'DifferentialRevisionActionTransaction',
|
||||
'DifferentialRevisionContentAddedHeraldField' => 'DifferentialRevisionHeraldField',
|
||||
'DifferentialRevisionContentHeraldField' => 'DifferentialRevisionHeraldField',
|
||||
'DifferentialRevisionContentRemovedHeraldField' => 'DifferentialRevisionHeraldField',
|
||||
|
@ -5206,14 +5223,21 @@ phutil_register_library_map(array(
|
|||
'DifferentialRevisionPHIDType' => 'PhabricatorPHIDType',
|
||||
'DifferentialRevisionPackageHeraldField' => 'DifferentialRevisionHeraldField',
|
||||
'DifferentialRevisionPackageOwnerHeraldField' => 'DifferentialRevisionHeraldField',
|
||||
'DifferentialRevisionPlanChangesTransaction' => 'DifferentialRevisionActionTransaction',
|
||||
'DifferentialRevisionQuery' => 'PhabricatorCursorPagedPolicyAwareQuery',
|
||||
'DifferentialRevisionReclaimTransaction' => 'DifferentialRevisionActionTransaction',
|
||||
'DifferentialRevisionRejectTransaction' => 'DifferentialRevisionReviewTransaction',
|
||||
'DifferentialRevisionRelationship' => 'PhabricatorObjectRelationship',
|
||||
'DifferentialRevisionRelationshipSource' => 'PhabricatorObjectRelationshipSource',
|
||||
'DifferentialRevisionReopenTransaction' => 'DifferentialRevisionActionTransaction',
|
||||
'DifferentialRevisionRepositoryHeraldField' => 'DifferentialRevisionHeraldField',
|
||||
'DifferentialRevisionRepositoryProjectsHeraldField' => 'DifferentialRevisionHeraldField',
|
||||
'DifferentialRevisionRepositoryTransaction' => 'DifferentialRevisionTransactionType',
|
||||
'DifferentialRevisionRequestReviewTransaction' => 'DifferentialRevisionActionTransaction',
|
||||
'DifferentialRevisionRequiredActionResultBucket' => 'DifferentialRevisionResultBucket',
|
||||
'DifferentialRevisionResignTransaction' => 'DifferentialRevisionReviewTransaction',
|
||||
'DifferentialRevisionResultBucket' => 'PhabricatorSearchResultBucket',
|
||||
'DifferentialRevisionReviewTransaction' => 'DifferentialRevisionActionTransaction',
|
||||
'DifferentialRevisionReviewersHeraldField' => 'DifferentialRevisionHeraldField',
|
||||
'DifferentialRevisionReviewersTransaction' => 'DifferentialRevisionTransactionType',
|
||||
'DifferentialRevisionSearchConduitAPIMethod' => 'PhabricatorSearchEngineAPIMethod',
|
||||
|
@ -6461,6 +6485,7 @@ phutil_register_library_map(array(
|
|||
'PHUIDiffGraphViewTestCase' => 'PhabricatorTestCase',
|
||||
'PHUIDiffInlineCommentDetailView' => 'PHUIDiffInlineCommentView',
|
||||
'PHUIDiffInlineCommentEditView' => 'PHUIDiffInlineCommentView',
|
||||
'PHUIDiffInlineCommentPreviewListView' => 'AphrontView',
|
||||
'PHUIDiffInlineCommentRowScaffold' => 'AphrontView',
|
||||
'PHUIDiffInlineCommentTableScaffold' => 'AphrontView',
|
||||
'PHUIDiffInlineCommentUndoView' => 'PHUIDiffInlineCommentView',
|
||||
|
@ -6692,6 +6717,7 @@ phutil_register_library_map(array(
|
|||
'PhabricatorApplicationsApplication' => 'PhabricatorApplication',
|
||||
'PhabricatorApplicationsController' => 'PhabricatorController',
|
||||
'PhabricatorApplicationsListController' => 'PhabricatorApplicationsController',
|
||||
'PhabricatorApplyEditField' => 'PhabricatorEditField',
|
||||
'PhabricatorAsanaAuthProvider' => 'PhabricatorOAuth2AuthProvider',
|
||||
'PhabricatorAsanaConfigOptions' => 'PhabricatorApplicationConfigOptions',
|
||||
'PhabricatorAsanaSubtaskHasObjectEdgeType' => 'PhabricatorEdgeType',
|
||||
|
@ -7515,6 +7541,7 @@ phutil_register_library_map(array(
|
|||
'PhabricatorEditEngineAPIMethod' => 'ConduitAPIMethod',
|
||||
'PhabricatorEditEngineColumnsCommentAction' => 'PhabricatorEditEngineCommentAction',
|
||||
'PhabricatorEditEngineCommentAction' => 'Phobject',
|
||||
'PhabricatorEditEngineCommentActionGroup' => 'Phobject',
|
||||
'PhabricatorEditEngineConfiguration' => array(
|
||||
'PhabricatorSearchDAO',
|
||||
'PhabricatorApplicationTransactionInterface',
|
||||
|
@ -7545,10 +7572,12 @@ phutil_register_library_map(array(
|
|||
'PhabricatorEditEngineExtensionModule' => 'PhabricatorConfigModule',
|
||||
'PhabricatorEditEngineListController' => 'PhabricatorEditEngineController',
|
||||
'PhabricatorEditEnginePointsCommentAction' => 'PhabricatorEditEngineCommentAction',
|
||||
'PhabricatorEditEngineProfileMenuItem' => 'PhabricatorProfileMenuItem',
|
||||
'PhabricatorEditEngineQuery' => 'PhabricatorCursorPagedPolicyAwareQuery',
|
||||
'PhabricatorEditEngineSearchEngine' => 'PhabricatorApplicationSearchEngine',
|
||||
'PhabricatorEditEngineSelectCommentAction' => 'PhabricatorEditEngineCommentAction',
|
||||
'PhabricatorEditEngineSettingsPanel' => 'PhabricatorSettingsPanel',
|
||||
'PhabricatorEditEngineStaticCommentAction' => 'PhabricatorEditEngineCommentAction',
|
||||
'PhabricatorEditEngineTokenizerCommentAction' => 'PhabricatorEditEngineCommentAction',
|
||||
'PhabricatorEditField' => 'Phobject',
|
||||
'PhabricatorEditPage' => 'Phobject',
|
||||
|
@ -7688,7 +7717,7 @@ phutil_register_library_map(array(
|
|||
'PhabricatorFileImageTransform' => 'PhabricatorFileTransform',
|
||||
'PhabricatorFileInfoController' => 'PhabricatorFileController',
|
||||
'PhabricatorFileLightboxController' => 'PhabricatorFileController',
|
||||
'PhabricatorFileLinkView' => 'AphrontView',
|
||||
'PhabricatorFileLinkView' => 'AphrontTagView',
|
||||
'PhabricatorFileListController' => 'PhabricatorFileController',
|
||||
'PhabricatorFileQuery' => 'PhabricatorCursorPagedPolicyAwareQuery',
|
||||
'PhabricatorFileROT13StorageFormat' => 'PhabricatorFileStorageFormat',
|
||||
|
|
|
@ -548,7 +548,31 @@ final class AphrontRequest extends Phobject {
|
|||
public function getAbsoluteRequestURI() {
|
||||
$uri = $this->getRequestURI();
|
||||
$uri->setDomain($this->getHost());
|
||||
$uri->setProtocol($this->isHTTPS() ? 'https' : 'http');
|
||||
|
||||
if ($this->isHTTPS()) {
|
||||
$protocol = 'https';
|
||||
} else {
|
||||
$protocol = 'http';
|
||||
}
|
||||
|
||||
$uri->setProtocol($protocol);
|
||||
|
||||
// If the request used a nonstandard port, preserve it while building the
|
||||
// absolute URI.
|
||||
|
||||
// First, get the default port for the request protocol.
|
||||
$default_port = id(new PhutilURI($protocol.'://example.com/'))
|
||||
->getPortWithProtocolDefault();
|
||||
|
||||
// NOTE: See note in getHost() about malicious "Host" headers. This
|
||||
// construction defuses some obscure potential attacks.
|
||||
$port = id(new PhutilURI($protocol.'://'.$this->host))
|
||||
->getPort();
|
||||
|
||||
if (($port !== null) && ($port !== $default_port)) {
|
||||
$uri->setPort($port);
|
||||
}
|
||||
|
||||
return $uri;
|
||||
}
|
||||
|
||||
|
|
|
@ -429,8 +429,8 @@ abstract class PhabricatorApplication
|
|||
}
|
||||
|
||||
$cache = PhabricatorCaches::getRequestCache();
|
||||
$viewer_phid = $viewer->getPHID();
|
||||
$key = 'app.'.$class.'.installed.'.$viewer_phid;
|
||||
$viewer_fragment = $viewer->getCacheFragment();
|
||||
$key = 'app.'.$class.'.installed.'.$viewer_fragment;
|
||||
|
||||
$result = $cache->getKey($key);
|
||||
if ($result === null) {
|
||||
|
|
|
@ -23,6 +23,14 @@ abstract class PhabricatorCalendarEventDateTransaction
|
|||
}
|
||||
|
||||
public function getTransactionHasEffect($object, $old, $new) {
|
||||
// If either value is `null` (for example, when setting a recurring event
|
||||
// end date for the first time) and the other value is not `null`, this
|
||||
// transaction has an effect.
|
||||
$has_null = (($old === null) || ($new === null));
|
||||
if ($has_null) {
|
||||
return ($old !== $new);
|
||||
}
|
||||
|
||||
$editor = $this->getEditor();
|
||||
|
||||
$actor = $this->getActor();
|
||||
|
|
|
@ -55,65 +55,28 @@ final class DifferentialActionEmailCommand
|
|||
}
|
||||
|
||||
public function getCommandObjects() {
|
||||
$actions = array(
|
||||
DifferentialAction::ACTION_REJECT => 'request',
|
||||
DifferentialAction::ACTION_ABANDON => 'abandon',
|
||||
DifferentialAction::ACTION_RECLAIM => 'reclaim',
|
||||
DifferentialAction::ACTION_RESIGN => 'resign',
|
||||
DifferentialAction::ACTION_RETHINK => 'planchanges',
|
||||
DifferentialAction::ACTION_CLAIM => 'commandeer',
|
||||
);
|
||||
|
||||
if (PhabricatorEnv::getEnvConfig('differential.enable-email-accept')) {
|
||||
$actions[DifferentialAction::ACTION_ACCEPT] = 'accept';
|
||||
}
|
||||
|
||||
$aliases = array(
|
||||
DifferentialAction::ACTION_REJECT => array('reject'),
|
||||
DifferentialAction::ACTION_CLAIM => array('claim'),
|
||||
DifferentialAction::ACTION_RETHINK => array('rethink'),
|
||||
);
|
||||
|
||||
$summaries = array(
|
||||
DifferentialAction::ACTION_REJECT =>
|
||||
pht('Request changes to a revision.'),
|
||||
DifferentialAction::ACTION_ABANDON =>
|
||||
pht('Abandon a revision.'),
|
||||
DifferentialAction::ACTION_RECLAIM =>
|
||||
pht('Reclaim a revision.'),
|
||||
DifferentialAction::ACTION_RESIGN =>
|
||||
pht('Resign from a revision.'),
|
||||
DifferentialAction::ACTION_RETHINK =>
|
||||
pht('Plan changes to a revision.'),
|
||||
DifferentialAction::ACTION_CLAIM =>
|
||||
pht('Commandeer a revision.'),
|
||||
DifferentialAction::ACTION_ACCEPT =>
|
||||
pht('Accept a revision.'),
|
||||
);
|
||||
|
||||
$descriptions = array(
|
||||
|
||||
);
|
||||
$actions = DifferentialRevisionActionTransaction::loadAllActions();
|
||||
$actions = msort($actions, 'getRevisionActionOrderVector');
|
||||
|
||||
$objects = array();
|
||||
foreach ($actions as $action => $keyword) {
|
||||
$object = id(new DifferentialActionEmailCommand())
|
||||
foreach ($actions as $action) {
|
||||
$keyword = $action->getCommandKeyword();
|
||||
if ($keyword === null) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$aliases = $action->getCommandAliases();
|
||||
$summary = $action->getCommandSummary();
|
||||
|
||||
$object = id(new self())
|
||||
->setCommand($keyword)
|
||||
->setAction($action)
|
||||
->setCommandSummary($summaries[$action]);
|
||||
|
||||
if (isset($aliases[$action])) {
|
||||
$object->setCommandAliases($aliases[$action]);
|
||||
}
|
||||
|
||||
if (isset($descriptions[$action])) {
|
||||
$object->setCommandDescription($descriptions[$action]);
|
||||
}
|
||||
->setCommandAliases($aliases)
|
||||
->setAction($action->getTransactionTypeConstant())
|
||||
->setCommandSummary($summary);
|
||||
|
||||
$objects[] = $object;
|
||||
}
|
||||
|
||||
|
||||
return $objects;
|
||||
}
|
||||
|
||||
|
@ -131,8 +94,8 @@ final class DifferentialActionEmailCommand
|
|||
$xactions = array();
|
||||
|
||||
$xactions[] = $object->getApplicationTransactionTemplate()
|
||||
->setTransactionType(DifferentialTransaction::TYPE_ACTION)
|
||||
->setNewValue($this->getAction());
|
||||
->setTransactionType($this->getAction())
|
||||
->setNewValue(true);
|
||||
|
||||
return $xactions;
|
||||
}
|
||||
|
|
|
@ -9,6 +9,7 @@ final class DifferentialReviewerStatus extends Phobject {
|
|||
const STATUS_COMMENTED = 'commented';
|
||||
const STATUS_ACCEPTED_OLDER = 'accepted-older';
|
||||
const STATUS_REJECTED_OLDER = 'rejected-older';
|
||||
const STATUS_RESIGNED = 'resigned';
|
||||
|
||||
/**
|
||||
* Returns the relative strength of a status, used to pick a winner when a
|
||||
|
@ -34,6 +35,7 @@ final class DifferentialReviewerStatus extends Phobject {
|
|||
|
||||
self::STATUS_ACCEPTED => 5,
|
||||
self::STATUS_REJECTED => 5,
|
||||
self::STATUS_RESIGNED => 5,
|
||||
);
|
||||
|
||||
return idx($map, $constant, 0);
|
||||
|
|
|
@ -1,139 +0,0 @@
|
|||
<?php
|
||||
|
||||
final class DifferentialCommentPreviewController
|
||||
extends DifferentialController {
|
||||
|
||||
public function handleRequest(AphrontRequest $request) {
|
||||
$viewer = $this->getViewer();
|
||||
$id = $request->getURIData('id');
|
||||
|
||||
$revision = id(new DifferentialRevisionQuery())
|
||||
->setViewer($viewer)
|
||||
->withIDs(array($id))
|
||||
->executeOne();
|
||||
if (!$revision) {
|
||||
return new Aphront404Response();
|
||||
}
|
||||
|
||||
$type_comment = PhabricatorTransactions::TYPE_COMMENT;
|
||||
$type_action = DifferentialTransaction::TYPE_ACTION;
|
||||
$type_edge = PhabricatorTransactions::TYPE_EDGE;
|
||||
$type_subscribers = PhabricatorTransactions::TYPE_SUBSCRIBERS;
|
||||
|
||||
$xactions = array();
|
||||
|
||||
$action = $request->getStr('action');
|
||||
switch ($action) {
|
||||
case DifferentialAction::ACTION_COMMENT:
|
||||
case DifferentialAction::ACTION_ADDREVIEWERS:
|
||||
case DifferentialAction::ACTION_ADDCCS:
|
||||
break;
|
||||
default:
|
||||
$xactions[] = id(new DifferentialTransaction())
|
||||
->setTransactionType($type_action)
|
||||
->setNewValue($action);
|
||||
break;
|
||||
}
|
||||
|
||||
$edge_reviewer = DifferentialRevisionHasReviewerEdgeType::EDGECONST;
|
||||
|
||||
$reviewers = $request->getStrList('reviewers');
|
||||
if (DifferentialAction::allowReviewers($action) && $reviewers) {
|
||||
$faux_edges = array();
|
||||
foreach ($reviewers as $phid) {
|
||||
$faux_edges[$phid] = array(
|
||||
'src' => $revision->getPHID(),
|
||||
'type' => $edge_reviewer,
|
||||
'dst' => $phid,
|
||||
);
|
||||
}
|
||||
|
||||
$xactions[] = id(new DifferentialTransaction())
|
||||
->setTransactionType($type_edge)
|
||||
->setMetadataValue('edge:type', $edge_reviewer)
|
||||
->setOldValue(array())
|
||||
->setNewValue($faux_edges);
|
||||
}
|
||||
|
||||
$ccs = $request->getStrList('ccs');
|
||||
if ($ccs) {
|
||||
$xactions[] = id(new DifferentialTransaction())
|
||||
->setTransactionType($type_subscribers)
|
||||
->setOldValue(array())
|
||||
->setNewValue(array_fuse($ccs));
|
||||
}
|
||||
|
||||
// Add a comment transaction if there's nothing, so we'll generate a
|
||||
// nonempty result.
|
||||
if (strlen($request->getStr('content')) || !$xactions) {
|
||||
$xactions[] = id(new DifferentialTransaction())
|
||||
->setTransactionType($type_comment)
|
||||
->attachComment(
|
||||
id(new ManiphestTransactionComment())
|
||||
->setContent($request->getStr('content')));
|
||||
}
|
||||
|
||||
foreach ($xactions as $xaction) {
|
||||
$xaction->setAuthorPHID($viewer->getPHID());
|
||||
}
|
||||
|
||||
$engine = new PhabricatorMarkupEngine();
|
||||
$engine->setViewer($request->getUser());
|
||||
foreach ($xactions as $xaction) {
|
||||
if ($xaction->hasComment()) {
|
||||
$engine->addObject(
|
||||
$xaction->getComment(),
|
||||
PhabricatorApplicationTransactionComment::MARKUP_FIELD_COMMENT);
|
||||
}
|
||||
}
|
||||
$engine->process();
|
||||
|
||||
$phids = mpull($xactions, 'getRequiredHandlePHIDs');
|
||||
$phids = array_mergev($phids);
|
||||
|
||||
$handles = id(new PhabricatorHandleQuery())
|
||||
->setViewer($viewer)
|
||||
->withPHIDs($phids)
|
||||
->execute();
|
||||
|
||||
foreach ($xactions as $xaction) {
|
||||
$xaction->setHandles($handles);
|
||||
}
|
||||
|
||||
$view = id(new DifferentialTransactionView())
|
||||
->setUser($viewer)
|
||||
->setTransactions($xactions)
|
||||
->setIsPreview(true);
|
||||
|
||||
$metadata = array(
|
||||
'reviewers' => $reviewers,
|
||||
'ccs' => $ccs,
|
||||
);
|
||||
if ($action != DifferentialAction::ACTION_COMMENT) {
|
||||
$metadata['action'] = $action;
|
||||
}
|
||||
|
||||
$draft_key = 'differential-comment-'.$id;
|
||||
$draft = id(new PhabricatorDraft())
|
||||
->setAuthorPHID($viewer->getPHID())
|
||||
->setDraftKey($draft_key)
|
||||
->setDraft($request->getStr('content'))
|
||||
->setMetadata($metadata)
|
||||
->replaceOrDelete();
|
||||
if ($draft->isDeleted()) {
|
||||
DifferentialDraft::deleteHasDraft(
|
||||
$viewer->getPHID(),
|
||||
$revision->getPHID(),
|
||||
$draft_key);
|
||||
} else {
|
||||
DifferentialDraft::markHasDraft(
|
||||
$viewer->getPHID(),
|
||||
$revision->getPHID(),
|
||||
$draft_key);
|
||||
}
|
||||
|
||||
return id(new AphrontAjaxResponse())
|
||||
->setContent((string)phutil_implode_html('', $view->buildEvents()));
|
||||
}
|
||||
|
||||
}
|
|
@ -1,143 +0,0 @@
|
|||
<?php
|
||||
|
||||
final class DifferentialCommentSaveController
|
||||
extends DifferentialController {
|
||||
|
||||
public function handleRequest(AphrontRequest $request) {
|
||||
$viewer = $this->getViewer();
|
||||
$id = $request->getURIData('id');
|
||||
|
||||
if (!$request->isFormPost()) {
|
||||
return new Aphront400Response();
|
||||
}
|
||||
|
||||
$revision = id(new DifferentialRevisionQuery())
|
||||
->setViewer($viewer)
|
||||
->withIDs(array($id))
|
||||
->needReviewerStatus(true)
|
||||
->needReviewerAuthority(true)
|
||||
->executeOne();
|
||||
if (!$revision) {
|
||||
return new Aphront404Response();
|
||||
}
|
||||
|
||||
$type_action = DifferentialTransaction::TYPE_ACTION;
|
||||
$type_subscribers = PhabricatorTransactions::TYPE_SUBSCRIBERS;
|
||||
$type_edge = PhabricatorTransactions::TYPE_EDGE;
|
||||
$type_comment = PhabricatorTransactions::TYPE_COMMENT;
|
||||
$type_inline = DifferentialTransaction::TYPE_INLINE;
|
||||
|
||||
$edge_reviewer = DifferentialRevisionHasReviewerEdgeType::EDGECONST;
|
||||
|
||||
$xactions = array();
|
||||
|
||||
$action = $request->getStr('action');
|
||||
switch ($action) {
|
||||
case DifferentialAction::ACTION_COMMENT:
|
||||
case DifferentialAction::ACTION_ADDREVIEWERS:
|
||||
case DifferentialAction::ACTION_ADDCCS:
|
||||
// These transaction types have no direct effect, they just
|
||||
// accompany other transaction types which can have an effect.
|
||||
break;
|
||||
default:
|
||||
$xactions[] = id(new DifferentialTransaction())
|
||||
->setTransactionType($type_action)
|
||||
->setNewValue($request->getStr('action'));
|
||||
break;
|
||||
}
|
||||
|
||||
$ccs = $request->getArr('ccs');
|
||||
if ($ccs) {
|
||||
$xactions[] = id(new DifferentialTransaction())
|
||||
->setTransactionType($type_subscribers)
|
||||
->setNewValue(array('+' => $ccs));
|
||||
}
|
||||
|
||||
$current_reviewers = mpull(
|
||||
$revision->getReviewerStatus(),
|
||||
null,
|
||||
'getReviewerPHID');
|
||||
|
||||
$reviewer_edges = array();
|
||||
$add_reviewers = $request->getArr('reviewers');
|
||||
foreach ($add_reviewers as $reviewer_phid) {
|
||||
if (isset($current_reviewers[$reviewer_phid])) {
|
||||
continue;
|
||||
}
|
||||
$reviewer = new DifferentialReviewerProxy(
|
||||
$reviewer_phid,
|
||||
array(
|
||||
'status' => DifferentialReviewerStatus::STATUS_ADDED,
|
||||
));
|
||||
$reviewer_edges[$reviewer_phid] = array(
|
||||
'data' => $reviewer->getEdgeData(),
|
||||
);
|
||||
}
|
||||
|
||||
if ($add_reviewers) {
|
||||
$xactions[] = id(new DifferentialTransaction())
|
||||
->setTransactionType($type_edge)
|
||||
->setMetadataValue('edge:type', $edge_reviewer)
|
||||
->setNewValue(array('+' => $reviewer_edges));
|
||||
}
|
||||
|
||||
$inlines = DifferentialTransactionQuery::loadUnsubmittedInlineComments(
|
||||
$viewer,
|
||||
$revision);
|
||||
foreach ($inlines as $inline) {
|
||||
$xactions[] = id(new DifferentialTransaction())
|
||||
->setTransactionType($type_inline)
|
||||
->attachComment($inline);
|
||||
}
|
||||
|
||||
// NOTE: If there are no other transactions, add an empty comment
|
||||
// transaction so that we'll raise a more user-friendly error message,
|
||||
// to the effect of "you can not post an empty comment".
|
||||
$no_xactions = !$xactions;
|
||||
|
||||
$comment = $request->getStr('comment');
|
||||
if (strlen($comment) || $no_xactions) {
|
||||
$xactions[] = id(new DifferentialTransaction())
|
||||
->setTransactionType($type_comment)
|
||||
->attachComment(
|
||||
id(new DifferentialTransactionComment())
|
||||
->setRevisionPHID($revision->getPHID())
|
||||
->setContent($comment));
|
||||
}
|
||||
|
||||
|
||||
$editor = id(new DifferentialTransactionEditor())
|
||||
->setActor($viewer)
|
||||
->setContentSourceFromRequest($request)
|
||||
->setContinueOnMissingFields(true)
|
||||
->setContinueOnNoEffect($request->isContinueRequest());
|
||||
|
||||
$revision_uri = '/D'.$revision->getID();
|
||||
|
||||
try {
|
||||
$editor->applyTransactions($revision, $xactions);
|
||||
} catch (PhabricatorApplicationTransactionNoEffectException $ex) {
|
||||
return id(new PhabricatorApplicationTransactionNoEffectResponse())
|
||||
->setCancelURI($revision_uri)
|
||||
->setException($ex);
|
||||
} catch (PhabricatorApplicationTransactionValidationException $ex) {
|
||||
return id(new PhabricatorApplicationTransactionValidationResponse())
|
||||
->setCancelURI($revision_uri)
|
||||
->setException($ex);
|
||||
}
|
||||
|
||||
$user = $request->getUser();
|
||||
$draft = id(new PhabricatorDraft())->loadOneWhere(
|
||||
'authorPHID = %s AND draftKey = %s',
|
||||
$user->getPHID(),
|
||||
'differential-comment-'.$revision->getID());
|
||||
if ($draft) {
|
||||
$draft->delete();
|
||||
}
|
||||
DifferentialDraft::deleteAllDrafts($user->getPHID(), $revision->getPHID());
|
||||
|
||||
return id(new AphrontRedirectResponse())
|
||||
->setURI('/D'.$revision->getID());
|
||||
}
|
||||
|
||||
}
|
|
@ -1,44 +0,0 @@
|
|||
<?php
|
||||
|
||||
final class DifferentialInlineCommentPreviewController
|
||||
extends PhabricatorInlineCommentPreviewController {
|
||||
|
||||
protected function loadInlineComments() {
|
||||
$viewer = $this->getViewer();
|
||||
|
||||
$revision = id(new DifferentialRevisionQuery())
|
||||
->setViewer($viewer)
|
||||
->withIDs(array($this->getRevisionID()))
|
||||
->executeOne();
|
||||
if (!$revision) {
|
||||
return array();
|
||||
}
|
||||
|
||||
return id(new DifferentialInlineCommentQuery())
|
||||
->setViewer($this->getViewer())
|
||||
->withDrafts(true)
|
||||
->withAuthorPHIDs(array($viewer->getPHID()))
|
||||
->withRevisionPHIDs(array($revision->getPHID()))
|
||||
->needHidden(true)
|
||||
->execute();
|
||||
}
|
||||
|
||||
protected function loadObjectOwnerPHID() {
|
||||
$viewer = $this->getViewer();
|
||||
|
||||
$revision = id(new DifferentialRevisionQuery())
|
||||
->setViewer($viewer)
|
||||
->withIDs(array($this->getRevisionID()))
|
||||
->executeOne();
|
||||
if (!$revision) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return $revision->getAuthorPHID();
|
||||
}
|
||||
|
||||
|
||||
private function getRevisionID() {
|
||||
return $this->getRequest()->getURIData('id');
|
||||
}
|
||||
}
|
|
@ -16,18 +16,17 @@ final class DifferentialRevisionViewController extends DifferentialController {
|
|||
|
||||
$revision = id(new DifferentialRevisionQuery())
|
||||
->withIDs(array($this->revisionID))
|
||||
->setViewer($request->getUser())
|
||||
->setViewer($viewer)
|
||||
->needRelationships(true)
|
||||
->needReviewerStatus(true)
|
||||
->needReviewerAuthority(true)
|
||||
->executeOne();
|
||||
|
||||
if (!$revision) {
|
||||
return new Aphront404Response();
|
||||
}
|
||||
|
||||
$diffs = id(new DifferentialDiffQuery())
|
||||
->setViewer($request->getUser())
|
||||
->setViewer($viewer)
|
||||
->withRevisionIDs(array($this->revisionID))
|
||||
->execute();
|
||||
$diffs = array_reverse($diffs, $preserve_keys = true);
|
||||
|
@ -255,17 +254,14 @@ final class DifferentialRevisionViewController extends DifferentialController {
|
|||
$target,
|
||||
$revision);
|
||||
|
||||
$comment_view = $this->buildTransactions(
|
||||
$timeline = $this->buildTransactions(
|
||||
$revision,
|
||||
$diff_vs ? $diffs[$diff_vs] : $target,
|
||||
$target,
|
||||
$old_ids,
|
||||
$new_ids);
|
||||
|
||||
if (!$viewer_is_anonymous) {
|
||||
$comment_view->setQuoteRef('D'.$revision->getID());
|
||||
$comment_view->setQuoteTargetID('comment-content');
|
||||
}
|
||||
$timeline->setQuoteRef($revision->getMonogram());
|
||||
|
||||
$changeset_view = id(new DifferentialChangesetListView())
|
||||
->setChangesets($changesets)
|
||||
|
@ -390,11 +386,6 @@ final class DifferentialRevisionViewController extends DifferentialController {
|
|||
->setBackground(PHUIObjectBoxView::BLUE_PROPERTY)
|
||||
->addTabGroup($tab_group);
|
||||
|
||||
$comment_form = null;
|
||||
if (!$viewer_is_anonymous) {
|
||||
$comment_form = $this->buildCommentForm($revision, $field_list);
|
||||
}
|
||||
|
||||
$signatures = DifferentialRequiredSignaturesField::loadForRevision(
|
||||
$revision);
|
||||
$missing_signatures = false;
|
||||
|
@ -426,20 +417,33 @@ final class DifferentialRevisionViewController extends DifferentialController {
|
|||
);
|
||||
}
|
||||
|
||||
if ($comment_form) {
|
||||
$footer[] = $comment_form;
|
||||
} else {
|
||||
// TODO: For now, just use this to get "Login to Comment".
|
||||
$footer[] = id(new PhabricatorApplicationTransactionCommentView())
|
||||
->setUser($viewer)
|
||||
->setRequestURI($request->getRequestURI());
|
||||
$comment_view = id(new DifferentialRevisionEditEngine())
|
||||
->setViewer($viewer)
|
||||
->buildEditEngineCommentView($revision);
|
||||
|
||||
$comment_view->setTransactionTimeline($timeline);
|
||||
|
||||
$review_warnings = array();
|
||||
foreach ($field_list->getFields() as $field) {
|
||||
$review_warnings[] = $field->getWarningsForDetailView();
|
||||
}
|
||||
$review_warnings = array_mergev($review_warnings);
|
||||
|
||||
if ($review_warnings) {
|
||||
$warnings_view = id(new PHUIInfoView())
|
||||
->setSeverity(PHUIInfoView::SEVERITY_WARNING)
|
||||
->setErrors($review_warnings);
|
||||
|
||||
$comment_view->setInfoView($warnings_view);
|
||||
}
|
||||
|
||||
$object_id = 'D'.$revision->getID();
|
||||
$footer[] = $comment_view;
|
||||
|
||||
$monogram = $revision->getMonogram();
|
||||
$operations_box = $this->buildOperationsBox($revision);
|
||||
|
||||
$crumbs = $this->buildApplicationCrumbs();
|
||||
$crumbs->addTextCrumb($object_id, '/'.$object_id);
|
||||
$crumbs->addTextCrumb($monogram, $revision->getURI());
|
||||
$crumbs->setBorder(true);
|
||||
|
||||
$filetree_on = $viewer->compareUserSetting(
|
||||
|
@ -452,39 +456,32 @@ final class DifferentialRevisionViewController extends DifferentialController {
|
|||
$collapsed_value = $viewer->getUserSetting($collapsed_key);
|
||||
|
||||
$nav = id(new DifferentialChangesetFileTreeSideNavBuilder())
|
||||
->setTitle('D'.$revision->getID())
|
||||
->setBaseURI(new PhutilURI('/D'.$revision->getID()))
|
||||
->setTitle($monogram)
|
||||
->setBaseURI(new PhutilURI($revision->getURI()))
|
||||
->setCollapsed((bool)$collapsed_value)
|
||||
->build($changesets);
|
||||
}
|
||||
|
||||
// Haunt Mode
|
||||
$pane_id = celerity_generate_unique_node_id();
|
||||
Javelin::initBehavior(
|
||||
'differential-keyboard-navigation',
|
||||
array(
|
||||
'haunt' => $pane_id,
|
||||
));
|
||||
Javelin::initBehavior('differential-user-select');
|
||||
|
||||
$view = id(new PHUITwoColumnView())
|
||||
->setHeader($header)
|
||||
->setSubheader($subheader)
|
||||
->setCurtain($curtain)
|
||||
->setID($pane_id)
|
||||
->setMainColumn(array(
|
||||
$operations_box,
|
||||
$info_view,
|
||||
$details,
|
||||
$diff_detail_box,
|
||||
$unit_box,
|
||||
$comment_view,
|
||||
$signature_message,
|
||||
))
|
||||
->setMainColumn(
|
||||
array(
|
||||
$operations_box,
|
||||
$info_view,
|
||||
$details,
|
||||
$diff_detail_box,
|
||||
$unit_box,
|
||||
$timeline,
|
||||
$signature_message,
|
||||
))
|
||||
->setFooter($footer);
|
||||
|
||||
$page = $this->newPage()
|
||||
->setTitle($object_id.' '.$revision->getTitle())
|
||||
->setTitle($monogram.' '.$revision->getTitle())
|
||||
->setCrumbs($crumbs)
|
||||
->setPageObjectPHIDs(array($revision->getPHID()))
|
||||
->appendChild($view);
|
||||
|
@ -613,172 +610,6 @@ final class DifferentialRevisionViewController extends DifferentialController {
|
|||
return $curtain;
|
||||
}
|
||||
|
||||
private function buildCommentForm(
|
||||
DifferentialRevision $revision,
|
||||
$field_list) {
|
||||
|
||||
$viewer = $this->getViewer();
|
||||
|
||||
$draft = id(new PhabricatorDraft())->loadOneWhere(
|
||||
'authorPHID = %s AND draftKey = %s',
|
||||
$viewer->getPHID(),
|
||||
'differential-comment-'.$revision->getID());
|
||||
|
||||
$reviewers = array();
|
||||
$ccs = array();
|
||||
if ($draft) {
|
||||
$reviewers = idx($draft->getMetadata(), 'reviewers', array());
|
||||
$ccs = idx($draft->getMetadata(), 'ccs', array());
|
||||
if ($reviewers || $ccs) {
|
||||
$handles = $this->loadViewerHandles(array_merge($reviewers, $ccs));
|
||||
$reviewers = array_select_keys($handles, $reviewers);
|
||||
$ccs = array_select_keys($handles, $ccs);
|
||||
}
|
||||
}
|
||||
|
||||
$comment_form = id(new DifferentialAddCommentView())
|
||||
->setRevision($revision);
|
||||
|
||||
$review_warnings = array();
|
||||
foreach ($field_list->getFields() as $field) {
|
||||
$review_warnings[] = $field->getWarningsForDetailView();
|
||||
}
|
||||
$review_warnings = array_mergev($review_warnings);
|
||||
|
||||
if ($review_warnings) {
|
||||
$review_warnings_panel = id(new PHUIInfoView())
|
||||
->setSeverity(PHUIInfoView::SEVERITY_WARNING)
|
||||
->setErrors($review_warnings);
|
||||
$comment_form->setInfoView($review_warnings_panel);
|
||||
}
|
||||
|
||||
$action_uri = $this->getApplicationURI(
|
||||
'comment/save/'.$revision->getID().'/');
|
||||
|
||||
$comment_form->setActions($this->getRevisionCommentActions($revision))
|
||||
->setActionURI($action_uri)
|
||||
->setUser($viewer)
|
||||
->setDraft($draft)
|
||||
->setReviewers(mpull($reviewers, 'getFullName', 'getPHID'))
|
||||
->setCCs(mpull($ccs, 'getFullName', 'getPHID'));
|
||||
|
||||
// TODO: This just makes the "Z" key work. Generalize this and remove
|
||||
// it at some point.
|
||||
$comment_form = phutil_tag(
|
||||
'div',
|
||||
array(
|
||||
'class' => 'differential-add-comment-panel',
|
||||
),
|
||||
$comment_form);
|
||||
return $comment_form;
|
||||
}
|
||||
|
||||
private function getRevisionCommentActions(DifferentialRevision $revision) {
|
||||
$actions = array(
|
||||
DifferentialAction::ACTION_COMMENT => true,
|
||||
);
|
||||
|
||||
$viewer = $this->getViewer();
|
||||
$viewer_phid = $viewer->getPHID();
|
||||
$viewer_is_owner = ($viewer_phid == $revision->getAuthorPHID());
|
||||
$viewer_is_reviewer = in_array($viewer_phid, $revision->getReviewers());
|
||||
$status = $revision->getStatus();
|
||||
|
||||
$viewer_has_accepted = false;
|
||||
$viewer_has_rejected = false;
|
||||
$status_accepted = DifferentialReviewerStatus::STATUS_ACCEPTED;
|
||||
$status_rejected = DifferentialReviewerStatus::STATUS_REJECTED;
|
||||
foreach ($revision->getReviewerStatus() as $reviewer) {
|
||||
if ($reviewer->getReviewerPHID() == $viewer_phid) {
|
||||
if ($reviewer->getStatus() == $status_accepted) {
|
||||
$viewer_has_accepted = true;
|
||||
}
|
||||
if ($reviewer->getStatus() == $status_rejected) {
|
||||
$viewer_has_rejected = true;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
$allow_self_accept = PhabricatorEnv::getEnvConfig(
|
||||
'differential.allow-self-accept');
|
||||
$always_allow_abandon = PhabricatorEnv::getEnvConfig(
|
||||
'differential.always-allow-abandon');
|
||||
$always_allow_close = PhabricatorEnv::getEnvConfig(
|
||||
'differential.always-allow-close');
|
||||
$allow_reopen = PhabricatorEnv::getEnvConfig(
|
||||
'differential.allow-reopen');
|
||||
|
||||
if ($viewer_is_owner) {
|
||||
switch ($status) {
|
||||
case ArcanistDifferentialRevisionStatus::NEEDS_REVIEW:
|
||||
$actions[DifferentialAction::ACTION_ACCEPT] = $allow_self_accept;
|
||||
$actions[DifferentialAction::ACTION_ABANDON] = true;
|
||||
$actions[DifferentialAction::ACTION_RETHINK] = true;
|
||||
break;
|
||||
case ArcanistDifferentialRevisionStatus::NEEDS_REVISION:
|
||||
case ArcanistDifferentialRevisionStatus::CHANGES_PLANNED:
|
||||
$actions[DifferentialAction::ACTION_ACCEPT] = $allow_self_accept;
|
||||
$actions[DifferentialAction::ACTION_ABANDON] = true;
|
||||
$actions[DifferentialAction::ACTION_REQUEST] = true;
|
||||
break;
|
||||
case ArcanistDifferentialRevisionStatus::ACCEPTED:
|
||||
$actions[DifferentialAction::ACTION_ABANDON] = true;
|
||||
$actions[DifferentialAction::ACTION_REQUEST] = true;
|
||||
$actions[DifferentialAction::ACTION_RETHINK] = true;
|
||||
$actions[DifferentialAction::ACTION_CLOSE] = true;
|
||||
break;
|
||||
case ArcanistDifferentialRevisionStatus::CLOSED:
|
||||
break;
|
||||
case ArcanistDifferentialRevisionStatus::ABANDONED:
|
||||
$actions[DifferentialAction::ACTION_RECLAIM] = true;
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
switch ($status) {
|
||||
case ArcanistDifferentialRevisionStatus::NEEDS_REVIEW:
|
||||
$actions[DifferentialAction::ACTION_ABANDON] = $always_allow_abandon;
|
||||
$actions[DifferentialAction::ACTION_ACCEPT] = true;
|
||||
$actions[DifferentialAction::ACTION_REJECT] = true;
|
||||
$actions[DifferentialAction::ACTION_RESIGN] = $viewer_is_reviewer;
|
||||
break;
|
||||
case ArcanistDifferentialRevisionStatus::NEEDS_REVISION:
|
||||
case ArcanistDifferentialRevisionStatus::CHANGES_PLANNED:
|
||||
$actions[DifferentialAction::ACTION_ABANDON] = $always_allow_abandon;
|
||||
$actions[DifferentialAction::ACTION_ACCEPT] = true;
|
||||
$actions[DifferentialAction::ACTION_REJECT] = !$viewer_has_rejected;
|
||||
$actions[DifferentialAction::ACTION_RESIGN] = $viewer_is_reviewer;
|
||||
break;
|
||||
case ArcanistDifferentialRevisionStatus::ACCEPTED:
|
||||
$actions[DifferentialAction::ACTION_ABANDON] = $always_allow_abandon;
|
||||
$actions[DifferentialAction::ACTION_ACCEPT] = !$viewer_has_accepted;
|
||||
$actions[DifferentialAction::ACTION_REJECT] = true;
|
||||
$actions[DifferentialAction::ACTION_RESIGN] = $viewer_is_reviewer;
|
||||
break;
|
||||
case ArcanistDifferentialRevisionStatus::CLOSED:
|
||||
case ArcanistDifferentialRevisionStatus::ABANDONED:
|
||||
break;
|
||||
}
|
||||
if ($status != ArcanistDifferentialRevisionStatus::CLOSED) {
|
||||
$actions[DifferentialAction::ACTION_CLAIM] = true;
|
||||
$actions[DifferentialAction::ACTION_CLOSE] = $always_allow_close;
|
||||
}
|
||||
}
|
||||
|
||||
$actions[DifferentialAction::ACTION_ADDREVIEWERS] = true;
|
||||
$actions[DifferentialAction::ACTION_ADDCCS] = true;
|
||||
$actions[DifferentialAction::ACTION_REOPEN] = $allow_reopen &&
|
||||
($status == ArcanistDifferentialRevisionStatus::CLOSED);
|
||||
|
||||
$actions = array_keys(array_filter($actions));
|
||||
$actions_dict = array();
|
||||
foreach ($actions as $action) {
|
||||
$actions_dict[$action] = DifferentialAction::getActionVerb($action);
|
||||
}
|
||||
|
||||
return $actions_dict;
|
||||
}
|
||||
|
||||
private function loadHistoryDiffStatus(array $diffs) {
|
||||
assert_instances_of($diffs, 'DifferentialDiff');
|
||||
|
||||
|
|
|
@ -32,9 +32,31 @@ final class DifferentialUnitField
|
|||
}
|
||||
|
||||
public function getWarningsForDetailView() {
|
||||
$status = $this->getObject()->getActiveDiff()->getUnitStatus();
|
||||
|
||||
$warnings = array();
|
||||
|
||||
$viewer = $this->getViewer();
|
||||
$diff = $this->getObject()->getActiveDiff();
|
||||
|
||||
$buildable = id(new HarbormasterBuildableQuery())
|
||||
->setViewer($viewer)
|
||||
->withBuildablePHIDs(array($diff->getPHID()))
|
||||
->withManualBuildables(false)
|
||||
->executeOne();
|
||||
if ($buildable) {
|
||||
switch ($buildable->getBuildableStatus()) {
|
||||
case HarbormasterBuildable::STATUS_BUILDING:
|
||||
$warnings[] = pht(
|
||||
'These changes have not finished building yet and may have build '.
|
||||
'failures.');
|
||||
break;
|
||||
case HarbormasterBuildable::STATUS_FAILED:
|
||||
$warnings[] = pht(
|
||||
'These changes have failed to build.');
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
$status = $this->getObject()->getActiveDiff()->getUnitStatus();
|
||||
if ($status < DifferentialUnitStatus::UNIT_WARN) {
|
||||
// Don't show any warnings.
|
||||
} else if ($status == DifferentialUnitStatus::UNIT_AUTO_SKIP) {
|
||||
|
|
|
@ -9,6 +9,9 @@ final class DifferentialRevisionEditEngine
|
|||
|
||||
const KEY_UPDATE = 'update';
|
||||
|
||||
const ACTIONGROUP_REVIEW = 'review';
|
||||
const ACTIONGROUP_REVISION = 'revision';
|
||||
|
||||
public function getEngineName() {
|
||||
return pht('Revisions');
|
||||
}
|
||||
|
@ -38,7 +41,8 @@ final class DifferentialRevisionEditEngine
|
|||
protected function newObjectQuery() {
|
||||
return id(new DifferentialRevisionQuery())
|
||||
->needActiveDiffs(true)
|
||||
->needReviewerStatus(true);
|
||||
->needReviewerStatus(true)
|
||||
->needReviewerAuthority(true);
|
||||
}
|
||||
|
||||
protected function getObjectCreateTitleText($object) {
|
||||
|
@ -73,6 +77,10 @@ final class DifferentialRevisionEditEngine
|
|||
return $object->getURI();
|
||||
}
|
||||
|
||||
protected function getEditorURI() {
|
||||
return $this->getApplication()->getApplicationURI('revision/edit/');
|
||||
}
|
||||
|
||||
public function setDiff(DifferentialDiff $diff) {
|
||||
$this->diff = $diff;
|
||||
return $this;
|
||||
|
@ -82,7 +90,19 @@ final class DifferentialRevisionEditEngine
|
|||
return $this->diff;
|
||||
}
|
||||
|
||||
protected function newCommentActionGroups() {
|
||||
return array(
|
||||
id(new PhabricatorEditEngineCommentActionGroup())
|
||||
->setKey(self::ACTIONGROUP_REVIEW)
|
||||
->setLabel(pht('Review Actions')),
|
||||
id(new PhabricatorEditEngineCommentActionGroup())
|
||||
->setKey(self::ACTIONGROUP_REVISION)
|
||||
->setLabel(pht('Revision Actions')),
|
||||
);
|
||||
}
|
||||
|
||||
protected function buildCustomEditFields($object) {
|
||||
$viewer = $this->getViewer();
|
||||
|
||||
$plan_required = PhabricatorEnv::getEnvConfig(
|
||||
'differential.require-test-plan-field');
|
||||
|
@ -176,6 +196,7 @@ final class DifferentialRevisionEditEngine
|
|||
->setUseEdgeTransactions(true)
|
||||
->setTransactionType(
|
||||
DifferentialRevisionReviewersTransaction::TRANSACTIONTYPE)
|
||||
->setCommentActionLabel(pht('Change Reviewers'))
|
||||
->setDescription(pht('Reviewers for this revision.'))
|
||||
->setConduitDescription(pht('Change the reviewers for this revision.'))
|
||||
->setConduitTypeDescription(pht('New reviewers.'))
|
||||
|
@ -207,6 +228,13 @@ final class DifferentialRevisionEditEngine
|
|||
->setConduitTypeDescription(pht('List of tasks.'))
|
||||
->setValue(array());
|
||||
|
||||
$actions = DifferentialRevisionActionTransaction::loadAllActions();
|
||||
$actions = msortv($actions, 'getRevisionActionOrderVector');
|
||||
|
||||
foreach ($actions as $key => $action) {
|
||||
$fields[] = $action->newEditField($object, $viewer);
|
||||
}
|
||||
|
||||
return $fields;
|
||||
}
|
||||
|
||||
|
@ -219,4 +247,77 @@ final class DifferentialRevisionEditEngine
|
|||
return isset($fields[$key]);
|
||||
}
|
||||
|
||||
protected function newAutomaticCommentTransactions($object) {
|
||||
$viewer = $this->getViewer();
|
||||
$xactions = array();
|
||||
|
||||
$inlines = DifferentialTransactionQuery::loadUnsubmittedInlineComments(
|
||||
$viewer,
|
||||
$object);
|
||||
$inlines = msort($inlines, 'getID');
|
||||
|
||||
foreach ($inlines as $inline) {
|
||||
$xactions[] = id(new DifferentialTransaction())
|
||||
->setTransactionType(DifferentialTransaction::TYPE_INLINE)
|
||||
->attachComment($inline);
|
||||
}
|
||||
|
||||
$viewer_phid = $viewer->getPHID();
|
||||
$viewer_is_author = ($object->getAuthorPHID() == $viewer_phid);
|
||||
if ($viewer_is_author) {
|
||||
$state_map = PhabricatorTransactions::getInlineStateMap();
|
||||
|
||||
$inlines = id(new DifferentialDiffInlineCommentQuery())
|
||||
->setViewer($viewer)
|
||||
->withRevisionPHIDs(array($object->getPHID()))
|
||||
->withFixedStates(array_keys($state_map))
|
||||
->execute();
|
||||
if ($inlines) {
|
||||
$old_value = mpull($inlines, 'getFixedState', 'getPHID');
|
||||
$new_value = array();
|
||||
foreach ($old_value as $key => $state) {
|
||||
$new_value[$key] = $state_map[$state];
|
||||
}
|
||||
|
||||
$xactions[] = id(new DifferentialTransaction())
|
||||
->setTransactionType(PhabricatorTransactions::TYPE_INLINESTATE)
|
||||
->setIgnoreOnNoEffect(true)
|
||||
->setOldValue($old_value)
|
||||
->setNewValue($new_value);
|
||||
}
|
||||
}
|
||||
|
||||
return $xactions;
|
||||
}
|
||||
|
||||
protected function newCommentPreviewContent($object, array $xactions) {
|
||||
$viewer = $this->getViewer();
|
||||
$type_inline = DifferentialTransaction::TYPE_INLINE;
|
||||
|
||||
$inlines = array();
|
||||
foreach ($xactions as $xaction) {
|
||||
if ($xaction->getTransactionType() === $type_inline) {
|
||||
$inlines[] = $xaction->getComment();
|
||||
}
|
||||
}
|
||||
|
||||
$content = array();
|
||||
|
||||
if ($inlines) {
|
||||
$inline_preview = id(new PHUIDiffInlineCommentPreviewListView())
|
||||
->setViewer($viewer)
|
||||
->setInlineComments($inlines);
|
||||
|
||||
$content[] = phutil_tag(
|
||||
'div',
|
||||
array(
|
||||
'id' => 'inline-comment-preview',
|
||||
),
|
||||
$inline_preview);
|
||||
}
|
||||
|
||||
return $content;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
|
|
@ -7,6 +7,7 @@ final class DifferentialTransactionEditor
|
|||
private $isCloseByCommit;
|
||||
private $repositoryPHIDOverride = false;
|
||||
private $didExpandInlineState = false;
|
||||
private $hasReviewTransaction = false;
|
||||
private $affectedPaths;
|
||||
|
||||
public function getEditorApplicationClass() {
|
||||
|
@ -58,6 +59,7 @@ final class DifferentialTransactionEditor
|
|||
$types[] = PhabricatorTransactions::TYPE_COMMENT;
|
||||
$types[] = PhabricatorTransactions::TYPE_VIEW_POLICY;
|
||||
$types[] = PhabricatorTransactions::TYPE_EDIT_POLICY;
|
||||
$types[] = PhabricatorTransactions::TYPE_INLINESTATE;
|
||||
|
||||
$types[] = DifferentialTransaction::TYPE_ACTION;
|
||||
$types[] = DifferentialTransaction::TYPE_INLINE;
|
||||
|
@ -256,6 +258,30 @@ final class DifferentialTransactionEditor
|
|||
return parent::applyCustomInternalTransaction($object, $xaction);
|
||||
}
|
||||
|
||||
protected function expandTransactions(
|
||||
PhabricatorLiskDAO $object,
|
||||
array $xactions) {
|
||||
|
||||
foreach ($xactions as $xaction) {
|
||||
switch ($xaction->getTransactionType()) {
|
||||
case PhabricatorTransactions::TYPE_INLINESTATE:
|
||||
// If we have an "Inline State" transaction already, the caller
|
||||
// built it for us so we don't need to expand it again.
|
||||
$this->didExpandInlineState = true;
|
||||
break;
|
||||
case DifferentialRevisionAcceptTransaction::TRANSACTIONTYPE:
|
||||
case DifferentialRevisionRejectTransaction::TRANSACTIONTYPE:
|
||||
case DifferentialRevisionResignTransaction::TRANSACTIONTYPE:
|
||||
// If we have a review transaction, we'll skip marking the user
|
||||
// as "Commented" later. This should get cleaner after T10967.
|
||||
$this->hasReviewTransaction = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return parent::expandTransactions($object, $xactions);
|
||||
}
|
||||
|
||||
protected function expandTransaction(
|
||||
PhabricatorLiskDAO $object,
|
||||
PhabricatorApplicationTransaction $xaction) {
|
||||
|
@ -288,16 +314,26 @@ final class DifferentialTransactionEditor
|
|||
$downgrade_accepts = true;
|
||||
}
|
||||
break;
|
||||
case DifferentialRevisionRequestReviewTransaction::TRANSACTIONTYPE:
|
||||
$downgrade_rejects = true;
|
||||
if ((!$is_sticky_accept) ||
|
||||
($object->getStatus() != $status_plan)) {
|
||||
// If the old state isn't "changes planned", downgrade the accepts.
|
||||
// This exception allows an accepted revision to go through
|
||||
// "Plan Changes" -> "Request Review" and return to "accepted" if
|
||||
// the author didn't update the revision, essentially undoing the
|
||||
// "Plan Changes".
|
||||
$downgrade_accepts = true;
|
||||
}
|
||||
break;
|
||||
|
||||
// TODO: Remove this, obsoleted by ModularTransactions above.
|
||||
case DifferentialTransaction::TYPE_ACTION:
|
||||
switch ($xaction->getNewValue()) {
|
||||
case DifferentialAction::ACTION_REQUEST:
|
||||
$downgrade_rejects = true;
|
||||
if ((!$is_sticky_accept) ||
|
||||
($object->getStatus() != $status_plan)) {
|
||||
// If the old state isn't "changes planned", downgrade the
|
||||
// accepts. This exception allows an accepted revision to
|
||||
// go through Plan Changes -> Request Review to return to
|
||||
// "accepted" if the author didn't update the revision.
|
||||
$downgrade_accepts = true;
|
||||
}
|
||||
break;
|
||||
|
@ -353,6 +389,7 @@ final class DifferentialTransactionEditor
|
|||
}
|
||||
}
|
||||
|
||||
$is_commandeer = false;
|
||||
switch ($xaction->getTransactionType()) {
|
||||
case DifferentialTransaction::TYPE_UPDATE:
|
||||
if ($this->getIsCloseByCommit()) {
|
||||
|
@ -397,6 +434,11 @@ final class DifferentialTransactionEditor
|
|||
// "added" to "commented" if they're also a reviewer. We may further
|
||||
// upgrade this based on other actions in the transaction group.
|
||||
|
||||
if ($this->hasReviewTransaction) {
|
||||
// If we're also applying a review transaction, skip this.
|
||||
break;
|
||||
}
|
||||
|
||||
$status_added = DifferentialReviewerStatus::STATUS_ADDED;
|
||||
$status_commented = DifferentialReviewerStatus::STATUS_COMMENTED;
|
||||
|
||||
|
@ -424,6 +466,10 @@ final class DifferentialTransactionEditor
|
|||
}
|
||||
break;
|
||||
|
||||
case DifferentialRevisionCommandeerTransaction::TRANSACTIONTYPE:
|
||||
$is_commandeer = true;
|
||||
break;
|
||||
|
||||
case DifferentialTransaction::TYPE_ACTION:
|
||||
$action_type = $xaction->getNewValue();
|
||||
|
||||
|
@ -463,41 +509,7 @@ final class DifferentialTransactionEditor
|
|||
break;
|
||||
|
||||
case DifferentialAction::ACTION_CLAIM:
|
||||
// If the user is commandeering, add the previous owner as a
|
||||
// reviewer and remove the actor.
|
||||
|
||||
$edits = array(
|
||||
'-' => array(
|
||||
$actor_phid => $actor_phid,
|
||||
),
|
||||
);
|
||||
|
||||
$owner_phid = $object->getAuthorPHID();
|
||||
if ($owner_phid) {
|
||||
$reviewer = new DifferentialReviewerProxy(
|
||||
$owner_phid,
|
||||
array(
|
||||
'status' => DifferentialReviewerStatus::STATUS_ADDED,
|
||||
));
|
||||
|
||||
$edits['+'] = array(
|
||||
$owner_phid => array(
|
||||
'data' => $reviewer->getEdgeData(),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
// NOTE: We're setting setIsCommandeerSideEffect() on this because
|
||||
// normally you can't add a revision's author as a reviewer, but
|
||||
// this action swaps them after validation executes.
|
||||
|
||||
$results[] = id(new DifferentialTransaction())
|
||||
->setTransactionType($type_edge)
|
||||
->setMetadataValue('edge:type', $edge_reviewer)
|
||||
->setIgnoreOnNoEffect(true)
|
||||
->setIsCommandeerSideEffect(true)
|
||||
->setNewValue($edits);
|
||||
|
||||
$is_commandeer = true;
|
||||
break;
|
||||
case DifferentialAction::ACTION_RESIGN:
|
||||
// If the user is resigning, add a separate reviewer edit
|
||||
|
@ -519,6 +531,10 @@ final class DifferentialTransactionEditor
|
|||
break;
|
||||
}
|
||||
|
||||
if ($is_commandeer) {
|
||||
$results[] = $this->newCommandeerReviewerTransaction($object);
|
||||
}
|
||||
|
||||
if (!$this->didExpandInlineState) {
|
||||
switch ($xaction->getTransactionType()) {
|
||||
case PhabricatorTransactions::TYPE_COMMENT:
|
||||
|
@ -1499,6 +1515,10 @@ final class DifferentialTransactionEditor
|
|||
return true;
|
||||
}
|
||||
break;
|
||||
case DifferentialRevisionCommandeerTransaction::TRANSACTIONTYPE:
|
||||
// When users commandeer revisions, we may need to trigger
|
||||
// signatures or author-based rules.
|
||||
return true;
|
||||
case DifferentialTransaction::TYPE_ACTION:
|
||||
switch ($xaction->getNewValue()) {
|
||||
case DifferentialAction::ACTION_CLAIM:
|
||||
|
@ -1922,4 +1942,35 @@ final class DifferentialTransactionEditor
|
|||
return $this;
|
||||
}
|
||||
|
||||
private function newCommandeerReviewerTransaction(
|
||||
DifferentialRevision $revision) {
|
||||
|
||||
$actor_phid = $this->getActingAsPHID();
|
||||
$owner_phid = $revision->getAuthorPHID();
|
||||
|
||||
// If the user is commandeering, add the previous owner as a
|
||||
// reviewer and remove the actor.
|
||||
|
||||
$edits = array(
|
||||
'-' => array(
|
||||
$actor_phid,
|
||||
),
|
||||
'+' => array(
|
||||
$owner_phid,
|
||||
),
|
||||
);
|
||||
|
||||
// NOTE: We're setting setIsCommandeerSideEffect() on this because normally
|
||||
// you can't add a revision's author as a reviewer, but this action swaps
|
||||
// them after validation executes.
|
||||
|
||||
$xaction_type = DifferentialRevisionReviewersTransaction::TRANSACTIONTYPE;
|
||||
|
||||
return id(new DifferentialTransaction())
|
||||
->setTransactionType($xaction_type)
|
||||
->setIgnoreOnNoEffect(true)
|
||||
->setIsCommandeerSideEffect(true)
|
||||
->setNewValue($edits);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -18,8 +18,25 @@ final class DifferentialRevisionIDCommitMessageField
|
|||
}
|
||||
|
||||
public function parseFieldValue($value) {
|
||||
// If the value is just "D123" or similar, parse the ID from it directly.
|
||||
// If the complete commit message we are parsing has unrecognized custom
|
||||
// fields at the end, they can end up parsed into the field value for this
|
||||
// field. For example, if the message looks like this:
|
||||
|
||||
// Differential Revision: xyz
|
||||
// Some-Other-Field: abc
|
||||
|
||||
// ...we will receive "xyz\nSome-Other-Field: abc" as the field value for
|
||||
// this field. Ideally, the install would define these fields so they can
|
||||
// parse formally, but we can reasonably assume that only the first line
|
||||
// of any value we encounter actually contains a revision identifier, so
|
||||
// start by throwing away any other lines.
|
||||
|
||||
$value = trim($value);
|
||||
$value = phutil_split_lines($value, false);
|
||||
$value = head($value);
|
||||
$value = trim($value);
|
||||
|
||||
// If the value is just "D123" or similar, parse the ID from it directly.
|
||||
$matches = null;
|
||||
if (preg_match('/^[dD]([1-9]\d*)\z/', $value, $matches)) {
|
||||
return (int)$matches[1];
|
||||
|
|
|
@ -0,0 +1,30 @@
|
|||
<?php
|
||||
|
||||
final class DifferentialCommitMessageFieldTestCase
|
||||
extends PhabricatorTestCase {
|
||||
|
||||
public function testRevisionCommitMessageFieldParsing() {
|
||||
$base_uri = 'https://www.example.com/';
|
||||
|
||||
$tests = array(
|
||||
'D123' => 123,
|
||||
'd123' => 123,
|
||||
" \n d123 \n " => 123,
|
||||
"D123\nSome-Custom-Field: The End" => 123,
|
||||
"{$base_uri}D123" => 123,
|
||||
"{$base_uri}D123\nSome-Custom-Field: The End" => 123,
|
||||
);
|
||||
|
||||
$env = PhabricatorEnv::beginScopedEnv();
|
||||
$env->overrideEnvConfig('phabricator.base-uri', $base_uri);
|
||||
|
||||
foreach ($tests as $input => $expect) {
|
||||
$actual = id(new DifferentialRevisionIDCommitMessageField())
|
||||
->parseFieldValue($input);
|
||||
$this->assertEqual($expect, $actual, pht('Parse of: %s', $input));
|
||||
}
|
||||
|
||||
unset($env);
|
||||
}
|
||||
|
||||
}
|
|
@ -155,20 +155,33 @@ final class DifferentialCommitMessageParser extends Phobject {
|
|||
$field = $key_title;
|
||||
|
||||
$seen = array();
|
||||
$lines = explode("\n", trim($corpus));
|
||||
|
||||
$lines = trim($corpus);
|
||||
$lines = phutil_split_lines($lines, false);
|
||||
|
||||
$field_map = array();
|
||||
foreach ($lines as $key => $line) {
|
||||
$match = null;
|
||||
if (preg_match($label_regexp, $line, $match)) {
|
||||
$lines[$key] = trim($match['text']);
|
||||
$field = $label_map[self::normalizeFieldLabel($match['field'])];
|
||||
if (!empty($seen[$field])) {
|
||||
$this->errors[] = pht(
|
||||
'Field "%s" occurs twice in commit message!',
|
||||
$field);
|
||||
}
|
||||
// We always parse the first line of the message as a title, even if it
|
||||
// contains something we recognize as a field header.
|
||||
if (!isset($seen[$key_title])) {
|
||||
$field = $key_title;
|
||||
|
||||
$lines[$key] = trim($line);
|
||||
$seen[$field] = true;
|
||||
} else {
|
||||
$match = null;
|
||||
if (preg_match($label_regexp, $line, $match)) {
|
||||
$lines[$key] = trim($match['text']);
|
||||
$field = $label_map[self::normalizeFieldLabel($match['field'])];
|
||||
if (!empty($seen[$field])) {
|
||||
$this->errors[] = pht(
|
||||
'Field "%s" occurs twice in commit message!',
|
||||
$match['field']);
|
||||
}
|
||||
$seen[$field] = true;
|
||||
}
|
||||
}
|
||||
|
||||
$field_map[$key] = $field;
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,11 @@
|
|||
color: orange
|
||||
~~~~~~~~~~
|
||||
{
|
||||
"color": "color"
|
||||
}
|
||||
~~~~~~~~~~
|
||||
{
|
||||
"title": "color: orange"
|
||||
}
|
||||
~~~~~~~~~~
|
||||
[]
|
|
@ -97,16 +97,22 @@ final class DifferentialChangesetOneUpRenderer
|
|||
$line = array($hidden, $line);
|
||||
}
|
||||
|
||||
$cells[] = phutil_tag('th', array('id' => $left_id), $line);
|
||||
$cells[] = phutil_tag(
|
||||
'th',
|
||||
array(
|
||||
'id' => $left_id,
|
||||
'class' => $class,
|
||||
),
|
||||
$line);
|
||||
|
||||
$cells[] = phutil_tag('th', array());
|
||||
$cells[] = phutil_tag('th', array('class' => $class));
|
||||
$cells[] = $no_copy;
|
||||
$cells[] = phutil_tag('td', array('class' => $class), $p['render']);
|
||||
$cells[] = $no_coverage;
|
||||
} else {
|
||||
if ($p['htype']) {
|
||||
$class = 'right new';
|
||||
$cells[] = phutil_tag('th', array());
|
||||
$cells[] = phutil_tag('th', array('class' => $class));
|
||||
} else {
|
||||
$class = 'right';
|
||||
if ($left_prefix) {
|
||||
|
@ -138,7 +144,13 @@ final class DifferentialChangesetOneUpRenderer
|
|||
$line = array($hidden, $line);
|
||||
}
|
||||
|
||||
$cells[] = phutil_tag('th', array('id' => $right_id), $line);
|
||||
$cells[] = phutil_tag(
|
||||
'th',
|
||||
array(
|
||||
'id' => $right_id,
|
||||
'class' => $class,
|
||||
),
|
||||
$line);
|
||||
|
||||
$cells[] = $no_copy;
|
||||
$cells[] = phutil_tag('td', array('class' => $class), $p['render']);
|
||||
|
|
|
@ -306,9 +306,9 @@ final class DifferentialChangesetTwoUpRenderer
|
|||
$zero_space = "\xE2\x80\x8B";
|
||||
|
||||
$html[] = phutil_tag('tr', array(), array(
|
||||
phutil_tag('th', array('id' => $o_id), $o_num),
|
||||
phutil_tag('th', array('id' => $o_id, 'class' => $o_classes), $o_num),
|
||||
phutil_tag('td', array('class' => $o_classes), $o_text),
|
||||
phutil_tag('th', array('id' => $n_id), $n_num),
|
||||
phutil_tag('th', array('id' => $n_id, 'class' => $n_classes), $n_num),
|
||||
$n_copy,
|
||||
phutil_tag(
|
||||
'td',
|
||||
|
|
|
@ -4,6 +4,7 @@ final class DifferentialDiff
|
|||
extends DifferentialDAO
|
||||
implements
|
||||
PhabricatorPolicyInterface,
|
||||
PhabricatorExtendedPolicyInterface,
|
||||
HarbormasterBuildableInterface,
|
||||
HarbormasterCircleCIBuildableInterface,
|
||||
PhabricatorApplicationTransactionInterface,
|
||||
|
@ -429,7 +430,7 @@ final class DifferentialDiff
|
|||
|
||||
public function getPolicy($capability) {
|
||||
if ($this->hasRevision()) {
|
||||
return $this->getRevision()->getPolicy($capability);
|
||||
return PhabricatorPolicies::getMostOpenPolicy();
|
||||
}
|
||||
|
||||
return $this->viewPolicy;
|
||||
|
@ -440,7 +441,7 @@ final class DifferentialDiff
|
|||
return $this->getRevision()->hasAutomaticCapability($capability, $viewer);
|
||||
}
|
||||
|
||||
return ($this->getAuthorPHID() == $viewer->getPhid());
|
||||
return ($this->getAuthorPHID() == $viewer->getPHID());
|
||||
}
|
||||
|
||||
public function describeAutomaticCapability($capability) {
|
||||
|
@ -448,10 +449,31 @@ final class DifferentialDiff
|
|||
return pht(
|
||||
'This diff is attached to a revision, and inherits its policies.');
|
||||
}
|
||||
|
||||
return pht('The author of a diff can see it.');
|
||||
}
|
||||
|
||||
|
||||
/* -( PhabricatorExtendedPolicyInterface )--------------------------------- */
|
||||
|
||||
|
||||
public function getExtendedPolicy($capability, PhabricatorUser $viewer) {
|
||||
$extended = array();
|
||||
|
||||
switch ($capability) {
|
||||
case PhabricatorPolicyCapability::CAN_VIEW:
|
||||
if ($this->hasRevision()) {
|
||||
$extended[] = array(
|
||||
$this->getRevision(),
|
||||
PhabricatorPolicyCapability::CAN_VIEW,
|
||||
);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
return $extended;
|
||||
}
|
||||
|
||||
|
||||
/* -( HarbormasterBuildableInterface )------------------------------------- */
|
||||
|
||||
|
@ -480,6 +502,10 @@ final class DifferentialDiff
|
|||
return null;
|
||||
}
|
||||
|
||||
public function getHarbormasterPublishablePHID() {
|
||||
return $this->getHarbormasterContainerPHID();
|
||||
}
|
||||
|
||||
public function getBuildVariables() {
|
||||
$results = array();
|
||||
|
||||
|
|
|
@ -442,6 +442,16 @@ final class DifferentialRevision extends DifferentialDAO
|
|||
return DifferentialRevisionStatus::isClosedStatus($this->getStatus());
|
||||
}
|
||||
|
||||
public function isAbandoned() {
|
||||
$status_abandoned = ArcanistDifferentialRevisionStatus::ABANDONED;
|
||||
return ($this->getStatus() == $status_abandoned);
|
||||
}
|
||||
|
||||
public function isAccepted() {
|
||||
$status_accepted = ArcanistDifferentialRevisionStatus::ACCEPTED;
|
||||
return ($this->getStatus() == $status_accepted);
|
||||
}
|
||||
|
||||
public function getStatusIcon() {
|
||||
$map = array(
|
||||
ArcanistDifferentialRevisionStatus::NEEDS_REVIEW
|
||||
|
@ -503,6 +513,10 @@ final class DifferentialRevision extends DifferentialDAO
|
|||
return $this->getPHID();
|
||||
}
|
||||
|
||||
public function getHarbormasterPublishablePHID() {
|
||||
return $this->getPHID();
|
||||
}
|
||||
|
||||
public function getBuildVariables() {
|
||||
return array();
|
||||
}
|
||||
|
|
|
@ -223,6 +223,12 @@ final class DifferentialTransaction
|
|||
case self::TYPE_INLINE:
|
||||
$tags[] = self::MAILTAG_COMMENT;
|
||||
break;
|
||||
case DifferentialRevisionReviewersTransaction::TRANSACTIONTYPE:
|
||||
$tags[] = self::MAILTAG_REVIEWERS;
|
||||
break;
|
||||
case DifferentialRevisionCloseTransaction::TRANSACTIONTYPE:
|
||||
$tags[] = self::MAILTAG_CLOSED;
|
||||
break;
|
||||
}
|
||||
|
||||
if (!$tags) {
|
||||
|
|
|
@ -14,6 +14,7 @@ final class DifferentialTransactionComment
|
|||
|
||||
private $replyToComment = self::ATTACHABLE;
|
||||
private $isHidden = self::ATTACHABLE;
|
||||
private $changeset = self::ATTACHABLE;
|
||||
|
||||
public function getApplicationTransactionObject() {
|
||||
return new DifferentialTransaction();
|
||||
|
|
|
@ -1,201 +0,0 @@
|
|||
<?php
|
||||
|
||||
final class DifferentialAddCommentView extends AphrontView {
|
||||
|
||||
private $revision;
|
||||
private $actions;
|
||||
private $actionURI;
|
||||
private $draft;
|
||||
private $reviewers = array();
|
||||
private $ccs = array();
|
||||
private $errorView;
|
||||
|
||||
public function setInfoView(PHUIInfoView $error_view) {
|
||||
$this->errorView = $error_view;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getErrorView() {
|
||||
return $this->errorView;
|
||||
}
|
||||
|
||||
public function setRevision($revision) {
|
||||
$this->revision = $revision;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function setActions(array $actions) {
|
||||
$this->actions = $actions;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function setActionURI($uri) {
|
||||
$this->actionURI = $uri;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function setDraft(PhabricatorDraft $draft = null) {
|
||||
$this->draft = $draft;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function setReviewers(array $names) {
|
||||
$this->reviewers = $names;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function setCCs(array $names) {
|
||||
$this->ccs = $names;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function render() {
|
||||
$viewer = $this->getViewer();
|
||||
|
||||
$this->requireResource('differential-revision-add-comment-css');
|
||||
$revision = $this->revision;
|
||||
|
||||
$action = null;
|
||||
if ($this->draft) {
|
||||
$action = idx($this->draft->getMetadata(), 'action');
|
||||
}
|
||||
|
||||
$enable_reviewers = DifferentialAction::allowReviewers($action);
|
||||
$enable_ccs = ($action == DifferentialAction::ACTION_ADDCCS);
|
||||
$add_reviewers_labels = array(
|
||||
'add_reviewers' => pht('Add Reviewers'),
|
||||
'request_review' => pht('Add Reviewers'),
|
||||
'resign' => pht('Suggest Reviewers'),
|
||||
);
|
||||
|
||||
$mailable_source = new PhabricatorMetaMTAMailableDatasource();
|
||||
|
||||
// TODO: This should be a reviewers datasource, but it's a mess.
|
||||
$reviewer_source = new PhabricatorMetaMTAMailableDatasource();
|
||||
|
||||
$form = new AphrontFormView();
|
||||
$form
|
||||
->setWorkflow(true)
|
||||
->setViewer($viewer)
|
||||
->setAction($this->actionURI)
|
||||
->addHiddenInput('revision_id', $revision->getID())
|
||||
->appendChild(
|
||||
id(new AphrontFormSelectControl())
|
||||
->setLabel(pht('Action'))
|
||||
->setName('action')
|
||||
->setValue($action)
|
||||
->setID('comment-action')
|
||||
->setOptions($this->actions))
|
||||
->appendControl(
|
||||
id(new AphrontFormTokenizerControl())
|
||||
->setLabel($enable_reviewers ? $add_reviewers_labels[$action] :
|
||||
$add_reviewers_labels['add_reviewers'])
|
||||
->setName('reviewers')
|
||||
->setControlID('add-reviewers')
|
||||
->setControlStyle($enable_reviewers ? null : 'display: none')
|
||||
->setID('add-reviewers-tokenizer')
|
||||
->setDisableBehavior(true)
|
||||
->setDatasource($reviewer_source))
|
||||
->appendControl(
|
||||
id(new AphrontFormTokenizerControl())
|
||||
->setLabel(pht('Add Subscribers'))
|
||||
->setName('ccs')
|
||||
->setControlID('add-ccs')
|
||||
->setControlStyle($enable_ccs ? null : 'display: none')
|
||||
->setID('add-ccs-tokenizer')
|
||||
->setDisableBehavior(true)
|
||||
->setDatasource($mailable_source))
|
||||
->appendChild(
|
||||
id(new PhabricatorRemarkupControl())
|
||||
->setName('comment')
|
||||
->setID('comment-content')
|
||||
->setLabel(pht('Comment'))
|
||||
->setValue($this->draft ? $this->draft->getDraft() : null)
|
||||
->setViewer($viewer))
|
||||
->appendChild(
|
||||
id(new AphrontFormSubmitControl())
|
||||
->setValue(pht('Submit')));
|
||||
|
||||
Javelin::initBehavior(
|
||||
'differential-add-reviewers-and-ccs',
|
||||
array(
|
||||
'dynamic' => array(
|
||||
'add-reviewers-tokenizer' => array(
|
||||
'actions' => array(
|
||||
'request_review' => 1,
|
||||
'add_reviewers' => 1,
|
||||
'resign' => 1,
|
||||
),
|
||||
'src' => $reviewer_source->getDatasourceURI(),
|
||||
'value' => $this->reviewers,
|
||||
'row' => 'add-reviewers',
|
||||
'labels' => $add_reviewers_labels,
|
||||
'placeholder' => $reviewer_source->getPlaceholderText(),
|
||||
),
|
||||
'add-ccs-tokenizer' => array(
|
||||
'actions' => array('add_ccs' => 1),
|
||||
'src' => $mailable_source->getDatasourceURI(),
|
||||
'value' => $this->ccs,
|
||||
'row' => 'add-ccs',
|
||||
'placeholder' => $mailable_source->getPlaceholderText(),
|
||||
),
|
||||
),
|
||||
'select' => 'comment-action',
|
||||
));
|
||||
|
||||
$diff = $revision->loadActiveDiff();
|
||||
$rev_id = $revision->getID();
|
||||
|
||||
Javelin::initBehavior(
|
||||
'differential-feedback-preview',
|
||||
array(
|
||||
'uri' => '/differential/comment/preview/'.$rev_id.'/',
|
||||
'preview' => 'comment-preview',
|
||||
'action' => 'comment-action',
|
||||
'content' => 'comment-content',
|
||||
'previewTokenizers' => array(
|
||||
'reviewers' => 'add-reviewers-tokenizer',
|
||||
'ccs' => 'add-ccs-tokenizer',
|
||||
),
|
||||
|
||||
'inlineuri' => '/differential/comment/inline/preview/'.$rev_id.'/',
|
||||
'inline' => 'inline-comment-preview',
|
||||
));
|
||||
|
||||
$is_serious = PhabricatorEnv::getEnvConfig('phabricator.serious-business');
|
||||
$header_text = $is_serious
|
||||
? pht('Add Comment')
|
||||
: pht('Leap Into Action!');
|
||||
|
||||
$header = id(new PHUIHeaderView())
|
||||
->setHeader($header_text);
|
||||
|
||||
$anchor = id(new PhabricatorAnchorView())
|
||||
->setAnchorName('comment')
|
||||
->setNavigationMarker(true);
|
||||
|
||||
$loading = phutil_tag(
|
||||
'span',
|
||||
array('class' => 'aphront-panel-preview-loading-text'),
|
||||
pht('Loading comment preview...'));
|
||||
|
||||
$preview = phutil_tag_div(
|
||||
'aphront-panel-preview aphront-panel-flush',
|
||||
array(
|
||||
phutil_tag('div', array('id' => 'comment-preview'), $loading),
|
||||
phutil_tag('div', array('id' => 'inline-comment-preview')),
|
||||
));
|
||||
|
||||
|
||||
$comment_box = id(new PHUIObjectBoxView())
|
||||
->setHeader($header)
|
||||
->appendChild($anchor)
|
||||
->appendChild($form);
|
||||
|
||||
if ($this->errorView) {
|
||||
$comment_box->setInfoView($this->errorView);
|
||||
}
|
||||
|
||||
return array($comment_box, $preview);
|
||||
}
|
||||
}
|
|
@ -3,7 +3,7 @@
|
|||
final class DifferentialTransactionView
|
||||
extends PhabricatorApplicationTransactionView {
|
||||
|
||||
private $changesets;
|
||||
private $changesets = array();
|
||||
private $revision;
|
||||
private $rightDiff;
|
||||
private $leftDiff;
|
||||
|
@ -93,6 +93,13 @@ final class DifferentialTransactionView
|
|||
$out[] = parent::renderTransactionContent($xaction);
|
||||
}
|
||||
|
||||
// If we're rendering a preview, we show the inline comments in a separate
|
||||
// section underneath the main transaction preview, so we skip rendering
|
||||
// them in the preview body.
|
||||
if ($this->getIsPreview()) {
|
||||
return $out;
|
||||
}
|
||||
|
||||
if (!$group) {
|
||||
return $out;
|
||||
}
|
||||
|
|
|
@ -0,0 +1,83 @@
|
|||
<?php
|
||||
|
||||
final class DifferentialRevisionAbandonTransaction
|
||||
extends DifferentialRevisionActionTransaction {
|
||||
|
||||
const TRANSACTIONTYPE = 'differential.revision.abandon';
|
||||
const ACTIONKEY = 'abandon';
|
||||
|
||||
protected function getRevisionActionLabel() {
|
||||
return pht('Abandon Revision');
|
||||
}
|
||||
|
||||
protected function getRevisionActionDescription() {
|
||||
return pht('This revision will be abandoned and closed.');
|
||||
}
|
||||
|
||||
public function getIcon() {
|
||||
return 'fa-plane';
|
||||
}
|
||||
|
||||
public function getColor() {
|
||||
return 'indigo';
|
||||
}
|
||||
|
||||
protected function getRevisionActionOrder() {
|
||||
return 500;
|
||||
}
|
||||
|
||||
public function getCommandKeyword() {
|
||||
return 'abandon';
|
||||
}
|
||||
|
||||
public function getCommandAliases() {
|
||||
return array();
|
||||
}
|
||||
|
||||
public function getCommandSummary() {
|
||||
return pht('Abandon a revision.');
|
||||
}
|
||||
|
||||
public function generateOldValue($object) {
|
||||
return $object->isAbandoned();
|
||||
}
|
||||
|
||||
public function applyInternalEffects($object, $value) {
|
||||
$object->setStatus(ArcanistDifferentialRevisionStatus::ABANDONED);
|
||||
}
|
||||
|
||||
protected function validateAction($object, PhabricatorUser $viewer) {
|
||||
if ($object->isClosed()) {
|
||||
throw new Exception(
|
||||
pht(
|
||||
'You can not abandon this revision because it has already been '.
|
||||
'closed. Only open revisions can be abandoned.'));
|
||||
}
|
||||
|
||||
$config_key = 'differential.always-allow-abandon';
|
||||
if (!PhabricatorEnv::getEnvConfig($config_key)) {
|
||||
if (!$this->isViewerRevisionAuthor($object, $viewer)) {
|
||||
throw new Exception(
|
||||
pht(
|
||||
'You can not abandon this revision because you are not the '.
|
||||
'author. You can only abandon revisions you own. You can change '.
|
||||
'this behavior by adjusting the "%s" setting in Config.',
|
||||
$config_key));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public function getTitle() {
|
||||
return pht(
|
||||
'%s abandoned this revision.',
|
||||
$this->renderAuthor());
|
||||
}
|
||||
|
||||
public function getTitleForFeed() {
|
||||
return pht(
|
||||
'%s abandoned %s.',
|
||||
$this->renderAuthor(),
|
||||
$this->renderObject());
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,99 @@
|
|||
<?php
|
||||
|
||||
final class DifferentialRevisionAcceptTransaction
|
||||
extends DifferentialRevisionReviewTransaction {
|
||||
|
||||
const TRANSACTIONTYPE = 'differential.revision.accept';
|
||||
const ACTIONKEY = 'accept';
|
||||
|
||||
protected function getRevisionActionLabel() {
|
||||
return pht("Accept Revision \xE2\x9C\x94");
|
||||
}
|
||||
|
||||
protected function getRevisionActionDescription() {
|
||||
return pht('These changes will be approved.');
|
||||
}
|
||||
|
||||
public function getIcon() {
|
||||
return 'fa-check-circle-o';
|
||||
}
|
||||
|
||||
public function getColor() {
|
||||
return 'green';
|
||||
}
|
||||
|
||||
protected function getRevisionActionOrder() {
|
||||
return 500;
|
||||
}
|
||||
|
||||
public function getCommandKeyword() {
|
||||
$accept_key = 'differential.enable-email-accept';
|
||||
$allow_email_accept = PhabricatorEnv::getEnvConfig($accept_key);
|
||||
if (!$allow_email_accept) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return 'accept';
|
||||
}
|
||||
|
||||
public function getCommandAliases() {
|
||||
return array();
|
||||
}
|
||||
|
||||
public function getCommandSummary() {
|
||||
return pht('Accept a revision.');
|
||||
}
|
||||
|
||||
public function generateOldValue($object) {
|
||||
$actor = $this->getActor();
|
||||
return $this->isViewerAcceptingReviewer($object, $actor);
|
||||
}
|
||||
|
||||
public function applyExternalEffects($object, $value) {
|
||||
$status = DifferentialReviewerStatus::STATUS_ACCEPTED;
|
||||
$actor = $this->getActor();
|
||||
$this->applyReviewerEffect($object, $actor, $value, $status);
|
||||
}
|
||||
|
||||
protected function validateAction($object, PhabricatorUser $viewer) {
|
||||
if ($object->isClosed()) {
|
||||
throw new Exception(
|
||||
pht(
|
||||
'You can not accept this revision because it has already been '.
|
||||
'closed. Only open revisions can be accepted.'));
|
||||
}
|
||||
|
||||
$config_key = 'differential.allow-self-accept';
|
||||
if (!PhabricatorEnv::getEnvConfig($config_key)) {
|
||||
if ($this->isViewerRevisionAuthor($object, $viewer)) {
|
||||
throw new Exception(
|
||||
pht(
|
||||
'You can not accept this revision because you are the revision '.
|
||||
'author. You can only accept revisions you do not own. You can '.
|
||||
'change this behavior by adjusting the "%s" setting in Config.',
|
||||
$config_key));
|
||||
}
|
||||
}
|
||||
|
||||
if ($this->isViewerAcceptingReviewer($object, $viewer)) {
|
||||
throw new Exception(
|
||||
pht(
|
||||
'You can not accept this revision because you have already '.
|
||||
'accepted it.'));
|
||||
}
|
||||
}
|
||||
|
||||
public function getTitle() {
|
||||
return pht(
|
||||
'%s accepted this revision.',
|
||||
$this->renderAuthor());
|
||||
}
|
||||
|
||||
public function getTitleForFeed() {
|
||||
return pht(
|
||||
'%s accepted %s.',
|
||||
$this->renderAuthor(),
|
||||
$this->renderObject());
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,128 @@
|
|||
<?php
|
||||
|
||||
abstract class DifferentialRevisionActionTransaction
|
||||
extends DifferentialRevisionTransactionType {
|
||||
|
||||
final public function getRevisionActionKey() {
|
||||
return $this->getPhobjectClassConstant('ACTIONKEY', 32);
|
||||
}
|
||||
|
||||
public function isActionAvailable($object, PhabricatorUser $viewer) {
|
||||
try {
|
||||
$this->validateAction($object, $viewer);
|
||||
return true;
|
||||
} catch (Exception $ex) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
abstract protected function validateAction($object, PhabricatorUser $viewer);
|
||||
abstract protected function getRevisionActionLabel();
|
||||
|
||||
public function getCommandKeyword() {
|
||||
return null;
|
||||
}
|
||||
|
||||
public function getCommandAliases() {
|
||||
return array();
|
||||
}
|
||||
|
||||
public function getCommandSummary() {
|
||||
return null;
|
||||
}
|
||||
|
||||
protected function getRevisionActionOrder() {
|
||||
return 1000;
|
||||
}
|
||||
|
||||
public function getRevisionActionOrderVector() {
|
||||
return id(new PhutilSortVector())
|
||||
->addInt($this->getRevisionActionOrder());
|
||||
}
|
||||
|
||||
protected function getRevisionActionGroupKey() {
|
||||
return DifferentialRevisionEditEngine::ACTIONGROUP_REVISION;
|
||||
}
|
||||
|
||||
protected function getRevisionActionDescription() {
|
||||
return null;
|
||||
}
|
||||
|
||||
public static function loadAllActions() {
|
||||
return id(new PhutilClassMapQuery())
|
||||
->setAncestorClass(__CLASS__)
|
||||
->setUniqueMethod('getRevisionActionKey')
|
||||
->execute();
|
||||
}
|
||||
|
||||
protected function isViewerRevisionAuthor(
|
||||
DifferentialRevision $revision,
|
||||
PhabricatorUser $viewer) {
|
||||
|
||||
if (!$viewer->getPHID()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return ($viewer->getPHID() === $revision->getAuthorPHID());
|
||||
}
|
||||
|
||||
public function newEditField(
|
||||
DifferentialRevision $revision,
|
||||
PhabricatorUser $viewer) {
|
||||
|
||||
$field = id(new PhabricatorApplyEditField())
|
||||
->setKey($this->getRevisionActionKey())
|
||||
->setTransactionType($this->getTransactionTypeConstant())
|
||||
->setValue(true);
|
||||
|
||||
if ($this->isActionAvailable($revision, $viewer)) {
|
||||
$label = $this->getRevisionActionLabel();
|
||||
if ($label !== null) {
|
||||
$field->setCommentActionLabel($label);
|
||||
|
||||
$description = $this->getRevisionActionDescription();
|
||||
$field->setActionDescription($description);
|
||||
|
||||
$group_key = $this->getRevisionActionGroupKey();
|
||||
$field->setCommentActionGroupKey($group_key);
|
||||
|
||||
// Currently, every revision action conflicts with every other
|
||||
// revision action: for example, you can not simultaneously Accept and
|
||||
// Reject a revision.
|
||||
|
||||
// Under some configurations, some combinations of actions are sort of
|
||||
// technically permissible. For example, you could reasonably Reject
|
||||
// and Abandon a revision if "anyone can abandon anything" is enabled.
|
||||
|
||||
// It's not clear that these combinations are actually useful, so just
|
||||
// keep things simple for now.
|
||||
$field->setActionConflictKey('revision.action');
|
||||
}
|
||||
}
|
||||
|
||||
return $field;
|
||||
}
|
||||
|
||||
public function validateTransactions($object, array $xactions) {
|
||||
$errors = array();
|
||||
$actor = $this->getActor();
|
||||
|
||||
$action_exception = null;
|
||||
try {
|
||||
$this->validateAction($object, $actor);
|
||||
} catch (Exception $ex) {
|
||||
$action_exception = $ex;
|
||||
}
|
||||
|
||||
foreach ($xactions as $xaction) {
|
||||
if ($action_exception) {
|
||||
$errors[] = $this->newInvalidError(
|
||||
$action_exception->getMessage(),
|
||||
$xaction);
|
||||
}
|
||||
}
|
||||
|
||||
return $errors;
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,89 @@
|
|||
<?php
|
||||
|
||||
final class DifferentialRevisionCloseTransaction
|
||||
extends DifferentialRevisionActionTransaction {
|
||||
|
||||
const TRANSACTIONTYPE = 'differential.revision.close';
|
||||
const ACTIONKEY = 'close';
|
||||
|
||||
protected function getRevisionActionLabel() {
|
||||
return pht('Close Revision');
|
||||
}
|
||||
|
||||
protected function getRevisionActionDescription() {
|
||||
return pht('This revision will be closed.');
|
||||
}
|
||||
|
||||
public function getIcon() {
|
||||
return 'fa-check';
|
||||
}
|
||||
|
||||
public function getColor() {
|
||||
return 'indigo';
|
||||
}
|
||||
|
||||
protected function getRevisionActionOrder() {
|
||||
return 300;
|
||||
}
|
||||
|
||||
public function generateOldValue($object) {
|
||||
return $object->isClosed();
|
||||
}
|
||||
|
||||
public function applyInternalEffects($object, $value) {
|
||||
$status_closed = ArcanistDifferentialRevisionStatus::CLOSED;
|
||||
$status_accepted = ArcanistDifferentialRevisionStatus::ACCEPTED;
|
||||
|
||||
$old_status = $object->getStatus();
|
||||
|
||||
$object->setStatus($status_closed);
|
||||
|
||||
$was_accepted = ($old_status == $status_accepted);
|
||||
|
||||
$object->setProperty(
|
||||
DifferentialRevision::PROPERTY_CLOSED_FROM_ACCEPTED,
|
||||
$was_accepted);
|
||||
}
|
||||
|
||||
protected function validateAction($object, PhabricatorUser $viewer) {
|
||||
if ($object->isClosed()) {
|
||||
throw new Exception(
|
||||
pht(
|
||||
'You can not close this revision because it has already been '.
|
||||
'closed. Only open revisions can be closed.'));
|
||||
}
|
||||
|
||||
if (!$object->isAccepted()) {
|
||||
throw new Exception(
|
||||
pht(
|
||||
'You can not close this revision because it has not been accepted. '.
|
||||
'Revisions must be accepted before they can be closed.'));
|
||||
}
|
||||
|
||||
$config_key = 'differential.always-allow-close';
|
||||
if (!PhabricatorEnv::getEnvConfig($config_key)) {
|
||||
if (!$this->isViewerRevisionAuthor($object, $viewer)) {
|
||||
throw new Exception(
|
||||
pht(
|
||||
'You can not close this revision because you are not the '.
|
||||
'author. You can only close revisions you own. You can change '.
|
||||
'this behavior by adjusting the "%s" setting in Config.',
|
||||
$config_key));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public function getTitle() {
|
||||
return pht(
|
||||
'%s closed this revision.',
|
||||
$this->renderAuthor());
|
||||
}
|
||||
|
||||
public function getTitleForFeed() {
|
||||
return pht(
|
||||
'%s closed %s.',
|
||||
$this->renderAuthor(),
|
||||
$this->renderObject());
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,85 @@
|
|||
<?php
|
||||
|
||||
final class DifferentialRevisionCommandeerTransaction
|
||||
extends DifferentialRevisionActionTransaction {
|
||||
|
||||
const TRANSACTIONTYPE = 'differential.revision.commandeer';
|
||||
const ACTIONKEY = 'commandeer';
|
||||
|
||||
protected function getRevisionActionLabel() {
|
||||
return pht('Commandeer Revision');
|
||||
}
|
||||
|
||||
protected function getRevisionActionDescription() {
|
||||
return pht('You will take control of this revision and become its author.');
|
||||
}
|
||||
|
||||
public function getIcon() {
|
||||
return 'fa-flag';
|
||||
}
|
||||
|
||||
public function getColor() {
|
||||
return 'sky';
|
||||
}
|
||||
|
||||
protected function getRevisionActionOrder() {
|
||||
return 700;
|
||||
}
|
||||
|
||||
public function getCommandKeyword() {
|
||||
return 'commandeer';
|
||||
}
|
||||
|
||||
public function getCommandAliases() {
|
||||
return array(
|
||||
'claim',
|
||||
);
|
||||
}
|
||||
|
||||
public function getCommandSummary() {
|
||||
return pht('Commadeer a revision.');
|
||||
}
|
||||
|
||||
public function generateOldValue($object) {
|
||||
return $object->getAuthorPHID();
|
||||
}
|
||||
|
||||
public function generateNewValue($object, $value) {
|
||||
$actor = $this->getActor();
|
||||
return $actor->getPHID();
|
||||
}
|
||||
|
||||
public function applyInternalEffects($object, $value) {
|
||||
$object->setAuthorPHID($value);
|
||||
}
|
||||
|
||||
protected function validateAction($object, PhabricatorUser $viewer) {
|
||||
if ($object->isClosed()) {
|
||||
throw new Exception(
|
||||
pht(
|
||||
'You can not commandeer this revision because it has already '.
|
||||
'been closed. You can only commandeer open revisions.'));
|
||||
}
|
||||
|
||||
if ($this->isViewerRevisionAuthor($object, $viewer)) {
|
||||
throw new Exception(
|
||||
pht(
|
||||
'You can not commandeer this revision because you are already '.
|
||||
'the author.'));
|
||||
}
|
||||
}
|
||||
|
||||
public function getTitle() {
|
||||
return pht(
|
||||
'%s commandeered this revision.',
|
||||
$this->renderAuthor());
|
||||
}
|
||||
|
||||
public function getTitleForFeed() {
|
||||
return pht(
|
||||
'%s commandeered %s.',
|
||||
$this->renderAuthor(),
|
||||
$this->renderObject());
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,93 @@
|
|||
<?php
|
||||
|
||||
final class DifferentialRevisionPlanChangesTransaction
|
||||
extends DifferentialRevisionActionTransaction {
|
||||
|
||||
const TRANSACTIONTYPE = 'differential.revision.plan';
|
||||
const ACTIONKEY = 'plan-changes';
|
||||
|
||||
protected function getRevisionActionLabel() {
|
||||
return pht('Plan Changes');
|
||||
}
|
||||
|
||||
protected function getRevisionActionDescription() {
|
||||
return pht(
|
||||
'This revision will be removed from review queues until it is revised.');
|
||||
}
|
||||
|
||||
public function getIcon() {
|
||||
return 'fa-headphones';
|
||||
}
|
||||
|
||||
public function getColor() {
|
||||
return 'red';
|
||||
}
|
||||
|
||||
protected function getRevisionActionOrder() {
|
||||
return 200;
|
||||
}
|
||||
|
||||
public function getCommandKeyword() {
|
||||
return 'planchanges';
|
||||
}
|
||||
|
||||
public function getCommandAliases() {
|
||||
return array(
|
||||
'rethink',
|
||||
);
|
||||
}
|
||||
|
||||
public function getCommandSummary() {
|
||||
return pht('Plan changes to a revision.');
|
||||
}
|
||||
|
||||
public function generateOldValue($object) {
|
||||
$status_planned = ArcanistDifferentialRevisionStatus::CHANGES_PLANNED;
|
||||
return ($object->getStatus() == $status_planned);
|
||||
}
|
||||
|
||||
public function applyInternalEffects($object, $value) {
|
||||
$status_planned = ArcanistDifferentialRevisionStatus::CHANGES_PLANNED;
|
||||
$object->setStatus($status_planned);
|
||||
}
|
||||
|
||||
protected function validateAction($object, PhabricatorUser $viewer) {
|
||||
$status_planned = ArcanistDifferentialRevisionStatus::CHANGES_PLANNED;
|
||||
|
||||
if ($object->getStatus() == $status_planned) {
|
||||
throw new Exception(
|
||||
pht(
|
||||
'You can not request review of this revision because this '.
|
||||
'revision is already under review and the action would have '.
|
||||
'no effect.'));
|
||||
}
|
||||
|
||||
if ($object->isClosed()) {
|
||||
throw new Exception(
|
||||
pht(
|
||||
'You can not plan changes to this this revision because it has '.
|
||||
'already been closed.'));
|
||||
}
|
||||
|
||||
if (!$this->isViewerRevisionAuthor($object, $viewer)) {
|
||||
throw new Exception(
|
||||
pht(
|
||||
'You can not plan changes to this revision because you do not '.
|
||||
'own it. Only the author of a revision can plan changes to it.'));
|
||||
}
|
||||
}
|
||||
|
||||
public function getTitle() {
|
||||
return pht(
|
||||
'%s planned changes to this revision.',
|
||||
$this->renderAuthor());
|
||||
}
|
||||
|
||||
public function getTitleForFeed() {
|
||||
return pht(
|
||||
'%s planned changes to %s.',
|
||||
$this->renderAuthor(),
|
||||
$this->renderObject());
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,78 @@
|
|||
<?php
|
||||
|
||||
final class DifferentialRevisionReclaimTransaction
|
||||
extends DifferentialRevisionActionTransaction {
|
||||
|
||||
const TRANSACTIONTYPE = 'differential.revision.reclaim';
|
||||
const ACTIONKEY = 'reclaim';
|
||||
|
||||
protected function getRevisionActionLabel() {
|
||||
return pht('Reclaim Revision');
|
||||
}
|
||||
|
||||
protected function getRevisionActionDescription() {
|
||||
return pht('This revision will be reclaimed and reopened.');
|
||||
}
|
||||
|
||||
public function getIcon() {
|
||||
return 'fa-bullhorn';
|
||||
}
|
||||
|
||||
public function getColor() {
|
||||
return 'sky';
|
||||
}
|
||||
|
||||
protected function getRevisionActionOrder() {
|
||||
return 600;
|
||||
}
|
||||
|
||||
public function getCommandKeyword() {
|
||||
return 'reclaim';
|
||||
}
|
||||
|
||||
public function getCommandAliases() {
|
||||
return array();
|
||||
}
|
||||
|
||||
public function getCommandSummary() {
|
||||
return pht('Reclaim a revision.');
|
||||
}
|
||||
|
||||
public function generateOldValue($object) {
|
||||
return !$object->isAbandoned();
|
||||
}
|
||||
|
||||
public function applyInternalEffects($object, $value) {
|
||||
$object->setStatus(ArcanistDifferentialRevisionStatus::NEEDS_REVIEW);
|
||||
}
|
||||
|
||||
protected function validateAction($object, PhabricatorUser $viewer) {
|
||||
if (!$object->isAbandoned()) {
|
||||
throw new Exception(
|
||||
pht(
|
||||
'You can not reclaim this revision because it has not been '.
|
||||
'abandoned. Only abandoned revisions can be reclaimed.'));
|
||||
}
|
||||
|
||||
if (!$this->isViewerRevisionAuthor($object, $viewer)) {
|
||||
throw new Exception(
|
||||
pht(
|
||||
'You can not reclaim this revision because you are not the '.
|
||||
'revision author. You can only reclaim revisions you own.'));
|
||||
}
|
||||
}
|
||||
|
||||
public function getTitle() {
|
||||
return pht(
|
||||
'%s reclaimed this revision.',
|
||||
$this->renderAuthor());
|
||||
}
|
||||
|
||||
public function getTitleForFeed() {
|
||||
return pht(
|
||||
'%s reclaimed %s.',
|
||||
$this->renderAuthor(),
|
||||
$this->renderObject());
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,92 @@
|
|||
<?php
|
||||
|
||||
final class DifferentialRevisionRejectTransaction
|
||||
extends DifferentialRevisionReviewTransaction {
|
||||
|
||||
const TRANSACTIONTYPE = 'differential.revision.reject';
|
||||
const ACTIONKEY = 'reject';
|
||||
|
||||
protected function getRevisionActionLabel() {
|
||||
return pht("Request Changes \xE2\x9C\x98");
|
||||
}
|
||||
|
||||
protected function getRevisionActionDescription() {
|
||||
return pht('This revision will be returned to the author for updates.');
|
||||
}
|
||||
|
||||
public function getIcon() {
|
||||
return 'fa-times-circle-o';
|
||||
}
|
||||
|
||||
public function getColor() {
|
||||
return 'red';
|
||||
}
|
||||
|
||||
protected function getRevisionActionOrder() {
|
||||
return 600;
|
||||
}
|
||||
|
||||
public function getCommandKeyword() {
|
||||
return 'request';
|
||||
}
|
||||
|
||||
public function getCommandAliases() {
|
||||
return array(
|
||||
'reject',
|
||||
);
|
||||
}
|
||||
|
||||
public function getCommandSummary() {
|
||||
return pht('Request changes to a revision.');
|
||||
}
|
||||
|
||||
public function generateOldValue($object) {
|
||||
$actor = $this->getActor();
|
||||
return $this->isViewerRejectingReviewer($object, $actor);
|
||||
}
|
||||
|
||||
public function applyExternalEffects($object, $value) {
|
||||
$status = DifferentialReviewerStatus::STATUS_REJECTED;
|
||||
$actor = $this->getActor();
|
||||
$this->applyReviewerEffect($object, $actor, $value, $status);
|
||||
}
|
||||
|
||||
protected function validateAction($object, PhabricatorUser $viewer) {
|
||||
if ($object->isClosed()) {
|
||||
throw new Exception(
|
||||
pht(
|
||||
'You can not request changes to this revision because it has '.
|
||||
'already been closed. You can only request changes to open '.
|
||||
'revisions.'));
|
||||
}
|
||||
|
||||
if ($this->isViewerRevisionAuthor($object, $viewer)) {
|
||||
throw new Exception(
|
||||
pht(
|
||||
'You can not request changes to this revision because you are the '.
|
||||
'revision author. You can only request changes to revisions you do '.
|
||||
'not own.'));
|
||||
}
|
||||
|
||||
if ($this->isViewerRejectingReviewer($object, $viewer)) {
|
||||
throw new Exception(
|
||||
pht(
|
||||
'You can not request changes to this revision because you have '.
|
||||
'already requested changes.'));
|
||||
}
|
||||
}
|
||||
|
||||
public function getTitle() {
|
||||
return pht(
|
||||
'%s requested changes to this revision.',
|
||||
$this->renderAuthor());
|
||||
}
|
||||
|
||||
public function getTitleForFeed() {
|
||||
return pht(
|
||||
'%s requested changes to %s.',
|
||||
$this->renderAuthor(),
|
||||
$this->renderObject());
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,72 @@
|
|||
<?php
|
||||
|
||||
final class DifferentialRevisionReopenTransaction
|
||||
extends DifferentialRevisionActionTransaction {
|
||||
|
||||
const TRANSACTIONTYPE = 'differential.revision.reopen';
|
||||
const ACTIONKEY = 'reopen';
|
||||
|
||||
protected function getRevisionActionLabel() {
|
||||
return pht('Reopen Revision');
|
||||
}
|
||||
|
||||
protected function getRevisionActionDescription() {
|
||||
return pht('This revision will be reopened for review.');
|
||||
}
|
||||
|
||||
public function getIcon() {
|
||||
return 'fa-bullhorn';
|
||||
}
|
||||
|
||||
public function getColor() {
|
||||
return 'sky';
|
||||
}
|
||||
|
||||
protected function getRevisionActionOrder() {
|
||||
return 400;
|
||||
}
|
||||
|
||||
public function generateOldValue($object) {
|
||||
return !$object->isClosed();
|
||||
}
|
||||
|
||||
public function applyInternalEffects($object, $value) {
|
||||
$object->setStatus(ArcanistDifferentialRevisionStatus::NEEDS_REVIEW);
|
||||
}
|
||||
|
||||
protected function validateAction($object, PhabricatorUser $viewer) {
|
||||
// Note that we're testing for "Closed", exactly, not just any closed
|
||||
// status.
|
||||
$status_closed = ArcanistDifferentialRevisionStatus::CLOSED;
|
||||
if ($object->getStatus() != $status_closed) {
|
||||
throw new Exception(
|
||||
pht(
|
||||
'You can not reopen this revision because it is not closed. '.
|
||||
'Only closed revisions can be reopened.'));
|
||||
}
|
||||
|
||||
$config_key = 'differential.allow-reopen';
|
||||
if (!PhabricatorEnv::getEnvConfig($config_key)) {
|
||||
throw new Exception(
|
||||
pht(
|
||||
'You can not reopen this revision because configuration prevents '.
|
||||
'any revision from being reopened. You can change this behavior '.
|
||||
'by adjusting the "%s" setting in Config.',
|
||||
$config_key));
|
||||
}
|
||||
}
|
||||
|
||||
public function getTitle() {
|
||||
return pht(
|
||||
'%s reopened this revision.',
|
||||
$this->renderAuthor());
|
||||
}
|
||||
|
||||
public function getTitleForFeed() {
|
||||
return pht(
|
||||
'%s reopened %s.',
|
||||
$this->renderAuthor(),
|
||||
$this->renderObject());
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,74 @@
|
|||
<?php
|
||||
|
||||
final class DifferentialRevisionRequestReviewTransaction
|
||||
extends DifferentialRevisionActionTransaction {
|
||||
|
||||
const TRANSACTIONTYPE = 'differential.revision.request';
|
||||
const ACTIONKEY = 'request-review';
|
||||
|
||||
protected function getRevisionActionLabel() {
|
||||
return pht('Request Review');
|
||||
}
|
||||
|
||||
protected function getRevisionActionDescription() {
|
||||
return pht('This revision will be returned to reviewers for feedback.');
|
||||
}
|
||||
|
||||
public function getColor() {
|
||||
return 'sky';
|
||||
}
|
||||
|
||||
protected function getRevisionActionOrder() {
|
||||
return 200;
|
||||
}
|
||||
|
||||
public function generateOldValue($object) {
|
||||
$status_review = ArcanistDifferentialRevisionStatus::NEEDS_REVIEW;
|
||||
return ($object->getStatus() == $status_review);
|
||||
}
|
||||
|
||||
public function applyInternalEffects($object, $value) {
|
||||
$status_review = ArcanistDifferentialRevisionStatus::NEEDS_REVIEW;
|
||||
$object->setStatus($status_review);
|
||||
}
|
||||
|
||||
protected function validateAction($object, PhabricatorUser $viewer) {
|
||||
$status_review = ArcanistDifferentialRevisionStatus::NEEDS_REVIEW;
|
||||
if ($object->getStatus() == $status_review) {
|
||||
throw new Exception(
|
||||
pht(
|
||||
'You can not request review of this revision because this '.
|
||||
'revision is already under review and the action would have '.
|
||||
'no effect.'));
|
||||
}
|
||||
|
||||
if ($object->isClosed()) {
|
||||
throw new Exception(
|
||||
pht(
|
||||
'You can not request review of this revision because it has '.
|
||||
'already been closed. You can only request review of open '.
|
||||
'revisions.'));
|
||||
}
|
||||
|
||||
if (!$this->isViewerRevisionAuthor($object, $viewer)) {
|
||||
throw new Exception(
|
||||
pht(
|
||||
'You can not request review of this revision because you are not '.
|
||||
'the author of the revision.'));
|
||||
}
|
||||
}
|
||||
|
||||
public function getTitle() {
|
||||
return pht(
|
||||
'%s requested review of this revision.',
|
||||
$this->renderAuthor());
|
||||
}
|
||||
|
||||
public function getTitleForFeed() {
|
||||
return pht(
|
||||
'%s requested review of %s.',
|
||||
$this->renderAuthor(),
|
||||
$this->renderObject());
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,82 @@
|
|||
<?php
|
||||
|
||||
final class DifferentialRevisionResignTransaction
|
||||
extends DifferentialRevisionReviewTransaction {
|
||||
|
||||
const TRANSACTIONTYPE = 'differential.revision.resign';
|
||||
const ACTIONKEY = 'resign';
|
||||
|
||||
protected function getRevisionActionLabel() {
|
||||
return pht('Resign as Reviewer');
|
||||
}
|
||||
|
||||
protected function getRevisionActionDescription() {
|
||||
return pht('You will resign as a reviewer for this change.');
|
||||
}
|
||||
|
||||
public function getIcon() {
|
||||
return 'fa-flag';
|
||||
}
|
||||
|
||||
public function getColor() {
|
||||
return 'orange';
|
||||
}
|
||||
|
||||
protected function getRevisionActionOrder() {
|
||||
return 700;
|
||||
}
|
||||
|
||||
public function getCommandKeyword() {
|
||||
return 'resign';
|
||||
}
|
||||
|
||||
public function getCommandAliases() {
|
||||
return array();
|
||||
}
|
||||
|
||||
public function getCommandSummary() {
|
||||
return pht('Resign from a revision.');
|
||||
}
|
||||
|
||||
public function generateOldValue($object) {
|
||||
$actor = $this->getActor();
|
||||
return !$this->isViewerAnyReviewer($object, $actor);
|
||||
}
|
||||
|
||||
public function applyExternalEffects($object, $value) {
|
||||
$status = DifferentialReviewerStatus::STATUS_RESIGNED;
|
||||
$actor = $this->getActor();
|
||||
$this->applyReviewerEffect($object, $actor, $value, $status);
|
||||
}
|
||||
|
||||
protected function validateAction($object, PhabricatorUser $viewer) {
|
||||
if ($object->isClosed()) {
|
||||
throw new Exception(
|
||||
pht(
|
||||
'You can not resign from this revision because it has already '.
|
||||
'been closed. You can only resign from open revisions.'));
|
||||
}
|
||||
|
||||
if (!$this->isViewerAnyReviewer($object, $viewer)) {
|
||||
throw new Exception(
|
||||
pht(
|
||||
'You can not resign from this revision because you are not a '.
|
||||
'reviewer. You can only resign from revisions where you are a '.
|
||||
'reviewer.'));
|
||||
}
|
||||
}
|
||||
|
||||
public function getTitle() {
|
||||
return pht(
|
||||
'%s resigned from this revision.',
|
||||
$this->renderAuthor());
|
||||
}
|
||||
|
||||
public function getTitleForFeed() {
|
||||
return pht(
|
||||
'%s resigned from %s.',
|
||||
$this->renderAuthor(),
|
||||
$this->renderObject());
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,120 @@
|
|||
<?php
|
||||
|
||||
abstract class DifferentialRevisionReviewTransaction
|
||||
extends DifferentialRevisionActionTransaction {
|
||||
|
||||
protected function getRevisionActionGroupKey() {
|
||||
return DifferentialRevisionEditEngine::ACTIONGROUP_REVIEW;
|
||||
}
|
||||
|
||||
protected function isViewerAnyReviewer(
|
||||
DifferentialRevision $revision,
|
||||
PhabricatorUser $viewer) {
|
||||
return ($this->getViewerReviewerStatus($revision, $viewer) !== null);
|
||||
}
|
||||
|
||||
protected function isViewerAcceptingReviewer(
|
||||
DifferentialRevision $revision,
|
||||
PhabricatorUser $viewer) {
|
||||
return $this->isViewerReviewerStatusAmong(
|
||||
$revision,
|
||||
$viewer,
|
||||
array(
|
||||
DifferentialReviewerStatus::STATUS_ACCEPTED,
|
||||
));
|
||||
}
|
||||
|
||||
protected function isViewerRejectingReviewer(
|
||||
DifferentialRevision $revision,
|
||||
PhabricatorUser $viewer) {
|
||||
return $this->isViewerReviewerStatusAmong(
|
||||
$revision,
|
||||
$viewer,
|
||||
array(
|
||||
DifferentialReviewerStatus::STATUS_REJECTED,
|
||||
));
|
||||
}
|
||||
|
||||
protected function getViewerReviewerStatus(
|
||||
DifferentialRevision $revision,
|
||||
PhabricatorUser $viewer) {
|
||||
|
||||
if (!$viewer->getPHID()) {
|
||||
return null;
|
||||
}
|
||||
|
||||
foreach ($revision->getReviewerStatus() as $reviewer) {
|
||||
if ($reviewer->getReviewerPHID() != $viewer->getPHID()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
return $reviewer->getStatus();
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
protected function isViewerReviewerStatusAmong(
|
||||
DifferentialRevision $revision,
|
||||
PhabricatorUser $viewer,
|
||||
array $status_list) {
|
||||
|
||||
$status = $this->getViewerReviewerStatus($revision, $viewer);
|
||||
if ($status === null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$status_map = array_fuse($status_list);
|
||||
return isset($status_map[$status]);
|
||||
}
|
||||
|
||||
protected function applyReviewerEffect(
|
||||
DifferentialRevision $revision,
|
||||
PhabricatorUser $viewer,
|
||||
$value,
|
||||
$status) {
|
||||
|
||||
$map = array();
|
||||
|
||||
// When you accept or reject, you may accept or reject on behalf of all
|
||||
// reviewers you have authority for. When you resign, you only affect
|
||||
// yourself.
|
||||
$with_authority = ($status != DifferentialReviewerStatus::STATUS_RESIGNED);
|
||||
if ($with_authority) {
|
||||
foreach ($revision->getReviewerStatus() as $reviewer) {
|
||||
if ($reviewer->hasAuthority($viewer)) {
|
||||
$map[$reviewer->getReviewerPHID()] = $status;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// In all cases, you affect yourself.
|
||||
$map[$viewer->getPHID()] = $status;
|
||||
|
||||
// Convert reviewer statuses into edge data.
|
||||
foreach ($map as $reviewer_phid => $reviewer_status) {
|
||||
$map[$reviewer_phid] = array(
|
||||
'data' => array(
|
||||
'status' => $reviewer_status,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
$src_phid = $revision->getPHID();
|
||||
$edge_type = DifferentialRevisionHasReviewerEdgeType::EDGECONST;
|
||||
|
||||
$editor = new PhabricatorEdgeEditor();
|
||||
foreach ($map as $dst_phid => $edge_data) {
|
||||
if ($status == DifferentialReviewerStatus::STATUS_RESIGNED) {
|
||||
// TODO: For now, we just remove these reviewers. In the future, we will
|
||||
// store resignations explicitly.
|
||||
$editor->removeEdge($src_phid, $edge_type, $dst_phid);
|
||||
} else {
|
||||
$editor->addEdge($src_phid, $edge_type, $dst_phid, $edge_data);
|
||||
}
|
||||
}
|
||||
|
||||
$editor->save();
|
||||
}
|
||||
|
||||
}
|
|
@ -3,6 +3,8 @@
|
|||
final class PhabricatorEmbedFileRemarkupRule
|
||||
extends PhabricatorObjectRemarkupRule {
|
||||
|
||||
private $viewer;
|
||||
|
||||
const KEY_EMBED_FILE_PHIDS = 'phabricator.embedded-file-phids';
|
||||
|
||||
protected function getObjectNamePrefix() {
|
||||
|
@ -12,9 +14,9 @@ final class PhabricatorEmbedFileRemarkupRule
|
|||
protected function loadObjects(array $ids) {
|
||||
$engine = $this->getEngine();
|
||||
|
||||
$viewer = $engine->getConfig('viewer');
|
||||
$this->viewer = $engine->getConfig('viewer');
|
||||
$objects = id(new PhabricatorFileQuery())
|
||||
->setViewer($viewer)
|
||||
->setViewer($this->viewer)
|
||||
->withIDs($ids)
|
||||
->needTransforms(
|
||||
array(
|
||||
|
@ -282,6 +284,7 @@ final class PhabricatorEmbedFileRemarkupRule
|
|||
array $options) {
|
||||
|
||||
return id(new PhabricatorFileLinkView())
|
||||
->setViewer($this->viewer)
|
||||
->setFilePHID($file->getPHID())
|
||||
->setFileName($this->assertFlatText($options['name']))
|
||||
->setFileDownloadURI($file->getDownloadURI())
|
||||
|
|
|
@ -497,17 +497,28 @@ final class HarbormasterBuildEngine extends Phobject {
|
|||
return;
|
||||
}
|
||||
|
||||
if (!($object instanceof PhabricatorApplicationTransactionInterface)) {
|
||||
$publish_phid = $object->getHarbormasterPublishablePHID();
|
||||
if (!$publish_phid) {
|
||||
return;
|
||||
}
|
||||
|
||||
// TODO: Publishing these transactions is causing a race. See T8650.
|
||||
// We shouldn't be publishing to diffs anyway.
|
||||
if ($object instanceof DifferentialDiff) {
|
||||
if ($publish_phid === $object->getPHID()) {
|
||||
$publish = $object;
|
||||
} else {
|
||||
$publish = id(new PhabricatorObjectQuery())
|
||||
->setViewer($viewer)
|
||||
->withPHIDs(array($publish_phid))
|
||||
->executeOne();
|
||||
if (!$publish) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (!($publish instanceof PhabricatorApplicationTransactionInterface)) {
|
||||
return;
|
||||
}
|
||||
|
||||
$template = $object->getApplicationTransactionTemplate();
|
||||
$template = $publish->getApplicationTransactionTemplate();
|
||||
if (!$template) {
|
||||
return;
|
||||
}
|
||||
|
@ -526,7 +537,7 @@ final class HarbormasterBuildEngine extends Phobject {
|
|||
$daemon_source = PhabricatorContentSource::newForSource(
|
||||
PhabricatorDaemonContentSource::SOURCECONST);
|
||||
|
||||
$editor = $object->getApplicationTransactionEditor()
|
||||
$editor = $publish->getApplicationTransactionEditor()
|
||||
->setActor($viewer)
|
||||
->setActingAsPHID($harbormaster_phid)
|
||||
->setContentSource($daemon_source)
|
||||
|
@ -534,7 +545,7 @@ final class HarbormasterBuildEngine extends Phobject {
|
|||
->setContinueOnMissingFields(true);
|
||||
|
||||
$editor->applyTransactions(
|
||||
$object->getApplicationTransactionObject(),
|
||||
$publish->getApplicationTransactionObject(),
|
||||
array($template));
|
||||
}
|
||||
|
||||
|
|
|
@ -18,6 +18,21 @@ interface HarbormasterBuildableInterface {
|
|||
public function getHarbormasterBuildablePHID();
|
||||
public function getHarbormasterContainerPHID();
|
||||
|
||||
|
||||
/**
|
||||
* Get the object PHID which build status should be published to.
|
||||
*
|
||||
* In some cases (like commits), this is the object itself. In other cases,
|
||||
* it is a different object: for example, diffs publish builds to revisions.
|
||||
*
|
||||
* This method can return `null` to disable publishing.
|
||||
*
|
||||
* @return phid|null Build status updates will be published to this object's
|
||||
* transaction timeline.
|
||||
*/
|
||||
public function getHarbormasterPublishablePHID();
|
||||
|
||||
|
||||
public function getBuildVariables();
|
||||
public function getAvailableBuildVariables();
|
||||
|
||||
|
|
|
@ -317,6 +317,10 @@ final class HarbormasterBuildable extends HarbormasterDAO
|
|||
return $this->getContainerPHID();
|
||||
}
|
||||
|
||||
public function getHarbormasterPublishablePHID() {
|
||||
return $this->getBuildableObject()->getHarbormasterPublishablePHID();
|
||||
}
|
||||
|
||||
public function getBuildVariables() {
|
||||
return array();
|
||||
}
|
||||
|
|
|
@ -22,9 +22,18 @@ final class PhabricatorOwnersPackageDatasource
|
|||
$results = array();
|
||||
|
||||
$query = id(new PhabricatorOwnersPackageQuery())
|
||||
->withNameNgrams($raw_query)
|
||||
->setOrder('name');
|
||||
|
||||
// If the user is querying by monogram explicitly, like "O123", do an ID
|
||||
// search. Otherwise, do an ngram substring search.
|
||||
if (preg_match('/^[oO]\d+\z/', $raw_query)) {
|
||||
$id = trim($raw_query, 'oO');
|
||||
$id = (int)$id;
|
||||
$query->withIDs(array($id));
|
||||
} else {
|
||||
$query->withNameNgrams($raw_query);
|
||||
}
|
||||
|
||||
$packages = $this->executeQuery($query);
|
||||
foreach ($packages as $package) {
|
||||
$name = $package->getName();
|
||||
|
|
|
@ -14,20 +14,52 @@ final class PhabricatorPasteTitleTransaction
|
|||
}
|
||||
|
||||
public function getTitle() {
|
||||
return pht(
|
||||
'%s changed the title of this paste from %s to %s.',
|
||||
$this->renderAuthor(),
|
||||
$this->renderOldValue(),
|
||||
$this->renderNewValue());
|
||||
$old = $this->getOldValue();
|
||||
$new = $this->getNeWValue();
|
||||
|
||||
if (strlen($old) && strlen($new)) {
|
||||
return pht(
|
||||
'%s changed the title of this paste from %s to %s.',
|
||||
$this->renderAuthor(),
|
||||
$this->renderOldValue(),
|
||||
$this->renderNewValue());
|
||||
} else if (strlen($new)) {
|
||||
return pht(
|
||||
'%s changed the title of this paste from untitled to %s.',
|
||||
$this->renderAuthor(),
|
||||
$this->renderNewValue());
|
||||
} else {
|
||||
return pht(
|
||||
'%s changed the title of this paste from %s to untitled.',
|
||||
$this->renderAuthor(),
|
||||
$this->renderOldValue());
|
||||
}
|
||||
}
|
||||
|
||||
public function getTitleForFeed() {
|
||||
return pht(
|
||||
'%s updated the title for %s from %s to %s.',
|
||||
$this->renderAuthor(),
|
||||
$this->renderObject(),
|
||||
$this->renderOldValue(),
|
||||
$this->renderNewValue());
|
||||
$old = $this->getOldValue();
|
||||
$new = $this->getNeWValue();
|
||||
|
||||
if (strlen($old) && strlen($new)) {
|
||||
return pht(
|
||||
'%s updated the title for %s from %s to %s.',
|
||||
$this->renderAuthor(),
|
||||
$this->renderObject(),
|
||||
$this->renderOldValue(),
|
||||
$this->renderNewValue());
|
||||
} else if (strlen($new)) {
|
||||
return pht(
|
||||
'%s updated the title for %s from untitled to %s.',
|
||||
$this->renderAuthor(),
|
||||
$this->renderObject(),
|
||||
$this->renderNewValue());
|
||||
} else {
|
||||
return pht(
|
||||
'%s updated the title for %s from %s to untitled.',
|
||||
$this->renderAuthor(),
|
||||
$this->renderObject(),
|
||||
$this->renderOldValue());
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -149,7 +149,22 @@ final class PhabricatorObjectListQuery extends Phobject {
|
|||
$missing = array();
|
||||
foreach ($name_map as $key => $name) {
|
||||
if (empty($objects[$key])) {
|
||||
$missing[] = $name;
|
||||
$missing[$key] = $name;
|
||||
}
|
||||
}
|
||||
|
||||
$result = array_unique(mpull($objects, 'getPHID'));
|
||||
|
||||
// For values which are plain PHIDs of allowed types, let them through
|
||||
// unchecked. This can happen occur if subscribers or reviewers which the
|
||||
// revision author does not have permission to see are added by Herald
|
||||
// rules. Any actual edits will be checked later: users are not allowed
|
||||
// to add new reviewers they can't see, but they can touch a field which
|
||||
// contains them.
|
||||
foreach ($missing as $key => $value) {
|
||||
if (isset($allowed[phid_get_type($value)])) {
|
||||
unset($missing[$key]);
|
||||
$result[$key] = $value;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -181,8 +196,6 @@ final class PhabricatorObjectListQuery extends Phobject {
|
|||
}
|
||||
}
|
||||
|
||||
$result = array_unique(mpull($objects, 'getPHID'));
|
||||
|
||||
if ($suffixes) {
|
||||
foreach ($result as $key => $phid) {
|
||||
$result[$key] = array(
|
||||
|
|
|
@ -123,6 +123,12 @@ final class PhabricatorPolicyFilter extends Phobject {
|
|||
return $objects;
|
||||
}
|
||||
|
||||
// Before doing any actual object checks, make sure the viewer can see
|
||||
// the applications that these objects belong to. This is normally enforced
|
||||
// in the Query layer before we reach object filtering, but execution
|
||||
// sometimes reaches policy filtering without running application checks.
|
||||
$objects = $this->applyApplicationChecks($objects);
|
||||
|
||||
$filtered = array();
|
||||
$viewer_phid = $viewer->getPHID();
|
||||
|
||||
|
@ -864,4 +870,49 @@ final class PhabricatorPolicyFilter extends Phobject {
|
|||
throw $exception;
|
||||
}
|
||||
|
||||
private function applyApplicationChecks(array $objects) {
|
||||
$viewer = $this->viewer;
|
||||
|
||||
foreach ($objects as $key => $object) {
|
||||
$phid = $object->getPHID();
|
||||
if (!$phid) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$application_class = $this->getApplicationForPHID($phid);
|
||||
if ($application_class === null) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$can_see = PhabricatorApplication::isClassInstalledForViewer(
|
||||
$application_class,
|
||||
$viewer);
|
||||
if ($can_see) {
|
||||
continue;
|
||||
}
|
||||
|
||||
unset($objects[$key]);
|
||||
|
||||
$application = newv($application_class, array());
|
||||
$this->rejectObject(
|
||||
$application,
|
||||
$application->getPolicy(PhabricatorPolicyCapability::CAN_VIEW),
|
||||
PhabricatorPolicyCapability::CAN_VIEW);
|
||||
}
|
||||
|
||||
return $objects;
|
||||
}
|
||||
|
||||
private function getApplicationForPHID($phid) {
|
||||
$phid_type = phid_get_type($phid);
|
||||
|
||||
$type_objects = PhabricatorPHIDType::getTypes(array($phid_type));
|
||||
$type_object = idx($type_objects, $phid_type);
|
||||
if (!$type_object) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return $type_object->getPHIDTypeApplicationClass();
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -39,6 +39,46 @@ final class PhabricatorProjectCoreTestCase extends PhabricatorTestCase {
|
|||
$this->assertFalse((bool)$this->refreshProject($proj, $user2));
|
||||
}
|
||||
|
||||
public function testApplicationPolicy() {
|
||||
$user = $this->createUser()
|
||||
->save();
|
||||
|
||||
$proj = $this->createProject($user);
|
||||
|
||||
$this->assertTrue(
|
||||
PhabricatorPolicyFilter::hasCapability(
|
||||
$user,
|
||||
$proj,
|
||||
PhabricatorPolicyCapability::CAN_VIEW));
|
||||
|
||||
// Change the "Can Use Application" policy for Projecs to "No One". This
|
||||
// should cause filtering checks to fail even when they are executed
|
||||
// directly rather than via a Query.
|
||||
$env = PhabricatorEnv::beginScopedEnv();
|
||||
$env->overrideEnvConfig(
|
||||
'phabricator.application-settings',
|
||||
array(
|
||||
'PHID-APPS-PhabricatorProjectApplication' => array(
|
||||
'policy' => array(
|
||||
'view' => PhabricatorPolicies::POLICY_NOONE,
|
||||
),
|
||||
),
|
||||
));
|
||||
|
||||
// Application visibility is cached because it does not normally change
|
||||
// over the course of a single request. Drop the cache so the next filter
|
||||
// test uses the new visibility.
|
||||
PhabricatorCaches::destroyRequestCache();
|
||||
|
||||
$this->assertFalse(
|
||||
PhabricatorPolicyFilter::hasCapability(
|
||||
$user,
|
||||
$proj,
|
||||
PhabricatorPolicyCapability::CAN_VIEW));
|
||||
|
||||
unset($env);
|
||||
}
|
||||
|
||||
public function testIsViewerMemberOrWatcher() {
|
||||
$user1 = $this->createUser()
|
||||
->save();
|
||||
|
|
|
@ -414,6 +414,13 @@ final class PhabricatorRepositoryDiscoveryEngine
|
|||
$epoch = PhabricatorTime::getNow();
|
||||
}
|
||||
|
||||
// If the epoch is not present at all, treat it as though it stores the
|
||||
// value "0". For discussion, see T12062. This behavior is consistent
|
||||
// with the behavior of "git show".
|
||||
if (!strlen($epoch)) {
|
||||
$epoch = 0;
|
||||
}
|
||||
|
||||
$refs[] = id(new PhabricatorRepositoryCommitRef())
|
||||
->setIdentifier($commit)
|
||||
->setEpoch($epoch)
|
||||
|
|
|
@ -413,6 +413,10 @@ final class PhabricatorRepositoryCommit
|
|||
return $this->getRepository()->getPHID();
|
||||
}
|
||||
|
||||
public function getHarbormasterPublishablePHID() {
|
||||
return $this->getPHID();
|
||||
}
|
||||
|
||||
public function getBuildVariables() {
|
||||
$results = array();
|
||||
|
||||
|
|
|
@ -0,0 +1,122 @@
|
|||
<?php
|
||||
|
||||
final class PhabricatorEditEngineProfileMenuItem
|
||||
extends PhabricatorProfileMenuItem {
|
||||
|
||||
const MENUITEMKEY = 'editengine';
|
||||
|
||||
private $form;
|
||||
|
||||
public function getMenuItemTypeIcon() {
|
||||
return 'fa-plus';
|
||||
}
|
||||
|
||||
public function getMenuItemTypeName() {
|
||||
return pht('Forms');
|
||||
}
|
||||
|
||||
public function canAddToObject($object) {
|
||||
return true;
|
||||
}
|
||||
|
||||
public function attachForm($form) {
|
||||
$this->form = $form;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getForm() {
|
||||
$form = $this->form;
|
||||
if (!$form) {
|
||||
return null;
|
||||
}
|
||||
return $form;
|
||||
}
|
||||
|
||||
public function willBuildNavigationItems(array $items) {
|
||||
$viewer = $this->getViewer();
|
||||
$engines = PhabricatorEditEngine::getAllEditEngines();
|
||||
$engine_keys = array_keys($engines);
|
||||
$forms = id(new PhabricatorEditEngineConfigurationQuery())
|
||||
->setViewer($viewer)
|
||||
->withEngineKeys($engine_keys)
|
||||
->withIsDisabled(false)
|
||||
->execute();
|
||||
$form_engines = mgroup($forms, 'getEngineKey');
|
||||
$form_ids = $forms;
|
||||
|
||||
$builtin_map = array();
|
||||
foreach ($form_engines as $engine_key => $form_engine) {
|
||||
$builtin_map[$engine_key] = mpull($form_engine, null, 'getBuiltinKey');
|
||||
}
|
||||
|
||||
foreach ($items as $item) {
|
||||
$key = $item->getMenuItemProperty('formKey');
|
||||
list($engine_key, $form_key) = explode('/', $key);
|
||||
if (is_numeric($form_key)) {
|
||||
$form = idx($form_ids, $form_key, null);
|
||||
$item->getMenuItem()->attachForm($form);
|
||||
} else if (isset($builtin_map[$engine_key][$form_key])) {
|
||||
$form = $builtin_map[$engine_key][$form_key];
|
||||
$item->getMenuItem()->attachForm($form);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public function getDisplayName(
|
||||
PhabricatorProfileMenuItemConfiguration $config) {
|
||||
$form = $this->getForm();
|
||||
if (!$form) {
|
||||
return pht('(Restricted/Invalid Form)');
|
||||
}
|
||||
if (strlen($this->getName($config))) {
|
||||
return $this->getName($config);
|
||||
} else {
|
||||
return $form->getName();
|
||||
}
|
||||
}
|
||||
|
||||
public function buildEditEngineFields(
|
||||
PhabricatorProfileMenuItemConfiguration $config) {
|
||||
return array(
|
||||
id(new PhabricatorTextEditField())
|
||||
->setKey('name')
|
||||
->setLabel(pht('Name'))
|
||||
->setValue($this->getName($config)),
|
||||
id(new PhabricatorDatasourceEditField())
|
||||
->setKey('formKey')
|
||||
->setLabel(pht('Form'))
|
||||
->setDatasource(new PhabricatorEditEngineDatasource())
|
||||
->setSingleValue($config->getMenuItemProperty('formKey')),
|
||||
);
|
||||
}
|
||||
|
||||
private function getName(
|
||||
PhabricatorProfileMenuItemConfiguration $config) {
|
||||
return $config->getMenuItemProperty('name');
|
||||
}
|
||||
|
||||
protected function newNavigationMenuItems(
|
||||
PhabricatorProfileMenuItemConfiguration $config) {
|
||||
|
||||
$form = $this->getForm();
|
||||
if (!$form) {
|
||||
return array();
|
||||
}
|
||||
$engine = $form->getEngine();
|
||||
$form_key = $form->getIdentifier();
|
||||
|
||||
$icon = $form->getIcon();
|
||||
$name = $this->getDisplayName($config);
|
||||
$href = $engine->getEditURI(null, "form/{$form_key}/");
|
||||
|
||||
$item = $this->newItem()
|
||||
->setHref($href)
|
||||
->setName($name)
|
||||
->setIcon($icon);
|
||||
|
||||
return array(
|
||||
$item,
|
||||
);
|
||||
}
|
||||
|
||||
}
|
|
@ -7,6 +7,8 @@ abstract class PhabricatorEditEngineCommentAction extends Phobject {
|
|||
private $value;
|
||||
private $initialValue;
|
||||
private $order;
|
||||
private $groupKey;
|
||||
private $conflictKey;
|
||||
|
||||
abstract public function getPHUIXControlType();
|
||||
abstract public function getPHUIXControlSpecification();
|
||||
|
@ -20,6 +22,24 @@ abstract class PhabricatorEditEngineCommentAction extends Phobject {
|
|||
return $this->key;
|
||||
}
|
||||
|
||||
public function setGroupKey($group_key) {
|
||||
$this->groupKey = $group_key;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getGroupKey() {
|
||||
return $this->groupKey;
|
||||
}
|
||||
|
||||
public function setConflictKey($conflict_key) {
|
||||
$this->conflictKey = $conflict_key;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getConflictKey() {
|
||||
return $this->conflictKey;
|
||||
}
|
||||
|
||||
public function setLabel($label) {
|
||||
$this->label = $label;
|
||||
return $this;
|
||||
|
|
|
@ -0,0 +1,27 @@
|
|||
<?php
|
||||
|
||||
final class PhabricatorEditEngineCommentActionGroup
|
||||
extends Phobject {
|
||||
|
||||
private $key;
|
||||
private $label;
|
||||
|
||||
public function setKey($key) {
|
||||
$this->key = $key;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getKey() {
|
||||
return $this->key;
|
||||
}
|
||||
|
||||
public function setLabel($label) {
|
||||
$this->label = $label;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getLabel() {
|
||||
return $this->label;
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,28 @@
|
|||
<?php
|
||||
|
||||
final class PhabricatorEditEngineStaticCommentAction
|
||||
extends PhabricatorEditEngineCommentAction {
|
||||
|
||||
private $description;
|
||||
|
||||
public function setDescription($description) {
|
||||
$this->description = $description;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getDescription() {
|
||||
return $this->description;
|
||||
}
|
||||
|
||||
public function getPHUIXControlType() {
|
||||
return 'static';
|
||||
}
|
||||
|
||||
public function getPHUIXControlSpecification() {
|
||||
return array(
|
||||
'value' => $this->getValue(),
|
||||
'description' => $this->getDescription(),
|
||||
);
|
||||
}
|
||||
|
||||
}
|
|
@ -48,7 +48,7 @@ final class PhabricatorApplicationTransactionCommentRawController
|
|||
$title = pht('Email Body Text');
|
||||
$body = $message->getRawTextBody();
|
||||
$details_text = pht(
|
||||
'For full details, run `/bin/mail show-outbound --id %d`',
|
||||
'For full details, run `/bin/mail show-inbound --id %d`',
|
||||
$source_id);
|
||||
$addendum = new PHUIRemarkupView($viewer, $details_text);
|
||||
}
|
||||
|
|
|
@ -49,7 +49,14 @@ abstract class PhabricatorEditEngine
|
|||
}
|
||||
|
||||
final public function getEngineKey() {
|
||||
return $this->getPhobjectClassConstant('ENGINECONST', 64);
|
||||
$key = $this->getPhobjectClassConstant('ENGINECONST', 64);
|
||||
if (strpos($key, '/') !== false) {
|
||||
throw new Exception(
|
||||
pht(
|
||||
'EditEngine ("%s") contains an invalid key character "/".',
|
||||
get_class($this)));
|
||||
}
|
||||
return $key;
|
||||
}
|
||||
|
||||
final public function getApplication() {
|
||||
|
@ -1549,6 +1556,9 @@ abstract class PhabricatorEditEngine
|
|||
|
||||
$view->setCommentActions($comment_actions);
|
||||
|
||||
$comment_groups = $this->newCommentActionGroups();
|
||||
$view->setCommentActionGroups($comment_groups);
|
||||
|
||||
return $view;
|
||||
}
|
||||
|
||||
|
@ -1780,6 +1790,11 @@ abstract class PhabricatorEditEngine
|
|||
}
|
||||
}
|
||||
|
||||
$auto_xactions = $this->newAutomaticCommentTransactions($object);
|
||||
foreach ($auto_xactions as $xaction) {
|
||||
$xactions[] = $xaction;
|
||||
}
|
||||
|
||||
if (strlen($comment_text) || !$xactions) {
|
||||
$xactions[] = id(clone $template)
|
||||
->setTransactionType(PhabricatorTransactions::TYPE_COMMENT)
|
||||
|
@ -1797,6 +1812,10 @@ abstract class PhabricatorEditEngine
|
|||
|
||||
try {
|
||||
$xactions = $editor->applyTransactions($object, $xactions);
|
||||
} catch (PhabricatorApplicationTransactionValidationException $ex) {
|
||||
return id(new PhabricatorApplicationTransactionValidationResponse())
|
||||
->setCancelURI($view_uri)
|
||||
->setException($ex);
|
||||
} catch (PhabricatorApplicationTransactionNoEffectException $ex) {
|
||||
return id(new PhabricatorApplicationTransactionNoEffectResponse())
|
||||
->setCancelURI($view_uri)
|
||||
|
@ -1811,10 +1830,13 @@ abstract class PhabricatorEditEngine
|
|||
}
|
||||
|
||||
if ($request->isAjax() && $is_preview) {
|
||||
$preview_content = $this->newCommentPreviewContent($object, $xactions);
|
||||
|
||||
return id(new PhabricatorApplicationTransactionResponse())
|
||||
->setViewer($viewer)
|
||||
->setTransactions($xactions)
|
||||
->setIsPreview($is_preview);
|
||||
->setIsPreview($is_preview)
|
||||
->setPreviewContent($preview_content);
|
||||
} else {
|
||||
return id(new AphrontRedirectResponse())
|
||||
->setURI($view_uri);
|
||||
|
@ -2157,6 +2179,18 @@ abstract class PhabricatorEditEngine
|
|||
return $request->getURIData('editAction');
|
||||
}
|
||||
|
||||
protected function newCommentActionGroups() {
|
||||
return array();
|
||||
}
|
||||
|
||||
protected function newAutomaticCommentTransactions($object) {
|
||||
return array();
|
||||
}
|
||||
|
||||
protected function newCommentPreviewContent($object, array $xactions) {
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
/* -( Form Pages )--------------------------------------------------------- */
|
||||
|
||||
|
|
|
@ -0,0 +1,51 @@
|
|||
<?php
|
||||
|
||||
final class PhabricatorApplyEditField
|
||||
extends PhabricatorEditField {
|
||||
|
||||
private $actionDescription;
|
||||
private $actionConflictKey;
|
||||
|
||||
protected function newControl() {
|
||||
return null;
|
||||
}
|
||||
|
||||
public function setActionDescription($action_description) {
|
||||
$this->actionDescription = $action_description;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getActionDescription() {
|
||||
return $this->actionDescription;
|
||||
}
|
||||
|
||||
public function setActionConflictKey($action_conflict_key) {
|
||||
$this->actionConflictKey = $action_conflict_key;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getActionConflictKey() {
|
||||
return $this->actionConflictKey;
|
||||
}
|
||||
|
||||
protected function newHTTPParameterType() {
|
||||
return new AphrontBoolHTTPParameterType();
|
||||
}
|
||||
|
||||
protected function newConduitParameterType() {
|
||||
return new ConduitBoolParameterType();
|
||||
}
|
||||
|
||||
public function shouldGenerateTransactionsFromSubmit() {
|
||||
// This type of edit field just applies a prebuilt action, like "Accept
|
||||
// Revision", and can not be submitted as part of an "Edit Object" form.
|
||||
return false;
|
||||
}
|
||||
|
||||
protected function newCommentAction() {
|
||||
return id(new PhabricatorEditEngineStaticCommentAction())
|
||||
->setDescription($this->getActionDescription())
|
||||
->setConflictKey($this->getActionConflictKey());
|
||||
}
|
||||
|
||||
}
|
|
@ -25,6 +25,7 @@ abstract class PhabricatorEditField extends Phobject {
|
|||
|
||||
private $commentActionLabel;
|
||||
private $commentActionValue;
|
||||
private $commentActionGroupKey;
|
||||
private $commentActionOrder = 1000;
|
||||
private $hasCommentActionValue;
|
||||
|
||||
|
@ -245,6 +246,15 @@ abstract class PhabricatorEditField extends Phobject {
|
|||
return $this->commentActionLabel;
|
||||
}
|
||||
|
||||
public function setCommentActionGroupKey($key) {
|
||||
$this->commentActionGroupKey = $key;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getCommentActionGroupKey() {
|
||||
return $this->commentActionGroupKey;
|
||||
}
|
||||
|
||||
public function setCommentActionOrder($order) {
|
||||
$this->commentActionOrder = $order;
|
||||
return $this;
|
||||
|
@ -719,7 +729,8 @@ abstract class PhabricatorEditField extends Phobject {
|
|||
->setKey($this->getKey())
|
||||
->setLabel($label)
|
||||
->setValue($this->getValueForCommentAction($value))
|
||||
->setOrder($this->getCommentActionOrder());
|
||||
->setOrder($this->getCommentActionOrder())
|
||||
->setGroupKey($this->getCommentActionGroupKey());
|
||||
|
||||
return $action;
|
||||
}
|
||||
|
|
|
@ -7,6 +7,7 @@ final class PhabricatorApplicationTransactionResponse
|
|||
private $transactions;
|
||||
private $isPreview;
|
||||
private $transactionView;
|
||||
private $previewContent;
|
||||
|
||||
public function setTransactionView($transaction_view) {
|
||||
$this->transactionView = $transaction_view;
|
||||
|
@ -46,6 +47,15 @@ final class PhabricatorApplicationTransactionResponse
|
|||
return $this;
|
||||
}
|
||||
|
||||
public function setPreviewContent($preview_content) {
|
||||
$this->previewContent = $preview_content;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getPreviewContent() {
|
||||
return $this->previewContent;
|
||||
}
|
||||
|
||||
public function reduceProxyResponse() {
|
||||
if ($this->transactionView) {
|
||||
$view = $this->transactionView;
|
||||
|
@ -75,7 +85,8 @@ final class PhabricatorApplicationTransactionResponse
|
|||
|
||||
$content = array(
|
||||
'xactions' => $xactions,
|
||||
'spacer' => PHUITimelineView::renderSpacer(),
|
||||
'spacer' => PHUITimelineView::renderSpacer(),
|
||||
'previewContent' => hsprintf('%s', $this->getPreviewContent()),
|
||||
);
|
||||
|
||||
return $this->getProxy()->setContent($content);
|
||||
|
|
|
@ -109,6 +109,14 @@ final class PhabricatorEditEngineConfiguration
|
|||
return $this;
|
||||
}
|
||||
|
||||
public function setBuiltinKey($key) {
|
||||
if (strpos($key, '/') !== false) {
|
||||
throw new Exception(
|
||||
pht('EditEngine BuiltinKey contains an invalid key character "/".'));
|
||||
}
|
||||
return parent::setBuiltinKey($key);
|
||||
}
|
||||
|
||||
public function attachEngine(PhabricatorEditEngine $engine) {
|
||||
$this->engine = $engine;
|
||||
return $this;
|
||||
|
|
|
@ -15,25 +15,47 @@ final class PhabricatorEditEngineDatasource
|
|||
return 'PhabricatorTransactionsApplication';
|
||||
}
|
||||
|
||||
protected function renderSpecialTokens(array $values) {
|
||||
return $this->renderTokensFromResults($this->buildResults(), $values);
|
||||
}
|
||||
|
||||
public function loadResults() {
|
||||
$results = $this->buildResults();
|
||||
return $this->filterResultsAgainstTokens($results);
|
||||
}
|
||||
|
||||
private function buildResults() {
|
||||
$query = id(new PhabricatorEditEngineConfigurationQuery());
|
||||
|
||||
$forms = $this->executeQuery($query);
|
||||
$results = array();
|
||||
foreach ($forms as $form) {
|
||||
|
||||
if ($form->getID()) {
|
||||
$key = $form->getEngineKey().'/'.$form->getID();
|
||||
} else {
|
||||
$key = $form->getEngineKey().'/'.$form->getBuiltinKey();
|
||||
}
|
||||
$result = id(new PhabricatorTypeaheadResult())
|
||||
->setName($form->getName())
|
||||
->setPHID($form->getPHID());
|
||||
->setPHID($key)
|
||||
->setIcon($form->getIcon());
|
||||
|
||||
if ($form->getIsDisabled()) {
|
||||
$result->setClosed(pht('Archived'));
|
||||
}
|
||||
|
||||
$results[] = $result;
|
||||
if ($form->getIsDefault()) {
|
||||
$result->addAttribute(pht('Create Form'));
|
||||
}
|
||||
|
||||
if ($form->getIsEdit()) {
|
||||
$result->addAttribute(pht('Edit Form'));
|
||||
}
|
||||
|
||||
$results[$key] = $result;
|
||||
}
|
||||
|
||||
return $this->filterResultsAgainstTokens($results);
|
||||
return $results;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -20,10 +20,12 @@ class PhabricatorApplicationTransactionCommentView extends AphrontView {
|
|||
private $headerText;
|
||||
private $noPermission;
|
||||
private $fullWidth;
|
||||
private $infoView;
|
||||
|
||||
private $currentVersion;
|
||||
private $versionedDraft;
|
||||
private $commentActions;
|
||||
private $commentActionGroups = array();
|
||||
private $transactionTimeline;
|
||||
|
||||
public function setObjectPHID($object_phid) {
|
||||
|
@ -108,6 +110,15 @@ class PhabricatorApplicationTransactionCommentView extends AphrontView {
|
|||
return $this;
|
||||
}
|
||||
|
||||
public function setInfoView(PHUIInfoView $info_view) {
|
||||
$this->infoView = $info_view;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getInfoView() {
|
||||
return $this->infoView;
|
||||
}
|
||||
|
||||
public function setCommentActions(array $comment_actions) {
|
||||
assert_instances_of($comment_actions, 'PhabricatorEditEngineCommentAction');
|
||||
$this->commentActions = $comment_actions;
|
||||
|
@ -118,6 +129,16 @@ class PhabricatorApplicationTransactionCommentView extends AphrontView {
|
|||
return $this->commentActions;
|
||||
}
|
||||
|
||||
public function setCommentActionGroups(array $groups) {
|
||||
assert_instances_of($groups, 'PhabricatorEditEngineCommentActionGroup');
|
||||
$this->commentActionGroups = $groups;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getCommentActionGroups() {
|
||||
return $this->commentActionGroups;
|
||||
}
|
||||
|
||||
public function setNoPermission($no_permission) {
|
||||
$this->noPermission = $no_permission;
|
||||
return $this;
|
||||
|
@ -279,16 +300,14 @@ class PhabricatorApplicationTransactionCommentView extends AphrontView {
|
|||
'type' => $comment_action->getPHUIXControlType(),
|
||||
'spec' => $comment_action->getPHUIXControlSpecification(),
|
||||
'initialValue' => $comment_action->getInitialValue(),
|
||||
'groupKey' => $comment_action->getGroupKey(),
|
||||
'conflictKey' => $comment_action->getConflictKey(),
|
||||
);
|
||||
|
||||
$type_map[$key] = $comment_action;
|
||||
}
|
||||
|
||||
$options = array();
|
||||
$options['+'] = pht('Add Action...');
|
||||
foreach ($action_map as $key => $item) {
|
||||
$options[$key] = $item['label'];
|
||||
}
|
||||
$options = $this->newCommentActionOptions($action_map);
|
||||
|
||||
$action_id = celerity_generate_unique_node_id();
|
||||
$input_id = celerity_generate_unique_node_id();
|
||||
|
@ -326,6 +345,12 @@ class PhabricatorApplicationTransactionCommentView extends AphrontView {
|
|||
));
|
||||
|
||||
$form->appendChild($action_bar);
|
||||
|
||||
$info_view = $this->getInfoView();
|
||||
if ($info_view) {
|
||||
$form->appendChild($info_view);
|
||||
}
|
||||
|
||||
$form->appendChild($invisi_bar);
|
||||
$form->addClass('phui-comment-has-actions');
|
||||
|
||||
|
@ -424,4 +449,46 @@ class PhabricatorApplicationTransactionCommentView extends AphrontView {
|
|||
return $this->commentID;
|
||||
}
|
||||
|
||||
private function newCommentActionOptions(array $action_map) {
|
||||
$options = array();
|
||||
$options['+'] = pht('Add Action...');
|
||||
|
||||
// Merge options into groups.
|
||||
$groups = array();
|
||||
foreach ($action_map as $key => $item) {
|
||||
$group_key = $item['groupKey'];
|
||||
if (!isset($groups[$group_key])) {
|
||||
$groups[$group_key] = array();
|
||||
}
|
||||
$groups[$group_key][$key] = $item;
|
||||
}
|
||||
|
||||
$group_specs = $this->getCommentActionGroups();
|
||||
$group_labels = mpull($group_specs, 'getLabel', 'getKey');
|
||||
|
||||
// Reorder groups to put them in the same order as the recognized
|
||||
// group definitions.
|
||||
$groups = array_select_keys($groups, array_keys($group_labels)) + $groups;
|
||||
|
||||
// Move options with no group to the end.
|
||||
$default_group = idx($groups, '');
|
||||
if ($default_group) {
|
||||
unset($groups['']);
|
||||
$groups[''] = $default_group;
|
||||
}
|
||||
|
||||
foreach ($groups as $group_key => $group_items) {
|
||||
if (strlen($group_key)) {
|
||||
$group_label = idx($group_labels, $group_key, $group_key);
|
||||
$options[$group_label] = ipull($group_items, 'label');
|
||||
} else {
|
||||
foreach ($group_items as $key => $item) {
|
||||
$options[$key] = $item['label'];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $options;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -21,15 +21,15 @@ final class PhabricatorApplicationTransactionTextDiffDetailView
|
|||
|
||||
$old_styles = array(
|
||||
'padding: 0 2px;',
|
||||
'color: #802b2b;',
|
||||
'background: rgba(251, 175, 175, .7);',
|
||||
'color: #333333;',
|
||||
'background: #f8cbcb;',
|
||||
);
|
||||
$old_styles = implode(' ', $old_styles);
|
||||
|
||||
$new_styles = array(
|
||||
'padding: 0 2px;',
|
||||
'color: #3e6d35;',
|
||||
'background: rgba(151, 234, 151, .6);',
|
||||
'color: #333333;',
|
||||
'background: #a6f3a6;',
|
||||
);
|
||||
$new_styles = implode(' ', $new_styles);
|
||||
|
||||
|
|
|
@ -55,6 +55,10 @@ class PhabricatorApplicationTransactionView extends AphrontView {
|
|||
return $this;
|
||||
}
|
||||
|
||||
public function getIsPreview() {
|
||||
return $this->isPreview;
|
||||
}
|
||||
|
||||
public function setShowEditActions($show_edit_actions) {
|
||||
$this->showEditActions = $show_edit_actions;
|
||||
return $this;
|
||||
|
|
|
@ -65,7 +65,7 @@ In most cases, you can find the upstream commit you've branched from like this:
|
|||
|
||||
```
|
||||
$ git merge-base HEAD origin/master
|
||||
````
|
||||
```
|
||||
|
||||
Note that if you report a bug and have local commits, we will almost always ask
|
||||
you to reproduce the issue against a clean copy of Phabricator before we
|
||||
|
|
|
@ -0,0 +1,75 @@
|
|||
<?php
|
||||
|
||||
final class PHUIDiffInlineCommentPreviewListView
|
||||
extends AphrontView {
|
||||
|
||||
private $inlineComments = array();
|
||||
private $ownerPHID;
|
||||
|
||||
public function setInlineComments(array $comments) {
|
||||
assert_instances_of($comments, 'PhabricatorApplicationTransactionComment');
|
||||
$this->inlineComments = $comments;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getInlineComments() {
|
||||
return $this->inlineComments;
|
||||
}
|
||||
|
||||
public function setOwnerPHID($owner_phid) {
|
||||
$this->ownerPHID = $owner_phid;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getOwnerPHID() {
|
||||
return $this->ownerPHID;
|
||||
}
|
||||
|
||||
public function render() {
|
||||
$viewer = $this->getViewer();
|
||||
|
||||
$config = array(
|
||||
'pht' => array(
|
||||
'view' => pht('View'),
|
||||
),
|
||||
);
|
||||
|
||||
Javelin::initBehavior('diff-preview-link', $config);
|
||||
|
||||
$inlines = $this->getInlineComments();
|
||||
foreach ($inlines as $key => $inline) {
|
||||
$inlines[$key] = DifferentialInlineComment::newFromModernComment(
|
||||
$inline);
|
||||
}
|
||||
|
||||
$engine = new PhabricatorMarkupEngine();
|
||||
$engine->setViewer($viewer);
|
||||
foreach ($inlines as $inline) {
|
||||
$engine->addObject(
|
||||
$inline,
|
||||
PhabricatorInlineCommentInterface::MARKUP_FIELD_BODY);
|
||||
}
|
||||
$engine->process();
|
||||
|
||||
$owner_phid = $this->getOwnerPHID();
|
||||
|
||||
$handles = $viewer->loadHandles(array($viewer->getPHID()));
|
||||
$handles = iterator_to_array($handles);
|
||||
|
||||
$views = array();
|
||||
foreach ($inlines as $inline) {
|
||||
$views[] = id(new PHUIDiffInlineCommentDetailView())
|
||||
->setUser($viewer)
|
||||
->setInlineComment($inline)
|
||||
->setMarkupEngine($engine)
|
||||
->setHandles($handles)
|
||||
->setEditable(false)
|
||||
->setPreview(true)
|
||||
->setCanMarkDone(false)
|
||||
->setObjectOwnerPHID($owner_phid);
|
||||
}
|
||||
|
||||
return $views;
|
||||
}
|
||||
|
||||
}
|
|
@ -1,6 +1,6 @@
|
|||
<?php
|
||||
|
||||
final class PhabricatorFileLinkView extends AphrontView {
|
||||
final class PhabricatorFileLinkView extends AphrontTagView {
|
||||
|
||||
private $fileName;
|
||||
private $fileDownloadURI;
|
||||
|
@ -87,7 +87,7 @@ final class PhabricatorFileLinkView extends AphrontView {
|
|||
return FileTypeIcon::getFileIcon($this->getFileName());
|
||||
}
|
||||
|
||||
public function getMetadata() {
|
||||
public function getMeta() {
|
||||
return array(
|
||||
'phid' => $this->getFilePHID(),
|
||||
'viewable' => $this->getFileViewable(),
|
||||
|
@ -100,21 +100,55 @@ final class PhabricatorFileLinkView extends AphrontView {
|
|||
);
|
||||
}
|
||||
|
||||
public function render() {
|
||||
require_celerity_resource('phabricator-remarkup-css');
|
||||
require_celerity_resource('phui-lightbox-css');
|
||||
protected function getTagName() {
|
||||
return 'div';
|
||||
}
|
||||
|
||||
protected function getTagAttributes() {
|
||||
$mustcapture = true;
|
||||
$sigil = 'lightboxable';
|
||||
$meta = $this->getMetadata();
|
||||
$meta = $this->getMeta();
|
||||
|
||||
$class = 'phabricator-remarkup-embed-layout-link';
|
||||
if ($this->getCustomClass()) {
|
||||
$class = $this->getCustomClass();
|
||||
}
|
||||
|
||||
return array(
|
||||
'href' => $this->getFileViewURI(),
|
||||
'class' => $class,
|
||||
'sigil' => $sigil,
|
||||
'meta' => $meta,
|
||||
'mustcapture' => $mustcapture,
|
||||
);
|
||||
}
|
||||
|
||||
protected function getTagContent() {
|
||||
require_celerity_resource('phabricator-remarkup-css');
|
||||
require_celerity_resource('phui-lightbox-css');
|
||||
|
||||
$icon = id(new PHUIIconView())
|
||||
->setIcon($this->getFileIcon());
|
||||
->setIcon($this->getFileIcon())
|
||||
->addClass('phabricator-remarkup-embed-layout-icon');
|
||||
|
||||
$dl_icon = id(new PHUIIconView())
|
||||
->setIcon('fa-download');
|
||||
|
||||
$download_form = phabricator_form(
|
||||
$this->getViewer(),
|
||||
array(
|
||||
'action' => $this->getFileDownloadURI(),
|
||||
'method' => 'POST',
|
||||
'class' => 'embed-download-form',
|
||||
'sigil' => 'embed-download-form download',
|
||||
),
|
||||
phutil_tag(
|
||||
'button',
|
||||
array(
|
||||
'class' => 'phabricator-remarkup-embed-layout-download',
|
||||
'type' => 'submit',
|
||||
),
|
||||
pht('Download')));
|
||||
|
||||
$info = phutil_tag(
|
||||
'span',
|
||||
|
@ -140,18 +174,10 @@ final class PhabricatorFileLinkView extends AphrontView {
|
|||
$info,
|
||||
));
|
||||
|
||||
return javelin_tag(
|
||||
'a',
|
||||
array(
|
||||
'href' => $this->getFileViewURI(),
|
||||
'class' => $class,
|
||||
'sigil' => $sigil,
|
||||
'meta' => $meta,
|
||||
'mustcapture' => $mustcapture,
|
||||
),
|
||||
array(
|
||||
$icon,
|
||||
$inner,
|
||||
));
|
||||
return array(
|
||||
$icon,
|
||||
$inner,
|
||||
$download_form,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -108,12 +108,9 @@
|
|||
padding: 0 2px;
|
||||
}
|
||||
|
||||
.prose-diff span.old {
|
||||
color: {$redtext};
|
||||
}
|
||||
|
||||
.prose-diff span.old,
|
||||
.prose-diff span.new {
|
||||
color: {$greentext};
|
||||
color: {$darkgreytext};
|
||||
}
|
||||
|
||||
.differential-diff th.selected {
|
||||
|
@ -124,12 +121,24 @@
|
|||
cursor: auto;
|
||||
}
|
||||
|
||||
.differential-diff td.old {
|
||||
background: rgba(251, 175, 175, .3);
|
||||
.differential-diff th.old {
|
||||
background: #ffdddd;
|
||||
border-right-color: #f8cbcb;
|
||||
}
|
||||
|
||||
.differential-diff td.new {
|
||||
background: rgba(151, 234, 151, .3);
|
||||
.differential-diff th.new {
|
||||
background: #dbffdb;
|
||||
border-right-color: #a6f3a6;
|
||||
}
|
||||
|
||||
.differential-diff td.old,
|
||||
.differential-diff td.old-full {
|
||||
background: #ffecec;
|
||||
}
|
||||
|
||||
.differential-diff td.new,
|
||||
.differential-diff td.new-full {
|
||||
background: #eaffea;
|
||||
}
|
||||
|
||||
.differential-diff td.old-rebase {
|
||||
|
@ -140,16 +149,14 @@
|
|||
background: #eeffee;
|
||||
}
|
||||
|
||||
.differential-diff td.old-full,
|
||||
.differential-diff td.old span.bright,
|
||||
.prose-diff span.old {
|
||||
background: rgba(251, 175, 175, .7);
|
||||
background: #f8cbcb;
|
||||
}
|
||||
|
||||
.differential-diff td.new-full,
|
||||
.differential-diff td.new span.bright,
|
||||
.prose-diff span.new {
|
||||
background: rgba(151, 234, 151, .6);
|
||||
background: #a6f3a6;
|
||||
}
|
||||
|
||||
.differential-diff td.copy {
|
||||
|
|
|
@ -66,7 +66,7 @@
|
|||
border-radius: 3px;
|
||||
box-shadow: inset 0 -1px 0 rgba({$alphablue},0.08);
|
||||
user-select: none;
|
||||
background: #f7f7f7;
|
||||
background: {$lightgreybackground};
|
||||
border: 1px solid {$lightgreyborder};
|
||||
}
|
||||
|
||||
|
@ -370,7 +370,7 @@ video.phabricator-media {
|
|||
}
|
||||
|
||||
.phabricator-remarkup-embed-layout-link {
|
||||
padding: 8px 8px 8px 32px;
|
||||
padding: 6px 6px 6px 42px;
|
||||
border-radius: 3px;
|
||||
margin: 0 0 4px;
|
||||
display: inline-block;
|
||||
|
@ -379,18 +379,17 @@ video.phabricator-media {
|
|||
border: 1px solid {$lightblueborder};
|
||||
border-radius: 3px;
|
||||
color: #000;
|
||||
min-width: 240px;
|
||||
min-width: 256px;
|
||||
position: relative;
|
||||
height: 22px;
|
||||
/*height: 22px;*/
|
||||
line-height: 20px;
|
||||
}
|
||||
|
||||
.phabricator-remarkup-embed-layout-link .phui-icon-view {
|
||||
margin-right: 8px;
|
||||
font-size: 20px;
|
||||
.phabricator-remarkup-embed-layout-icon {
|
||||
font-size: 28px;
|
||||
position: absolute;
|
||||
top: 8px;
|
||||
left: 8px;
|
||||
top: 10px;
|
||||
left: 10px;
|
||||
}
|
||||
|
||||
.phabricator-remarkup-embed-layout-info {
|
||||
|
@ -402,11 +401,46 @@ video.phabricator-media {
|
|||
|
||||
.phabricator-remarkup-embed-layout-link:hover {
|
||||
border-color: {$violet};
|
||||
cursor: pointer;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
.phabricator-remarkup-embed-layout-link:hover .phui-icon-view {
|
||||
color: {$violet};
|
||||
.phabricator-remarkup-embed-layout-link:hover
|
||||
.phabricator-remarkup-embed-layout-icon {
|
||||
color: {$violet};
|
||||
}
|
||||
|
||||
.phabricator-remarkup-embed-layout-info-block {
|
||||
display: block;
|
||||
}
|
||||
|
||||
.embed-download-form {
|
||||
display: inline-block;
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.phabricator-remarkup-embed-layout-link
|
||||
.phabricator-remarkup-embed-layout-download {
|
||||
color: {$lightgreytext};
|
||||
border: none;
|
||||
background: rgba(0, 0, 0, 0);
|
||||
box-shadow: none;
|
||||
outline: 0;
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
text-align: left;
|
||||
text-shadow: none;
|
||||
border-radius: 0;
|
||||
font: inherit;
|
||||
display: inline;
|
||||
min-width: 0;
|
||||
font-weight: normal !important;
|
||||
}
|
||||
|
||||
.phabricator-remarkup-embed-layout-download:hover {
|
||||
color: {$anchor};
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
.phabricator-remarkup-embed-float-left {
|
||||
|
|
|
@ -163,3 +163,11 @@ body.device .phui-box.phui-object-box.phui-comment-form-view {
|
|||
.phui-comment-form-view .aphront-form-error .phui-icon-view {
|
||||
padding: 4px;
|
||||
}
|
||||
|
||||
.phui-comment-form-view .phui-form-view .phui-info-view {
|
||||
margin: 16px;
|
||||
}
|
||||
|
||||
.device-phone .phui-comment-form-view .phui-form-view .phui-info-view {
|
||||
margin: 8px;
|
||||
}
|
||||
|
|
|
@ -545,3 +545,8 @@ properly, and submit values. */
|
|||
.device-desktop .aphront-form-error .phui-icon-view:hover {
|
||||
color: {$red};
|
||||
}
|
||||
|
||||
.phui-form-static-action {
|
||||
padding: 4px;
|
||||
color: {$bluetext};
|
||||
}
|
||||
|
|
36
webroot/rsrc/js/application/diff/behavior-preview-link.js
Normal file
36
webroot/rsrc/js/application/diff/behavior-preview-link.js
Normal file
|
@ -0,0 +1,36 @@
|
|||
/**
|
||||
* @provides javelin-behavior-diff-preview-link
|
||||
* @requires javelin-behavior
|
||||
* javelin-stratcom
|
||||
* javelin-dom
|
||||
*/
|
||||
|
||||
JX.behavior('diff-preview-link', function(config, statics) {
|
||||
if (statics.initialized) {
|
||||
return;
|
||||
}
|
||||
statics.initialized = true;
|
||||
|
||||
var pht = JX.phtize(config.pht);
|
||||
|
||||
// After inline comment previews are rendered, hook up the links to the
|
||||
// comments that are visible on the current page.
|
||||
function link_inline_preview(e) {
|
||||
var root = e.getData().rootNode;
|
||||
var links = JX.DOM.scry(root, 'a', 'differential-inline-preview-jump');
|
||||
|
||||
for (var ii = 0; ii < links.length; ii++) {
|
||||
var data = JX.Stratcom.getData(links[ii]);
|
||||
try {
|
||||
JX.$(data.anchor);
|
||||
links[ii].href = '#' + data.anchor;
|
||||
JX.DOM.setContent(links[ii], pht('view'));
|
||||
} catch (ignored) {
|
||||
// This inline comment isn't visible, e.g. on some other diff.
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
JX.Stratcom.listen('EditEngine.didCommentPreview', null, link_inline_preview);
|
||||
});
|
|
@ -183,7 +183,7 @@ JX.install('DifferentialInlineCommentEditor', {
|
|||
|
||||
this._completed = true;
|
||||
|
||||
JX.Stratcom.invoke('differential-inline-comment-update');
|
||||
this._didUpdate();
|
||||
this.invoke('done');
|
||||
},
|
||||
|
||||
|
@ -335,7 +335,15 @@ JX.install('DifferentialInlineCommentEditor', {
|
|||
},
|
||||
|
||||
_didUpdate: function() {
|
||||
JX.Stratcom.invoke('differential-inline-comment-update');
|
||||
// After making changes to inline comments, refresh the transaction
|
||||
// preview at the bottom of the page.
|
||||
|
||||
// TODO: This isn't the cleanest way to find the preview form, but
|
||||
// rendering no longer has direct access to it.
|
||||
var forms = JX.DOM.scry(document.body, 'form', 'transaction-append');
|
||||
if (forms.length) {
|
||||
JX.DOM.invoke(forms[0], 'shouldRefresh');
|
||||
}
|
||||
}
|
||||
|
||||
},
|
||||
|
|
|
@ -43,59 +43,13 @@ JX.behavior('comment-actions', function(config) {
|
|||
return null;
|
||||
}
|
||||
|
||||
function add_row(option) {
|
||||
var action = action_map[option.value];
|
||||
if (!action) {
|
||||
return;
|
||||
function remove_action(key) {
|
||||
var row = rows[key];
|
||||
if (row) {
|
||||
JX.DOM.remove(row.node);
|
||||
row.option.disabled = false;
|
||||
delete rows[key];
|
||||
}
|
||||
|
||||
option.disabled = true;
|
||||
|
||||
var icon = new JX.PHUIXIconView()
|
||||
.setIcon('fa-times-circle');
|
||||
var remove = JX.$N('a', {href: '#'}, icon.getNode());
|
||||
|
||||
var control = new JX.PHUIXFormControl()
|
||||
.setLabel(action.label)
|
||||
.setError(remove)
|
||||
.setControl(action.type, action.spec)
|
||||
.setClass('phui-comment-action');
|
||||
var node = control.getNode();
|
||||
|
||||
JX.Stratcom.addSigil(node, 'touchable');
|
||||
|
||||
var remove_action = function() {
|
||||
JX.DOM.remove(node);
|
||||
delete rows[action.key];
|
||||
option.disabled = false;
|
||||
};
|
||||
|
||||
JX.DOM.listen(node, 'gesture.swipe.end', null, function(e) {
|
||||
var data = e.getData();
|
||||
|
||||
if (data.direction != 'left') {
|
||||
// Didn't swipe left.
|
||||
return;
|
||||
}
|
||||
|
||||
if (data.length <= (JX.Vector.getDim(node).x / 2)) {
|
||||
// Didn't swipe far enough.
|
||||
return;
|
||||
}
|
||||
|
||||
remove_action();
|
||||
});
|
||||
|
||||
rows[action.key] = control;
|
||||
|
||||
JX.DOM.listen(remove, 'click', null, function(e) {
|
||||
e.kill();
|
||||
remove_action();
|
||||
});
|
||||
|
||||
place_node.parentNode.insertBefore(node, place_node);
|
||||
|
||||
return control;
|
||||
}
|
||||
|
||||
function serialize_actions() {
|
||||
|
@ -104,7 +58,7 @@ JX.behavior('comment-actions', function(config) {
|
|||
for (var k in rows) {
|
||||
data.push({
|
||||
type: k,
|
||||
value: rows[k].getValue(),
|
||||
value: rows[k].control.getValue(),
|
||||
initialValue: action_map[k].initialValue || null
|
||||
});
|
||||
}
|
||||
|
@ -147,13 +101,115 @@ JX.behavior('comment-actions', function(config) {
|
|||
if (!response.xactions.length) {
|
||||
JX.DOM.hide(panel);
|
||||
} else {
|
||||
var preview_root = JX.$(config.timelineID);
|
||||
JX.DOM.setContent(
|
||||
JX.$(config.timelineID),
|
||||
JX.$H(response.xactions.join('')));
|
||||
preview_root,
|
||||
[
|
||||
JX.$H(response.xactions.join('')),
|
||||
JX.$H(response.previewContent)
|
||||
]);
|
||||
JX.DOM.show(panel);
|
||||
|
||||
// NOTE: Resonses are currently processed before associated behaviors are
|
||||
// registered. We need to defer invoking this event so that any behaviors
|
||||
// accompanying the response are registered.
|
||||
var invoke_preview = function() {
|
||||
JX.Stratcom.invoke(
|
||||
'EditEngine.didCommentPreview',
|
||||
null,
|
||||
{
|
||||
rootNode: preview_root
|
||||
});
|
||||
};
|
||||
setTimeout(invoke_preview, 0);
|
||||
}
|
||||
}
|
||||
|
||||
function force_preview() {
|
||||
if (!config.shouldPreview) {
|
||||
return;
|
||||
}
|
||||
|
||||
new JX.Request(config.actionURI, onresponse)
|
||||
.setData(get_data())
|
||||
.send();
|
||||
}
|
||||
|
||||
function add_row(option) {
|
||||
var action = action_map[option.value];
|
||||
if (!action) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Remove any conflicting actions. For example, "Accept Revision" conflicts
|
||||
// with "Reject Revision".
|
||||
var conflict_key = action.conflictKey || null;
|
||||
if (conflict_key !== null) {
|
||||
for (var k in action_map) {
|
||||
if (k === action.key) {
|
||||
continue;
|
||||
}
|
||||
if (action_map[k].conflictKey !== conflict_key) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!(k in rows)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
remove_action(k);
|
||||
}
|
||||
}
|
||||
|
||||
option.disabled = true;
|
||||
|
||||
var icon = new JX.PHUIXIconView()
|
||||
.setIcon('fa-times-circle');
|
||||
var remove = JX.$N('a', {href: '#'}, icon.getNode());
|
||||
|
||||
var control = new JX.PHUIXFormControl()
|
||||
.setLabel(action.label)
|
||||
.setError(remove)
|
||||
.setControl(action.type, action.spec)
|
||||
.setClass('phui-comment-action');
|
||||
var node = control.getNode();
|
||||
|
||||
JX.Stratcom.addSigil(node, 'touchable');
|
||||
|
||||
JX.DOM.listen(node, 'gesture.swipe.end', null, function(e) {
|
||||
var data = e.getData();
|
||||
|
||||
if (data.direction != 'left') {
|
||||
// Didn't swipe left.
|
||||
return;
|
||||
}
|
||||
|
||||
if (data.length <= (JX.Vector.getDim(node).x / 2)) {
|
||||
// Didn't swipe far enough.
|
||||
return;
|
||||
}
|
||||
|
||||
remove_action(action.key);
|
||||
});
|
||||
|
||||
rows[action.key] = {
|
||||
control: control,
|
||||
node: node,
|
||||
option: option
|
||||
};
|
||||
|
||||
JX.DOM.listen(remove, 'click', null, function(e) {
|
||||
e.kill();
|
||||
remove_action(action.key);
|
||||
});
|
||||
|
||||
place_node.parentNode.insertBefore(node, place_node);
|
||||
|
||||
force_preview();
|
||||
|
||||
return control;
|
||||
}
|
||||
|
||||
JX.DOM.listen(form_node, ['submit', 'didSyntheticSubmit'], null, function() {
|
||||
input_node.value = serialize_actions();
|
||||
});
|
||||
|
@ -168,13 +224,7 @@ JX.behavior('comment-actions', function(config) {
|
|||
|
||||
JX.DOM.listen(form_node, 'keydown', null, trigger);
|
||||
|
||||
var always_trigger = function() {
|
||||
new JX.Request(config.actionURI, onresponse)
|
||||
.setData(get_data())
|
||||
.send();
|
||||
};
|
||||
|
||||
JX.DOM.listen(form_node, 'shouldRefresh', null, always_trigger);
|
||||
JX.DOM.listen(form_node, 'shouldRefresh', null, force_preview);
|
||||
request.start();
|
||||
|
||||
var old_device = JX.Device.getDevice();
|
||||
|
@ -189,7 +239,7 @@ JX.behavior('comment-actions', function(config) {
|
|||
// Force an immediate refresh if we switched from another device type
|
||||
// to desktop.
|
||||
if (old_device != new_device) {
|
||||
always_trigger();
|
||||
force_preview();
|
||||
}
|
||||
} else {
|
||||
// On mobile, don't show live previews and only save drafts every
|
||||
|
|
|
@ -65,15 +65,48 @@ JX.install('Tooltip', {
|
|||
|
||||
_getSmartPosition: function (align, root, node) {
|
||||
var self = JX.Tooltip;
|
||||
var pos = self._proposePosition(align, root, node);
|
||||
|
||||
// If toolip is offscreen, try to be clever
|
||||
if (!JX.Tooltip.isOnScreen(pos, node)) {
|
||||
align = self._getImprovedOrientation(pos, node);
|
||||
pos = self._proposePosition(align, root, node);
|
||||
// Figure out how to position the tooltip on screen. We will try the
|
||||
// configured aligment first.
|
||||
var try_alignments = [align];
|
||||
|
||||
// If the configured alignment does not fit, we'll try the opposite
|
||||
// alignment.
|
||||
var opposites = {
|
||||
N: 'S',
|
||||
S: 'N',
|
||||
E: 'W',
|
||||
W: 'E'
|
||||
};
|
||||
try_alignments.push(opposites[align]);
|
||||
|
||||
// Then we'll try the other alignments, in arbitrary order.
|
||||
for (var k in opposites) {
|
||||
try_alignments.push(k);
|
||||
}
|
||||
|
||||
self._setAnchor(align);
|
||||
var use_alignment = null;
|
||||
var use_pos = null;
|
||||
for (var ii = 0; ii < try_alignments.length; ii++) {
|
||||
var try_alignment = try_alignments[ii];
|
||||
|
||||
var pos = self._proposePosition(try_alignment, root, node);
|
||||
if (self.isOnScreen(pos, node)) {
|
||||
use_alignment = try_alignment;
|
||||
use_pos = pos;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// If we don't come up with a good answer, default to the configured
|
||||
// alignment.
|
||||
if (use_alignment === null) {
|
||||
use_alignment = align;
|
||||
use_pos = self._proposePosition(use_alignment, root, node);
|
||||
}
|
||||
|
||||
self._setAnchor(use_alignment);
|
||||
|
||||
return pos;
|
||||
},
|
||||
|
||||
|
@ -108,56 +141,24 @@ JX.install('Tooltip', {
|
|||
},
|
||||
|
||||
isOnScreen: function (a, node) {
|
||||
var s = JX.Vector.getScroll();
|
||||
var v = JX.Vector.getViewport();
|
||||
var max_x = s.x + v.x;
|
||||
var max_y = s.y + v.y;
|
||||
|
||||
var view = this._getViewBoundaries();
|
||||
var corners = this._getNodeCornerPositions(a, node);
|
||||
|
||||
// Check if any of the corners are offscreen
|
||||
// Check if any of the corners are offscreen.
|
||||
for (var i = 0; i < corners.length; i++) {
|
||||
var corner = corners[i];
|
||||
if (corner.x < s.x ||
|
||||
corner.y < s.y ||
|
||||
corner.x > max_x ||
|
||||
corner.y > max_y) {
|
||||
if (corner.x < view.w ||
|
||||
corner.y < view.n ||
|
||||
corner.x > view.e ||
|
||||
corner.y > view.s) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
},
|
||||
|
||||
_getImprovedOrientation: function (a, node) {
|
||||
// Try to predict the "more correct" orientation
|
||||
var s = JX.Vector.getScroll();
|
||||
var v = JX.Vector.getViewport();
|
||||
var max_x = s.x + v.x;
|
||||
var max_y = s.y + v.y;
|
||||
|
||||
var corners = this._getNodeCornerPositions(a, node);
|
||||
|
||||
for (var i = 0; i < corners.length; i++) {
|
||||
var corner = corners[i];
|
||||
if (corner.y < v.y) {
|
||||
return 'S';
|
||||
} else
|
||||
if (corner.x < v.x) {
|
||||
return 'E';
|
||||
} else
|
||||
if (corner.y > max_y) {
|
||||
return 'N';
|
||||
} else
|
||||
if (corner.x > max_x) {
|
||||
return 'W';
|
||||
} else {
|
||||
return 'N';
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
_getNodeCornerPositions: function(pos, node) {
|
||||
// Get positions of all four corners of a node
|
||||
// Get positions of all four corners of a node.
|
||||
var n = JX.Vector.getDim(node);
|
||||
return [new JX.Vector(pos.x, pos.y),
|
||||
new JX.Vector(pos.x + n.x, pos.y),
|
||||
|
@ -165,6 +166,24 @@ JX.install('Tooltip', {
|
|||
new JX.Vector(pos.x + n.x, pos.y + n.y)];
|
||||
},
|
||||
|
||||
_getViewBoundaries: function() {
|
||||
var s = JX.Vector.getScroll();
|
||||
var v = JX.Vector.getViewport();
|
||||
var max_x = s.x + v.x;
|
||||
var max_y = s.y + v.y;
|
||||
|
||||
// Even if the corner is technically on the screen, don't allow the
|
||||
// tip to display too close to the edge of the screen.
|
||||
var margin = 16;
|
||||
|
||||
return {
|
||||
w: s.x + margin,
|
||||
e: max_x - margin,
|
||||
n: s.y + margin,
|
||||
s: max_y - margin
|
||||
};
|
||||
},
|
||||
|
||||
_setAnchor: function (align) {
|
||||
// Orient the little tail
|
||||
JX.DOM.alterClass(this._node, 'jx-tooltip-align-' + align, true);
|
||||
|
|
|
@ -51,7 +51,7 @@ JX.behavior('lightbox-attachments', function (config) {
|
|||
e.kill();
|
||||
|
||||
var mainFrame = JX.$('main-page-frame');
|
||||
var links = JX.DOM.scry(mainFrame, 'a', 'lightboxable');
|
||||
var links = JX.DOM.scry(mainFrame, '*', 'lightboxable');
|
||||
var phids = {};
|
||||
var data;
|
||||
for (var i = 0; i < links.length; i++) {
|
||||
|
@ -371,9 +371,20 @@ JX.behavior('lightbox-attachments', function (config) {
|
|||
form.submit();
|
||||
};
|
||||
|
||||
var _startPageDownload = function(e) {
|
||||
e.kill();
|
||||
var form = e.getNode('tag:form');
|
||||
form.submit();
|
||||
};
|
||||
|
||||
JX.Stratcom.listen(
|
||||
'click',
|
||||
'lightbox-download-submit',
|
||||
_startDownload);
|
||||
|
||||
JX.Stratcom.listen(
|
||||
'click',
|
||||
'embed-download-form',
|
||||
_startPageDownload);
|
||||
|
||||
});
|
||||
|
|
|
@ -47,6 +47,9 @@ JX.install('PHUIXFormControl', {
|
|||
case 'optgroups':
|
||||
input = this._newOptgroups(spec);
|
||||
break;
|
||||
case 'static':
|
||||
input = this._newStatic(spec);
|
||||
break;
|
||||
default:
|
||||
// TODO: Default or better error?
|
||||
JX.$E('Bad Input Type');
|
||||
|
@ -172,6 +175,25 @@ JX.install('PHUIXFormControl', {
|
|||
};
|
||||
},
|
||||
|
||||
_newStatic: function(spec) {
|
||||
var node = JX.$N(
|
||||
'div',
|
||||
{
|
||||
className: 'phui-form-static-action'
|
||||
},
|
||||
spec.description || '');
|
||||
|
||||
return {
|
||||
node: node,
|
||||
get: function() {
|
||||
return true;
|
||||
},
|
||||
set: function() {
|
||||
return;
|
||||
}
|
||||
};
|
||||
},
|
||||
|
||||
_newPoints: function(spec) {
|
||||
var attrs = {
|
||||
type: 'text',
|
||||
|
|
Loading…
Reference in a new issue