Merge branch 'master' into blender-tweaks
Conflicts: resources/celerity/map.php src/applications/differential/controller/DifferentialDiffCreateController.php
This commit is contained in:
1
bin/worker
Symbolic link
1
bin/worker
Symbolic link
@@ -0,0 +1 @@
|
||||
../scripts/setup/manage_worker.php
|
@@ -7,11 +7,11 @@
|
||||
*/
|
||||
return array(
|
||||
'names' => array(
|
||||
'core.pkg.css' => '61fe1c6e',
|
||||
'core.pkg.js' => 'cbdbd552',
|
||||
'core.pkg.css' => 'dab8bb9c',
|
||||
'core.pkg.js' => 'e64447dc',
|
||||
'darkconsole.pkg.js' => 'df001cab',
|
||||
'differential.pkg.css' => '8af45893',
|
||||
'differential.pkg.js' => '85cb2027',
|
||||
'differential.pkg.js' => '42c10e78',
|
||||
'diffusion.pkg.css' => '591664fa',
|
||||
'diffusion.pkg.js' => 'bfc0737b',
|
||||
'maniphest.pkg.css' => 'e34dfbec',
|
||||
@@ -30,7 +30,7 @@ return array(
|
||||
'rsrc/css/aphront/phabricator-nav-view.css' => '9283c2df',
|
||||
'rsrc/css/aphront/table-view.css' => 'b22b7216',
|
||||
'rsrc/css/aphront/tokenizer.css' => '82ce2142',
|
||||
'rsrc/css/aphront/tooltip.css' => '9c90229d',
|
||||
'rsrc/css/aphront/tooltip.css' => '4099b97e',
|
||||
'rsrc/css/aphront/transaction.css' => '5d0cae25',
|
||||
'rsrc/css/aphront/two-column.css' => '16ab3ad2',
|
||||
'rsrc/css/aphront/typeahead.css' => 'a989b5b3',
|
||||
@@ -68,7 +68,7 @@ return array(
|
||||
'rsrc/css/application/flag/flag.css' => '5337623f',
|
||||
'rsrc/css/application/harbormaster/harbormaster.css' => '49d64eb4',
|
||||
'rsrc/css/application/herald/herald-test.css' => '778b008e',
|
||||
'rsrc/css/application/herald/herald.css' => 'c544dd1c',
|
||||
'rsrc/css/application/herald/herald.css' => '826075fa',
|
||||
'rsrc/css/application/maniphest/batch-editor.css' => '8f380ebc',
|
||||
'rsrc/css/application/maniphest/report.css' => '6fc16517',
|
||||
'rsrc/css/application/maniphest/task-edit.css' => '8e23031b',
|
||||
@@ -85,7 +85,7 @@ return array(
|
||||
'rsrc/css/application/phortune/phortune.css' => '9149f103',
|
||||
'rsrc/css/application/phrequent/phrequent.css' => 'ffc185ad',
|
||||
'rsrc/css/application/phriction/phriction-document-css.css' => '7d7f0071',
|
||||
'rsrc/css/application/policy/policy-edit.css' => '05cca26a',
|
||||
'rsrc/css/application/policy/policy-edit.css' => '815c66f7',
|
||||
'rsrc/css/application/policy/policy-transaction-detail.css' => '82100a43',
|
||||
'rsrc/css/application/policy/policy.css' => '957ea14c',
|
||||
'rsrc/css/application/ponder/comments.css' => '6cdccea7',
|
||||
@@ -103,7 +103,7 @@ return array(
|
||||
'rsrc/css/application/tokens/tokens.css' => '3d0f239e',
|
||||
'rsrc/css/application/uiexample/example.css' => '528b19de',
|
||||
'rsrc/css/core/core.css' => '40151074',
|
||||
'rsrc/css/core/remarkup.css' => '45313445',
|
||||
'rsrc/css/core/remarkup.css' => '63c9a0a7',
|
||||
'rsrc/css/core/syntax.css' => '56c1ba38',
|
||||
'rsrc/css/core/z-index.css' => '44e1d311',
|
||||
'rsrc/css/diviner/diviner-shared.css' => '38813222',
|
||||
@@ -122,11 +122,11 @@ return array(
|
||||
'rsrc/css/phui/phui-action-header-view.css' => '89c497e7',
|
||||
'rsrc/css/phui/phui-action-list.css' => '9ee9910a',
|
||||
'rsrc/css/phui/phui-box.css' => '7b3a2eed',
|
||||
'rsrc/css/phui/phui-button.css' => 'c7412aa1',
|
||||
'rsrc/css/phui/phui-button.css' => '008ba5e2',
|
||||
'rsrc/css/phui/phui-document.css' => 'a5615198',
|
||||
'rsrc/css/phui/phui-feed-story.css' => '55dc7732',
|
||||
'rsrc/css/phui/phui-feed-story.css' => 'dd3c5ff5',
|
||||
'rsrc/css/phui/phui-fontkit.css' => '9c3d2dce',
|
||||
'rsrc/css/phui/phui-form-view.css' => '26e6016f',
|
||||
'rsrc/css/phui/phui-form-view.css' => '4ac1192d',
|
||||
'rsrc/css/phui/phui-form.css' => 'b78ec020',
|
||||
'rsrc/css/phui/phui-header-view.css' => '39594ac0',
|
||||
'rsrc/css/phui/phui-icon.css' => 'b4963a4f',
|
||||
@@ -140,9 +140,9 @@ return array(
|
||||
'rsrc/css/phui/phui-remarkup-preview.css' => '19ad512b',
|
||||
'rsrc/css/phui/phui-spacing.css' => '042804d6',
|
||||
'rsrc/css/phui/phui-status.css' => '888cedb8',
|
||||
'rsrc/css/phui/phui-tag-view.css' => 'b0c282e0',
|
||||
'rsrc/css/phui/phui-tag-view.css' => '6b74282b',
|
||||
'rsrc/css/phui/phui-text.css' => 'cf019f54',
|
||||
'rsrc/css/phui/phui-timeline-view.css' => '8c6fefe7',
|
||||
'rsrc/css/phui/phui-timeline-view.css' => '26bb3fd4',
|
||||
'rsrc/css/phui/phui-workboard-view.css' => '2bf82d00',
|
||||
'rsrc/css/phui/phui-workpanel-view.css' => '198c7e6c',
|
||||
'rsrc/css/sprite-apps-large.css' => '20ec0cc0',
|
||||
@@ -371,7 +371,7 @@ return array(
|
||||
'rsrc/js/application/differential/behavior-diff-radios.js' => 'e1ff79b1',
|
||||
'rsrc/js/application/differential/behavior-dropdown-menus.js' => '710f209e',
|
||||
'rsrc/js/application/differential/behavior-edit-inline-comments.js' => '00861799',
|
||||
'rsrc/js/application/differential/behavior-keyboard-nav.js' => '8d199d97',
|
||||
'rsrc/js/application/differential/behavior-keyboard-nav.js' => '2c426492',
|
||||
'rsrc/js/application/differential/behavior-populate.js' => 'bdb3e4d0',
|
||||
'rsrc/js/application/differential/behavior-show-all-comments.js' => '7c273581',
|
||||
'rsrc/js/application/differential/behavior-show-field-details.js' => 'bba9eedf',
|
||||
@@ -389,7 +389,7 @@ return array(
|
||||
'rsrc/js/application/doorkeeper/behavior-doorkeeper-tag.js' => 'e5822781',
|
||||
'rsrc/js/application/files/behavior-icon-composer.js' => '8ef9ab58',
|
||||
'rsrc/js/application/files/behavior-launch-icon-composer.js' => '48086888',
|
||||
'rsrc/js/application/herald/HeraldRuleEditor.js' => '3fc2c8f2',
|
||||
'rsrc/js/application/herald/HeraldRuleEditor.js' => '335fd41f',
|
||||
'rsrc/js/application/herald/PathTypeahead.js' => 'f7fc67ec',
|
||||
'rsrc/js/application/herald/herald-rule-editor.js' => '7ebaeed3',
|
||||
'rsrc/js/application/maniphest/behavior-batch-editor.js' => 'f588412e',
|
||||
@@ -445,13 +445,13 @@ return array(
|
||||
'rsrc/js/core/Hovercard.js' => '7e8468ae',
|
||||
'rsrc/js/core/KeyboardShortcut.js' => '1ae869f2',
|
||||
'rsrc/js/core/KeyboardShortcutManager.js' => 'ad7a69ca',
|
||||
'rsrc/js/core/MultirowRowManager.js' => '41e47dea',
|
||||
'rsrc/js/core/MultirowRowManager.js' => 'b5d57730',
|
||||
'rsrc/js/core/Notification.js' => '0c6946e7',
|
||||
'rsrc/js/core/Prefab.js' => 'bbae734c',
|
||||
'rsrc/js/core/ShapedRequest.js' => '7cbe244b',
|
||||
'rsrc/js/core/TextAreaUtils.js' => '5c93c52c',
|
||||
'rsrc/js/core/Title.js' => '5c1c758c',
|
||||
'rsrc/js/core/ToolTip.js' => '3915d490',
|
||||
'rsrc/js/core/ToolTip.js' => '031d4411',
|
||||
'rsrc/js/core/behavior-active-nav.js' => 'e379b58e',
|
||||
'rsrc/js/core/behavior-audio-source.js' => '59b251eb',
|
||||
'rsrc/js/core/behavior-autofocus.js' => '7319e029',
|
||||
@@ -483,7 +483,7 @@ return array(
|
||||
'rsrc/js/core/behavior-remarkup-preview.js' => 'f7379f45',
|
||||
'rsrc/js/core/behavior-reorder-applications.js' => '76b9fc3e',
|
||||
'rsrc/js/core/behavior-reveal-content.js' => '60821bc7',
|
||||
'rsrc/js/core/behavior-search-typeahead.js' => 'd712ac5f',
|
||||
'rsrc/js/core/behavior-search-typeahead.js' => '724b1247',
|
||||
'rsrc/js/core/behavior-select-on-click.js' => '4e3e79a6',
|
||||
'rsrc/js/core/behavior-toggle-class.js' => 'e566f52c',
|
||||
'rsrc/js/core/behavior-tokenizer.js' => 'b3a4b884',
|
||||
@@ -510,7 +510,7 @@ return array(
|
||||
'aphront-panel-view-css' => '5846dfa2',
|
||||
'aphront-table-view-css' => 'b22b7216',
|
||||
'aphront-tokenizer-control-css' => '82ce2142',
|
||||
'aphront-tooltip-css' => '9c90229d',
|
||||
'aphront-tooltip-css' => '4099b97e',
|
||||
'aphront-two-column-view-css' => '16ab3ad2',
|
||||
'aphront-typeahead-control-css' => 'a989b5b3',
|
||||
'auth-css' => '1e655982',
|
||||
@@ -538,8 +538,8 @@ return array(
|
||||
'font-source-sans-pro' => '91d53463',
|
||||
'global-drag-and-drop-css' => '697324ad',
|
||||
'harbormaster-css' => '49d64eb4',
|
||||
'herald-css' => 'c544dd1c',
|
||||
'herald-rule-editor' => '3fc2c8f2',
|
||||
'herald-css' => '826075fa',
|
||||
'herald-rule-editor' => '335fd41f',
|
||||
'herald-test-css' => '778b008e',
|
||||
'inline-comment-summary-css' => '8cfd34e8',
|
||||
'javelin-aphlict' => '4a07e8e3',
|
||||
@@ -574,7 +574,7 @@ return array(
|
||||
'javelin-behavior-differential-dropdown-menus' => '710f209e',
|
||||
'javelin-behavior-differential-edit-inline-comments' => '00861799',
|
||||
'javelin-behavior-differential-feedback-preview' => '6932def3',
|
||||
'javelin-behavior-differential-keyboard-navigation' => '8d199d97',
|
||||
'javelin-behavior-differential-keyboard-navigation' => '2c426492',
|
||||
'javelin-behavior-differential-populate' => 'bdb3e4d0',
|
||||
'javelin-behavior-differential-show-field-details' => 'bba9eedf',
|
||||
'javelin-behavior-differential-show-more' => 'dd7e8ef5',
|
||||
@@ -624,7 +624,7 @@ return array(
|
||||
'javelin-behavior-phabricator-oncopy' => '2926fff2',
|
||||
'javelin-behavior-phabricator-remarkup-assist' => 'e32d14ab',
|
||||
'javelin-behavior-phabricator-reveal-content' => '60821bc7',
|
||||
'javelin-behavior-phabricator-search-typeahead' => 'd712ac5f',
|
||||
'javelin-behavior-phabricator-search-typeahead' => '724b1247',
|
||||
'javelin-behavior-phabricator-show-all-transactions' => '7c273581',
|
||||
'javelin-behavior-phabricator-tooltips' => '3ee3408b',
|
||||
'javelin-behavior-phabricator-transaction-comment-form' => '9f7309fb',
|
||||
@@ -699,7 +699,7 @@ return array(
|
||||
'maniphest-report-css' => '6fc16517',
|
||||
'maniphest-task-edit-css' => '8e23031b',
|
||||
'maniphest-task-summary-css' => '13ed8360',
|
||||
'multirow-row-manager' => '41e47dea',
|
||||
'multirow-row-manager' => 'b5d57730',
|
||||
'owners-path-editor' => 'aa1733d0',
|
||||
'owners-path-editor-css' => '2f00933b',
|
||||
'paste-css' => 'aa1767d1',
|
||||
@@ -734,7 +734,7 @@ return array(
|
||||
'phabricator-phtize' => 'd254d646',
|
||||
'phabricator-prefab' => 'bbae734c',
|
||||
'phabricator-profile-css' => '28f433ef',
|
||||
'phabricator-remarkup-css' => '45313445',
|
||||
'phabricator-remarkup-css' => '63c9a0a7',
|
||||
'phabricator-search-results-css' => 'f240504c',
|
||||
'phabricator-shaped-request' => '7cbe244b',
|
||||
'phabricator-side-menu-view-css' => 'a2ccd7bd',
|
||||
@@ -743,7 +743,7 @@ return array(
|
||||
'phabricator-standard-page-view' => '3f5b9311',
|
||||
'phabricator-textareautils' => '5c93c52c',
|
||||
'phabricator-title' => '5c1c758c',
|
||||
'phabricator-tooltip' => '3915d490',
|
||||
'phabricator-tooltip' => '031d4411',
|
||||
'phabricator-transaction-view-css' => '5d0cae25',
|
||||
'phabricator-ui-example-css' => '528b19de',
|
||||
'phabricator-uiexample-javelin-view' => 'd4a14807',
|
||||
@@ -769,17 +769,17 @@ return array(
|
||||
'phriction-document-css' => '7d7f0071',
|
||||
'phui-action-header-view-css' => '89c497e7',
|
||||
'phui-box-css' => '7b3a2eed',
|
||||
'phui-button-css' => 'c7412aa1',
|
||||
'phui-button-css' => '008ba5e2',
|
||||
'phui-calendar-css' => '8675968e',
|
||||
'phui-calendar-day-css' => 'de035c8a',
|
||||
'phui-calendar-list-css' => 'c1d0ca59',
|
||||
'phui-calendar-month-css' => 'a92e47d2',
|
||||
'phui-document-view-css' => 'a5615198',
|
||||
'phui-feed-story-css' => '55dc7732',
|
||||
'phui-feed-story-css' => 'dd3c5ff5',
|
||||
'phui-font-icon-base-css' => '3dad2ae3',
|
||||
'phui-fontkit-css' => '9c3d2dce',
|
||||
'phui-form-css' => 'b78ec020',
|
||||
'phui-form-view-css' => '26e6016f',
|
||||
'phui-form-view-css' => '4ac1192d',
|
||||
'phui-header-view-css' => '39594ac0',
|
||||
'phui-icon-view-css' => 'b4963a4f',
|
||||
'phui-image-mask-css' => '5a8b09c8',
|
||||
@@ -792,16 +792,16 @@ return array(
|
||||
'phui-remarkup-preview-css' => '19ad512b',
|
||||
'phui-spacing-css' => '042804d6',
|
||||
'phui-status-list-view-css' => '888cedb8',
|
||||
'phui-tag-view-css' => 'b0c282e0',
|
||||
'phui-tag-view-css' => '6b74282b',
|
||||
'phui-text-css' => 'cf019f54',
|
||||
'phui-timeline-view-css' => '8c6fefe7',
|
||||
'phui-timeline-view-css' => '26bb3fd4',
|
||||
'phui-workboard-view-css' => '2bf82d00',
|
||||
'phui-workpanel-view-css' => '198c7e6c',
|
||||
'phuix-action-list-view' => 'b5c256b8',
|
||||
'phuix-action-view' => '6e8cefa4',
|
||||
'phuix-dropdown-menu' => 'bd4c8dca',
|
||||
'policy-css' => '957ea14c',
|
||||
'policy-edit-css' => '05cca26a',
|
||||
'policy-edit-css' => '815c66f7',
|
||||
'policy-transaction-detail-css' => '82100a43',
|
||||
'ponder-comment-table-css' => '6cdccea7',
|
||||
'ponder-feed-view-css' => 'e62615b6',
|
||||
@@ -842,6 +842,12 @@ return array(
|
||||
'029a133d' => array(
|
||||
'aphront-dialog-view-css',
|
||||
),
|
||||
'031d4411' => array(
|
||||
'javelin-install',
|
||||
'javelin-util',
|
||||
'javelin-dom',
|
||||
'javelin-vector',
|
||||
),
|
||||
'03d6ed07' => array(
|
||||
'javelin-behavior',
|
||||
'javelin-stratcom',
|
||||
@@ -1012,6 +1018,12 @@ return array(
|
||||
'javelin-stratcom',
|
||||
'javelin-dom',
|
||||
),
|
||||
'2c426492' => array(
|
||||
'javelin-behavior',
|
||||
'javelin-dom',
|
||||
'javelin-stratcom',
|
||||
'phabricator-keyboard-shortcut',
|
||||
),
|
||||
'2fa810fc' => array(
|
||||
'javelin-behavior',
|
||||
'javelin-dom',
|
||||
@@ -1022,6 +1034,15 @@ return array(
|
||||
'javelin-install',
|
||||
'javelin-typeahead-source',
|
||||
),
|
||||
'335fd41f' => array(
|
||||
'multirow-row-manager',
|
||||
'javelin-install',
|
||||
'javelin-util',
|
||||
'javelin-dom',
|
||||
'javelin-stratcom',
|
||||
'javelin-json',
|
||||
'phabricator-prefab',
|
||||
),
|
||||
'357b6e9b' => array(
|
||||
'javelin-behavior',
|
||||
'javelin-stratcom',
|
||||
@@ -1034,12 +1055,6 @@ return array(
|
||||
'javelin-behavior',
|
||||
'javelin-dom',
|
||||
),
|
||||
'3915d490' => array(
|
||||
'javelin-install',
|
||||
'javelin-util',
|
||||
'javelin-dom',
|
||||
'javelin-vector',
|
||||
),
|
||||
'3ab51e2c' => array(
|
||||
'javelin-behavior',
|
||||
'javelin-behavior-device',
|
||||
@@ -1067,15 +1082,6 @@ return array(
|
||||
'javelin-stratcom',
|
||||
'phabricator-tooltip',
|
||||
),
|
||||
'3fc2c8f2' => array(
|
||||
'multirow-row-manager',
|
||||
'javelin-install',
|
||||
'javelin-util',
|
||||
'javelin-dom',
|
||||
'javelin-stratcom',
|
||||
'javelin-json',
|
||||
'phabricator-prefab',
|
||||
),
|
||||
'40a6a403' => array(
|
||||
'javelin-install',
|
||||
'javelin-dom',
|
||||
@@ -1093,12 +1099,6 @@ return array(
|
||||
'phuix-action-list-view',
|
||||
'phuix-action-view',
|
||||
),
|
||||
'41e47dea' => array(
|
||||
'javelin-install',
|
||||
'javelin-stratcom',
|
||||
'javelin-dom',
|
||||
'javelin-util',
|
||||
),
|
||||
'44168bad' => array(
|
||||
'javelin-behavior',
|
||||
'javelin-dom',
|
||||
@@ -1281,6 +1281,16 @@ return array(
|
||||
'phabricator-phtize',
|
||||
'changeset-view-manager',
|
||||
),
|
||||
'724b1247' => array(
|
||||
'javelin-behavior',
|
||||
'javelin-typeahead-ondemand-source',
|
||||
'javelin-typeahead',
|
||||
'javelin-dom',
|
||||
'javelin-uri',
|
||||
'javelin-util',
|
||||
'javelin-stratcom',
|
||||
'phabricator-prefab',
|
||||
),
|
||||
'7319e029' => array(
|
||||
'javelin-behavior',
|
||||
'javelin-dom',
|
||||
@@ -1419,12 +1429,6 @@ return array(
|
||||
'javelin-uri',
|
||||
'phabricator-file-upload',
|
||||
),
|
||||
'8d199d97' => array(
|
||||
'javelin-behavior',
|
||||
'javelin-dom',
|
||||
'javelin-stratcom',
|
||||
'phabricator-keyboard-shortcut',
|
||||
),
|
||||
'8ef9ab58' => array(
|
||||
'javelin-behavior',
|
||||
'javelin-dom',
|
||||
@@ -1608,6 +1612,12 @@ return array(
|
||||
'javelin-install',
|
||||
'javelin-dom',
|
||||
),
|
||||
'b5d57730' => array(
|
||||
'javelin-install',
|
||||
'javelin-stratcom',
|
||||
'javelin-dom',
|
||||
'javelin-util',
|
||||
),
|
||||
'b6d401d6' => array(
|
||||
'javelin-dom',
|
||||
'javelin-dynval',
|
||||
@@ -1736,16 +1746,6 @@ return array(
|
||||
'javelin-stratcom',
|
||||
'javelin-dom',
|
||||
),
|
||||
'd712ac5f' => array(
|
||||
'javelin-behavior',
|
||||
'javelin-typeahead-ondemand-source',
|
||||
'javelin-typeahead',
|
||||
'javelin-dom',
|
||||
'javelin-uri',
|
||||
'javelin-util',
|
||||
'javelin-stratcom',
|
||||
'phabricator-prefab',
|
||||
),
|
||||
'd75709e6' => array(
|
||||
'javelin-behavior',
|
||||
'javelin-workflow',
|
||||
|
@@ -16,11 +16,16 @@ foreach (new LiskMigrationIterator($table) as $doc) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// project documents get the project policy
|
||||
if (PhrictionDocument::isProjectSlug($doc->getSlug())) {
|
||||
// If this was previously a magical project wiki page (under projects/, but
|
||||
// not projects/ itself) we need to apply the project policies. Otherwise,
|
||||
// apply the default policies.
|
||||
$slug = $doc->getSlug();
|
||||
$slug = PhabricatorSlug::normalize($slug);
|
||||
$prefix = 'projects/';
|
||||
if (($slug != $prefix) && (strncmp($slug, $prefix, strlen($prefix)) === 0)) {
|
||||
$parts = explode('/', $slug);
|
||||
$project_slug = $parts[1].'/';
|
||||
|
||||
$project_slug =
|
||||
PhrictionDocument::getProjectSlugIdentifier($doc->getSlug());
|
||||
$project_slugs = array($project_slug);
|
||||
$project = id(new PhabricatorProjectQuery())
|
||||
->setViewer(PhabricatorUser::getOmnipotentUser())
|
||||
|
22
resources/sql/autopatches/20141113.auditdupes.php
Normal file
22
resources/sql/autopatches/20141113.auditdupes.php
Normal file
@@ -0,0 +1,22 @@
|
||||
<?php
|
||||
|
||||
$table = new PhabricatorRepositoryAuditRequest();
|
||||
$conn_w = $table->establishConnection('w');
|
||||
|
||||
echo "Removing duplicate Audit requests...\n";
|
||||
$seen_audit_map = array();
|
||||
foreach (new LiskMigrationIterator($table) as $request) {
|
||||
$commit_phid = $request->getCommitPHID();
|
||||
$auditor_phid = $request->getAuditorPHID();
|
||||
if (isset($seen_audit_map[$commit_phid][$auditor_phid])) {
|
||||
$request->delete();
|
||||
}
|
||||
|
||||
if (!isset($seen_audit_map[$commit_phid])) {
|
||||
$seen_audit_map[$commit_phid] = array();
|
||||
}
|
||||
|
||||
$seen_audit_map[$commit_phid][$auditor_phid] = 1;
|
||||
}
|
||||
|
||||
echo "Done.\n";
|
19
resources/sql/autopatches/20141118.diffxaction.sql
Normal file
19
resources/sql/autopatches/20141118.diffxaction.sql
Normal file
@@ -0,0 +1,19 @@
|
||||
CREATE TABLE {$NAMESPACE}_differential.differential_difftransaction (
|
||||
id INT UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY,
|
||||
phid VARBINARY(64) NOT NULL,
|
||||
authorPHID VARBINARY(64) NOT NULL,
|
||||
objectPHID VARBINARY(64) NOT NULL,
|
||||
viewPolicy VARBINARY(64) NOT NULL,
|
||||
editPolicy VARBINARY(64) NOT NULL,
|
||||
commentPHID VARBINARY(64),
|
||||
commentVersion INT UNSIGNED NOT NULL,
|
||||
transactionType VARCHAR(32) COLLATE {$COLLATE_TEXT} NOT NULL,
|
||||
oldValue LONGTEXT COLLATE {$COLLATE_TEXT} NOT NULL,
|
||||
newValue LONGTEXT COLLATE {$COLLATE_TEXT} NOT NULL,
|
||||
contentSource LONGTEXT COLLATE {$COLLATE_TEXT} NOT NULL,
|
||||
metadata LONGTEXT COLLATE {$COLLATE_TEXT} NOT NULL,
|
||||
dateCreated INT UNSIGNED NOT NULL,
|
||||
dateModified INT UNSIGNED NOT NULL,
|
||||
UNIQUE KEY `key_phid` (`phid`),
|
||||
KEY `key_object` (`objectPHID`)
|
||||
) ENGINE=InnoDB, COLLATE {$COLLATE_TEXT};
|
11
resources/sql/autopatches/20141119.commitpedge.sql
Normal file
11
resources/sql/autopatches/20141119.commitpedge.sql
Normal file
@@ -0,0 +1,11 @@
|
||||
INSERT IGNORE INTO {$NAMESPACE}_repository.edge
|
||||
(src, type, dst, dateCreated, seq)
|
||||
SELECT src, 41, dst, dateCreated, seq
|
||||
FROM {$NAMESPACE}_repository.edge
|
||||
WHERE type = 15;
|
||||
|
||||
INSERT IGNORE INTO {$NAMESPACE}_project.edge
|
||||
(src, type, dst, dateCreated, seq)
|
||||
SELECT src, 42, dst, dateCreated, seq
|
||||
FROM {$NAMESPACE}_project.edge
|
||||
WHERE type = 16;
|
@@ -0,0 +1,5 @@
|
||||
ALTER TABLE {$NAMESPACE}_differential.differential_diff
|
||||
ADD viewPolicy VARBINARY(64) NOT NULL;
|
||||
|
||||
UPDATE {$NAMESPACE}_differential.differential_diff
|
||||
SET viewPolicy = 'users' WHERE viewPolicy = '';
|
2
resources/sql/autopatches/20141119.sshtrust.sql
Normal file
2
resources/sql/autopatches/20141119.sshtrust.sql
Normal file
@@ -0,0 +1,2 @@
|
||||
ALTER TABLE {$NAMESPACE}_auth.auth_sshkey
|
||||
ADD isTrusted BOOL NOT NULL;
|
2
resources/sql/autopatches/20141123.taskpriority.1.sql
Normal file
2
resources/sql/autopatches/20141123.taskpriority.1.sql
Normal file
@@ -0,0 +1,2 @@
|
||||
UPDATE {$NAMESPACE}_worker.worker_activetask
|
||||
SET priority = 5000 - priority;
|
2
resources/sql/autopatches/20141123.taskpriority.2.sql
Normal file
2
resources/sql/autopatches/20141123.taskpriority.2.sql
Normal file
@@ -0,0 +1,2 @@
|
||||
UPDATE {$NAMESPACE}_worker.worker_archivetask
|
||||
SET priority = 5000 - priority;
|
21
scripts/setup/manage_worker.php
Executable file
21
scripts/setup/manage_worker.php
Executable file
@@ -0,0 +1,21 @@
|
||||
#!/usr/bin/env php
|
||||
<?php
|
||||
|
||||
$root = dirname(dirname(dirname(__FILE__)));
|
||||
require_once $root.'/scripts/__init_script__.php';
|
||||
|
||||
$args = new PhutilArgumentParser($argv);
|
||||
$args->setTagline('manage task queue');
|
||||
$args->setSynopsis(<<<EOSYNOPSIS
|
||||
**worker** __command__ [__options__]
|
||||
Manage the task queue.
|
||||
|
||||
EOSYNOPSIS
|
||||
);
|
||||
$args->parseStandardArguments();
|
||||
|
||||
$workflows = id(new PhutilSymbolLoader())
|
||||
->setAncestorClass('PhabricatorWorkerManagementWorkflow')
|
||||
->loadObjects();
|
||||
$workflows[] = new PhutilHelpArgumentWorkflow();
|
||||
$args->parseWorkflows($workflows);
|
@@ -19,7 +19,7 @@ phutil_register_library_map(array(
|
||||
'AlmanacBindingTransaction' => 'applications/almanac/storage/AlmanacBindingTransaction.php',
|
||||
'AlmanacBindingTransactionQuery' => 'applications/almanac/query/AlmanacBindingTransactionQuery.php',
|
||||
'AlmanacBindingViewController' => 'applications/almanac/controller/AlmanacBindingViewController.php',
|
||||
'AlmanacConduitUtil' => 'applications/almanac/util/AlmanacConduitUtil.php',
|
||||
'AlmanacConduitAPIMethod' => 'applications/almanac/conduit/AlmanacConduitAPIMethod.php',
|
||||
'AlmanacConsoleController' => 'applications/almanac/controller/AlmanacConsoleController.php',
|
||||
'AlmanacController' => 'applications/almanac/controller/AlmanacController.php',
|
||||
'AlmanacCoreCustomField' => 'applications/almanac/customfield/AlmanacCoreCustomField.php',
|
||||
@@ -45,7 +45,8 @@ phutil_register_library_map(array(
|
||||
'AlmanacInterfacePHIDType' => 'applications/almanac/phid/AlmanacInterfacePHIDType.php',
|
||||
'AlmanacInterfaceQuery' => 'applications/almanac/query/AlmanacInterfaceQuery.php',
|
||||
'AlmanacInterfaceTableView' => 'applications/almanac/view/AlmanacInterfaceTableView.php',
|
||||
'AlmanacManagementRegisterWorkflow' => 'applications/almanac/management/AlmanacManagementRegisterWorkflow.php',
|
||||
'AlmanacManagementTrustKeyWorkflow' => 'applications/almanac/management/AlmanacManagementTrustKeyWorkflow.php',
|
||||
'AlmanacManagementUntrustKeyWorkflow' => 'applications/almanac/management/AlmanacManagementUntrustKeyWorkflow.php',
|
||||
'AlmanacManagementWorkflow' => 'applications/almanac/management/AlmanacManagementWorkflow.php',
|
||||
'AlmanacNames' => 'applications/almanac/util/AlmanacNames.php',
|
||||
'AlmanacNamesTestCase' => 'applications/almanac/util/__tests__/AlmanacNamesTestCase.php',
|
||||
@@ -66,6 +67,7 @@ phutil_register_library_map(array(
|
||||
'AlmanacPropertyInterface' => 'applications/almanac/property/AlmanacPropertyInterface.php',
|
||||
'AlmanacPropertyQuery' => 'applications/almanac/query/AlmanacPropertyQuery.php',
|
||||
'AlmanacQuery' => 'applications/almanac/query/AlmanacQuery.php',
|
||||
'AlmanacQueryServicesConduitAPIMethod' => 'applications/almanac/conduit/AlmanacQueryServicesConduitAPIMethod.php',
|
||||
'AlmanacSchemaSpec' => 'applications/almanac/storage/AlmanacSchemaSpec.php',
|
||||
'AlmanacService' => 'applications/almanac/storage/AlmanacService.php',
|
||||
'AlmanacServiceController' => 'applications/almanac/controller/AlmanacServiceController.php',
|
||||
@@ -323,6 +325,7 @@ phutil_register_library_map(array(
|
||||
'DifferentialDiffQuery' => 'applications/differential/query/DifferentialDiffQuery.php',
|
||||
'DifferentialDiffTableOfContentsView' => 'applications/differential/view/DifferentialDiffTableOfContentsView.php',
|
||||
'DifferentialDiffTestCase' => 'applications/differential/storage/__tests__/DifferentialDiffTestCase.php',
|
||||
'DifferentialDiffTransaction' => 'applications/differential/storage/DifferentialDiffTransaction.php',
|
||||
'DifferentialDiffViewController' => 'applications/differential/controller/DifferentialDiffViewController.php',
|
||||
'DifferentialDoorkeeperRevisionFeedStoryPublisher' => 'applications/differential/doorkeeper/DifferentialDoorkeeperRevisionFeedStoryPublisher.php',
|
||||
'DifferentialDraft' => 'applications/differential/storage/DifferentialDraft.php',
|
||||
@@ -722,6 +725,7 @@ phutil_register_library_map(array(
|
||||
'FileReplyHandler' => 'applications/files/mail/FileReplyHandler.php',
|
||||
'FileUploadConduitAPIMethod' => 'applications/files/conduit/FileUploadConduitAPIMethod.php',
|
||||
'FileUploadHashConduitAPIMethod' => 'applications/files/conduit/FileUploadHashConduitAPIMethod.php',
|
||||
'FilesDefaultViewCapability' => 'applications/files/capability/FilesDefaultViewCapability.php',
|
||||
'FlagConduitAPIMethod' => 'applications/flag/conduit/FlagConduitAPIMethod.php',
|
||||
'FlagDeleteConduitAPIMethod' => 'applications/flag/conduit/FlagDeleteConduitAPIMethod.php',
|
||||
'FlagEditConduitAPIMethod' => 'applications/flag/conduit/FlagEditConduitAPIMethod.php',
|
||||
@@ -1015,6 +1019,7 @@ phutil_register_library_map(array(
|
||||
'ManiphestTaskResultListView' => 'applications/maniphest/view/ManiphestTaskResultListView.php',
|
||||
'ManiphestTaskSearchEngine' => 'applications/maniphest/query/ManiphestTaskSearchEngine.php',
|
||||
'ManiphestTaskStatus' => 'applications/maniphest/constants/ManiphestTaskStatus.php',
|
||||
'ManiphestTaskStatusDatasource' => 'applications/maniphest/typeahead/ManiphestTaskStatusDatasource.php',
|
||||
'ManiphestTaskStatusTestCase' => 'applications/maniphest/constants/__tests__/ManiphestTaskStatusTestCase.php',
|
||||
'ManiphestTaskSubscriber' => 'applications/maniphest/storage/ManiphestTaskSubscriber.php',
|
||||
'ManiphestTransaction' => 'applications/maniphest/storage/ManiphestTransaction.php',
|
||||
@@ -1321,8 +1326,13 @@ phutil_register_library_map(array(
|
||||
'PhabricatorAuthRegisterController' => 'applications/auth/controller/PhabricatorAuthRegisterController.php',
|
||||
'PhabricatorAuthRevokeTokenController' => 'applications/auth/controller/PhabricatorAuthRevokeTokenController.php',
|
||||
'PhabricatorAuthSSHKey' => 'applications/auth/storage/PhabricatorAuthSSHKey.php',
|
||||
'PhabricatorAuthSSHKeyController' => 'applications/auth/controller/PhabricatorAuthSSHKeyController.php',
|
||||
'PhabricatorAuthSSHKeyDeleteController' => 'applications/auth/controller/PhabricatorAuthSSHKeyDeleteController.php',
|
||||
'PhabricatorAuthSSHKeyEditController' => 'applications/auth/controller/PhabricatorAuthSSHKeyEditController.php',
|
||||
'PhabricatorAuthSSHKeyGenerateController' => 'applications/auth/controller/PhabricatorAuthSSHKeyGenerateController.php',
|
||||
'PhabricatorAuthSSHKeyQuery' => 'applications/auth/query/PhabricatorAuthSSHKeyQuery.php',
|
||||
'PhabricatorAuthSSHPublicKey' => 'applications/auth/storage/PhabricatorAuthSSHPublicKey.php',
|
||||
'PhabricatorAuthSSHKeyTableView' => 'applications/auth/view/PhabricatorAuthSSHKeyTableView.php',
|
||||
'PhabricatorAuthSSHPublicKey' => 'applications/auth/sshkey/PhabricatorAuthSSHPublicKey.php',
|
||||
'PhabricatorAuthSession' => 'applications/auth/storage/PhabricatorAuthSession.php',
|
||||
'PhabricatorAuthSessionEngine' => 'applications/auth/engine/PhabricatorAuthSessionEngine.php',
|
||||
'PhabricatorAuthSessionGarbageCollector' => 'applications/auth/garbagecollector/PhabricatorAuthSessionGarbageCollector.php',
|
||||
@@ -2140,6 +2150,7 @@ phutil_register_library_map(array(
|
||||
'PhabricatorProjectUIEventListener' => 'applications/project/events/PhabricatorProjectUIEventListener.php',
|
||||
'PhabricatorProjectUpdateController' => 'applications/project/controller/PhabricatorProjectUpdateController.php',
|
||||
'PhabricatorProjectWatchController' => 'applications/project/controller/PhabricatorProjectWatchController.php',
|
||||
'PhabricatorProjectWikiExplainController' => 'applications/project/controller/PhabricatorProjectWikiExplainController.php',
|
||||
'PhabricatorQuery' => 'infrastructure/query/PhabricatorQuery.php',
|
||||
'PhabricatorRecaptchaConfigOptions' => 'applications/config/option/PhabricatorRecaptchaConfigOptions.php',
|
||||
'PhabricatorRedirectController' => 'applications/base/controller/PhabricatorRedirectController.php',
|
||||
@@ -2253,6 +2264,7 @@ phutil_register_library_map(array(
|
||||
'PhabricatorSSHKeyGenerator' => 'infrastructure/util/PhabricatorSSHKeyGenerator.php',
|
||||
'PhabricatorSSHLog' => 'infrastructure/log/PhabricatorSSHLog.php',
|
||||
'PhabricatorSSHPassthruCommand' => 'infrastructure/ssh/PhabricatorSSHPassthruCommand.php',
|
||||
'PhabricatorSSHPublicKeyInterface' => 'applications/auth/sshkey/PhabricatorSSHPublicKeyInterface.php',
|
||||
'PhabricatorSSHWorkflow' => 'infrastructure/ssh/PhabricatorSSHWorkflow.php',
|
||||
'PhabricatorSavedQuery' => 'applications/search/storage/PhabricatorSavedQuery.php',
|
||||
'PhabricatorSavedQueryQuery' => 'applications/search/query/PhabricatorSavedQueryQuery.php',
|
||||
@@ -2508,6 +2520,8 @@ phutil_register_library_map(array(
|
||||
'PhabricatorWorkerArchiveTask' => 'infrastructure/daemon/workers/storage/PhabricatorWorkerArchiveTask.php',
|
||||
'PhabricatorWorkerDAO' => 'infrastructure/daemon/workers/storage/PhabricatorWorkerDAO.php',
|
||||
'PhabricatorWorkerLeaseQuery' => 'infrastructure/daemon/workers/query/PhabricatorWorkerLeaseQuery.php',
|
||||
'PhabricatorWorkerManagementFloodWorkflow' => 'infrastructure/daemon/workers/management/PhabricatorWorkerManagementFloodWorkflow.php',
|
||||
'PhabricatorWorkerManagementWorkflow' => 'infrastructure/daemon/workers/management/PhabricatorWorkerManagementWorkflow.php',
|
||||
'PhabricatorWorkerPermanentFailureException' => 'infrastructure/daemon/workers/exception/PhabricatorWorkerPermanentFailureException.php',
|
||||
'PhabricatorWorkerTask' => 'infrastructure/daemon/workers/storage/PhabricatorWorkerTask.php',
|
||||
'PhabricatorWorkerTaskData' => 'infrastructure/daemon/workers/storage/PhabricatorWorkerTaskData.php',
|
||||
@@ -2772,11 +2786,11 @@ phutil_register_library_map(array(
|
||||
'PhrictionDiffController' => 'applications/phriction/controller/PhrictionDiffController.php',
|
||||
'PhrictionDocument' => 'applications/phriction/storage/PhrictionDocument.php',
|
||||
'PhrictionDocumentController' => 'applications/phriction/controller/PhrictionDocumentController.php',
|
||||
'PhrictionDocumentHeraldAdapter' => 'applications/phriction/herald/PhrictionDocumentHeraldAdapter.php',
|
||||
'PhrictionDocumentPHIDType' => 'applications/phriction/phid/PhrictionDocumentPHIDType.php',
|
||||
'PhrictionDocumentPreviewController' => 'applications/phriction/controller/PhrictionDocumentPreviewController.php',
|
||||
'PhrictionDocumentQuery' => 'applications/phriction/query/PhrictionDocumentQuery.php',
|
||||
'PhrictionDocumentStatus' => 'applications/phriction/constants/PhrictionDocumentStatus.php',
|
||||
'PhrictionDocumentTestCase' => 'applications/phriction/storage/__tests__/PhrictionDocumentTestCase.php',
|
||||
'PhrictionEditConduitAPIMethod' => 'applications/phriction/conduit/PhrictionEditConduitAPIMethod.php',
|
||||
'PhrictionEditController' => 'applications/phriction/controller/PhrictionEditController.php',
|
||||
'PhrictionHistoryConduitAPIMethod' => 'applications/phriction/conduit/PhrictionHistoryConduitAPIMethod.php',
|
||||
@@ -2841,6 +2855,9 @@ phutil_register_library_map(array(
|
||||
'ProjectConduitAPIMethod' => 'applications/project/conduit/ProjectConduitAPIMethod.php',
|
||||
'ProjectCreateConduitAPIMethod' => 'applications/project/conduit/ProjectCreateConduitAPIMethod.php',
|
||||
'ProjectCreateProjectsCapability' => 'applications/project/capability/ProjectCreateProjectsCapability.php',
|
||||
'ProjectDefaultEditCapability' => 'applications/project/capability/ProjectDefaultEditCapability.php',
|
||||
'ProjectDefaultJoinCapability' => 'applications/project/capability/ProjectDefaultJoinCapability.php',
|
||||
'ProjectDefaultViewCapability' => 'applications/project/capability/ProjectDefaultViewCapability.php',
|
||||
'ProjectQueryConduitAPIMethod' => 'applications/project/conduit/ProjectQueryConduitAPIMethod.php',
|
||||
'ProjectRemarkupRule' => 'applications/project/remarkup/ProjectRemarkupRule.php',
|
||||
'ProjectRemarkupRuleTestCase' => 'applications/project/remarkup/__tests__/ProjectRemarkupRuleTestCase.php',
|
||||
@@ -2993,7 +3010,7 @@ phutil_register_library_map(array(
|
||||
'AlmanacBindingTransaction' => 'PhabricatorApplicationTransaction',
|
||||
'AlmanacBindingTransactionQuery' => 'PhabricatorApplicationTransactionQuery',
|
||||
'AlmanacBindingViewController' => 'AlmanacServiceController',
|
||||
'AlmanacConduitUtil' => 'Phobject',
|
||||
'AlmanacConduitAPIMethod' => 'ConduitAPIMethod',
|
||||
'AlmanacConsoleController' => 'AlmanacController',
|
||||
'AlmanacController' => 'PhabricatorController',
|
||||
'AlmanacCoreCustomField' => array(
|
||||
@@ -3011,6 +3028,7 @@ phutil_register_library_map(array(
|
||||
'PhabricatorCustomFieldInterface',
|
||||
'PhabricatorApplicationTransactionInterface',
|
||||
'PhabricatorProjectInterface',
|
||||
'PhabricatorSSHPublicKeyInterface',
|
||||
'AlmanacPropertyInterface',
|
||||
),
|
||||
'AlmanacDeviceController' => 'AlmanacController',
|
||||
@@ -3032,7 +3050,8 @@ phutil_register_library_map(array(
|
||||
'AlmanacInterfacePHIDType' => 'PhabricatorPHIDType',
|
||||
'AlmanacInterfaceQuery' => 'PhabricatorCursorPagedPolicyAwareQuery',
|
||||
'AlmanacInterfaceTableView' => 'AphrontView',
|
||||
'AlmanacManagementRegisterWorkflow' => 'AlmanacManagementWorkflow',
|
||||
'AlmanacManagementTrustKeyWorkflow' => 'AlmanacManagementWorkflow',
|
||||
'AlmanacManagementUntrustKeyWorkflow' => 'AlmanacManagementWorkflow',
|
||||
'AlmanacManagementWorkflow' => 'PhabricatorManagementWorkflow',
|
||||
'AlmanacNames' => 'Phobject',
|
||||
'AlmanacNamesTestCase' => 'PhabricatorTestCase',
|
||||
@@ -3058,6 +3077,7 @@ phutil_register_library_map(array(
|
||||
'AlmanacPropertyEditController' => 'AlmanacDeviceController',
|
||||
'AlmanacPropertyQuery' => 'PhabricatorCursorPagedPolicyAwareQuery',
|
||||
'AlmanacQuery' => 'PhabricatorCursorPagedPolicyAwareQuery',
|
||||
'AlmanacQueryServicesConduitAPIMethod' => 'AlmanacConduitAPIMethod',
|
||||
'AlmanacSchemaSpec' => 'PhabricatorConfigSchemaSpec',
|
||||
'AlmanacService' => array(
|
||||
'AlmanacDAO',
|
||||
@@ -3305,12 +3325,13 @@ phutil_register_library_map(array(
|
||||
),
|
||||
'DifferentialDiffCreateController' => 'DifferentialController',
|
||||
'DifferentialDiffCreationRejectException' => 'Exception',
|
||||
'DifferentialDiffEditor' => 'PhabricatorEditor',
|
||||
'DifferentialDiffEditor' => 'PhabricatorApplicationTransactionEditor',
|
||||
'DifferentialDiffPHIDType' => 'PhabricatorPHIDType',
|
||||
'DifferentialDiffProperty' => 'DifferentialDAO',
|
||||
'DifferentialDiffQuery' => 'PhabricatorCursorPagedPolicyAwareQuery',
|
||||
'DifferentialDiffTableOfContentsView' => 'AphrontView',
|
||||
'DifferentialDiffTestCase' => 'ArcanistPhutilTestCase',
|
||||
'DifferentialDiffTransaction' => 'PhabricatorApplicationTransaction',
|
||||
'DifferentialDiffViewController' => 'DifferentialController',
|
||||
'DifferentialDoorkeeperRevisionFeedStoryPublisher' => 'DoorkeeperFeedStoryPublisher',
|
||||
'DifferentialDraft' => 'DifferentialDAO',
|
||||
@@ -3716,6 +3737,7 @@ phutil_register_library_map(array(
|
||||
'FileReplyHandler' => 'PhabricatorMailReplyHandler',
|
||||
'FileUploadConduitAPIMethod' => 'FileConduitAPIMethod',
|
||||
'FileUploadHashConduitAPIMethod' => 'FileConduitAPIMethod',
|
||||
'FilesDefaultViewCapability' => 'PhabricatorPolicyCapability',
|
||||
'FlagConduitAPIMethod' => 'ConduitAPIMethod',
|
||||
'FlagDeleteConduitAPIMethod' => 'FlagConduitAPIMethod',
|
||||
'FlagEditConduitAPIMethod' => 'FlagConduitAPIMethod',
|
||||
@@ -4072,6 +4094,7 @@ phutil_register_library_map(array(
|
||||
'ManiphestTaskResultListView' => 'ManiphestView',
|
||||
'ManiphestTaskSearchEngine' => 'PhabricatorApplicationSearchEngine',
|
||||
'ManiphestTaskStatus' => 'ManiphestConstants',
|
||||
'ManiphestTaskStatusDatasource' => 'PhabricatorTypeaheadDatasource',
|
||||
'ManiphestTaskStatusTestCase' => 'PhabricatorTestCase',
|
||||
'ManiphestTaskSubscriber' => 'ManiphestDAO',
|
||||
'ManiphestTransaction' => 'PhabricatorApplicationTransaction',
|
||||
@@ -4394,7 +4417,12 @@ phutil_register_library_map(array(
|
||||
'PhabricatorAuthDAO',
|
||||
'PhabricatorPolicyInterface',
|
||||
),
|
||||
'PhabricatorAuthSSHKeyController' => 'PhabricatorAuthController',
|
||||
'PhabricatorAuthSSHKeyDeleteController' => 'PhabricatorAuthSSHKeyController',
|
||||
'PhabricatorAuthSSHKeyEditController' => 'PhabricatorAuthSSHKeyController',
|
||||
'PhabricatorAuthSSHKeyGenerateController' => 'PhabricatorAuthSSHKeyController',
|
||||
'PhabricatorAuthSSHKeyQuery' => 'PhabricatorCursorPagedPolicyAwareQuery',
|
||||
'PhabricatorAuthSSHKeyTableView' => 'AphrontView',
|
||||
'PhabricatorAuthSSHPublicKey' => 'Phobject',
|
||||
'PhabricatorAuthSession' => array(
|
||||
'PhabricatorAuthDAO',
|
||||
@@ -5259,6 +5287,7 @@ phutil_register_library_map(array(
|
||||
'PhabricatorProjectUIEventListener' => 'PhabricatorEventListener',
|
||||
'PhabricatorProjectUpdateController' => 'PhabricatorProjectController',
|
||||
'PhabricatorProjectWatchController' => 'PhabricatorProjectController',
|
||||
'PhabricatorProjectWikiExplainController' => 'PhabricatorProjectController',
|
||||
'PhabricatorRecaptchaConfigOptions' => 'PhabricatorApplicationConfigOptions',
|
||||
'PhabricatorRedirectController' => 'PhabricatorController',
|
||||
'PhabricatorRefreshCSRFController' => 'PhabricatorAuthController',
|
||||
@@ -5299,6 +5328,7 @@ phutil_register_library_map(array(
|
||||
'PhabricatorRepositoryDAO',
|
||||
'PhabricatorPolicyInterface',
|
||||
'PhabricatorFlaggableInterface',
|
||||
'PhabricatorProjectInterface',
|
||||
'PhabricatorTokenReceiverInterface',
|
||||
'PhabricatorSubscribableInterface',
|
||||
'PhabricatorMentionableInterface',
|
||||
@@ -5618,6 +5648,7 @@ phutil_register_library_map(array(
|
||||
'PhabricatorPolicyInterface',
|
||||
'PhabricatorCustomFieldInterface',
|
||||
'PhabricatorDestructibleInterface',
|
||||
'PhabricatorSSHPublicKeyInterface',
|
||||
),
|
||||
'PhabricatorUserBlurbField' => 'PhabricatorUserCustomField',
|
||||
'PhabricatorUserConfigOptions' => 'PhabricatorApplicationConfigOptions',
|
||||
@@ -5657,6 +5688,8 @@ phutil_register_library_map(array(
|
||||
'PhabricatorWorkerArchiveTask' => 'PhabricatorWorkerTask',
|
||||
'PhabricatorWorkerDAO' => 'PhabricatorLiskDAO',
|
||||
'PhabricatorWorkerLeaseQuery' => 'PhabricatorQuery',
|
||||
'PhabricatorWorkerManagementFloodWorkflow' => 'PhabricatorWorkerManagementWorkflow',
|
||||
'PhabricatorWorkerManagementWorkflow' => 'PhabricatorManagementWorkflow',
|
||||
'PhabricatorWorkerPermanentFailureException' => 'Exception',
|
||||
'PhabricatorWorkerTask' => 'PhabricatorWorkerDAO',
|
||||
'PhabricatorWorkerTaskData' => 'PhabricatorWorkerDAO',
|
||||
@@ -5986,13 +6019,14 @@ phutil_register_library_map(array(
|
||||
'PhabricatorFlaggableInterface',
|
||||
'PhabricatorTokenReceiverInterface',
|
||||
'PhabricatorDestructibleInterface',
|
||||
'PhabricatorApplicationTransactionInterface',
|
||||
),
|
||||
'PhrictionDocumentController' => 'PhrictionController',
|
||||
'PhrictionDocumentHeraldAdapter' => 'HeraldAdapter',
|
||||
'PhrictionDocumentPHIDType' => 'PhabricatorPHIDType',
|
||||
'PhrictionDocumentPreviewController' => 'PhrictionController',
|
||||
'PhrictionDocumentQuery' => 'PhabricatorCursorPagedPolicyAwareQuery',
|
||||
'PhrictionDocumentStatus' => 'PhrictionConstants',
|
||||
'PhrictionDocumentTestCase' => 'PhabricatorTestCase',
|
||||
'PhrictionEditConduitAPIMethod' => 'PhrictionConduitAPIMethod',
|
||||
'PhrictionEditController' => 'PhrictionController',
|
||||
'PhrictionHistoryConduitAPIMethod' => 'PhrictionConduitAPIMethod',
|
||||
@@ -6073,6 +6107,9 @@ phutil_register_library_map(array(
|
||||
'ProjectConduitAPIMethod' => 'ConduitAPIMethod',
|
||||
'ProjectCreateConduitAPIMethod' => 'ProjectConduitAPIMethod',
|
||||
'ProjectCreateProjectsCapability' => 'PhabricatorPolicyCapability',
|
||||
'ProjectDefaultEditCapability' => 'PhabricatorPolicyCapability',
|
||||
'ProjectDefaultJoinCapability' => 'PhabricatorPolicyCapability',
|
||||
'ProjectDefaultViewCapability' => 'PhabricatorPolicyCapability',
|
||||
'ProjectQueryConduitAPIMethod' => 'ProjectConduitAPIMethod',
|
||||
'ProjectRemarkupRule' => 'PhabricatorObjectRemarkupRule',
|
||||
'ProjectRemarkupRuleTestCase' => 'PhabricatorTestCase',
|
||||
|
20
src/applications/almanac/conduit/AlmanacConduitAPIMethod.php
Normal file
20
src/applications/almanac/conduit/AlmanacConduitAPIMethod.php
Normal file
@@ -0,0 +1,20 @@
|
||||
<?php
|
||||
|
||||
abstract class AlmanacConduitAPIMethod extends ConduitAPIMethod {
|
||||
|
||||
final public function getApplication() {
|
||||
return PhabricatorApplication::getByClass(
|
||||
'PhabricatorAlmanacApplication');
|
||||
}
|
||||
|
||||
public function getMethodStatus() {
|
||||
return self::METHOD_STATUS_UNSTABLE;
|
||||
}
|
||||
|
||||
public function getMethodStatusDescription() {
|
||||
return pht(
|
||||
'Almanac is a prototype application and its APIs are '.
|
||||
'subject to change.');
|
||||
}
|
||||
|
||||
}
|
@@ -0,0 +1,142 @@
|
||||
<?php
|
||||
|
||||
final class AlmanacQueryServicesConduitAPIMethod
|
||||
extends AlmanacConduitAPIMethod {
|
||||
|
||||
public function getAPIMethodName() {
|
||||
return 'almanac.queryservices';
|
||||
}
|
||||
|
||||
public function getMethodDescription() {
|
||||
return pht('Query Almanac services.');
|
||||
}
|
||||
|
||||
public function defineParamTypes() {
|
||||
return array(
|
||||
'ids' => 'optional list<id>',
|
||||
'phids' => 'optional list<phid>',
|
||||
'names' => 'optional list<phid>',
|
||||
) + self::getPagerParamTypes();
|
||||
}
|
||||
|
||||
public function defineReturnType() {
|
||||
return 'list<wild>';
|
||||
}
|
||||
|
||||
public function defineErrorTypes() {
|
||||
return array();
|
||||
}
|
||||
|
||||
protected function execute(ConduitAPIRequest $request) {
|
||||
$viewer = $request->getUser();
|
||||
|
||||
$query = id(new AlmanacServiceQuery())
|
||||
->setViewer($viewer);
|
||||
|
||||
$ids = $request->getValue('ids');
|
||||
if ($ids !== null) {
|
||||
$query->withIDs($ids);
|
||||
}
|
||||
|
||||
$phids = $request->getValue('phids');
|
||||
if ($phids !== null) {
|
||||
$query->withPHIDs($phids);
|
||||
}
|
||||
|
||||
$names = $request->getValue('names');
|
||||
if ($names !== null) {
|
||||
$query->withNames($names);
|
||||
}
|
||||
|
||||
$pager = $this->newPager($request);
|
||||
|
||||
$services = $query->executeWithCursorPager($pager);
|
||||
|
||||
if ($services) {
|
||||
$bindings = id(new AlmanacBindingQuery())
|
||||
->setViewer($viewer)
|
||||
->withServicePHIDs(mpull($services, 'getPHID'))
|
||||
->execute();
|
||||
$bindings = mgroup($bindings, 'getServicePHID');
|
||||
} else {
|
||||
$bindings = array();
|
||||
}
|
||||
|
||||
$data = array();
|
||||
foreach ($services as $service) {
|
||||
$phid = $service->getPHID();
|
||||
|
||||
$properties = $service->getAlmanacProperties();
|
||||
$properties = mpull($properties, 'getFieldValue', 'getFieldName');
|
||||
|
||||
$service_bindings = idx($bindings, $phid, array());
|
||||
$service_bindings = array_values($service_bindings);
|
||||
foreach ($service_bindings as $key => $service_binding) {
|
||||
$service_bindings[$key] = $this->getBindingDictionary($service_binding);
|
||||
}
|
||||
|
||||
$data[] = $this->getServiceDictionary($service) + array(
|
||||
'bindings' => $service_bindings,
|
||||
);
|
||||
}
|
||||
|
||||
$results = array(
|
||||
'data' => $data,
|
||||
);
|
||||
|
||||
return $this->addPagerResults($results, $pager);
|
||||
}
|
||||
|
||||
private function getServiceDictionary(AlmanacService $service) {
|
||||
return array(
|
||||
'id' => (int)$service->getID(),
|
||||
'phid' => $service->getPHID(),
|
||||
'name' => $service->getName(),
|
||||
'uri' => PhabricatorEnv::getProductionURI($service->getURI()),
|
||||
'properties' => $this->getPropertiesDictionary($service),
|
||||
);
|
||||
}
|
||||
|
||||
private function getBindingDictionary(AlmanacBinding $binding) {
|
||||
return array(
|
||||
'id' => (int)$binding->getID(),
|
||||
'phid' => $binding->getPHID(),
|
||||
'properties' => $this->getPropertiesDictionary($binding),
|
||||
'interface' => $this->getInterfaceDictionary($binding->getInterface()),
|
||||
);
|
||||
}
|
||||
|
||||
private function getPropertiesDictionary(AlmanacPropertyInterface $obj) {
|
||||
$properties = $obj->getAlmanacProperties();
|
||||
return (object)mpull($properties, 'getFieldValue', 'getFieldName');
|
||||
}
|
||||
|
||||
private function getInterfaceDictionary(AlmanacInterface $interface) {
|
||||
return array(
|
||||
'id' => (int)$interface->getID(),
|
||||
'phid' => $interface->getPHID(),
|
||||
'address' => $interface->getAddress(),
|
||||
'port' => (int)$interface->getPort(),
|
||||
'device' => $this->getDeviceDictionary($interface->getDevice()),
|
||||
'network' => $this->getNetworkDictionary($interface->getNetwork()),
|
||||
);
|
||||
}
|
||||
|
||||
private function getDeviceDictionary(AlmanacDevice $device) {
|
||||
return array(
|
||||
'id' => (int)$device->getID(),
|
||||
'phid' => $device->getPHID(),
|
||||
'name' => $device->getName(),
|
||||
'properties' => $this->getPropertiesDictionary($device),
|
||||
);
|
||||
}
|
||||
|
||||
private function getNetworkDictionary(AlmanacNetwork $network) {
|
||||
return array(
|
||||
'id' => (int)$network->getID(),
|
||||
'phid' => $network->getPHID(),
|
||||
'name' => $network->getName(),
|
||||
);
|
||||
}
|
||||
|
||||
}
|
@@ -57,6 +57,7 @@ final class AlmanacDeviceViewController
|
||||
$box,
|
||||
$interfaces,
|
||||
$this->buildAlmanacPropertiesTable($device),
|
||||
$this->buildSSHKeysTable($device),
|
||||
$xaction_view,
|
||||
),
|
||||
array(
|
||||
@@ -141,4 +142,67 @@ final class AlmanacDeviceViewController
|
||||
->appendChild($table);
|
||||
}
|
||||
|
||||
private function buildSSHKeysTable(AlmanacDevice $device) {
|
||||
$viewer = $this->getViewer();
|
||||
$id = $device->getID();
|
||||
$device_phid = $device->getPHID();
|
||||
|
||||
$can_edit = PhabricatorPolicyFilter::hasCapability(
|
||||
$viewer,
|
||||
$device,
|
||||
PhabricatorPolicyCapability::CAN_EDIT);
|
||||
|
||||
$keys = id(new PhabricatorAuthSSHKeyQuery())
|
||||
->setViewer($viewer)
|
||||
->withObjectPHIDs(array($device_phid))
|
||||
->execute();
|
||||
|
||||
$table = id(new PhabricatorAuthSSHKeyTableView())
|
||||
->setUser($viewer)
|
||||
->setKeys($keys)
|
||||
->setCanEdit($can_edit)
|
||||
->setShowID(true)
|
||||
->setShowTrusted(true)
|
||||
->setNoDataString(pht('This device has no associated SSH public keys.'));
|
||||
|
||||
try {
|
||||
PhabricatorSSHKeyGenerator::assertCanGenerateKeypair();
|
||||
$can_generate = true;
|
||||
} catch (Exception $ex) {
|
||||
$can_generate = false;
|
||||
}
|
||||
|
||||
$generate_uri = '/auth/sshkey/generate/?objectPHID='.$device_phid;
|
||||
$upload_uri = '/auth/sshkey/upload/?objectPHID='.$device_phid;
|
||||
|
||||
$header = id(new PHUIHeaderView())
|
||||
->setHeader(pht('SSH Public Keys'))
|
||||
->addActionLink(
|
||||
id(new PHUIButtonView())
|
||||
->setTag('a')
|
||||
->setHref($generate_uri)
|
||||
->setWorkflow(true)
|
||||
->setDisabled(!$can_edit || !$can_generate)
|
||||
->setText(pht('Generate Keypair'))
|
||||
->setIcon(
|
||||
id(new PHUIIconView())
|
||||
->setIconFont('fa-lock')))
|
||||
->addActionLink(
|
||||
id(new PHUIButtonView())
|
||||
->setTag('a')
|
||||
->setHref($upload_uri)
|
||||
->setWorkflow(true)
|
||||
->setDisabled(!$can_edit)
|
||||
->setText(pht('Upload Public Key'))
|
||||
->setIcon(
|
||||
id(new PHUIIconView())
|
||||
->setIconFont('fa-upload')));
|
||||
|
||||
return id(new PHUIObjectBoxView())
|
||||
->setHeader($header)
|
||||
->appendChild($table);
|
||||
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
@@ -1,64 +0,0 @@
|
||||
<?php
|
||||
|
||||
final class AlmanacManagementRegisterWorkflow
|
||||
extends AlmanacManagementWorkflow {
|
||||
|
||||
public function didConstruct() {
|
||||
$this
|
||||
->setName('register')
|
||||
->setSynopsis(pht('Register this host for authorized Conduit access.'))
|
||||
->setArguments(array());
|
||||
}
|
||||
|
||||
public function execute(PhutilArgumentParser $args) {
|
||||
$console = PhutilConsole::getConsole();
|
||||
|
||||
if (Filesystem::pathExists(AlmanacConduitUtil::getHostPrivateKeyPath())) {
|
||||
throw new Exception(
|
||||
'This host already has a private key for Conduit access.');
|
||||
}
|
||||
|
||||
$pair = PhabricatorSSHKeyGenerator::generateKeypair();
|
||||
list($public_key, $private_key) = $pair;
|
||||
|
||||
$host = id(new AlmanacDevice())
|
||||
->setName(php_uname('n'))
|
||||
->save();
|
||||
|
||||
id(new AlmanacProperty())
|
||||
->setObjectPHID($host->getPHID())
|
||||
->setName('conduitPublicOpenSSHKey')
|
||||
->setValue($public_key)
|
||||
->save();
|
||||
|
||||
id(new AlmanacProperty())
|
||||
->setObjectPHID($host->getPHID())
|
||||
->setName('conduitPublicOpenSSLKey')
|
||||
->setValue($this->convertToOpenSSLPublicKey($public_key))
|
||||
->save();
|
||||
|
||||
Filesystem::writeFile(
|
||||
AlmanacConduitUtil::getHostPrivateKeyPath(),
|
||||
$private_key);
|
||||
|
||||
Filesystem::writeFile(
|
||||
AlmanacConduitUtil::getHostIDPath(),
|
||||
$host->getID());
|
||||
|
||||
$console->writeOut("Registered as device %d.\n", $host->getID());
|
||||
}
|
||||
|
||||
private function convertToOpenSSLPublicKey($openssh_public_key) {
|
||||
$ssh_public_key_file = new TempFile();
|
||||
Filesystem::writeFile($ssh_public_key_file, $openssh_public_key);
|
||||
|
||||
list($public_key, $stderr) = id(new ExecFuture(
|
||||
'ssh-keygen -e -f %s -m pkcs8',
|
||||
$ssh_public_key_file))->resolvex();
|
||||
|
||||
unset($ssh_public_key_file);
|
||||
|
||||
return $public_key;
|
||||
}
|
||||
|
||||
}
|
@@ -0,0 +1,85 @@
|
||||
<?php
|
||||
|
||||
final class AlmanacManagementTrustKeyWorkflow
|
||||
extends AlmanacManagementWorkflow {
|
||||
|
||||
public function didConstruct() {
|
||||
$this
|
||||
->setName('trust-key')
|
||||
->setSynopsis(pht('Mark a public key as trusted.'))
|
||||
->setArguments(
|
||||
array(
|
||||
array(
|
||||
'name' => 'id',
|
||||
'param' => 'id',
|
||||
'help' => pht('ID of the key to trust.'),
|
||||
),
|
||||
));
|
||||
}
|
||||
|
||||
public function execute(PhutilArgumentParser $args) {
|
||||
$console = PhutilConsole::getConsole();
|
||||
|
||||
$id = $args->getArg('id');
|
||||
if (!$id) {
|
||||
throw new PhutilArgumentUsageException(
|
||||
pht('Specify a public key to trust with --id.'));
|
||||
}
|
||||
|
||||
$key = id(new PhabricatorAuthSSHKeyQuery())
|
||||
->setViewer($this->getViewer())
|
||||
->withIDs(array($id))
|
||||
->executeOne();
|
||||
if (!$key) {
|
||||
throw new PhutilArgumentUsageException(
|
||||
pht('No public key exists with ID "%s".', $id));
|
||||
}
|
||||
|
||||
if ($key->getIsTrusted()) {
|
||||
throw new PhutilArgumentUsageException(
|
||||
pht('Public key with ID %s is already trusted.', $id));
|
||||
}
|
||||
|
||||
if (!($key->getObject() instanceof AlmanacDevice)) {
|
||||
throw new PhutilArgumentUsageException(
|
||||
pht('You can only trust keys associated with Almanac devices.'));
|
||||
}
|
||||
|
||||
$handle = id(new PhabricatorHandleQuery())
|
||||
->setViewer($this->getViewer())
|
||||
->withPHIDs(array($key->getObject()->getPHID()))
|
||||
->executeOne();
|
||||
|
||||
$console->writeOut(
|
||||
"**<bg:red> %s </bg>**\n\n%s\n\n%s\n\n%s",
|
||||
pht('IMPORTANT!'),
|
||||
phutil_console_wrap(
|
||||
pht(
|
||||
'Trusting a public key gives anyone holding the corresponding '.
|
||||
'private key complete, unrestricted access to all data in '.
|
||||
'Phabricator. The private key will be able to sign requests that '.
|
||||
'skip policy and security checks.')),
|
||||
phutil_console_wrap(
|
||||
pht(
|
||||
'This is an advanced feature which should normally be used only '.
|
||||
'when building a Phabricator cluster. This feature is very '.
|
||||
'dangerous if misused.')),
|
||||
pht('This key is associated with device "%s".', $handle->getName()));
|
||||
|
||||
$prompt = pht(
|
||||
'Really trust this key?');
|
||||
if (!phutil_console_confirm($prompt)) {
|
||||
throw new PhutilArgumentUsageException(
|
||||
pht('User aborted workflow.'));
|
||||
}
|
||||
|
||||
$key->setIsTrusted(1);
|
||||
$key->save();
|
||||
|
||||
$console->writeOut(
|
||||
"**<bg:green> %s </bg>** %s\n",
|
||||
pht('TRUSTED'),
|
||||
pht('Key %s has been marked as trusted.', $id));
|
||||
}
|
||||
|
||||
}
|
@@ -0,0 +1,52 @@
|
||||
<?php
|
||||
|
||||
final class AlmanacManagementUntrustKeyWorkflow
|
||||
extends AlmanacManagementWorkflow {
|
||||
|
||||
public function didConstruct() {
|
||||
$this
|
||||
->setName('untrust-key')
|
||||
->setSynopsis(pht('Revoke trust of a public key.'))
|
||||
->setArguments(
|
||||
array(
|
||||
array(
|
||||
'name' => 'id',
|
||||
'param' => 'id',
|
||||
'help' => pht('ID of the key to revoke trust for.'),
|
||||
),
|
||||
));
|
||||
}
|
||||
|
||||
public function execute(PhutilArgumentParser $args) {
|
||||
$console = PhutilConsole::getConsole();
|
||||
|
||||
$id = $args->getArg('id');
|
||||
if (!$id) {
|
||||
throw new PhutilArgumentUsageException(
|
||||
pht('Specify a public key to revoke trust for with --id.'));
|
||||
}
|
||||
|
||||
$key = id(new PhabricatorAuthSSHKeyQuery())
|
||||
->setViewer($this->getViewer())
|
||||
->withIDs(array($id))
|
||||
->executeOne();
|
||||
if (!$key) {
|
||||
throw new PhutilArgumentUsageException(
|
||||
pht('No public key exists with ID "%s".', $id));
|
||||
}
|
||||
|
||||
if (!$key->getIsTrusted()) {
|
||||
throw new PhutilArgumentUsageException(
|
||||
pht('Public key with ID %s is not trusted.', $id));
|
||||
}
|
||||
|
||||
$key->setIsTrusted(0);
|
||||
$key->save();
|
||||
|
||||
$console->writeOut(
|
||||
"**<bg:green> %s </bg>** %s\n",
|
||||
pht('TRUST REVOKED'),
|
||||
pht('Trust has been revoked for public key %s.', $id));
|
||||
}
|
||||
|
||||
}
|
@@ -21,7 +21,8 @@ final class AlmanacBinding
|
||||
|
||||
public static function initializeNewBinding(AlmanacService $service) {
|
||||
return id(new AlmanacBinding())
|
||||
->setServicePHID($service->getPHID());
|
||||
->setServicePHID($service->getPHID())
|
||||
->attachAlmanacProperties(array());
|
||||
}
|
||||
|
||||
public function getConfiguration() {
|
||||
|
@@ -7,6 +7,7 @@ final class AlmanacDevice
|
||||
PhabricatorCustomFieldInterface,
|
||||
PhabricatorApplicationTransactionInterface,
|
||||
PhabricatorProjectInterface,
|
||||
PhabricatorSSHPublicKeyInterface,
|
||||
AlmanacPropertyInterface {
|
||||
|
||||
protected $name;
|
||||
@@ -21,7 +22,8 @@ final class AlmanacDevice
|
||||
public static function initializeNewDevice() {
|
||||
return id(new AlmanacDevice())
|
||||
->setViewPolicy(PhabricatorPolicies::POLICY_USER)
|
||||
->setEditPolicy(PhabricatorPolicies::POLICY_ADMIN);
|
||||
->setEditPolicy(PhabricatorPolicies::POLICY_ADMIN)
|
||||
->attachAlmanacProperties(array());
|
||||
}
|
||||
|
||||
public function getConfiguration() {
|
||||
@@ -160,4 +162,17 @@ final class AlmanacDevice
|
||||
return new AlmanacDeviceTransaction();
|
||||
}
|
||||
|
||||
|
||||
/* -( PhabricatorSSHPublicKeyInterface )----------------------------------- */
|
||||
|
||||
|
||||
public function getSSHPublicKeyManagementURI(PhabricatorUser $viewer) {
|
||||
return $this->getURI();
|
||||
}
|
||||
|
||||
public function getSSHKeyDefaultName() {
|
||||
return $this->getName();
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
@@ -21,7 +21,8 @@ final class AlmanacService
|
||||
public static function initializeNewService() {
|
||||
return id(new AlmanacService())
|
||||
->setViewPolicy(PhabricatorPolicies::POLICY_USER)
|
||||
->setEditPolicy(PhabricatorPolicies::POLICY_ADMIN);
|
||||
->setEditPolicy(PhabricatorPolicies::POLICY_ADMIN)
|
||||
->attachAlmanacProperties(array());
|
||||
}
|
||||
|
||||
public function getConfiguration() {
|
||||
|
@@ -1,17 +0,0 @@
|
||||
<?php
|
||||
|
||||
final class AlmanacConduitUtil extends Phobject {
|
||||
|
||||
public static function getHostPrivateKeyPath() {
|
||||
$root = dirname(phutil_get_library_root('phabricator'));
|
||||
$path = $root.'/conf/local/HOSTKEY';
|
||||
return $path;
|
||||
}
|
||||
|
||||
public static function getHostIDPath() {
|
||||
$root = dirname(phutil_get_library_root('phabricator'));
|
||||
$path = $root.'/conf/local/HOSTID';
|
||||
return $path;
|
||||
}
|
||||
|
||||
}
|
@@ -109,6 +109,12 @@ final class PhabricatorAuthApplication extends PhabricatorApplication {
|
||||
=> 'PhabricatorAuthDowngradeSessionController',
|
||||
'multifactor/'
|
||||
=> 'PhabricatorAuthNeedsMultiFactorController',
|
||||
'sshkey/' => array(
|
||||
'generate/' => 'PhabricatorAuthSSHKeyGenerateController',
|
||||
'upload/' => 'PhabricatorAuthSSHKeyEditController',
|
||||
'edit/(?P<id>\d+)/' => 'PhabricatorAuthSSHKeyEditController',
|
||||
'delete/(?P<id>\d+)/' => 'PhabricatorAuthSSHKeyDeleteController',
|
||||
),
|
||||
),
|
||||
|
||||
'/oauth/(?P<provider>\w+)/login/'
|
||||
|
@@ -0,0 +1,33 @@
|
||||
<?php
|
||||
|
||||
abstract class PhabricatorAuthSSHKeyController
|
||||
extends PhabricatorAuthController {
|
||||
|
||||
protected function newKeyForObjectPHID($object_phid) {
|
||||
$viewer = $this->getViewer();
|
||||
|
||||
$object = id(new PhabricatorObjectQuery())
|
||||
->setViewer($viewer)
|
||||
->withPHIDs(array($object_phid))
|
||||
->requireCapabilities(
|
||||
array(
|
||||
PhabricatorPolicyCapability::CAN_VIEW,
|
||||
PhabricatorPolicyCapability::CAN_EDIT,
|
||||
))
|
||||
->executeOne();
|
||||
if (!$object) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// If this kind of object can't have SSH keys, don't let the viewer
|
||||
// add them.
|
||||
if (!($object instanceof PhabricatorSSHPublicKeyInterface)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return id(new PhabricatorAuthSSHKey())
|
||||
->setObjectPHID($object_phid)
|
||||
->attachObject($object);
|
||||
}
|
||||
|
||||
}
|
@@ -0,0 +1,48 @@
|
||||
<?php
|
||||
|
||||
final class PhabricatorAuthSSHKeyDeleteController
|
||||
extends PhabricatorAuthSSHKeyController {
|
||||
|
||||
public function handleRequest(AphrontRequest $request) {
|
||||
$viewer = $this->getViewer();
|
||||
|
||||
$key = id(new PhabricatorAuthSSHKeyQuery())
|
||||
->setViewer($viewer)
|
||||
->withIDs(array($request->getURIData('id')))
|
||||
->requireCapabilities(
|
||||
array(
|
||||
PhabricatorPolicyCapability::CAN_VIEW,
|
||||
PhabricatorPolicyCapability::CAN_EDIT,
|
||||
))
|
||||
->executeOne();
|
||||
if (!$key) {
|
||||
return new Aphront404Response();
|
||||
}
|
||||
|
||||
$cancel_uri = $key->getObject()->getSSHPublicKeyManagementURI($viewer);
|
||||
|
||||
$token = id(new PhabricatorAuthSessionEngine())->requireHighSecuritySession(
|
||||
$viewer,
|
||||
$request,
|
||||
$cancel_uri);
|
||||
|
||||
if ($request->isFormPost()) {
|
||||
// TODO: It would be nice to write an edge transaction here or something.
|
||||
$key->delete();
|
||||
return id(new AphrontRedirectResponse())->setURI($cancel_uri);
|
||||
}
|
||||
|
||||
$name = phutil_tag('strong', array(), $key->getName());
|
||||
|
||||
return $this->newDialog()
|
||||
->setTitle(pht('Really delete SSH Public Key?'))
|
||||
->appendParagraph(
|
||||
pht(
|
||||
'The key "%s" will be permanently deleted, and you will not longer '.
|
||||
'be able to use the corresponding private key to authenticate.',
|
||||
$name))
|
||||
->addSubmitButton(pht('Delete Public Key'))
|
||||
->addCancelButton($cancel_uri);
|
||||
}
|
||||
|
||||
}
|
@@ -0,0 +1,143 @@
|
||||
<?php
|
||||
|
||||
final class PhabricatorAuthSSHKeyEditController
|
||||
extends PhabricatorAuthSSHKeyController {
|
||||
|
||||
public function handleRequest(AphrontRequest $request) {
|
||||
$viewer = $this->getViewer();
|
||||
|
||||
$id = $request->getURIData('id');
|
||||
if ($id) {
|
||||
$key = id(new PhabricatorAuthSSHKeyQuery())
|
||||
->setViewer($viewer)
|
||||
->withIDs(array($id))
|
||||
->requireCapabilities(
|
||||
array(
|
||||
PhabricatorPolicyCapability::CAN_VIEW,
|
||||
PhabricatorPolicyCapability::CAN_EDIT,
|
||||
))
|
||||
->executeOne();
|
||||
if (!$key) {
|
||||
return new Aphront404Response();
|
||||
}
|
||||
|
||||
$is_new = false;
|
||||
} else {
|
||||
$key = $this->newKeyForObjectPHID($request->getStr('objectPHID'));
|
||||
if (!$key) {
|
||||
return new Aphront404Response();
|
||||
}
|
||||
$is_new = true;
|
||||
}
|
||||
|
||||
$cancel_uri = $key->getObject()->getSSHPublicKeyManagementURI($viewer);
|
||||
|
||||
if ($key->getIsTrusted()) {
|
||||
$id = $key->getID();
|
||||
|
||||
return $this->newDialog()
|
||||
->setTitle(pht('Can Not Edit Trusted Key'))
|
||||
->appendParagraph(
|
||||
pht(
|
||||
'This key is trusted. Trusted keys can not be edited. '.
|
||||
'Use %s to revoke trust before editing the key.',
|
||||
phutil_tag(
|
||||
'tt',
|
||||
array(),
|
||||
"bin/almanac untrust-key --id {$id}")))
|
||||
->addCancelButton($cancel_uri, pht('Okay'));
|
||||
}
|
||||
|
||||
$token = id(new PhabricatorAuthSessionEngine())->requireHighSecuritySession(
|
||||
$viewer,
|
||||
$request,
|
||||
$cancel_uri);
|
||||
|
||||
$v_name = $key->getName();
|
||||
$e_name = strlen($v_name) ? null : true;
|
||||
|
||||
$v_key = $key->getEntireKey();
|
||||
$e_key = strlen($v_key) ? null : true;
|
||||
|
||||
$errors = array();
|
||||
if ($request->isFormPost()) {
|
||||
$v_name = $request->getStr('name');
|
||||
$v_key = $request->getStr('key');
|
||||
|
||||
if (!strlen($v_name)) {
|
||||
$errors[] = pht('You must provide a name for this public key.');
|
||||
$e_name = pht('Required');
|
||||
} else {
|
||||
$key->setName($v_name);
|
||||
}
|
||||
|
||||
if (!strlen($v_key)) {
|
||||
$errors[] = pht('You must provide a public key.');
|
||||
$e_key = pht('Required');
|
||||
} else {
|
||||
try {
|
||||
$public_key = PhabricatorAuthSSHPublicKey::newFromRawKey($v_key);
|
||||
|
||||
$type = $public_key->getType();
|
||||
$body = $public_key->getBody();
|
||||
$comment = $public_key->getComment();
|
||||
|
||||
$key->setKeyType($type);
|
||||
$key->setKeyBody($body);
|
||||
$key->setKeyComment($comment);
|
||||
|
||||
$e_key = null;
|
||||
} catch (Exception $ex) {
|
||||
$e_key = pht('Invalid');
|
||||
$errors[] = $ex->getMessage();
|
||||
}
|
||||
}
|
||||
|
||||
if (!$errors) {
|
||||
try {
|
||||
$key->save();
|
||||
return id(new AphrontRedirectResponse())->setURI($cancel_uri);
|
||||
} catch (Exception $ex) {
|
||||
$e_key = pht('Duplicate');
|
||||
$errors[] = pht(
|
||||
'This public key is already associated with another user or '.
|
||||
'device. Each key must unambiguously identify a single unique '.
|
||||
'owner.');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$form = id(new AphrontFormView())
|
||||
->setUser($viewer)
|
||||
->appendChild(
|
||||
id(new AphrontFormTextControl())
|
||||
->setLabel(pht('Name'))
|
||||
->setName('name')
|
||||
->setError($e_name)
|
||||
->setValue($v_name))
|
||||
->appendChild(
|
||||
id(new AphrontFormTextAreaControl())
|
||||
->setLabel(pht('Public Key'))
|
||||
->setName('key')
|
||||
->setValue($v_key)
|
||||
->setError($e_key));
|
||||
|
||||
if ($is_new) {
|
||||
$title = pht('Upload SSH Public Key');
|
||||
$save_button = pht('Upload Public Key');
|
||||
$form->addHiddenInput('objectPHID', $key->getObject()->getPHID());
|
||||
} else {
|
||||
$title = pht('Edit SSH Public Key');
|
||||
$save_button = pht('Save Changes');
|
||||
}
|
||||
|
||||
return $this->newDialog()
|
||||
->setTitle($title)
|
||||
->setWidth(AphrontDialogView::WIDTH_FORM)
|
||||
->setErrors($errors)
|
||||
->appendForm($form)
|
||||
->addSubmitButton($save_button)
|
||||
->addCancelButton($cancel_uri);
|
||||
}
|
||||
|
||||
}
|
@@ -0,0 +1,92 @@
|
||||
<?php
|
||||
|
||||
final class PhabricatorAuthSSHKeyGenerateController
|
||||
extends PhabricatorAuthSSHKeyController {
|
||||
|
||||
public function handleRequest(AphrontRequest $request) {
|
||||
$viewer = $this->getViewer();
|
||||
|
||||
$key = $this->newKeyForObjectPHID($request->getStr('objectPHID'));
|
||||
if (!$key) {
|
||||
return new Aphront404Response();
|
||||
}
|
||||
|
||||
$cancel_uri = $key->getObject()->getSSHPublicKeyManagementURI($viewer);
|
||||
|
||||
$token = id(new PhabricatorAuthSessionEngine())->requireHighSecuritySession(
|
||||
$viewer,
|
||||
$request,
|
||||
$cancel_uri);
|
||||
|
||||
if ($request->isFormPost()) {
|
||||
$default_name = $key->getObject()->getSSHKeyDefaultName();
|
||||
|
||||
$keys = PhabricatorSSHKeyGenerator::generateKeypair();
|
||||
list($public_key, $private_key) = $keys;
|
||||
|
||||
$file = PhabricatorFile::buildFromFileDataOrHash(
|
||||
$private_key,
|
||||
array(
|
||||
'name' => $default_name.'.key',
|
||||
'ttl' => time() + (60 * 10),
|
||||
'viewPolicy' => $viewer->getPHID(),
|
||||
));
|
||||
|
||||
$public_key = PhabricatorAuthSSHPublicKey::newFromRawKey($public_key);
|
||||
|
||||
$type = $public_key->getType();
|
||||
$body = $public_key->getBody();
|
||||
|
||||
$key
|
||||
->setName($default_name)
|
||||
->setKeyType($type)
|
||||
->setKeyBody($body)
|
||||
->setKeyComment(pht('Generated'))
|
||||
->save();
|
||||
|
||||
// NOTE: We're disabling workflow on submit so the download works. We're
|
||||
// disabling workflow on cancel so the page reloads, showing the new
|
||||
// key.
|
||||
|
||||
return $this->newDialog()
|
||||
->setTitle(pht('Download Private Key'))
|
||||
->setDisableWorkflowOnCancel(true)
|
||||
->setDisableWorkflowOnSubmit(true)
|
||||
->setSubmitURI($file->getDownloadURI())
|
||||
->appendParagraph(
|
||||
pht(
|
||||
'A keypair has been generated, and the public key has been '.
|
||||
'added as a recognized key. Use the button below to download '.
|
||||
'the private key.'))
|
||||
->appendParagraph(
|
||||
pht(
|
||||
'After you download the private key, it will be destroyed. '.
|
||||
'You will not be able to retrieve it if you lose your copy.'))
|
||||
->addSubmitButton(pht('Download Private Key'))
|
||||
->addCancelButton($cancel_uri, pht('Done'));
|
||||
}
|
||||
|
||||
try {
|
||||
PhabricatorSSHKeyGenerator::assertCanGenerateKeypair();
|
||||
|
||||
return $this->newDialog()
|
||||
->setTitle(pht('Generate New Keypair'))
|
||||
->addHiddenInput('objectPHID', $key->getObject()->getPHID())
|
||||
->appendParagraph(
|
||||
pht(
|
||||
'This workflow will generate a new SSH keypair, add the public '.
|
||||
'key, and let you download the private key.'))
|
||||
->appendParagraph(
|
||||
pht(
|
||||
'Phabricator will not retain a copy of the private key.'))
|
||||
->addSubmitButton(pht('Generate New Keypair'))
|
||||
->addCancelButton($cancel_uri);
|
||||
} catch (Exception $ex) {
|
||||
return $this->newDialog()
|
||||
->setTitle(pht('Unable to Generate Keys'))
|
||||
->appendParagraph($ex->getMessage())
|
||||
->addCancelButton($cancel_uri);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@@ -50,10 +50,14 @@ final class PhabricatorAuthSSHKeyQuery
|
||||
|
||||
foreach ($keys as $key => $ssh_key) {
|
||||
$object = idx($objects, $ssh_key->getObjectPHID());
|
||||
if (!$object) {
|
||||
|
||||
// We must have an object, and that object must be a valid object for
|
||||
// SSH keys.
|
||||
if (!$object || !($object instanceof PhabricatorSSHPublicKeyInterface)) {
|
||||
unset($keys[$key]);
|
||||
continue;
|
||||
}
|
||||
|
||||
$ssh_key->attachObject($object);
|
||||
}
|
||||
|
||||
|
@@ -99,4 +99,26 @@ final class PhabricatorAuthSSHPublicKey extends Phobject {
|
||||
return PhabricatorHash::digestForIndex($body);
|
||||
}
|
||||
|
||||
public function getEntireKey() {
|
||||
$key = $this->type.' '.$this->body;
|
||||
if (strlen($this->comment)) {
|
||||
$key = $key.' '.$this->comment;
|
||||
}
|
||||
return $key;
|
||||
}
|
||||
|
||||
public function toPKCS8() {
|
||||
|
||||
// TODO: Put a cache in front of this.
|
||||
|
||||
$tmp = new TempFile();
|
||||
Filesystem::writeFile($tmp, $this->getEntireKey());
|
||||
list($pem_key) = execx(
|
||||
'ssh-keygen -e -m PKCS8 -f %s',
|
||||
$tmp);
|
||||
unset($tmp);
|
||||
|
||||
return $pem_key;
|
||||
}
|
||||
|
||||
}
|
@@ -0,0 +1,20 @@
|
||||
<?php
|
||||
|
||||
interface PhabricatorSSHPublicKeyInterface {
|
||||
|
||||
/**
|
||||
* Provide a URI for SSH key workflows to return to after completing.
|
||||
*
|
||||
* When an actor adds, edits or deletes a public key, they'll be returned to
|
||||
* this URI. For example, editing user keys returns the actor to the settings
|
||||
* panel. Editing device keys returns the actor to the device page.
|
||||
*/
|
||||
public function getSSHPublicKeyManagementURI(PhabricatorUser $viewer);
|
||||
|
||||
|
||||
/**
|
||||
* Provide a default name for generated SSH keys.
|
||||
*/
|
||||
public function getSSHKeyDefaultName();
|
||||
|
||||
}
|
@@ -10,6 +10,7 @@ final class PhabricatorAuthSSHKey
|
||||
protected $keyIndex;
|
||||
protected $keyBody;
|
||||
protected $keyComment = '';
|
||||
protected $isTrusted = 0;
|
||||
|
||||
private $object = self::ATTACHABLE;
|
||||
|
||||
@@ -21,6 +22,7 @@ final class PhabricatorAuthSSHKey
|
||||
'keyIndex' => 'bytes12',
|
||||
'keyBody' => 'text',
|
||||
'keyComment' => 'text255',
|
||||
'isTrusted' => 'bool',
|
||||
),
|
||||
self::CONFIG_KEY_SCHEMA => array(
|
||||
'key_object' => array(
|
||||
@@ -56,7 +58,7 @@ final class PhabricatorAuthSSHKey
|
||||
return $this->assertAttached($this->object);
|
||||
}
|
||||
|
||||
public function attachObject($object) {
|
||||
public function attachObject(PhabricatorSSHPublicKeyInterface $object) {
|
||||
$this->object = $object;
|
||||
return $this;
|
||||
}
|
||||
|
110
src/applications/auth/view/PhabricatorAuthSSHKeyTableView.php
Normal file
110
src/applications/auth/view/PhabricatorAuthSSHKeyTableView.php
Normal file
@@ -0,0 +1,110 @@
|
||||
<?php
|
||||
|
||||
final class PhabricatorAuthSSHKeyTableView extends AphrontView {
|
||||
|
||||
private $keys;
|
||||
private $canEdit;
|
||||
private $noDataString;
|
||||
private $showTrusted;
|
||||
private $showID;
|
||||
|
||||
public function setNoDataString($no_data_string) {
|
||||
$this->noDataString = $no_data_string;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function setCanEdit($can_edit) {
|
||||
$this->canEdit = $can_edit;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function setShowTrusted($show_trusted) {
|
||||
$this->showTrusted = $show_trusted;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function setShowID($show_id) {
|
||||
$this->showID = $show_id;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function setKeys(array $keys) {
|
||||
assert_instances_of($keys, 'PhabricatorAuthSSHKey');
|
||||
$this->keys = $keys;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function render() {
|
||||
$keys = $this->keys;
|
||||
$viewer = $this->getUser();
|
||||
|
||||
if ($this->canEdit) {
|
||||
$delete_class = 'small grey button';
|
||||
} else {
|
||||
$delete_class = 'small grey button disabled';
|
||||
}
|
||||
|
||||
$trusted_icon = id(new PHUIIconView())
|
||||
->setIconFont('fa-star blue');
|
||||
$untrusted_icon = id(new PHUIIconView())
|
||||
->setIconFont('fa-times grey');
|
||||
|
||||
$rows = array();
|
||||
foreach ($keys as $key) {
|
||||
$rows[] = array(
|
||||
$key->getID(),
|
||||
javelin_tag(
|
||||
'a',
|
||||
array(
|
||||
'href' => '/auth/sshkey/edit/'.$key->getID().'/',
|
||||
'sigil' => 'workflow',
|
||||
),
|
||||
$key->getName()),
|
||||
$key->getIsTrusted() ? $trusted_icon : $untrusted_icon,
|
||||
$key->getKeyComment(),
|
||||
$key->getKeyType(),
|
||||
phabricator_datetime($key->getDateCreated(), $viewer),
|
||||
javelin_tag(
|
||||
'a',
|
||||
array(
|
||||
'href' => '/auth/sshkey/delete/'.$key->getID().'/',
|
||||
'class' => $delete_class,
|
||||
'sigil' => 'workflow',
|
||||
),
|
||||
pht('Delete')),
|
||||
);
|
||||
}
|
||||
|
||||
$table = id(new AphrontTableView($rows))
|
||||
->setNoDataString($this->noDataString)
|
||||
->setHeaders(
|
||||
array(
|
||||
pht('ID'),
|
||||
pht('Name'),
|
||||
pht('Trusted'),
|
||||
pht('Comment'),
|
||||
pht('Type'),
|
||||
pht('Added'),
|
||||
null,
|
||||
))
|
||||
->setColumnVisibility(
|
||||
array(
|
||||
$this->showID,
|
||||
true,
|
||||
$this->showTrusted,
|
||||
))
|
||||
->setColumnClasses(
|
||||
array(
|
||||
'',
|
||||
'wide pri',
|
||||
'center',
|
||||
'',
|
||||
'',
|
||||
'right',
|
||||
'action',
|
||||
));
|
||||
|
||||
return $table;
|
||||
}
|
||||
|
||||
}
|
@@ -526,4 +526,39 @@ abstract class PhabricatorController extends AphrontController {
|
||||
->setSubmitURI($submit_uri);
|
||||
}
|
||||
|
||||
protected function buildTransactionTimeline(
|
||||
PhabricatorApplicationTransactionInterface $object,
|
||||
PhabricatorApplicationTransactionQuery $query,
|
||||
PhabricatorMarkupEngine $engine = null) {
|
||||
|
||||
$viewer = $this->getRequest()->getUser();
|
||||
$xaction = $object->getApplicationTransactionTemplate();
|
||||
$view = $xaction->getApplicationTransactionViewObject();
|
||||
|
||||
$xactions = $query
|
||||
->setViewer($viewer)
|
||||
->withObjectPHIDs(array($object->getPHID()))
|
||||
->needComments(true)
|
||||
->execute();
|
||||
|
||||
if ($engine) {
|
||||
foreach ($xactions as $xaction) {
|
||||
if ($xaction->getComment()) {
|
||||
$engine->addObject(
|
||||
$xaction->getComment(),
|
||||
PhabricatorApplicationTransactionComment::MARKUP_FIELD_COMMENT);
|
||||
}
|
||||
}
|
||||
$engine->process();
|
||||
$view->setMarkupEngine($engine);
|
||||
}
|
||||
|
||||
$timeline = $view
|
||||
->setUser($viewer)
|
||||
->setObjectPHID($object->getPHID())
|
||||
->setTransactions($xactions);
|
||||
|
||||
return $timeline;
|
||||
}
|
||||
|
||||
}
|
||||
|
@@ -209,6 +209,93 @@ final class PhabricatorConduitAPIController
|
||||
$request->getUser());
|
||||
}
|
||||
|
||||
$auth_type = idx($metadata, 'auth.type');
|
||||
if ($auth_type === ConduitClient::AUTH_ASYMMETRIC) {
|
||||
$host = idx($metadata, 'auth.host');
|
||||
if (!$host) {
|
||||
return array(
|
||||
'ERR-INVALID-AUTH',
|
||||
pht(
|
||||
'Request is missing required "auth.host" parameter.'),
|
||||
);
|
||||
}
|
||||
|
||||
// TODO: Validate that we are the host!
|
||||
|
||||
$raw_key = idx($metadata, 'auth.key');
|
||||
$public_key = PhabricatorAuthSSHPublicKey::newFromRawKey($raw_key);
|
||||
$ssl_public_key = $public_key->toPKCS8();
|
||||
|
||||
// First, verify the signature.
|
||||
try {
|
||||
$protocol_data = $metadata;
|
||||
|
||||
// TODO: We should stop writing this into the protocol data when
|
||||
// processing a request.
|
||||
unset($protocol_data['scope']);
|
||||
|
||||
ConduitClient::verifySignature(
|
||||
$this->method,
|
||||
$api_request->getAllParameters(),
|
||||
$protocol_data,
|
||||
$ssl_public_key);
|
||||
} catch (Exception $ex) {
|
||||
return array(
|
||||
'ERR-INVALID-AUTH',
|
||||
pht(
|
||||
'Signature verification failure. %s',
|
||||
$ex->getMessage()),
|
||||
);
|
||||
}
|
||||
|
||||
// If the signature is valid, find the user or device which is
|
||||
// associated with this public key.
|
||||
|
||||
$stored_key = id(new PhabricatorAuthSSHKeyQuery())
|
||||
->setViewer(PhabricatorUser::getOmnipotentUser())
|
||||
->withKeys(array($public_key))
|
||||
->executeOne();
|
||||
if (!$stored_key) {
|
||||
return array(
|
||||
'ERR-INVALID-AUTH',
|
||||
pht(
|
||||
'No user or device is associated with that public key.'),
|
||||
);
|
||||
}
|
||||
|
||||
$object = $stored_key->getObject();
|
||||
|
||||
if ($object instanceof PhabricatorUser) {
|
||||
$user = $object;
|
||||
} else {
|
||||
if (!$stored_key->getIsTrusted()) {
|
||||
return array(
|
||||
'ERR-INVALID-AUTH',
|
||||
pht(
|
||||
'The key which signed this request is not trusted. Only '.
|
||||
'trusted keys can be used to sign API calls.'),
|
||||
);
|
||||
}
|
||||
|
||||
throw new Exception(
|
||||
pht('Not Implemented: Would authenticate Almanac device.'));
|
||||
}
|
||||
|
||||
return $this->validateAuthenticatedUser(
|
||||
$api_request,
|
||||
$user);
|
||||
} else if ($auth_type === null) {
|
||||
// No specified authentication type, continue with other authentication
|
||||
// methods below.
|
||||
} else {
|
||||
return array(
|
||||
'ERR-INVALID-AUTH',
|
||||
pht(
|
||||
'Provided "auth.type" ("%s") is not recognized.',
|
||||
$auth_type),
|
||||
);
|
||||
}
|
||||
|
||||
// handle oauth
|
||||
$access_token = $request->getStr('access_token');
|
||||
$method_scope = $metadata['scope'];
|
||||
|
@@ -92,6 +92,53 @@ final class PhabricatorSetupCheckBinaries extends PhabricatorSetupCheck {
|
||||
'version control system. It will not work without the VCS binary.');
|
||||
$this->raiseWarning($binary, $message);
|
||||
}
|
||||
|
||||
switch ($binary) {
|
||||
case PhabricatorRepositoryType::REPOSITORY_TYPE_GIT:
|
||||
$minimum_version = null;
|
||||
$bad_versions = array();
|
||||
list($err, $stdout, $stderr) = exec_manual('git --version');
|
||||
$version = trim(substr($stdout, strlen('git version ')));
|
||||
break;
|
||||
case PhabricatorRepositoryType::REPOSITORY_TYPE_SVN:
|
||||
$minimum_version = null;
|
||||
$bad_versions = array(
|
||||
'1.7.1' => pht('This version of Subversion has a bug where '.
|
||||
'"svn diff -c N" does not work for files added '.
|
||||
'in rN (Subverison issue #2873), fixed in 1.7.2.'),);
|
||||
list($err, $stdout, $stderr) = exec_manual('svn --version --quiet');
|
||||
$version = trim($stdout);
|
||||
break;
|
||||
case PhabricatorRepositoryType::REPOSITORY_TYPE_MERCURIAL:
|
||||
$minimum_version = '1.9';
|
||||
$bad_versions = array(
|
||||
'2.1' => pht('This version of Mercurial returns a bad exit code '.
|
||||
'after a successful pull.'),
|
||||
'2.2' => pht('This version of Mercurial has a significant memory '.
|
||||
'leak, fixed in 2.2.1. Pushing fails with this '.
|
||||
'version as well; see T3046#54922.'),);
|
||||
list($err, $stdout, $stderr) = exec_manual('hg --version --quiet');
|
||||
$version = rtrim(
|
||||
substr($stdout, strlen('Mercurial Distributed SCM (version ')),
|
||||
")\n");
|
||||
break;
|
||||
}
|
||||
|
||||
if ($minimum_version &&
|
||||
version_compare($version, $minimum_version, '<')) {
|
||||
$this->raiseMinimumVersionWarning(
|
||||
$binary,
|
||||
$minimum_version,
|
||||
$version);
|
||||
}
|
||||
|
||||
foreach ($bad_versions as $bad_version => $details) {
|
||||
if ($bad_version === $version) {
|
||||
$this->raiseBadVersionWarning(
|
||||
$binary,
|
||||
$bad_version);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -126,4 +173,61 @@ final class PhabricatorSetupCheckBinaries extends PhabricatorSetupCheck {
|
||||
->addPhabricatorConfig('environment.append-paths');
|
||||
}
|
||||
|
||||
private function raiseMinimumVersionWarning(
|
||||
$binary,
|
||||
$minimum_version,
|
||||
$version) {
|
||||
|
||||
switch ($binary) {
|
||||
case PhabricatorRepositoryType::REPOSITORY_TYPE_GIT:
|
||||
break;
|
||||
case PhabricatorRepositoryType::REPOSITORY_TYPE_SVN:
|
||||
break;
|
||||
case PhabricatorRepositoryType::REPOSITORY_TYPE_MERCURIAL:
|
||||
$summary = pht(
|
||||
"The '%s' binary is version %s and Phabricator requires version ".
|
||||
"%s or higher.",
|
||||
$binary,
|
||||
$version,
|
||||
$minimum_version);
|
||||
$message = pht(
|
||||
"Please upgrade the '%s' binary to a more modern version.",
|
||||
$binary);
|
||||
$this->newIssue('bin.'.$binary)
|
||||
->setShortName(pht("Unsupported '%s' Version", $binary))
|
||||
->setName(pht("Unsupported '%s' Version", $binary))
|
||||
->setSummary($summary)
|
||||
->setMessage($summary.' '.$message);
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
private function raiseBadVersionWarning($binary, $bad_version) {
|
||||
|
||||
switch ($binary) {
|
||||
case PhabricatorRepositoryType::REPOSITORY_TYPE_GIT:
|
||||
break;
|
||||
case PhabricatorRepositoryType::REPOSITORY_TYPE_SVN:
|
||||
case PhabricatorRepositoryType::REPOSITORY_TYPE_MERCURIAL:
|
||||
$summary = pht(
|
||||
"The '%s' binary is version %s which has bugs that break ".
|
||||
"Phabricator.",
|
||||
$binary,
|
||||
$bad_version);
|
||||
$message = pht(
|
||||
"Please upgrade the '%s' binary to a more modern version.",
|
||||
$binary);
|
||||
$this->newIssue('bin.'.$binary)
|
||||
->setShortName(pht("Unsupported '%s' Version", $binary))
|
||||
->setName(pht("Unsupported '%s' Version", $binary))
|
||||
->setSummary($summary)
|
||||
->setMessage($summary.' '.$message);
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
@@ -34,6 +34,7 @@ final class PhabricatorSetupCheckMySQL extends PhabricatorSetupCheck {
|
||||
|
||||
$modes = self::loadRawConfigValue('sql_mode');
|
||||
$modes = explode(',', $modes);
|
||||
|
||||
if (!in_array('STRICT_ALL_TABLES', $modes)) {
|
||||
$summary = pht(
|
||||
'MySQL is not in strict mode, but using strict mode is strongly '.
|
||||
@@ -67,6 +68,45 @@ final class PhabricatorSetupCheckMySQL extends PhabricatorSetupCheck {
|
||||
->setMessage($message)
|
||||
->addMySQLConfig('sql_mode');
|
||||
}
|
||||
if (in_array('ONLY_FULL_GROUP_BY', $modes)) {
|
||||
$summary = pht(
|
||||
'MySQL is in ONLY_FULL_GROUP_BY mode, but using this mode is strongly '.
|
||||
'discouraged.');
|
||||
|
||||
$message = pht(
|
||||
"On your MySQL instance, the global %s is set to %s. ".
|
||||
"It is strongly encouraged that you disable this mode when running ".
|
||||
"Phabricator.\n\n".
|
||||
"With %s enabled, MySQL rejects queries for which the select list ".
|
||||
"or (as of MySQL 5.0.23) %s list refer to nonaggregated columns ".
|
||||
"that are not named in the %s clause. More importantly, Phabricator ".
|
||||
"does not work properly with this mode enabled.\n\n".
|
||||
"You can find more information about this mode (and how to configure ".
|
||||
"it) in the MySQL manual. Usually, it is sufficient to change the %s ".
|
||||
"in your %s file (in the %s section) and then restart %s:\n\n".
|
||||
"%s\n".
|
||||
"(Note that if you run other applications against the same database, ".
|
||||
"they may not work with %s. Be careful about enabling ".
|
||||
"it in these cases and consider migrating Phabricator to a different ".
|
||||
"database.)",
|
||||
phutil_tag('tt', array(), 'sql_mode'),
|
||||
phutil_tag('tt', array(), 'ONLY_FULL_GROUP_BY'),
|
||||
phutil_tag('tt', array(), 'ONLY_FULL_GROUP_BY'),
|
||||
phutil_tag('tt', array(), 'HAVING'),
|
||||
phutil_tag('tt', array(), 'GROUP BY'),
|
||||
phutil_tag('tt', array(), 'sql_mode'),
|
||||
phutil_tag('tt', array(), 'my.cnf'),
|
||||
phutil_tag('tt', array(), '[mysqld]'),
|
||||
phutil_tag('tt', array(), 'mysqld'),
|
||||
phutil_tag('pre', array(), 'sql_mode=STRICT_ALL_TABLES'),
|
||||
phutil_tag('tt', array(), 'ONLY_FULL_GROUP_BY'));
|
||||
|
||||
$this->newIssue('mysql.mode')
|
||||
->setName(pht('MySQL ONLY_FULL_GROUP_BY Mode Set'))
|
||||
->setSummary($summary)
|
||||
->setMessage($message)
|
||||
->addMySQLConfig('sql_mode');
|
||||
}
|
||||
|
||||
$stopword_file = self::loadRawConfigValue('ft_stopword_file');
|
||||
if (!PhabricatorDefaultSearchEngineSelector::shouldUseElasticSearch()) {
|
||||
|
@@ -120,10 +120,8 @@ final class PhabricatorConfigEditController
|
||||
->setSeverity(AphrontErrorView::SEVERITY_WARNING)
|
||||
->appendChild(phutil_tag('p', array(), $msg));
|
||||
} else if ($option->getLocked()) {
|
||||
$msg = pht(
|
||||
'This configuration is locked and can not be edited from the web '.
|
||||
'interface. Use `./bin/config` in `phabricator/` to edit it.');
|
||||
|
||||
$msg = $option->getLockedMessage();
|
||||
$error_view = id(new AphrontErrorView())
|
||||
->setTitle(pht('Configuration Locked'))
|
||||
->setSeverity(AphrontErrorView::SEVERITY_NOTICE)
|
||||
|
@@ -59,8 +59,8 @@ final class PhabricatorConfigManagementGetWorkflow
|
||||
);
|
||||
}
|
||||
|
||||
$database_config = new PhabricatorConfigDatabaseSource('default');
|
||||
try {
|
||||
$database_config = new PhabricatorConfigDatabaseSource('default');
|
||||
$database_value = $database_config->getKeys(array($key));
|
||||
if (empty($database_value)) {
|
||||
$values['database'] = array(
|
||||
|
@@ -55,13 +55,14 @@ final class PhabricatorConfigManagementMigrateWorkflow
|
||||
'Skipping option "%s"; already in database config.', $key)."\n");
|
||||
continue;
|
||||
} else {
|
||||
PhabricatorConfigEditor::deleteConfig(
|
||||
PhabricatorConfigEditor::storeNewValue(
|
||||
$this->getViewer(),
|
||||
$option,
|
||||
id(new PhabricatorConfigEntry())
|
||||
->loadOneWhere('namespace = %s AND key = %s', 'default', $key),
|
||||
PhabricatorContentSource::newConsoleSource());
|
||||
$key_count++;
|
||||
$console->writeOut(pht(
|
||||
'Migrated option "%s" from file to local config.', $key)."\n");
|
||||
'Migrated option "%s" from file to database config.', $key)."\n");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -14,6 +14,7 @@ final class PhabricatorConfigOption
|
||||
private $group;
|
||||
private $examples;
|
||||
private $locked;
|
||||
private $lockedMessage;
|
||||
private $hidden;
|
||||
private $masked;
|
||||
private $baseClass;
|
||||
@@ -85,6 +86,20 @@ final class PhabricatorConfigOption
|
||||
false);
|
||||
}
|
||||
|
||||
public function setLockedMessage($message) {
|
||||
$this->lockedMessage = $message;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getLockedMessage() {
|
||||
if ($this->lockedMessage !== null) {
|
||||
return $this->lockedMessage;
|
||||
}
|
||||
return pht(
|
||||
'This configuration is locked and can not be edited from the web '.
|
||||
'interface. Use `./bin/config` in `phabricator/` to edit it.');
|
||||
}
|
||||
|
||||
public function addExample($value, $description) {
|
||||
$this->examples[] = array($value, $description);
|
||||
return $this;
|
||||
|
@@ -27,6 +27,7 @@ final class PhabricatorCoreConfigOptions
|
||||
$proto_doc_href = PhabricatorEnv::getDoclink(
|
||||
'User Guide: Prototype Applications');
|
||||
$proto_doc_name = pht('User Guide: Prototype Applications');
|
||||
$applications_app_href = '/applications/';
|
||||
|
||||
return array(
|
||||
$this->newOption('phabricator.base-uri', 'string', null)
|
||||
@@ -183,6 +184,14 @@ final class PhabricatorCoreConfigOptions
|
||||
->setDescription(pht('Unit test value.')),
|
||||
$this->newOption('phabricator.uninstalled-applications', 'set', array())
|
||||
->setLocked(true)
|
||||
->setLockedMessage(pht(
|
||||
'Use the %s to manage installed applications.',
|
||||
phutil_tag(
|
||||
'a',
|
||||
array(
|
||||
'href' => $applications_app_href,
|
||||
),
|
||||
pht('Applications application'))))
|
||||
->setDescription(
|
||||
pht('Array containing list of Uninstalled applications.')),
|
||||
$this->newOption('phabricator.application-settings', 'wild', array())
|
||||
|
@@ -98,6 +98,12 @@ from replying. However, it may also cause deliverability issues -- notably, you
|
||||
currently can not send this header via Amazon SES, and enabling this option with
|
||||
SES will prevent delivery of any affected mail.
|
||||
EODOC
|
||||
));
|
||||
|
||||
$email_preferences_description = $this->deformat(pht(<<<EODOC
|
||||
You can disable the email preference link in emails if users prefer smaller
|
||||
emails.
|
||||
EODOC
|
||||
));
|
||||
|
||||
$re_prefix_description = $this->deformat(pht(<<<EODOC
|
||||
@@ -257,6 +263,14 @@ EODOC
|
||||
))
|
||||
->setSummary(pht('Show "To:" and "Cc:" footer hints in email.'))
|
||||
->setDescription($recipient_hints_description),
|
||||
$this->newOption('metamta.email-preferences', 'bool', true)
|
||||
->setBoolOptions(
|
||||
array(
|
||||
pht('Show Email Preferences Link'),
|
||||
pht('No Email Preferences Link'),
|
||||
))
|
||||
->setSummary(pht('Show email preferences link in email.'))
|
||||
->setDescription($email_preferences_description),
|
||||
$this->newOption('metamta.precedence-bulk', 'bool', false)
|
||||
->setBoolOptions(
|
||||
array(
|
||||
|
@@ -87,10 +87,11 @@ final class ConpherenceNewController extends ConpherenceController {
|
||||
->setError($e_participants))
|
||||
->appendChild(
|
||||
id(new PhabricatorRemarkupControl())
|
||||
->setName('message')
|
||||
->setValue($message)
|
||||
->setLabel(pht('Message'))
|
||||
->setError($e_message));
|
||||
->setUser($user)
|
||||
->setName('message')
|
||||
->setValue($message)
|
||||
->setLabel(pht('Message'))
|
||||
->setError($e_message));
|
||||
|
||||
$dialog->appendChild($form);
|
||||
|
||||
|
@@ -35,7 +35,9 @@ final class DifferentialParseRenderTestCase extends PhabricatorTestCase {
|
||||
$parser = new ArcanistDiffParser();
|
||||
$changes = $parser->parseDiff($data);
|
||||
|
||||
$diff = DifferentialDiff::newFromRawChanges($changes);
|
||||
$diff = DifferentialDiff::newFromRawChanges(
|
||||
PhabricatorUser::getOmnipotentUser(),
|
||||
$changes);
|
||||
if (count($diff->getChangesets()) !== 1) {
|
||||
throw new Exception("Expected one changeset: {$file}");
|
||||
}
|
||||
|
@@ -69,20 +69,14 @@ final class DifferentialCreateDiffConduitAPIMethod
|
||||
$changes[] = ArcanistDiffChange::newFromDictionary($dict);
|
||||
}
|
||||
|
||||
$diff = DifferentialDiff::newFromRawChanges($changes);
|
||||
$diff->setSourcePath($request->getValue('sourcePath'));
|
||||
$diff->setSourceMachine($request->getValue('sourceMachine'));
|
||||
$diff = DifferentialDiff::newFromRawChanges($viewer, $changes);
|
||||
|
||||
$diff->setBranch($request->getValue('branch'));
|
||||
$diff->setCreationMethod($request->getValue('creationMethod'));
|
||||
$diff->setAuthorPHID($viewer->getPHID());
|
||||
$diff->setBookmark($request->getValue('bookmark'));
|
||||
|
||||
// TODO: Remove this eventually; for now continue writing the UUID. Note
|
||||
// that we'll overwrite it below if we identify a repository, and `arc`
|
||||
// no longer sends it. This stuff is retained for backward compatibility.
|
||||
$diff->setRepositoryUUID($request->getValue('repositoryUUID'));
|
||||
// TODO: Remove repository UUID eventually; for now continue writing
|
||||
// the UUID. Note that we'll overwrite it below if we identify a
|
||||
// repository, and `arc` no longer sends it. This stuff is retained for
|
||||
// backward compatibility.
|
||||
|
||||
$repository_uuid = $request->getValue('repositoryUUID');
|
||||
$repository_phid = $request->getValue('repositoryPHID');
|
||||
if ($repository_phid) {
|
||||
$repository = id(new PhabricatorRepositoryQuery())
|
||||
@@ -90,17 +84,11 @@ final class DifferentialCreateDiffConduitAPIMethod
|
||||
->withPHIDs(array($repository_phid))
|
||||
->executeOne();
|
||||
if ($repository) {
|
||||
$diff->setRepositoryPHID($repository->getPHID());
|
||||
$diff->setRepositoryUUID($repository->getUUID());
|
||||
$repository_phid = $repository->getPHID();
|
||||
$repository_uuid = $repository->getUUID();
|
||||
}
|
||||
}
|
||||
|
||||
$system = $request->getValue('sourceControlSystem');
|
||||
$diff->setSourceControlSystem($system);
|
||||
$diff->setSourceControlPath($request->getValue('sourceControlPath'));
|
||||
$diff->setSourceControlBaseRevision(
|
||||
$request->getValue('sourceControlBaseRevision'));
|
||||
|
||||
$project_name = $request->getValue('arcanistProject');
|
||||
$project_phid = null;
|
||||
if ($project_name) {
|
||||
@@ -116,73 +104,75 @@ final class DifferentialCreateDiffConduitAPIMethod
|
||||
$project_phid = $arcanist_project->getPHID();
|
||||
}
|
||||
|
||||
$diff->setArcanistProjectPHID($project_phid);
|
||||
|
||||
switch ($request->getValue('lintStatus')) {
|
||||
case 'skip':
|
||||
$diff->setLintStatus(DifferentialLintStatus::LINT_SKIP);
|
||||
$lint_status = DifferentialLintStatus::LINT_SKIP;
|
||||
break;
|
||||
case 'okay':
|
||||
$diff->setLintStatus(DifferentialLintStatus::LINT_OKAY);
|
||||
$lint_status = DifferentialLintStatus::LINT_OKAY;
|
||||
break;
|
||||
case 'warn':
|
||||
$diff->setLintStatus(DifferentialLintStatus::LINT_WARN);
|
||||
$lint_status = DifferentialLintStatus::LINT_WARN;
|
||||
break;
|
||||
case 'fail':
|
||||
$diff->setLintStatus(DifferentialLintStatus::LINT_FAIL);
|
||||
$lint_status = DifferentialLintStatus::LINT_FAIL;
|
||||
break;
|
||||
case 'postponed':
|
||||
$diff->setLintStatus(DifferentialLintStatus::LINT_POSTPONED);
|
||||
$lint_status = DifferentialLintStatus::LINT_POSTPONED;
|
||||
break;
|
||||
case 'none':
|
||||
default:
|
||||
$diff->setLintStatus(DifferentialLintStatus::LINT_NONE);
|
||||
$lint_status = DifferentialLintStatus::LINT_NONE;
|
||||
break;
|
||||
}
|
||||
|
||||
switch ($request->getValue('unitStatus')) {
|
||||
case 'skip':
|
||||
$diff->setUnitStatus(DifferentialUnitStatus::UNIT_SKIP);
|
||||
$unit_status = DifferentialUnitStatus::UNIT_SKIP;
|
||||
break;
|
||||
case 'okay':
|
||||
$diff->setUnitStatus(DifferentialUnitStatus::UNIT_OKAY);
|
||||
$unit_status = DifferentialUnitStatus::UNIT_OKAY;
|
||||
break;
|
||||
case 'warn':
|
||||
$diff->setUnitStatus(DifferentialUnitStatus::UNIT_WARN);
|
||||
$unit_status = DifferentialUnitStatus::UNIT_WARN;
|
||||
break;
|
||||
case 'fail':
|
||||
$diff->setUnitStatus(DifferentialUnitStatus::UNIT_FAIL);
|
||||
$unit_status = DifferentialUnitStatus::UNIT_FAIL;
|
||||
break;
|
||||
case 'postponed':
|
||||
$diff->setUnitStatus(DifferentialUnitStatus::UNIT_POSTPONED);
|
||||
$unit_status = DifferentialUnitStatus::UNIT_POSTPONED;
|
||||
break;
|
||||
case 'none':
|
||||
default:
|
||||
$diff->setUnitStatus(DifferentialUnitStatus::UNIT_NONE);
|
||||
$unit_status = DifferentialUnitStatus::UNIT_NONE;
|
||||
break;
|
||||
}
|
||||
|
||||
$diff_data_dict = array(
|
||||
'sourcePath' => $request->getValue('sourcePath'),
|
||||
'sourceMachine' => $request->getValue('sourceMachine'),
|
||||
'branch' => $request->getValue('branch'),
|
||||
'creationMethod' => $request->getValue('creationMethod'),
|
||||
'authorPHID' => $viewer->getPHID(),
|
||||
'bookmark' => $request->getValue('bookmark'),
|
||||
'repositoryUUID' => $repository_uuid,
|
||||
'repositoryPHID' => $repository_phid,
|
||||
'sourceControlSystem' => $request->getValue('sourceControlSystem'),
|
||||
'sourceControlPath' => $request->getValue('sourceControlPath'),
|
||||
'sourceControlBaseRevision' =>
|
||||
$request->getValue('sourceControlBaseRevision'),
|
||||
'arcanistProjectPHID' => $project_phid,
|
||||
'lintStatus' => $lint_status,
|
||||
'unitStatus' => $unit_status,);
|
||||
|
||||
$xactions = array(id(new DifferentialTransaction())
|
||||
->setTransactionType(DifferentialDiffTransaction::TYPE_DIFF_CREATE)
|
||||
->setNewValue($diff_data_dict),);
|
||||
|
||||
id(new DifferentialDiffEditor())
|
||||
->setActor($viewer)
|
||||
->setContentSource(
|
||||
PhabricatorContentSource::newFromConduitRequest($request))
|
||||
->saveDiff($diff);
|
||||
|
||||
// If we didn't get an explicit `repositoryPHID` (which means the client is
|
||||
// old, or couldn't figure out which repository the working copy belongs
|
||||
// to), apply heuristics to try to figure it out.
|
||||
|
||||
if (!$repository_phid) {
|
||||
$repository = id(new DifferentialRepositoryLookup())
|
||||
->setDiff($diff)
|
||||
->setViewer($viewer)
|
||||
->lookupRepository();
|
||||
if ($repository) {
|
||||
$diff->setRepositoryPHID($repository->getPHID());
|
||||
$diff->setRepositoryUUID($repository->getUUID());
|
||||
$diff->save();
|
||||
}
|
||||
}
|
||||
->setContentSourceFromConduitRequest($request)
|
||||
->applyTransactions($diff, $xactions);
|
||||
|
||||
$path = '/differential/diff/'.$diff->getID().'/';
|
||||
$uri = PhabricatorEnv::getURI($path);
|
||||
|
@@ -15,6 +15,7 @@ final class DifferentialCreateRawDiffConduitAPIMethod
|
||||
return array(
|
||||
'diff' => 'required string',
|
||||
'repositoryPHID' => 'optional string',
|
||||
'viewPolicy' => 'optional string',
|
||||
);
|
||||
}
|
||||
|
||||
@@ -41,29 +42,34 @@ final class DifferentialCreateRawDiffConduitAPIMethod
|
||||
throw new Exception(
|
||||
pht('No such repository "%s"!', $repository_phid));
|
||||
}
|
||||
} else {
|
||||
$repository = null;
|
||||
}
|
||||
|
||||
$parser = new ArcanistDiffParser();
|
||||
$changes = $parser->parseDiff($raw_diff);
|
||||
$diff = DifferentialDiff::newFromRawChanges($changes);
|
||||
$diff = DifferentialDiff::newFromRawChanges($viewer, $changes);
|
||||
|
||||
$diff->setLintStatus(DifferentialLintStatus::LINT_SKIP);
|
||||
$diff->setUnitStatus(DifferentialUnitStatus::UNIT_SKIP);
|
||||
$diff_data_dict = array(
|
||||
'creationMethod' => 'web',
|
||||
'authorPHID' => $viewer->getPHID(),
|
||||
'repositoryPHID' => $repository_phid,
|
||||
'lintStatus' => DifferentialLintStatus::LINT_SKIP,
|
||||
'unitStatus' => DifferentialUnitStatus::UNIT_SKIP,);
|
||||
|
||||
$diff->setAuthorPHID($viewer->getPHID());
|
||||
$diff->setCreationMethod('web');
|
||||
$xactions = array(id(new DifferentialTransaction())
|
||||
->setTransactionType(DifferentialDiffTransaction::TYPE_DIFF_CREATE)
|
||||
->setNewValue($diff_data_dict),);
|
||||
|
||||
if ($repository) {
|
||||
$diff->setRepositoryPHID($repository->getPHID());
|
||||
if ($request->getValue('viewPolicy')) {
|
||||
$xactions[] = id(new DifferentialTransaction())
|
||||
->setTransactionType(PhabricatorTransactions::TYPE_VIEW_POLICY)
|
||||
->setNewValue($request->getValue('viewPolicy'));
|
||||
}
|
||||
|
||||
id(new DifferentialDiffEditor())
|
||||
->setActor($viewer)
|
||||
->setContentSource(
|
||||
PhabricatorContentSource::newFromConduitRequest($request))
|
||||
->saveDiff($diff);
|
||||
->setContentSourceFromConduitRequest($request)
|
||||
->setLookupRepository(false) // respect user choice
|
||||
->applyTransactions($diff, $xactions);
|
||||
|
||||
return $this->buildDiffInfoDictionary($diff);
|
||||
}
|
||||
|
@@ -5,12 +5,24 @@ final class DifferentialDiffCreateController extends DifferentialController {
|
||||
public function processRequest() {
|
||||
|
||||
$request = $this->getRequest();
|
||||
$viewer = $request->getUser();
|
||||
|
||||
$diff = null;
|
||||
// This object is just for policy stuff
|
||||
$diff_object = DifferentialDiff::initializeNewDiff($viewer);
|
||||
$repository_phid = null;
|
||||
$repository_value = array();
|
||||
$errors = array();
|
||||
$e_diff = null;
|
||||
$e_file = null;
|
||||
$validation_exception = null;
|
||||
if ($request->isFormPost()) {
|
||||
$diff = null;
|
||||
|
||||
$repository_tokenizer = $request->getArr(
|
||||
id(new DifferentialRepositoryField())->getFieldKey());
|
||||
if ($repository_tokenizer) {
|
||||
$repository_phid = reset($repository_tokenizer);
|
||||
}
|
||||
|
||||
if ($request->getFileExists('diff-file')) {
|
||||
$diff = PhabricatorFile::readUploadedFileData($_FILES['diff-file']);
|
||||
@@ -27,16 +39,20 @@ final class DifferentialDiffCreateController extends DifferentialController {
|
||||
}
|
||||
|
||||
if (!$errors) {
|
||||
$call = new ConduitCall(
|
||||
'differential.createrawdiff',
|
||||
array(
|
||||
'diff' => $diff,
|
||||
));
|
||||
$call->setUser($request->getUser());
|
||||
$result = $call->execute();
|
||||
|
||||
$path = id(new PhutilURI($result['uri']))->getPath();
|
||||
return id(new AphrontRedirectResponse())->setURI($path);
|
||||
try {
|
||||
$call = new ConduitCall(
|
||||
'differential.createrawdiff',
|
||||
array(
|
||||
'diff' => $diff,
|
||||
'repositoryPHID' => $repository_phid,
|
||||
'viewPolicy' => $request->getStr('viewPolicy'),));
|
||||
$call->setUser($viewer);
|
||||
$result = $call->execute();
|
||||
$path = id(new PhutilURI($result['uri']))->getPath();
|
||||
return id(new AphrontRedirectResponse())->setURI($path);
|
||||
} catch (PhabricatorApplicationTransactionValidationException $ex) {
|
||||
$validation_exception = $ex;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -68,15 +84,25 @@ final class DifferentialDiffCreateController extends DifferentialController {
|
||||
phutil_tag('tt', array(), 'hg diff --git'));
|
||||
}
|
||||
|
||||
if ($repository_phid) {
|
||||
$repository_value = $this->loadViewerHandles(array($repository_phid));
|
||||
}
|
||||
|
||||
$policies = id(new PhabricatorPolicyQuery())
|
||||
->setViewer($viewer)
|
||||
->setObject($diff_object)
|
||||
->execute();
|
||||
|
||||
$form
|
||||
->setAction('/differential/diff/create/')
|
||||
->setEncType('multipart/form-data')
|
||||
->setUser($request->getUser())
|
||||
->setUser($viewer)
|
||||
->appendInstructions($instructions)
|
||||
->appendChild(
|
||||
id(new AphrontFormTextAreaControl())
|
||||
->setLabel(pht('Raw Diff'))
|
||||
->setName('diff')
|
||||
->setValue($diff)
|
||||
->setHeight(AphrontFormTextAreaControl::HEIGHT_VERY_TALL)
|
||||
->setError($e_diff))
|
||||
->appendChild(
|
||||
@@ -84,6 +110,13 @@ final class DifferentialDiffCreateController extends DifferentialController {
|
||||
->setLabel(pht('Raw Diff From File'))
|
||||
->setName('diff-file')
|
||||
->setError($e_file))
|
||||
->appendChild(
|
||||
id(new AphrontFormTokenizerControl())
|
||||
->setName(id(new DifferentialRepositoryField())->getFieldKey())
|
||||
->setLabel(pht('Repository'))
|
||||
->setDatasource(new DiffusionRepositoryDatasource())
|
||||
->setValue($repository_value)
|
||||
->setLimit(1))
|
||||
->appendChild(
|
||||
id(new AphrontFormSubmitControl())
|
||||
->addCancelButton($cancel_uri)
|
||||
@@ -91,6 +124,7 @@ final class DifferentialDiffCreateController extends DifferentialController {
|
||||
|
||||
$form_box = id(new PHUIObjectBoxView())
|
||||
->setHeaderText(pht('Create New Diff'))
|
||||
->setValidationException($validation_exception)
|
||||
->setForm($form)
|
||||
->setFormErrors($errors);
|
||||
|
||||
|
@@ -80,6 +80,9 @@ final class DifferentialDiffViewController extends DifferentialController {
|
||||
->setAction('/differential/revision/edit/')
|
||||
->addHiddenInput('diffID', $diff->getID())
|
||||
->addHiddenInput('viaDiffView', 1)
|
||||
->addHiddenInput(
|
||||
id(new DifferentialRepositoryField())->getFieldKey(),
|
||||
$diff->getRepositoryPHID())
|
||||
->appendRemarkupInstructions(
|
||||
pht(
|
||||
'Review the diff for correctness. When you are satisfied, either '.
|
||||
|
@@ -71,16 +71,48 @@ final class DifferentialRevisionEditController
|
||||
->setViewer($viewer)
|
||||
->readFieldsFromStorage($revision);
|
||||
|
||||
if ($request->getStr('viaDiffView') && $diff) {
|
||||
$repo_key = id(new DifferentialRepositoryField())->getFieldKey();
|
||||
$repository_field = idx(
|
||||
$field_list->getFields(),
|
||||
$repo_key);
|
||||
if ($repository_field) {
|
||||
$repository_field->setValue($request->getStr($repo_key));
|
||||
}
|
||||
$view_policy_key = id(new DifferentialViewPolicyField())->getFieldKey();
|
||||
$view_policy_field = idx(
|
||||
$field_list->getFields(),
|
||||
$view_policy_key);
|
||||
if ($view_policy_field) {
|
||||
$view_policy_field->setValue($diff->getViewPolicy());
|
||||
}
|
||||
}
|
||||
|
||||
$validation_exception = null;
|
||||
if ($request->isFormPost() && !$request->getStr('viaDiffView')) {
|
||||
|
||||
$editor = id(new DifferentialTransactionEditor())
|
||||
->setActor($viewer)
|
||||
->setContentSourceFromRequest($request)
|
||||
->setContinueOnNoEffect(true);
|
||||
|
||||
$xactions = $field_list->buildFieldTransactionsFromRequest(
|
||||
new DifferentialTransaction(),
|
||||
$request);
|
||||
|
||||
if ($diff) {
|
||||
$repository_phid = null;
|
||||
$repository_tokenizer = $request->getArr(
|
||||
id(new DifferentialRepositoryField())->getFieldKey());
|
||||
if ($repository_tokenizer) {
|
||||
$repository_phid = reset($repository_tokenizer);
|
||||
}
|
||||
|
||||
$xactions[] = id(new DifferentialTransaction())
|
||||
->setTransactionType(DifferentialTransaction::TYPE_UPDATE)
|
||||
->setNewValue($diff->getPHID());
|
||||
|
||||
$editor->setRepositoryPHIDOverride($repository_phid);
|
||||
}
|
||||
|
||||
$comments = $request->getStr('comments');
|
||||
@@ -92,11 +124,6 @@ final class DifferentialRevisionEditController
|
||||
->setContent($comments));
|
||||
}
|
||||
|
||||
$editor = id(new DifferentialTransactionEditor())
|
||||
->setActor($viewer)
|
||||
->setContentSourceFromRequest($request)
|
||||
->setContinueOnNoEffect(true);
|
||||
|
||||
try {
|
||||
$editor->applyTransactions($revision, $xactions);
|
||||
$revision_uri = '/D'.$revision->getID();
|
||||
|
@@ -85,6 +85,7 @@ final class DifferentialRevertPlanField
|
||||
|
||||
public function renderEditControl(array $handles) {
|
||||
return id(new PhabricatorRemarkupControl())
|
||||
->setUser($this->getViewer())
|
||||
->setName($this->getFieldKey())
|
||||
->setValue($this->getValue())
|
||||
->setLabel($this->getFieldName());
|
||||
|
@@ -39,6 +39,7 @@ final class DifferentialSummaryField
|
||||
|
||||
public function renderEditControl(array $handles) {
|
||||
return id(new PhabricatorRemarkupControl())
|
||||
->setUser($this->getViewer())
|
||||
->setName($this->getFieldKey())
|
||||
->setValue($this->getValue())
|
||||
->setError($this->getFieldError())
|
||||
|
@@ -53,6 +53,7 @@ final class DifferentialTestPlanField
|
||||
|
||||
public function renderEditControl(array $handles) {
|
||||
return id(new PhabricatorRemarkupControl())
|
||||
->setUser($this->getViewer())
|
||||
->setName($this->getFieldKey())
|
||||
->setValue($this->getValue())
|
||||
->setError($this->getFieldError())
|
||||
|
@@ -1,80 +1,247 @@
|
||||
<?php
|
||||
|
||||
final class DifferentialDiffEditor extends PhabricatorEditor {
|
||||
final class DifferentialDiffEditor
|
||||
extends PhabricatorApplicationTransactionEditor {
|
||||
|
||||
private $contentSource;
|
||||
private $diffDataDict;
|
||||
private $lookupRepository = true;
|
||||
|
||||
public function setContentSource($content_source) {
|
||||
$this->contentSource = $content_source;
|
||||
public function setLookupRepository($bool) {
|
||||
$this->lookupRepository = $bool;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getContentSource() {
|
||||
return $this->contentSource;
|
||||
public function getEditorApplicationClass() {
|
||||
return 'PhabricatorDifferentialApplication';
|
||||
}
|
||||
|
||||
public function saveDiff(DifferentialDiff $diff) {
|
||||
$actor = $this->requireActor();
|
||||
public function getEditorObjectsDescription() {
|
||||
return pht('Differential Diffs');
|
||||
}
|
||||
|
||||
// Generate a PHID first, so the transcript will point at the object if
|
||||
// we deicde to preserve it.
|
||||
$phid = $diff->generatePHID();
|
||||
$diff->setPHID($phid);
|
||||
public function getTransactionTypes() {
|
||||
$types = parent::getTransactionTypes();
|
||||
|
||||
$types[] = PhabricatorTransactions::TYPE_VIEW_POLICY;
|
||||
$types[] = DifferentialDiffTransaction::TYPE_DIFF_CREATE;
|
||||
|
||||
return $types;
|
||||
}
|
||||
|
||||
protected function getCustomTransactionOldValue(
|
||||
PhabricatorLiskDAO $object,
|
||||
PhabricatorApplicationTransaction $xaction) {
|
||||
|
||||
switch ($xaction->getTransactionType()) {
|
||||
case DifferentialDiffTransaction::TYPE_DIFF_CREATE:
|
||||
return null;
|
||||
}
|
||||
|
||||
return parent::getCustomTransactionOldValue($object, $xaction);
|
||||
}
|
||||
|
||||
protected function getCustomTransactionNewValue(
|
||||
PhabricatorLiskDAO $object,
|
||||
PhabricatorApplicationTransaction $xaction) {
|
||||
|
||||
switch ($xaction->getTransactionType()) {
|
||||
case DifferentialDiffTransaction::TYPE_DIFF_CREATE:
|
||||
$this->diffDataDict = $xaction->getNewValue();
|
||||
return true;
|
||||
}
|
||||
|
||||
return parent::getCustomTransactionNewValue($object, $xaction);
|
||||
}
|
||||
|
||||
protected function applyCustomInternalTransaction(
|
||||
PhabricatorLiskDAO $object,
|
||||
PhabricatorApplicationTransaction $xaction) {
|
||||
|
||||
switch ($xaction->getTransactionType()) {
|
||||
case DifferentialDiffTransaction::TYPE_DIFF_CREATE:
|
||||
$dict = $this->diffDataDict;
|
||||
$this->updateDiffFromDict($object, $dict);
|
||||
return;
|
||||
case PhabricatorTransactions::TYPE_VIEW_POLICY:
|
||||
$object->setViewPolicy($xaction->getNewValue());
|
||||
return;
|
||||
}
|
||||
|
||||
return parent::applyCustomInternalTransaction($object, $xaction);
|
||||
}
|
||||
|
||||
protected function applyCustomExternalTransaction(
|
||||
PhabricatorLiskDAO $object,
|
||||
PhabricatorApplicationTransaction $xaction) {
|
||||
|
||||
switch ($xaction->getTransactionType()) {
|
||||
case DifferentialDiffTransaction::TYPE_DIFF_CREATE:
|
||||
case PhabricatorTransactions::TYPE_VIEW_POLICY:
|
||||
return;
|
||||
}
|
||||
|
||||
return parent::applyCustomExternalTransaction($object, $xaction);
|
||||
}
|
||||
|
||||
protected function applyFinalEffects(
|
||||
PhabricatorLiskDAO $object,
|
||||
array $xactions) {
|
||||
|
||||
// If we didn't get an explicit `repositoryPHID` (which means the client
|
||||
// is old, or couldn't figure out which repository the working copy
|
||||
// belongs to), apply heuristics to try to figure it out.
|
||||
|
||||
if ($this->lookupRepository && !$object->getRepositoryPHID()) {
|
||||
$repository = id(new DifferentialRepositoryLookup())
|
||||
->setDiff($object)
|
||||
->setViewer($this->getActor())
|
||||
->lookupRepository();
|
||||
if ($repository) {
|
||||
$object->setRepositoryPHID($repository->getPHID());
|
||||
$object->setRepositoryUUID($repository->getUUID());
|
||||
$object->save();
|
||||
}
|
||||
}
|
||||
|
||||
return $xactions;
|
||||
}
|
||||
|
||||
/**
|
||||
* We run Herald as part of transaction validation because Herald can
|
||||
* block diff creation for Differential diffs. Its important to do this
|
||||
* separately so no Herald logs are saved; these logs could expose
|
||||
* information the Herald rules are inteneded to block.
|
||||
*/
|
||||
protected function validateTransaction(
|
||||
PhabricatorLiskDAO $object,
|
||||
$type,
|
||||
array $xactions) {
|
||||
|
||||
$errors = parent::validateTransaction($object, $type, $xactions);
|
||||
|
||||
foreach ($xactions as $xaction) {
|
||||
switch ($type) {
|
||||
case DifferentialDiffTransaction::TYPE_DIFF_CREATE:
|
||||
$diff = clone $object;
|
||||
$diff = $this->updateDiffFromDict($diff, $xaction->getNewValue());
|
||||
|
||||
$adapter = $this->buildHeraldAdapter($diff, $xactions);
|
||||
$adapter->setContentSource($this->getContentSource());
|
||||
$adapter->setIsNewObject($this->getIsNewObject());
|
||||
|
||||
$engine = new HeraldEngine();
|
||||
|
||||
$rules = $engine->loadRulesForAdapter($adapter);
|
||||
$rules = mpull($rules, null, 'getID');
|
||||
|
||||
$effects = $engine->applyRules($rules, $adapter);
|
||||
|
||||
$blocking_effect = null;
|
||||
foreach ($effects as $effect) {
|
||||
if ($effect->getAction() == HeraldAdapter::ACTION_BLOCK) {
|
||||
$blocking_effect = $effect;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if ($blocking_effect) {
|
||||
$rule = idx($rules, $effect->getRuleID());
|
||||
if ($rule && strlen($rule->getName())) {
|
||||
$rule_name = $rule->getName();
|
||||
} else {
|
||||
$rule_name = pht('Unnamed Herald Rule');
|
||||
}
|
||||
|
||||
$message = $effect->getTarget();
|
||||
if (!strlen($message)) {
|
||||
$message = pht('(None.)');
|
||||
}
|
||||
|
||||
$errors[] = new PhabricatorApplicationTransactionValidationError(
|
||||
$type,
|
||||
pht('Rejected by Herald'),
|
||||
pht(
|
||||
"Creation of this diff was rejected by Herald rule %s.\n".
|
||||
" Rule: %s\n".
|
||||
"Reason: %s",
|
||||
'H'.$effect->getRuleID(),
|
||||
$rule_name,
|
||||
$message));
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return $errors;
|
||||
}
|
||||
|
||||
|
||||
protected function shouldPublishFeedStory(
|
||||
PhabricatorLiskDAO $object,
|
||||
array $xactions) {
|
||||
return false;
|
||||
}
|
||||
|
||||
protected function shouldSendMail(
|
||||
PhabricatorLiskDAO $object,
|
||||
array $xactions) {
|
||||
return false;
|
||||
}
|
||||
|
||||
protected function supportsSearch() {
|
||||
return false;
|
||||
}
|
||||
|
||||
/* -( Herald Integration )------------------------------------------------- */
|
||||
|
||||
/**
|
||||
* See @{method:validateTransaction}. The only Herald action is to block
|
||||
* the creation of Diffs. We thus have to be careful not to save any
|
||||
* data and do this validation very early.
|
||||
*/
|
||||
protected function shouldApplyHeraldRules(
|
||||
PhabricatorLiskDAO $object,
|
||||
array $xactions) {
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
protected function buildHeraldAdapter(
|
||||
PhabricatorLiskDAO $object,
|
||||
array $xactions) {
|
||||
|
||||
$adapter = id(new HeraldDifferentialDiffAdapter())
|
||||
->setDiff($diff);
|
||||
|
||||
$adapter->setContentSource($this->getContentSource());
|
||||
$adapter->setIsNewObject(true);
|
||||
|
||||
$engine = new HeraldEngine();
|
||||
|
||||
$rules = $engine->loadRulesForAdapter($adapter);
|
||||
$rules = mpull($rules, null, 'getID');
|
||||
|
||||
$effects = $engine->applyRules($rules, $adapter);
|
||||
|
||||
$blocking_effect = null;
|
||||
foreach ($effects as $effect) {
|
||||
if ($effect->getAction() == HeraldAdapter::ACTION_BLOCK) {
|
||||
$blocking_effect = $effect;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if ($blocking_effect) {
|
||||
$rule = idx($rules, $effect->getRuleID());
|
||||
if ($rule && strlen($rule->getName())) {
|
||||
$rule_name = $rule->getName();
|
||||
} else {
|
||||
$rule_name = pht('Unnamed Herald Rule');
|
||||
}
|
||||
|
||||
$message = $effect->getTarget();
|
||||
if (!strlen($message)) {
|
||||
$message = pht('(None.)');
|
||||
}
|
||||
|
||||
throw new DifferentialDiffCreationRejectException(
|
||||
pht(
|
||||
"Creation of this diff was rejected by Herald rule %s.\n".
|
||||
" Rule: %s\n".
|
||||
"Reason: %s",
|
||||
'H'.$effect->getRuleID(),
|
||||
$rule_name,
|
||||
$message));
|
||||
}
|
||||
|
||||
$diff->save();
|
||||
|
||||
// NOTE: We only save the transcript if we didn't block the diff.
|
||||
// Otherwise, we might save some of the diff's content in the transcript
|
||||
// table, which would defeat the purpose of allowing rules to block
|
||||
// storage of key material.
|
||||
|
||||
$engine->applyEffects($effects, $adapter, $rules);
|
||||
$xscript = $engine->getTranscript();
|
||||
->setDiff($object);
|
||||
|
||||
return $adapter;
|
||||
}
|
||||
|
||||
protected function didApplyHeraldRules(
|
||||
PhabricatorLiskDAO $object,
|
||||
HeraldAdapter $adapter,
|
||||
HeraldTranscript $transcript) {
|
||||
|
||||
$xactions = array();
|
||||
return $xactions;
|
||||
}
|
||||
|
||||
private function updateDiffFromDict(DifferentialDiff $diff, $dict) {
|
||||
$diff
|
||||
->setSourcePath(idx($dict, 'sourcePath'))
|
||||
->setSourceMachine(idx($dict, 'sourceMachine'))
|
||||
->setBranch(idx($dict, 'branch'))
|
||||
->setCreationMethod(idx($dict, 'creationMethod'))
|
||||
->setAuthorPHID(idx($dict, 'authorPHID', $this->getActor()))
|
||||
->setBookmark(idx($dict, 'bookmark'))
|
||||
->setRepositoryPHID(idx($dict, 'repositoryPHID'))
|
||||
->setRepositoryUUID(idx($dict, 'repositoryUUID'))
|
||||
->setSourceControlSystem(idx($dict, 'sourceControlSystem'))
|
||||
->setSourceControlPath(idx($dict, 'sourceControlPath'))
|
||||
->setSourceControlBaseRevision(idx($dict, 'sourceControlBaseRevision'))
|
||||
->setLintStatus(idx($dict, 'lintStatus'))
|
||||
->setUnitStatus(idx($dict, 'unitStatus'))
|
||||
->setArcanistProjectPHID(idx($dict, 'arcanistProjectPHID'));
|
||||
|
||||
return $diff;
|
||||
}
|
||||
}
|
||||
|
@@ -6,6 +6,7 @@ final class DifferentialTransactionEditor
|
||||
private $heraldEmailPHIDs;
|
||||
private $changedPriorToCommitURI;
|
||||
private $isCloseByCommit;
|
||||
private $repositoryPHIDOverride = false;
|
||||
|
||||
public function getEditorApplicationClass() {
|
||||
return 'PhabricatorDifferentialApplication';
|
||||
@@ -45,6 +46,11 @@ final class DifferentialTransactionEditor
|
||||
return $this->changedPriorToCommitURI;
|
||||
}
|
||||
|
||||
public function setRepositoryPHIDOverride($phid_or_null) {
|
||||
$this->repositoryPHIDOverride = $phid_or_null;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getTransactionTypes() {
|
||||
$types = parent::getTransactionTypes();
|
||||
|
||||
@@ -205,7 +211,11 @@ final class DifferentialTransactionEditor
|
||||
$diff = $this->requireDiff($xaction->getNewValue());
|
||||
|
||||
$object->setLineCount($diff->getLineCount());
|
||||
$object->setRepositoryPHID($diff->getRepositoryPHID());
|
||||
if ($this->repositoryPHIDOverride !== false) {
|
||||
$object->setRepositoryPHID($this->repositoryPHIDOverride);
|
||||
} else {
|
||||
$object->setRepositoryPHID($diff->getRepositoryPHID());
|
||||
}
|
||||
$object->setArcanistProjectPHID($diff->getArcanistProjectPHID());
|
||||
$object->attachActiveDiff($diff);
|
||||
|
||||
@@ -1523,8 +1533,6 @@ final class DifferentialTransactionEditor
|
||||
$adapter->setExplicitReviewers($reviewer_phids);
|
||||
$adapter->setForbiddenCCs($unsubscribed_phids);
|
||||
|
||||
$adapter->setIsNewObject($this->getIsNewObject());
|
||||
|
||||
return $adapter;
|
||||
}
|
||||
|
||||
|
@@ -90,6 +90,7 @@ final class DifferentialChangesetParser {
|
||||
const ATTR_DELETED = 'attr:deleted';
|
||||
const ATTR_UNCHANGED = 'attr:unchanged';
|
||||
const ATTR_WHITELINES = 'attr:white';
|
||||
const ATTR_MOVEAWAY = 'attr:moveaway';
|
||||
|
||||
const LINES_CONTEXT = 8;
|
||||
|
||||
@@ -438,6 +439,10 @@ final class DifferentialChangesetParser {
|
||||
return idx($this->specialAttributes, self::ATTR_WHITELINES, false);
|
||||
}
|
||||
|
||||
public function isMoveAway() {
|
||||
return idx($this->specialAttributes, self::ATTR_MOVEAWAY, false);
|
||||
}
|
||||
|
||||
private function applyIntraline(&$render, $intra, $corpus) {
|
||||
|
||||
foreach ($render as $key => $text) {
|
||||
@@ -594,6 +599,7 @@ final class DifferentialChangesetParser {
|
||||
}
|
||||
}
|
||||
|
||||
$moveaway = false;
|
||||
$changetype = $this->changeset->getChangeType();
|
||||
if ($changetype == DifferentialChangeType::TYPE_MOVE_AWAY) {
|
||||
// sometimes we show moved files as unchanged, sometimes deleted,
|
||||
@@ -601,12 +607,14 @@ final class DifferentialChangesetParser {
|
||||
// destination of the move. Rather than make a false claim,
|
||||
// omit the 'not changed' notice if this is the source of a move
|
||||
$unchanged = false;
|
||||
$moveaway = true;
|
||||
}
|
||||
|
||||
$this->setSpecialAttributes(array(
|
||||
self::ATTR_UNCHANGED => $unchanged,
|
||||
self::ATTR_DELETED => $hunk_parser->getIsDeleted(),
|
||||
self::ATTR_WHITELINES => !$hunk_parser->getHasTextChanges(),
|
||||
self::ATTR_MOVEAWAY => $moveaway,
|
||||
));
|
||||
|
||||
$hunk_parser->generateIntraLineDiffs();
|
||||
@@ -775,6 +783,8 @@ final class DifferentialChangesetParser {
|
||||
$shield = $renderer->renderShield(
|
||||
pht('The contents of this file were not changed.'),
|
||||
$type);
|
||||
} else if ($this->isMoveAway()) {
|
||||
$shield = null;
|
||||
} else if ($this->isWhitespaceOnly()) {
|
||||
$shield = $renderer->renderShield(
|
||||
pht('This file was changed only by adding or removing whitespace.'),
|
||||
|
@@ -40,7 +40,9 @@ final class DifferentialHunkParserTestCase extends PhabricatorTestCase {
|
||||
throw new Exception("Expected 1 changeset for '{$name}'!");
|
||||
}
|
||||
|
||||
$diff = DifferentialDiff::newFromRawChanges($changes);
|
||||
$diff = DifferentialDiff::newFromRawChanges(
|
||||
PhabricatorUser::getOmnipotentUser(),
|
||||
$changes);
|
||||
return head($diff->getChangesets())->getHunks();
|
||||
}
|
||||
|
||||
|
@@ -62,7 +62,6 @@ final class DifferentialDiffQuery
|
||||
|
||||
foreach ($diffs as $key => $diff) {
|
||||
if (!$diff->getRevisionID()) {
|
||||
$diff->attachRevision(null);
|
||||
continue;
|
||||
}
|
||||
|
||||
|
@@ -409,16 +409,8 @@ abstract class DifferentialChangesetHTMLRenderer
|
||||
$content)));
|
||||
}
|
||||
|
||||
private function renderColgroup() {
|
||||
return phutil_tag('colgroup', array(), array(
|
||||
phutil_tag('col', array('class' => 'num')),
|
||||
phutil_tag('col', array('class' => 'left')),
|
||||
phutil_tag('col', array('class' => 'num')),
|
||||
phutil_tag('col', array('class' => 'copy')),
|
||||
phutil_tag('col', array('class' => 'right')),
|
||||
phutil_tag('col', array('class' => 'cov')),
|
||||
));
|
||||
}
|
||||
abstract protected function renderColgroup();
|
||||
|
||||
|
||||
protected function wrapChangeInTable($content) {
|
||||
if (!$content) {
|
||||
|
@@ -7,6 +7,14 @@ final class DifferentialChangesetOneUpRenderer
|
||||
return true;
|
||||
}
|
||||
|
||||
protected function renderColgroup() {
|
||||
return phutil_tag('colgroup', array(), array(
|
||||
phutil_tag('col', array('class' => 'num')),
|
||||
phutil_tag('col', array('class' => 'num')),
|
||||
phutil_tag('col', array('class' => 'unified')),
|
||||
));
|
||||
}
|
||||
|
||||
public function renderTextChange(
|
||||
$range_start,
|
||||
$range_len,
|
||||
|
@@ -7,6 +7,17 @@ final class DifferentialChangesetTwoUpRenderer
|
||||
return false;
|
||||
}
|
||||
|
||||
protected function renderColgroup() {
|
||||
return phutil_tag('colgroup', array(), array(
|
||||
phutil_tag('col', array('class' => 'num')),
|
||||
phutil_tag('col', array('class' => 'left')),
|
||||
phutil_tag('col', array('class' => 'num')),
|
||||
phutil_tag('col', array('class' => 'copy')),
|
||||
phutil_tag('col', array('class' => 'right')),
|
||||
phutil_tag('col', array('class' => 'cov')),
|
||||
));
|
||||
}
|
||||
|
||||
public function renderTextChange(
|
||||
$range_start,
|
||||
$range_len,
|
||||
|
@@ -33,6 +33,8 @@ final class DifferentialDiff
|
||||
|
||||
protected $description;
|
||||
|
||||
protected $viewPolicy;
|
||||
|
||||
private $unsavedChangesets = array();
|
||||
private $changesets = self::ATTACHABLE;
|
||||
private $arcanistProject = self::ATTACHABLE;
|
||||
@@ -136,10 +138,27 @@ final class DifferentialDiff
|
||||
return $ret;
|
||||
}
|
||||
|
||||
public static function newFromRawChanges(array $changes) {
|
||||
assert_instances_of($changes, 'ArcanistDiffChange');
|
||||
$diff = new DifferentialDiff();
|
||||
public static function initializeNewDiff(PhabricatorUser $actor) {
|
||||
$app = id(new PhabricatorApplicationQuery())
|
||||
->setViewer($actor)
|
||||
->withClasses(array('PhabricatorDifferentialApplication'))
|
||||
->executeOne();
|
||||
$view_policy = $app->getPolicy(
|
||||
DifferentialDefaultViewCapability::CAPABILITY);
|
||||
|
||||
$diff = id(new DifferentialDiff())
|
||||
->setViewPolicy($view_policy);
|
||||
|
||||
return $diff;
|
||||
}
|
||||
|
||||
public static function newFromRawChanges(
|
||||
PhabricatorUser $actor,
|
||||
array $changes) {
|
||||
|
||||
assert_instances_of($changes, 'ArcanistDiffChange');
|
||||
|
||||
$diff = self::initializeNewDiff($actor);
|
||||
// There may not be any changes; initialize the changesets list so that
|
||||
// we don't throw later when accessing it.
|
||||
$diff->attachChangesets(array());
|
||||
@@ -289,6 +308,10 @@ final class DifferentialDiff
|
||||
return $changes;
|
||||
}
|
||||
|
||||
public function hasRevision() {
|
||||
return $this->revision !== self::ATTACHABLE;
|
||||
}
|
||||
|
||||
public function getRevision() {
|
||||
return $this->assertAttached($this->revision);
|
||||
}
|
||||
@@ -318,27 +341,27 @@ final class DifferentialDiff
|
||||
}
|
||||
|
||||
public function getPolicy($capability) {
|
||||
if ($this->getRevision()) {
|
||||
if ($this->hasRevision()) {
|
||||
return $this->getRevision()->getPolicy($capability);
|
||||
}
|
||||
|
||||
return PhabricatorPolicies::POLICY_USER;
|
||||
return $this->viewPolicy;
|
||||
}
|
||||
|
||||
public function hasAutomaticCapability($capability, PhabricatorUser $viewer) {
|
||||
if ($this->getRevision()) {
|
||||
if ($this->hasRevision()) {
|
||||
return $this->getRevision()->hasAutomaticCapability($capability, $viewer);
|
||||
}
|
||||
|
||||
return false;
|
||||
return ($this->getAuthorPHID() == $viewer->getPhid());
|
||||
}
|
||||
|
||||
public function describeAutomaticCapability($capability) {
|
||||
if ($this->getRevision()) {
|
||||
if ($this->hasRevision()) {
|
||||
return pht(
|
||||
'This diff is attached to a revision, and inherits its policies.');
|
||||
}
|
||||
return null;
|
||||
return pht('The author of a diff can see it.');
|
||||
}
|
||||
|
||||
|
||||
|
@@ -0,0 +1,64 @@
|
||||
<?php
|
||||
|
||||
final class DifferentialDiffTransaction
|
||||
extends PhabricatorApplicationTransaction {
|
||||
|
||||
const TYPE_DIFF_CREATE = 'differential:diff:create';
|
||||
|
||||
public function getApplicationName() {
|
||||
return 'differential';
|
||||
}
|
||||
|
||||
public function getApplicationTransactionType() {
|
||||
return DifferentialDiffPHIDType::TYPECONST;
|
||||
}
|
||||
|
||||
public function shouldHideForMail(array $xactions) {
|
||||
return true;
|
||||
}
|
||||
|
||||
public function getActionName() {
|
||||
switch ($this->getTransactionType()) {
|
||||
case self::TYPE_DIFF_CREATE;
|
||||
return pht('Created');
|
||||
}
|
||||
|
||||
return parent::getActionName();
|
||||
}
|
||||
|
||||
public function getTitle() {
|
||||
$author_phid = $this->getAuthorPHID();
|
||||
$author_handle = $this->renderHandleLink($author_phid);
|
||||
|
||||
$old = $this->getOldValue();
|
||||
$new = $this->getNewValue();
|
||||
|
||||
switch ($this->getTransactionType()) {
|
||||
case self::TYPE_DIFF_CREATE;
|
||||
return pht(
|
||||
'%s created this diff.',
|
||||
$author_handle);
|
||||
}
|
||||
|
||||
return parent::getTitle();
|
||||
}
|
||||
|
||||
public function getIcon() {
|
||||
switch ($this->getTransactionType()) {
|
||||
case self::TYPE_DIFF_CREATE:
|
||||
return 'fa-refresh';
|
||||
}
|
||||
|
||||
return parent::getIcon();
|
||||
}
|
||||
|
||||
public function getColor() {
|
||||
switch ($this->getTransactionType()) {
|
||||
case self::TYPE_DIFF_CREATE:
|
||||
return PhabricatorTransactions::COLOR_SKY;
|
||||
}
|
||||
|
||||
return parent::getColor();
|
||||
}
|
||||
|
||||
}
|
@@ -7,6 +7,7 @@ final class DifferentialDiffTestCase extends ArcanistPhutilTestCase {
|
||||
$parser = new ArcanistDiffParser();
|
||||
|
||||
$diff = DifferentialDiff::newFromRawChanges(
|
||||
PhabricatorUser::getOmnipotentUser(),
|
||||
$parser->parseDiff(Filesystem::readFile($root.'lint_engine.diff')));
|
||||
$copies = idx(head($diff->getChangesets())->getMetadata(), 'copy:lines');
|
||||
|
||||
@@ -46,7 +47,9 @@ index 123457..0000000
|
||||
{$oblock}
|
||||
EODIFF;
|
||||
|
||||
$diff = DifferentialDiff::newFromRawChanges($parser->parseDiff($raw_diff));
|
||||
$diff = DifferentialDiff::newFromRawChanges(
|
||||
PhabricatorUser::getOmnipotentUser(),
|
||||
$parser->parseDiff($raw_diff));
|
||||
|
||||
$this->assertTrue(true);
|
||||
}
|
||||
|
@@ -22,7 +22,9 @@ final class DiffusionChangeController extends DiffusionController {
|
||||
$drequest->updateSymbolicCommit($data['effectiveCommit']);
|
||||
|
||||
$raw_changes = ArcanistDiffChange::newFromConduit($data['changes']);
|
||||
$diff = DifferentialDiff::newFromRawChanges($raw_changes);
|
||||
$diff = DifferentialDiff::newFromRawChanges(
|
||||
$viewer,
|
||||
$raw_changes);
|
||||
$changesets = $diff->getChangesets();
|
||||
$changeset = reset($changesets);
|
||||
|
||||
|
@@ -276,6 +276,7 @@ final class DiffusionCommitController extends DiffusionController {
|
||||
$content[] = $change_panel;
|
||||
|
||||
$changesets = DiffusionPathChange::convertToDifferentialChangesets(
|
||||
$user,
|
||||
$changes);
|
||||
|
||||
$vcs = $repository->getVersionControlSystem();
|
||||
@@ -419,7 +420,6 @@ final class DiffusionCommitController extends DiffusionController {
|
||||
->withSourcePHIDs(array($commit_phid))
|
||||
->withEdgeTypes(array(
|
||||
DiffusionCommitHasTaskEdgeType::EDGECONST,
|
||||
PhabricatorEdgeConfig::TYPE_COMMIT_HAS_PROJECT,
|
||||
PhabricatorEdgeConfig::TYPE_COMMIT_HAS_DREV,
|
||||
));
|
||||
|
||||
@@ -427,8 +427,6 @@ final class DiffusionCommitController extends DiffusionController {
|
||||
|
||||
$task_phids = array_keys(
|
||||
$edges[$commit_phid][DiffusionCommitHasTaskEdgeType::EDGECONST]);
|
||||
$proj_phids = array_keys(
|
||||
$edges[$commit_phid][PhabricatorEdgeConfig::TYPE_COMMIT_HAS_PROJECT]);
|
||||
$revision_phid = key(
|
||||
$edges[$commit_phid][PhabricatorEdgeConfig::TYPE_COMMIT_HAS_DREV]);
|
||||
|
||||
@@ -628,15 +626,6 @@ final class DiffusionCommitController extends DiffusionController {
|
||||
$props['Tasks'] = $task_list;
|
||||
}
|
||||
|
||||
if ($proj_phids) {
|
||||
$proj_list = array();
|
||||
foreach ($proj_phids as $phid) {
|
||||
$proj_list[] = $handles[$phid]->renderLink();
|
||||
}
|
||||
$proj_list = phutil_implode_html(phutil_tag('br'), $proj_list);
|
||||
$props['Projects'] = $proj_list;
|
||||
}
|
||||
|
||||
return $props;
|
||||
}
|
||||
|
||||
|
@@ -22,7 +22,7 @@ final class DiffusionCommitEditController extends DiffusionController {
|
||||
}
|
||||
|
||||
$commit_phid = $commit->getPHID();
|
||||
$edge_type = PhabricatorEdgeConfig::TYPE_COMMIT_HAS_PROJECT;
|
||||
$edge_type = PhabricatorProjectObjectHasProjectEdgeType::EDGECONST;
|
||||
$current_proj_phids = PhabricatorEdgeQuery::loadDestinationPHIDs(
|
||||
$commit_phid,
|
||||
$edge_type);
|
||||
@@ -30,23 +30,17 @@ final class DiffusionCommitEditController extends DiffusionController {
|
||||
$proj_t_values = $handles;
|
||||
|
||||
if ($request->isFormPost()) {
|
||||
$xactions = array();
|
||||
$proj_phids = $request->getArr('projects');
|
||||
$new_proj_phids = array_values($proj_phids);
|
||||
$rem_proj_phids = array_diff($current_proj_phids,
|
||||
$new_proj_phids);
|
||||
|
||||
$editor = id(new PhabricatorEdgeEditor());
|
||||
foreach ($rem_proj_phids as $phid) {
|
||||
$editor->removeEdge($commit_phid, $edge_type, $phid);
|
||||
}
|
||||
foreach ($new_proj_phids as $phid) {
|
||||
$editor->addEdge($commit_phid, $edge_type, $phid);
|
||||
}
|
||||
$editor->save();
|
||||
|
||||
id(new PhabricatorSearchIndexer())
|
||||
->queueDocumentForIndexing($commit->getPHID());
|
||||
|
||||
$xactions[] = id(new PhabricatorAuditTransaction())
|
||||
->setTransactionType(PhabricatorTransactions::TYPE_EDGE)
|
||||
->setMetadataValue('edge:type', $edge_type)
|
||||
->setNewValue(array('=' => array_fuse($proj_phids)));
|
||||
$editor = id(new PhabricatorAuditEditor())
|
||||
->setActor($user)
|
||||
->setContinueOnNoEffect(true)
|
||||
->setContentSourceFromRequest($request);
|
||||
$xactions = $editor->applyTransactions($commit, $xactions);
|
||||
return id(new AphrontRedirectResponse())
|
||||
->setURI('/r'.$callsign.$commit->getCommitIdentifier());
|
||||
}
|
||||
|
@@ -54,7 +54,9 @@ final class DiffusionDiffController extends DiffusionController {
|
||||
));
|
||||
$drequest->updateSymbolicCommit($data['effectiveCommit']);
|
||||
$raw_changes = ArcanistDiffChange::newFromConduit($data['changes']);
|
||||
$diff = DifferentialDiff::newFromRawChanges($raw_changes);
|
||||
$diff = DifferentialDiff::newFromRawChanges(
|
||||
$user,
|
||||
$raw_changes);
|
||||
$changesets = $diff->getChangesets();
|
||||
$changeset = reset($changesets);
|
||||
|
||||
|
@@ -104,6 +104,8 @@ final class DiffusionRepositoryCreateController
|
||||
$type_local_path = PhabricatorRepositoryTransaction::TYPE_LOCAL_PATH;
|
||||
$type_remote_uri = PhabricatorRepositoryTransaction::TYPE_REMOTE_URI;
|
||||
$type_hosting = PhabricatorRepositoryTransaction::TYPE_HOSTING;
|
||||
$type_http = PhabricatorRepositoryTransaction::TYPE_PROTOCOL_HTTP;
|
||||
$type_ssh = PhabricatorRepositoryTransaction::TYPE_PROTOCOL_SSH;
|
||||
$type_credential = PhabricatorRepositoryTransaction::TYPE_CREDENTIAL;
|
||||
$type_view = PhabricatorTransactions::TYPE_VIEW_POLICY;
|
||||
$type_edit = PhabricatorTransactions::TYPE_EDIT_POLICY;
|
||||
@@ -157,6 +159,26 @@ final class DiffusionRepositoryCreateController
|
||||
$xactions[] = id(clone $template)
|
||||
->setTransactionType($type_hosting)
|
||||
->setNewValue(true);
|
||||
$vcs = $form->getPage('vcs')->getControl('vcs')->getValue();
|
||||
if ($vcs != PhabricatorRepositoryType::REPOSITORY_TYPE_SVN) {
|
||||
if (PhabricatorEnv::getEnvConfig('diffusion.allow-http-auth')) {
|
||||
$v_http_mode = PhabricatorRepository::SERVE_READWRITE;
|
||||
} else {
|
||||
$v_http_mode = PhabricatorRepository::SERVE_OFF;
|
||||
}
|
||||
$xactions[] = id(clone $template)
|
||||
->setTransactionType($type_http)
|
||||
->setNewValue($v_http_mode);
|
||||
}
|
||||
|
||||
if (PhabricatorEnv::getEnvConfig('diffusion.ssh-user')) {
|
||||
$v_ssh_mode = PhabricatorRepository::SERVE_READWRITE;
|
||||
} else {
|
||||
$v_ssh_mode = PhabricatorRepository::SERVE_OFF;
|
||||
}
|
||||
$xactions[] = id(clone $template)
|
||||
->setTransactionType($type_ssh)
|
||||
->setNewValue($v_ssh_mode);
|
||||
}
|
||||
|
||||
if ($is_auth) {
|
||||
|
@@ -120,6 +120,7 @@ final class DiffusionRepositoryEditBasicController
|
||||
$form
|
||||
->appendChild(
|
||||
id(new PhabricatorRemarkupControl())
|
||||
->setUser($user)
|
||||
->setName('description')
|
||||
->setLabel(pht('Description'))
|
||||
->setValue($v_desc))
|
||||
|
@@ -142,10 +142,12 @@ final class DiffusionPathChange {
|
||||
return array_select_keys($result, $direct);
|
||||
}
|
||||
|
||||
final public static function convertToDifferentialChangesets(array $changes) {
|
||||
final public static function convertToDifferentialChangesets(
|
||||
PhabricatorUser $user,
|
||||
array $changes) {
|
||||
assert_instances_of($changes, 'DiffusionPathChange');
|
||||
$arcanist_changes = self::convertToArcanistChanges($changes);
|
||||
$diff = DifferentialDiff::newFromRawChanges($arcanist_changes);
|
||||
$diff = DifferentialDiff::newFromRawChanges($user, $arcanist_changes);
|
||||
return $diff->getChangesets();
|
||||
}
|
||||
|
||||
|
@@ -1115,7 +1115,9 @@ final class DiffusionCommitHookEngine extends Phobject {
|
||||
|
||||
$parser = new ArcanistDiffParser();
|
||||
$changes = $parser->parseDiff($raw_diff);
|
||||
$diff = DifferentialDiff::newFromRawChanges($changes);
|
||||
$diff = DifferentialDiff::newFromRawChanges(
|
||||
$this->getViewer(),
|
||||
$changes);
|
||||
return $diff->getChangesets();
|
||||
}
|
||||
|
||||
|
@@ -140,6 +140,10 @@ final class DivinerSymbolRemarkupRule extends PhutilRemarkupRule {
|
||||
$link = $title;
|
||||
}
|
||||
} else if ($href) {
|
||||
if ($this->getEngine()->isHTMLMailMode()) {
|
||||
$href = PhabricatorEnv::getProductionURI($href);
|
||||
}
|
||||
|
||||
$link = $this->newTag(
|
||||
'a',
|
||||
array(
|
||||
|
@@ -11,7 +11,7 @@ final class PhabricatorFilesApplication extends PhabricatorApplication {
|
||||
}
|
||||
|
||||
public function getShortDescription() {
|
||||
return 'Store and Share Files';
|
||||
return pht('Store and Share Files');
|
||||
}
|
||||
|
||||
public function getIconName() {
|
||||
@@ -40,6 +40,15 @@ final class PhabricatorFilesApplication extends PhabricatorApplication {
|
||||
);
|
||||
}
|
||||
|
||||
protected function getCustomCapabilities() {
|
||||
return array(
|
||||
FilesDefaultViewCapability::CAPABILITY => array(
|
||||
'caption' => pht(
|
||||
'Default view policy for newly created files.'),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
public function getRoutes() {
|
||||
return array(
|
||||
'/F(?P<id>[1-9]\d*)' => 'PhabricatorFileInfoController',
|
||||
|
@@ -0,0 +1,16 @@
|
||||
<?php
|
||||
|
||||
final class FilesDefaultViewCapability
|
||||
extends PhabricatorPolicyCapability {
|
||||
|
||||
const CAPABILITY = 'files.default.view';
|
||||
|
||||
public function getCapabilityName() {
|
||||
return pht('Default View Policy');
|
||||
}
|
||||
|
||||
public function shouldAllowPublicPolicySetting() {
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
@@ -6,7 +6,7 @@ final class PhabricatorFileUploadController extends PhabricatorFileController {
|
||||
$request = $this->getRequest();
|
||||
$viewer = $request->getUser();
|
||||
|
||||
$file = new PhabricatorFile();
|
||||
$file = PhabricatorFile::initializeNewFile();
|
||||
|
||||
$e_file = true;
|
||||
$errors = array();
|
||||
|
@@ -53,7 +53,16 @@ final class PhabricatorFile extends PhabricatorFileDAO
|
||||
private $originalFile = self::ATTACHABLE;
|
||||
|
||||
public static function initializeNewFile() {
|
||||
$app = id(new PhabricatorApplicationQuery())
|
||||
->setViewer(PhabricatorUser::getOmnipotentUser())
|
||||
->withClasses(array('PhabricatorFilesApplication'))
|
||||
->executeOne();
|
||||
|
||||
$view_policy = $app->getPolicy(
|
||||
FilesDefaultViewCapability::CAPABILITY);
|
||||
|
||||
return id(new PhabricatorFile())
|
||||
->setViewPolicy($view_policy)
|
||||
->attachOriginalFile(null)
|
||||
->attachObjects(array())
|
||||
->attachObjectPHIDs(array());
|
||||
|
@@ -200,11 +200,13 @@ final class FundInitiativeEditController
|
||||
->setOptions($merchant_options))
|
||||
->appendChild(
|
||||
id(new PhabricatorRemarkupControl())
|
||||
->setUser($viewer)
|
||||
->setName('description')
|
||||
->setLabel(pht('Description'))
|
||||
->setValue($v_desc))
|
||||
->appendChild(
|
||||
id(new PhabricatorRemarkupControl())
|
||||
->setUser($viewer)
|
||||
->setName('risks')
|
||||
->setLabel(pht('Risks/Challenges'))
|
||||
->setValue($v_risk))
|
||||
|
@@ -155,6 +155,7 @@ final class HarbormasterStepEditController extends HarbormasterController {
|
||||
$form
|
||||
->appendChild(
|
||||
id(new PhabricatorRemarkupControl())
|
||||
->setUser($viewer)
|
||||
->setName('description')
|
||||
->setLabel(pht('Description'))
|
||||
->setError($e_description)
|
||||
|
@@ -40,8 +40,10 @@ abstract class HeraldAdapter {
|
||||
const FIELD_COMMITTER_RAW = 'committer-raw';
|
||||
const FIELD_IS_NEW_OBJECT = 'new-object';
|
||||
const FIELD_TASK_PRIORITY = 'taskpriority';
|
||||
const FIELD_TASK_STATUS = 'taskstatus';
|
||||
const FIELD_ARCANIST_PROJECT = 'arcanist-project';
|
||||
const FIELD_PUSHER_IS_COMMITTER = 'pusher-is-committer';
|
||||
const FIELD_PATH = 'path';
|
||||
|
||||
const CONDITION_CONTAINS = 'contains';
|
||||
const CONDITION_NOT_CONTAINS = '!contains';
|
||||
@@ -95,6 +97,7 @@ abstract class HeraldAdapter {
|
||||
const VALUE_USER_OR_PROJECT = 'userorproject';
|
||||
const VALUE_BUILD_PLAN = 'buildplan';
|
||||
const VALUE_TASK_PRIORITY = 'taskpriority';
|
||||
const VALUE_TASK_STATUS = 'taskstatus';
|
||||
const VALUE_ARCANIST_PROJECT = 'arcanistprojects';
|
||||
const VALUE_LEGAL_DOCUMENTS = 'legaldocuments';
|
||||
|
||||
@@ -310,8 +313,10 @@ abstract class HeraldAdapter {
|
||||
self::FIELD_COMMITTER_RAW => pht('Raw committer name'),
|
||||
self::FIELD_IS_NEW_OBJECT => pht('Is newly created?'),
|
||||
self::FIELD_TASK_PRIORITY => pht('Task priority'),
|
||||
self::FIELD_TASK_STATUS => pht('Task status'),
|
||||
self::FIELD_ARCANIST_PROJECT => pht('Arcanist Project'),
|
||||
self::FIELD_PUSHER_IS_COMMITTER => pht('Pusher same as committer'),
|
||||
self::FIELD_PATH => pht('Path'),
|
||||
) + $this->getCustomFieldNameMap();
|
||||
}
|
||||
|
||||
@@ -353,6 +358,7 @@ abstract class HeraldAdapter {
|
||||
case self::FIELD_BODY:
|
||||
case self::FIELD_COMMITTER_RAW:
|
||||
case self::FIELD_AUTHOR_RAW:
|
||||
case self::FIELD_PATH:
|
||||
return array(
|
||||
self::CONDITION_CONTAINS,
|
||||
self::CONDITION_NOT_CONTAINS,
|
||||
@@ -363,6 +369,7 @@ abstract class HeraldAdapter {
|
||||
case self::FIELD_REVIEWER:
|
||||
case self::FIELD_PUSHER:
|
||||
case self::FIELD_TASK_PRIORITY:
|
||||
case self::FIELD_TASK_STATUS:
|
||||
case self::FIELD_ARCANIST_PROJECT:
|
||||
return array(
|
||||
self::CONDITION_IS_ANY,
|
||||
@@ -840,6 +847,8 @@ abstract class HeraldAdapter {
|
||||
return self::VALUE_REPOSITORY;
|
||||
case self::FIELD_TASK_PRIORITY:
|
||||
return self::VALUE_TASK_PRIORITY;
|
||||
case self::FIELD_TASK_STATUS:
|
||||
return self::VALUE_TASK_STATUS;
|
||||
case self::FIELD_ARCANIST_PROJECT:
|
||||
return self::VALUE_ARCANIST_PROJECT;
|
||||
default:
|
||||
@@ -1159,6 +1168,15 @@ abstract class HeraldAdapter {
|
||||
}
|
||||
}
|
||||
break;
|
||||
case self::FIELD_TASK_STATUS:
|
||||
$status_map = ManiphestTaskStatus::getTaskStatusMap();
|
||||
foreach ($value as $index => $val) {
|
||||
$name = idx($status_map, $val);
|
||||
if ($name) {
|
||||
$value[$index] = $name;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case HeraldPreCommitRefAdapter::FIELD_REF_CHANGE:
|
||||
$change_map =
|
||||
PhabricatorRepositoryPushLog::getHeraldChangeFlagConditionOptions();
|
||||
|
@@ -346,7 +346,9 @@ final class HeraldCommitAdapter extends HeraldAdapter {
|
||||
$parser = new ArcanistDiffParser();
|
||||
$changes = $parser->parseDiff($raw);
|
||||
|
||||
$diff = DifferentialDiff::newFromRawChanges($changes);
|
||||
$diff = DifferentialDiff::newFromRawChanges(
|
||||
PhabricatorUser::getOmnipotentUser(),
|
||||
$changes);
|
||||
return $diff;
|
||||
}
|
||||
|
||||
|
@@ -89,6 +89,7 @@ final class HeraldManiphestTaskAdapter extends HeraldAdapter {
|
||||
self::FIELD_CONTENT_SOURCE,
|
||||
self::FIELD_PROJECTS,
|
||||
self::FIELD_TASK_PRIORITY,
|
||||
self::FIELD_TASK_STATUS,
|
||||
self::FIELD_IS_NEW_OBJECT,
|
||||
),
|
||||
parent::getFields());
|
||||
@@ -145,6 +146,8 @@ final class HeraldManiphestTaskAdapter extends HeraldAdapter {
|
||||
PhabricatorProjectObjectHasProjectEdgeType::EDGECONST);
|
||||
case self::FIELD_TASK_PRIORITY:
|
||||
return $this->getTask()->getPriority();
|
||||
case self::FIELD_TASK_STATUS:
|
||||
return $this->getTask()->getStatus();
|
||||
}
|
||||
|
||||
return parent::getHeraldField($field);
|
||||
|
@@ -358,6 +358,14 @@ final class HeraldRuleController extends HeraldController {
|
||||
}
|
||||
$value = $value_map;
|
||||
break;
|
||||
case HeraldAdapter::FIELD_TASK_STATUS:
|
||||
$value_map = array();
|
||||
$status_map = ManiphestTaskStatus::getTaskStatusMap();
|
||||
foreach ($value as $status) {
|
||||
$value_map[$status] = idx($status_map, $status);
|
||||
}
|
||||
$value = $value_map;
|
||||
break;
|
||||
default:
|
||||
if (is_array($value)) {
|
||||
$value_map = array();
|
||||
@@ -586,6 +594,7 @@ final class HeraldRuleController extends HeraldController {
|
||||
'repository' => new DiffusionRepositoryDatasource(),
|
||||
'legaldocuments' => new LegalpadDocumentDatasource(),
|
||||
'taskpriority' => new ManiphestTaskPriorityDatasource(),
|
||||
'taskstatus' => new ManiphestTaskStatusDatasource(),
|
||||
'buildplan' => new HarbormasterBuildPlanDatasource(),
|
||||
'arcanistprojects' => new DiffusionArcanistProjectDatasource(),
|
||||
'package' => new PhabricatorOwnersPackageDatasource(),
|
||||
|
@@ -47,6 +47,9 @@ final class HeraldTestConsoleController extends HeraldController {
|
||||
} else if ($object instanceof PholioMock) {
|
||||
$adapter = id(new HeraldPholioMockAdapter())
|
||||
->setMock($object);
|
||||
} else if ($object instanceof PhrictionDocument) {
|
||||
$adapter = id(new PhrictionDocumentHeraldAdapter())
|
||||
->setDocument($object);
|
||||
} else {
|
||||
throw new Exception('Can not build adapter for object!');
|
||||
}
|
||||
|
@@ -18,7 +18,7 @@ final class HeraldRule extends HeraldDAO
|
||||
protected $isDisabled = 0;
|
||||
protected $triggerObjectPHID;
|
||||
|
||||
protected $configVersion = 37;
|
||||
protected $configVersion = 38;
|
||||
|
||||
// PHIDs for which this rule has been applied
|
||||
private $ruleApplied = self::ATTACHABLE;
|
||||
|
@@ -143,20 +143,22 @@ final class LegalpadDocumentEditController extends LegalpadController {
|
||||
$form
|
||||
->appendChild(
|
||||
id(new PhabricatorRemarkupControl())
|
||||
->setID('preamble')
|
||||
->setLabel(pht('Preamble'))
|
||||
->setValue($v_preamble)
|
||||
->setName('preamble')
|
||||
->setCaption(
|
||||
pht('Optional help text for users signing this document.')))
|
||||
->setUser($user)
|
||||
->setID('preamble')
|
||||
->setLabel(pht('Preamble'))
|
||||
->setValue($v_preamble)
|
||||
->setName('preamble')
|
||||
->setCaption(
|
||||
pht('Optional help text for users signing this document.')))
|
||||
->appendChild(
|
||||
id(new PhabricatorRemarkupControl())
|
||||
->setID('document-text')
|
||||
->setLabel(pht('Document Body'))
|
||||
->setError($e_text)
|
||||
->setValue($text)
|
||||
->setHeight(AphrontFormTextAreaControl::HEIGHT_VERY_TALL)
|
||||
->setName('text'));
|
||||
->setUser($user)
|
||||
->setID('document-text')
|
||||
->setLabel(pht('Document Body'))
|
||||
->setError($e_text)
|
||||
->setValue($text)
|
||||
->setHeight(AphrontFormTextAreaControl::HEIGHT_VERY_TALL)
|
||||
->setName('text'));
|
||||
|
||||
$policies = id(new PhabricatorPolicyQuery())
|
||||
->setViewer($user)
|
||||
|
@@ -14,7 +14,11 @@ final class PhabricatorIconRemarkupRule extends PhutilRemarkupRule {
|
||||
}
|
||||
|
||||
public function markupIcon($matches) {
|
||||
if (!$this->isFlatText($matches[0])) {
|
||||
$engine = $this->getEngine();
|
||||
$text_mode = $engine->isTextMode();
|
||||
$mail_mode = $engine->isHTMLMailMode();
|
||||
|
||||
if (!$this->isFlatText($matches[0]) || $text_mode || $mail_mode) {
|
||||
return $matches[0];
|
||||
}
|
||||
|
||||
@@ -69,6 +73,7 @@ final class PhabricatorIconRemarkupRule extends PhutilRemarkupRule {
|
||||
$icon_view = id(new PHUIIconView())
|
||||
->setIconFont('fa-'.$icon, $color);
|
||||
|
||||
|
||||
return $this->getEngine()->storeText($icon_view);
|
||||
}
|
||||
|
||||
|
@@ -109,6 +109,8 @@ final class PhabricatorImageMacroRemarkupRule extends PhutilRemarkupRule {
|
||||
$result = $spec['original'].' <'.$src_uri.'>';
|
||||
$engine->overwriteStoredText($spec['token'], $result);
|
||||
continue;
|
||||
} else if ($this->getEngine()->isHTMLMailMode()) {
|
||||
$src_uri = PhabricatorEnv::getProductionURI($src_uri);
|
||||
}
|
||||
|
||||
$file_data = $file->getMetadata();
|
||||
|
@@ -34,6 +34,10 @@ final class PhabricatorMemeRemarkupRule extends PhutilRemarkupRule {
|
||||
->alter('uppertext', $options['above'])
|
||||
->alter('lowertext', $options['below']);
|
||||
|
||||
if ($this->getEngine()->isHTMLMailMode()) {
|
||||
$uri = PhabricatorEnv::getProductionURI($uri);
|
||||
}
|
||||
|
||||
if ($this->getEngine()->isTextMode()) {
|
||||
$img =
|
||||
($options['above'] != '' ? "\"{$options['above']}\"\n" : '').
|
||||
|
@@ -75,6 +75,10 @@ final class PhabricatorManiphestApplication extends PhabricatorApplication {
|
||||
public function loadStatus(PhabricatorUser $user) {
|
||||
$status = array();
|
||||
|
||||
if (!$user->isLoggedIn()) {
|
||||
return $status;
|
||||
}
|
||||
|
||||
$query = id(new ManiphestTaskQuery())
|
||||
->setViewer($user)
|
||||
->withStatuses(ManiphestTaskStatus::getOpenStatusConstants())
|
||||
|
@@ -64,6 +64,7 @@ final class ManiphestGetTaskTransactionsConduitAPIMethod
|
||||
|
||||
$results[$task_id][] = array(
|
||||
'taskID' => $task_id,
|
||||
'transactionPHID' => $transaction->getPHID(),
|
||||
'transactionType' => $transaction->getTransactionType(),
|
||||
'oldValue' => $transaction->getOldValue(),
|
||||
'newValue' => $transaction->getNewValue(),
|
||||
|
@@ -114,12 +114,9 @@ final class ManiphestBatchEditController extends ManiphestController {
|
||||
'name' => 'actions',
|
||||
'id' => 'batch-form-actions',
|
||||
)));
|
||||
$form->appendChild(
|
||||
phutil_tag('p', array(), pht('These tasks will be edited:')));
|
||||
$form->appendChild($list);
|
||||
$form->appendChild(
|
||||
id(new PHUIFormInsetView())
|
||||
->setTitle('Actions')
|
||||
->setTitle(pht('Actions'))
|
||||
->setRightButton(javelin_tag(
|
||||
'a',
|
||||
array(
|
||||
@@ -146,18 +143,22 @@ final class ManiphestBatchEditController extends ManiphestController {
|
||||
$crumbs = $this->buildApplicationCrumbs();
|
||||
$crumbs->addTextCrumb($title);
|
||||
|
||||
$task_box = id(new PHUIObjectBoxView())
|
||||
->setHeaderText(pht('Selected Tasks'))
|
||||
->appendChild($list);
|
||||
|
||||
$form_box = id(new PHUIObjectBoxView())
|
||||
->setHeaderText(pht('Batch Edit Tasks'))
|
||||
->setHeaderText(pht('Batch Editor'))
|
||||
->setForm($form);
|
||||
|
||||
return $this->buildApplicationPage(
|
||||
array(
|
||||
$crumbs,
|
||||
$task_box,
|
||||
$form_box,
|
||||
),
|
||||
array(
|
||||
'title' => $title,
|
||||
'device' => false,
|
||||
));
|
||||
}
|
||||
|
||||
|
@@ -38,12 +38,6 @@ final class ManiphestTaskDetailController extends ManiphestController {
|
||||
->executeOne();
|
||||
}
|
||||
|
||||
$transactions = id(new ManiphestTransactionQuery())
|
||||
->setViewer($user)
|
||||
->withObjectPHIDs(array($task->getPHID()))
|
||||
->needComments(true)
|
||||
->execute();
|
||||
|
||||
$field_list = PhabricatorCustomField::getObjectFields(
|
||||
$task,
|
||||
PhabricatorCustomField::ROLE_VIEW);
|
||||
@@ -137,15 +131,11 @@ final class ManiphestTaskDetailController extends ManiphestController {
|
||||
$engine = new PhabricatorMarkupEngine();
|
||||
$engine->setViewer($user);
|
||||
$engine->addObject($task, ManiphestTask::MARKUP_FIELD_DESCRIPTION);
|
||||
foreach ($transactions as $modern_xaction) {
|
||||
if ($modern_xaction->getComment()) {
|
||||
$engine->addObject(
|
||||
$modern_xaction->getComment(),
|
||||
PhabricatorApplicationTransactionComment::MARKUP_FIELD_COMMENT);
|
||||
}
|
||||
}
|
||||
|
||||
$engine->process();
|
||||
$timeline = $this->buildTransactionTimeline(
|
||||
$task,
|
||||
new ManiphestTransactionQuery(),
|
||||
$engine);
|
||||
|
||||
$resolution_types = ManiphestTaskStatus::getTaskStatusMap();
|
||||
|
||||
@@ -265,6 +255,7 @@ final class ManiphestTaskDetailController extends ManiphestController {
|
||||
->setControlStyle('display: none'))
|
||||
->appendChild(
|
||||
id(new PhabricatorRemarkupControl())
|
||||
->setUser($user)
|
||||
->setLabel(pht('Comments'))
|
||||
->setName('comments')
|
||||
->setValue($draft_text)
|
||||
@@ -338,12 +329,6 @@ final class ManiphestTaskDetailController extends ManiphestController {
|
||||
'aphront-panel-preview-loading-text',
|
||||
pht('Loading preview...'))));
|
||||
|
||||
$timeline = id(new PhabricatorApplicationTransactionView())
|
||||
->setUser($user)
|
||||
->setObjectPHID($task->getPHID())
|
||||
->setTransactions($transactions)
|
||||
->setMarkupEngine($engine);
|
||||
|
||||
$object_name = 'T'.$task->getID();
|
||||
$actions = $this->buildActionView($task);
|
||||
|
||||
|
@@ -501,6 +501,34 @@ final class ManiphestTransactionEditor
|
||||
pht('TASK DETAIL'),
|
||||
PhabricatorEnv::getProductionURI('/T'.$object->getID()));
|
||||
|
||||
|
||||
$board_phids = array();
|
||||
$type_column = ManiphestTransaction::TYPE_PROJECT_COLUMN;
|
||||
foreach ($xactions as $xaction) {
|
||||
if ($xaction->getTransactionType() == $type_column) {
|
||||
$new = $xaction->getNewValue();
|
||||
$project_phid = idx($new, 'projectPHID');
|
||||
if ($project_phid) {
|
||||
$board_phids[] = $project_phid;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ($board_phids) {
|
||||
$projects = id(new PhabricatorProjectQuery())
|
||||
->setViewer($this->requireActor())
|
||||
->withPHIDs($board_phids)
|
||||
->execute();
|
||||
|
||||
foreach ($projects as $project) {
|
||||
$body->addLinkSection(
|
||||
pht('WORKBOARD'),
|
||||
PhabricatorEnv::getProductionURI(
|
||||
'/project/board/'.$project->getID().'/'));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
return $body;
|
||||
}
|
||||
|
||||
|
@@ -39,14 +39,6 @@ final class ManiphestSearchIndexer extends PhabricatorSearchDocumentIndexer {
|
||||
new ManiphestTransactionQuery(),
|
||||
array($phid));
|
||||
|
||||
foreach ($task->getProjectPHIDs() as $phid) {
|
||||
$doc->addRelationship(
|
||||
PhabricatorSearchRelationship::RELATIONSHIP_PROJECT,
|
||||
$phid,
|
||||
PhabricatorProjectProjectPHIDType::TYPECONST,
|
||||
$task->getDateModified()); // Bogus.
|
||||
}
|
||||
|
||||
$owner = $task->getOwnerPHID();
|
||||
if ($owner) {
|
||||
$doc->addRelationship(
|
||||
|
@@ -213,6 +213,11 @@ final class ManiphestTransaction
|
||||
return 'yellow';
|
||||
}
|
||||
|
||||
case self::TYPE_MERGED_FROM:
|
||||
return 'orange';
|
||||
|
||||
case self::TYPE_MERGED_INTO:
|
||||
return 'black';
|
||||
}
|
||||
|
||||
return parent::getColor();
|
||||
@@ -347,6 +352,7 @@ final class ManiphestTransaction
|
||||
return 'fa-columns';
|
||||
|
||||
case self::TYPE_MERGED_INTO:
|
||||
return 'fa-check';
|
||||
case self::TYPE_MERGED_FROM:
|
||||
return 'fa-compress';
|
||||
|
||||
@@ -591,7 +597,7 @@ final class ManiphestTransaction
|
||||
|
||||
case self::TYPE_MERGED_INTO:
|
||||
return pht(
|
||||
'%s merged this task into %s.',
|
||||
'%s closed this task as a duplicate of %s.',
|
||||
$this->renderHandleLink($author_phid),
|
||||
$this->renderHandleLink($new));
|
||||
break;
|
||||
|
@@ -0,0 +1,30 @@
|
||||
<?php
|
||||
|
||||
final class ManiphestTaskStatusDatasource
|
||||
extends PhabricatorTypeaheadDatasource {
|
||||
|
||||
public function getPlaceholderText() {
|
||||
return pht('Type a task status name...');
|
||||
}
|
||||
|
||||
public function getDatasourceApplicationClass() {
|
||||
return 'PhabricatorManiphestApplication';
|
||||
}
|
||||
|
||||
public function loadResults() {
|
||||
$viewer = $this->getViewer();
|
||||
$raw_query = $this->getRawQuery();
|
||||
|
||||
$results = array();
|
||||
|
||||
$status_map = ManiphestTaskStatus::getTaskStatusMap();
|
||||
foreach ($status_map as $value => $name) {
|
||||
// NOTE: $value is not a PHID but is unique. This'll work.
|
||||
$results[] = id(new PhabricatorTypeaheadResult())
|
||||
->setPHID($value)
|
||||
->setName($name);
|
||||
}
|
||||
|
||||
return $results;
|
||||
}
|
||||
}
|
@@ -124,6 +124,31 @@ abstract class PhabricatorMailReplyHandler {
|
||||
return $body;
|
||||
}
|
||||
|
||||
final public function getRecipientsSummaryHTML(
|
||||
array $to_handles,
|
||||
array $cc_handles) {
|
||||
assert_instances_of($to_handles, 'PhabricatorObjectHandle');
|
||||
assert_instances_of($cc_handles, 'PhabricatorObjectHandle');
|
||||
|
||||
if (PhabricatorEnv::getEnvConfig('metamta.recipients.show-hints')) {
|
||||
$body = array();
|
||||
if ($to_handles) {
|
||||
$body[] = phutil_tag('strong', array(), 'To: ');
|
||||
$body[] = phutil_implode_html(', ', mpull($to_handles, 'getName'));
|
||||
$body[] = phutil_tag('br');
|
||||
}
|
||||
if ($cc_handles) {
|
||||
$body[] = phutil_tag('strong', array(), 'Cc: ');
|
||||
$body[] = phutil_implode_html(', ', mpull($cc_handles, 'getName'));
|
||||
$body[] = phutil_tag('br');
|
||||
}
|
||||
return phutil_tag('div', array(), $body);
|
||||
} else {
|
||||
return '';
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
final public function multiplexMail(
|
||||
PhabricatorMetaMTAMail $mail_template,
|
||||
array $to_handles,
|
||||
@@ -184,8 +209,13 @@ abstract class PhabricatorMailReplyHandler {
|
||||
$body .= "\n";
|
||||
$body .= $this->getRecipientsSummary($to_handles, $cc_handles);
|
||||
|
||||
foreach ($recipients as $phid => $recipient) {
|
||||
$html_body = $mail_template->getHTMLBody();
|
||||
if (strlen($html_body)) {
|
||||
$html_body .= hsprintf('%s',
|
||||
$this->getRecipientsSummaryHTML($to_handles, $cc_handles));
|
||||
}
|
||||
|
||||
foreach ($recipients as $phid => $recipient) {
|
||||
|
||||
$mail = clone $mail_template;
|
||||
if (isset($to_handles[$phid])) {
|
||||
@@ -198,6 +228,7 @@ abstract class PhabricatorMailReplyHandler {
|
||||
}
|
||||
|
||||
$mail->setBody($body);
|
||||
$mail->setHTMLBody($html_body);
|
||||
|
||||
$reply_to = null;
|
||||
if (!$reply_to && $this->supportsPrivateReplies()) {
|
||||
|
@@ -12,6 +12,15 @@ final class PhabricatorMetaMTAMailBody {
|
||||
private $htmlSections = array();
|
||||
private $attachments = array();
|
||||
|
||||
private $viewer;
|
||||
|
||||
public function getViewer() {
|
||||
return $this->viewer;
|
||||
}
|
||||
|
||||
public function setViewer($viewer) {
|
||||
$this->viewer = $viewer;
|
||||
}
|
||||
|
||||
/* -( Composition )-------------------------------------------------------- */
|
||||
|
||||
@@ -33,6 +42,39 @@ final class PhabricatorMetaMTAMailBody {
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function addRemarkupSection($text) {
|
||||
try {
|
||||
$engine = PhabricatorMarkupEngine::newMarkupEngine(array());
|
||||
$engine->setConfig('viewer', $this->getViewer());
|
||||
$engine->setMode(PhutilRemarkupEngine::MODE_TEXT);
|
||||
$styled_text = $engine->markupText($text);
|
||||
$this->sections[] = $styled_text;
|
||||
} catch (Exception $ex) {
|
||||
phlog($ex);
|
||||
$this->sections[] = $text;
|
||||
}
|
||||
|
||||
try {
|
||||
$mail_engine = PhabricatorMarkupEngine::newMarkupEngine(array());
|
||||
$mail_engine->setConfig('viewer', $this->getViewer());
|
||||
$mail_engine->setMode(PhutilRemarkupEngine::MODE_HTML_MAIL);
|
||||
$mail_engine->setConfig(
|
||||
'uri.base',
|
||||
PhabricatorEnv::getProductionURI('/'));
|
||||
$html = $mail_engine->markupText($text);
|
||||
$this->htmlSections[] = $html;
|
||||
} catch (Exception $ex) {
|
||||
phlog($ex);
|
||||
$this->htmlSections[] = phutil_escape_html_newlines(
|
||||
phutil_tag(
|
||||
'div',
|
||||
array(),
|
||||
$text));
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function addRawPlaintextSection($text) {
|
||||
if (strlen($text)) {
|
||||
$text = rtrim($text);
|
||||
@@ -137,6 +179,23 @@ final class PhabricatorMetaMTAMailBody {
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a section with a link to email preferences.
|
||||
*
|
||||
* @return this
|
||||
* @task compose
|
||||
*/
|
||||
public function addEmailPreferenceSection() {
|
||||
if (!PhabricatorEnv::getEnvConfig('metamta.email-preferences')) {
|
||||
return $this;
|
||||
}
|
||||
|
||||
$href = PhabricatorEnv::getProductionURI(
|
||||
'/settings/panel/emailpreferences/');
|
||||
$this->addLinkSection(pht('EMAIL PREFERENCES'), $href);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add an attachment.
|
||||
|
@@ -79,6 +79,7 @@ final class PassphraseQueryConduitAPIMethod
|
||||
$material['publicKey'] = $public_key;
|
||||
}
|
||||
break;
|
||||
case PassphraseCredentialTypeSSHGeneratedKey::CREDENTIAL_TYPE:
|
||||
case PassphraseCredentialTypeSSHPrivateKeyText::CREDENTIAL_TYPE:
|
||||
if ($secret) {
|
||||
$material['privateKey'] = $secret;
|
||||
|
@@ -52,6 +52,7 @@ final class PhabricatorUserBlurbField
|
||||
|
||||
public function renderEditControl(array $handles) {
|
||||
return id(new PhabricatorRemarkupControl())
|
||||
->setUser($this->getViewer())
|
||||
->setName($this->getFieldKey())
|
||||
->setValue($this->value)
|
||||
->setLabel($this->getFieldName());
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user