mirror of
https://we.phorge.it/source/phorge.git
synced 2024-12-03 20:22:46 +01:00
(stable) Promote 2017 Week 4
This commit is contained in:
commit
2604c5af55
44 changed files with 2514 additions and 980 deletions
|
@ -7,12 +7,12 @@
|
|||
*/
|
||||
return array(
|
||||
'names' => array(
|
||||
'conpherence.pkg.css' => '0b64e988',
|
||||
'conpherence.pkg.css' => 'e25569a9',
|
||||
'conpherence.pkg.js' => '6249a1cf',
|
||||
'core.pkg.css' => 'e75e4f9d',
|
||||
'core.pkg.css' => 'ea0e9c0c',
|
||||
'core.pkg.js' => '2291d3b2',
|
||||
'darkconsole.pkg.js' => 'e7393ebb',
|
||||
'differential.pkg.css' => '9535a7e6',
|
||||
'differential.pkg.css' => '4815647b',
|
||||
'differential.pkg.js' => 'ddfeb49b',
|
||||
'diffusion.pkg.css' => '91c5d3a6',
|
||||
'diffusion.pkg.js' => '84c8f8fd',
|
||||
|
@ -47,7 +47,7 @@ return array(
|
|||
'rsrc/css/application/config/setup-issue.css' => 'f794cfc3',
|
||||
'rsrc/css/application/config/unhandled-exception.css' => '4c96257a',
|
||||
'rsrc/css/application/conpherence/durable-column.css' => 'd82e130c',
|
||||
'rsrc/css/application/conpherence/header-pane.css' => '1c81cda6',
|
||||
'rsrc/css/application/conpherence/header-pane.css' => 'db93ebc6',
|
||||
'rsrc/css/application/conpherence/menu.css' => '4f51db5a',
|
||||
'rsrc/css/application/conpherence/message-pane.css' => 'b085d40d',
|
||||
'rsrc/css/application/conpherence/notification.css' => '965db05b',
|
||||
|
@ -59,9 +59,9 @@ 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' => 'e1621fd5',
|
||||
'rsrc/css/application/differential/changeset-view.css' => '6a9bdf9c',
|
||||
'rsrc/css/application/differential/core.css' => '5b7b8ff4',
|
||||
'rsrc/css/application/differential/phui-inline-comment.css' => '5953c28e',
|
||||
'rsrc/css/application/differential/phui-inline-comment.css' => 'be663c95',
|
||||
'rsrc/css/application/differential/revision-comment.css' => '14b8565a',
|
||||
'rsrc/css/application/differential/revision-history.css' => '0e8eb855',
|
||||
'rsrc/css/application/differential/revision-list.css' => 'f3c47d33',
|
||||
|
@ -83,7 +83,7 @@ return array(
|
|||
'rsrc/css/application/owners/owners-path-editor.css' => '2f00933b',
|
||||
'rsrc/css/application/paste/paste.css' => '1898e534',
|
||||
'rsrc/css/application/people/people-profile.css' => '2473d929',
|
||||
'rsrc/css/application/phame/phame.css' => 'aeb61182',
|
||||
'rsrc/css/application/phame/phame.css' => '53fa6236',
|
||||
'rsrc/css/application/pholio/pholio-edit.css' => '07676f51',
|
||||
'rsrc/css/application/pholio/pholio-inline-comments.css' => '8e545e49',
|
||||
'rsrc/css/application/pholio/pholio.css' => 'ca89d380',
|
||||
|
@ -96,8 +96,8 @@ return array(
|
|||
'rsrc/css/application/policy/policy-transaction-detail.css' => '82100a43',
|
||||
'rsrc/css/application/policy/policy.css' => '957ea14c',
|
||||
'rsrc/css/application/ponder/ponder-view.css' => 'fbd45f96',
|
||||
'rsrc/css/application/project/project-card-view.css' => 'd27c67ae',
|
||||
'rsrc/css/application/project/project-view.css' => '1e6f7072',
|
||||
'rsrc/css/application/project/project-card-view.css' => 'f25746f5',
|
||||
'rsrc/css/application/project/project-view.css' => '6936dc6e',
|
||||
'rsrc/css/application/releeph/releeph-core.css' => '9b3c5733',
|
||||
'rsrc/css/application/releeph/releeph-preview-branch.css' => 'b7a6f4a5',
|
||||
'rsrc/css/application/releeph/releeph-request-differential-create-dialog.css' => '8d8b92cd',
|
||||
|
@ -107,7 +107,7 @@ return array(
|
|||
'rsrc/css/application/slowvote/slowvote.css' => 'a94b7230',
|
||||
'rsrc/css/application/tokens/tokens.css' => '3d0f239e',
|
||||
'rsrc/css/application/uiexample/example.css' => '528b19de',
|
||||
'rsrc/css/core/core.css' => 'd0801452',
|
||||
'rsrc/css/core/core.css' => '50dd5fa6',
|
||||
'rsrc/css/core/remarkup.css' => '4a2de2bb',
|
||||
'rsrc/css/core/syntax.css' => '769d3498',
|
||||
'rsrc/css/core/z-index.css' => '5e72c4e0',
|
||||
|
@ -134,7 +134,7 @@ return array(
|
|||
'rsrc/css/phui/phui-basic-nav-view.css' => '7093573b',
|
||||
'rsrc/css/phui/phui-big-info-view.css' => 'bd903741',
|
||||
'rsrc/css/phui/phui-box.css' => '33b629f8',
|
||||
'rsrc/css/phui/phui-button.css' => '9718cb0c',
|
||||
'rsrc/css/phui/phui-button.css' => '00ddac15',
|
||||
'rsrc/css/phui/phui-chart.css' => '6bf6f78e',
|
||||
'rsrc/css/phui/phui-cms.css' => 'be43c8a8',
|
||||
'rsrc/css/phui/phui-comment-form.css' => '48fbd65d',
|
||||
|
@ -145,11 +145,11 @@ return array(
|
|||
'rsrc/css/phui/phui-document-summary.css' => '9ca48bdf',
|
||||
'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-fontkit.css' => '0b2da2d5',
|
||||
'rsrc/css/phui/phui-form-view.css' => 'adca31ce',
|
||||
'rsrc/css/phui/phui-form.css' => '2342b0e5',
|
||||
'rsrc/css/phui/phui-form.css' => '5815af7b',
|
||||
'rsrc/css/phui/phui-head-thing.css' => 'fd311e5f',
|
||||
'rsrc/css/phui/phui-header-view.css' => '6ec8f155',
|
||||
'rsrc/css/phui/phui-header-view.css' => '92935c02',
|
||||
'rsrc/css/phui/phui-hovercard.css' => 'e904f5dc',
|
||||
'rsrc/css/phui/phui-icon-set-selector.css' => '1ab67aad',
|
||||
'rsrc/css/phui/phui-icon.css' => '09f46dd9',
|
||||
|
@ -159,7 +159,7 @@ return array(
|
|||
'rsrc/css/phui/phui-invisible-character-view.css' => '6993d9f0',
|
||||
'rsrc/css/phui/phui-lightbox.css' => '0a035e40',
|
||||
'rsrc/css/phui/phui-list.css' => '9da2aa00',
|
||||
'rsrc/css/phui/phui-object-box.css' => '6b487c57',
|
||||
'rsrc/css/phui/phui-object-box.css' => '8b289e3d',
|
||||
'rsrc/css/phui/phui-pager.css' => 'bea33d23',
|
||||
'rsrc/css/phui/phui-pinboard-view.css' => '2495140e',
|
||||
'rsrc/css/phui/phui-profile-menu.css' => 'c71ecdcd',
|
||||
|
@ -170,7 +170,7 @@ return array(
|
|||
'rsrc/css/phui/phui-status.css' => 'd5263e49',
|
||||
'rsrc/css/phui/phui-tag-view.css' => '84d65f26',
|
||||
'rsrc/css/phui/phui-timeline-view.css' => 'bc523970',
|
||||
'rsrc/css/phui/phui-two-column-view.css' => 'a0d3858a',
|
||||
'rsrc/css/phui/phui-two-column-view.css' => 'f63cad3c',
|
||||
'rsrc/css/phui/workboards/phui-workboard-color.css' => 'b60ef38a',
|
||||
'rsrc/css/phui/workboards/phui-workboard.css' => 'c88912ee',
|
||||
'rsrc/css/phui/workboards/phui-workcard.css' => 'cca5fa92',
|
||||
|
@ -439,7 +439,7 @@ return array(
|
|||
'rsrc/js/application/phortune/phortune-credit-card-form.js' => '2290aeef',
|
||||
'rsrc/js/application/policy/behavior-policy-control.js' => 'd0c516d5',
|
||||
'rsrc/js/application/policy/behavior-policy-rule-editor.js' => '5e9f347c',
|
||||
'rsrc/js/application/projects/WorkboardBoard.js' => 'fe7cb52a',
|
||||
'rsrc/js/application/projects/WorkboardBoard.js' => '8935deef',
|
||||
'rsrc/js/application/projects/WorkboardCard.js' => 'c587b80f',
|
||||
'rsrc/js/application/projects/WorkboardColumn.js' => '21df4ff5',
|
||||
'rsrc/js/application/projects/WorkboardController.js' => '55baf5ed',
|
||||
|
@ -540,7 +540,7 @@ return array(
|
|||
'rsrc/js/phui/behavior-phui-tab-group.js' => '0a0b10e9',
|
||||
'rsrc/js/phuix/PHUIXActionListView.js' => 'b5c256b8',
|
||||
'rsrc/js/phuix/PHUIXActionView.js' => 'b3465b9b',
|
||||
'rsrc/js/phuix/PHUIXAutocomplete.js' => '6d86ce8b',
|
||||
'rsrc/js/phuix/PHUIXAutocomplete.js' => '7c492cd2',
|
||||
'rsrc/js/phuix/PHUIXDropdownMenu.js' => '8018ee50',
|
||||
'rsrc/js/phuix/PHUIXFormControl.js' => 'bbece68d',
|
||||
'rsrc/js/phuix/PHUIXIconView.js' => 'bff6884b',
|
||||
|
@ -565,7 +565,7 @@ return array(
|
|||
'config-options-css' => '0ede4c9b',
|
||||
'config-page-css' => 'c1d5121b',
|
||||
'conpherence-durable-column-view' => 'd82e130c',
|
||||
'conpherence-header-pane-css' => '1c81cda6',
|
||||
'conpherence-header-pane-css' => 'db93ebc6',
|
||||
'conpherence-menu-css' => '4f51db5a',
|
||||
'conpherence-message-pane-css' => 'b085d40d',
|
||||
'conpherence-notification-css' => '965db05b',
|
||||
|
@ -573,7 +573,7 @@ return array(
|
|||
'conpherence-thread-manager' => 'c8b5ee6f',
|
||||
'conpherence-transaction-css' => '85129c68',
|
||||
'd3' => 'a11a5ff2',
|
||||
'differential-changeset-view-css' => 'e1621fd5',
|
||||
'differential-changeset-view-css' => '6a9bdf9c',
|
||||
'differential-core-view-css' => '5b7b8ff4',
|
||||
'differential-inline-comment-editor' => '2e3f9738',
|
||||
'differential-revision-add-comment-css' => 'c47f8c40',
|
||||
|
@ -765,7 +765,7 @@ return array(
|
|||
'javelin-view-renderer' => '6c2b09a2',
|
||||
'javelin-view-visitor' => 'efe49472',
|
||||
'javelin-websocket' => 'e292eaf4',
|
||||
'javelin-workboard-board' => 'fe7cb52a',
|
||||
'javelin-workboard-board' => '8935deef',
|
||||
'javelin-workboard-card' => 'c587b80f',
|
||||
'javelin-workboard-column' => '21df4ff5',
|
||||
'javelin-workboard-controller' => '55baf5ed',
|
||||
|
@ -785,7 +785,7 @@ return array(
|
|||
'phabricator-busy' => '59a7976a',
|
||||
'phabricator-chatlog-css' => 'd295b020',
|
||||
'phabricator-content-source-view-css' => '4b8b05d4',
|
||||
'phabricator-core-css' => 'd0801452',
|
||||
'phabricator-core-css' => '50dd5fa6',
|
||||
'phabricator-countdown-css' => '16c52f5c',
|
||||
'phabricator-dashboard-css' => 'bc6f2127',
|
||||
'phabricator-drag-and-drop-file-upload' => '58dea2fa',
|
||||
|
@ -827,7 +827,7 @@ return array(
|
|||
'phabricator-uiexample-reactor-sendclass' => '1def2711',
|
||||
'phabricator-uiexample-reactor-sendproperties' => 'b1f0ccee',
|
||||
'phabricator-zindex-css' => '5e72c4e0',
|
||||
'phame-css' => 'aeb61182',
|
||||
'phame-css' => '53fa6236',
|
||||
'pholio-css' => 'ca89d380',
|
||||
'pholio-edit-css' => '07676f51',
|
||||
'pholio-inline-comments-css' => '8e545e49',
|
||||
|
@ -842,7 +842,7 @@ return array(
|
|||
'phui-basic-nav-view-css' => '7093573b',
|
||||
'phui-big-info-view-css' => 'bd903741',
|
||||
'phui-box-css' => '33b629f8',
|
||||
'phui-button-css' => '9718cb0c',
|
||||
'phui-button-css' => '00ddac15',
|
||||
'phui-calendar-css' => '477acfaa',
|
||||
'phui-calendar-day-css' => '572b1893',
|
||||
'phui-calendar-list-css' => 'fcc9fb41',
|
||||
|
@ -858,11 +858,11 @@ return array(
|
|||
'phui-document-view-pro-css' => 'f56738ed',
|
||||
'phui-feed-story-css' => '44a9c8e9',
|
||||
'phui-font-icon-base-css' => '870a7360',
|
||||
'phui-fontkit-css' => '9cda225e',
|
||||
'phui-form-css' => '2342b0e5',
|
||||
'phui-fontkit-css' => '0b2da2d5',
|
||||
'phui-form-css' => '5815af7b',
|
||||
'phui-form-view-css' => 'adca31ce',
|
||||
'phui-head-thing-view-css' => 'fd311e5f',
|
||||
'phui-header-view-css' => '6ec8f155',
|
||||
'phui-header-view-css' => '92935c02',
|
||||
'phui-hovercard' => '1bd28176',
|
||||
'phui-hovercard-view-css' => 'e904f5dc',
|
||||
'phui-icon-set-selector-css' => '1ab67aad',
|
||||
|
@ -870,11 +870,11 @@ return array(
|
|||
'phui-image-mask-css' => 'a8498f9c',
|
||||
'phui-info-panel-css' => '27ea50a1',
|
||||
'phui-info-view-css' => 'ec92802a',
|
||||
'phui-inline-comment-view-css' => '5953c28e',
|
||||
'phui-inline-comment-view-css' => 'be663c95',
|
||||
'phui-invisible-character-view-css' => '6993d9f0',
|
||||
'phui-lightbox-css' => '0a035e40',
|
||||
'phui-list-view-css' => '9da2aa00',
|
||||
'phui-object-box-css' => '6b487c57',
|
||||
'phui-object-box-css' => '8b289e3d',
|
||||
'phui-oi-big-ui-css' => '19f9369b',
|
||||
'phui-oi-color-css' => 'cd2b9b77',
|
||||
'phui-oi-drag-ui-css' => 'f12cbc9f',
|
||||
|
@ -892,14 +892,14 @@ return array(
|
|||
'phui-tag-view-css' => '84d65f26',
|
||||
'phui-theme-css' => '798c69b8',
|
||||
'phui-timeline-view-css' => 'bc523970',
|
||||
'phui-two-column-view-css' => 'a0d3858a',
|
||||
'phui-two-column-view-css' => 'f63cad3c',
|
||||
'phui-workboard-color-css' => 'b60ef38a',
|
||||
'phui-workboard-view-css' => 'c88912ee',
|
||||
'phui-workcard-view-css' => 'cca5fa92',
|
||||
'phui-workpanel-view-css' => 'a3a63478',
|
||||
'phuix-action-list-view' => 'b5c256b8',
|
||||
'phuix-action-view' => 'b3465b9b',
|
||||
'phuix-autocomplete' => '6d86ce8b',
|
||||
'phuix-autocomplete' => '7c492cd2',
|
||||
'phuix-dropdown-menu' => '8018ee50',
|
||||
'phuix-form-control-view' => 'bbece68d',
|
||||
'phuix-icon-view' => 'bff6884b',
|
||||
|
@ -907,8 +907,8 @@ return array(
|
|||
'policy-edit-css' => '815c66f7',
|
||||
'policy-transaction-detail-css' => '82100a43',
|
||||
'ponder-view-css' => 'fbd45f96',
|
||||
'project-card-view-css' => 'd27c67ae',
|
||||
'project-view-css' => '1e6f7072',
|
||||
'project-card-view-css' => 'f25746f5',
|
||||
'project-view-css' => '6936dc6e',
|
||||
'releeph-core' => '9b3c5733',
|
||||
'releeph-preview-branch' => 'b7a6f4a5',
|
||||
'releeph-request-differential-create-dialog' => '8d8b92cd',
|
||||
|
@ -1396,6 +1396,9 @@ return array(
|
|||
'69adf288' => array(
|
||||
'javelin-install',
|
||||
),
|
||||
'6a9bdf9c' => array(
|
||||
'phui-inline-comment-view-css',
|
||||
),
|
||||
'6ad39b6f' => array(
|
||||
'javelin-install',
|
||||
'javelin-event',
|
||||
|
@ -1420,12 +1423,6 @@ return array(
|
|||
'javelin-typeahead',
|
||||
'javelin-uri',
|
||||
),
|
||||
'6d86ce8b' => array(
|
||||
'javelin-install',
|
||||
'javelin-dom',
|
||||
'phuix-icon-view',
|
||||
'phabricator-prefab',
|
||||
),
|
||||
'70baed2f' => array(
|
||||
'javelin-install',
|
||||
'javelin-dom',
|
||||
|
@ -1493,6 +1490,12 @@ return array(
|
|||
'owners-path-editor',
|
||||
'javelin-behavior',
|
||||
),
|
||||
'7c492cd2' => array(
|
||||
'javelin-install',
|
||||
'javelin-dom',
|
||||
'phuix-icon-view',
|
||||
'phabricator-prefab',
|
||||
),
|
||||
'7cbe244b' => array(
|
||||
'javelin-install',
|
||||
'javelin-util',
|
||||
|
@ -1570,6 +1573,15 @@ return array(
|
|||
'javelin-stratcom',
|
||||
'javelin-dom',
|
||||
),
|
||||
'8935deef' => array(
|
||||
'javelin-install',
|
||||
'javelin-dom',
|
||||
'javelin-util',
|
||||
'javelin-stratcom',
|
||||
'javelin-workflow',
|
||||
'phabricator-draggable-list',
|
||||
'javelin-workboard-column',
|
||||
),
|
||||
'8a41885b' => array(
|
||||
'javelin-install',
|
||||
'javelin-dom',
|
||||
|
@ -2085,9 +2097,6 @@ return array(
|
|||
'javelin-request',
|
||||
'javelin-util',
|
||||
),
|
||||
'e1621fd5' => array(
|
||||
'phui-inline-comment-view-css',
|
||||
),
|
||||
'e1d25dfb' => array(
|
||||
'javelin-behavior',
|
||||
'javelin-stratcom',
|
||||
|
@ -2249,15 +2258,6 @@ return array(
|
|||
'javelin-view-visitor',
|
||||
'javelin-util',
|
||||
),
|
||||
'fe7cb52a' => array(
|
||||
'javelin-install',
|
||||
'javelin-dom',
|
||||
'javelin-util',
|
||||
'javelin-stratcom',
|
||||
'javelin-workflow',
|
||||
'phabricator-draggable-list',
|
||||
'javelin-workboard-column',
|
||||
),
|
||||
'fea0eb47' => array(
|
||||
'javelin-install',
|
||||
),
|
||||
|
|
1626
resources/emoji/manifest.json
Normal file
1626
resources/emoji/manifest.json
Normal file
File diff suppressed because it is too large
Load diff
50
scripts/celerity/generate_emoji.php
Executable file
50
scripts/celerity/generate_emoji.php
Executable file
|
@ -0,0 +1,50 @@
|
|||
#!/usr/bin/env php
|
||||
<?php
|
||||
|
||||
require_once dirname(dirname(__FILE__)).'/__init_script__.php';
|
||||
|
||||
$args = new PhutilArgumentParser($argv);
|
||||
$args->setTagline(pht('regenerate Emoji data sheets'));
|
||||
$args->setSynopsis(<<<EOHELP
|
||||
**emoji**
|
||||
Rebuild Emoji data sheets.
|
||||
|
||||
EOHELP
|
||||
);
|
||||
$args->parseStandardArguments();
|
||||
$args->parse(
|
||||
array(
|
||||
array(
|
||||
'name' => 'force',
|
||||
'help' => pht('Force regeneration even if sources have not changed.'),
|
||||
),
|
||||
));
|
||||
|
||||
$root = dirname(phutil_get_library_root('phabricator'));
|
||||
// move this to an argument?
|
||||
$path = $root.'/emoji_strategy.json';
|
||||
$export_path = $root.'/resources/emoji/manifest.json';
|
||||
|
||||
if (Filesystem::pathExists($path)) {
|
||||
$json = Filesystem::readFile($path);
|
||||
|
||||
$emojis = phutil_json_decode($json);
|
||||
$data = array();
|
||||
foreach ($emojis as $shortname => $emoji) {
|
||||
$unicode = $emoji['unicode'];
|
||||
$codes = explode('-', $unicode);
|
||||
$hex = '';
|
||||
foreach ($codes as $code) {
|
||||
$hex .= phutil_utf8_encode_codepoint(hexdec($code));
|
||||
}
|
||||
$data[$shortname] = $hex;
|
||||
}
|
||||
|
||||
ksort($data);
|
||||
$json = new PhutilJSON();
|
||||
$data = $json->encodeFormatted($data);
|
||||
Filesystem::writeFile($export_path, $data);
|
||||
echo pht('Done.')."\n";
|
||||
} else {
|
||||
echo pht('Path %s not exist.', $path)."\n";
|
||||
}
|
|
@ -671,12 +671,13 @@ phutil_register_library_map(array(
|
|||
'DiffusionCommitRevisionReviewersHeraldField' => 'applications/diffusion/herald/DiffusionCommitRevisionReviewersHeraldField.php',
|
||||
'DiffusionCommitRevisionSubscribersHeraldField' => 'applications/diffusion/herald/DiffusionCommitRevisionSubscribersHeraldField.php',
|
||||
'DiffusionCommitSearchConduitAPIMethod' => 'applications/diffusion/conduit/DiffusionCommitSearchConduitAPIMethod.php',
|
||||
'DiffusionCommitStateTransaction' => 'applications/diffusion/xaction/DiffusionCommitStateTransaction.php',
|
||||
'DiffusionCommitTagsController' => 'applications/diffusion/controller/DiffusionCommitTagsController.php',
|
||||
'DiffusionCommitTransactionType' => 'applications/diffusion/xaction/DiffusionCommitTransactionType.php',
|
||||
'DiffusionCommitVerifyTransaction' => 'applications/diffusion/xaction/DiffusionCommitVerifyTransaction.php',
|
||||
'DiffusionCompareController' => 'applications/diffusion/controller/DiffusionCompareController.php',
|
||||
'DiffusionConduitAPIMethod' => 'applications/diffusion/conduit/DiffusionConduitAPIMethod.php',
|
||||
'DiffusionController' => 'applications/diffusion/controller/DiffusionController.php',
|
||||
'DiffusionCreateCommentConduitAPIMethod' => 'applications/diffusion/conduit/DiffusionCreateCommentConduitAPIMethod.php',
|
||||
'DiffusionCreateRepositoriesCapability' => 'applications/diffusion/capability/DiffusionCreateRepositoriesCapability.php',
|
||||
'DiffusionDaemonLockException' => 'applications/diffusion/exception/DiffusionDaemonLockException.php',
|
||||
'DiffusionDefaultEditCapability' => 'applications/diffusion/capability/DiffusionDefaultEditCapability.php',
|
||||
|
@ -1412,6 +1413,7 @@ phutil_register_library_map(array(
|
|||
'LiskRawMigrationIterator' => 'infrastructure/storage/lisk/LiskRawMigrationIterator.php',
|
||||
'MacroConduitAPIMethod' => 'applications/macro/conduit/MacroConduitAPIMethod.php',
|
||||
'MacroCreateMemeConduitAPIMethod' => 'applications/macro/conduit/MacroCreateMemeConduitAPIMethod.php',
|
||||
'MacroEmojiExample' => 'applications/uiexample/examples/MacroEmojiExample.php',
|
||||
'MacroQueryConduitAPIMethod' => 'applications/macro/conduit/MacroQueryConduitAPIMethod.php',
|
||||
'ManiphestAssignEmailCommand' => 'applications/maniphest/command/ManiphestAssignEmailCommand.php',
|
||||
'ManiphestAssigneeDatasource' => 'applications/maniphest/typeahead/ManiphestAssigneeDatasource.php',
|
||||
|
@ -1830,7 +1832,6 @@ phutil_register_library_map(array(
|
|||
'PhabricatorApplicationSearchEngine' => 'applications/search/engine/PhabricatorApplicationSearchEngine.php',
|
||||
'PhabricatorApplicationSearchEngineTestCase' => 'applications/search/engine/__tests__/PhabricatorApplicationSearchEngineTestCase.php',
|
||||
'PhabricatorApplicationSearchResultView' => 'applications/search/view/PhabricatorApplicationSearchResultView.php',
|
||||
'PhabricatorApplicationStatusView' => 'applications/meta/view/PhabricatorApplicationStatusView.php',
|
||||
'PhabricatorApplicationTestCase' => 'applications/base/__tests__/PhabricatorApplicationTestCase.php',
|
||||
'PhabricatorApplicationTransaction' => 'applications/transactions/storage/PhabricatorApplicationTransaction.php',
|
||||
'PhabricatorApplicationTransactionComment' => 'applications/transactions/storage/PhabricatorApplicationTransactionComment.php',
|
||||
|
@ -2627,6 +2628,7 @@ phutil_register_library_map(array(
|
|||
'PhabricatorEmailVarySubjectsSetting' => 'applications/settings/setting/PhabricatorEmailVarySubjectsSetting.php',
|
||||
'PhabricatorEmailVerificationController' => 'applications/auth/controller/PhabricatorEmailVerificationController.php',
|
||||
'PhabricatorEmbedFileRemarkupRule' => 'applications/files/markup/PhabricatorEmbedFileRemarkupRule.php',
|
||||
'PhabricatorEmojiDatasource' => 'applications/macro/typeahead/PhabricatorEmojiDatasource.php',
|
||||
'PhabricatorEmojiRemarkupRule' => 'applications/macro/markup/PhabricatorEmojiRemarkupRule.php',
|
||||
'PhabricatorEmojiTranslation' => 'infrastructure/internationalization/translation/PhabricatorEmojiTranslation.php',
|
||||
'PhabricatorEmptyQueryException' => 'infrastructure/query/PhabricatorEmptyQueryException.php',
|
||||
|
@ -5377,12 +5379,13 @@ phutil_register_library_map(array(
|
|||
'DiffusionCommitRevisionReviewersHeraldField' => 'DiffusionCommitHeraldField',
|
||||
'DiffusionCommitRevisionSubscribersHeraldField' => 'DiffusionCommitHeraldField',
|
||||
'DiffusionCommitSearchConduitAPIMethod' => 'PhabricatorSearchEngineAPIMethod',
|
||||
'DiffusionCommitStateTransaction' => 'DiffusionCommitTransactionType',
|
||||
'DiffusionCommitTagsController' => 'DiffusionController',
|
||||
'DiffusionCommitTransactionType' => 'PhabricatorModularTransactionType',
|
||||
'DiffusionCommitVerifyTransaction' => 'DiffusionCommitAuditTransaction',
|
||||
'DiffusionCompareController' => 'DiffusionController',
|
||||
'DiffusionConduitAPIMethod' => 'ConduitAPIMethod',
|
||||
'DiffusionController' => 'PhabricatorController',
|
||||
'DiffusionCreateCommentConduitAPIMethod' => 'DiffusionConduitAPIMethod',
|
||||
'DiffusionCreateRepositoriesCapability' => 'PhabricatorPolicyCapability',
|
||||
'DiffusionDaemonLockException' => 'Exception',
|
||||
'DiffusionDefaultEditCapability' => 'PhabricatorPolicyCapability',
|
||||
|
@ -6243,6 +6246,7 @@ phutil_register_library_map(array(
|
|||
'LiskRawMigrationIterator' => 'PhutilBufferedIterator',
|
||||
'MacroConduitAPIMethod' => 'ConduitAPIMethod',
|
||||
'MacroCreateMemeConduitAPIMethod' => 'MacroConduitAPIMethod',
|
||||
'MacroEmojiExample' => 'PhabricatorUIExample',
|
||||
'MacroQueryConduitAPIMethod' => 'MacroConduitAPIMethod',
|
||||
'ManiphestAssignEmailCommand' => 'ManiphestEmailCommand',
|
||||
'ManiphestAssigneeDatasource' => 'PhabricatorTypeaheadCompositeDatasource',
|
||||
|
@ -6712,7 +6716,6 @@ phutil_register_library_map(array(
|
|||
'PhabricatorApplicationSearchEngine' => 'Phobject',
|
||||
'PhabricatorApplicationSearchEngineTestCase' => 'PhabricatorTestCase',
|
||||
'PhabricatorApplicationSearchResultView' => 'Phobject',
|
||||
'PhabricatorApplicationStatusView' => 'AphrontView',
|
||||
'PhabricatorApplicationTestCase' => 'PhabricatorTestCase',
|
||||
'PhabricatorApplicationTransaction' => array(
|
||||
'PhabricatorLiskDAO',
|
||||
|
@ -7641,6 +7644,7 @@ phutil_register_library_map(array(
|
|||
'PhabricatorEmailVarySubjectsSetting' => 'PhabricatorSelectSetting',
|
||||
'PhabricatorEmailVerificationController' => 'PhabricatorAuthController',
|
||||
'PhabricatorEmbedFileRemarkupRule' => 'PhabricatorObjectRemarkupRule',
|
||||
'PhabricatorEmojiDatasource' => 'PhabricatorTypeaheadDatasource',
|
||||
'PhabricatorEmojiRemarkupRule' => 'PhutilRemarkupRule',
|
||||
'PhabricatorEmojiTranslation' => 'PhutilTranslation',
|
||||
'PhabricatorEmptyQueryException' => 'Exception',
|
||||
|
|
|
@ -7,12 +7,14 @@ final class PhabricatorAuditCommitStatusConstants extends Phobject {
|
|||
const CONCERN_RAISED = 2;
|
||||
const PARTIALLY_AUDITED = 3;
|
||||
const FULLY_AUDITED = 4;
|
||||
const NEEDS_VERIFICATION = 5;
|
||||
|
||||
public static function getStatusNameMap() {
|
||||
$map = array(
|
||||
self::NONE => pht('No Audits'),
|
||||
self::NEEDS_AUDIT => pht('Audit Required'),
|
||||
self::CONCERN_RAISED => pht('Concern Raised'),
|
||||
self::NEEDS_VERIFICATION => pht('Needs Verification'),
|
||||
self::PARTIALLY_AUDITED => pht('Partially Audited'),
|
||||
self::FULLY_AUDITED => pht('Audited'),
|
||||
);
|
||||
|
@ -28,6 +30,7 @@ final class PhabricatorAuditCommitStatusConstants extends Phobject {
|
|||
return array(
|
||||
self::CONCERN_RAISED,
|
||||
self::NEEDS_AUDIT,
|
||||
self::NEEDS_VERIFICATION,
|
||||
self::PARTIALLY_AUDITED,
|
||||
);
|
||||
}
|
||||
|
@ -49,6 +52,9 @@ final class PhabricatorAuditCommitStatusConstants extends Phobject {
|
|||
case self::NONE:
|
||||
$color = 'bluegrey';
|
||||
break;
|
||||
case self::NEEDS_VERIFICATION:
|
||||
$color = 'indigo';
|
||||
break;
|
||||
default:
|
||||
$color = null;
|
||||
break;
|
||||
|
@ -56,7 +62,7 @@ final class PhabricatorAuditCommitStatusConstants extends Phobject {
|
|||
return $color;
|
||||
}
|
||||
|
||||
public static function getStatusIcon($code) {
|
||||
public static function getStatusIcon($code) {
|
||||
switch ($code) {
|
||||
case self::CONCERN_RAISED:
|
||||
$icon = 'fa-times-circle';
|
||||
|
@ -73,6 +79,9 @@ final class PhabricatorAuditCommitStatusConstants extends Phobject {
|
|||
case self::NONE:
|
||||
$icon = 'fa-check';
|
||||
break;
|
||||
case self::NEEDS_VERIFICATION:
|
||||
$icon = 'fa-refresh';
|
||||
break;
|
||||
default:
|
||||
$icon = null;
|
||||
break;
|
||||
|
|
|
@ -2,42 +2,6 @@
|
|||
|
||||
final class PhabricatorAuditCommentEditor extends PhabricatorEditor {
|
||||
|
||||
/**
|
||||
* Load the PHIDs for all objects the user has the authority to act as an
|
||||
* audit for. This includes themselves, and any packages they are an owner
|
||||
* of.
|
||||
*/
|
||||
public static function loadAuditPHIDsForUser(PhabricatorUser $user) {
|
||||
$phids = array();
|
||||
|
||||
// TODO: This method doesn't really use the right viewer, but in practice we
|
||||
// never issue this query of this type on behalf of another user and are
|
||||
// unlikely to do so in the future. This entire method should be refactored
|
||||
// into a Query class, however, and then we should use a proper viewer.
|
||||
|
||||
// The user can audit on their own behalf.
|
||||
$phids[$user->getPHID()] = true;
|
||||
|
||||
$owned_packages = id(new PhabricatorOwnersPackageQuery())
|
||||
->setViewer($user)
|
||||
->withAuthorityPHIDs(array($user->getPHID()))
|
||||
->execute();
|
||||
foreach ($owned_packages as $package) {
|
||||
$phids[$package->getPHID()] = true;
|
||||
}
|
||||
|
||||
// The user can audit on behalf of all projects they are a member of.
|
||||
$projects = id(new PhabricatorProjectQuery())
|
||||
->setViewer($user)
|
||||
->withMemberPHIDs(array($user->getPHID()))
|
||||
->execute();
|
||||
foreach ($projects as $project) {
|
||||
$phids[$project->getPHID()] = true;
|
||||
}
|
||||
|
||||
return array_keys($phids);
|
||||
}
|
||||
|
||||
public static function getMailThreading(
|
||||
PhabricatorRepository $repository,
|
||||
PhabricatorRepositoryCommit $commit) {
|
||||
|
|
|
@ -11,6 +11,7 @@ final class PhabricatorAuditEditor
|
|||
private $auditorPHIDs = array();
|
||||
|
||||
private $didExpandInlineState = false;
|
||||
private $oldAuditStatus = null;
|
||||
|
||||
public function addAuditReason($phid, $reason) {
|
||||
if (!isset($this->auditReasonMap[$phid])) {
|
||||
|
@ -60,7 +61,6 @@ final class PhabricatorAuditEditor
|
|||
|
||||
// TODO: These will get modernized eventually, but that can happen one
|
||||
// at a time later on.
|
||||
$types[] = PhabricatorAuditActionConstants::ACTION;
|
||||
$types[] = PhabricatorAuditActionConstants::INLINE;
|
||||
$types[] = PhabricatorAuditActionConstants::ADD_AUDITORS;
|
||||
|
||||
|
@ -79,6 +79,12 @@ final class PhabricatorAuditEditor
|
|||
}
|
||||
}
|
||||
|
||||
$this->oldAuditStatus = $object->getAuditStatus();
|
||||
|
||||
$object->loadAndAttachAuditAuthority(
|
||||
$this->getActor(),
|
||||
$this->getActingAsPHID());
|
||||
|
||||
return parent::expandTransactions($object, $xactions);
|
||||
}
|
||||
|
||||
|
@ -98,7 +104,6 @@ final class PhabricatorAuditEditor
|
|||
PhabricatorLiskDAO $object,
|
||||
PhabricatorApplicationTransaction $xaction) {
|
||||
switch ($xaction->getTransactionType()) {
|
||||
case PhabricatorAuditActionConstants::ACTION:
|
||||
case PhabricatorAuditActionConstants::INLINE:
|
||||
case PhabricatorAuditTransaction::TYPE_COMMIT:
|
||||
return null;
|
||||
|
@ -116,7 +121,6 @@ final class PhabricatorAuditEditor
|
|||
PhabricatorApplicationTransaction $xaction) {
|
||||
|
||||
switch ($xaction->getTransactionType()) {
|
||||
case PhabricatorAuditActionConstants::ACTION:
|
||||
case PhabricatorAuditActionConstants::INLINE:
|
||||
case PhabricatorAuditActionConstants::ADD_AUDITORS:
|
||||
case PhabricatorAuditTransaction::TYPE_COMMIT:
|
||||
|
@ -131,7 +135,6 @@ final class PhabricatorAuditEditor
|
|||
PhabricatorApplicationTransaction $xaction) {
|
||||
|
||||
switch ($xaction->getTransactionType()) {
|
||||
case PhabricatorAuditActionConstants::ACTION:
|
||||
case PhabricatorAuditActionConstants::INLINE:
|
||||
case PhabricatorAuditActionConstants::ADD_AUDITORS:
|
||||
case PhabricatorAuditTransaction::TYPE_COMMIT:
|
||||
|
@ -146,7 +149,6 @@ final class PhabricatorAuditEditor
|
|||
PhabricatorApplicationTransaction $xaction) {
|
||||
|
||||
switch ($xaction->getTransactionType()) {
|
||||
case PhabricatorAuditActionConstants::ACTION:
|
||||
case PhabricatorAuditTransaction::TYPE_COMMIT:
|
||||
return;
|
||||
case PhabricatorAuditActionConstants::INLINE:
|
||||
|
@ -267,108 +269,44 @@ final class PhabricatorAuditEditor
|
|||
case PhabricatorAuditTransaction::TYPE_COMMIT:
|
||||
$import_status_flag = PhabricatorRepositoryCommit::IMPORTED_HERALD;
|
||||
break;
|
||||
case PhabricatorAuditActionConstants::ACTION:
|
||||
$new = $xaction->getNewValue();
|
||||
switch ($new) {
|
||||
case PhabricatorAuditActionConstants::CLOSE:
|
||||
// "Close" means wipe out all the concerns.
|
||||
$requests = $object->getAudits();
|
||||
foreach ($requests as $request) {
|
||||
if ($request->getAuditStatus() == $status_concerned) {
|
||||
$request
|
||||
->setAuditStatus($status_closed)
|
||||
->save();
|
||||
}
|
||||
}
|
||||
break;
|
||||
case PhabricatorAuditActionConstants::RESIGN:
|
||||
$requests = $object->getAudits();
|
||||
$requests = mpull($requests, null, 'getAuditorPHID');
|
||||
$actor_request = idx($requests, $actor_phid);
|
||||
|
||||
// If the actor doesn't currently have a relationship to the
|
||||
// commit, add one explicitly. For example, this allows members
|
||||
// of a project to resign from a commit and have it drop out of
|
||||
// their queue.
|
||||
|
||||
if (!$actor_request) {
|
||||
$actor_request = id(new PhabricatorRepositoryAuditRequest())
|
||||
->setCommitPHID($object->getPHID())
|
||||
->setAuditorPHID($actor_phid);
|
||||
|
||||
$requests[] = $actor_request;
|
||||
$object->attachAudits($requests);
|
||||
}
|
||||
|
||||
$actor_request
|
||||
->setAuditStatus($status_resigned)
|
||||
->save();
|
||||
break;
|
||||
case PhabricatorAuditActionConstants::ACCEPT:
|
||||
case PhabricatorAuditActionConstants::CONCERN:
|
||||
if ($new == PhabricatorAuditActionConstants::ACCEPT) {
|
||||
$new_status = $status_accepted;
|
||||
} else {
|
||||
$new_status = $status_concerned;
|
||||
}
|
||||
|
||||
$requests = $object->getAudits();
|
||||
$requests = mpull($requests, null, 'getAuditorPHID');
|
||||
|
||||
// Figure out which requests the actor has authority over: these
|
||||
// are user requests where they are the auditor, and packages
|
||||
// and projects they are a member of.
|
||||
|
||||
if ($actor_is_author) {
|
||||
// When modifying your own commits, you act only on behalf of
|
||||
// yourself, not your packages/projects -- the idea being that
|
||||
// you can't accept your own commits.
|
||||
$authority_phids = array($actor_phid);
|
||||
} else {
|
||||
$authority_phids =
|
||||
PhabricatorAuditCommentEditor::loadAuditPHIDsForUser(
|
||||
$this->requireActor());
|
||||
}
|
||||
|
||||
$authority = array_select_keys(
|
||||
$requests,
|
||||
$authority_phids);
|
||||
|
||||
if (!$authority) {
|
||||
// If the actor has no authority over any existing requests,
|
||||
// create a new request for them.
|
||||
|
||||
$actor_request = id(new PhabricatorRepositoryAuditRequest())
|
||||
->setCommitPHID($object->getPHID())
|
||||
->setAuditorPHID($actor_phid)
|
||||
->setAuditStatus($new_status)
|
||||
->save();
|
||||
|
||||
$requests[$actor_phid] = $actor_request;
|
||||
$object->attachAudits($requests);
|
||||
} else {
|
||||
// Otherwise, update the audit status of the existing requests.
|
||||
foreach ($authority as $request) {
|
||||
$request
|
||||
->setAuditStatus($new_status)
|
||||
->save();
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
$old_status = $this->oldAuditStatus;
|
||||
|
||||
$requests = $object->getAudits();
|
||||
$object->updateAuditStatus($requests);
|
||||
|
||||
$new_status = $object->getAuditStatus();
|
||||
|
||||
$object->save();
|
||||
|
||||
if ($import_status_flag) {
|
||||
$object->writeImportStatusFlag($import_status_flag);
|
||||
}
|
||||
|
||||
$partial_status = PhabricatorAuditCommitStatusConstants::PARTIALLY_AUDITED;
|
||||
|
||||
// If the commit has changed state after this edit, add an informational
|
||||
// transaction about the state change.
|
||||
if ($old_status != $new_status) {
|
||||
if ($new_status == $partial_status) {
|
||||
// This state isn't interesting enough to get a transaction. The
|
||||
// best way we could lead the user forward is something like "This
|
||||
// commit still requires additional audits." but that's redundant and
|
||||
// probably not very useful.
|
||||
} else {
|
||||
$xaction = $object->getApplicationTransactionTemplate()
|
||||
->setTransactionType(DiffusionCommitStateTransaction::TRANSACTIONTYPE)
|
||||
->setOldValue($old_status)
|
||||
->setNewValue($new_status);
|
||||
|
||||
$xaction = $this->populateTransaction($object, $xaction);
|
||||
|
||||
$xaction->save();
|
||||
}
|
||||
}
|
||||
|
||||
// Collect auditor PHIDs for building mail.
|
||||
$this->auditorPHIDs = mpull($object->getAudits(), 'getAuditorPHID');
|
||||
|
||||
|
@ -396,7 +334,6 @@ final class PhabricatorAuditEditor
|
|||
if (!$this->didExpandInlineState) {
|
||||
switch ($xaction->getTransactionType()) {
|
||||
case PhabricatorTransactions::TYPE_COMMENT:
|
||||
case PhabricatorAuditActionConstants::ACTION:
|
||||
$this->didExpandInlineState = true;
|
||||
|
||||
$actor_phid = $this->getActingAsPHID();
|
||||
|
@ -487,69 +424,6 @@ final class PhabricatorAuditEditor
|
|||
return array_values(array_merge($head, $tail));
|
||||
}
|
||||
|
||||
protected function validateTransaction(
|
||||
PhabricatorLiskDAO $object,
|
||||
$type,
|
||||
array $xactions) {
|
||||
|
||||
$errors = parent::validateTransaction($object, $type, $xactions);
|
||||
|
||||
foreach ($xactions as $xaction) {
|
||||
switch ($type) {
|
||||
case PhabricatorAuditActionConstants::ACTION:
|
||||
$error = $this->validateAuditAction(
|
||||
$object,
|
||||
$type,
|
||||
$xaction,
|
||||
$xaction->getNewValue());
|
||||
if ($error) {
|
||||
$errors[] = new PhabricatorApplicationTransactionValidationError(
|
||||
$type,
|
||||
pht('Invalid'),
|
||||
$error,
|
||||
$xaction);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return $errors;
|
||||
}
|
||||
|
||||
private function validateAuditAction(
|
||||
PhabricatorLiskDAO $object,
|
||||
$type,
|
||||
PhabricatorAuditTransaction $xaction,
|
||||
$action) {
|
||||
|
||||
$can_author_close_key = 'audit.can-author-close-audit';
|
||||
$can_author_close = PhabricatorEnv::getEnvConfig($can_author_close_key);
|
||||
|
||||
$actor_is_author = ($object->getAuthorPHID()) &&
|
||||
($object->getAuthorPHID() == $this->getActingAsPHID());
|
||||
|
||||
switch ($action) {
|
||||
case PhabricatorAuditActionConstants::CLOSE:
|
||||
if (!$actor_is_author) {
|
||||
return pht(
|
||||
'You can not close this audit because you are not the author '.
|
||||
'of the commit.');
|
||||
}
|
||||
|
||||
if (!$can_author_close) {
|
||||
return pht(
|
||||
'You can not close this audit because "%s" is disabled in '.
|
||||
'the Phabricator configuration.',
|
||||
$can_author_close_key);
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
protected function supportsSearch() {
|
||||
return true;
|
||||
}
|
||||
|
|
|
@ -498,4 +498,22 @@ final class PhabricatorAuditTransaction
|
|||
}
|
||||
return $tags;
|
||||
}
|
||||
|
||||
public function shouldDisplayGroupWith(array $group) {
|
||||
// Make the "This commit now requires audit." state message stand alone.
|
||||
$type_state = DiffusionCommitStateTransaction::TRANSACTIONTYPE;
|
||||
|
||||
if ($this->getTransactionType() == $type_state) {
|
||||
return false;
|
||||
}
|
||||
|
||||
foreach ($group as $xaction) {
|
||||
if ($xaction->getTransactionType() == $type_state) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return parent::shouldDisplayGroupWith($group);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -12,8 +12,6 @@ abstract class PhabricatorApplication
|
|||
extends Phobject
|
||||
implements PhabricatorPolicyInterface {
|
||||
|
||||
const MAX_STATUS_ITEMS = 100;
|
||||
|
||||
const GROUP_CORE = 'core';
|
||||
const GROUP_UTILITIES = 'util';
|
||||
const GROUP_ADMIN = 'admin';
|
||||
|
@ -272,20 +270,6 @@ abstract class PhabricatorApplication
|
|||
/* -( UI Integration )----------------------------------------------------- */
|
||||
|
||||
|
||||
/**
|
||||
* Render status elements (like "3 Waiting Reviews") for application list
|
||||
* views. These provide a way to alert users to new or pending action items
|
||||
* in applications.
|
||||
*
|
||||
* @param PhabricatorUser Viewing user.
|
||||
* @return list<PhabricatorApplicationStatusView> Application status elements.
|
||||
* @task ui
|
||||
*/
|
||||
public function loadStatus(PhabricatorUser $user) {
|
||||
return array();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* You can provide an optional piece of flavor text for the application. This
|
||||
* is currently rendered in application launch views if the application has no
|
||||
|
|
|
@ -20,13 +20,13 @@ final class CelerityDefaultPostprocessor
|
|||
public function buildVariables() {
|
||||
return array(
|
||||
// Fonts
|
||||
'basefont' => "13px 'Segoe UI', 'Segoe UI Web Regular', ".
|
||||
"'Segoe UI Symbol', 'Lato', 'Helvetica Neue', Helvetica, ".
|
||||
"Arial, sans-serif",
|
||||
'basefont' => "13px 'Segoe UI', 'Segoe UI Emoji', ".
|
||||
"'Segoe UI Symbol', 'Lato', 'Helvetica Neue', ".
|
||||
"Helvetica, Arial, sans-serif",
|
||||
|
||||
'fontfamily' => "'Segoe UI', 'Segoe UI Web Regular', ".
|
||||
"'Segoe UI Symbol', 'Lato', 'Helvetica Neue', Helvetica, ".
|
||||
"Arial, sans-serif",
|
||||
'fontfamily' => "'Segoe UI', 'Segoe UI Emoji', ".
|
||||
"'Segoe UI Symbol', 'Lato', 'Helvetica Neue', ".
|
||||
"Helvetica, Arial, sans-serif",
|
||||
|
||||
// Drop Shadow
|
||||
'dropshadow' => '0 2px 12px rgba(0, 0, 0, .20)',
|
||||
|
|
|
@ -103,82 +103,6 @@ final class PhabricatorDifferentialApplication extends PhabricatorApplication {
|
|||
);
|
||||
}
|
||||
|
||||
public static function loadNeedAttentionRevisions(PhabricatorUser $viewer) {
|
||||
if (!$viewer->isLoggedIn()) {
|
||||
return array();
|
||||
}
|
||||
|
||||
$viewer_phid = $viewer->getPHID();
|
||||
|
||||
$responsible_phids = id(new DifferentialResponsibleDatasource())
|
||||
->setViewer($viewer)
|
||||
->evaluateTokens(array($viewer_phid));
|
||||
|
||||
$revision_query = id(new DifferentialRevisionQuery())
|
||||
->setViewer($viewer)
|
||||
->withStatus(DifferentialRevisionQuery::STATUS_OPEN)
|
||||
->withResponsibleUsers($responsible_phids)
|
||||
->needReviewerStatus(true)
|
||||
->needRelationships(true)
|
||||
->needFlags(true)
|
||||
->needDrafts(true)
|
||||
->setLimit(self::MAX_STATUS_ITEMS);
|
||||
|
||||
$revisions = $revision_query->execute();
|
||||
|
||||
$query = id(new PhabricatorSavedQuery())
|
||||
->attachParameterMap(
|
||||
array(
|
||||
'responsiblePHIDs' => $responsible_phids,
|
||||
));
|
||||
|
||||
$groups = id(new DifferentialRevisionRequiredActionResultBucket())
|
||||
->setViewer($viewer)
|
||||
->newResultGroups($query, $revisions);
|
||||
|
||||
$include = array();
|
||||
foreach ($groups as $group) {
|
||||
switch ($group->getKey()) {
|
||||
case DifferentialRevisionRequiredActionResultBucket::KEY_MUSTREVIEW:
|
||||
case DifferentialRevisionRequiredActionResultBucket::KEY_SHOULDREVIEW:
|
||||
foreach ($group->getObjects() as $object) {
|
||||
$include[] = $object;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return $include;
|
||||
}
|
||||
|
||||
public function loadStatus(PhabricatorUser $user) {
|
||||
$revisions = self::loadNeedAttentionRevisions($user);
|
||||
$limit = self::MAX_STATUS_ITEMS;
|
||||
|
||||
if (count($revisions) >= $limit) {
|
||||
$display_count = ($limit - 1);
|
||||
$display_label = pht(
|
||||
'%s+ Active Review(s)',
|
||||
new PhutilNumber($display_count));
|
||||
} else {
|
||||
$display_count = count($revisions);
|
||||
$display_label = pht(
|
||||
'%s Review(s) Need Attention',
|
||||
new PhutilNumber($display_count));
|
||||
}
|
||||
|
||||
$status = array();
|
||||
|
||||
$status[] = id(new PhabricatorApplicationStatusView())
|
||||
->setType(PhabricatorApplicationStatusView::TYPE_WARNING)
|
||||
->setText($display_label)
|
||||
->setCount($display_count);
|
||||
|
||||
return $status;
|
||||
}
|
||||
|
||||
public function supportsEmailIntegration() {
|
||||
return true;
|
||||
}
|
||||
|
|
|
@ -64,52 +64,11 @@ abstract class DifferentialCoreCustomField
|
|||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
if (is_string($value)) {
|
||||
$parser = $this->getFieldParser();
|
||||
$result = $parser->parseCorpus($value);
|
||||
|
||||
unset($result['__title__']);
|
||||
unset($result['__summary__']);
|
||||
|
||||
if ($result) {
|
||||
$error = new PhabricatorApplicationTransactionValidationError(
|
||||
$type,
|
||||
pht('Invalid'),
|
||||
pht(
|
||||
'The value you have entered in "%s" can not be parsed '.
|
||||
'unambiguously when rendered in a commit message. Edit the '.
|
||||
'message so that keywords like "Summary:" and "Test Plan:" do '.
|
||||
'not appear at the beginning of lines. Parsed keys: %s.',
|
||||
$this->getFieldName(),
|
||||
implode(', ', array_keys($result))),
|
||||
$xaction);
|
||||
$errors[] = $error;
|
||||
$this->setFieldError(pht('Invalid'));
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $errors;
|
||||
}
|
||||
|
||||
private function getFieldParser() {
|
||||
if (!$this->fieldParser) {
|
||||
$viewer = $this->getViewer();
|
||||
$parser = DifferentialCommitMessageParser::newStandardParser($viewer);
|
||||
|
||||
// Set custom title and summary keys so we can detect the presence of
|
||||
// "Summary:" in, e.g., a test plan.
|
||||
$parser->setTitleKey('__title__');
|
||||
$parser->setSummaryKey('__summary__');
|
||||
|
||||
$this->fieldParser = $parser;
|
||||
}
|
||||
|
||||
return $this->fieldParser;
|
||||
}
|
||||
|
||||
public function canDisableField() {
|
||||
return false;
|
||||
}
|
||||
|
|
|
@ -50,4 +50,11 @@ final class DifferentialTestPlanCommitMessageField
|
|||
);
|
||||
}
|
||||
|
||||
public function validateTransactions($object, array $xactions) {
|
||||
return $this->validateCommitMessageCorpusTransactions(
|
||||
$object,
|
||||
$xactions,
|
||||
pht('Test Plan'));
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -54,4 +54,11 @@ final class DifferentialRevisionSummaryTransaction
|
|||
return $changes;
|
||||
}
|
||||
|
||||
public function validateTransactions($object, array $xactions) {
|
||||
return $this->validateCommitMessageCorpusTransactions(
|
||||
$object,
|
||||
$xactions,
|
||||
pht('Summary'));
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -1,4 +1,60 @@
|
|||
<?php
|
||||
|
||||
abstract class DifferentialRevisionTransactionType
|
||||
extends PhabricatorModularTransactionType {}
|
||||
extends PhabricatorModularTransactionType {
|
||||
|
||||
protected function validateCommitMessageCorpusTransactions(
|
||||
$object,
|
||||
array $xactions,
|
||||
$field_name) {
|
||||
|
||||
$errors = array();
|
||||
foreach ($xactions as $xaction) {
|
||||
$error = $this->validateMessageCorpus($xaction, $field_name);
|
||||
if ($error) {
|
||||
$errors[] = $error;
|
||||
}
|
||||
}
|
||||
|
||||
return $errors;
|
||||
}
|
||||
|
||||
private function validateMessageCorpus($xaction, $field_name) {
|
||||
$value = $xaction->getNewValue();
|
||||
if (!strlen($value)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// Put a placeholder title on the message, because the first line of a
|
||||
// message is now always parsed as a title.
|
||||
$value = "<placeholder>\n".$value;
|
||||
|
||||
$viewer = $this->getActor();
|
||||
$parser = DifferentialCommitMessageParser::newStandardParser($viewer);
|
||||
|
||||
// Set custom title and summary keys so we can detect the presence of
|
||||
// "Summary:" in, e.g., a test plan.
|
||||
$parser->setTitleKey('__title__');
|
||||
$parser->setSummaryKey('__summary__');
|
||||
|
||||
$result = $parser->parseCorpus($value);
|
||||
|
||||
unset($result['__title__']);
|
||||
unset($result['__summary__']);
|
||||
|
||||
if (!$result) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return $this->newInvalidError(
|
||||
pht(
|
||||
'The value you have entered in "%s" can not be parsed '.
|
||||
'unambiguously when rendered in a commit message. Edit the '.
|
||||
'message so that keywords like "Summary:" and "Test Plan:" do '.
|
||||
'not appear at the beginning of lines. Parsed keys: %s.',
|
||||
$field_name,
|
||||
implode(', ', array_keys($result))),
|
||||
$xaction);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -1,106 +0,0 @@
|
|||
<?php
|
||||
|
||||
final class DiffusionCreateCommentConduitAPIMethod
|
||||
extends DiffusionConduitAPIMethod {
|
||||
|
||||
public function getAPIMethodName() {
|
||||
return 'diffusion.createcomment';
|
||||
}
|
||||
|
||||
public function getMethodStatus() {
|
||||
return self::METHOD_STATUS_DEPRECATED;
|
||||
}
|
||||
|
||||
public function getMethodDescription() {
|
||||
return pht(
|
||||
'Add a comment to a Diffusion commit. By specifying an action '.
|
||||
'of "%s", "%s", "%s", or "%s", auditing actions can '.
|
||||
'be triggered. Defaults to "%s".',
|
||||
'concern',
|
||||
'accept',
|
||||
'resign',
|
||||
'close',
|
||||
'comment');
|
||||
}
|
||||
|
||||
protected function defineParamTypes() {
|
||||
return array(
|
||||
'phid' => 'required string',
|
||||
'action' => 'optional string',
|
||||
'message' => 'required string',
|
||||
'silent' => 'optional bool',
|
||||
);
|
||||
}
|
||||
|
||||
protected function defineReturnType() {
|
||||
return 'bool';
|
||||
}
|
||||
|
||||
protected function defineErrorTypes() {
|
||||
return array(
|
||||
'ERR_BAD_COMMIT' => pht('No commit found with that PHID.'),
|
||||
'ERR_BAD_ACTION' => pht('Invalid action type.'),
|
||||
'ERR_MISSING_MESSAGE' => pht('Message is required.'),
|
||||
);
|
||||
}
|
||||
|
||||
protected function execute(ConduitAPIRequest $request) {
|
||||
$commit_phid = $request->getValue('phid');
|
||||
$commit = id(new DiffusionCommitQuery())
|
||||
->setViewer($request->getUser())
|
||||
->withPHIDs(array($commit_phid))
|
||||
->needAuditRequests(true)
|
||||
->executeOne();
|
||||
if (!$commit) {
|
||||
throw new ConduitException('ERR_BAD_COMMIT');
|
||||
}
|
||||
|
||||
$message = trim($request->getValue('message'));
|
||||
if (!$message) {
|
||||
throw new ConduitException('ERR_MISSING_MESSAGE');
|
||||
}
|
||||
|
||||
$action = $request->getValue('action');
|
||||
if (!$action) {
|
||||
$action = PhabricatorAuditActionConstants::COMMENT;
|
||||
}
|
||||
|
||||
// Disallow ADD_CCS, ADD_AUDITORS forever.
|
||||
if (!in_array($action, array(
|
||||
PhabricatorAuditActionConstants::CONCERN,
|
||||
PhabricatorAuditActionConstants::ACCEPT,
|
||||
PhabricatorAuditActionConstants::COMMENT,
|
||||
PhabricatorAuditActionConstants::RESIGN,
|
||||
PhabricatorAuditActionConstants::CLOSE,
|
||||
))) {
|
||||
throw new ConduitException('ERR_BAD_ACTION');
|
||||
}
|
||||
|
||||
$xactions = array();
|
||||
|
||||
if ($action != PhabricatorAuditActionConstants::COMMENT) {
|
||||
$xactions[] = id(new PhabricatorAuditTransaction())
|
||||
->setTransactionType(PhabricatorAuditActionConstants::ACTION)
|
||||
->setNewValue($action);
|
||||
}
|
||||
|
||||
if (strlen($message)) {
|
||||
$xactions[] = id(new PhabricatorAuditTransaction())
|
||||
->setTransactionType(PhabricatorTransactions::TYPE_COMMENT)
|
||||
->attachComment(
|
||||
id(new PhabricatorAuditTransactionComment())
|
||||
->setCommitPHID($commit->getPHID())
|
||||
->setContent($message));
|
||||
}
|
||||
|
||||
id(new PhabricatorAuditEditor())
|
||||
->setActor($request->getUser())
|
||||
->setContentSource($request->newContentSource())
|
||||
->setDisableEmail($request->getValue('silent'))
|
||||
->setContinueOnMissingFields(true)
|
||||
->applyTransactions($commit, $xactions);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
|
@ -4,9 +4,6 @@ final class DiffusionCommitController extends DiffusionController {
|
|||
|
||||
const CHANGES_LIMIT = 100;
|
||||
|
||||
private $auditAuthorityPHIDs;
|
||||
private $highlightedAudits;
|
||||
|
||||
private $commitParents;
|
||||
private $commitRefs;
|
||||
private $commitMerges;
|
||||
|
@ -67,8 +64,7 @@ final class DiffusionCommitController extends DiffusionController {
|
|||
}
|
||||
|
||||
$audit_requests = $commit->getAudits();
|
||||
$this->auditAuthorityPHIDs =
|
||||
PhabricatorAuditCommentEditor::loadAuditPHIDsForUser($viewer);
|
||||
$commit->loadAndAttachAuditAuthority($viewer);
|
||||
|
||||
$commit_data = $commit->getCommitData();
|
||||
$is_foreign = $commit_data->getCommitDetail('foreign-svn-stub');
|
||||
|
@ -209,10 +205,6 @@ final class DiffusionCommitController extends DiffusionController {
|
|||
$timeline = $this->buildComments($commit);
|
||||
$merge_table = $this->buildMergesTable($commit);
|
||||
|
||||
$highlighted_audits = $commit->getAuthorityAudits(
|
||||
$viewer,
|
||||
$this->auditAuthorityPHIDs);
|
||||
|
||||
$show_changesets = false;
|
||||
$info_panel = null;
|
||||
$change_list = null;
|
||||
|
@ -520,13 +512,13 @@ final class DiffusionCommitController extends DiffusionController {
|
|||
if ($user_requests) {
|
||||
$view->addProperty(
|
||||
pht('Auditors'),
|
||||
$this->renderAuditStatusView($user_requests));
|
||||
$this->renderAuditStatusView($commit, $user_requests));
|
||||
}
|
||||
|
||||
if ($other_requests) {
|
||||
$view->addProperty(
|
||||
pht('Group Auditors'),
|
||||
$this->renderAuditStatusView($other_requests));
|
||||
$this->renderAuditStatusView($commit, $other_requests));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -739,85 +731,6 @@ final class DiffusionCommitController extends DiffusionController {
|
|||
return $comment_view;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a map of available audit actions for rendering into a <select />.
|
||||
* This shows the user valid actions, and does not show nonsense/invalid
|
||||
* actions (like closing an already-closed commit, or resigning from a commit
|
||||
* you have no association with).
|
||||
*/
|
||||
private function getAuditActions(
|
||||
PhabricatorRepositoryCommit $commit,
|
||||
array $audit_requests) {
|
||||
assert_instances_of($audit_requests, 'PhabricatorRepositoryAuditRequest');
|
||||
$viewer = $this->getViewer();
|
||||
|
||||
$user_is_author = ($commit->getAuthorPHID() == $viewer->getPHID());
|
||||
|
||||
$user_request = null;
|
||||
foreach ($audit_requests as $audit_request) {
|
||||
if ($audit_request->getAuditorPHID() == $viewer->getPHID()) {
|
||||
$user_request = $audit_request;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
$actions = array();
|
||||
$actions[PhabricatorAuditActionConstants::COMMENT] = true;
|
||||
|
||||
// We allow you to accept your own commits. A use case here is that you
|
||||
// notice an issue with your own commit and "Raise Concern" as an indicator
|
||||
// to other auditors that you're on top of the issue, then later resolve it
|
||||
// and "Accept". You can not accept on behalf of projects or packages,
|
||||
// however.
|
||||
$actions[PhabricatorAuditActionConstants::ACCEPT] = true;
|
||||
$actions[PhabricatorAuditActionConstants::CONCERN] = true;
|
||||
|
||||
// To resign, a user must have authority on some request and not be the
|
||||
// commit's author.
|
||||
if (!$user_is_author) {
|
||||
$may_resign = false;
|
||||
|
||||
$authority_map = array_fill_keys($this->auditAuthorityPHIDs, true);
|
||||
foreach ($audit_requests as $request) {
|
||||
if (empty($authority_map[$request->getAuditorPHID()])) {
|
||||
continue;
|
||||
}
|
||||
$may_resign = true;
|
||||
break;
|
||||
}
|
||||
|
||||
// If the user has already resigned, don't show "Resign...".
|
||||
$status_resigned = PhabricatorAuditStatusConstants::RESIGNED;
|
||||
if ($user_request) {
|
||||
if ($user_request->getAuditStatus() == $status_resigned) {
|
||||
$may_resign = false;
|
||||
}
|
||||
}
|
||||
|
||||
if ($may_resign) {
|
||||
$actions[PhabricatorAuditActionConstants::RESIGN] = true;
|
||||
}
|
||||
}
|
||||
|
||||
$status_concern = PhabricatorAuditCommitStatusConstants::CONCERN_RAISED;
|
||||
$concern_raised = ($commit->getAuditStatus() == $status_concern);
|
||||
$can_close_option = PhabricatorEnv::getEnvConfig(
|
||||
'audit.can-author-close-audit');
|
||||
if ($can_close_option && $user_is_author && $concern_raised) {
|
||||
$actions[PhabricatorAuditActionConstants::CLOSE] = true;
|
||||
}
|
||||
|
||||
$actions[PhabricatorAuditActionConstants::ADD_AUDITORS] = true;
|
||||
$actions[PhabricatorAuditActionConstants::ADD_CCS] = true;
|
||||
|
||||
foreach ($actions as $constant => $ignored) {
|
||||
$actions[$constant] =
|
||||
PhabricatorAuditActionConstants::getActionName($constant);
|
||||
}
|
||||
|
||||
return $actions;
|
||||
}
|
||||
|
||||
private function buildMergesTable(PhabricatorRepositoryCommit $commit) {
|
||||
$viewer = $this->getViewer();
|
||||
$drequest = $this->getDiffusionRequest();
|
||||
|
@ -927,12 +840,12 @@ final class DiffusionCommitController extends DiffusionController {
|
|||
return $file->getRedirectResponse();
|
||||
}
|
||||
|
||||
private function renderAuditStatusView(array $audit_requests) {
|
||||
private function renderAuditStatusView(
|
||||
PhabricatorRepositoryCommit $commit,
|
||||
array $audit_requests) {
|
||||
assert_instances_of($audit_requests, 'PhabricatorRepositoryAuditRequest');
|
||||
$viewer = $this->getViewer();
|
||||
|
||||
$authority_map = array_fill_keys($this->auditAuthorityPHIDs, true);
|
||||
|
||||
$view = new PHUIStatusListView();
|
||||
foreach ($audit_requests as $request) {
|
||||
$code = $request->getAuditStatus();
|
||||
|
@ -952,7 +865,7 @@ final class DiffusionCommitController extends DiffusionController {
|
|||
$target = $viewer->renderHandle($auditor_phid);
|
||||
$item->setTarget($target);
|
||||
|
||||
if (isset($authority_map[$auditor_phid])) {
|
||||
if ($commit->hasAuditAuthority($viewer, $request)) {
|
||||
$item->setHighlighted(true);
|
||||
}
|
||||
|
||||
|
|
|
@ -33,6 +33,11 @@ final class DiffusionCommitRequiredActionResultBucket
|
|||
->setNoDataString(pht('None of your commits have active concerns.'))
|
||||
->setObjects($this->filterConcernRaised($phids));
|
||||
|
||||
$groups[] = $this->newGroup()
|
||||
->setName(pht('Needs Verification'))
|
||||
->setNoDataString(pht('No commits are awaiting your verification.'))
|
||||
->setObjects($this->filterNeedsVerification($phids));
|
||||
|
||||
$groups[] = $this->newGroup()
|
||||
->setName(pht('Ready to Audit'))
|
||||
->setNoDataString(pht('No commits are waiting for you to audit them.'))
|
||||
|
@ -82,6 +87,36 @@ final class DiffusionCommitRequiredActionResultBucket
|
|||
return $results;
|
||||
}
|
||||
|
||||
private function filterNeedsVerification(array $phids) {
|
||||
$results = array();
|
||||
$objects = $this->objects;
|
||||
|
||||
$status_verify = PhabricatorAuditCommitStatusConstants::NEEDS_VERIFICATION;
|
||||
$has_concern = array(
|
||||
PhabricatorAuditStatusConstants::CONCERNED,
|
||||
);
|
||||
$has_concern = array_fuse($has_concern);
|
||||
|
||||
foreach ($objects as $key => $object) {
|
||||
if (isset($phids[$object->getAuthorPHID()])) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if ($object->getAuditStatus() != $status_verify) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!$this->hasAuditorsWithStatus($object, $phids, $has_concern)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$results[$key] = $object;
|
||||
unset($this->objects[$key]);
|
||||
}
|
||||
|
||||
return $results;
|
||||
}
|
||||
|
||||
private function filterShouldAudit(array $phids) {
|
||||
$results = array();
|
||||
$objects = $this->objects;
|
||||
|
@ -132,6 +167,7 @@ final class DiffusionCommitRequiredActionResultBucket
|
|||
|
||||
$status_waiting = array(
|
||||
PhabricatorAuditCommitStatusConstants::NEEDS_AUDIT,
|
||||
PhabricatorAuditCommitStatusConstants::NEEDS_VERIFICATION,
|
||||
PhabricatorAuditCommitStatusConstants::PARTIALLY_AUDITED,
|
||||
);
|
||||
$status_waiting = array_fuse($status_waiting);
|
||||
|
|
|
@ -30,11 +30,6 @@ final class DiffusionCommitAcceptTransaction
|
|||
return pht('Accepted');
|
||||
}
|
||||
|
||||
public function generateOldValue($object) {
|
||||
$actor = $this->getActor();
|
||||
return $this->isViewerAcceptingAuditor($object, $actor);
|
||||
}
|
||||
|
||||
public function applyExternalEffects($object, $value) {
|
||||
$status = PhabricatorAuditStatusConstants::ACCEPTED;
|
||||
$actor = $this->getActor();
|
||||
|
@ -54,7 +49,7 @@ final class DiffusionCommitAcceptTransaction
|
|||
}
|
||||
}
|
||||
|
||||
if ($this->isViewerAcceptingAuditor($object, $viewer)) {
|
||||
if ($this->isViewerFullyAccepted($object, $viewer)) {
|
||||
throw new Exception(
|
||||
pht(
|
||||
'You can not accept this commit because you have already '.
|
||||
|
|
|
@ -7,6 +7,10 @@ abstract class DiffusionCommitAuditTransaction
|
|||
return DiffusionCommitEditEngine::ACTIONGROUP_AUDIT;
|
||||
}
|
||||
|
||||
public function generateOldValue($object) {
|
||||
return false;
|
||||
}
|
||||
|
||||
protected function isViewerAnyAuditor(
|
||||
PhabricatorRepositoryCommit $commit,
|
||||
PhabricatorUser $viewer) {
|
||||
|
@ -18,22 +22,23 @@ abstract class DiffusionCommitAuditTransaction
|
|||
PhabricatorUser $viewer) {
|
||||
|
||||
// This omits various inactive states like "Resigned" and "Not Required".
|
||||
$active = array(
|
||||
PhabricatorAuditStatusConstants::AUDIT_REQUIRED,
|
||||
PhabricatorAuditStatusConstants::CONCERNED,
|
||||
PhabricatorAuditStatusConstants::ACCEPTED,
|
||||
PhabricatorAuditStatusConstants::AUDIT_REQUESTED,
|
||||
);
|
||||
$active = array_fuse($active);
|
||||
|
||||
return $this->isViewerAuditStatusAmong(
|
||||
$commit,
|
||||
$viewer,
|
||||
array(
|
||||
PhabricatorAuditStatusConstants::AUDIT_REQUIRED,
|
||||
PhabricatorAuditStatusConstants::CONCERNED,
|
||||
PhabricatorAuditStatusConstants::ACCEPTED,
|
||||
PhabricatorAuditStatusConstants::AUDIT_REQUESTED,
|
||||
));
|
||||
$viewer_status = $this->getViewerAuditStatus($commit, $viewer);
|
||||
|
||||
return isset($active[$viewer_status]);
|
||||
}
|
||||
|
||||
protected function isViewerAcceptingAuditor(
|
||||
protected function isViewerFullyAccepted(
|
||||
PhabricatorRepositoryCommit $commit,
|
||||
PhabricatorUser $viewer) {
|
||||
return $this->isViewerAuditStatusAmong(
|
||||
return $this->isViewerAuditStatusFullyAmong(
|
||||
$commit,
|
||||
$viewer,
|
||||
array(
|
||||
|
@ -41,10 +46,10 @@ abstract class DiffusionCommitAuditTransaction
|
|||
));
|
||||
}
|
||||
|
||||
protected function isViewerRejectingAuditor(
|
||||
protected function isViewerFullyRejected(
|
||||
PhabricatorRepositoryCommit $commit,
|
||||
PhabricatorUser $viewer) {
|
||||
return $this->isViewerAuditStatusAmong(
|
||||
return $this->isViewerAuditStatusFullyAmong(
|
||||
$commit,
|
||||
$viewer,
|
||||
array(
|
||||
|
@ -71,7 +76,7 @@ abstract class DiffusionCommitAuditTransaction
|
|||
return null;
|
||||
}
|
||||
|
||||
protected function isViewerAuditStatusAmong(
|
||||
protected function isViewerAuditStatusFullyAmong(
|
||||
PhabricatorRepositoryCommit $commit,
|
||||
PhabricatorUser $viewer,
|
||||
array $status_list) {
|
||||
|
@ -82,7 +87,20 @@ abstract class DiffusionCommitAuditTransaction
|
|||
}
|
||||
|
||||
$status_map = array_fuse($status_list);
|
||||
return isset($status_map[$status]);
|
||||
foreach ($commit->getAudits() as $audit) {
|
||||
if (!$commit->hasAuditAuthority($viewer, $audit)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$status = $audit->getAuditStatus();
|
||||
if (isset($status_map[$status])) {
|
||||
continue;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
protected function applyAuditorEffect(
|
||||
|
@ -91,6 +109,9 @@ abstract class DiffusionCommitAuditTransaction
|
|||
$value,
|
||||
$status) {
|
||||
|
||||
$actor = $this->getActor();
|
||||
$acting_phid = $this->getActingAsPHID();
|
||||
|
||||
$audits = $commit->getAudits();
|
||||
$audits = mpull($audits, null, 'getAuditorPHID');
|
||||
|
||||
|
@ -98,13 +119,9 @@ abstract class DiffusionCommitAuditTransaction
|
|||
|
||||
$with_authority = ($status != PhabricatorAuditStatusConstants::RESIGNED);
|
||||
if ($with_authority) {
|
||||
$has_authority = PhabricatorAuditCommentEditor::loadAuditPHIDsForUser(
|
||||
$viewer);
|
||||
$has_authority = array_fuse($has_authority);
|
||||
foreach ($audits as $audit) {
|
||||
$auditor_phid = $audit->getAuditorPHID();
|
||||
if (isset($has_authority[$auditor_phid])) {
|
||||
$map[$auditor_phid] = $status;
|
||||
if ($commit->hasAuditAuthority($actor, $audit, $acting_phid)) {
|
||||
$map[$audit->getAuditorPHID()] = $status;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -30,9 +30,11 @@ final class DiffusionCommitConcernTransaction
|
|||
return pht('Raised Concern');
|
||||
}
|
||||
|
||||
public function generateOldValue($object) {
|
||||
$actor = $this->getActor();
|
||||
return $this->isViewerRejectingAuditor($object, $actor);
|
||||
public function applyInternalEffects($object, $value) {
|
||||
// NOTE: We force the commit directly into "Concern Raised" so that we
|
||||
// override a possible "Needs Verification" state.
|
||||
$object->setAuditStatus(
|
||||
PhabricatorAuditCommitStatusConstants::CONCERN_RAISED);
|
||||
}
|
||||
|
||||
public function applyExternalEffects($object, $value) {
|
||||
|
@ -50,11 +52,17 @@ final class DiffusionCommitConcernTransaction
|
|||
'you did not author.'));
|
||||
}
|
||||
|
||||
if ($this->isViewerRejectingAuditor($object, $viewer)) {
|
||||
throw new Exception(
|
||||
pht(
|
||||
'You can not raise a concern with this commit because you have '.
|
||||
'already raised a concern with it.'));
|
||||
// Even if you've already raised a concern, you can raise again as long
|
||||
// as the author requsted you verify.
|
||||
$state_verify = PhabricatorAuditCommitStatusConstants::NEEDS_VERIFICATION;
|
||||
|
||||
if ($this->isViewerFullyRejected($object, $viewer)) {
|
||||
if ($object->getAuditStatus() != $state_verify) {
|
||||
throw new Exception(
|
||||
pht(
|
||||
'You can not raise a concern with this commit because you have '.
|
||||
'already raised a concern with it.'));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,72 @@
|
|||
<?php
|
||||
|
||||
final class DiffusionCommitStateTransaction
|
||||
extends DiffusionCommitTransactionType {
|
||||
|
||||
const TRANSACTIONTYPE = 'diffusion.commit.state';
|
||||
|
||||
public function generateNewValue($object, $value) {
|
||||
// NOTE: This transaction can not be generated or applied normally. It is
|
||||
// written to the transaction log as a side effect of a state change.
|
||||
throw new PhutilMethodNotImplementedException();
|
||||
}
|
||||
|
||||
public function getIcon() {
|
||||
$new = $this->getNewValue();
|
||||
return PhabricatorAuditCommitStatusConstants::getStatusIcon($new);
|
||||
}
|
||||
|
||||
public function getColor() {
|
||||
$new = $this->getNewValue();
|
||||
return PhabricatorAuditCommitStatusConstants::getStatusColor($new);
|
||||
}
|
||||
|
||||
public function getTitle() {
|
||||
$new = $this->getNewValue();
|
||||
|
||||
switch ($new) {
|
||||
case PhabricatorAuditCommitStatusConstants::NONE:
|
||||
return pht('This commit no longer requires audit.');
|
||||
case PhabricatorAuditCommitStatusConstants::NEEDS_AUDIT:
|
||||
return pht('This commit now requires audit.');
|
||||
case PhabricatorAuditCommitStatusConstants::CONCERN_RAISED:
|
||||
return pht('This commit now has outstanding concerns.');
|
||||
case PhabricatorAuditCommitStatusConstants::NEEDS_VERIFICATION:
|
||||
return pht('This commit now requires verification by auditors.');
|
||||
case PhabricatorAuditCommitStatusConstants::FULLY_AUDITED:
|
||||
return pht('All concerns with this commit have now been addressed.');
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public function getTitleForFeed() {
|
||||
$new = $this->getNewValue();
|
||||
|
||||
switch ($new) {
|
||||
case PhabricatorAuditCommitStatusConstants::NONE:
|
||||
return pht(
|
||||
'%s no longer requires audit.',
|
||||
$this->renderObject());
|
||||
case PhabricatorAuditCommitStatusConstants::NEEDS_AUDIT:
|
||||
return pht(
|
||||
'%s now requires audit.',
|
||||
$this->renderObject());
|
||||
case PhabricatorAuditCommitStatusConstants::CONCERN_RAISED:
|
||||
return pht(
|
||||
'%s now has outstanding concerns.',
|
||||
$this->renderObject());
|
||||
case PhabricatorAuditCommitStatusConstants::NEEDS_VERIFICATION:
|
||||
return pht(
|
||||
'%s now requires verification by auditors.',
|
||||
$this->renderObject());
|
||||
case PhabricatorAuditCommitStatusConstants::FULLY_AUDITED:
|
||||
return pht(
|
||||
'All concerns with %s have now been addressed.',
|
||||
$this->renderObject());
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,73 @@
|
|||
<?php
|
||||
|
||||
final class DiffusionCommitVerifyTransaction
|
||||
extends DiffusionCommitAuditTransaction {
|
||||
|
||||
const TRANSACTIONTYPE = 'diffusion.commit.verify';
|
||||
const ACTIONKEY = 'verify';
|
||||
|
||||
protected function getCommitActionLabel() {
|
||||
return pht('Request Verification');
|
||||
}
|
||||
|
||||
protected function getCommitActionDescription() {
|
||||
return pht(
|
||||
'Auditors will be asked to verify that concerns have been addressed.');
|
||||
}
|
||||
|
||||
protected function getCommitActionGroupKey() {
|
||||
return DiffusionCommitEditEngine::ACTIONGROUP_COMMIT;
|
||||
}
|
||||
|
||||
public function getIcon() {
|
||||
return 'fa-refresh';
|
||||
}
|
||||
|
||||
public function getColor() {
|
||||
return 'indigo';
|
||||
}
|
||||
|
||||
protected function getCommitActionOrder() {
|
||||
return 600;
|
||||
}
|
||||
|
||||
public function getActionName() {
|
||||
return pht('Requested Verification');
|
||||
}
|
||||
|
||||
public function applyInternalEffects($object, $value) {
|
||||
$object->setAuditStatus(
|
||||
PhabricatorAuditCommitStatusConstants::NEEDS_VERIFICATION);
|
||||
}
|
||||
|
||||
protected function validateAction($object, PhabricatorUser $viewer) {
|
||||
if (!$this->isViewerCommitAuthor($object, $viewer)) {
|
||||
throw new Exception(
|
||||
pht(
|
||||
'You can not request verification of this commit because you '.
|
||||
'are not the author.'));
|
||||
}
|
||||
|
||||
$status = $object->getAuditStatus();
|
||||
if ($status != PhabricatorAuditCommitStatusConstants::CONCERN_RAISED) {
|
||||
throw new Exception(
|
||||
pht(
|
||||
'You can not request verification of this commit because no '.
|
||||
'auditors have raised conerns with it.'));
|
||||
}
|
||||
}
|
||||
|
||||
public function getTitle() {
|
||||
return pht(
|
||||
'%s requested verification of this commit.',
|
||||
$this->renderAuthor());
|
||||
}
|
||||
|
||||
public function getTitleForFeed() {
|
||||
return pht(
|
||||
'%s requested verification of %s.',
|
||||
$this->renderAuthor(),
|
||||
$this->renderObject());
|
||||
}
|
||||
|
||||
}
|
|
@ -32,32 +32,6 @@ final class PhabricatorFlagsApplication extends PhabricatorApplication {
|
|||
return self::GROUP_UTILITIES;
|
||||
}
|
||||
|
||||
public function loadStatus(PhabricatorUser $user) {
|
||||
$status = array();
|
||||
$limit = self::MAX_STATUS_ITEMS;
|
||||
|
||||
$flags = id(new PhabricatorFlagQuery())
|
||||
->setViewer($user)
|
||||
->withOwnerPHIDs(array($user->getPHID()))
|
||||
->setLimit(self::MAX_STATUS_ITEMS)
|
||||
->execute();
|
||||
|
||||
$count = count($flags);
|
||||
if ($count >= $limit) {
|
||||
$count_str = pht('%s+ Flagged Object(s)', new PhutilNumber($limit - 1));
|
||||
} else {
|
||||
$count_str = pht('%s Flagged Object(s)', new PhutilNumber($count));
|
||||
}
|
||||
|
||||
$type = PhabricatorApplicationStatusView::TYPE_WARNING;
|
||||
$status[] = id(new PhabricatorApplicationStatusView())
|
||||
->setType($type)
|
||||
->setText($count_str)
|
||||
->setCount($count);
|
||||
|
||||
return $status;
|
||||
}
|
||||
|
||||
public function getRoutes() {
|
||||
return array(
|
||||
'/flag/' => array(
|
||||
|
|
|
@ -31,7 +31,6 @@ abstract class PhabricatorHomeController extends PhabricatorController {
|
|||
|
||||
$tiles[] = id(new PhabricatorApplicationLaunchView())
|
||||
->setApplication($home_app)
|
||||
->setApplicationStatus($home_app->loadStatus($user))
|
||||
->addClass('phabricator-application-launch-phone-only')
|
||||
->setUser($user);
|
||||
|
||||
|
@ -44,7 +43,6 @@ abstract class PhabricatorHomeController extends PhabricatorController {
|
|||
|
||||
$tile = id(new PhabricatorApplicationLaunchView())
|
||||
->setApplication($application)
|
||||
->setApplicationStatus($application->loadStatus($user))
|
||||
->setUser($user);
|
||||
|
||||
$tiles[] = $tile;
|
||||
|
|
|
@ -7,8 +7,10 @@ final class PhabricatorHomeMenuItemController
|
|||
$viewer = $this->getViewer();
|
||||
$type = $request->getURIData('type');
|
||||
$custom_phid = null;
|
||||
$menu = PhabricatorProfileMenuEngine::MENU_GLOBAL;
|
||||
if ($type == 'personal') {
|
||||
$custom_phid = $viewer->getPHID();
|
||||
$menu = PhabricatorProfileMenuEngine::MENU_PERSONAL;
|
||||
}
|
||||
|
||||
$application = 'PhabricatorHomeApplication';
|
||||
|
@ -21,7 +23,9 @@ final class PhabricatorHomeMenuItemController
|
|||
$engine = id(new PhabricatorHomeProfileMenuEngine())
|
||||
->setProfileObject($home_app)
|
||||
->setCustomPHID($custom_phid)
|
||||
->setController($this);
|
||||
->setMenuType($menu)
|
||||
->setController($this)
|
||||
->setShowNavigation(false);
|
||||
|
||||
return $engine->buildResponse();
|
||||
}
|
||||
|
|
|
@ -13,6 +13,13 @@ final class PhabricatorEmojiRemarkupRule extends PhutilRemarkupRule {
|
|||
$text);
|
||||
}
|
||||
|
||||
public function markupEmojiJSON() {
|
||||
$root = dirname(phutil_get_library_root('phabricator'));
|
||||
$json = Filesystem::readFile(
|
||||
$root.'/resources/emoji/manifest.json');
|
||||
return $json;
|
||||
}
|
||||
|
||||
public function markupEmoji(array $matches) {
|
||||
if (!$this->isFlatText($matches[0])) {
|
||||
return $matches[0];
|
||||
|
|
|
@ -0,0 +1,47 @@
|
|||
<?php
|
||||
|
||||
final class PhabricatorEmojiDatasource extends PhabricatorTypeaheadDatasource {
|
||||
|
||||
public function getPlaceholderText() {
|
||||
return pht('Type an emoji name...');
|
||||
}
|
||||
|
||||
public function getBrowseTitle() {
|
||||
return pht('Browse Emojis');
|
||||
}
|
||||
|
||||
public function getDatasourceApplicationClass() {
|
||||
return 'PhabricatorMacroApplication';
|
||||
}
|
||||
|
||||
public function loadResults() {
|
||||
$results = $this->buildResults();
|
||||
return $this->filterResultsAgainstTokens($results);
|
||||
}
|
||||
|
||||
protected function renderSpecialTokens(array $values) {
|
||||
return $this->renderTokensFromResults($this->buildResults(), $values);
|
||||
}
|
||||
|
||||
private function buildResults() {
|
||||
$raw_query = $this->getRawQuery();
|
||||
|
||||
$data = id(new PhabricatorEmojiRemarkupRule())->markupEmojiJSON();
|
||||
$emojis = phutil_json_decode($data);
|
||||
|
||||
$results = array();
|
||||
foreach ($emojis as $shortname => $emoji) {
|
||||
$display_name = $emoji.' '.$shortname;
|
||||
$name = str_replace('_', ' ', $shortname);
|
||||
$result = id(new PhabricatorTypeaheadResult())
|
||||
->setPHID($shortname)
|
||||
->setName($name)
|
||||
->setDisplayname($display_name)
|
||||
->setAutocomplete($emoji);
|
||||
|
||||
$results[$shortname] = $result;
|
||||
}
|
||||
return $results;
|
||||
}
|
||||
|
||||
}
|
|
@ -59,37 +59,6 @@ final class PhabricatorManiphestApplication extends PhabricatorApplication {
|
|||
);
|
||||
}
|
||||
|
||||
public function loadStatus(PhabricatorUser $user) {
|
||||
$status = array();
|
||||
|
||||
if (!$user->isLoggedIn()) {
|
||||
return $status;
|
||||
}
|
||||
|
||||
$limit = self::MAX_STATUS_ITEMS;
|
||||
|
||||
$query = id(new ManiphestTaskQuery())
|
||||
->setViewer($user)
|
||||
->withStatuses(ManiphestTaskStatus::getOpenStatusConstants())
|
||||
->withOwners(array($user->getPHID()))
|
||||
->setLimit($limit);
|
||||
|
||||
$count = count($query->execute());
|
||||
if ($count >= $limit) {
|
||||
$count_str = pht('%s+ Assigned Task(s)', new PhutilNumber($limit - 1));
|
||||
} else {
|
||||
$count_str = pht('%s Assigned Task(s)', new PhutilNumber($count));
|
||||
}
|
||||
|
||||
$type = PhabricatorApplicationStatusView::TYPE_WARNING;
|
||||
$status[] = id(new PhabricatorApplicationStatusView())
|
||||
->setType($type)
|
||||
->setText($count_str)
|
||||
->setCount($count);
|
||||
|
||||
return $status;
|
||||
}
|
||||
|
||||
public function supportsEmailIntegration() {
|
||||
return true;
|
||||
}
|
||||
|
|
|
@ -3,18 +3,12 @@
|
|||
final class PhabricatorApplicationLaunchView extends AphrontTagView {
|
||||
|
||||
private $application;
|
||||
private $status;
|
||||
|
||||
public function setApplication(PhabricatorApplication $application) {
|
||||
$this->application = $application;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function setApplicationStatus(array $status) {
|
||||
$this->status = $status;
|
||||
return $this;
|
||||
}
|
||||
|
||||
protected function getTagName() {
|
||||
return $this->application ? 'a' : 'div';
|
||||
}
|
||||
|
@ -49,65 +43,9 @@ final class PhabricatorApplicationLaunchView extends AphrontTagView {
|
|||
),
|
||||
$application->getShortDescription());
|
||||
|
||||
$counts = array();
|
||||
$text = array();
|
||||
if ($this->status) {
|
||||
foreach ($this->status as $status) {
|
||||
$type = $status->getType();
|
||||
$counts[$type] = idx($counts, $type, 0) + $status->getCount();
|
||||
if ($status->getCount()) {
|
||||
$text[] = $status->getText();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$attention = PhabricatorApplicationStatusView::TYPE_NEEDS_ATTENTION;
|
||||
$warning = PhabricatorApplicationStatusView::TYPE_WARNING;
|
||||
if (!empty($counts[$attention]) || !empty($counts[$warning])) {
|
||||
$count = idx($counts, $attention, 0);
|
||||
$count1 = $count2 = '';
|
||||
if ($count > 0) {
|
||||
$count1 = phutil_tag(
|
||||
'span',
|
||||
array(
|
||||
'class' => 'phabricator-application-attention-count',
|
||||
),
|
||||
$this->formatStatusItemCount($count));
|
||||
}
|
||||
|
||||
|
||||
if (!empty($counts[$warning])) {
|
||||
$count2 = phutil_tag(
|
||||
'span',
|
||||
array(
|
||||
'class' => 'phabricator-application-warning-count',
|
||||
),
|
||||
$this->formatStatusItemCount($counts[$warning]));
|
||||
}
|
||||
|
||||
if (nonempty($count1) && nonempty($count2)) {
|
||||
$numbers = array($count1, ' / ', $count2);
|
||||
} else {
|
||||
$numbers = array($count1, $count2);
|
||||
}
|
||||
|
||||
Javelin::initBehavior('phabricator-tooltips');
|
||||
$content[] = javelin_tag(
|
||||
'span',
|
||||
array(
|
||||
'sigil' => 'has-tooltip',
|
||||
'meta' => array(
|
||||
'tip' => implode("\n", $text),
|
||||
'size' => 300,
|
||||
'align' => 'E',
|
||||
),
|
||||
'class' => 'phabricator-application-launch-attention',
|
||||
),
|
||||
$numbers);
|
||||
}
|
||||
|
||||
$classes = array();
|
||||
$classes[] = 'phabricator-application-launch-icon';
|
||||
|
||||
$styles = array();
|
||||
$classes[] = $application->getIcon();
|
||||
$classes[] = 'phui-icon-view';
|
||||
|
@ -128,13 +66,4 @@ final class PhabricatorApplicationLaunchView extends AphrontTagView {
|
|||
);
|
||||
}
|
||||
|
||||
private function formatStatusItemCount($count) {
|
||||
$limit = PhabricatorApplication::MAX_STATUS_ITEMS;
|
||||
if ($count >= $limit) {
|
||||
return pht('%s+', new PhutilNumber($limit - 1));
|
||||
} else {
|
||||
return pht('%s', new PhutilNumber($count));
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -1,61 +0,0 @@
|
|||
<?php
|
||||
|
||||
final class PhabricatorApplicationStatusView extends AphrontView {
|
||||
|
||||
private $count;
|
||||
private $text;
|
||||
private $type;
|
||||
|
||||
const TYPE_NEEDS_ATTENTION = 'needs';
|
||||
const TYPE_INFO = 'info';
|
||||
const TYPE_OKAY = 'okay';
|
||||
const TYPE_WARNING = 'warning';
|
||||
const TYPE_EMPTY = 'empty';
|
||||
|
||||
public function setType($type) {
|
||||
$this->type = $type;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getType() {
|
||||
return $this->type;
|
||||
}
|
||||
|
||||
public function setText($text) {
|
||||
$this->text = $text;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getText() {
|
||||
return $this->text;
|
||||
}
|
||||
|
||||
public function setCount($count) {
|
||||
$this->count = $count;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getCount() {
|
||||
return $this->count;
|
||||
}
|
||||
|
||||
public function render() {
|
||||
$type = $this->type;
|
||||
if (!$this->count) {
|
||||
$type = self::TYPE_EMPTY;
|
||||
}
|
||||
|
||||
$classes = array(
|
||||
'phabricator-application-status',
|
||||
'phabricator-application-status-type-'.$type,
|
||||
);
|
||||
|
||||
return phutil_tag(
|
||||
'span',
|
||||
array(
|
||||
'class' => implode(' ', $classes),
|
||||
),
|
||||
$this->text);
|
||||
}
|
||||
|
||||
}
|
|
@ -185,6 +185,7 @@ final class PhabricatorPasteQuery
|
|||
$paste->getFilePHID(),
|
||||
$paste->getLanguage(),
|
||||
'snippet',
|
||||
'v2',
|
||||
PhabricatorHash::digestForIndex($paste->getTitle()),
|
||||
));
|
||||
}
|
||||
|
@ -294,7 +295,8 @@ final class PhabricatorPasteQuery
|
|||
$snippet_data = phutil_json_decode($caches[$key], true);
|
||||
$snippet = new PhabricatorPasteSnippet(
|
||||
phutil_safe_html($snippet_data['content']),
|
||||
$snippet_data['type']);
|
||||
$snippet_data['type'],
|
||||
$snippet_data['contentLineCount']);
|
||||
$paste->attachSnippet($snippet);
|
||||
$have_cache[$paste->getPHID()] = true;
|
||||
} else {
|
||||
|
@ -326,6 +328,7 @@ final class PhabricatorPasteQuery
|
|||
$snippet_data = array(
|
||||
'content' => (string)$snippet->getContent(),
|
||||
'type' => (string)$snippet->getType(),
|
||||
'contentLineCount' => $snippet->getContentLineCount(),
|
||||
);
|
||||
$write_data[$this->getSnippetCacheKey($paste)] = phutil_json_encode(
|
||||
$snippet_data);
|
||||
|
@ -358,7 +361,8 @@ final class PhabricatorPasteQuery
|
|||
}
|
||||
|
||||
$lines = phutil_split_lines($snippet);
|
||||
if (count($lines) > 5) {
|
||||
$line_count = count($lines);
|
||||
if ($line_count > 5) {
|
||||
$snippet_type = PhabricatorPasteSnippet::FIRST_LINES;
|
||||
$snippet = implode('', array_slice($lines, 0, 5));
|
||||
}
|
||||
|
@ -368,7 +372,8 @@ final class PhabricatorPasteQuery
|
|||
$snippet,
|
||||
$paste->getTitle(),
|
||||
$paste->getLanguage()),
|
||||
$snippet_type);
|
||||
$snippet_type,
|
||||
$line_count);
|
||||
}
|
||||
|
||||
private function highlightSource($source, $title, $language) {
|
||||
|
|
|
@ -166,7 +166,7 @@ final class PhabricatorPasteSearchEngine
|
|||
$preview);
|
||||
|
||||
$created = phabricator_datetime($paste->getDateCreated(), $viewer);
|
||||
$line_count = count($lines);
|
||||
$line_count = $paste->getSnippet()->getContentLineCount();
|
||||
$line_count = pht(
|
||||
'%s Line(s)',
|
||||
new PhutilNumber($line_count));
|
||||
|
|
|
@ -8,10 +8,12 @@ final class PhabricatorPasteSnippet extends Phobject {
|
|||
|
||||
private $content;
|
||||
private $type;
|
||||
private $contentLineCount;
|
||||
|
||||
public function __construct($content, $type) {
|
||||
public function __construct($content, $type, $content_line_count) {
|
||||
$this->content = $content;
|
||||
$this->type = $type;
|
||||
$this->contentLineCount = $content_line_count;
|
||||
}
|
||||
|
||||
public function getContent() {
|
||||
|
@ -21,4 +23,8 @@ final class PhabricatorPasteSnippet extends Phobject {
|
|||
public function getType() {
|
||||
return $this->type;
|
||||
}
|
||||
|
||||
public function getContentLineCount() {
|
||||
return $this->contentLineCount;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -92,44 +92,6 @@ final class PhabricatorPeopleApplication extends PhabricatorApplication {
|
|||
);
|
||||
}
|
||||
|
||||
public function loadStatus(PhabricatorUser $user) {
|
||||
if (!$user->getIsAdmin()) {
|
||||
return array();
|
||||
}
|
||||
$limit = self::MAX_STATUS_ITEMS;
|
||||
|
||||
$need_approval = id(new PhabricatorPeopleQuery())
|
||||
->setViewer($user)
|
||||
->withIsApproved(false)
|
||||
->withIsDisabled(false)
|
||||
->setLimit($limit)
|
||||
->execute();
|
||||
if (!$need_approval) {
|
||||
return array();
|
||||
}
|
||||
|
||||
$status = array();
|
||||
|
||||
$count = count($need_approval);
|
||||
if ($count >= $limit) {
|
||||
$count_str = pht(
|
||||
'%s+ User(s) Need Approval',
|
||||
new PhutilNumber($limit - 1));
|
||||
} else {
|
||||
$count_str = pht(
|
||||
'%s User(s) Need Approval',
|
||||
new PhutilNumber($count));
|
||||
}
|
||||
|
||||
$type = PhabricatorApplicationStatusView::TYPE_NEEDS_ATTENTION;
|
||||
$status[] = id(new PhabricatorApplicationStatusView())
|
||||
->setType($type)
|
||||
->setText($count_str)
|
||||
->setCount($count);
|
||||
|
||||
return $status;
|
||||
}
|
||||
|
||||
public function getApplicationSearchDocumentTypes() {
|
||||
return array(
|
||||
PhabricatorPeopleUserPHIDType::TYPECONST,
|
||||
|
|
|
@ -46,27 +46,4 @@ final class PhabricatorPhrequentApplication extends PhabricatorApplication {
|
|||
);
|
||||
}
|
||||
|
||||
public function loadStatus(PhabricatorUser $user) {
|
||||
$status = array();
|
||||
$limit = self::MAX_STATUS_ITEMS;
|
||||
|
||||
// Show number of objects that are currently
|
||||
// being tracked for a user.
|
||||
|
||||
$count = PhrequentUserTimeQuery::getUserTotalObjectsTracked($user, $limit);
|
||||
if ($count >= $limit) {
|
||||
$count_str = pht('%s+ Object(s) Tracked', new PhutilNumber($limit - 1));
|
||||
} else {
|
||||
$count_str = pht('%s Object(s) Tracked', new PhutilNumber($count));
|
||||
}
|
||||
|
||||
$type = PhabricatorApplicationStatusView::TYPE_NEEDS_ATTENTION;
|
||||
$status[] = id(new PhabricatorApplicationStatusView())
|
||||
->setType($type)
|
||||
->setText($count_str)
|
||||
->setCount($count);
|
||||
|
||||
return $status;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -41,6 +41,7 @@ final class PhabricatorRepositoryCommit
|
|||
private $repository = self::ATTACHABLE;
|
||||
private $customFields = self::ATTACHABLE;
|
||||
private $drafts = array();
|
||||
private $auditAuthorityPHIDs = array();
|
||||
|
||||
public function attachRepository(PhabricatorRepository $repository) {
|
||||
$this->repository = $repository;
|
||||
|
@ -180,30 +181,85 @@ final class PhabricatorRepositoryCommit
|
|||
return $this->assertAttached($this->audits);
|
||||
}
|
||||
|
||||
public function getAuthorityAudits(
|
||||
PhabricatorUser $user,
|
||||
array $authority_phids) {
|
||||
public function loadAndAttachAuditAuthority(
|
||||
PhabricatorUser $viewer,
|
||||
$actor_phid = null) {
|
||||
|
||||
$authority = array_fill_keys($authority_phids, true);
|
||||
$audits = $this->getAudits();
|
||||
$authority_audits = array();
|
||||
foreach ($audits as $audit) {
|
||||
$has_authority = !empty($authority[$audit->getAuditorPHID()]);
|
||||
if ($has_authority) {
|
||||
$commit_author = $this->getAuthorPHID();
|
||||
if ($actor_phid === null) {
|
||||
$actor_phid = $viewer->getPHID();
|
||||
}
|
||||
|
||||
// You don't have authority over package and project audits on your
|
||||
// own commits.
|
||||
// TODO: This method is a little weird and sketchy, but worlds better than
|
||||
// what came before it. Eventually, this should probably live in a Query
|
||||
// class.
|
||||
|
||||
$auditor_is_user = ($audit->getAuditorPHID() == $user->getPHID());
|
||||
$user_is_author = ($commit_author == $user->getPHID());
|
||||
// Figure out which requests the actor has authority over: these are user
|
||||
// requests where they are the auditor, and packages and projects they are
|
||||
// a member of.
|
||||
|
||||
if ($auditor_is_user || !$user_is_author) {
|
||||
$authority_audits[$audit->getID()] = $audit;
|
||||
if (!$actor_phid) {
|
||||
$attach_key = $viewer->getCacheFragment();
|
||||
$phids = array();
|
||||
} else {
|
||||
$attach_key = $actor_phid;
|
||||
// At least currently, when modifying your own commits, you act only on
|
||||
// behalf of yourself, not your packages/projects -- the idea being that
|
||||
// you can't accept your own commits. This may change or depend on
|
||||
// config.
|
||||
$actor_is_author = ($actor_phid == $this->getAuthorPHID());
|
||||
if ($actor_is_author) {
|
||||
$phids = array($actor_phid);
|
||||
} else {
|
||||
$phids = array();
|
||||
$phids[$actor_phid] = true;
|
||||
|
||||
$owned_packages = id(new PhabricatorOwnersPackageQuery())
|
||||
->setViewer($viewer)
|
||||
->withAuthorityPHIDs(array($actor_phid))
|
||||
->execute();
|
||||
foreach ($owned_packages as $package) {
|
||||
$phids[$package->getPHID()] = true;
|
||||
}
|
||||
|
||||
$projects = id(new PhabricatorProjectQuery())
|
||||
->setViewer($viewer)
|
||||
->withMemberPHIDs(array($actor_phid))
|
||||
->execute();
|
||||
foreach ($projects as $project) {
|
||||
$phids[$project->getPHID()] = true;
|
||||
}
|
||||
|
||||
$phids = array_keys($phids);
|
||||
}
|
||||
}
|
||||
return $authority_audits;
|
||||
|
||||
$this->auditAuthorityPHIDs[$attach_key] = array_fuse($phids);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function hasAuditAuthority(
|
||||
PhabricatorUser $viewer,
|
||||
PhabricatorRepositoryAuditRequest $audit,
|
||||
$actor_phid = null) {
|
||||
|
||||
if ($actor_phid === null) {
|
||||
$actor_phid = $viewer->getPHID();
|
||||
}
|
||||
|
||||
if (!$actor_phid) {
|
||||
$attach_key = $viewer->getCacheFragment();
|
||||
} else {
|
||||
$attach_key = $actor_phid;
|
||||
}
|
||||
|
||||
$map = $this->assertAttachedKey($this->auditAuthorityPHIDs, $attach_key);
|
||||
|
||||
if (!$actor_phid) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return isset($map[$audit->getAuditorPHID()]);
|
||||
}
|
||||
|
||||
public function getAuditorPHIDsForEdit() {
|
||||
|
@ -271,8 +327,17 @@ final class PhabricatorRepositoryCommit
|
|||
}
|
||||
}
|
||||
|
||||
$current_status = $this->getAuditStatus();
|
||||
$status_verify = PhabricatorAuditCommitStatusConstants::NEEDS_VERIFICATION;
|
||||
|
||||
if ($any_concern) {
|
||||
$status = PhabricatorAuditCommitStatusConstants::CONCERN_RAISED;
|
||||
if ($current_status == $status_verify) {
|
||||
// If the change is in "Needs Verification", we keep it there as
|
||||
// long as any auditors still have concerns.
|
||||
$status = $status_verify;
|
||||
} else {
|
||||
$status = PhabricatorAuditCommitStatusConstants::CONCERN_RAISED;
|
||||
}
|
||||
} else if ($any_accept) {
|
||||
if ($any_need) {
|
||||
$status = PhabricatorAuditCommitStatusConstants::PARTIALLY_AUDITED;
|
||||
|
|
|
@ -497,10 +497,34 @@ abstract class PhabricatorProfileMenuEngine extends Phobject {
|
|||
$viewer = $this->getViewer();
|
||||
$object = $this->getProfileObject();
|
||||
|
||||
PhabricatorPolicyFilter::requireCapability(
|
||||
$viewer,
|
||||
$object,
|
||||
PhabricatorPolicyCapability::CAN_EDIT);
|
||||
// If you're reordering global items, you need to be able to edit the
|
||||
// object the menu appears on. If you're reordering custom items, you only
|
||||
// need to be able to edit the custom object. Currently, the custom object
|
||||
// is always the viewing user's own user object.
|
||||
$custom_phid = $this->getCustomPHID();
|
||||
if (!$custom_phid) {
|
||||
PhabricatorPolicyFilter::requireCapability(
|
||||
$viewer,
|
||||
$object,
|
||||
PhabricatorPolicyCapability::CAN_EDIT);
|
||||
} else {
|
||||
$policy_object = id(new PhabricatorObjectQuery())
|
||||
->setViewer($viewer)
|
||||
->withPHIDs(array($custom_phid))
|
||||
->executeOne();
|
||||
|
||||
if (!$policy_object) {
|
||||
throw new Exception(
|
||||
pht(
|
||||
'Failed to load custom PHID "%s"!',
|
||||
$custom_phid));
|
||||
}
|
||||
|
||||
PhabricatorPolicyFilter::requireCapability(
|
||||
$viewer,
|
||||
$policy_object,
|
||||
PhabricatorPolicyCapability::CAN_EDIT);
|
||||
}
|
||||
|
||||
$controller = $this->getController();
|
||||
$request = $controller->getRequest();
|
||||
|
|
47
src/applications/uiexample/examples/MacroEmojiExample.php
Normal file
47
src/applications/uiexample/examples/MacroEmojiExample.php
Normal file
|
@ -0,0 +1,47 @@
|
|||
<?php
|
||||
|
||||
final class MacroEmojiExample extends PhabricatorUIExample {
|
||||
|
||||
public function getName() {
|
||||
return pht('Emoji Support');
|
||||
}
|
||||
|
||||
public function getDescription() {
|
||||
return pht('Shiny happy people holding hands');
|
||||
}
|
||||
|
||||
public function renderExample() {
|
||||
|
||||
$raw = id(new PhabricatorEmojiRemarkupRule())
|
||||
->markupEmojiJSON();
|
||||
|
||||
$json = phutil_json_decode($raw);
|
||||
|
||||
$content = array();
|
||||
foreach ($json as $shortname => $hex) {
|
||||
|
||||
$display_name = ' '.$hex.' '.$shortname;
|
||||
|
||||
$content[] = phutil_tag(
|
||||
'div',
|
||||
array(
|
||||
'class' => 'ms grouped',
|
||||
'style' => 'width: 240px; height: 24px; float: left;',
|
||||
),
|
||||
$display_name);
|
||||
|
||||
}
|
||||
|
||||
$wrap = id(new PHUIObjectBoxView())
|
||||
->setHeaderText(pht('Emojis'))
|
||||
->addClass('grouped')
|
||||
->appendChild($content);
|
||||
|
||||
return phutil_tag(
|
||||
'div',
|
||||
array(),
|
||||
array(
|
||||
$wrap,
|
||||
));
|
||||
}
|
||||
}
|
|
@ -1,71 +1,125 @@
|
|||
@title Audit User Guide
|
||||
@group userguide
|
||||
|
||||
Guide to the Audit (post-push code review) tool and workflow.
|
||||
Guide to using Phabricator to audit published commits.
|
||||
|
||||
= Overview =
|
||||
|
||||
Phabricator supports two code review workflows, "review" (pre-push) and
|
||||
"audit" (post-push). To understand the differences between the two, see
|
||||
Overview
|
||||
========
|
||||
|
||||
Phabricator supports two code review workflows, "review" (pre-publish) and
|
||||
"audit" (post-publish). To understand the differences between the two, see
|
||||
@{article:User Guide: Review vs Audit}.
|
||||
|
||||
This document summarizes the post-push "audit" workflow implemented by the
|
||||
creatively-named //Audit// tool.
|
||||
|
||||
= How Audit Works =
|
||||
How Audit Works
|
||||
===============
|
||||
|
||||
Using auditing allows you to push and deploy code without waiting for code
|
||||
review, while still doing code review eventually. The Audit tool primarily keeps
|
||||
track of two things:
|
||||
The audit workflow occurs after changes have been published. It provides ways
|
||||
to track, discuss, and resolve issues with commits that are discovered after
|
||||
they go through whatever review process you have in place (if you have one).
|
||||
|
||||
Two examples of how you might use audit are:
|
||||
|
||||
**Fix Issues**: If a problem is discovered after a change has already been
|
||||
published, users can find the commit which introduced the problem and raise a
|
||||
concern on it. This notifies the author of the commit and prompts them to
|
||||
remedy the issue.
|
||||
|
||||
**Watch Changes**: In some cases, you may want to passively look over changes
|
||||
that satisfy some criteria as they are published. For example, you may want to
|
||||
review all Javascript changes at the end of the week to keep an eye on things,
|
||||
or make sure that code which impacts a subsystem is looked at by someone on
|
||||
that team, eventually.
|
||||
|
||||
Developers may also want other developers to take a second look at things if
|
||||
they realize they aren't sure about something after a change has been published,
|
||||
or just want to provide a heads-up.
|
||||
|
||||
You can configure Herald rules and Owners packages to automatically trigger
|
||||
audits of commits that satisfy particular criteria.
|
||||
|
||||
|
||||
Audit States and Actions
|
||||
========================
|
||||
|
||||
The audit workflow primarily keeps track of two things:
|
||||
|
||||
- **Commits** and their audit state (like "Not Audited", "Approved", or
|
||||
"Concern Raised").
|
||||
- **Audit Requests** which ask a user (or some other entity) to audit a
|
||||
commit. These can be triggered in a number of ways (see below).
|
||||
- **Audit Requests** which ask a user (or some other entity, like a project
|
||||
or package) to audit a commit. These can be triggered in a number of ways
|
||||
(see below).
|
||||
|
||||
In the Audit tool's home screen and on the homepage you can see commits and
|
||||
requests that require your action:
|
||||
Users interact with commits by leaving comments and applying actions, like
|
||||
accepting the changes or raising a concern. These actions change the state of
|
||||
their own audit and the overall audit state of the commit. Here's an example of
|
||||
a typical audit workflow:
|
||||
|
||||
- **Required Audits** are open audit requests that require you, a project
|
||||
you are a member of, or a package you own to audit a commit. An audit
|
||||
request is closed when you approve the associated commit.
|
||||
- **Problem Commits** are commits you authored which someone has raised a
|
||||
concern about in audit. Problem commits go away when you satisfy all the
|
||||
auditors and get them to "Approve" the commit.
|
||||
- Alice publishes a commit containing some Javascript.
|
||||
- This triggers an audit request to Bailey, the Javascript technical
|
||||
lead on the project (see below for a description of trigger mechanisms).
|
||||
- Later, Bailey logs into Phabrictor and sees the audit request. She ignores
|
||||
it for the moment, since it isn't blocking anything. At the end of the
|
||||
week she looks through her open requests to see what the team has been
|
||||
up to.
|
||||
- Bailey notices a few minor problems with Alice's commit. She leaves
|
||||
comments describing improvements and uses "Raise Concern" to send the
|
||||
commit back into Alice's queue.
|
||||
- Later, Alice logs into Phabricator and sees that Bailey has raised a
|
||||
concern (usually, Alice will also get an email). She resolves the issue
|
||||
somehow, maybe by making a followup commit with fixes.
|
||||
- After the issues have been dealt with, she uses "Request Verification" to
|
||||
return the change to Bailey so Bailey can verify that the concerns have
|
||||
been addressed.
|
||||
- Bailey uses "Accept Commit" to close the audit.
|
||||
|
||||
For example:
|
||||
In {nav Diffusion > Browse Commits}, you can review commits and query for
|
||||
commits with certain audit states. The default "Active Audits" view shows
|
||||
all of the commits which are relevant to you given their audit state, divided
|
||||
into buckets:
|
||||
|
||||
- Evan creates commit `abcdef1234` and pushes it to the remote.
|
||||
- This triggers an audit request to Bob through some mechanism (see below for
|
||||
a description of trigger mechanisms).
|
||||
- Later, Bob logs into Phabricator and sees the audit request on his homepage.
|
||||
- Bob clicks through and examines the commit. He notices a problem, so he
|
||||
selects "Raise Concern" and describes the issue in a comment.
|
||||
- Evan receives an email that Bob has raised a concern about his commit. He
|
||||
opts not to deal with it immediately.
|
||||
- Later, Evan logs into Phabricator and sees the commit on his homepage
|
||||
under "Problem Commits".
|
||||
- Evan resolves the issue somehow (e.g., by discussing it with Bob, or fixing
|
||||
it in another commit).
|
||||
- Now satisfied, Bob "Accepts" the original commit.
|
||||
- This causes the request to disappear from Bob's queue, and the commit to
|
||||
disappear from Evan's queue.
|
||||
- **Needs Attention**: These are commits which you authored that another
|
||||
user has raised a concern about: for example, maybe they believe they have
|
||||
found a bug or some other problem. You should address the concerns.
|
||||
- **Needs Verification**: These are commits which someone else authored
|
||||
that you previously raised a concern about. The author has indicated that
|
||||
they believe the concern has been addressed. You should verify that the
|
||||
remedy is satisfactory and accept the change, or raise a further concern.
|
||||
- **Ready to Audit**: These are commits which someone else authored that you
|
||||
have been asked to audit, either by a user or by a system rule. You should
|
||||
look over the changes and either accept them or raise concerns.
|
||||
- **Waiting on Authors**: These are commits which someone else authored that
|
||||
you previously raised a concern about. The author has not responded to the
|
||||
concern yet. You may want to follow up.
|
||||
- **Waiting on Auditors**: These are commits which you authored that someone
|
||||
else needs to audit.
|
||||
|
||||
= Audit Triggers =
|
||||
You can use the query constraints to filter this list or find commits that
|
||||
match certain criteria.
|
||||
|
||||
|
||||
Audit Triggers
|
||||
==============
|
||||
|
||||
Audit requests can be triggered in a number of ways:
|
||||
|
||||
- You can add auditors explicitly from the web UI, using either "Edit Commit"
|
||||
or the "Change Auditors" action. You might do this if you realize you are
|
||||
not sure about something that you recently published and want a second
|
||||
opinion.
|
||||
- If you put `Auditors: username1, username2` in your commit message, it will
|
||||
trigger an audit request to those users when you push it to a tracked
|
||||
branch.
|
||||
- You can create rules in Herald that trigger audits based on properties
|
||||
of the commit -- like the files it touches, the text of the change, the
|
||||
author, etc.
|
||||
- You can create an audit request for yourself by commenting on any commit.
|
||||
- You can create an Owners package and select "Enable Auditing" (this is an
|
||||
advanced feature which is only likely to be useful for very large teams).
|
||||
- You can create an Owners package and enable automatic auditing for the
|
||||
package.
|
||||
|
||||
= Audits in Small Teams =
|
||||
|
||||
Audits in Small Teams
|
||||
=====================
|
||||
|
||||
If you have a small team and don't need complicated trigger rules, you can set
|
||||
up a simple audit workflow like this:
|
||||
|
@ -84,7 +138,9 @@ commit should have //someone// look at it".
|
|||
Once your team gets bigger, you can refine this ruleset so that developers see
|
||||
only changes that are relevant to them.
|
||||
|
||||
= Audit Tips =
|
||||
|
||||
Audit Tips
|
||||
==========
|
||||
|
||||
- When viewing a commit, audit requests you are responsible for are
|
||||
highlighted. You are responsible for a request if it's a user request
|
||||
|
@ -99,6 +155,8 @@ only changes that are relevant to them.
|
|||
you submit a comment at the bottom of the page.
|
||||
- Press "?" to view keyboard shortcuts.
|
||||
|
||||
= Next Steps =
|
||||
|
||||
Next Steps
|
||||
==========
|
||||
|
||||
- Learn more about Herald at @{article:Herald User Guide}.
|
||||
|
|
|
@ -1,30 +1,45 @@
|
|||
@title User Guide: Review vs Audit
|
||||
@group userguide
|
||||
|
||||
Discusses the differences between Review and Audit workflows.
|
||||
Discusses the differences between "review" and "audit" workflows.
|
||||
|
||||
= Overview =
|
||||
Overview
|
||||
========
|
||||
|
||||
Phabricator supports two similar but separate code review workflows:
|
||||
Phabricator supports two similar but separate code review workflows: "review"
|
||||
and "audit".
|
||||
|
||||
- **Differential** is used for pre-push code review, called "reviews"
|
||||
elsewhere in the documentation. You can learn more in
|
||||
@{article:Differential User Guide}.
|
||||
- **Audit** is used for post-push code reviews, called "audits" elsewhere in
|
||||
the documentation. You can learn more in @{article:Audit User Guide}.
|
||||
Review occurs in **Differential**, before changes are published. You can learn
|
||||
more in @{article:Differential User Guide}.
|
||||
|
||||
(By "pre-push", this document means review which blocks deployment of changes,
|
||||
while "post-push" means review which happens after changes are deployed or
|
||||
en route to deployment.)
|
||||
Audit occurs in **Diffusion**, after changes are published. You can learn more
|
||||
in @{article:Audit User Guide}.
|
||||
|
||||
Both are lightweight, asynchronous web-based workflows where reviewers/auditors
|
||||
inspect code independently, from their own machines -- not synchronous review
|
||||
sessions where authors and reviewers meet in person to discuss changes.
|
||||
When this documentation discusses "unpublished changes", it refers to changes
|
||||
which are still subject to being reworked in response to feedback. In many
|
||||
workflows, these changes will only exist locally on the developer's machine,
|
||||
but some workflows push tentative or temporary changes into remotes. The step
|
||||
that "publishes" changes might be either pushing or merging them, depending on
|
||||
your workflow.
|
||||
|
||||
= Advantages of Review =
|
||||
Both the audit and review workflows are lightweight, asynchronous web-based
|
||||
workflows where reviewers or auditors inspect code independently, from their
|
||||
own machines -- not synchronous review sessions where authors and reviewers
|
||||
meet in person to discuss changes.
|
||||
|
||||
Pre-push review is significantly more powerful than post-push auditing. You
|
||||
gain these advantages by requiring review //before// changes may be pushed:
|
||||
Broadly, review is normally a //blocking// workflow: in review workflows,
|
||||
authors usually can not publish changes until review completes and reviewers
|
||||
are satisfied.
|
||||
|
||||
In contrast, audit is normally a //nonblocking// workflow: in audit workflows,
|
||||
changes usually move forward by default.
|
||||
|
||||
Advantages of Review
|
||||
====================
|
||||
|
||||
Pre-publish review is significantly more powerful than post-publish auditing.
|
||||
You gain these advantages by requiring review //before// changes may be
|
||||
published:
|
||||
|
||||
- Authors have a strong incentive to craft small, well-formed changes that
|
||||
will be readily understood, to explain them adequately, and to provide
|
||||
|
@ -32,11 +47,12 @@ gain these advantages by requiring review //before// changes may be pushed:
|
|||
- Reviewers have a real opportunity to make significant suggestions about
|
||||
architecture or approach in review. These suggestions are less attractive
|
||||
to adopt from audit, and may be much more difficult to adopt if significant
|
||||
time has passed between push and audit.
|
||||
time has passed between publish and audit.
|
||||
- Authors have a strong incentive to fix problems and respond to feedback
|
||||
received during review, because it blocks them. Authors have a much weaker
|
||||
incentive to address problems raised during audit.
|
||||
- Authors can ask reviewers to apply and verify fixes before they are pushed.
|
||||
received during review because it blocks them. Authors have a much weaker
|
||||
incentive to promptly address problems raised during audit.
|
||||
- Authors can ask reviewers to apply and verify fixes before they are
|
||||
published.
|
||||
- Authors can easily pursue feedback early, and get course corrections on
|
||||
approach or direction.
|
||||
- Reviewers are better prepared to support a given change once it is in
|
||||
|
@ -54,7 +70,7 @@ a blocking step into the process and generally wastes developer time that could
|
|||
be better spent developing. This is less true than it appears, because the costs
|
||||
are low and pay for themselves in other ways:
|
||||
|
||||
- Differential is fast and provides a very lightweight process for submitting
|
||||
- Differential is fast and provides a lightweight process for submitting
|
||||
code for review and for performing review.
|
||||
- Authors are free to pursue other changes while code is being reviewed. With
|
||||
appropriate change management (like local branching in Git) they can even
|
||||
|
@ -87,13 +103,15 @@ are low and pay for themselves in other ways:
|
|||
- With `arc patch`, it is roughly as easy to pull a change out of Differential
|
||||
as it is to pull it out of the remote.
|
||||
|
||||
= Advantages of Audit =
|
||||
Advantages of Audit
|
||||
===================
|
||||
|
||||
Post-push review is significantly better than nothing. If you are unpersuaded
|
||||
Post-publish audit is a less powerful workflow than pre-publish review, but can
|
||||
supplement review and is better than nothing on its own. If you are unpersuaded
|
||||
by the arguments above (or work on a team that is unswayed), audits provide
|
||||
some of the benefits of review with less friction:
|
||||
|
||||
- Audits are driven entirely by Phabricator, users do not need to install
|
||||
- Audits are driven entirely by Phabricator: users do not need to install
|
||||
`arc`.
|
||||
- Audits require little adjustment to existing workflows and little training.
|
||||
- Audits are completely nonblocking, and send fewer notifications than review.
|
||||
|
@ -101,7 +119,8 @@ some of the benefits of review with less friction:
|
|||
on lower-importance changes or raise issues that are discovered after
|
||||
review.
|
||||
|
||||
= Recommendations =
|
||||
Recommendations
|
||||
===============
|
||||
|
||||
Here are super biased recommendations from developers of code review software:
|
||||
|
||||
|
@ -117,7 +136,8 @@ Here are super biased recommendations from developers of code review software:
|
|||
- If you aren't interested in review, just do audits. You can always
|
||||
change your mind later. But consider review! It's really good, we promise!
|
||||
|
||||
= Next Steps =
|
||||
Next Steps
|
||||
==========
|
||||
|
||||
- Learn more about reviews in @{article:Differential User Guide}; or
|
||||
- learn more about audits in @{article:Audit User Guide}.
|
||||
|
|
|
@ -55,6 +55,7 @@ final class PhabricatorRemarkupControl extends AphrontFormTextAreaControl {
|
|||
$root_id = celerity_generate_unique_node_id();
|
||||
|
||||
$user_datasource = new PhabricatorPeopleDatasource();
|
||||
$emoji_datasource = new PhabricatorEmojiDatasource();
|
||||
$proj_datasource = id(new PhabricatorProjectDatasource())
|
||||
->setParameters(
|
||||
array(
|
||||
|
@ -91,6 +92,12 @@ final class PhabricatorRemarkupControl extends AphrontFormTextAreaControl {
|
|||
'headerText' => pht('Find Project:'),
|
||||
'hintText' => $proj_datasource->getPlaceholderText(),
|
||||
),
|
||||
58 => array( // ":"
|
||||
'datasourceURI' => $emoji_datasource->getDatasourceURI(),
|
||||
'headerIcon' => 'fa-smile-o',
|
||||
'headerText' => pht('Find Emoji:'),
|
||||
'hintText' => $emoji_datasource->getPlaceholderText(),
|
||||
),
|
||||
),
|
||||
));
|
||||
Javelin::initBehavior('phabricator-tooltips', array());
|
||||
|
|
|
@ -204,7 +204,14 @@ JX.install('WorkboardBoard', {
|
|||
|
||||
if (!this._templates[phid]) {
|
||||
for (var add_phid in response.columnMaps) {
|
||||
this.getColumn(add_phid).newCard(phid);
|
||||
var target_column = this.getColumn(add_phid);
|
||||
|
||||
if (!target_column) {
|
||||
// If the column isn't visible, don't try to add a card to it.
|
||||
continue;
|
||||
}
|
||||
|
||||
target_column.newCard(phid);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -118,7 +118,6 @@ JX.install('PHUIXAutocomplete', {
|
|||
case '|': // Might be a table cell.
|
||||
case '>': // Might be a blockquote.
|
||||
case '!': // Might be a blockquote attribution line.
|
||||
case ':': // Might be a "NOTE:".
|
||||
// We'll let these autocomplete.
|
||||
break;
|
||||
default:
|
||||
|
|
Loading…
Reference in a new issue