Merge branch 'master' into blender-tweaks

This commit is contained in:
2019-12-17 13:02:09 +01:00
142 changed files with 3039 additions and 997 deletions

View File

@@ -1 +0,0 @@
../scripts/ssh/ssh-auth-key.php

View File

@@ -9,10 +9,10 @@ return array(
'names' => array(
'conpherence.pkg.css' => '3c8a0668',
'conpherence.pkg.js' => '020aebcf',
'core.pkg.css' => '9d167919',
'core.pkg.js' => '6e5c894f',
'core.pkg.css' => '2d63dec5',
'core.pkg.js' => '705aec2c',
'differential.pkg.css' => '607c84be',
'differential.pkg.js' => 'a0212a0b',
'differential.pkg.js' => '1b97518d',
'diffusion.pkg.css' => '42c75c37',
'diffusion.pkg.js' => 'a98c0bf7',
'maniphest.pkg.css' => '35995d6d',
@@ -30,13 +30,13 @@ return array(
'rsrc/css/aphront/notification.css' => '30240bd2',
'rsrc/css/aphront/panel-view.css' => '46923d46',
'rsrc/css/aphront/phabricator-nav-view.css' => 'f8a0c1bf',
'rsrc/css/aphront/table-view.css' => '5f13a9e4',
'rsrc/css/aphront/table-view.css' => '0bb61df1',
'rsrc/css/aphront/tokenizer.css' => 'b52d0668',
'rsrc/css/aphront/tooltip.css' => 'e3f2412f',
'rsrc/css/aphront/typeahead-browse.css' => 'b7ed02d2',
'rsrc/css/aphront/typeahead.css' => '8779483d',
'rsrc/css/application/almanac/almanac.css' => '2e050f4f',
'rsrc/css/application/auth/auth.css' => 'add92fd8',
'rsrc/css/application/auth/auth.css' => 'c2f23d74',
'rsrc/css/application/base/main-menu-view.css' => 'a663a13c',
'rsrc/css/application/base/notification-menu.css' => '4df1ee30',
'rsrc/css/application/base/phui-theme.css' => '63311e09',
@@ -155,7 +155,7 @@ return array(
'rsrc/css/phui/phui-form-view.css' => 'a8e0a1ab',
'rsrc/css/phui/phui-form.css' => '159e2d9c',
'rsrc/css/phui/phui-head-thing.css' => 'd7f293df',
'rsrc/css/phui/phui-header-view.css' => 'b500eeea',
'rsrc/css/phui/phui-header-view.css' => 'be09cc83',
'rsrc/css/phui/phui-hovercard.css' => '6ca90fa0',
'rsrc/css/phui/phui-icon-set-selector.css' => '7aa5f3ec',
'rsrc/css/phui/phui-icon.css' => '4cbc684a',
@@ -430,7 +430,7 @@ return array(
'rsrc/js/application/releeph/releeph-preview-branch.js' => '75184d68',
'rsrc/js/application/releeph/releeph-request-state-change.js' => '9f081f05',
'rsrc/js/application/releeph/releeph-request-typeahead.js' => 'aa3a100c',
'rsrc/js/application/repository/repository-crossreference.js' => 'c15122b4',
'rsrc/js/application/repository/repository-crossreference.js' => '1c95ea63',
'rsrc/js/application/search/behavior-reorder-profile-menu-items.js' => 'e5bdb730',
'rsrc/js/application/search/behavior-reorder-queries.js' => 'b86f297f',
'rsrc/js/application/transactions/behavior-comment-actions.js' => '4dffaeb2',
@@ -450,7 +450,7 @@ return array(
'rsrc/js/application/uiexample/notification-example.js' => '29819b75',
'rsrc/js/core/Busy.js' => '5202e831',
'rsrc/js/core/DragAndDropFileUpload.js' => '4370900d',
'rsrc/js/core/DraggableList.js' => 'c9ad6f70',
'rsrc/js/core/DraggableList.js' => '0169e425',
'rsrc/js/core/Favicon.js' => '7930776a',
'rsrc/js/core/FileUpload.js' => 'ab85e184',
'rsrc/js/core/Hovercard.js' => '074f0783',
@@ -537,12 +537,12 @@ return array(
'aphront-list-filter-view-css' => 'feb64255',
'aphront-multi-column-view-css' => 'fbc00ba3',
'aphront-panel-view-css' => '46923d46',
'aphront-table-view-css' => '5f13a9e4',
'aphront-table-view-css' => '0bb61df1',
'aphront-tokenizer-control-css' => 'b52d0668',
'aphront-tooltip-css' => 'e3f2412f',
'aphront-typeahead-control-css' => '8779483d',
'application-search-view-css' => '0f7c06d8',
'auth-css' => 'add92fd8',
'auth-css' => 'c2f23d74',
'bulk-job-css' => '73af99f5',
'conduit-api-css' => 'ce2cfc41',
'config-options-css' => '16c920ae',
@@ -684,7 +684,7 @@ return array(
'javelin-behavior-reorder-applications' => 'aa371860',
'javelin-behavior-reorder-columns' => '8ac32fd9',
'javelin-behavior-reorder-profile-menu-items' => 'e5bdb730',
'javelin-behavior-repository-crossreference' => 'c15122b4',
'javelin-behavior-repository-crossreference' => '1c95ea63',
'javelin-behavior-scrollbar' => '92388bae',
'javelin-behavior-search-reorder-queries' => 'b86f297f',
'javelin-behavior-select-content' => 'e8240b50',
@@ -779,7 +779,7 @@ return array(
'phabricator-diff-changeset-list' => '0f5c016d',
'phabricator-diff-inline' => 'a4a14a94',
'phabricator-drag-and-drop-file-upload' => '4370900d',
'phabricator-draggable-list' => 'c9ad6f70',
'phabricator-draggable-list' => '0169e425',
'phabricator-fatal-config-template-css' => '20babf50',
'phabricator-favicon' => '7930776a',
'phabricator-feed-css' => 'd8b6e3f8',
@@ -846,7 +846,7 @@ return array(
'phui-form-css' => '159e2d9c',
'phui-form-view-css' => 'a8e0a1ab',
'phui-head-thing-view-css' => 'd7f293df',
'phui-header-view-css' => 'b500eeea',
'phui-header-view-css' => 'be09cc83',
'phui-hovercard' => '074f0783',
'phui-hovercard-view-css' => '6ca90fa0',
'phui-icon-set-selector-css' => '7aa5f3ec',
@@ -923,6 +923,14 @@ return array(
'javelin-uri',
'phabricator-notification',
),
'0169e425' => array(
'javelin-install',
'javelin-dom',
'javelin-stratcom',
'javelin-util',
'javelin-vector',
'javelin-magical-init',
),
'022516b4' => array(
'javelin-install',
'javelin-util',
@@ -1034,6 +1042,12 @@ return array(
'javelin-install',
'javelin-util',
),
'1c95ea63' => array(
'javelin-behavior',
'javelin-dom',
'javelin-stratcom',
'javelin-uri',
),
'1cab0e9a' => array(
'javelin-behavior',
'javelin-dom',
@@ -1980,12 +1994,6 @@ return array(
'c03f2fb4' => array(
'javelin-install',
),
'c15122b4' => array(
'javelin-behavior',
'javelin-dom',
'javelin-stratcom',
'javelin-uri',
),
'c2c500a7' => array(
'javelin-install',
'javelin-dom',
@@ -2035,14 +2043,6 @@ return array(
'javelin-util',
'phabricator-keyboard-shortcut-manager',
),
'c9ad6f70' => array(
'javelin-install',
'javelin-dom',
'javelin-stratcom',
'javelin-util',
'javelin-vector',
'javelin-magical-init',
),
'cf32921f' => array(
'javelin-behavior',
'javelin-dom',

View File

@@ -0,0 +1,4 @@
<?php
PhabricatorRebuildIndexesWorker::rebuildObjectsWithQuery(
'PhabricatorRepositoryQuery');

View File

@@ -0,0 +1,2 @@
ALTER TABLE {$NAMESPACE}_repository.repository_identity
ADD emailAddress VARCHAR(255) COLLATE {$COLLATE_SORT};

View File

@@ -0,0 +1,26 @@
<?php
$table = new PhabricatorRepositoryIdentity();
$conn = $table->establishConnection('w');
$iterator = new LiskRawMigrationIterator($conn, $table->getTableName());
foreach ($iterator as $row) {
$name = $row['identityNameRaw'];
$name = phutil_utf8ize($name);
$email = new PhutilEmailAddress($name);
$address = $email->getAddress();
try {
queryfx(
$conn,
'UPDATE %R SET emailAddress = %ns WHERE id = %d',
$table,
$address,
$row['id']);
} catch (Exception $ex) {
// We may occasionally run into issues with binary or very long addresses.
// Just skip over them.
continue;
}
}

View File

@@ -0,0 +1,3 @@
UPDATE {$NAMESPACE}_repository.repository_identity
SET currentEffectiveUserPHID = NULL
WHERE currentEffectiveUserPHID = 'unassigned()';

View File

@@ -0,0 +1,2 @@
ALTER TABLE {$NAMESPACE}_user.user_email
ADD phid VARBINARY(64) NOT NULL;

View File

@@ -0,0 +1,18 @@
<?php
$table = new PhabricatorUserEmail();
$conn = $table->establishConnection('w');
$iterator = new LiskRawMigrationIterator($conn, $table->getTableName());
foreach ($iterator as $row) {
$phid = $row['phid'];
if (!strlen($phid)) {
queryfx(
$conn,
'UPDATE %R SET phid = %s WHERE id = %d',
$table,
$table->generatePHID(),
$row['id']);
}
}

View File

@@ -1,42 +0,0 @@
#!/usr/bin/env php
<?php
$root = dirname(dirname(dirname(__FILE__)));
require_once $root.'/scripts/__init_script__.php';
try {
$cert = file_get_contents('php://stdin');
$public_key = PhabricatorAuthSSHPublicKey::newFromRawKey($cert);
} catch (Exception $ex) {
exit(1);
}
$key = id(new PhabricatorAuthSSHKeyQuery())
->setViewer(PhabricatorUser::getOmnipotentUser())
->withKeys(array($public_key))
->withIsActive(true)
->executeOne();
if (!$key) {
exit(1);
}
$object = $key->getObject();
if (!($object instanceof PhabricatorUser)) {
exit(1);
}
$bin = $root.'/bin/ssh-exec';
$cmd = csprintf('%s --phabricator-ssh-user %s', $bin, $object->getUsername());
// This is additional escaping for the SSH 'command="..."' string.
$cmd = addcslashes($cmd, '"\\');
$options = array(
'command="'.$cmd.'"',
'no-port-forwarding',
'no-X11-forwarding',
'no-agent-forwarding',
'no-pty',
);
echo implode(',', $options);
exit(0);

View File

@@ -4,6 +4,24 @@
$root = dirname(dirname(dirname(__FILE__)));
require_once $root.'/scripts/init/init-script.php';
// TODO: For now, this is using "parseParital()", not "parse()". This allows
// the script to accept (and ignore) additional arguments. This preserves
// backward compatibility until installs have time to migrate to the new
// syntax.
$args = id(new PhutilArgumentParser($argv))
->parsePartial(
array(
array(
'name' => 'sshd-key',
'param' => 'k',
'help' => pht(
'Accepts the "%%k" parameter from "AuthorizedKeysCommand".'),
),
));
$sshd_key = $args->getArg('sshd-key');
// NOTE: We are caching a datastructure rather than the flat key file because
// the path on disk to "ssh-exec" is arbitrarily mutable at runtime. See T12397.
@@ -85,6 +103,22 @@ if ($authstruct === null) {
$cache->setKey($authstruct_key, $authstruct_raw, $ttl);
}
// If we've received an "--sshd-key" argument and it matches some known key,
// only emit that key. (For now, if the key doesn't match, we'll fall back to
// emitting all keys.)
if ($sshd_key !== null) {
$matches = array();
foreach ($authstruct['keys'] as $key => $key_struct) {
if ($key_struct['key'] === $sshd_key) {
$matches[$key] = $key_struct;
}
}
if ($matches) {
$authstruct['keys'] = $matches;
}
}
$bin = $root.'/bin/ssh-exec';
$instance = PhabricatorEnv::getEnvConfig('cluster.instance');

View File

@@ -984,7 +984,9 @@ phutil_register_library_map(array(
'DiffusionRepositoryEditUpdateController' => 'applications/diffusion/controller/DiffusionRepositoryEditUpdateController.php',
'DiffusionRepositoryFunctionDatasource' => 'applications/diffusion/typeahead/DiffusionRepositoryFunctionDatasource.php',
'DiffusionRepositoryHistoryManagementPanel' => 'applications/diffusion/management/DiffusionRepositoryHistoryManagementPanel.php',
'DiffusionRepositoryIdentityDestructionEngineExtension' => 'applications/diffusion/identity/DiffusionRepositoryIdentityDestructionEngineExtension.php',
'DiffusionRepositoryIdentityEditor' => 'applications/diffusion/editor/DiffusionRepositoryIdentityEditor.php',
'DiffusionRepositoryIdentityEngine' => 'applications/diffusion/identity/DiffusionRepositoryIdentityEngine.php',
'DiffusionRepositoryIdentitySearchEngine' => 'applications/diffusion/query/DiffusionRepositoryIdentitySearchEngine.php',
'DiffusionRepositoryLimitsManagementPanel' => 'applications/diffusion/management/DiffusionRepositoryLimitsManagementPanel.php',
'DiffusionRepositoryListController' => 'applications/diffusion/controller/DiffusionRepositoryListController.php',
@@ -996,6 +998,7 @@ phutil_register_library_map(array(
'DiffusionRepositoryManagementOtherPanelGroup' => 'applications/diffusion/management/DiffusionRepositoryManagementOtherPanelGroup.php',
'DiffusionRepositoryManagementPanel' => 'applications/diffusion/management/DiffusionRepositoryManagementPanel.php',
'DiffusionRepositoryManagementPanelGroup' => 'applications/diffusion/management/DiffusionRepositoryManagementPanelGroup.php',
'DiffusionRepositoryMetricsSearchEngineAttachment' => 'applications/diffusion/engineextension/DiffusionRepositoryMetricsSearchEngineAttachment.php',
'DiffusionRepositoryPath' => 'applications/diffusion/data/DiffusionRepositoryPath.php',
'DiffusionRepositoryPoliciesManagementPanel' => 'applications/diffusion/management/DiffusionRepositoryPoliciesManagementPanel.php',
'DiffusionRepositoryProfilePictureController' => 'applications/diffusion/controller/DiffusionRepositoryProfilePictureController.php',
@@ -1337,6 +1340,8 @@ phutil_register_library_map(array(
'HarbormasterArcLintBuildStepImplementation' => 'applications/harbormaster/step/HarbormasterArcLintBuildStepImplementation.php',
'HarbormasterArcUnitBuildStepImplementation' => 'applications/harbormaster/step/HarbormasterArcUnitBuildStepImplementation.php',
'HarbormasterArtifact' => 'applications/harbormaster/artifact/HarbormasterArtifact.php',
'HarbormasterArtifactSearchConduitAPIMethod' => 'applications/harbormaster/conduit/HarbormasterArtifactSearchConduitAPIMethod.php',
'HarbormasterArtifactSearchEngine' => 'applications/harbormaster/query/HarbormasterArtifactSearchEngine.php',
'HarbormasterAutotargetsTestCase' => 'applications/harbormaster/__tests__/HarbormasterAutotargetsTestCase.php',
'HarbormasterBuild' => 'applications/harbormaster/storage/build/HarbormasterBuild.php',
'HarbormasterBuildAbortedException' => 'applications/harbormaster/exception/HarbormasterBuildAbortedException.php',
@@ -2437,6 +2442,14 @@ phutil_register_library_map(array(
'PhabricatorAuthSSHKeyTransaction' => 'applications/auth/storage/PhabricatorAuthSSHKeyTransaction.php',
'PhabricatorAuthSSHKeyTransactionQuery' => 'applications/auth/query/PhabricatorAuthSSHKeyTransactionQuery.php',
'PhabricatorAuthSSHKeyViewController' => 'applications/auth/controller/PhabricatorAuthSSHKeyViewController.php',
'PhabricatorAuthSSHPrivateKey' => 'applications/auth/sshkey/PhabricatorAuthSSHPrivateKey.php',
'PhabricatorAuthSSHPrivateKeyException' => 'applications/auth/exception/privatekey/PhabricatorAuthSSHPrivateKeyException.php',
'PhabricatorAuthSSHPrivateKeyFormatException' => 'applications/auth/exception/privatekey/PhabricatorAuthSSHPrivateKeyFormatException.php',
'PhabricatorAuthSSHPrivateKeyIncorrectPassphraseException' => 'applications/auth/exception/privatekey/PhabricatorAuthSSHPrivateKeyIncorrectPassphraseException.php',
'PhabricatorAuthSSHPrivateKeyMissingPassphraseException' => 'applications/auth/exception/privatekey/PhabricatorAuthSSHPrivateKeyMissingPassphraseException.php',
'PhabricatorAuthSSHPrivateKeyPassphraseException' => 'applications/auth/exception/privatekey/PhabricatorAuthSSHPrivateKeyPassphraseException.php',
'PhabricatorAuthSSHPrivateKeySurplusPassphraseException' => 'applications/auth/exception/privatekey/PhabricatorAuthSSHPrivateKeySurplusPassphraseException.php',
'PhabricatorAuthSSHPrivateKeyUnknownException' => 'applications/auth/exception/privatekey/PhabricatorAuthSSHPrivateKeyUnknownException.php',
'PhabricatorAuthSSHPublicKey' => 'applications/auth/sshkey/PhabricatorAuthSSHPublicKey.php',
'PhabricatorAuthSSHRevoker' => 'applications/auth/revoker/PhabricatorAuthSSHRevoker.php',
'PhabricatorAuthSession' => 'applications/auth/storage/PhabricatorAuthSession.php',
@@ -4110,6 +4123,8 @@ phutil_register_library_map(array(
'PhabricatorPeopleTasksProfileMenuItem' => 'applications/people/menuitem/PhabricatorPeopleTasksProfileMenuItem.php',
'PhabricatorPeopleTestDataGenerator' => 'applications/people/lipsum/PhabricatorPeopleTestDataGenerator.php',
'PhabricatorPeopleTransactionQuery' => 'applications/people/query/PhabricatorPeopleTransactionQuery.php',
'PhabricatorPeopleUserEmailPHIDType' => 'applications/people/phid/PhabricatorPeopleUserEmailPHIDType.php',
'PhabricatorPeopleUserEmailQuery' => 'applications/people/query/PhabricatorPeopleUserEmailQuery.php',
'PhabricatorPeopleUserFunctionDatasource' => 'applications/people/typeahead/PhabricatorPeopleUserFunctionDatasource.php',
'PhabricatorPeopleUserPHIDType' => 'applications/people/phid/PhabricatorPeopleUserPHIDType.php',
'PhabricatorPeopleUsernameMailEngine' => 'applications/people/mail/PhabricatorPeopleUsernameMailEngine.php',
@@ -4208,7 +4223,6 @@ phutil_register_library_map(array(
'PhabricatorPolicyRule' => 'applications/policy/rule/PhabricatorPolicyRule.php',
'PhabricatorPolicyRulesView' => 'applications/policy/view/PhabricatorPolicyRulesView.php',
'PhabricatorPolicySearchEngineExtension' => 'applications/policy/engineextension/PhabricatorPolicySearchEngineExtension.php',
'PhabricatorPolicyStrengthConstants' => 'applications/policy/constants/PhabricatorPolicyStrengthConstants.php',
'PhabricatorPolicyTestCase' => 'applications/policy/__tests__/PhabricatorPolicyTestCase.php',
'PhabricatorPolicyTestObject' => 'applications/policy/__tests__/PhabricatorPolicyTestObject.php',
'PhabricatorPolicyType' => 'applications/policy/constants/PhabricatorPolicyType.php',
@@ -4646,10 +4660,12 @@ phutil_register_library_map(array(
'PhabricatorSearchEngineExtensionModule' => 'applications/search/engineextension/PhabricatorSearchEngineExtensionModule.php',
'PhabricatorSearchFerretNgramGarbageCollector' => 'applications/search/garbagecollector/PhabricatorSearchFerretNgramGarbageCollector.php',
'PhabricatorSearchField' => 'applications/search/field/PhabricatorSearchField.php',
'PhabricatorSearchHandleController' => 'applications/search/controller/PhabricatorSearchHandleController.php',
'PhabricatorSearchHost' => 'infrastructure/cluster/search/PhabricatorSearchHost.php',
'PhabricatorSearchHovercardController' => 'applications/search/controller/PhabricatorSearchHovercardController.php',
'PhabricatorSearchIndexVersion' => 'applications/search/storage/PhabricatorSearchIndexVersion.php',
'PhabricatorSearchIndexVersionDestructionEngineExtension' => 'applications/search/engineextension/PhabricatorSearchIndexVersionDestructionEngineExtension.php',
'PhabricatorSearchIntField' => 'applications/search/field/PhabricatorSearchIntField.php',
'PhabricatorSearchManagementIndexWorkflow' => 'applications/search/management/PhabricatorSearchManagementIndexWorkflow.php',
'PhabricatorSearchManagementInitWorkflow' => 'applications/search/management/PhabricatorSearchManagementInitWorkflow.php',
'PhabricatorSearchManagementNgramsWorkflow' => 'applications/search/management/PhabricatorSearchManagementNgramsWorkflow.php',
@@ -4870,6 +4886,7 @@ phutil_register_library_map(array(
'PhabricatorSystemActionRateLimitException' => 'applications/system/exception/PhabricatorSystemActionRateLimitException.php',
'PhabricatorSystemApplication' => 'applications/system/application/PhabricatorSystemApplication.php',
'PhabricatorSystemDAO' => 'applications/system/storage/PhabricatorSystemDAO.php',
'PhabricatorSystemDebugUIEventListener' => 'applications/system/events/PhabricatorSystemDebugUIEventListener.php',
'PhabricatorSystemDestructionGarbageCollector' => 'applications/system/garbagecollector/PhabricatorSystemDestructionGarbageCollector.php',
'PhabricatorSystemDestructionLog' => 'applications/system/storage/PhabricatorSystemDestructionLog.php',
'PhabricatorSystemObjectController' => 'applications/system/controller/PhabricatorSystemObjectController.php',
@@ -5046,6 +5063,7 @@ phutil_register_library_map(array(
'PhabricatorWeekStartDaySetting' => 'applications/settings/setting/PhabricatorWeekStartDaySetting.php',
'PhabricatorWildConfigType' => 'applications/config/type/PhabricatorWildConfigType.php',
'PhabricatorWordPressAuthProvider' => 'applications/auth/provider/PhabricatorWordPressAuthProvider.php',
'PhabricatorWorkboardInterface' => 'applications/project/interface/PhabricatorWorkboardInterface.php',
'PhabricatorWorkboardViewState' => 'applications/project/state/PhabricatorWorkboardViewState.php',
'PhabricatorWorker' => 'infrastructure/daemon/workers/PhabricatorWorker.php',
'PhabricatorWorkerActiveTask' => 'infrastructure/daemon/workers/storage/PhabricatorWorkerActiveTask.php',
@@ -6953,7 +6971,9 @@ phutil_register_library_map(array(
'DiffusionRepositoryEditUpdateController' => 'DiffusionRepositoryManageController',
'DiffusionRepositoryFunctionDatasource' => 'PhabricatorTypeaheadCompositeDatasource',
'DiffusionRepositoryHistoryManagementPanel' => 'DiffusionRepositoryManagementPanel',
'DiffusionRepositoryIdentityDestructionEngineExtension' => 'PhabricatorDestructionEngineExtension',
'DiffusionRepositoryIdentityEditor' => 'PhabricatorApplicationTransactionEditor',
'DiffusionRepositoryIdentityEngine' => 'Phobject',
'DiffusionRepositoryIdentitySearchEngine' => 'PhabricatorApplicationSearchEngine',
'DiffusionRepositoryLimitsManagementPanel' => 'DiffusionRepositoryManagementPanel',
'DiffusionRepositoryListController' => 'DiffusionController',
@@ -6965,6 +6985,7 @@ phutil_register_library_map(array(
'DiffusionRepositoryManagementOtherPanelGroup' => 'DiffusionRepositoryManagementPanelGroup',
'DiffusionRepositoryManagementPanel' => 'Phobject',
'DiffusionRepositoryManagementPanelGroup' => 'Phobject',
'DiffusionRepositoryMetricsSearchEngineAttachment' => 'PhabricatorSearchEngineAttachment',
'DiffusionRepositoryPath' => 'Phobject',
'DiffusionRepositoryPoliciesManagementPanel' => 'DiffusionRepositoryManagementPanel',
'DiffusionRepositoryProfilePictureController' => 'DiffusionController',
@@ -7368,6 +7389,8 @@ phutil_register_library_map(array(
'HarbormasterArcLintBuildStepImplementation' => 'HarbormasterBuildStepImplementation',
'HarbormasterArcUnitBuildStepImplementation' => 'HarbormasterBuildStepImplementation',
'HarbormasterArtifact' => 'Phobject',
'HarbormasterArtifactSearchConduitAPIMethod' => 'PhabricatorSearchEngineAPIMethod',
'HarbormasterArtifactSearchEngine' => 'PhabricatorApplicationSearchEngine',
'HarbormasterAutotargetsTestCase' => 'PhabricatorTestCase',
'HarbormasterBuild' => array(
'HarbormasterDAO',
@@ -7383,6 +7406,7 @@ phutil_register_library_map(array(
'HarbormasterDAO',
'PhabricatorPolicyInterface',
'PhabricatorDestructibleInterface',
'PhabricatorConduitResultInterface',
),
'HarbormasterBuildArtifactPHIDType' => 'PhabricatorPHIDType',
'HarbormasterBuildArtifactQuery' => 'PhabricatorCursorPagedPolicyAwareQuery',
@@ -8671,6 +8695,14 @@ phutil_register_library_map(array(
'PhabricatorAuthSSHKeyTransaction' => 'PhabricatorApplicationTransaction',
'PhabricatorAuthSSHKeyTransactionQuery' => 'PhabricatorApplicationTransactionQuery',
'PhabricatorAuthSSHKeyViewController' => 'PhabricatorAuthSSHKeyController',
'PhabricatorAuthSSHPrivateKey' => 'Phobject',
'PhabricatorAuthSSHPrivateKeyException' => 'Exception',
'PhabricatorAuthSSHPrivateKeyFormatException' => 'PhabricatorAuthSSHPrivateKeyException',
'PhabricatorAuthSSHPrivateKeyIncorrectPassphraseException' => 'PhabricatorAuthSSHPrivateKeyPassphraseException',
'PhabricatorAuthSSHPrivateKeyMissingPassphraseException' => 'PhabricatorAuthSSHPrivateKeyPassphraseException',
'PhabricatorAuthSSHPrivateKeyPassphraseException' => 'PhabricatorAuthSSHPrivateKeyException',
'PhabricatorAuthSSHPrivateKeySurplusPassphraseException' => 'PhabricatorAuthSSHPrivateKeyPassphraseException',
'PhabricatorAuthSSHPrivateKeyUnknownException' => 'PhabricatorAuthSSHPrivateKeyException',
'PhabricatorAuthSSHPublicKey' => 'Phobject',
'PhabricatorAuthSSHRevoker' => 'PhabricatorAuthRevoker',
'PhabricatorAuthSession' => array(
@@ -10592,6 +10624,8 @@ phutil_register_library_map(array(
'PhabricatorPeopleTasksProfileMenuItem' => 'PhabricatorProfileMenuItem',
'PhabricatorPeopleTestDataGenerator' => 'PhabricatorTestDataGenerator',
'PhabricatorPeopleTransactionQuery' => 'PhabricatorApplicationTransactionQuery',
'PhabricatorPeopleUserEmailPHIDType' => 'PhabricatorPHIDType',
'PhabricatorPeopleUserEmailQuery' => 'PhabricatorCursorPagedPolicyAwareQuery',
'PhabricatorPeopleUserFunctionDatasource' => 'PhabricatorTypeaheadCompositeDatasource',
'PhabricatorPeopleUserPHIDType' => 'PhabricatorPHIDType',
'PhabricatorPeopleUsernameMailEngine' => 'PhabricatorPeopleMailEngine',
@@ -10706,7 +10740,6 @@ phutil_register_library_map(array(
'PhabricatorPolicyRule' => 'Phobject',
'PhabricatorPolicyRulesView' => 'AphrontView',
'PhabricatorPolicySearchEngineExtension' => 'PhabricatorSearchEngineExtension',
'PhabricatorPolicyStrengthConstants' => 'PhabricatorPolicyConstants',
'PhabricatorPolicyTestCase' => 'PhabricatorTestCase',
'PhabricatorPolicyTestObject' => array(
'Phobject',
@@ -10751,6 +10784,7 @@ phutil_register_library_map(array(
'PhabricatorColumnProxyInterface',
'PhabricatorSpacesInterface',
'PhabricatorEditEngineSubtypeInterface',
'PhabricatorWorkboardInterface',
),
'PhabricatorProjectActivityChartEngine' => 'PhabricatorChartEngine',
'PhabricatorProjectAddHeraldAction' => 'PhabricatorProjectHeraldAction',
@@ -11263,10 +11297,12 @@ phutil_register_library_map(array(
'PhabricatorSearchEngineExtensionModule' => 'PhabricatorConfigModule',
'PhabricatorSearchFerretNgramGarbageCollector' => 'PhabricatorGarbageCollector',
'PhabricatorSearchField' => 'Phobject',
'PhabricatorSearchHandleController' => 'PhabricatorSearchBaseController',
'PhabricatorSearchHost' => 'Phobject',
'PhabricatorSearchHovercardController' => 'PhabricatorSearchBaseController',
'PhabricatorSearchIndexVersion' => 'PhabricatorSearchDAO',
'PhabricatorSearchIndexVersionDestructionEngineExtension' => 'PhabricatorDestructionEngineExtension',
'PhabricatorSearchIntField' => 'PhabricatorSearchField',
'PhabricatorSearchManagementIndexWorkflow' => 'PhabricatorSearchManagementWorkflow',
'PhabricatorSearchManagementInitWorkflow' => 'PhabricatorSearchManagementWorkflow',
'PhabricatorSearchManagementNgramsWorkflow' => 'PhabricatorSearchManagementWorkflow',
@@ -11504,6 +11540,7 @@ phutil_register_library_map(array(
'PhabricatorSystemActionRateLimitException' => 'Exception',
'PhabricatorSystemApplication' => 'PhabricatorApplication',
'PhabricatorSystemDAO' => 'PhabricatorLiskDAO',
'PhabricatorSystemDebugUIEventListener' => 'PhabricatorEventListener',
'PhabricatorSystemDestructionGarbageCollector' => 'PhabricatorGarbageCollector',
'PhabricatorSystemDestructionLog' => 'PhabricatorSystemDAO',
'PhabricatorSystemObjectController' => 'PhabricatorController',
@@ -11654,7 +11691,11 @@ phutil_register_library_map(array(
'PhabricatorUserEditEngine' => 'PhabricatorEditEngine',
'PhabricatorUserEditor' => 'PhabricatorEditor',
'PhabricatorUserEditorTestCase' => 'PhabricatorTestCase',
'PhabricatorUserEmail' => 'PhabricatorUserDAO',
'PhabricatorUserEmail' => array(
'PhabricatorUserDAO',
'PhabricatorDestructibleInterface',
'PhabricatorPolicyInterface',
),
'PhabricatorUserEmailTestCase' => 'PhabricatorTestCase',
'PhabricatorUserEmpowerTransaction' => 'PhabricatorUserTransactionType',
'PhabricatorUserFerretEngine' => 'PhabricatorFerretEngine',

View File

@@ -93,6 +93,12 @@ final class PhabricatorAuditManagementDeleteWorkflow
if ($repos) {
$query->withRepositoryIDs(mpull($repos, 'getID'));
// See T13457. If we're iterating over commits in a single large
// repository, the lack of a "<repositoryID, [id]>" key can slow things
// down. Iterate in a specific order to use a key which is present
// on the table ("<repositoryID, epoch, [id]>").
$query->setOrderVector(array('-epoch', '-id'));
}
$auditor_map = array();
@@ -105,7 +111,11 @@ final class PhabricatorAuditManagementDeleteWorkflow
$query->withPHIDs(mpull($commits, 'getPHID'));
}
$commit_iterator = new PhabricatorQueryIterator($query);
$commit_iterator = id(new PhabricatorQueryIterator($query));
// See T13457. We may be examining many commits; each commit is small so
// we can safely increase the page size to improve performance a bit.
$commit_iterator->setPageSize(1000);
$audits = array();
foreach ($commit_iterator as $commit) {

View File

@@ -14,7 +14,9 @@ final class PhutilAsanaAuthAdapter extends PhutilOAuthAuthAdapter {
}
public function getAccountID() {
return $this->getOAuthAccountData('id');
// See T13453. The Asana API has changed to string IDs and now returns a
// "gid" field (previously, it returned an "id" field).
return $this->getOAuthAccountData('gid');
}
public function getAccountEmail() {

View File

@@ -286,4 +286,26 @@ abstract class PhabricatorAuthController extends PhabricatorController {
->appendChild($invite_list);
}
final protected function newCustomStartMessage() {
$viewer = $this->getViewer();
$text = PhabricatorAuthMessage::loadMessageText(
$viewer,
PhabricatorAuthLoginMessageType::MESSAGEKEY);
if (!strlen($text)) {
return null;
}
$remarkup_view = new PHUIRemarkupView($viewer, $text);
return phutil_tag(
'div',
array(
'class' => 'auth-custom-message',
),
$remarkup_view);
}
}

View File

@@ -238,18 +238,24 @@ final class PhabricatorAuthLoginController
$content) {
$crumbs = $this->buildApplicationCrumbs();
$viewer = $this->getViewer();
if ($this->getRequest()->getUser()->isLoggedIn()) {
if ($viewer->isLoggedIn()) {
$crumbs->addTextCrumb(pht('Link Account'), $provider->getSettingsURI());
} else {
$crumbs->addTextCrumb(pht('Log In'), $this->getApplicationURI('start/'));
$crumbs->addTextCrumb(pht('Login'), $this->getApplicationURI('start/'));
$content = array(
$this->newCustomStartMessage(),
$content,
);
}
$crumbs->addTextCrumb($provider->getProviderName());
$crumbs->setBorder(true);
return $this->newPage()
->setTitle(pht('Log In'))
->setTitle(pht('Login'))
->setCrumbs($crumbs)
->appendChild($content);
}

View File

@@ -298,27 +298,6 @@ final class PhabricatorAuthStartController
->setURI($auto_uri);
}
private function newCustomStartMessage() {
$viewer = $this->getViewer();
$text = PhabricatorAuthMessage::loadMessageText(
$viewer,
PhabricatorAuthLoginMessageType::MESSAGEKEY);
if (!strlen($text)) {
return null;
}
$remarkup_view = new PHUIRemarkupView($viewer, $text);
return phutil_tag(
'div',
array(
'class' => 'auth-custom-message',
),
$remarkup_view);
}
private function newEmailLoginView(array $configs) {
assert_instances_of($configs, 'PhabricatorAuthProviderConfig');

View File

@@ -0,0 +1,9 @@
<?php
abstract class PhabricatorAuthSSHPrivateKeyException
extends Exception {
abstract public function isFormatException();
abstract public function isPassphraseException();
}

View File

@@ -0,0 +1,14 @@
<?php
final class PhabricatorAuthSSHPrivateKeyFormatException
extends PhabricatorAuthSSHPrivateKeyException {
public function isFormatException() {
return true;
}
public function isPassphraseException() {
return false;
}
}

View File

@@ -0,0 +1,4 @@
<?php
final class PhabricatorAuthSSHPrivateKeyIncorrectPassphraseException
extends PhabricatorAuthSSHPrivateKeyPassphraseException {}

View File

@@ -0,0 +1,4 @@
<?php
final class PhabricatorAuthSSHPrivateKeyMissingPassphraseException
extends PhabricatorAuthSSHPrivateKeyPassphraseException {}

View File

@@ -0,0 +1,14 @@
<?php
abstract class PhabricatorAuthSSHPrivateKeyPassphraseException
extends PhabricatorAuthSSHPrivateKeyException {
final public function isFormatException() {
return false;
}
final public function isPassphraseException() {
return true;
}
}

View File

@@ -0,0 +1,4 @@
<?php
final class PhabricatorAuthSSHPrivateKeySurplusPassphraseException
extends PhabricatorAuthSSHPrivateKeyPassphraseException {}

View File

@@ -0,0 +1,14 @@
<?php
final class PhabricatorAuthSSHPrivateKeyUnknownException
extends PhabricatorAuthSSHPrivateKeyException {
public function isFormatException() {
return true;
}
public function isPassphraseException() {
return true;
}
}

View File

@@ -0,0 +1,210 @@
<?php
/**
* Data structure representing a raw private key.
*/
final class PhabricatorAuthSSHPrivateKey extends Phobject {
private $body;
private $passphrase;
private function __construct() {
// <internal>
}
public function setPassphrase(PhutilOpaqueEnvelope $passphrase) {
$this->passphrase = $passphrase;
return $this;
}
public function getPassphrase() {
return $this->passphrase;
}
public static function newFromRawKey(PhutilOpaqueEnvelope $entire_key) {
$key = new self();
$key->body = $entire_key;
return $key;
}
public function getKeyBody() {
return $this->body;
}
public function newBarePrivateKey() {
if (!Filesystem::binaryExists('ssh-keygen')) {
throw new Exception(
pht(
'Analyzing or decrypting SSH keys requires the "ssh-keygen" binary, '.
'but it is not available in "$PATH". Make it available to work with '.
'SSH private keys.'));
}
$old_body = $this->body;
// Some versions of "ssh-keygen" are sensitive to trailing whitespace for
// some keys. Trim any trailing whitespace and replace it with a single
// newline.
$raw_body = $old_body->openEnvelope();
$raw_body = rtrim($raw_body)."\n";
$old_body = new PhutilOpaqueEnvelope($raw_body);
$tmp = $this->newTemporaryPrivateKeyFile($old_body);
// See T13454 for discussion of why this is so awkward. In broad strokes,
// we don't have a straightforward way to distinguish between keys with an
// invalid format and keys with a passphrase which we don't know.
// First, try to extract the public key from the file using the (possibly
// empty) passphrase we were given. If everything is in good shape, this
// should work.
$passphrase = $this->getPassphrase();
if ($passphrase) {
list($err, $stdout, $stderr) = exec_manual(
'ssh-keygen -y -P %P -f %R',
$passphrase,
$tmp);
} else {
list($err, $stdout, $stderr) = exec_manual(
'ssh-keygen -y -P %s -f %R',
'',
$tmp);
}
// If that worked, the key is good and the (possibly empty) passphrase is
// correct. Strip the passphrase if we have one, then return the bare key.
if (!$err) {
if ($passphrase) {
execx(
'ssh-keygen -y -P %P -N %s -f %R',
$passphrase,
'',
$tmp);
$new_body = new PhutilOpaqueEnvelope(Filesystem::readFile($tmp));
unset($tmp);
} else {
$new_body = $old_body;
}
return self::newFromRawKey($new_body);
}
// We were not able to extract the public key. Try to figure out why. The
// reasons we expect are:
//
// - We were given a passphrase, but the key has no passphrase.
// - We were given a passphrase, but the passphrase is wrong.
// - We were not given a passphrase, but the key has a passphrase.
// - The key format is invalid.
//
// Our ability to separate these cases varies a lot, particularly because
// some versions of "ssh-keygen" return very similar diagnostic messages
// for any error condition. Try our best.
if ($passphrase) {
// First, test for "we were given a passphrase, but the key has no
// passphrase", since this is a conclusive test.
list($err) = exec_manual(
'ssh-keygen -y -P %s -f %R',
'',
$tmp);
if (!$err) {
throw new PhabricatorAuthSSHPrivateKeySurplusPassphraseException(
pht(
'A passphrase was provided for this private key, but it does '.
'not require a passphrase. Check that you supplied the correct '.
'key, or omit the passphrase.'));
}
}
// We're out of conclusive tests, so try to guess why the error occurred.
// In some versions of "ssh-keygen", we get a usable diagnostic message. In
// other versions, not so much.
$reason_format = 'format';
$reason_passphrase = 'passphrase';
$reason_unknown = 'unknown';
$patterns = array(
// macOS 10.14.6
'/incorrect passphrase supplied to decrypt private key/'
=> $reason_passphrase,
// macOS 10.14.6
'/invalid format/' => $reason_format,
// Ubuntu 14
'/load failed/' => $reason_unknown,
);
$reason = 'unknown';
foreach ($patterns as $pattern => $pattern_reason) {
$ok = preg_match($pattern, $stderr);
if ($ok === false) {
throw new Exception(
pht(
'Pattern "%s" is not valid.',
$pattern));
}
if ($ok) {
$reason = $pattern_reason;
break;
}
}
if ($reason === $reason_format) {
throw new PhabricatorAuthSSHPrivateKeyFormatException(
pht(
'This private key is not formatted correctly. Check that you '.
'have provided the complete text of a valid private key.'));
}
if ($reason === $reason_passphrase) {
if ($passphrase) {
throw new PhabricatorAuthSSHPrivateKeyIncorrectPassphraseException(
pht(
'This private key requires a passphrase, but the wrong '.
'passphrase was provided. Check that you supplied the correct '.
'key and passphrase.'));
} else {
throw new PhabricatorAuthSSHPrivateKeyIncorrectPassphraseException(
pht(
'This private key requires a passphrase, but no passphrase was '.
'provided. Check that you supplied the correct key, or provide '.
'the passphrase.'));
}
}
if ($passphrase) {
throw new PhabricatorAuthSSHPrivateKeyUnknownException(
pht(
'This private key could not be opened with the provided passphrase. '.
'This might mean that the passphrase is wrong or that the key is '.
'not formatted correctly. Check that you have supplied the '.
'complete text of a valid private key and the correct passphrase.'));
} else {
throw new PhabricatorAuthSSHPrivateKeyUnknownException(
pht(
'This private key could not be opened. This might mean that the '.
'key requires a passphrase, or might mean that the key is not '.
'formatted correctly. Check that you have supplied the complete '.
'text of a valid private key and the correct passphrase.'));
}
}
private function newTemporaryPrivateKeyFile(PhutilOpaqueEnvelope $key_body) {
$tmp = new TempFile();
Filesystem::writeFile($tmp, $key_body->openEnvelope());
return $tmp;
}
}

View File

@@ -99,9 +99,6 @@ final class PhabricatorConduitAPIController
list($error_code, $error_info) = $auth_error;
}
} catch (Exception $ex) {
if (!($ex instanceof ConduitMethodNotFoundException)) {
phlog($ex);
}
$result = null;
$error_code = ($ex instanceof ConduitException
? 'ERR-CONDUIT-CALL'

View File

@@ -113,7 +113,8 @@ final class PhabricatorManualActivitySetupCheck
'pre',
array(),
(string)csprintf(
'phabricator/ $ ./bin/repository rebuild-identities --all'));
'phabricator/ $ '.
'./bin/repository rebuild-identities --all-repositories'));
$message[] = pht(
'You can find more information about this new identity mapping '.

View File

@@ -64,13 +64,12 @@ final class PhabricatorConfigAllController
$content = id(new PHUITwoColumnView())
->setHeader($header)
->setNavigation($nav)
->setFixed(true)
->setMainColumn($view);
->setFooter($view);
return $this->newPage()
->setTitle($title)
->setCrumbs($crumbs)
->setNavigation($nav)
->appendChild($content);
}

View File

@@ -18,9 +18,7 @@ final class PhabricatorConfigApplicationController
$content = id(new PHUITwoColumnView())
->setHeader($header)
->setNavigation($nav)
->setFixed(true)
->setMainColumn($apps_list);
->setFooter($apps_list);
$crumbs = $this->buildApplicationCrumbs()
->addTextCrumb($title)
@@ -29,6 +27,7 @@ final class PhabricatorConfigApplicationController
return $this->newPage()
->setTitle($title)
->setCrumbs($crumbs)
->setNavigation($nav)
->appendChild($content);
}

View File

@@ -33,13 +33,12 @@ final class PhabricatorConfigCacheController
$content = id(new PHUITwoColumnView())
->setHeader($header)
->setNavigation($nav)
->setFixed(true)
->setMainColumn($page);
->setFooter($page);
return $this->newPage()
->setTitle($title)
->setCrumbs($crumbs)
->setNavigation($nav)
->appendChild($content);
}

View File

@@ -26,13 +26,12 @@ final class PhabricatorConfigClusterDatabasesController
$content = id(new PHUITwoColumnView())
->setHeader($header)
->setNavigation($nav)
->setFixed(true)
->setMainColumn($status);
->setFooter($status);
return $this->newPage()
->setTitle($title)
->setCrumbs($crumbs)
->setNavigation($nav)
->appendChild($content);
}

View File

@@ -28,13 +28,12 @@ final class PhabricatorConfigClusterNotificationsController
$content = id(new PHUITwoColumnView())
->setHeader($header)
->setNavigation($nav)
->setFixed(true)
->setMainColumn($status);
->setFooter($status);
return $this->newPage()
->setTitle($title)
->setCrumbs($crumbs)
->setNavigation($nav)
->appendChild($content);
}

View File

@@ -32,16 +32,16 @@ final class PhabricatorConfigClusterRepositoriesController
$content = id(new PHUITwoColumnView())
->setHeader($header)
->setNavigation($nav)
->setFixed(true)
->setMainColumn(array(
$repo_status,
$repo_errors,
));
->setFooter(
array(
$repo_status,
$repo_errors,
));
return $this->newPage()
->setTitle($title)
->setCrumbs($crumbs)
->setNavigation($nav)
->appendChild($content);
}

View File

@@ -26,13 +26,12 @@ final class PhabricatorConfigClusterSearchController
$content = id(new PHUITwoColumnView())
->setHeader($header)
->setNavigation($nav)
->setFixed(true)
->setMainColumn($search_status);
->setFooter($search_status);
return $this->newPage()
->setTitle($title)
->setCrumbs($crumbs)
->setNavigation($nav)
->appendChild($content);
}

View File

@@ -7,8 +7,6 @@ abstract class PhabricatorConfigController extends PhabricatorController {
}
public function buildSideNavView($filter = null, $for_app = false) {
$guide_href = new PhutilURI('/guides/');
$nav = new AphrontSideNavFilterView();
$nav->setBaseURI(new PhutilURI($this->getApplicationURI()));

View File

@@ -167,13 +167,12 @@ final class PhabricatorConfigDatabaseIssueController
$content = id(new PHUITwoColumnView())
->setHeader($header)
->setNavigation($nav)
->setFixed(true)
->setMainColumn($view);
->setFooter($view);
return $this->newPage()
->setTitle($title)
->setCrumbs($crumbs)
->setNavigation($nav)
->appendChild($content);
}

View File

@@ -142,13 +142,12 @@ final class PhabricatorConfigDatabaseStatusController
$content = id(new PHUITwoColumnView())
->setHeader($header)
->setNavigation($nav)
->setFixed(true)
->setMainColumn($body);
->setFooter($body);
return $this->newPage()
->setTitle($title)
->setCrumbs($crumbs)
->setNavigation($nav)
->appendChild($content);
}

View File

@@ -237,9 +237,8 @@ final class PhabricatorConfigEditController
$view = id(new PHUITwoColumnView())
->setHeader($header)
->setNavigation($nav)
->setFixed(true)
->setMainColumn(array(
->setFooter(
array(
$error_view,
$form_box,
$status_items,
@@ -250,6 +249,7 @@ final class PhabricatorConfigEditController
return $this->newPage()
->setTitle($title)
->setCrumbs($crumbs)
->setNavigation($nav)
->appendChild($view);
}

View File

@@ -36,13 +36,12 @@ final class PhabricatorConfigGroupController
$content = id(new PHUITwoColumnView())
->setHeader($header)
->setNavigation($nav)
->setFixed(true)
->setMainColumn($view);
->setFooter($view);
return $this->newPage()
->setTitle($title)
->setCrumbs($crumbs)
->setNavigation($nav)
->appendChild($content);
}

View File

@@ -36,13 +36,12 @@ final class PhabricatorConfigHistoryController
$content = id(new PHUITwoColumnView())
->setHeader($header)
->setNavigation($nav)
->setFixed(true)
->setMainColumn($timeline);
->setFooter($timeline);
return $this->newPage()
->setTitle($title)
->setCrumbs($crumbs)
->setNavigation($nav)
->appendChild($content);
}

View File

@@ -59,13 +59,12 @@ final class PhabricatorConfigIssueListController
$content = id(new PHUITwoColumnView())
->setHeader($header)
->setNavigation($nav)
->setFixed(true)
->setMainColumn($issue_list);
->setFooter($issue_list);
return $this->newPage()
->setTitle($title)
->setCrumbs($crumbs)
->setNavigation($nav)
->appendChild($content);
}

View File

@@ -47,13 +47,12 @@ final class PhabricatorConfigIssueViewController
$content = id(new PHUITwoColumnView())
->setHeader($header)
->setNavigation($nav)
->setFixed(true)
->setMainColumn($content);
->setFooter($content);
return $this->newPage()
->setTitle($title)
->setCrumbs($crumbs)
->setNavigation($nav)
->appendChild($content);
}

View File

@@ -22,13 +22,12 @@ final class PhabricatorConfigListController
$content = id(new PHUITwoColumnView())
->setHeader($header)
->setNavigation($nav)
->setFixed(true)
->setMainColumn($core_list);
->setFooter($core_list);
return $this->newPage()
->setTitle($title)
->setCrumbs($crumbs)
->setNavigation($nav)
->appendChild($content);
}

View File

@@ -28,13 +28,12 @@ final class PhabricatorConfigModuleController
$content = id(new PHUITwoColumnView())
->setHeader($header)
->setNavigation($nav)
->setFixed(true)
->setMainColumn($view);
->setFooter($view);
return $this->newPage()
->setTitle($title)
->setCrumbs($crumbs)
->setNavigation($nav)
->appendChild($content);
}

View File

@@ -23,15 +23,13 @@ final class PhabricatorConfigVersionController
$content = id(new PHUITwoColumnView())
->setHeader($header)
->setNavigation($nav)
->setFixed(true)
->setMainColumn($view);
->setFooter($view);
return $this->newPage()
->setTitle($title)
->setCrumbs($crumbs)
->setNavigation($nav)
->appendChild($content);
}
public function renderModuleStatus($viewer) {

View File

@@ -329,9 +329,16 @@ final class PhabricatorDashboardPanelRenderingEngine extends Phobject {
$actions = array();
if ($panel) {
$panel_actions = $panel->newHeaderEditActions(
$viewer,
$context_phid);
try {
$panel_actions = $panel->newHeaderEditActions(
$viewer,
$context_phid);
} catch (Exception $ex) {
$error_action = id(new PhabricatorActionView())
->setIcon('fa-exclamation-triangle red')
->setName(pht('<Rendering Exception>'));
$panel_actions[] = $error_action;
}
if ($panel_actions) {
foreach ($panel_actions as $panel_action) {

View File

@@ -9,4 +9,14 @@ final class PhabricatorDashboardTextPanelTextTransaction
return 'text';
}
public function newRemarkupChanges() {
$changes = array();
$changes[] = $this->newRemarkupChange()
->setOldValue($this->getOldValue())
->setNewValue($this->getNewValue());
return $changes;
}
}

View File

@@ -103,7 +103,7 @@ final class PhabricatorDifferentialRevisionTestDataGenerator
$newcode2[] = $altcodearr[$randomlines_new[$c++]];
}
}
return implode($newcode2, "\n");
return implode("\n", $newcode2);
}
}

View File

@@ -1686,40 +1686,72 @@ final class DifferentialChangesetParser extends Phobject {
break;
}
$old_ref = id(new PhabricatorDocumentRef())
->setName($changeset->getOldFile());
if ($old_file) {
$old_ref->setFile($old_file);
} else {
$old_data = $this->old;
$old_data = ipull($old_data, 'text');
$old_data = implode('', $old_data);
$type_delete = DifferentialChangeType::TYPE_DELETE;
$type_add = DifferentialChangeType::TYPE_ADD;
$change_type = $changeset->getChangeType();
$old_ref->setData($old_data);
$no_old = ($change_type == $type_add);
$no_new = ($change_type == $type_delete);
if ($no_old) {
$old_ref = null;
} else {
$old_ref = id(new PhabricatorDocumentRef())
->setName($changeset->getOldFile());
if ($old_file) {
$old_ref->setFile($old_file);
} else {
$old_data = $this->old;
$old_data = ipull($old_data, 'text');
$old_data = implode('', $old_data);
$old_ref->setData($old_data);
}
}
$new_ref = id(new PhabricatorDocumentRef())
->setName($changeset->getFilename());
if ($new_file) {
$new_ref->setFile($new_file);
if ($no_new) {
$new_ref = null;
} else {
$new_data = $this->new;
$new_data = ipull($new_data, 'text');
$new_data = implode('', $new_data);
$new_ref = id(new PhabricatorDocumentRef())
->setName($changeset->getFilename());
if ($new_file) {
$new_ref->setFile($new_file);
} else {
$new_data = $this->new;
$new_data = ipull($new_data, 'text');
$new_data = implode('', $new_data);
$new_ref->setData($new_data);
$new_ref->setData($new_data);
}
}
$old_engines = PhabricatorDocumentEngine::getEnginesForRef(
$viewer,
$old_ref);
$new_engines = PhabricatorDocumentEngine::getEnginesForRef(
$viewer,
$new_ref);
$old_engines = null;
if ($old_ref) {
$old_engines = PhabricatorDocumentEngine::getEnginesForRef(
$viewer,
$old_ref);
}
$shared_engines = array_intersect_key($new_engines, $old_engines);
$default_engine = head_key($new_engines);
$new_engines = null;
if ($new_ref) {
$new_engines = PhabricatorDocumentEngine::getEnginesForRef(
$viewer,
$new_ref);
}
if ($new_engines !== null && $old_engines !== null) {
$shared_engines = array_intersect_key($new_engines, $old_engines);
$default_engine = head_key($new_engines);
} else if ($new_engines !== null) {
$shared_engines = $new_engines;
$default_engine = head_key($shared_engines);
} else if ($old_engines !== null) {
$shared_engines = $old_engines;
$default_engine = head_key($shared_engines);
} else {
return null;
}
foreach ($shared_engines as $key => $shared_engine) {
if (!$shared_engine->canDiffDocuments($old_ref, $new_ref)) {

View File

@@ -44,15 +44,6 @@ final class DifferentialRevisionPHIDType extends PhabricatorPHIDType {
if ($revision->isClosed()) {
$handle->setStatus(PhabricatorObjectHandle::STATUS_CLOSED);
}
$icon = $revision->getStatusIcon();
$color = $revision->getStatusIconColor();
$name = $revision->getStatusDisplayName();
$handle
->setStateIcon($icon)
->setStateColor($color)
->setStateName($name);
}
}

View File

@@ -22,6 +22,10 @@ final class DifferentialChangesetSearchEngine
return 'PhabricatorDifferentialApplication';
}
public function canUseInPanelContext() {
return false;
}
public function newQuery() {
$query = id(new DifferentialChangesetQuery());

View File

@@ -371,17 +371,27 @@ final class DifferentialChangesetOneUpRenderer
$cell_classes = $block_diff->getNewClasses();
}
} else if ($row_type === 'old') {
if (!$old_ref || !$old) {
continue;
}
$cell_content = $engine->newBlockContentView(
$old_ref,
$old);
$cell_classes[] = 'old';
$cell_classes[] = 'old-full';
$new_key = null;
} else if ($row_type === 'new') {
if (!$new_ref || !$new) {
continue;
}
$cell_content = $engine->newBlockContentView(
$new_ref,
$new);
$cell_classes[] = 'new';
$cell_classes[] = 'new-full';

View File

@@ -22,11 +22,13 @@ final class DiffusionIdentityViewController
$header = id(new PHUIHeaderView())
->setUser($viewer)
->setHeader($identity->getIdentityShortName())
->setHeaderIcon('fa-globe')
->setPolicyObject($identity);
->setHeaderIcon('fa-globe');
$crumbs = $this->buildApplicationCrumbs();
$crumbs->addTextCrumb($identity->getID());
$crumbs->addTextCrumb(
pht('Identities'),
$this->getApplicationURI('identity/'));
$crumbs->addTextCrumb($identity->getObjectName());
$crumbs->setBorder(true);
$timeline = $this->buildTransactionTimeline(
@@ -83,7 +85,11 @@ final class DiffusionIdentityViewController
$viewer = $this->getViewer();
$properties = id(new PHUIPropertyListView())
->setUser($viewer);
->setViewer($viewer);
$properties->addProperty(
pht('Email Address'),
$identity->getEmailAddress());
$effective_phid = $identity->getCurrentEffectiveUserPHID();
$automatic_phid = $identity->getAutomaticGuessedUserPHID();
@@ -109,7 +115,7 @@ final class DiffusionIdentityViewController
pht('Automatically Detected User'),
$this->buildPropertyValue($automatic_phid));
$properties->addProperty(
pht('Manually Set User'),
pht('Assigned To'),
$this->buildPropertyValue($manual_phid));
$header = id(new PHUIHeaderView())
@@ -127,7 +133,7 @@ final class DiffusionIdentityViewController
if ($value == DiffusionIdentityUnassignedDatasource::FUNCTION_TOKEN) {
return phutil_tag('em', array(), pht('Explicitly Unassigned'));
} else if (!$value) {
return null;
return phutil_tag('em', array(), pht('None'));
} else {
return $viewer->renderHandle($value);
}

View File

@@ -17,6 +17,14 @@ final class DiffusionRepositoryListController extends DiffusionController {
->setName(pht('Browse Commits'))
->setHref($this->getApplicationURI('commit/'));
$items[] = id(new PHUIListItemView())
->setType(PHUIListItemView::TYPE_LABEL)
->setName(pht('Identities'));
$items[] = id(new PHUIListItemView())
->setName(pht('Browse Identities'))
->setHref($this->getApplicationURI('identity/'));
return id(new PhabricatorRepositorySearchEngine())
->setController($this)
->setNavigationItems($items)

View File

@@ -26,20 +26,45 @@ final class DiffusionHovercardEngineExtension
$viewer = $this->getViewer();
$author_phid = $commit->getAuthorPHID();
if ($author_phid) {
$author = $viewer->renderHandle($author_phid);
} else {
$commit_data = $commit->loadCommitData();
$author = phutil_tag('em', array(), $commit_data->getAuthorName());
$commit = id(new DiffusionCommitQuery())
->setViewer($viewer)
->needIdentities(true)
->needCommitData(true)
->withPHIDs(array($commit->getPHID()))
->executeOne();
if (!$commit) {
return;
}
$author_phid = $commit->getAuthorDisplayPHID();
$committer_phid = $commit->getCommitterDisplayPHID();
$repository_phid = $commit->getRepository()->getPHID();
$phids = array();
$phids[] = $author_phid;
$phids[] = $committer_phid;
$phids[] = $repository_phid;
$handles = $viewer->loadHandles($phids);
$hovercard->setTitle($handle->getName());
$hovercard->setDetail($commit->getSummary());
$hovercard->addField(pht('Author'), $author);
$hovercard->addField(pht('Date'),
phabricator_date($commit->getEpoch(), $viewer));
$repository = $handles[$repository_phid]->renderLink();
$hovercard->addField(pht('Repository'), $repository);
$author = $handles[$author_phid]->renderLink();
if ($author_phid) {
$hovercard->addField(pht('Author'), $author);
}
if ($committer_phid && ($committer_phid !== $author_phid)) {
$committer = $handles[$committer_phid]->renderLink();
$hovercard->addField(pht('Committer'), $committer);
}
$date = phabricator_date($commit->getEpoch(), $viewer);
$hovercard->addField(pht('Date'), $date);
if (!$commit->isAuditStatusNoAudit()) {
$status = $commit->getAuditStatusObject();

View File

@@ -0,0 +1,41 @@
<?php
final class DiffusionRepositoryMetricsSearchEngineAttachment
extends PhabricatorSearchEngineAttachment {
public function getAttachmentName() {
return pht('Repository Metrics');
}
public function getAttachmentDescription() {
return pht(
'Get metrics (like commit count and most recent commit) for each '.
'repository.');
}
public function willLoadAttachmentData($query, $spec) {
$query
->needCommitCounts(true)
->needMostRecentCommits(true);
}
public function getAttachmentForObject($object, $data, $spec) {
$commit = $object->getMostRecentCommit();
if ($commit !== null) {
$recent_commit = $commit->getFieldValuesForConduit();
} else {
$recent_commit = null;
}
$commit_count = $object->getCommitCount();
if ($commit_count !== null) {
$commit_count = (int)$commit_count;
}
return array(
'commitCount' => $commit_count,
'recentCommit' => $recent_commit,
);
}
}

View File

@@ -0,0 +1,40 @@
<?php
final class DiffusionRepositoryIdentityDestructionEngineExtension
extends PhabricatorDestructionEngineExtension {
const EXTENSIONKEY = 'repository-identities';
public function getExtensionName() {
return pht('Repository Identities');
}
public function didDestroyObject(
PhabricatorDestructionEngine $engine,
$object) {
// When users or email addresses are destroyed, queue a task to update
// any repository identities that are associated with them. See T13444.
$related_phids = array();
$email_addresses = array();
if ($object instanceof PhabricatorUser) {
$related_phids[] = $object->getPHID();
}
if ($object instanceof PhabricatorUserEmail) {
$email_addresses[] = $object->getAddress();
}
if ($related_phids || $email_addresses) {
PhabricatorWorker::scheduleTask(
'PhabricatorRepositoryIdentityChangeWorker',
array(
'relatedPHIDs' => $related_phids,
'emailAddresses' => $email_addresses,
));
}
}
}

View File

@@ -0,0 +1,120 @@
<?php
final class DiffusionRepositoryIdentityEngine
extends Phobject {
private $viewer;
private $sourcePHID;
private $dryRun;
public function setViewer(PhabricatorUser $viewer) {
$this->viewer = $viewer;
return $this;
}
public function getViewer() {
return $this->viewer;
}
public function setSourcePHID($source_phid) {
$this->sourcePHID = $source_phid;
return $this;
}
public function getSourcePHID() {
if (!$this->sourcePHID) {
throw new PhutilInvalidStateException('setSourcePHID');
}
return $this->sourcePHID;
}
public function setDryRun($dry_run) {
$this->dryRun = $dry_run;
return $this;
}
public function getDryRun() {
return $this->dryRun;
}
public function newResolvedIdentity($raw_identity) {
$identity = $this->loadRawIdentity($raw_identity);
if (!$identity) {
$identity = $this->newIdentity($raw_identity);
}
return $this->updateIdentity($identity);
}
public function newUpdatedIdentity(PhabricatorRepositoryIdentity $identity) {
return $this->updateIdentity($identity);
}
private function loadRawIdentity($raw_identity) {
$viewer = $this->getViewer();
return id(new PhabricatorRepositoryIdentityQuery())
->setViewer($viewer)
->withIdentityNames(array($raw_identity))
->executeOne();
}
private function newIdentity($raw_identity) {
$source_phid = $this->getSourcePHID();
return id(new PhabricatorRepositoryIdentity())
->setAuthorPHID($source_phid)
->setIdentityName($raw_identity);
}
private function resolveIdentity(PhabricatorRepositoryIdentity $identity) {
$raw_identity = $identity->getIdentityName();
return id(new DiffusionResolveUserQuery())
->withName($raw_identity)
->execute();
}
private function updateIdentity(PhabricatorRepositoryIdentity $identity) {
// If we're updating an identity and it has a manual user PHID associated
// with it but the user is no longer valid, remove the value. This likely
// corresponds to a user that was destroyed.
$assigned_phid = $identity->getManuallySetUserPHID();
$unassigned = DiffusionIdentityUnassignedDatasource::FUNCTION_TOKEN;
if ($assigned_phid && ($assigned_phid !== $unassigned)) {
$viewer = $this->getViewer();
$user = id(new PhabricatorPeopleQuery())
->setViewer($viewer)
->withPHIDs(array($assigned_phid))
->executeOne();
if (!$user) {
$identity->setManuallySetUserPHID(null);
}
}
$resolved_phid = $this->resolveIdentity($identity);
$identity->setAutomaticGuessedUserPHID($resolved_phid);
if ($this->getDryRun()) {
$identity->makeEphemeral();
} else {
$identity->save();
}
return $identity;
}
public function didUpdateEmailAddress($raw_address) {
PhabricatorWorker::scheduleTask(
'PhabricatorRepositoryIdentityChangeWorker',
array(
'emailAddresses' => array($raw_address),
));
}
}

View File

@@ -23,6 +23,7 @@ final class DiffusionRepositoryBranchesManagementPanel
$has_any =
$repository->getDetail('default-branch') ||
$repository->getFetchRules() ||
$repository->getTrackOnlyRules() ||
$repository->getPermanentRefRules();

View File

@@ -17,21 +17,35 @@ final class DiffusionRepositoryIdentitySearchEngine
protected function buildCustomSearchFields() {
return array(
id(new PhabricatorUsersSearchField())
->setLabel(pht('Matching Users'))
->setKey('effectivePHIDs')
->setAliases(
array(
'effective',
'effectivePHID',
))
->setDescription(pht('Search for identities by effective user.')),
id(new DiffusionIdentityAssigneeSearchField())
->setLabel(pht('Assigned To'))
->setKey('assignee')
->setDescription(pht('Search for identities by assignee.')),
->setKey('assignedPHIDs')
->setAliases(
array(
'assigned',
'assignedPHID',
))
->setDescription(pht('Search for identities by explicit assignee.')),
id(new PhabricatorSearchTextField())
->setLabel(pht('Identity Contains'))
->setKey('match')
->setDescription(pht('Search for identities by substring.')),
id(new PhabricatorSearchThreeStateField())
->setLabel(pht('Is Assigned'))
->setLabel(pht('Has Matching User'))
->setKey('hasEffectivePHID')
->setOptions(
pht('(Show All)'),
pht('Show Only Assigned Identities'),
pht('Show Only Unassigned Identities')),
pht('Show Identities With Matching Users'),
pht('Show Identities Without Matching Users')),
);
}
@@ -46,8 +60,12 @@ final class DiffusionRepositoryIdentitySearchEngine
$query->withIdentityNameLike($map['match']);
}
if ($map['assignee']) {
$query->withAssigneePHIDs($map['assignee']);
if ($map['assignedPHIDs']) {
$query->withAssignedPHIDs($map['assignedPHIDs']);
}
if ($map['effectivePHIDs']) {
$query->withEffectivePHIDs($map['effectivePHIDs']);
}
return $query;
@@ -86,15 +104,54 @@ final class DiffusionRepositoryIdentitySearchEngine
$viewer = $this->requireViewer();
$list = new PHUIObjectItemListView();
$list->setUser($viewer);
$list = id(new PHUIObjectItemListView())
->setViewer($viewer);
$phids = array();
foreach ($identities as $identity) {
$phids[] = $identity->getCurrentEffectiveUserPHID();
$phids[] = $identity->getManuallySetUserPHID();
}
$handles = $viewer->loadHandles($phids);
$unassigned = DiffusionIdentityUnassignedDatasource::FUNCTION_TOKEN;
foreach ($identities as $identity) {
$item = id(new PHUIObjectItemView())
->setObjectName(pht('Identity %d', $identity->getID()))
->setObjectName($identity->getObjectName())
->setHeader($identity->getIdentityShortName())
->setHref($identity->getURI())
->setObject($identity);
$status_icon = 'fa-circle-o grey';
$effective_phid = $identity->getCurrentEffectiveUserPHID();
if ($effective_phid) {
$item->addIcon(
'fa-id-badge',
pht('Matches User: %s', $handles[$effective_phid]->getName()));
$status_icon = 'fa-id-badge';
}
$assigned_phid = $identity->getManuallySetUserPHID();
if ($assigned_phid) {
if ($assigned_phid === $unassigned) {
$item->addIcon(
'fa-ban',
pht('Explicitly Unassigned'));
$status_icon = 'fa-ban';
} else {
$item->addIcon(
'fa-user',
pht('Assigned To: %s', $handles[$assigned_phid]->getName()));
$status_icon = 'fa-user';
}
}
$item->setStatusIcon($status_icon);
$list->addItem($item);
}

View File

@@ -8,25 +8,14 @@
final class DiffusionResolveUserQuery extends Phobject {
private $name;
private $commit;
public function withName($name) {
$this->name = $name;
return $this;
}
public function withCommit($commit) {
$this->commit = $commit;
return $this;
}
public function execute() {
$user_name = $this->name;
$phid = $this->findUserPHID($this->name);
$phid = $this->fireLookupEvent($phid);
return $phid;
return $this->findUserPHID($this->name);
}
private function findUserPHID($user_name) {
@@ -75,33 +64,15 @@ final class DiffusionResolveUserQuery extends Phobject {
}
/**
* Emit an event so installs can do custom lookup of commit authors who may
* not be naturally resolvable.
*/
private function fireLookupEvent($guess) {
$type = PhabricatorEventType::TYPE_DIFFUSION_LOOKUPUSER;
$data = array(
'commit' => $this->commit,
'query' => $this->name,
'result' => $guess,
);
$event = new PhabricatorEvent($type, $data);
PhutilEventEngine::dispatchEvent($event);
return $event->getValue('result');
}
private function findUserByUserName($user_name) {
$by_username = id(new PhabricatorUser())->loadOneWhere(
'userName = %s',
$user_name);
if ($by_username) {
return $by_username->getPHID();
}
return null;
}
@@ -112,18 +83,22 @@ final class DiffusionResolveUserQuery extends Phobject {
$by_realname = id(new PhabricatorUser())->loadAllWhere(
'realName = %s',
$real_name);
if (count($by_realname) == 1) {
return reset($by_realname)->getPHID();
return head($by_realname)->getPHID();
}
return null;
}
private function findUserByEmailAddress($email_address) {
$by_email = PhabricatorUser::loadOneWithEmailAddress($email_address);
if ($by_email) {
return $by_email->getPHID();
}
return null;
}

View File

@@ -123,8 +123,11 @@ final class DoorkeeperBridgeAsana extends DoorkeeperBridge {
}
public function fillObjectFromData(DoorkeeperExternalObject $obj, $result) {
$id = $result['id'];
$uri = "https://app.asana.com/0/{$id}/{$id}";
$gid = $result['gid'];
$uri = urisprintf(
'https://app.asana.com/0/%s/%s',
$gid,
$gid);
$obj->setObjectURI($uri);
}

View File

@@ -102,7 +102,10 @@ final class PhabricatorAsanaConfigOptions
pht('Workspace Name'));
$out[] = '| ------------ | -------------- |';
foreach ($workspaces as $workspace) {
$out[] = sprintf('| `%s` | `%s` |', $workspace['id'], $workspace['name']);
$out[] = sprintf(
'| `%s` | `%s` |',
$workspace['gid'],
$workspace['name']);
}
$out = implode("\n", $out);

View File

@@ -358,7 +358,7 @@ final class DoorkeeperAsanaFeedWorker extends DoorkeeperFeedWorker {
'POST',
$subtask_data + array(
'assignee' => $phid_aid_map[$user_phid],
'completed' => $is_completed,
'completed' => (int)$is_completed,
'parent' => $parent_ref->getObjectID(),
));
@@ -393,7 +393,7 @@ final class DoorkeeperAsanaFeedWorker extends DoorkeeperFeedWorker {
'PUT',
$subtask_data + array(
'assignee' => $phid_aid_map[$user_phid],
'completed' => $is_completed,
'completed' => (int)$is_completed,
));
}
@@ -484,7 +484,7 @@ final class DoorkeeperAsanaFeedWorker extends DoorkeeperFeedWorker {
return array(
'name' => $title,
'notes' => $notes,
'completed' => $is_completed,
'completed' => (int)$is_completed,
);
}
@@ -632,7 +632,7 @@ final class DoorkeeperAsanaFeedWorker extends DoorkeeperFeedWorker {
->setApplicationType(DoorkeeperBridgeAsana::APPTYPE_ASANA)
->setApplicationDomain(DoorkeeperBridgeAsana::APPDOMAIN_ASANA)
->setObjectType($type)
->setObjectID($result['id'])
->setObjectID($result['gid'])
->setIsVisible(true);
$xobj = $ref->newExternalObject();

View File

@@ -29,6 +29,7 @@ final class PhabricatorFactChartFunction
$key_id = id(new PhabricatorFactKeyDimension())
->newDimensionID($fact->getKey());
if (!$key_id) {
$this->map = array();
return;
}

View File

@@ -15,7 +15,10 @@ final class PhabricatorDocumentEngineBlocks
return $this->messages;
}
public function addBlockList(PhabricatorDocumentRef $ref, array $blocks) {
public function addBlockList(
PhabricatorDocumentRef $ref = null,
array $blocks = array()) {
assert_instances_of($blocks, 'PhabricatorDocumentEngineBlock');
$this->lists[] = array(

View File

@@ -32,8 +32,8 @@ abstract class PhabricatorDocumentEngine
}
public function canDiffDocuments(
PhabricatorDocumentRef $uref,
PhabricatorDocumentRef $vref) {
PhabricatorDocumentRef $uref = null,
PhabricatorDocumentRef $vref = null) {
return false;
}

View File

@@ -18,21 +18,38 @@ final class PhabricatorImageDocumentEngine
}
public function canDiffDocuments(
PhabricatorDocumentRef $uref,
PhabricatorDocumentRef $vref) {
PhabricatorDocumentRef $uref = null,
PhabricatorDocumentRef $vref = null) {
// For now, we can only render a rich image diff if both documents have
// For now, we can only render a rich image diff if the documents have
// their data stored in Files already.
return ($uref->getFile() && $vref->getFile());
if ($uref && !$uref->getFile()) {
return false;
}
if ($vref && !$vref->getFile()) {
return false;
}
return true;
}
public function newEngineBlocks(
PhabricatorDocumentRef $uref,
PhabricatorDocumentRef $vref) {
PhabricatorDocumentRef $uref = null,
PhabricatorDocumentRef $vref = null) {
$u_blocks = $this->newDiffBlocks($uref);
$v_blocks = $this->newDiffBlocks($vref);
if ($uref) {
$u_blocks = $this->newDiffBlocks($uref);
} else {
$u_blocks = array();
}
if ($vref) {
$v_blocks = $this->newDiffBlocks($vref);
} else {
$v_blocks = array();
}
return id(new PhabricatorDocumentEngineBlocks())
->addBlockList($uref, $u_blocks)

View File

@@ -36,20 +36,29 @@ final class PhabricatorJupyterDocumentEngine
}
public function canDiffDocuments(
PhabricatorDocumentRef $uref,
PhabricatorDocumentRef $vref) {
PhabricatorDocumentRef $uref = null,
PhabricatorDocumentRef $vref = null) {
return true;
}
public function newEngineBlocks(
PhabricatorDocumentRef $uref,
PhabricatorDocumentRef $vref) {
PhabricatorDocumentRef $uref = null,
PhabricatorDocumentRef $vref = null) {
$blocks = new PhabricatorDocumentEngineBlocks();
try {
$u_blocks = $this->newDiffBlocks($uref);
$v_blocks = $this->newDiffBlocks($vref);
if ($uref) {
$u_blocks = $this->newDiffBlocks($uref);
} else {
$u_blocks = array();
}
if ($vref) {
$v_blocks = $this->newDiffBlocks($vref);
} else {
$v_blocks = array();
}
$blocks->addBlockList($uref, $u_blocks);
$blocks->addBlockList($vref, $v_blocks);

View File

@@ -0,0 +1,18 @@
<?php
final class HarbormasterArtifactSearchConduitAPIMethod
extends PhabricatorSearchEngineAPIMethod {
public function getAPIMethodName() {
return 'harbormaster.artifact.search';
}
public function newSearchEngine() {
return new HarbormasterArtifactSearchEngine();
}
public function getMethodSummary() {
return pht('Query information about build artifacts.');
}
}

View File

@@ -0,0 +1,93 @@
<?php
final class HarbormasterArtifactSearchEngine
extends PhabricatorApplicationSearchEngine {
public function getResultTypeDescription() {
return pht('Harbormaster Artifacts');
}
public function getApplicationClassName() {
return 'PhabricatorHarbormasterApplication';
}
public function newQuery() {
return new HarbormasterBuildArtifactQuery();
}
protected function buildCustomSearchFields() {
return array(
id(new PhabricatorPHIDsSearchField())
->setLabel(pht('Targets'))
->setKey('buildTargetPHIDs')
->setAliases(
array(
'buildTargetPHID',
'buildTargets',
'buildTarget',
'targetPHIDs',
'targetPHID',
'targets',
'target',
))
->setDescription(
pht('Search for artifacts attached to particular build targets.')),
);
}
protected function buildQueryFromParameters(array $map) {
$query = $this->newQuery();
if ($map['buildTargetPHIDs']) {
$query->withBuildTargetPHIDs($map['buildTargetPHIDs']);
}
return $query;
}
protected function getURI($path) {
return '/harbormaster/artifact/'.$path;
}
protected function getBuiltinQueryNames() {
return array(
'all' => pht('All Artifacts'),
);
}
public function buildSavedQueryFromBuiltin($query_key) {
$query = $this->newSavedQuery();
$query->setQueryKey($query_key);
switch ($query_key) {
case 'all':
return $query;
}
return parent::buildSavedQueryFromBuiltin($query_key);
}
protected function renderResultList(
array $artifacts,
PhabricatorSavedQuery $query,
array $handles) {
assert_instances_of($artifacts, 'HarbormasterBuildArtifact');
$viewer = $this->requireViewer();
$list = new PHUIObjectItemListView();
foreach ($artifacts as $artifact) {
$id = $artifact->getID();
$item = id(new PHUIObjectItemView())
->setObjectName(pht('Artifact %d', $id));
$list->addItem($item);
}
return id(new PhabricatorApplicationSearchResultView())
->setObjectList($list)
->setNoDataString(pht('No artifacts found.'));
}
}

View File

@@ -4,7 +4,8 @@ final class HarbormasterBuildArtifact
extends HarbormasterDAO
implements
PhabricatorPolicyInterface,
PhabricatorDestructibleInterface {
PhabricatorDestructibleInterface,
PhabricatorConduitResultInterface {
protected $buildTargetPHID;
protected $artifactType;
@@ -18,6 +19,7 @@ final class HarbormasterBuildArtifact
public static function initializeNewBuildArtifact(
HarbormasterBuildTarget $build_target) {
return id(new HarbormasterBuildArtifact())
->attachBuildTarget($build_target)
->setBuildTargetPHID($build_target->getPHID());
@@ -53,9 +55,8 @@ final class HarbormasterBuildArtifact
) + parent::getConfiguration();
}
public function generatePHID() {
return PhabricatorPHID::generateNewPHID(
HarbormasterBuildArtifactPHIDType::TYPECONST);
public function getPHIDType() {
return HarbormasterBuildArtifactPHIDType::TYPECONST;
}
public function attachBuildTarget(HarbormasterBuildTarget $build_target) {
@@ -147,7 +148,8 @@ final class HarbormasterBuildArtifact
}
public function describeAutomaticCapability($capability) {
return pht('Users must be able to see a buildable to see its artifacts.');
return pht(
'Users must be able to see a build target to see its artifacts.');
}
@@ -165,4 +167,40 @@ final class HarbormasterBuildArtifact
$this->saveTransaction();
}
/* -( PhabricatorConduitResultInterface )---------------------------------- */
public function getFieldSpecificationsForConduit() {
return array(
id(new PhabricatorConduitSearchFieldSpecification())
->setKey('buildTargetPHID')
->setType('phid')
->setDescription(pht('The build target this artifact is attached to.')),
id(new PhabricatorConduitSearchFieldSpecification())
->setKey('artifactType')
->setType('string')
->setDescription(pht('The artifact type.')),
id(new PhabricatorConduitSearchFieldSpecification())
->setKey('artifactKey')
->setType('string')
->setDescription(pht('The artifact key.')),
id(new PhabricatorConduitSearchFieldSpecification())
->setKey('isReleased')
->setType('bool')
->setDescription(pht('True if this artifact has been released.')),
);
}
public function getFieldValuesForConduit() {
return array(
'buildTargetPHID' => $this->getBuildTargetPHID(),
'artifactType' => $this->getArtifactType(),
'artifactKey' => $this->getArtifactKey(),
'isReleased' => (bool)$this->getIsReleased(),
);
}
public function getConduitSearchAttachments() {
return array();
}
}

View File

@@ -399,7 +399,7 @@ abstract class HeraldAdapter extends Phobject {
self::CONDITION_IS_NOT_ANY => pht('is not any of'),
self::CONDITION_INCLUDE_ALL => pht('include all of'),
self::CONDITION_INCLUDE_ANY => pht('include any of'),
self::CONDITION_INCLUDE_NONE => pht('do not include'),
self::CONDITION_INCLUDE_NONE => pht('include none of'),
self::CONDITION_IS_ME => pht('is myself'),
self::CONDITION_IS_NOT_ME => pht('is not myself'),
self::CONDITION_REGEXP => pht('matches regexp'),

View File

@@ -179,11 +179,14 @@ final class ManiphestTaskDetailController extends ManiphestController {
->addTabGroup($tab_group);
}
$changes_view = $this->newChangesView($task, $edges);
$view = id(new PHUITwoColumnView())
->setHeader($header)
->setCurtain($curtain)
->setMainColumn(
array(
$changes_view,
$tab_view,
$timeline,
$comment_view,
@@ -395,58 +398,6 @@ final class ManiphestTaskDetailController extends ManiphestController {
$source));
}
$edge_types = array(
ManiphestTaskHasRevisionEdgeType::EDGECONST
=> pht('Differential Revisions'),
);
$revisions_commits = array();
$commit_phids = array_keys(
$edges[ManiphestTaskHasCommitEdgeType::EDGECONST]);
if ($commit_phids) {
$commit_drev = DiffusionCommitHasRevisionEdgeType::EDGECONST;
$drev_edges = id(new PhabricatorEdgeQuery())
->withSourcePHIDs($commit_phids)
->withEdgeTypes(array($commit_drev))
->execute();
foreach ($commit_phids as $phid) {
$revisions_commits[$phid] = $handles->renderHandle($phid)
->setShowHovercard(true)
->setShowStateIcon(true);
$revision_phid = key($drev_edges[$phid][$commit_drev]);
$revision_handle = $handles->getHandleIfExists($revision_phid);
if ($revision_handle) {
$task_drev = ManiphestTaskHasRevisionEdgeType::EDGECONST;
unset($edges[$task_drev][$revision_phid]);
$revisions_commits[$phid] = hsprintf(
'%s / %s',
$revision_handle->renderHovercardLink($revision_handle->getName()),
$revisions_commits[$phid]);
}
}
}
foreach ($edge_types as $edge_type => $edge_name) {
if (!$edges[$edge_type]) {
continue;
}
$edge_handles = $viewer->loadHandles(array_keys($edges[$edge_type]));
$edge_list = $edge_handles->renderList()
->setShowStateIcons(true);
$view->addProperty($edge_name, $edge_list);
}
if ($revisions_commits) {
$view->addProperty(
pht('Commits'),
phutil_implode_html(phutil_tag('br'), $revisions_commits));
}
$field_list->appendFieldsToPropertyList(
$task,
$viewer,
@@ -596,5 +547,291 @@ final class ManiphestTaskDetailController extends ManiphestController {
return $handles->newSublist($phids);
}
private function newChangesView(ManiphestTask $task, array $edges) {
$viewer = $this->getViewer();
$revision_type = ManiphestTaskHasRevisionEdgeType::EDGECONST;
$commit_type = ManiphestTaskHasCommitEdgeType::EDGECONST;
$revision_phids = idx($edges, $revision_type, array());
$revision_phids = array_keys($revision_phids);
$revision_phids = array_fuse($revision_phids);
$commit_phids = idx($edges, $commit_type, array());
$commit_phids = array_keys($commit_phids);
$commit_phids = array_fuse($commit_phids);
if (!$revision_phids && !$commit_phids) {
return null;
}
if ($commit_phids) {
$link_type = DiffusionCommitHasRevisionEdgeType::EDGECONST;
$link_query = id(new PhabricatorEdgeQuery())
->withSourcePHIDs($commit_phids)
->withEdgeTypes(array($link_type));
$link_query->execute();
$commits = id(new DiffusionCommitQuery())
->setViewer($viewer)
->withPHIDs($commit_phids)
->execute();
$commits = mpull($commits, null, 'getPHID');
} else {
$commits = array();
}
if ($revision_phids) {
$revisions = id(new DifferentialRevisionQuery())
->setViewer($viewer)
->withPHIDs($revision_phids)
->execute();
$revisions = mpull($revisions, null, 'getPHID');
} else {
$revisions = array();
}
$handle_phids = array();
$any_linked = false;
$any_status = false;
$idx = 0;
$objects = array();
foreach ($commit_phids as $commit_phid) {
$handle_phids[] = $commit_phid;
$link_phids = $link_query->getDestinationPHIDs(array($commit_phid));
foreach ($link_phids as $link_phid) {
$handle_phids[] = $link_phid;
unset($revision_phids[$link_phid]);
$any_linked = true;
}
$commit = idx($commits, $commit_phid);
if ($commit) {
$repository_phid = $commit->getRepository()->getPHID();
$handle_phids[] = $repository_phid;
} else {
$repository_phid = null;
}
$status_view = null;
if ($commit) {
$status = $commit->getAuditStatusObject();
if (!$status->isNoAudit()) {
$status_view = id(new PHUITagView())
->setType(PHUITagView::TYPE_SHADE)
->setIcon($status->getIcon())
->setColor($status->getColor())
->setName($status->getName());
}
}
$object_link = null;
if ($commit) {
$commit_monogram = $commit->getDisplayName();
$commit_monogram = phutil_tag(
'span',
array(
'class' => 'object-name',
),
$commit_monogram);
$commit_link = javelin_tag(
'a',
array(
'href' => $commit->getURI(),
'sigil' => 'hovercard',
'meta' => array(
'hoverPHID' => $commit->getPHID(),
),
),
$commit->getSummary());
$object_link = array(
$commit_monogram,
' ',
$commit_link,
);
}
$objects[] = array(
'objectPHID' => $commit_phid,
'objectLink' => $object_link,
'repositoryPHID' => $repository_phid,
'revisionPHIDs' => $link_phids,
'status' => $status_view,
'order' => id(new PhutilSortVector())
->addInt($repository_phid ? 1 : 0)
->addString((string)$repository_phid)
->addInt(1)
->addInt($idx++),
);
}
foreach ($revision_phids as $revision_phid) {
$handle_phids[] = $revision_phid;
$revision = idx($revisions, $revision_phid);
if ($revision) {
$repository_phid = $revision->getRepositoryPHID();
$handle_phids[] = $repository_phid;
} else {
$repository_phid = null;
}
if ($revision) {
$icon = $revision->getStatusIcon();
$color = $revision->getStatusIconColor();
$name = $revision->getStatusDisplayName();
$status_view = id(new PHUITagView())
->setType(PHUITagView::TYPE_SHADE)
->setIcon($icon)
->setColor($color)
->setName($name);
} else {
$status_view = null;
}
$object_link = null;
if ($revision) {
$revision_monogram = $revision->getMonogram();
$revision_monogram = phutil_tag(
'span',
array(
'class' => 'object-name',
),
$revision_monogram);
$revision_link = javelin_tag(
'a',
array(
'href' => $revision->getURI(),
'sigil' => 'hovercard',
'meta' => array(
'hoverPHID' => $revision->getPHID(),
),
),
$revision->getTitle());
$object_link = array(
$revision_monogram,
' ',
$revision_link,
);
}
$objects[] = array(
'objectPHID' => $revision_phid,
'objectLink' => $object_link,
'repositoryPHID' => $repository_phid,
'revisionPHIDs' => array(),
'status' => $status_view,
'order' => id(new PhutilSortVector())
->addInt($repository_phid ? 1 : 0)
->addString((string)$repository_phid)
->addInt(0)
->addInt($idx++),
);
}
$handles = $viewer->loadHandles($handle_phids);
$order = ipull($objects, 'order');
$order = msortv($order, 'getSelf');
$objects = array_select_keys($objects, array_keys($order));
$last_repository = false;
$rows = array();
$rowd = array();
foreach ($objects as $object) {
$repository_phid = $object['repositoryPHID'];
if ($repository_phid !== $last_repository) {
$repository_link = null;
if ($repository_phid) {
$repository_handle = $handles[$repository_phid];
$rows[] = array(
$repository_handle->renderLink(),
);
$rowd[] = true;
}
$last_repository = $repository_phid;
}
$object_phid = $object['objectPHID'];
$handle = $handles[$object_phid];
$object_link = $object['objectLink'];
if ($object_link === null) {
$object_link = $handle->renderLink();
}
$object_icon = id(new PHUIIconView())
->setIcon($handle->getIcon());
$status_view = $object['status'];
if ($status_view) {
$any_status = true;
}
$revision_tags = array();
foreach ($object['revisionPHIDs'] as $link_phid) {
$revision_handle = $handles[$link_phid];
$revision_name = $revision_handle->getName();
$revision_tags[] = $revision_handle
->renderHovercardLink($revision_name);
}
$revision_tags = phutil_implode_html(
phutil_tag('br'),
$revision_tags);
$rowd[] = false;
$rows[] = array(
$object_icon,
$status_view,
$revision_tags,
$object_link,
);
}
$changes_table = id(new AphrontTableView($rows))
->setNoDataString(pht('This task has no related commits or revisions.'))
->setRowDividers($rowd)
->setColumnClasses(
array(
'indent center',
null,
null,
'wide pri object-link',
))
->setColumnVisibility(
array(
true,
$any_status,
$any_linked,
true,
))
->setDeviceVisibility(
array(
false,
$any_status,
false,
true,
));
$changes_header = id(new PHUIHeaderView())
->setHeader(pht('Revisions and Commits'));
$changes_view = id(new PHUIObjectBoxView())
->setHeader($changes_header)
->setBackground(PHUIObjectBoxView::BLUE_PROPERTY)
->setTable($changes_table);
return $changes_view;
}
}

View File

@@ -226,7 +226,7 @@ final class ManiphestTransactionEditor
$body->addLinkSection(
pht('TASK DETAIL'),
PhabricatorEnv::getProductionURI('/T'.$object->getID()));
$this->getObjectLinkButtonURIForMail($object));
$board_phids = array();

View File

@@ -39,6 +39,15 @@ final class ManiphestTaskPolicyCodex
$rules = array();
$rules[] = $this->newRule()
->setCapabilities(
array(
PhabricatorPolicyCapability::CAN_VIEW,
PhabricatorPolicyCapability::CAN_EDIT,
))
->setDescription(
pht('The owner of a task can always view and edit it.'));
$rules[] = $this->newRule()
->setCapabilities(
array(

View File

@@ -358,10 +358,6 @@ final class ManiphestTask extends ManiphestDAO
return false;
}
public function describeAutomaticCapability($capability) {
return pht('The owner of a task can always view and edit it.');
}
/* -( PhabricatorTokenReceiverInterface )---------------------------------- */

View File

@@ -91,11 +91,22 @@ final class PhabricatorOwnersPath extends PhabricatorOwnersDAO {
}
private static function getScalarKeyForRef(array $ref) {
// See T13464. When building refs from raw transactions, the path has
// not been normalized yet and doesn't have a separate "display" path.
// If the "display" path isn't populated, just use the actual path to
// build the ref key.
if (isset($ref['display'])) {
$display = $ref['display'];
} else {
$display = $ref['path'];
}
return sprintf(
'repository=%s path=%s display=%s excluded=%d',
$ref['repositoryPHID'],
$ref['path'],
$ref['display'],
$display,
$ref['excluded']);
}

View File

@@ -80,6 +80,7 @@ final class PassphraseCredentialEditController extends PassphraseController {
$validation_exception = null;
$errors = array();
$e_password = null;
$e_secret = null;
if ($request->isFormPost()) {
$v_name = $request->getStr('name');
@@ -97,22 +98,36 @@ final class PassphraseCredentialEditController extends PassphraseController {
$env_secret = new PhutilOpaqueEnvelope($v_secret);
$env_password = new PhutilOpaqueEnvelope($v_password);
if ($type->requiresPassword($env_secret)) {
$has_secret = !preg_match('/^('.$bullet.')+$/', trim($v_decrypt));
// Validate and repair SSH private keys, and apply passwords if they
// are provided. See T13454 for discussion.
// This should eventually be refactored to be modular rather than a
// hard-coded set of behaviors here in the Controller, but this is
// likely a fairly extensive change.
$is_ssh = ($type instanceof PassphraseSSHPrivateKeyTextCredentialType);
if ($is_ssh && $has_secret) {
$old_object = PhabricatorAuthSSHPrivateKey::newFromRawKey($env_secret);
if (strlen($v_password)) {
$v_decrypt = $type->decryptSecret($env_secret, $env_password);
if ($v_decrypt === null) {
$e_password = pht('Incorrect');
$errors[] = pht(
'This key requires a password, but the password you provided '.
'is incorrect.');
} else {
$v_decrypt = $v_decrypt->openEnvelope();
$old_object->setPassphrase($env_password);
}
try {
$new_object = $old_object->newBarePrivateKey();
$v_decrypt = $new_object->getKeyBody()->openEnvelope();
} catch (PhabricatorAuthSSHPrivateKeyException $ex) {
$errors[] = $ex->getMessage();
if ($ex->isFormatException()) {
$e_secret = pht('Invalid');
}
if ($ex->isPassphraseException()) {
$e_password = pht('Invalid');
}
} else {
$e_password = pht('Required');
$errors[] = pht(
'This key requires a password. You must provide the password '.
'for the key.');
}
}
@@ -166,13 +181,14 @@ final class PassphraseCredentialEditController extends PassphraseController {
->setTransactionType($type_username)
->setNewValue($v_username);
}
// If some value other than a sequence of bullets was provided for
// the credential, update it. In particular, note that we are
// explicitly allowing empty secrets: one use case is HTTP auth where
// the username is a secret token which covers both identity and
// authentication.
if (!preg_match('/^('.$bullet.')+$/', trim($v_decrypt))) {
if ($has_secret) {
// If the credential was previously destroyed, restore it when it is
// edited if a secret is provided.
$xactions[] = id(new PassphraseCredentialTransaction())
@@ -182,6 +198,7 @@ final class PassphraseCredentialEditController extends PassphraseController {
$new_secret = id(new PassphraseSecret())
->setSecretData($v_decrypt)
->save();
$xactions[] = id(new PassphraseCredentialTransaction())
->setTransactionType($type_secret_id)
->setNewValue($new_secret->getID());
@@ -287,7 +304,8 @@ final class PassphraseCredentialEditController extends PassphraseController {
->setName('secret')
->setLabel($type->getSecretLabel())
->setDisabled($credential_is_locked)
->setValue($v_secret));
->setValue($v_secret)
->setError($e_secret));
if ($type->shouldShowPasswordField()) {
$form->appendChild(

View File

@@ -102,35 +102,6 @@ abstract class PassphraseCredentialType extends Phobject {
return pht('Password');
}
/**
* Return true if the provided credential requires a password to decrypt.
*
* @param PhutilOpaqueEnvelope Credential secret value.
* @return bool True if the credential needs a password.
*
* @task password
*/
public function requiresPassword(PhutilOpaqueEnvelope $secret) {
return false;
}
/**
* Return the decrypted credential secret, or `null` if the password does
* not decrypt the credential.
*
* @param PhutilOpaqueEnvelope Credential secret value.
* @param PhutilOpaqueEnvelope Credential password.
* @return
* @task password
*/
public function decryptSecret(
PhutilOpaqueEnvelope $secret,
PhutilOpaqueEnvelope $password) {
return $secret;
}
public function shouldRequireUsername() {
return true;
}

View File

@@ -29,40 +29,4 @@ final class PassphraseSSHPrivateKeyTextCredentialType
return pht('Password for Key');
}
public function requiresPassword(PhutilOpaqueEnvelope $secret) {
// According to the internet, this is the canonical test for an SSH private
// key with a password.
return preg_match('/ENCRYPTED/', $secret->openEnvelope());
}
public function decryptSecret(
PhutilOpaqueEnvelope $secret,
PhutilOpaqueEnvelope $password) {
$tmp = new TempFile();
Filesystem::writeFile($tmp, $secret->openEnvelope());
if (!Filesystem::binaryExists('ssh-keygen')) {
throw new Exception(
pht(
'Decrypting SSH keys requires the `%s` binary, but it '.
'is not available in %s. Either make it available or strip the '.
'password from this SSH key manually before uploading it.',
'ssh-keygen',
'$PATH'));
}
list($err, $stdout, $stderr) = exec_manual(
'ssh-keygen -p -P %P -N %s -f %s',
$password,
'',
(string)$tmp);
if ($err) {
return null;
} else {
return new PhutilOpaqueEnvelope(Filesystem::readFile($tmp));
}
}
}

View File

@@ -89,6 +89,9 @@ final class PhabricatorUserEditor extends PhabricatorEditor {
$this->didVerifyEmail($user, $email);
}
id(new DiffusionRepositoryIdentityEngine())
->didUpdateEmailAddress($email->getAddress());
return $this;
}
@@ -202,11 +205,8 @@ final class PhabricatorUserEditor extends PhabricatorEditor {
$user->endWriteLocking();
$user->saveTransaction();
// Try and match this new address against unclaimed `RepositoryIdentity`s
PhabricatorWorker::scheduleTask(
'PhabricatorRepositoryIdentityChangeWorker',
array('userPHID' => $user->getPHID()),
array('objectPHID' => $user->getPHID()));
id(new DiffusionRepositoryIdentityEngine())
->didUpdateEmailAddress($email->getAddress());
return $this;
}
@@ -241,7 +241,9 @@ final class PhabricatorUserEditor extends PhabricatorEditor {
throw new Exception(pht('Email not owned by user!'));
}
$email->delete();
$destruction_engine = id(new PhabricatorDestructionEngine())
->setWaitToFinalizeDestruction(true)
->destroyObject($email);
$log = PhabricatorUserLog::initializeNewLog(
$actor,
@@ -254,6 +256,7 @@ final class PhabricatorUserEditor extends PhabricatorEditor {
$user->saveTransaction();
$this->revokePasswordResetLinks($user);
$destruction_engine->finalizeDestruction();
return $this;
}
@@ -326,7 +329,6 @@ final class PhabricatorUserEditor extends PhabricatorEditor {
}
$email->sendNewPrimaryEmail($user);
$this->revokePasswordResetLinks($user);
return $this;
@@ -440,6 +442,9 @@ final class PhabricatorUserEditor extends PhabricatorEditor {
$user->endWriteLocking();
$user->saveTransaction();
id(new DiffusionRepositoryIdentityEngine())
->didUpdateEmailAddress($email->getAddress());
}

View File

@@ -0,0 +1,41 @@
<?php
final class PhabricatorPeopleUserEmailPHIDType
extends PhabricatorPHIDType {
const TYPECONST = 'EADR';
public function getTypeName() {
return pht('User Email');
}
public function newObject() {
return new PhabricatorUserEmail();
}
public function getPHIDTypeApplicationClass() {
return 'PhabricatorPeopleApplication';
}
protected function buildQueryForObjects(
PhabricatorObjectQuery $query,
array $phids) {
return id(new PhabricatorPeopleUserEmailQuery())
->withPHIDs($phids);
}
public function loadHandles(
PhabricatorHandleQuery $query,
array $handles,
array $objects) {
foreach ($handles as $phid => $handle) {
$email = $objects[$phid];
$handle->setName($email->getAddress());
}
return null;
}
}

View File

@@ -0,0 +1,81 @@
<?php
final class PhabricatorPeopleUserEmailQuery
extends PhabricatorCursorPagedPolicyAwareQuery {
private $ids;
private $phids;
public function withIDs(array $ids) {
$this->ids = $ids;
return $this;
}
public function withPHIDs(array $phids) {
$this->phids = $phids;
return $this;
}
public function newResultObject() {
return new PhabricatorUserEmail();
}
protected function loadPage() {
return $this->loadStandardPage($this->newResultObject());
}
protected function getPrimaryTableAlias() {
return 'email';
}
protected function buildWhereClauseParts(AphrontDatabaseConnection $conn) {
$where = parent::buildWhereClauseParts($conn);
if ($this->ids !== null) {
$where[] = qsprintf(
$conn,
'email.id IN (%Ld)',
$this->ids);
}
if ($this->phids !== null) {
$where[] = qsprintf(
$conn,
'email.phid IN (%Ls)',
$this->phids);
}
return $where;
}
protected function willLoadPage(array $page) {
$user_phids = mpull($page, 'getUserPHID');
$users = id(new PhabricatorPeopleQuery())
->setViewer($this->getViewer())
->setParentQuery($this)
->withPHIDs($user_phids)
->execute();
$users = mpull($users, null, 'getPHID');
foreach ($page as $key => $address) {
$user = idx($users, $address->getUserPHID());
if (!$user) {
unset($page[$key]);
$this->didRejectResult($address);
continue;
}
$address->attachUser($user);
}
return $page;
}
public function getQueryApplicationClass() {
return 'PhabricatorPeopleApplication';
}
}

View File

@@ -1222,7 +1222,7 @@ final class PhabricatorUser
'userPHID = %s',
$this->getPHID());
foreach ($emails as $email) {
$email->delete();
$engine->destroyObject($email);
}
$sessions = id(new PhabricatorAuthSession())->loadAllWhere(

View File

@@ -4,7 +4,11 @@
* @task restrictions Domain Restrictions
* @task email Email About Email
*/
final class PhabricatorUserEmail extends PhabricatorUserDAO {
final class PhabricatorUserEmail
extends PhabricatorUserDAO
implements
PhabricatorDestructibleInterface,
PhabricatorPolicyInterface {
protected $userPHID;
protected $address;
@@ -12,10 +16,13 @@ final class PhabricatorUserEmail extends PhabricatorUserDAO {
protected $isPrimary;
protected $verificationCode;
private $user = self::ATTACHABLE;
const MAX_ADDRESS_LENGTH = 128;
protected function getConfiguration() {
return array(
self::CONFIG_AUX_PHID => true,
self::CONFIG_COLUMN_SCHEMA => array(
'address' => 'sort128',
'isVerified' => 'bool',
@@ -34,6 +41,10 @@ final class PhabricatorUserEmail extends PhabricatorUserDAO {
) + parent::getConfiguration();
}
public function getPHIDType() {
return PhabricatorPeopleUserEmailPHIDType::TYPECONST;
}
public function getVerificationURI() {
return '/emailverify/'.$this->getVerificationCode().'/';
}
@@ -45,6 +56,15 @@ final class PhabricatorUserEmail extends PhabricatorUserDAO {
return parent::save();
}
public function attachUser(PhabricatorUser $user) {
$this->user = $user;
return $this;
}
public function getUser() {
return $this->assertAttached($this->user);
}
/* -( Domain Restrictions )------------------------------------------------ */
@@ -271,4 +291,37 @@ final class PhabricatorUserEmail extends PhabricatorUserDAO {
return $this;
}
/* -( PhabricatorDestructibleInterface )----------------------------------- */
public function destroyObjectPermanently(
PhabricatorDestructionEngine $engine) {
$this->delete();
}
/* -( PhabricatorPolicyInterface )----------------------------------------- */
public function getCapabilities() {
return array(
PhabricatorPolicyCapability::CAN_VIEW,
PhabricatorPolicyCapability::CAN_EDIT,
);
}
public function getPolicy($capability) {
$user = $this->getUser();
if ($this->getIsSystemAgent() || $this->getIsMailingList()) {
return PhabricatorPolicies::POLICY_ADMIN;
}
return $user->getPHID();
}
public function hasAutomaticCapability($capability, PhabricatorUser $viewer) {
return false;
}
}

View File

@@ -38,7 +38,7 @@ final class PhabricatorUserCardView extends AphrontTagView {
}
return array(
'class' => implode($classes, ' '),
'class' => implode(' ', $classes),
);
}

View File

@@ -71,21 +71,30 @@ final class PhabricatorUserUsernameTransaction
}
if (!strlen($new)) {
$errors[] = $this->newRequiredError(
pht('New username is required.'), $xaction);
$errors[] = $this->newInvalidError(
pht('New username is required.'),
$xaction);
} else if (!PhabricatorUser::validateUsername($new)) {
$errors[] = $this->newInvalidError(
PhabricatorUser::describeValidUsername(), $xaction);
PhabricatorUser::describeValidUsername(),
$xaction);
}
$user = id(new PhabricatorPeopleQuery())
->setViewer(PhabricatorUser::getOmnipotentUser())
->withUsernames(array($new))
->executeOne();
if ($user) {
$errors[] = $this->newInvalidError(
pht('Another user already has that username.'), $xaction);
// See T13446. We may be changing the letter case of a username, which
// is a perfectly fine edit.
$is_self = ($user->getPHID() === $object->getPHID());
if (!$is_self) {
$errors[] = $this->newInvalidError(
pht(
'Another user already has the username "%s".',
$new),
$xaction);
}
}
}

View File

@@ -33,10 +33,6 @@ final class PhabricatorObjectHandle
private $commandLineObjectName;
private $mailStampName;
private $stateIcon;
private $stateColor;
private $stateName;
public function setIcon($icon) {
$this->icon = $icon;
return $this;
@@ -299,55 +295,6 @@ final class PhabricatorObjectHandle
return $this->complete;
}
public function setStateIcon($state_icon) {
$this->stateIcon = $state_icon;
return $this;
}
public function getStateIcon() {
return $this->stateIcon;
}
public function setStateColor($state_color) {
$this->stateColor = $state_color;
return $this;
}
public function getStateColor() {
return $this->stateColor;
}
public function setStateName($state_name) {
$this->stateName = $state_name;
return $this;
}
public function getStateName() {
return $this->stateName;
}
public function renderStateIcon() {
$icon = $this->getStateIcon();
if ($icon === null) {
$icon = 'fa-question-circle-o';
}
$color = $this->getStateColor();
$name = $this->getStateName();
if ($name === null) {
$name = pht('Unknown');
}
return id(new PHUIIconView())
->setIcon($icon, $color)
->addSigil('has-tooltip')
->setMetadata(
array(
'tip' => $name,
));
}
public function renderLink($name = null) {
return $this->renderLinkWithAttributes($name, array());
}

View File

@@ -13,7 +13,6 @@ final class PHUIHandleListView
private $handleList;
private $asInline;
private $asText;
private $showStateIcons;
private $glyphLimit;
public function setHandleList(PhabricatorHandleList $list) {
@@ -39,15 +38,6 @@ final class PHUIHandleListView
return $this->asText;
}
public function setShowStateIcons($show_state_icons) {
$this->showStateIcons = $show_state_icons;
return $this;
}
public function getShowStateIcons() {
return $this->showStateIcons;
}
public function setGlyphLimit($glyph_limit) {
$this->glyphLimit = $glyph_limit;
return $this;
@@ -70,7 +60,6 @@ final class PHUIHandleListView
protected function getTagContent() {
$list = $this->handleList;
$show_state_icons = $this->getShowStateIcons();
$glyph_limit = $this->getGlyphLimit();
$items = array();
@@ -79,10 +68,6 @@ final class PHUIHandleListView
->setShowHovercard(true)
->setAsText($this->getAsText());
if ($show_state_icons) {
$view->setShowStateIcon(true);
}
if ($glyph_limit) {
$view->setGlyphLimit($glyph_limit);
}

View File

@@ -17,7 +17,6 @@ final class PHUIHandleView
private $asText;
private $useShortName;
private $showHovercard;
private $showStateIcon;
private $glyphLimit;
public function setHandleList(PhabricatorHandleList $list) {
@@ -50,15 +49,6 @@ final class PHUIHandleView
return $this;
}
public function setShowStateIcon($show_state_icon) {
$this->showStateIcon = $show_state_icon;
return $this;
}
public function getShowStateIcon() {
return $this->showStateIcon;
}
public function setGlyphLimit($glyph_limit) {
$this->glyphLimit = $glyph_limit;
return $this;
@@ -104,11 +94,6 @@ final class PHUIHandleView
$link = $handle->renderLink($name);
}
if ($this->showStateIcon) {
$icon = $handle->renderStateIcon();
$link = array($icon, ' ', $link);
}
return $link;
}

View File

@@ -41,23 +41,6 @@ final class PhrictionDocumentPolicyCodex
->executeOne();
}
public function compareToDefaultPolicy(PhabricatorPolicy $policy) {
$root_policy = $this->getDefaultPolicy();
$strongest_policy = $this->getStrongestPolicy();
// Note that we never return 'weaker', because Phriction documents can
// never have weaker permissions than their parents. If this object has
// been set to weaker permissions anyway, return 'adjusted'.
if ($root_policy == $strongest_policy) {
$strength = null;
} else if ($strongest_policy->isStrongerThan($root_policy)) {
$strength = PhabricatorPolicyStrengthConstants::STRONGER;
} else {
$strength = PhabricatorPolicyStrengthConstants::ADJUSTED;
}
return $strength;
}
private function getStrongestPolicy() {
$ancestors = $this->getObject()->getAncestors();
$ancestors[] = $this->getObject();

View File

@@ -16,8 +16,23 @@ final class PhrictionRemarkupRule extends PhutilRemarkupRule {
}
public function markupDocumentLink(array $matches) {
$name = trim(idx($matches, 2, ''));
if (empty($matches[2])) {
$name = null;
}
$path = trim($matches[1]);
if (!$this->isFlatText($name)) {
return $matches[0];
}
if (!$this->isFlatText($path)) {
return $matches[0];
}
// If the link contains an anchor, separate that off first.
$parts = explode('#', trim($matches[1]), 2);
$parts = explode('#', $path, 2);
if (count($parts) == 2) {
$link = $parts[0];
$anchor = $parts[1];
@@ -48,11 +63,6 @@ final class PhrictionRemarkupRule extends PhutilRemarkupRule {
}
}
$name = trim(idx($matches, 2, ''));
if (empty($matches[2])) {
$name = null;
}
// Link is now used for slug detection, so append a slash if one
// is needed.
$link = rtrim($link, '/').'/';

View File

@@ -40,10 +40,6 @@ abstract class PhabricatorPolicyCodex
$this->capability);
}
public function compareToDefaultPolicy(PhabricatorPolicy $policy) {
return null;
}
final protected function newRule() {
return new PhabricatorPolicyCodexRuleDescription();
}

View File

@@ -1,9 +0,0 @@
<?php
final class PhabricatorPolicyStrengthConstants
extends PhabricatorPolicyConstants {
const WEAKER = 'weaker';
const STRONGER = 'stronger';
const ADJUSTED = 'adjusted';
}

View File

@@ -163,69 +163,6 @@ final class PhabricatorPolicyExplainController
return $space_section;
}
private function getStrengthInformation(
PhabricatorPolicyInterface $object,
PhabricatorPolicy $policy,
$capability) {
$viewer = $this->getViewer();
$strength = null;
if ($object instanceof PhabricatorPolicyCodexInterface) {
$codex = id(PhabricatorPolicyCodex::newFromObject($object, $viewer))
->setCapability($capability);
$strength = $codex->compareToDefaultPolicy($policy);
$default_policy = $codex->getDefaultPolicy();
} else {
$default_policy = PhabricatorPolicyQuery::getDefaultPolicyForObject(
$viewer,
$object,
$capability);
if ($default_policy) {
if ($default_policy->getPHID() == $policy->getPHID()) {
return;
}
if ($default_policy->getPHID() != $policy->getPHID()) {
if ($default_policy->isStrongerThan($policy)) {
$strength = PhabricatorPolicyStrengthConstants::WEAKER;
} else if ($policy->isStrongerThan($default_policy)) {
$strength = PhabricatorPolicyStrengthConstants::STRONGER;
} else {
$strength = PhabricatorPolicyStrengthConstants::ADJUSTED;
}
}
}
}
if (!$strength) {
return;
}
if ($strength == PhabricatorPolicyStrengthConstants::WEAKER) {
$info = pht(
'This object has a less restrictive policy ("%s") than the default '.
'policy for similar objects (which is "%s").',
$policy->getShortName(),
$default_policy->getShortName());
} else if ($strength == PhabricatorPolicyStrengthConstants::STRONGER) {
$info = pht(
'This object has a more restrictive policy ("%s") than the default '.
'policy for similar objects (which is "%s").',
$policy->getShortName(),
$default_policy->getShortName());
} else {
$info = pht(
'This object has a different policy ("%s") than the default policy '.
'for similar objects (which is "%s").',
$policy->getShortName(),
$default_policy->getShortName());
}
return $info;
}
private function getCapabilityName($capability) {
$capability_name = $capability;
$capobj = PhabricatorPolicyCapability::getCapabilityByKey($capability);
@@ -344,11 +281,6 @@ final class PhabricatorPolicyExplainController
$object_section->appendRulesView($rules_view);
}
$strength = $this->getStrengthInformation($object, $policy, $capability);
if ($strength) {
$object_section->appendHint($strength);
}
return $object_section;
}

View File

@@ -1537,8 +1537,7 @@ final class PhabricatorProjectCoreTestCase extends PhabricatorTestCase {
PhabricatorProject $parent = null,
$is_milestone = false) {
$project = PhabricatorProject::initializeNewProject($user);
$project = PhabricatorProject::initializeNewProject($user, $parent);
$name = pht('Test Project %d', mt_rand());

View File

@@ -53,7 +53,11 @@ final class PhabricatorProjectBurndownChartEngine
$open_function = $this->newFunction(
array(
'accumulate',
array('fact', 'tasks.open-count.create'),
array(
'sum',
array('fact', 'tasks.open-count.create'),
array('fact', 'tasks.open-count.status'),
),
));
$closed_function = $this->newFunction(

View File

@@ -63,14 +63,14 @@ final class PhabricatorProjectProfileController
$member_list = id(new PhabricatorProjectMemberListView())
->setUser($viewer)
->setProject($project)
->setLimit(5)
->setLimit(10)
->setBackground(PHUIObjectBoxView::BLUE_PROPERTY)
->setUserPHIDs($project->getMemberPHIDs());
$watcher_list = id(new PhabricatorProjectWatcherListView())
->setUser($viewer)
->setProject($project)
->setLimit(5)
->setLimit(10)
->setBackground(PHUIObjectBoxView::BLUE_PROPERTY)
->setUserPHIDs($project->getWatcherPHIDs());

View File

@@ -336,40 +336,68 @@ final class PhabricatorProjectTransactionEditor
$type_edge = PhabricatorTransactions::TYPE_EDGE;
$edgetype_member = PhabricatorProjectProjectHasMemberEdgeType::EDGECONST;
$member_xaction = null;
foreach ($xactions as $xaction) {
if ($xaction->getTransactionType() !== $type_edge) {
continue;
}
$edgetype = $xaction->getMetadataValue('edge:type');
if ($edgetype !== $edgetype_member) {
continue;
}
$member_xaction = $xaction;
// See T13462. If we're creating a milestone, set a dummy milestone
// number so the project behaves like a milestone and uses milestone
// policy rules. Otherwise, we'll end up checking the default policies
// (which are not relevant to milestones) instead of the parent project
// policies (which are the correct policies).
if ($this->getIsMilestone() && !$copy->isMilestone()) {
$copy->setMilestoneNumber(1);
}
if ($member_xaction) {
$object_phid = $object->getPHID();
$hint = null;
if ($this->getIsMilestone()) {
// See T13462. If we're creating a milestone, predict that the members
// of the newly created milestone will be the same as the members of the
// parent project, since this is the governing rule.
if ($object_phid) {
$project = id(new PhabricatorProjectQuery())
->setViewer($this->getActor())
->withPHIDs(array($object_phid))
->needMembers(true)
->executeOne();
$members = $project->getMemberPHIDs();
} else {
$members = array();
$parent = $copy->getParentProject();
$parent = id(new PhabricatorProjectQuery())
->setViewer($this->getActor())
->withPHIDs(array($parent->getPHID()))
->needMembers(true)
->executeOne();
$members = $parent->getMemberPHIDs();
$hint = array_fuse($members);
} else {
$member_xaction = null;
foreach ($xactions as $xaction) {
if ($xaction->getTransactionType() !== $type_edge) {
continue;
}
$edgetype = $xaction->getMetadataValue('edge:type');
if ($edgetype !== $edgetype_member) {
continue;
}
$member_xaction = $xaction;
}
$clone_xaction = clone $member_xaction;
$hint = $this->getPHIDTransactionNewValue($clone_xaction, $members);
if ($member_xaction) {
$object_phid = $object->getPHID();
if ($object_phid) {
$project = id(new PhabricatorProjectQuery())
->setViewer($this->getActor())
->withPHIDs(array($object_phid))
->needMembers(true)
->executeOne();
$members = $project->getMemberPHIDs();
} else {
$members = array();
}
$clone_xaction = clone $member_xaction;
$hint = $this->getPHIDTransactionNewValue($clone_xaction, $members);
$hint = array_fuse($hint);
}
}
if ($hint !== null) {
$rule = new PhabricatorProjectMembersPolicyRule();
$hint = array_fuse($hint);
PhabricatorPolicyRule::passTransactionHintToRule(
$copy,
$rule,

View File

@@ -301,6 +301,12 @@ final class PhabricatorBoardLayoutEngine extends Phobject {
->execute();
$boards = mpull($boards, null, 'getPHID');
foreach ($boards as $key => $board) {
if (!($board instanceof PhabricatorWorkboardInterface)) {
unset($boards[$key]);
}
}
if (!$this->fetchAllBoards) {
foreach ($boards as $key => $board) {
if (!$board->getHasWorkboard()) {

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