Merge branch 'master' into blender-tweaks
This commit is contained in:
@@ -1 +0,0 @@
|
||||
../scripts/ssh/ssh-auth-key.php
|
@@ -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',
|
||||
|
@@ -0,0 +1,4 @@
|
||||
<?php
|
||||
|
||||
PhabricatorRebuildIndexesWorker::rebuildObjectsWithQuery(
|
||||
'PhabricatorRepositoryQuery');
|
2
resources/sql/autopatches/20191113.identity.01.email.sql
Normal file
2
resources/sql/autopatches/20191113.identity.01.email.sql
Normal file
@@ -0,0 +1,2 @@
|
||||
ALTER TABLE {$NAMESPACE}_repository.repository_identity
|
||||
ADD emailAddress VARCHAR(255) COLLATE {$COLLATE_SORT};
|
26
resources/sql/autopatches/20191113.identity.02.populate.php
Normal file
26
resources/sql/autopatches/20191113.identity.02.populate.php
Normal 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;
|
||||
}
|
||||
}
|
@@ -0,0 +1,3 @@
|
||||
UPDATE {$NAMESPACE}_repository.repository_identity
|
||||
SET currentEffectiveUserPHID = NULL
|
||||
WHERE currentEffectiveUserPHID = 'unassigned()';
|
2
resources/sql/autopatches/20191114.email.01.phid.sql
Normal file
2
resources/sql/autopatches/20191114.email.01.phid.sql
Normal file
@@ -0,0 +1,2 @@
|
||||
ALTER TABLE {$NAMESPACE}_user.user_email
|
||||
ADD phid VARBINARY(64) NOT NULL;
|
18
resources/sql/autopatches/20191114.email.02.populate.php
Normal file
18
resources/sql/autopatches/20191114.email.02.populate.php
Normal 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']);
|
||||
}
|
||||
}
|
@@ -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);
|
@@ -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');
|
||||
|
||||
|
@@ -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',
|
||||
|
@@ -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) {
|
||||
|
@@ -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() {
|
||||
|
@@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
|
@@ -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);
|
||||
}
|
||||
|
@@ -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');
|
||||
|
||||
|
@@ -0,0 +1,9 @@
|
||||
<?php
|
||||
|
||||
abstract class PhabricatorAuthSSHPrivateKeyException
|
||||
extends Exception {
|
||||
|
||||
abstract public function isFormatException();
|
||||
abstract public function isPassphraseException();
|
||||
|
||||
}
|
@@ -0,0 +1,14 @@
|
||||
<?php
|
||||
|
||||
final class PhabricatorAuthSSHPrivateKeyFormatException
|
||||
extends PhabricatorAuthSSHPrivateKeyException {
|
||||
|
||||
public function isFormatException() {
|
||||
return true;
|
||||
}
|
||||
|
||||
public function isPassphraseException() {
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
@@ -0,0 +1,4 @@
|
||||
<?php
|
||||
|
||||
final class PhabricatorAuthSSHPrivateKeyIncorrectPassphraseException
|
||||
extends PhabricatorAuthSSHPrivateKeyPassphraseException {}
|
@@ -0,0 +1,4 @@
|
||||
<?php
|
||||
|
||||
final class PhabricatorAuthSSHPrivateKeyMissingPassphraseException
|
||||
extends PhabricatorAuthSSHPrivateKeyPassphraseException {}
|
@@ -0,0 +1,14 @@
|
||||
<?php
|
||||
|
||||
abstract class PhabricatorAuthSSHPrivateKeyPassphraseException
|
||||
extends PhabricatorAuthSSHPrivateKeyException {
|
||||
|
||||
final public function isFormatException() {
|
||||
return false;
|
||||
}
|
||||
|
||||
final public function isPassphraseException() {
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
@@ -0,0 +1,4 @@
|
||||
<?php
|
||||
|
||||
final class PhabricatorAuthSSHPrivateKeySurplusPassphraseException
|
||||
extends PhabricatorAuthSSHPrivateKeyPassphraseException {}
|
@@ -0,0 +1,14 @@
|
||||
<?php
|
||||
|
||||
final class PhabricatorAuthSSHPrivateKeyUnknownException
|
||||
extends PhabricatorAuthSSHPrivateKeyException {
|
||||
|
||||
public function isFormatException() {
|
||||
return true;
|
||||
}
|
||||
|
||||
public function isPassphraseException() {
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
210
src/applications/auth/sshkey/PhabricatorAuthSSHPrivateKey.php
Normal file
210
src/applications/auth/sshkey/PhabricatorAuthSSHPrivateKey.php
Normal 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;
|
||||
}
|
||||
|
||||
}
|
@@ -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'
|
||||
|
@@ -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 '.
|
||||
|
@@ -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);
|
||||
|
||||
}
|
||||
|
@@ -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);
|
||||
}
|
||||
|
||||
|
@@ -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);
|
||||
}
|
||||
|
||||
|
@@ -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);
|
||||
}
|
||||
|
||||
|
@@ -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);
|
||||
}
|
||||
|
||||
|
@@ -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);
|
||||
}
|
||||
|
||||
|
@@ -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);
|
||||
}
|
||||
|
||||
|
@@ -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()));
|
||||
|
@@ -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);
|
||||
}
|
||||
|
||||
|
@@ -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);
|
||||
}
|
||||
|
||||
|
@@ -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);
|
||||
}
|
||||
|
||||
|
@@ -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);
|
||||
}
|
||||
|
||||
|
@@ -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);
|
||||
}
|
||||
|
||||
|
@@ -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);
|
||||
}
|
||||
|
||||
|
@@ -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);
|
||||
}
|
||||
|
||||
|
@@ -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);
|
||||
}
|
||||
|
||||
|
@@ -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);
|
||||
}
|
||||
|
||||
|
@@ -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) {
|
||||
|
@@ -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) {
|
||||
|
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
|
@@ -103,7 +103,7 @@ final class PhabricatorDifferentialRevisionTestDataGenerator
|
||||
$newcode2[] = $altcodearr[$randomlines_new[$c++]];
|
||||
}
|
||||
}
|
||||
return implode($newcode2, "\n");
|
||||
return implode("\n", $newcode2);
|
||||
}
|
||||
|
||||
}
|
||||
|
@@ -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)) {
|
||||
|
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -22,6 +22,10 @@ final class DifferentialChangesetSearchEngine
|
||||
return 'PhabricatorDifferentialApplication';
|
||||
}
|
||||
|
||||
public function canUseInPanelContext() {
|
||||
return false;
|
||||
}
|
||||
|
||||
public function newQuery() {
|
||||
$query = id(new DifferentialChangesetQuery());
|
||||
|
||||
|
@@ -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';
|
||||
|
||||
|
@@ -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);
|
||||
}
|
||||
|
@@ -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)
|
||||
|
@@ -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();
|
||||
|
@@ -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,
|
||||
);
|
||||
}
|
||||
|
||||
}
|
@@ -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,
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@@ -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),
|
||||
));
|
||||
}
|
||||
|
||||
}
|
@@ -23,6 +23,7 @@ final class DiffusionRepositoryBranchesManagementPanel
|
||||
|
||||
$has_any =
|
||||
$repository->getDetail('default-branch') ||
|
||||
$repository->getFetchRules() ||
|
||||
$repository->getTrackOnlyRules() ||
|
||||
$repository->getPermanentRefRules();
|
||||
|
||||
|
@@ -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);
|
||||
}
|
||||
|
||||
|
@@ -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;
|
||||
}
|
||||
|
||||
|
@@ -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);
|
||||
}
|
||||
|
||||
|
@@ -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);
|
||||
|
@@ -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();
|
||||
|
@@ -29,6 +29,7 @@ final class PhabricatorFactChartFunction
|
||||
$key_id = id(new PhabricatorFactKeyDimension())
|
||||
->newDimensionID($fact->getKey());
|
||||
if (!$key_id) {
|
||||
$this->map = array();
|
||||
return;
|
||||
}
|
||||
|
||||
|
@@ -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(
|
||||
|
@@ -32,8 +32,8 @@ abstract class PhabricatorDocumentEngine
|
||||
}
|
||||
|
||||
public function canDiffDocuments(
|
||||
PhabricatorDocumentRef $uref,
|
||||
PhabricatorDocumentRef $vref) {
|
||||
PhabricatorDocumentRef $uref = null,
|
||||
PhabricatorDocumentRef $vref = null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@@ -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)
|
||||
|
@@ -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);
|
||||
|
@@ -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.');
|
||||
}
|
||||
|
||||
}
|
@@ -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.'));
|
||||
}
|
||||
|
||||
}
|
@@ -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();
|
||||
}
|
||||
}
|
||||
|
@@ -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'),
|
||||
|
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
@@ -226,7 +226,7 @@ final class ManiphestTransactionEditor
|
||||
|
||||
$body->addLinkSection(
|
||||
pht('TASK DETAIL'),
|
||||
PhabricatorEnv::getProductionURI('/T'.$object->getID()));
|
||||
$this->getObjectLinkButtonURIForMail($object));
|
||||
|
||||
|
||||
$board_phids = array();
|
||||
|
@@ -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(
|
||||
|
@@ -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 )---------------------------------- */
|
||||
|
||||
|
@@ -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']);
|
||||
}
|
||||
|
||||
|
@@ -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(
|
||||
|
@@ -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;
|
||||
}
|
||||
|
@@ -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));
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
@@ -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());
|
||||
}
|
||||
|
||||
|
||||
|
@@ -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;
|
||||
}
|
||||
|
||||
}
|
@@ -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';
|
||||
}
|
||||
|
||||
}
|
@@ -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(
|
||||
|
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
|
@@ -38,7 +38,7 @@ final class PhabricatorUserCardView extends AphrontTagView {
|
||||
}
|
||||
|
||||
return array(
|
||||
'class' => implode($classes, ' '),
|
||||
'class' => implode(' ', $classes),
|
||||
);
|
||||
}
|
||||
|
||||
|
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
@@ -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());
|
||||
}
|
||||
|
@@ -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);
|
||||
}
|
||||
|
@@ -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;
|
||||
}
|
||||
|
||||
|
@@ -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();
|
||||
|
@@ -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, '/').'/';
|
||||
|
@@ -40,10 +40,6 @@ abstract class PhabricatorPolicyCodex
|
||||
$this->capability);
|
||||
}
|
||||
|
||||
public function compareToDefaultPolicy(PhabricatorPolicy $policy) {
|
||||
return null;
|
||||
}
|
||||
|
||||
final protected function newRule() {
|
||||
return new PhabricatorPolicyCodexRuleDescription();
|
||||
}
|
||||
|
@@ -1,9 +0,0 @@
|
||||
<?php
|
||||
|
||||
final class PhabricatorPolicyStrengthConstants
|
||||
extends PhabricatorPolicyConstants {
|
||||
|
||||
const WEAKER = 'weaker';
|
||||
const STRONGER = 'stronger';
|
||||
const ADJUSTED = 'adjusted';
|
||||
}
|
@@ -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;
|
||||
}
|
||||
|
||||
|
@@ -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());
|
||||
|
||||
|
@@ -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(
|
||||
|
@@ -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());
|
||||
|
||||
|
@@ -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,
|
||||
|
@@ -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
Reference in New Issue
Block a user