Merge branch 'master' into blender-tweaks

Conflicts:
	resources/celerity/map.php
	src/applications/differential/controller/DifferentialDiffCreateController.php
This commit is contained in:
2014-11-29 18:37:00 +05:00
177 changed files with 3457 additions and 1323 deletions

1
bin/worker Symbolic link
View File

@@ -0,0 +1 @@
../scripts/setup/manage_worker.php

View File

@@ -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',

View File

@@ -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())

View 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";

View 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};

View 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;

View File

@@ -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 = '';

View File

@@ -0,0 +1,2 @@
ALTER TABLE {$NAMESPACE}_auth.auth_sshkey
ADD isTrusted BOOL NOT NULL;

View File

@@ -0,0 +1,2 @@
UPDATE {$NAMESPACE}_worker.worker_activetask
SET priority = 5000 - priority;

View File

@@ -0,0 +1,2 @@
UPDATE {$NAMESPACE}_worker.worker_archivetask
SET priority = 5000 - priority;

21
scripts/setup/manage_worker.php Executable file
View 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);

View File

@@ -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',

View 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.');
}
}

View File

@@ -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(),
);
}
}

View File

@@ -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);
}
}

View File

@@ -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;
}
}

View File

@@ -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));
}
}

View File

@@ -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));
}
}

View File

@@ -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() {

View File

@@ -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();
}
}

View File

@@ -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() {

View File

@@ -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;
}
}

View File

@@ -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/'

View File

@@ -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);
}
}

View File

@@ -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);
}
}

View File

@@ -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);
}
}

View File

@@ -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);
}
}
}

View File

@@ -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);
}

View File

@@ -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;
}
}

View File

@@ -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();
}

View File

@@ -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;
}

View 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;
}
}

View File

@@ -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;
}
}

View File

@@ -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'];

View File

@@ -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;
}
}
}

View File

@@ -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()) {

View File

@@ -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)

View File

@@ -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(

View File

@@ -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");
}
}
}

View File

@@ -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;

View File

@@ -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())

View File

@@ -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(

View File

@@ -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);

View File

@@ -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}");
}

View 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);

View File

@@ -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);
}

View File

@@ -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);

View File

@@ -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 '.

View File

@@ -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();

View File

@@ -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());

View File

@@ -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())

View File

@@ -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())

View File

@@ -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;
}
}

View File

@@ -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;
}

View File

@@ -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.'),

View File

@@ -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();
}

View File

@@ -62,7 +62,6 @@ final class DifferentialDiffQuery
foreach ($diffs as $key => $diff) {
if (!$diff->getRevisionID()) {
$diff->attachRevision(null);
continue;
}

View File

@@ -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) {

View File

@@ -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,

View File

@@ -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,

View File

@@ -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.');
}

View File

@@ -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();
}
}

View File

@@ -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);
}

View File

@@ -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);

View File

@@ -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;
}

View File

@@ -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());
}

View File

@@ -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);

View File

@@ -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) {

View File

@@ -120,6 +120,7 @@ final class DiffusionRepositoryEditBasicController
$form
->appendChild(
id(new PhabricatorRemarkupControl())
->setUser($user)
->setName('description')
->setLabel(pht('Description'))
->setValue($v_desc))

View File

@@ -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();
}

View File

@@ -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();
}

View File

@@ -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(

View File

@@ -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',

View File

@@ -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;
}
}

View File

@@ -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();

View File

@@ -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());

View File

@@ -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))

View File

@@ -155,6 +155,7 @@ final class HarbormasterStepEditController extends HarbormasterController {
$form
->appendChild(
id(new PhabricatorRemarkupControl())
->setUser($viewer)
->setName('description')
->setLabel(pht('Description'))
->setError($e_description)

View File

@@ -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();

View File

@@ -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;
}

View File

@@ -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);

View File

@@ -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(),

View File

@@ -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!');
}

View File

@@ -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;

View File

@@ -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)

View File

@@ -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);
}

View File

@@ -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();

View File

@@ -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" : '').

View File

@@ -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())

View File

@@ -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(),

View File

@@ -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,
));
}

View File

@@ -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);

View File

@@ -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;
}

View File

@@ -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(

View File

@@ -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;

View File

@@ -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;
}
}

View File

@@ -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()) {

View File

@@ -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.

View File

@@ -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;

View File

@@ -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