mirror of
https://we.phorge.it/source/phorge.git
synced 2025-02-03 18:38:27 +01:00
(stable) Promote 2018 Week 45
This commit is contained in:
commit
de56ef9466
43 changed files with 1050 additions and 235 deletions
|
@ -422,7 +422,6 @@ return array(
|
||||||
'rsrc/js/application/repository/repository-crossreference.js' => '9a860428',
|
'rsrc/js/application/repository/repository-crossreference.js' => '9a860428',
|
||||||
'rsrc/js/application/search/behavior-reorder-profile-menu-items.js' => 'e2e0a072',
|
'rsrc/js/application/search/behavior-reorder-profile-menu-items.js' => 'e2e0a072',
|
||||||
'rsrc/js/application/search/behavior-reorder-queries.js' => 'e9581f08',
|
'rsrc/js/application/search/behavior-reorder-queries.js' => 'e9581f08',
|
||||||
'rsrc/js/application/slowvote/behavior-slowvote-embed.js' => '887ad43f',
|
|
||||||
'rsrc/js/application/transactions/behavior-comment-actions.js' => '038bf27f',
|
'rsrc/js/application/transactions/behavior-comment-actions.js' => '038bf27f',
|
||||||
'rsrc/js/application/transactions/behavior-reorder-configs.js' => 'd7a74243',
|
'rsrc/js/application/transactions/behavior-reorder-configs.js' => 'd7a74243',
|
||||||
'rsrc/js/application/transactions/behavior-reorder-fields.js' => 'b59e1e96',
|
'rsrc/js/application/transactions/behavior-reorder-fields.js' => 'b59e1e96',
|
||||||
|
@ -674,7 +673,6 @@ return array(
|
||||||
'javelin-behavior-select-content' => 'bf5374ef',
|
'javelin-behavior-select-content' => 'bf5374ef',
|
||||||
'javelin-behavior-select-on-click' => '4e3e79a6',
|
'javelin-behavior-select-on-click' => '4e3e79a6',
|
||||||
'javelin-behavior-setup-check-https' => '491416b3',
|
'javelin-behavior-setup-check-https' => '491416b3',
|
||||||
'javelin-behavior-slowvote-embed' => '887ad43f',
|
|
||||||
'javelin-behavior-stripe-payment-form' => 'a6b98425',
|
'javelin-behavior-stripe-payment-form' => 'a6b98425',
|
||||||
'javelin-behavior-test-payment-form' => 'fc91ab6c',
|
'javelin-behavior-test-payment-form' => 'fc91ab6c',
|
||||||
'javelin-behavior-time-typeahead' => '522431f7',
|
'javelin-behavior-time-typeahead' => '522431f7',
|
||||||
|
@ -1550,12 +1548,6 @@ return array(
|
||||||
'phabricator-keyboard-shortcut',
|
'phabricator-keyboard-shortcut',
|
||||||
'javelin-stratcom',
|
'javelin-stratcom',
|
||||||
),
|
),
|
||||||
'887ad43f' => array(
|
|
||||||
'javelin-behavior',
|
|
||||||
'javelin-request',
|
|
||||||
'javelin-stratcom',
|
|
||||||
'javelin-dom',
|
|
||||||
),
|
|
||||||
'8935deef' => array(
|
'8935deef' => array(
|
||||||
'javelin-install',
|
'javelin-install',
|
||||||
'javelin-dom',
|
'javelin-dom',
|
||||||
|
|
14
resources/sql/autopatches/20181106.repo.01.sync.sql
Normal file
14
resources/sql/autopatches/20181106.repo.01.sync.sql
Normal file
|
@ -0,0 +1,14 @@
|
||||||
|
CREATE TABLE {$NAMESPACE}_repository.repository_syncevent (
|
||||||
|
id INT UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY,
|
||||||
|
phid VARBINARY(64) NOT NULL,
|
||||||
|
repositoryPHID VARBINARY(64) NOT NULL,
|
||||||
|
epoch INT UNSIGNED NOT NULL,
|
||||||
|
devicePHID VARBINARY(64) NOT NULL,
|
||||||
|
fromDevicePHID VARBINARY(64) NOT NULL,
|
||||||
|
deviceVersion INT UNSIGNED,
|
||||||
|
fromDeviceVersion INT UNSIGNED,
|
||||||
|
resultType VARCHAR(32) NOT NULL COLLATE {$COLLATE_TEXT},
|
||||||
|
resultCode INT UNSIGNED NOT NULL,
|
||||||
|
syncWait BIGINT UNSIGNED NOT NULL,
|
||||||
|
properties LONGTEXT NOT NULL COLLATE {$COLLATE_TEXT}
|
||||||
|
) ENGINE=InnoDB, COLLATE {$COLLATE_TEXT};
|
2
resources/sql/autopatches/20181106.repo.02.hook.sql
Normal file
2
resources/sql/autopatches/20181106.repo.02.hook.sql
Normal file
|
@ -0,0 +1,2 @@
|
||||||
|
ALTER TABLE {$NAMESPACE}_repository.repository_pushevent
|
||||||
|
ADD hookWait BIGINT UNSIGNED;
|
|
@ -17,6 +17,8 @@
|
||||||
// subclasses of PhabricatorConfigSiteSource to read it and build an instance
|
// subclasses of PhabricatorConfigSiteSource to read it and build an instance
|
||||||
// environment.
|
// environment.
|
||||||
|
|
||||||
|
$hook_start = microtime(true);
|
||||||
|
|
||||||
if ($argc > 1) {
|
if ($argc > 1) {
|
||||||
$context = $argv[1];
|
$context = $argv[1];
|
||||||
$context = explode(':', $context, 2);
|
$context = explode(':', $context, 2);
|
||||||
|
@ -35,7 +37,8 @@ if ($argc < 2) {
|
||||||
throw new Exception(pht('usage: commit-hook <repository>'));
|
throw new Exception(pht('usage: commit-hook <repository>'));
|
||||||
}
|
}
|
||||||
|
|
||||||
$engine = new DiffusionCommitHookEngine();
|
$engine = id(new DiffusionCommitHookEngine())
|
||||||
|
->setStartTime($hook_start);
|
||||||
|
|
||||||
$repository = id(new PhabricatorRepositoryQuery())
|
$repository = id(new PhabricatorRepositoryQuery())
|
||||||
->setViewer(PhabricatorUser::getOmnipotentUser())
|
->setViewer(PhabricatorUser::getOmnipotentUser())
|
||||||
|
@ -204,23 +207,23 @@ try {
|
||||||
+---------------------------------------------------------------+
|
+---------------------------------------------------------------+
|
||||||
| * * * PUSH REJECTED BY EVIL DRAGON BUREAUCRATS * * * |
|
| * * * PUSH REJECTED BY EVIL DRAGON BUREAUCRATS * * * |
|
||||||
+---------------------------------------------------------------+
|
+---------------------------------------------------------------+
|
||||||
\
|
\
|
||||||
\ ^ /^
|
\ ^ /^
|
||||||
\ / \ // \
|
\ / \ // \
|
||||||
\ |\___/| / \// .\
|
\ |\___/| / \// .\
|
||||||
\ /V V \__ / // | \ \ *----*
|
\ /V V \__ / // | \ \ *----*
|
||||||
/ / \/_/ // | \ \ \ |
|
/ / \/_/ // | \ \ \ |
|
||||||
@___@` \/_ // | \ \ \/\ \
|
@___@` \/_ // | \ \ \/\ \
|
||||||
0/0/| \/_ // | \ \ \ \
|
0/0/| \/_ // | \ \ \ \
|
||||||
0/0/0/0/| \/// | \ \ | |
|
0/0/0/0/| \/// | \ \ | |
|
||||||
0/0/0/0/0/_|_ / ( // | \ _\ | /
|
0/0/0/0/0/_|_ / ( // | \ _\ | /
|
||||||
0/0/0/0/0/0/`/,_ _ _/ ) ; -. | _ _\.-~ / /
|
0/0/0/0/0/0/`/,_ _ _/ ) ; -. | _ _\.-~ / /
|
||||||
,-} _ *-.|.-~-. .~ ~
|
,-} _ *-.|.-~-. .~ ~
|
||||||
\ \__/ `/\ / ~-. _ .-~ /
|
* \__/ `/\ / ~-. _ .-~ /
|
||||||
\____(Oo) *. } { /
|
\____(Oo) *. } { /
|
||||||
( (--) .----~-.\ \-` .~
|
( (..) .----~-.\ \-` .~
|
||||||
//__\\\\ \ DENIED! ///.----..< \ _ -~
|
//___\\\\ \ DENIED! ///.----..< \ _ -~
|
||||||
// \\\\ ///-._ _ _ _ _ _ _{^ - - - - ~
|
// \\\\ ///-._ _ _ _ _ _ _{^ - - - - ~
|
||||||
|
|
||||||
EOTXT
|
EOTXT
|
||||||
);
|
);
|
||||||
|
|
|
@ -307,7 +307,7 @@ try {
|
||||||
$ssh_log->setData(
|
$ssh_log->setData(
|
||||||
array(
|
array(
|
||||||
'c' => $err,
|
'c' => $err,
|
||||||
'T' => (int)(1000000 * (microtime(true) - $ssh_start_time)),
|
'T' => phutil_microseconds_since($ssh_start_time),
|
||||||
));
|
));
|
||||||
|
|
||||||
exit($err);
|
exit($err);
|
||||||
|
|
|
@ -992,6 +992,9 @@ phutil_register_library_map(array(
|
||||||
'DiffusionSymbolController' => 'applications/diffusion/controller/DiffusionSymbolController.php',
|
'DiffusionSymbolController' => 'applications/diffusion/controller/DiffusionSymbolController.php',
|
||||||
'DiffusionSymbolDatasource' => 'applications/diffusion/typeahead/DiffusionSymbolDatasource.php',
|
'DiffusionSymbolDatasource' => 'applications/diffusion/typeahead/DiffusionSymbolDatasource.php',
|
||||||
'DiffusionSymbolQuery' => 'applications/diffusion/query/DiffusionSymbolQuery.php',
|
'DiffusionSymbolQuery' => 'applications/diffusion/query/DiffusionSymbolQuery.php',
|
||||||
|
'DiffusionSyncLogListController' => 'applications/diffusion/controller/DiffusionSyncLogListController.php',
|
||||||
|
'DiffusionSyncLogListView' => 'applications/diffusion/view/DiffusionSyncLogListView.php',
|
||||||
|
'DiffusionSyncLogSearchEngine' => 'applications/diffusion/query/DiffusionSyncLogSearchEngine.php',
|
||||||
'DiffusionTagListController' => 'applications/diffusion/controller/DiffusionTagListController.php',
|
'DiffusionTagListController' => 'applications/diffusion/controller/DiffusionTagListController.php',
|
||||||
'DiffusionTagListView' => 'applications/diffusion/view/DiffusionTagListView.php',
|
'DiffusionTagListView' => 'applications/diffusion/view/DiffusionTagListView.php',
|
||||||
'DiffusionTagTableView' => 'applications/diffusion/view/DiffusionTagTableView.php',
|
'DiffusionTagTableView' => 'applications/diffusion/view/DiffusionTagTableView.php',
|
||||||
|
@ -4163,6 +4166,9 @@ phutil_register_library_map(array(
|
||||||
'PhabricatorRepositorySvnCommitChangeParserWorker' => 'applications/repository/worker/commitchangeparser/PhabricatorRepositorySvnCommitChangeParserWorker.php',
|
'PhabricatorRepositorySvnCommitChangeParserWorker' => 'applications/repository/worker/commitchangeparser/PhabricatorRepositorySvnCommitChangeParserWorker.php',
|
||||||
'PhabricatorRepositorySvnCommitMessageParserWorker' => 'applications/repository/worker/commitmessageparser/PhabricatorRepositorySvnCommitMessageParserWorker.php',
|
'PhabricatorRepositorySvnCommitMessageParserWorker' => 'applications/repository/worker/commitmessageparser/PhabricatorRepositorySvnCommitMessageParserWorker.php',
|
||||||
'PhabricatorRepositorySymbol' => 'applications/repository/storage/PhabricatorRepositorySymbol.php',
|
'PhabricatorRepositorySymbol' => 'applications/repository/storage/PhabricatorRepositorySymbol.php',
|
||||||
|
'PhabricatorRepositorySyncEvent' => 'applications/repository/storage/PhabricatorRepositorySyncEvent.php',
|
||||||
|
'PhabricatorRepositorySyncEventPHIDType' => 'applications/repository/phid/PhabricatorRepositorySyncEventPHIDType.php',
|
||||||
|
'PhabricatorRepositorySyncEventQuery' => 'applications/repository/query/PhabricatorRepositorySyncEventQuery.php',
|
||||||
'PhabricatorRepositoryTestCase' => 'applications/repository/storage/__tests__/PhabricatorRepositoryTestCase.php',
|
'PhabricatorRepositoryTestCase' => 'applications/repository/storage/__tests__/PhabricatorRepositoryTestCase.php',
|
||||||
'PhabricatorRepositoryTransaction' => 'applications/repository/storage/PhabricatorRepositoryTransaction.php',
|
'PhabricatorRepositoryTransaction' => 'applications/repository/storage/PhabricatorRepositoryTransaction.php',
|
||||||
'PhabricatorRepositoryTransactionQuery' => 'applications/repository/query/PhabricatorRepositoryTransactionQuery.php',
|
'PhabricatorRepositoryTransactionQuery' => 'applications/repository/query/PhabricatorRepositoryTransactionQuery.php',
|
||||||
|
@ -6364,6 +6370,9 @@ phutil_register_library_map(array(
|
||||||
'DiffusionSymbolController' => 'DiffusionController',
|
'DiffusionSymbolController' => 'DiffusionController',
|
||||||
'DiffusionSymbolDatasource' => 'PhabricatorTypeaheadDatasource',
|
'DiffusionSymbolDatasource' => 'PhabricatorTypeaheadDatasource',
|
||||||
'DiffusionSymbolQuery' => 'PhabricatorOffsetPagedQuery',
|
'DiffusionSymbolQuery' => 'PhabricatorOffsetPagedQuery',
|
||||||
|
'DiffusionSyncLogListController' => 'DiffusionLogController',
|
||||||
|
'DiffusionSyncLogListView' => 'AphrontView',
|
||||||
|
'DiffusionSyncLogSearchEngine' => 'PhabricatorApplicationSearchEngine',
|
||||||
'DiffusionTagListController' => 'DiffusionController',
|
'DiffusionTagListController' => 'DiffusionController',
|
||||||
'DiffusionTagListView' => 'DiffusionView',
|
'DiffusionTagListView' => 'DiffusionView',
|
||||||
'DiffusionTagTableView' => 'DiffusionView',
|
'DiffusionTagTableView' => 'DiffusionView',
|
||||||
|
@ -7131,7 +7140,10 @@ phutil_register_library_map(array(
|
||||||
'LegalpadTransactionQuery' => 'PhabricatorApplicationTransactionQuery',
|
'LegalpadTransactionQuery' => 'PhabricatorApplicationTransactionQuery',
|
||||||
'LegalpadTransactionView' => 'PhabricatorApplicationTransactionView',
|
'LegalpadTransactionView' => 'PhabricatorApplicationTransactionView',
|
||||||
'LiskChunkTestCase' => 'PhabricatorTestCase',
|
'LiskChunkTestCase' => 'PhabricatorTestCase',
|
||||||
'LiskDAO' => 'Phobject',
|
'LiskDAO' => array(
|
||||||
|
'Phobject',
|
||||||
|
'AphrontDatabaseTableRefInterface',
|
||||||
|
),
|
||||||
'LiskDAOSet' => 'Phobject',
|
'LiskDAOSet' => 'Phobject',
|
||||||
'LiskDAOTestCase' => 'PhabricatorTestCase',
|
'LiskDAOTestCase' => 'PhabricatorTestCase',
|
||||||
'LiskEphemeralObjectException' => 'Exception',
|
'LiskEphemeralObjectException' => 'Exception',
|
||||||
|
@ -10108,6 +10120,12 @@ phutil_register_library_map(array(
|
||||||
'PhabricatorRepositorySvnCommitChangeParserWorker' => 'PhabricatorRepositoryCommitChangeParserWorker',
|
'PhabricatorRepositorySvnCommitChangeParserWorker' => 'PhabricatorRepositoryCommitChangeParserWorker',
|
||||||
'PhabricatorRepositorySvnCommitMessageParserWorker' => 'PhabricatorRepositoryCommitMessageParserWorker',
|
'PhabricatorRepositorySvnCommitMessageParserWorker' => 'PhabricatorRepositoryCommitMessageParserWorker',
|
||||||
'PhabricatorRepositorySymbol' => 'PhabricatorRepositoryDAO',
|
'PhabricatorRepositorySymbol' => 'PhabricatorRepositoryDAO',
|
||||||
|
'PhabricatorRepositorySyncEvent' => array(
|
||||||
|
'PhabricatorRepositoryDAO',
|
||||||
|
'PhabricatorPolicyInterface',
|
||||||
|
),
|
||||||
|
'PhabricatorRepositorySyncEventPHIDType' => 'PhabricatorPHIDType',
|
||||||
|
'PhabricatorRepositorySyncEventQuery' => 'PhabricatorCursorPagedPolicyAwareQuery',
|
||||||
'PhabricatorRepositoryTestCase' => 'PhabricatorTestCase',
|
'PhabricatorRepositoryTestCase' => 'PhabricatorTestCase',
|
||||||
'PhabricatorRepositoryTransaction' => 'PhabricatorApplicationTransaction',
|
'PhabricatorRepositoryTransaction' => 'PhabricatorApplicationTransaction',
|
||||||
'PhabricatorRepositoryTransactionQuery' => 'PhabricatorApplicationTransactionQuery',
|
'PhabricatorRepositoryTransactionQuery' => 'PhabricatorApplicationTransactionQuery',
|
||||||
|
|
|
@ -63,7 +63,7 @@ final class AlmanacKeys extends Phobject {
|
||||||
// protocol does not have a mechanism like a "Host" header.
|
// protocol does not have a mechanism like a "Host" header.
|
||||||
$username = PhabricatorEnv::getEnvConfig('cluster.instance');
|
$username = PhabricatorEnv::getEnvConfig('cluster.instance');
|
||||||
if (strlen($username)) {
|
if (strlen($username)) {
|
||||||
return $username;
|
// return $username;
|
||||||
}
|
}
|
||||||
|
|
||||||
$username = PhabricatorEnv::getEnvConfig('diffusion.ssh-user');
|
$username = PhabricatorEnv::getEnvConfig('diffusion.ssh-user');
|
||||||
|
|
|
@ -99,6 +99,82 @@ final class PhabricatorAuthPasswordTestCase extends PhabricatorTestCase {
|
||||||
$this->assertTrue($account_engine->isUniquePassword($password2));
|
$this->assertTrue($account_engine->isUniquePassword($password2));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function testPasswordBlocklisting() {
|
||||||
|
$user = $this->generateNewTestUser();
|
||||||
|
|
||||||
|
$user
|
||||||
|
->setUsername('iasimov')
|
||||||
|
->setRealName('Isaac Asimov')
|
||||||
|
->save();
|
||||||
|
|
||||||
|
$test_type = PhabricatorAuthPassword::PASSWORD_TYPE_TEST;
|
||||||
|
$content_source = $this->newContentSource();
|
||||||
|
|
||||||
|
$engine = id(new PhabricatorAuthPasswordEngine())
|
||||||
|
->setViewer($user)
|
||||||
|
->setContentSource($content_source)
|
||||||
|
->setPasswordType($test_type)
|
||||||
|
->setObject($user);
|
||||||
|
|
||||||
|
$env = PhabricatorEnv::beginScopedEnv();
|
||||||
|
$env->overrideEnvConfig('account.minimum-password-length', 4);
|
||||||
|
|
||||||
|
$passwords = array(
|
||||||
|
'a23li432m9mdf' => true,
|
||||||
|
|
||||||
|
// Empty.
|
||||||
|
'' => false,
|
||||||
|
|
||||||
|
// Password length tests.
|
||||||
|
'xh3' => false,
|
||||||
|
'xh32' => true,
|
||||||
|
|
||||||
|
// In common password blocklist.
|
||||||
|
'password1' => false,
|
||||||
|
|
||||||
|
// Tests for the account identifier blocklist.
|
||||||
|
'isaac' => false,
|
||||||
|
'iasimov' => false,
|
||||||
|
'iasimov1' => false,
|
||||||
|
'asimov' => false,
|
||||||
|
'iSaAc' => false,
|
||||||
|
'32IASIMOV' => false,
|
||||||
|
'i-am-iasimov-this-is-my-long-strong-password' => false,
|
||||||
|
'iasimo' => false,
|
||||||
|
|
||||||
|
// These are okay: although they're visually similar, they aren't mutual
|
||||||
|
// substrings of any identifier.
|
||||||
|
'iasimo1' => true,
|
||||||
|
'isa1mov' => true,
|
||||||
|
);
|
||||||
|
|
||||||
|
foreach ($passwords as $password => $expect) {
|
||||||
|
$this->assertBlocklistedPassword($engine, $password, $expect);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private function assertBlocklistedPassword(
|
||||||
|
PhabricatorAuthPasswordEngine $engine,
|
||||||
|
$raw_password,
|
||||||
|
$expect_valid) {
|
||||||
|
|
||||||
|
$envelope_1 = new PhutilOpaqueEnvelope($raw_password);
|
||||||
|
$envelope_2 = new PhutilOpaqueEnvelope($raw_password);
|
||||||
|
|
||||||
|
$caught = null;
|
||||||
|
try {
|
||||||
|
$engine->checkNewPassword($envelope_1, $envelope_2);
|
||||||
|
} catch (PhabricatorAuthPasswordException $exception) {
|
||||||
|
$caught = $exception;
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->assertEqual(
|
||||||
|
$expect_valid,
|
||||||
|
!($caught instanceof PhabricatorAuthPasswordException),
|
||||||
|
pht('Validity of password "%s".', $raw_password));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
public function testPasswordUpgrade() {
|
public function testPasswordUpgrade() {
|
||||||
$weak_hasher = new PhabricatorIteratedMD5PasswordHasher();
|
$weak_hasher = new PhabricatorIteratedMD5PasswordHasher();
|
||||||
|
|
||||||
|
|
|
@ -115,7 +115,9 @@ final class PhabricatorAuthPasswordEngine
|
||||||
// revoked passwords or colliding passwords either, so we can skip these
|
// revoked passwords or colliding passwords either, so we can skip these
|
||||||
// checks.
|
// checks.
|
||||||
|
|
||||||
if ($this->getObject()->getPHID()) {
|
$object = $this->getObject();
|
||||||
|
|
||||||
|
if ($object->getPHID()) {
|
||||||
if ($this->isRevokedPassword($password)) {
|
if ($this->isRevokedPassword($password)) {
|
||||||
throw new PhabricatorAuthPasswordException(
|
throw new PhabricatorAuthPasswordException(
|
||||||
pht(
|
pht(
|
||||||
|
@ -132,6 +134,66 @@ final class PhabricatorAuthPasswordEngine
|
||||||
pht('Not Unique'));
|
pht('Not Unique'));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Prevent use of passwords which are similar to any object identifier.
|
||||||
|
// For example, if your username is "alincoln", your password may not be
|
||||||
|
// "alincoln", "lincoln", or "alincoln1".
|
||||||
|
$viewer = $this->getViewer();
|
||||||
|
$blocklist = $object->newPasswordBlocklist($viewer, $this);
|
||||||
|
|
||||||
|
// Smallest number of overlapping characters that we'll consider to be
|
||||||
|
// too similar.
|
||||||
|
$minimum_similarity = 4;
|
||||||
|
|
||||||
|
// Add the domain name to the blocklist.
|
||||||
|
$base_uri = PhabricatorEnv::getAnyBaseURI();
|
||||||
|
$base_uri = new PhutilURI($base_uri);
|
||||||
|
$blocklist[] = $base_uri->getDomain();
|
||||||
|
|
||||||
|
// Generate additional subterms by splitting the raw blocklist on
|
||||||
|
// characters like "@", " " (space), and "." to break up email addresses,
|
||||||
|
// readable names, and domain names into components.
|
||||||
|
$terms_map = array();
|
||||||
|
foreach ($blocklist as $term) {
|
||||||
|
$terms_map[$term] = $term;
|
||||||
|
foreach (preg_split('/[ @.]/', $term) as $subterm) {
|
||||||
|
$terms_map[$subterm] = $term;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Skip very short terms: it's okay if your password has the substring
|
||||||
|
// "com" in it somewhere even if the install is on "mycompany.com".
|
||||||
|
foreach ($terms_map as $term => $source) {
|
||||||
|
if (strlen($term) < $minimum_similarity) {
|
||||||
|
unset($terms_map[$term]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Normalize terms for comparison.
|
||||||
|
$normal_map = array();
|
||||||
|
foreach ($terms_map as $term => $source) {
|
||||||
|
$term = phutil_utf8_strtolower($term);
|
||||||
|
$normal_map[$term] = $source;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Finally, make sure that none of the terms appear in the password,
|
||||||
|
// and that the password does not appear in any of the terms.
|
||||||
|
$normal_password = phutil_utf8_strtolower($raw_password);
|
||||||
|
if (strlen($normal_password) >= $minimum_similarity) {
|
||||||
|
foreach ($normal_map as $term => $source) {
|
||||||
|
if (strpos($term, $normal_password) === false &&
|
||||||
|
strpos($normal_password, $term) === false) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
throw new PhabricatorAuthPasswordException(
|
||||||
|
pht(
|
||||||
|
'The password you entered is very similar to a nonsecret account '.
|
||||||
|
'identifier (like a username or email address). Choose a more '.
|
||||||
|
'distinct password.'),
|
||||||
|
pht('Not Distinct'));
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public function isValidPassword(PhutilOpaqueEnvelope $envelope) {
|
public function isValidPassword(PhutilOpaqueEnvelope $envelope) {
|
||||||
|
|
|
@ -185,7 +185,7 @@ final class PhabricatorTOTPAuthFactor extends PhabricatorAuthFactor {
|
||||||
|
|
||||||
|
|
||||||
public static function generateNewTOTPKey() {
|
public static function generateNewTOTPKey() {
|
||||||
return strtoupper(Filesystem::readRandomCharacters(16));
|
return strtoupper(Filesystem::readRandomCharacters(32));
|
||||||
}
|
}
|
||||||
|
|
||||||
public static function verifyTOTPCode(
|
public static function verifyTOTPCode(
|
||||||
|
|
|
@ -6,4 +6,22 @@ interface PhabricatorAuthPasswordHashInterface {
|
||||||
PhutilOpaqueEnvelope $envelope,
|
PhutilOpaqueEnvelope $envelope,
|
||||||
PhabricatorAuthPassword $password);
|
PhabricatorAuthPassword $password);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return a list of strings which passwords associated with this object may
|
||||||
|
* not be similar to.
|
||||||
|
*
|
||||||
|
* This method allows you to prevent users from selecting their username
|
||||||
|
* as their password or picking other passwords which are trivially similar
|
||||||
|
* to an account or object identifier.
|
||||||
|
*
|
||||||
|
* @param PhabricatorUser The user selecting the password.
|
||||||
|
* @param PhabricatorAuthPasswordEngine The password engine updating a
|
||||||
|
* password.
|
||||||
|
* @return list<string> Blocklist of nonsecret identifiers which the password
|
||||||
|
* should not be similar to.
|
||||||
|
*/
|
||||||
|
public function newPasswordBlocklist(
|
||||||
|
PhabricatorUser $viewer,
|
||||||
|
PhabricatorAuthPasswordEngine $engine);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -109,15 +109,13 @@ final class PhabricatorConduitAPIController
|
||||||
$error_info = $ex->getMessage();
|
$error_info = $ex->getMessage();
|
||||||
}
|
}
|
||||||
|
|
||||||
$time_end = microtime(true);
|
|
||||||
|
|
||||||
$log
|
$log
|
||||||
->setCallerPHID(
|
->setCallerPHID(
|
||||||
isset($conduit_user)
|
isset($conduit_user)
|
||||||
? $conduit_user->getPHID()
|
? $conduit_user->getPHID()
|
||||||
: null)
|
: null)
|
||||||
->setError((string)$error_code)
|
->setError((string)$error_code)
|
||||||
->setDuration(1000000 * ($time_end - $time_start));
|
->setDuration(phutil_microseconds_since($time_start));
|
||||||
|
|
||||||
if (!PhabricatorEnv::isReadOnly()) {
|
if (!PhabricatorEnv::isReadOnly()) {
|
||||||
$unguarded = AphrontWriteGuard::beginScopedUnguardedWrites();
|
$unguarded = AphrontWriteGuard::beginScopedUnguardedWrites();
|
||||||
|
|
|
@ -73,15 +73,13 @@ final class ConduitSSHWorkflow extends PhabricatorSSHWorkflow {
|
||||||
// if the response is large and the receiver is slow to read it.
|
// if the response is large and the receiver is slow to read it.
|
||||||
$this->getIOChannel()->flush();
|
$this->getIOChannel()->flush();
|
||||||
|
|
||||||
$time_end = microtime(true);
|
|
||||||
|
|
||||||
$connection_id = idx($metadata, 'connectionID');
|
$connection_id = idx($metadata, 'connectionID');
|
||||||
$log = id(new PhabricatorConduitMethodCallLog())
|
$log = id(new PhabricatorConduitMethodCallLog())
|
||||||
->setCallerPHID($this->getSSHUser()->getPHID())
|
->setCallerPHID($this->getSSHUser()->getPHID())
|
||||||
->setConnectionID($connection_id)
|
->setConnectionID($connection_id)
|
||||||
->setMethod($method)
|
->setMethod($method)
|
||||||
->setError((string)$error_code)
|
->setError((string)$error_code)
|
||||||
->setDuration(1000000 * ($time_end - $time_start))
|
->setDuration(phutil_microseconds_since($time_start))
|
||||||
->save();
|
->save();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -118,6 +118,9 @@ final class PhabricatorDiffusionApplication extends PhabricatorApplication {
|
||||||
$this->getQueryRoutePattern() => 'DiffusionPushLogListController',
|
$this->getQueryRoutePattern() => 'DiffusionPushLogListController',
|
||||||
'view/(?P<id>\d+)/' => 'DiffusionPushEventViewController',
|
'view/(?P<id>\d+)/' => 'DiffusionPushEventViewController',
|
||||||
),
|
),
|
||||||
|
'synclog/' => array(
|
||||||
|
$this->getQueryRoutePattern() => 'DiffusionSyncLogListController',
|
||||||
|
),
|
||||||
'pulllog/' => array(
|
'pulllog/' => array(
|
||||||
$this->getQueryRoutePattern() => 'DiffusionPullLogListController',
|
$this->getQueryRoutePattern() => 'DiffusionPullLogListController',
|
||||||
),
|
),
|
||||||
|
|
|
@ -370,8 +370,17 @@ final class DiffusionRepositoryController extends DiffusionController {
|
||||||
$action_view->addAction(
|
$action_view->addAction(
|
||||||
id(new PhabricatorActionView())
|
id(new PhabricatorActionView())
|
||||||
->setName(pht('View Push Logs'))
|
->setName(pht('View Push Logs'))
|
||||||
->setIcon('fa-list-alt')
|
->setIcon('fa-upload')
|
||||||
->setHref($push_uri));
|
->setHref($push_uri));
|
||||||
|
|
||||||
|
$pull_uri = $this->getApplicationURI(
|
||||||
|
'synclog/?repositories='.$repository->getPHID());
|
||||||
|
|
||||||
|
$action_view->addAction(
|
||||||
|
id(new PhabricatorActionView())
|
||||||
|
->setName(pht('View Sync Logs'))
|
||||||
|
->setIcon('fa-exchange')
|
||||||
|
->setHref($pull_uri));
|
||||||
}
|
}
|
||||||
|
|
||||||
$pull_uri = $this->getApplicationURI(
|
$pull_uri = $this->getApplicationURI(
|
||||||
|
@ -380,7 +389,7 @@ final class DiffusionRepositoryController extends DiffusionController {
|
||||||
$action_view->addAction(
|
$action_view->addAction(
|
||||||
id(new PhabricatorActionView())
|
id(new PhabricatorActionView())
|
||||||
->setName(pht('View Pull Logs'))
|
->setName(pht('View Pull Logs'))
|
||||||
->setIcon('fa-list-alt')
|
->setIcon('fa-download')
|
||||||
->setHref($pull_uri));
|
->setHref($pull_uri));
|
||||||
|
|
||||||
return $action_view;
|
return $action_view;
|
||||||
|
|
|
@ -0,0 +1,17 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
final class DiffusionSyncLogListController
|
||||||
|
extends DiffusionLogController {
|
||||||
|
|
||||||
|
public function handleRequest(AphrontRequest $request) {
|
||||||
|
return id(new DiffusionSyncLogSearchEngine())
|
||||||
|
->setController($this)
|
||||||
|
->buildResponse();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function buildApplicationCrumbs() {
|
||||||
|
return parent::buildApplicationCrumbs()
|
||||||
|
->addTextCrumb(pht('Sync Logs'), $this->getApplicationURI('synclog/'));
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -31,6 +31,7 @@ final class DiffusionCommitHookEngine extends Phobject {
|
||||||
private $mercurialHook;
|
private $mercurialHook;
|
||||||
private $mercurialCommits = array();
|
private $mercurialCommits = array();
|
||||||
private $gitCommits = array();
|
private $gitCommits = array();
|
||||||
|
private $startTime;
|
||||||
|
|
||||||
private $heraldViewerProjects;
|
private $heraldViewerProjects;
|
||||||
private $rejectCode = PhabricatorRepositoryPushLog::REJECT_BROKEN;
|
private $rejectCode = PhabricatorRepositoryPushLog::REJECT_BROKEN;
|
||||||
|
@ -70,6 +71,15 @@ final class DiffusionCommitHookEngine extends Phobject {
|
||||||
return $this->requestIdentifier;
|
return $this->requestIdentifier;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function setStartTime($start_time) {
|
||||||
|
$this->startTime = $start_time;
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getStartTime() {
|
||||||
|
return $this->startTime;
|
||||||
|
}
|
||||||
|
|
||||||
public function setSubversionTransactionInfo($transaction, $repository) {
|
public function setSubversionTransactionInfo($transaction, $repository) {
|
||||||
$this->subversionTransaction = $transaction;
|
$this->subversionTransaction = $transaction;
|
||||||
$this->subversionRepository = $repository;
|
$this->subversionRepository = $repository;
|
||||||
|
@ -1102,11 +1112,14 @@ final class DiffusionCommitHookEngine extends Phobject {
|
||||||
private function newPushEvent() {
|
private function newPushEvent() {
|
||||||
$viewer = $this->getViewer();
|
$viewer = $this->getViewer();
|
||||||
|
|
||||||
|
$hook_start = $this->getStartTime();
|
||||||
|
|
||||||
$event = PhabricatorRepositoryPushEvent::initializeNewEvent($viewer)
|
$event = PhabricatorRepositoryPushEvent::initializeNewEvent($viewer)
|
||||||
->setRepositoryPHID($this->getRepository()->getPHID())
|
->setRepositoryPHID($this->getRepository()->getPHID())
|
||||||
->setRemoteAddress($this->getRemoteAddress())
|
->setRemoteAddress($this->getRemoteAddress())
|
||||||
->setRemoteProtocol($this->getRemoteProtocol())
|
->setRemoteProtocol($this->getRemoteProtocol())
|
||||||
->setEpoch(PhabricatorTime::getNow());
|
->setEpoch(PhabricatorTime::getNow())
|
||||||
|
->setHookWait(phutil_microseconds_since($hook_start));
|
||||||
|
|
||||||
$identifier = $this->getRequestIdentifier();
|
$identifier = $this->getRequestIdentifier();
|
||||||
if (strlen($identifier)) {
|
if (strlen($identifier)) {
|
||||||
|
|
|
@ -206,7 +206,10 @@ final class DiffusionRepositoryClusterEngine extends Phobject {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
$this->synchronizeWorkingCopyFromDevices($fetchable);
|
$this->synchronizeWorkingCopyFromDevices(
|
||||||
|
$fetchable,
|
||||||
|
$this_version,
|
||||||
|
$max_version);
|
||||||
} else {
|
} else {
|
||||||
$this->synchronizeWorkingCopyFromRemote();
|
$this->synchronizeWorkingCopyFromRemote();
|
||||||
}
|
}
|
||||||
|
@ -407,8 +410,8 @@ final class DiffusionRepositoryClusterEngine extends Phobject {
|
||||||
|
|
||||||
$log = $this->logger;
|
$log = $this->logger;
|
||||||
if ($log) {
|
if ($log) {
|
||||||
$log->writeClusterEngineLogProperty('readWait', $read_wait);
|
|
||||||
$log->writeClusterEngineLogProperty('writeWait', $write_wait);
|
$log->writeClusterEngineLogProperty('writeWait', $write_wait);
|
||||||
|
$log->writeClusterEngineLogProperty('readWait', $read_wait);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -653,7 +656,11 @@ final class DiffusionRepositoryClusterEngine extends Phobject {
|
||||||
/**
|
/**
|
||||||
* @task internal
|
* @task internal
|
||||||
*/
|
*/
|
||||||
private function synchronizeWorkingCopyFromDevices(array $device_phids) {
|
private function synchronizeWorkingCopyFromDevices(
|
||||||
|
array $device_phids,
|
||||||
|
$local_version,
|
||||||
|
$remote_version) {
|
||||||
|
|
||||||
$repository = $this->getRepository();
|
$repository = $this->getRepository();
|
||||||
|
|
||||||
$service = $repository->loadAlmanacService();
|
$service = $repository->loadAlmanacService();
|
||||||
|
@ -694,7 +701,10 @@ final class DiffusionRepositoryClusterEngine extends Phobject {
|
||||||
$caught = null;
|
$caught = null;
|
||||||
foreach ($fetchable as $binding) {
|
foreach ($fetchable as $binding) {
|
||||||
try {
|
try {
|
||||||
$this->synchronizeWorkingCopyFromBinding($binding);
|
$this->synchronizeWorkingCopyFromBinding(
|
||||||
|
$binding,
|
||||||
|
$local_version,
|
||||||
|
$remote_version);
|
||||||
$caught = null;
|
$caught = null;
|
||||||
break;
|
break;
|
||||||
} catch (Exception $ex) {
|
} catch (Exception $ex) {
|
||||||
|
@ -711,14 +721,17 @@ final class DiffusionRepositoryClusterEngine extends Phobject {
|
||||||
/**
|
/**
|
||||||
* @task internal
|
* @task internal
|
||||||
*/
|
*/
|
||||||
private function synchronizeWorkingCopyFromBinding($binding) {
|
private function synchronizeWorkingCopyFromBinding(
|
||||||
|
AlmanacBinding $binding,
|
||||||
|
$local_version,
|
||||||
|
$remote_version) {
|
||||||
|
|
||||||
$repository = $this->getRepository();
|
$repository = $this->getRepository();
|
||||||
$device = AlmanacKeys::getLiveDevice();
|
$device = AlmanacKeys::getLiveDevice();
|
||||||
|
|
||||||
$this->logLine(
|
$this->logLine(
|
||||||
pht(
|
pht(
|
||||||
'Synchronizing this device ("%s") from cluster leader ("%s") before '.
|
'Synchronizing this device ("%s") from cluster leader ("%s").',
|
||||||
'read.',
|
|
||||||
$device->getName(),
|
$device->getName(),
|
||||||
$binding->getDevice()->getName()));
|
$binding->getDevice()->getName()));
|
||||||
|
|
||||||
|
@ -746,17 +759,57 @@ final class DiffusionRepositoryClusterEngine extends Phobject {
|
||||||
|
|
||||||
$future->setCWD($local_path);
|
$future->setCWD($local_path);
|
||||||
|
|
||||||
|
$log = PhabricatorRepositorySyncEvent::initializeNewEvent()
|
||||||
|
->setRepositoryPHID($repository->getPHID())
|
||||||
|
->setEpoch(PhabricatorTime::getNow())
|
||||||
|
->setDevicePHID($device->getPHID())
|
||||||
|
->setFromDevicePHID($binding->getDevice()->getPHID())
|
||||||
|
->setDeviceVersion($local_version)
|
||||||
|
->setFromDeviceVersion($remote_version);
|
||||||
|
|
||||||
|
$sync_start = microtime(true);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
$future->resolvex();
|
$future->resolvex();
|
||||||
} catch (Exception $ex) {
|
} catch (Exception $ex) {
|
||||||
|
$log->setSyncWait(phutil_microseconds_since($sync_start));
|
||||||
|
|
||||||
|
if ($ex instanceof CommandException) {
|
||||||
|
if ($future->getWasKilledByTimeout()) {
|
||||||
|
$result_type = PhabricatorRepositorySyncEvent::RESULT_TIMEOUT;
|
||||||
|
} else {
|
||||||
|
$result_type = PhabricatorRepositorySyncEvent::RESULT_ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
|
$log
|
||||||
|
->setResultCode($ex->getError())
|
||||||
|
->setResultType($result_type)
|
||||||
|
->setProperty('stdout', $ex->getStdout())
|
||||||
|
->setProperty('stderr', $ex->getStderr());
|
||||||
|
} else {
|
||||||
|
$log
|
||||||
|
->setResultCode(1)
|
||||||
|
->setResultType(PhabricatorRepositorySyncEvent::RESULT_EXCEPTION)
|
||||||
|
->setProperty('message', $ex->getMessage());
|
||||||
|
}
|
||||||
|
|
||||||
|
$log->save();
|
||||||
|
|
||||||
$this->logLine(
|
$this->logLine(
|
||||||
pht(
|
pht(
|
||||||
'Synchronization of "%s" from leader "%s" failed: %s',
|
'Synchronization of "%s" from leader "%s" failed: %s',
|
||||||
$device->getName(),
|
$device->getName(),
|
||||||
$binding->getDevice()->getName(),
|
$binding->getDevice()->getName(),
|
||||||
$ex->getMessage()));
|
$ex->getMessage()));
|
||||||
|
|
||||||
throw $ex;
|
throw $ex;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$log
|
||||||
|
->setSyncWait(phutil_microseconds_since($sync_start))
|
||||||
|
->setResultCode(0)
|
||||||
|
->setResultType(PhabricatorRepositorySyncEvent::RESULT_SYNC)
|
||||||
|
->save();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,154 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
final class DiffusionSyncLogSearchEngine
|
||||||
|
extends PhabricatorApplicationSearchEngine {
|
||||||
|
|
||||||
|
public function getResultTypeDescription() {
|
||||||
|
return pht('Sync Logs');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getApplicationClassName() {
|
||||||
|
return 'PhabricatorDiffusionApplication';
|
||||||
|
}
|
||||||
|
|
||||||
|
public function newQuery() {
|
||||||
|
return new PhabricatorRepositorySyncEventQuery();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function buildQueryFromParameters(array $map) {
|
||||||
|
$query = $this->newQuery();
|
||||||
|
|
||||||
|
if ($map['repositoryPHIDs']) {
|
||||||
|
$query->withRepositoryPHIDs($map['repositoryPHIDs']);
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($map['createdStart'] || $map['createdEnd']) {
|
||||||
|
$query->withEpochBetween(
|
||||||
|
$map['createdStart'],
|
||||||
|
$map['createdEnd']);
|
||||||
|
}
|
||||||
|
|
||||||
|
return $query;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function buildCustomSearchFields() {
|
||||||
|
return array(
|
||||||
|
id(new PhabricatorSearchDatasourceField())
|
||||||
|
->setDatasource(new DiffusionRepositoryDatasource())
|
||||||
|
->setKey('repositoryPHIDs')
|
||||||
|
->setAliases(array('repository', 'repositories', 'repositoryPHID'))
|
||||||
|
->setLabel(pht('Repositories'))
|
||||||
|
->setDescription(
|
||||||
|
pht('Search for sync logs for specific repositories.')),
|
||||||
|
id(new PhabricatorSearchDateField())
|
||||||
|
->setLabel(pht('Created After'))
|
||||||
|
->setKey('createdStart'),
|
||||||
|
id(new PhabricatorSearchDateField())
|
||||||
|
->setLabel(pht('Created Before'))
|
||||||
|
->setKey('createdEnd'),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function newExportFields() {
|
||||||
|
$viewer = $this->requireViewer();
|
||||||
|
|
||||||
|
$fields = array(
|
||||||
|
id(new PhabricatorPHIDExportField())
|
||||||
|
->setKey('repositoryPHID')
|
||||||
|
->setLabel(pht('Repository PHID')),
|
||||||
|
id(new PhabricatorStringExportField())
|
||||||
|
->setKey('repository')
|
||||||
|
->setLabel(pht('Repository')),
|
||||||
|
id(new PhabricatorPHIDExportField())
|
||||||
|
->setKey('devicePHID')
|
||||||
|
->setLabel(pht('Device PHID')),
|
||||||
|
id(new PhabricatorPHIDExportField())
|
||||||
|
->setKey('fromDevicePHID')
|
||||||
|
->setLabel(pht('From Device PHID')),
|
||||||
|
id(new PhabricatorIntExportField())
|
||||||
|
->setKey('deviceVersion')
|
||||||
|
->setLabel(pht('Device Version')),
|
||||||
|
id(new PhabricatorIntExportField())
|
||||||
|
->setKey('fromDeviceVersion')
|
||||||
|
->setLabel(pht('From Device Version')),
|
||||||
|
id(new PhabricatorStringExportField())
|
||||||
|
->setKey('result')
|
||||||
|
->setLabel(pht('Result')),
|
||||||
|
id(new PhabricatorIntExportField())
|
||||||
|
->setKey('code')
|
||||||
|
->setLabel(pht('Code')),
|
||||||
|
id(new PhabricatorEpochExportField())
|
||||||
|
->setKey('date')
|
||||||
|
->setLabel(pht('Date')),
|
||||||
|
id(new PhabricatorIntExportField())
|
||||||
|
->setKey('syncWait')
|
||||||
|
->setLabel(pht('Sync Wait')),
|
||||||
|
);
|
||||||
|
|
||||||
|
return $fields;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function newExportData(array $events) {
|
||||||
|
$viewer = $this->requireViewer();
|
||||||
|
|
||||||
|
$export = array();
|
||||||
|
foreach ($events as $event) {
|
||||||
|
$repository = $event->getRepository();
|
||||||
|
$repository_phid = $repository->getPHID();
|
||||||
|
$repository_name = $repository->getDisplayName();
|
||||||
|
|
||||||
|
$map = array(
|
||||||
|
'repositoryPHID' => $repository_phid,
|
||||||
|
'repository' => $repository_name,
|
||||||
|
'devicePHID' => $event->getDevicePHID(),
|
||||||
|
'fromDevicePHID' => $event->getFromDevicePHID(),
|
||||||
|
'deviceVersion' => $event->getDeviceVersion(),
|
||||||
|
'fromDeviceVersion' => $event->getFromDeviceVersion(),
|
||||||
|
'result' => $event->getResultType(),
|
||||||
|
'code' => $event->getResultCode(),
|
||||||
|
'date' => $event->getEpoch(),
|
||||||
|
'syncWait' => $event->getSyncWait(),
|
||||||
|
);
|
||||||
|
|
||||||
|
$export[] = $map;
|
||||||
|
}
|
||||||
|
|
||||||
|
return $export;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function getURI($path) {
|
||||||
|
return '/diffusion/synclog/'.$path;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function getBuiltinQueryNames() {
|
||||||
|
return array(
|
||||||
|
'all' => pht('All Sync Logs'),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
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 $logs,
|
||||||
|
PhabricatorSavedQuery $query,
|
||||||
|
array $handles) {
|
||||||
|
|
||||||
|
$table = id(new DiffusionSyncLogListView())
|
||||||
|
->setViewer($this->requireViewer())
|
||||||
|
->setLogs($logs);
|
||||||
|
|
||||||
|
return id(new PhabricatorApplicationSearchResultView())
|
||||||
|
->setTable($table);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -30,7 +30,7 @@ final class DiffusionGitReceivePackSSHWorkflow extends DiffusionGitSSHWorkflow {
|
||||||
|
|
||||||
if ($this->shouldProxy()) {
|
if ($this->shouldProxy()) {
|
||||||
$command = $this->getProxyCommand(true);
|
$command = $this->getProxyCommand(true);
|
||||||
$did_synchronize = false;
|
$did_write = false;
|
||||||
|
|
||||||
if ($device) {
|
if ($device) {
|
||||||
$this->writeClusterEngineLogMessage(
|
$this->writeClusterEngineLogMessage(
|
||||||
|
@ -40,7 +40,7 @@ final class DiffusionGitReceivePackSSHWorkflow extends DiffusionGitSSHWorkflow {
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
$command = csprintf('git-receive-pack %s', $repository->getLocalPath());
|
$command = csprintf('git-receive-pack %s', $repository->getLocalPath());
|
||||||
$did_synchronize = true;
|
$did_write = true;
|
||||||
$cluster_engine->synchronizeWorkingCopyBeforeWrite();
|
$cluster_engine->synchronizeWorkingCopyBeforeWrite();
|
||||||
|
|
||||||
if ($device) {
|
if ($device) {
|
||||||
|
@ -60,7 +60,7 @@ final class DiffusionGitReceivePackSSHWorkflow extends DiffusionGitSSHWorkflow {
|
||||||
|
|
||||||
// We've committed the write (or rejected it), so we can release the lock
|
// We've committed the write (or rejected it), so we can release the lock
|
||||||
// without waiting for the client to receive the acknowledgement.
|
// without waiting for the client to receive the acknowledgement.
|
||||||
if ($did_synchronize) {
|
if ($did_write) {
|
||||||
$cluster_engine->synchronizeWorkingCopyAfterWrite();
|
$cluster_engine->synchronizeWorkingCopyAfterWrite();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -69,18 +69,23 @@ final class DiffusionGitReceivePackSSHWorkflow extends DiffusionGitSSHWorkflow {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!$err) {
|
if (!$err) {
|
||||||
$repository->writeStatusMessage(
|
|
||||||
PhabricatorRepositoryStatusMessage::TYPE_NEEDS_UPDATE,
|
|
||||||
PhabricatorRepositoryStatusMessage::CODE_OKAY);
|
|
||||||
$this->waitForGitClient();
|
$this->waitForGitClient();
|
||||||
|
|
||||||
$host_wait_end = microtime(true);
|
// When a repository is clustered, we reach this cleanup code on both
|
||||||
|
// the proxy and the actual final endpoint node. Don't do more cleanup
|
||||||
|
// or logging than we need to.
|
||||||
|
if ($did_write) {
|
||||||
|
$repository->writeStatusMessage(
|
||||||
|
PhabricatorRepositoryStatusMessage::TYPE_NEEDS_UPDATE,
|
||||||
|
PhabricatorRepositoryStatusMessage::CODE_OKAY);
|
||||||
|
|
||||||
$this->updatePushLogWithTimingInformation(
|
$host_wait_end = microtime(true);
|
||||||
$this->getClusterEngineLogProperty('writeWait'),
|
|
||||||
$this->getClusterEngineLogProperty('readWait'),
|
|
||||||
($host_wait_end - $host_wait_start));
|
|
||||||
|
|
||||||
|
$this->updatePushLogWithTimingInformation(
|
||||||
|
$this->getClusterEngineLogProperty('writeWait'),
|
||||||
|
$this->getClusterEngineLogProperty('readWait'),
|
||||||
|
($host_wait_end - $host_wait_start));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return $err;
|
return $err;
|
||||||
|
|
|
@ -41,9 +41,10 @@ final class DiffusionPushLogListView extends AphrontView {
|
||||||
$any_host = false;
|
$any_host = false;
|
||||||
foreach ($logs as $log) {
|
foreach ($logs as $log) {
|
||||||
$repository = $log->getRepository();
|
$repository = $log->getRepository();
|
||||||
|
$event = $log->getPushEvent();
|
||||||
|
|
||||||
if ($remotes_visible) {
|
if ($remotes_visible) {
|
||||||
$remote_address = $log->getPushEvent()->getRemoteAddress();
|
$remote_address = $event->getRemoteAddress();
|
||||||
} else {
|
} else {
|
||||||
$remote_address = null;
|
$remote_address = null;
|
||||||
}
|
}
|
||||||
|
@ -79,10 +80,10 @@ final class DiffusionPushLogListView extends AphrontView {
|
||||||
phutil_tag('br'),
|
phutil_tag('br'),
|
||||||
$flag_names);
|
$flag_names);
|
||||||
|
|
||||||
$reject_code = $log->getPushEvent()->getRejectCode();
|
$reject_code = $event->getRejectCode();
|
||||||
|
|
||||||
if ($reject_code == $reject_herald) {
|
if ($reject_code == $reject_herald) {
|
||||||
$rule_phid = $log->getPushEvent()->getRejectDetails();
|
$rule_phid = $event->getRejectDetails();
|
||||||
$handle = $viewer->renderHandle($rule_phid);
|
$handle = $viewer->renderHandle($rule_phid);
|
||||||
$reject_label = pht('Blocked: %s', $handle);
|
$reject_label = pht('Blocked: %s', $handle);
|
||||||
} else {
|
} else {
|
||||||
|
@ -92,6 +93,11 @@ final class DiffusionPushLogListView extends AphrontView {
|
||||||
pht('Unknown ("%s")', $reject_code));
|
pht('Unknown ("%s")', $reject_code));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$host_wait = $this->formatMicroseconds($event->getHostWait());
|
||||||
|
$write_wait = $this->formatMicroseconds($event->getWriteWait());
|
||||||
|
$read_wait = $this->formatMicroseconds($event->getReadWait());
|
||||||
|
$hook_wait = $this->formatMicroseconds($event->getHookWait());
|
||||||
|
|
||||||
$rows[] = array(
|
$rows[] = array(
|
||||||
phutil_tag(
|
phutil_tag(
|
||||||
'a',
|
'a',
|
||||||
|
@ -107,7 +113,7 @@ final class DiffusionPushLogListView extends AphrontView {
|
||||||
$repository->getDisplayName()),
|
$repository->getDisplayName()),
|
||||||
$viewer->renderHandle($log->getPusherPHID()),
|
$viewer->renderHandle($log->getPusherPHID()),
|
||||||
$remote_address,
|
$remote_address,
|
||||||
$log->getPushEvent()->getRemoteProtocol(),
|
$event->getRemoteProtocol(),
|
||||||
$device,
|
$device,
|
||||||
$log->getRefType(),
|
$log->getRefType(),
|
||||||
$log->getRefName(),
|
$log->getRefName(),
|
||||||
|
@ -121,6 +127,10 @@ final class DiffusionPushLogListView extends AphrontView {
|
||||||
$flag_names,
|
$flag_names,
|
||||||
$reject_label,
|
$reject_label,
|
||||||
$viewer->formatShortDateTime($log->getEpoch()),
|
$viewer->formatShortDateTime($log->getEpoch()),
|
||||||
|
$host_wait,
|
||||||
|
$write_wait,
|
||||||
|
$read_wait,
|
||||||
|
$hook_wait,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -140,6 +150,10 @@ final class DiffusionPushLogListView extends AphrontView {
|
||||||
pht('Flags'),
|
pht('Flags'),
|
||||||
pht('Result'),
|
pht('Result'),
|
||||||
pht('Date'),
|
pht('Date'),
|
||||||
|
pht('Host Wait'),
|
||||||
|
pht('Write Wait'),
|
||||||
|
pht('Read Wait'),
|
||||||
|
pht('Hook Wait'),
|
||||||
))
|
))
|
||||||
->setColumnClasses(
|
->setColumnClasses(
|
||||||
array(
|
array(
|
||||||
|
@ -156,6 +170,10 @@ final class DiffusionPushLogListView extends AphrontView {
|
||||||
'',
|
'',
|
||||||
'',
|
'',
|
||||||
'right',
|
'right',
|
||||||
|
'n right',
|
||||||
|
'n right',
|
||||||
|
'n right',
|
||||||
|
'n right',
|
||||||
))
|
))
|
||||||
->setColumnVisibility(
|
->setColumnVisibility(
|
||||||
array(
|
array(
|
||||||
|
@ -170,4 +188,12 @@ final class DiffusionPushLogListView extends AphrontView {
|
||||||
return $table;
|
return $table;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private function formatMicroseconds($duration) {
|
||||||
|
if ($duration === null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return pht('%sus', new PhutilNumber($duration));
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
79
src/applications/diffusion/view/DiffusionSyncLogListView.php
Normal file
79
src/applications/diffusion/view/DiffusionSyncLogListView.php
Normal file
|
@ -0,0 +1,79 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
final class DiffusionSyncLogListView extends AphrontView {
|
||||||
|
|
||||||
|
private $logs;
|
||||||
|
|
||||||
|
public function setLogs(array $logs) {
|
||||||
|
assert_instances_of($logs, 'PhabricatorRepositorySyncEvent');
|
||||||
|
$this->logs = $logs;
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function render() {
|
||||||
|
$events = $this->logs;
|
||||||
|
$viewer = $this->getViewer();
|
||||||
|
|
||||||
|
$rows = array();
|
||||||
|
foreach ($events as $event) {
|
||||||
|
$repository = $event->getRepository();
|
||||||
|
$repository_link = phutil_tag(
|
||||||
|
'a',
|
||||||
|
array(
|
||||||
|
'href' => $repository->getURI(),
|
||||||
|
),
|
||||||
|
$repository->getDisplayName());
|
||||||
|
|
||||||
|
$event_id = $event->getID();
|
||||||
|
|
||||||
|
$sync_wait = pht('%sus', new PhutilNumber($event->getSyncWait()));
|
||||||
|
|
||||||
|
$device_link = $viewer->renderHandle($event->getDevicePHID());
|
||||||
|
$from_device_link = $viewer->renderHandle($event->getFromDevicePHID());
|
||||||
|
|
||||||
|
$rows[] = array(
|
||||||
|
$event_id,
|
||||||
|
$repository_link,
|
||||||
|
$device_link,
|
||||||
|
$from_device_link,
|
||||||
|
$event->getDeviceVersion(),
|
||||||
|
$event->getFromDeviceVersion(),
|
||||||
|
$event->getResultType(),
|
||||||
|
$event->getResultCode(),
|
||||||
|
phabricator_datetime($event->getEpoch(), $viewer),
|
||||||
|
$sync_wait,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
$table = id(new AphrontTableView($rows))
|
||||||
|
->setHeaders(
|
||||||
|
array(
|
||||||
|
pht('Sync'),
|
||||||
|
pht('Repository'),
|
||||||
|
pht('Device'),
|
||||||
|
pht('From Device'),
|
||||||
|
pht('Version'),
|
||||||
|
pht('From Version'),
|
||||||
|
pht('Result'),
|
||||||
|
pht('Code'),
|
||||||
|
pht('Date'),
|
||||||
|
pht('Sync Wait'),
|
||||||
|
))
|
||||||
|
->setColumnClasses(
|
||||||
|
array(
|
||||||
|
'n',
|
||||||
|
'',
|
||||||
|
'',
|
||||||
|
'',
|
||||||
|
'n',
|
||||||
|
'n',
|
||||||
|
'wide right',
|
||||||
|
'n',
|
||||||
|
'right',
|
||||||
|
'n right',
|
||||||
|
));
|
||||||
|
|
||||||
|
return $table;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -30,8 +30,8 @@ final class DrydockSSHCommandInterface extends DrydockCommandInterface {
|
||||||
$full_command = call_user_func_array('csprintf', $argv);
|
$full_command = call_user_func_array('csprintf', $argv);
|
||||||
|
|
||||||
$flags = array();
|
$flags = array();
|
||||||
$flags[] = '-o';
|
// $flags[] = '-o';
|
||||||
$flags[] = 'LogLevel=quiet';
|
// $flags[] = 'LogLevel=quiet';
|
||||||
|
|
||||||
$flags[] = '-o';
|
$flags[] = '-o';
|
||||||
$flags[] = 'StrictHostKeyChecking=no';
|
$flags[] = 'StrictHostKeyChecking=no';
|
||||||
|
|
|
@ -174,6 +174,15 @@ final class PhabricatorMemeEngine extends Phobject {
|
||||||
private function newAssetData(PhabricatorFile $template) {
|
private function newAssetData(PhabricatorFile $template) {
|
||||||
$template_data = $template->loadFileData();
|
$template_data = $template->loadFileData();
|
||||||
|
|
||||||
|
// When we aren't adding text, just return the data unmodified. This saves
|
||||||
|
// us from doing expensive stitching when we aren't actually making any
|
||||||
|
// changes to the image.
|
||||||
|
$above_text = $this->getAboveText();
|
||||||
|
$below_text = $this->getBelowText();
|
||||||
|
if (!strlen(trim($above_text)) && !strlen(trim($below_text))) {
|
||||||
|
return $template_data;
|
||||||
|
}
|
||||||
|
|
||||||
$result = $this->newImagemagickAsset($template, $template_data);
|
$result = $this->newImagemagickAsset($template, $template_data);
|
||||||
if ($result) {
|
if ($result) {
|
||||||
return $result;
|
return $result;
|
||||||
|
|
|
@ -290,7 +290,7 @@ final class ManiphestTransactionEditor
|
||||||
$copy->setOwnerPHID($xaction->getNewValue());
|
$copy->setOwnerPHID($xaction->getNewValue());
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
continue;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1665,5 +1665,23 @@ final class PhabricatorUser
|
||||||
return new PhutilOpaqueEnvelope($digest);
|
return new PhutilOpaqueEnvelope($digest);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function newPasswordBlocklist(
|
||||||
|
PhabricatorUser $viewer,
|
||||||
|
PhabricatorAuthPasswordEngine $engine) {
|
||||||
|
|
||||||
|
$list = array();
|
||||||
|
$list[] = $this->getUsername();
|
||||||
|
$list[] = $this->getRealName();
|
||||||
|
|
||||||
|
$emails = id(new PhabricatorUserEmail())->loadAllWhere(
|
||||||
|
'userPHID = %s',
|
||||||
|
$this->getPHID());
|
||||||
|
foreach ($emails as $email) {
|
||||||
|
$list[] = $email->getAddress();
|
||||||
|
}
|
||||||
|
|
||||||
|
return $list;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -120,33 +120,71 @@ final class PhabricatorRepositoryManagementThawWorkflow
|
||||||
$repository->getDisplayName()));
|
$repository->getDisplayName()));
|
||||||
}
|
}
|
||||||
|
|
||||||
$bindings = $service->getActiveBindings();
|
|
||||||
$bindings = mpull($bindings, null, 'getDevicePHID');
|
|
||||||
if (empty($bindings[$device->getPHID()])) {
|
|
||||||
throw new PhutilArgumentUsageException(
|
|
||||||
pht(
|
|
||||||
'Repository "%s" has no active binding to device "%s". Only '.
|
|
||||||
'actively bound devices can be promoted or demoted.',
|
|
||||||
$repository->getDisplayName(),
|
|
||||||
$device->getName()));
|
|
||||||
}
|
|
||||||
|
|
||||||
$versions = PhabricatorRepositoryWorkingCopyVersion::loadVersions(
|
|
||||||
$repository->getPHID());
|
|
||||||
|
|
||||||
$versions = mpull($versions, null, 'getDevicePHID');
|
|
||||||
$versions = array_select_keys($versions, array_keys($bindings));
|
|
||||||
|
|
||||||
if ($versions && $promote) {
|
|
||||||
throw new PhutilArgumentUsageException(
|
|
||||||
pht(
|
|
||||||
'Unable to promote "%s" for repository "%s": the leaders for '.
|
|
||||||
'this cluster are not ambiguous.',
|
|
||||||
$device->getName(),
|
|
||||||
$repository->getDisplayName()));
|
|
||||||
}
|
|
||||||
|
|
||||||
if ($promote) {
|
if ($promote) {
|
||||||
|
// You can only promote active devices. (You may demote active or
|
||||||
|
// inactive devices.)
|
||||||
|
$bindings = $service->getActiveBindings();
|
||||||
|
$bindings = mpull($bindings, null, 'getDevicePHID');
|
||||||
|
if (empty($bindings[$device->getPHID()])) {
|
||||||
|
throw new PhutilArgumentUsageException(
|
||||||
|
pht(
|
||||||
|
'Repository "%s" has no active binding to device "%s". Only '.
|
||||||
|
'actively bound devices can be promoted.',
|
||||||
|
$repository->getDisplayName(),
|
||||||
|
$device->getName()));
|
||||||
|
}
|
||||||
|
|
||||||
|
$versions = PhabricatorRepositoryWorkingCopyVersion::loadVersions(
|
||||||
|
$repository->getPHID());
|
||||||
|
$versions = mpull($versions, null, 'getDevicePHID');
|
||||||
|
|
||||||
|
// Before we promote, make sure there are no outstanding versions on
|
||||||
|
// devices with inactive bindings. If there are, you need to demote
|
||||||
|
// these first.
|
||||||
|
$inactive = array();
|
||||||
|
foreach ($versions as $device_phid => $version) {
|
||||||
|
if (isset($bindings[$device_phid])) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
$inactive[$device_phid] = $version;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($inactive) {
|
||||||
|
$handles = $viewer->loadHandles(array_keys($inactive));
|
||||||
|
|
||||||
|
$handle_list = iterator_to_array($handles);
|
||||||
|
$handle_list = mpull($handle_list, 'getName');
|
||||||
|
$handle_list = implode(', ', $handle_list);
|
||||||
|
|
||||||
|
throw new PhutilArgumentUsageException(
|
||||||
|
pht(
|
||||||
|
'Repository "%s" has versions on inactive devices. Demote '.
|
||||||
|
'(or reactivate) these devices before promoting a new '.
|
||||||
|
'leader: %s.',
|
||||||
|
$repository->getDisplayName(),
|
||||||
|
$handle_list));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Now, make sure there are no outstanding versions on devices with
|
||||||
|
// active bindings. These also need to be demoted (or promoting is a
|
||||||
|
// mistake or already happened).
|
||||||
|
$active = array_select_keys($versions, array_keys($bindings));
|
||||||
|
if ($active) {
|
||||||
|
$handles = $viewer->loadHandles(array_keys($active));
|
||||||
|
|
||||||
|
$handle_list = iterator_to_array($handles);
|
||||||
|
$handle_list = mpull($handle_list, 'getName');
|
||||||
|
$handle_list = implode(', ', $handle_list);
|
||||||
|
|
||||||
|
throw new PhutilArgumentUsageException(
|
||||||
|
pht(
|
||||||
|
'Unable to promote "%s" for repository "%s" because this '.
|
||||||
|
'cluster already has one or more unambiguous leaders: %s.',
|
||||||
|
$device->getName(),
|
||||||
|
$repository->getDisplayName(),
|
||||||
|
$handle_list));
|
||||||
|
}
|
||||||
|
|
||||||
PhabricatorRepositoryWorkingCopyVersion::updateVersion(
|
PhabricatorRepositoryWorkingCopyVersion::updateVersion(
|
||||||
$repository->getPHID(),
|
$repository->getPHID(),
|
||||||
$device->getPHID(),
|
$device->getPHID(),
|
||||||
|
|
|
@ -0,0 +1,39 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
final class PhabricatorRepositorySyncEventPHIDType extends PhabricatorPHIDType {
|
||||||
|
|
||||||
|
const TYPECONST = 'SYNE';
|
||||||
|
|
||||||
|
public function getTypeName() {
|
||||||
|
return pht('Sync Event');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function newObject() {
|
||||||
|
return new PhabricatorRepositorySyncEvent();
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getPHIDTypeApplicationClass() {
|
||||||
|
return 'PhabricatorDiffusionApplication';
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function buildQueryForObjects(
|
||||||
|
PhabricatorObjectQuery $query,
|
||||||
|
array $phids) {
|
||||||
|
|
||||||
|
return id(new PhabricatorRepositorySyncEventQuery())
|
||||||
|
->withPHIDs($phids);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function loadHandles(
|
||||||
|
PhabricatorHandleQuery $query,
|
||||||
|
array $handles,
|
||||||
|
array $objects) {
|
||||||
|
|
||||||
|
foreach ($handles as $phid => $handle) {
|
||||||
|
$event = $objects[$phid];
|
||||||
|
|
||||||
|
$handle->setName(pht('Sync Event %d', $event->getID()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -162,6 +162,9 @@ final class PhabricatorRepositoryPushLogSearchEngine
|
||||||
id(new PhabricatorStringExportField())
|
id(new PhabricatorStringExportField())
|
||||||
->setKey('resultDetails')
|
->setKey('resultDetails')
|
||||||
->setLabel(pht('Result Details')),
|
->setLabel(pht('Result Details')),
|
||||||
|
id(new PhabricatorIntExportField())
|
||||||
|
->setKey('hostWait')
|
||||||
|
->setLabel(pht('Host Wait (us)')),
|
||||||
id(new PhabricatorIntExportField())
|
id(new PhabricatorIntExportField())
|
||||||
->setKey('writeWait')
|
->setKey('writeWait')
|
||||||
->setLabel(pht('Write Wait (us)')),
|
->setLabel(pht('Write Wait (us)')),
|
||||||
|
@ -169,8 +172,8 @@ final class PhabricatorRepositoryPushLogSearchEngine
|
||||||
->setKey('readWait')
|
->setKey('readWait')
|
||||||
->setLabel(pht('Read Wait (us)')),
|
->setLabel(pht('Read Wait (us)')),
|
||||||
id(new PhabricatorIntExportField())
|
id(new PhabricatorIntExportField())
|
||||||
->setKey('hostWait')
|
->setKey('hookWait')
|
||||||
->setLabel(pht('Host Wait (us)')),
|
->setLabel(pht('Hook Wait (us)')),
|
||||||
);
|
);
|
||||||
|
|
||||||
if ($viewer->getIsAdmin()) {
|
if ($viewer->getIsAdmin()) {
|
||||||
|
@ -251,9 +254,10 @@ final class PhabricatorRepositoryPushLogSearchEngine
|
||||||
'result' => $result,
|
'result' => $result,
|
||||||
'resultName' => $result_name,
|
'resultName' => $result_name,
|
||||||
'resultDetails' => $event->getRejectDetails(),
|
'resultDetails' => $event->getRejectDetails(),
|
||||||
|
'hostWait' => $event->getHostWait(),
|
||||||
'writeWait' => $event->getWriteWait(),
|
'writeWait' => $event->getWriteWait(),
|
||||||
'readWait' => $event->getReadWait(),
|
'readWait' => $event->getReadWait(),
|
||||||
'hostWait' => $event->getHostWait(),
|
'hookWait' => $event->getHookWait(),
|
||||||
);
|
);
|
||||||
|
|
||||||
if ($viewer->getIsAdmin()) {
|
if ($viewer->getIsAdmin()) {
|
||||||
|
|
|
@ -0,0 +1,115 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
final class PhabricatorRepositorySyncEventQuery
|
||||||
|
extends PhabricatorCursorPagedPolicyAwareQuery {
|
||||||
|
|
||||||
|
private $ids;
|
||||||
|
private $phids;
|
||||||
|
private $repositoryPHIDs;
|
||||||
|
private $epochMin;
|
||||||
|
private $epochMax;
|
||||||
|
|
||||||
|
public function withIDs(array $ids) {
|
||||||
|
$this->ids = $ids;
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function withPHIDs(array $phids) {
|
||||||
|
$this->phids = $phids;
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function withRepositoryPHIDs(array $repository_phids) {
|
||||||
|
$this->repositoryPHIDs = $repository_phids;
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function withEpochBetween($min, $max) {
|
||||||
|
$this->epochMin = $min;
|
||||||
|
$this->epochMax = $max;
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function newResultObject() {
|
||||||
|
return new PhabricatorRepositorySyncEvent();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function loadPage() {
|
||||||
|
return $this->loadStandardPage($this->newResultObject());
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function willFilterPage(array $events) {
|
||||||
|
$repository_phids = mpull($events, 'getRepositoryPHID');
|
||||||
|
$repository_phids = array_filter($repository_phids);
|
||||||
|
|
||||||
|
if ($repository_phids) {
|
||||||
|
$repositories = id(new PhabricatorRepositoryQuery())
|
||||||
|
->setViewer($this->getViewer())
|
||||||
|
->withPHIDs($repository_phids)
|
||||||
|
->execute();
|
||||||
|
$repositories = mpull($repositories, null, 'getPHID');
|
||||||
|
} else {
|
||||||
|
$repositories = array();
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach ($events as $key => $event) {
|
||||||
|
$phid = $event->getRepositoryPHID();
|
||||||
|
|
||||||
|
if (empty($repositories[$phid])) {
|
||||||
|
unset($events[$key]);
|
||||||
|
$this->didRejectResult($event);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
$event->attachRepository($repositories[$phid]);
|
||||||
|
}
|
||||||
|
|
||||||
|
return $events;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function buildWhereClauseParts(AphrontDatabaseConnection $conn) {
|
||||||
|
$where = parent::buildWhereClauseParts($conn);
|
||||||
|
|
||||||
|
if ($this->ids !== null) {
|
||||||
|
$where[] = qsprintf(
|
||||||
|
$conn,
|
||||||
|
'id IN (%Ld)',
|
||||||
|
$this->ids);
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($this->phids !== null) {
|
||||||
|
$where[] = qsprintf(
|
||||||
|
$conn,
|
||||||
|
'phid IN (%Ls)',
|
||||||
|
$this->phids);
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($this->repositoryPHIDs !== null) {
|
||||||
|
$where[] = qsprintf(
|
||||||
|
$conn,
|
||||||
|
'repositoryPHID IN (%Ls)',
|
||||||
|
$this->repositoryPHIDs);
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($this->epochMin !== null) {
|
||||||
|
$where[] = qsprintf(
|
||||||
|
$conn,
|
||||||
|
'epoch >= %d',
|
||||||
|
$this->epochMin);
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($this->epochMax !== null) {
|
||||||
|
$where[] = qsprintf(
|
||||||
|
$conn,
|
||||||
|
'epoch <= %d',
|
||||||
|
$this->epochMax);
|
||||||
|
}
|
||||||
|
|
||||||
|
return $where;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getQueryApplicationClass() {
|
||||||
|
return 'PhabricatorDiffusionApplication';
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -19,6 +19,7 @@ final class PhabricatorRepositoryPushEvent
|
||||||
protected $writeWait;
|
protected $writeWait;
|
||||||
protected $readWait;
|
protected $readWait;
|
||||||
protected $hostWait;
|
protected $hostWait;
|
||||||
|
protected $hookWait;
|
||||||
|
|
||||||
private $repository = self::ATTACHABLE;
|
private $repository = self::ATTACHABLE;
|
||||||
private $logs = self::ATTACHABLE;
|
private $logs = self::ATTACHABLE;
|
||||||
|
@ -41,6 +42,7 @@ final class PhabricatorRepositoryPushEvent
|
||||||
'writeWait' => 'uint64?',
|
'writeWait' => 'uint64?',
|
||||||
'readWait' => 'uint64?',
|
'readWait' => 'uint64?',
|
||||||
'hostWait' => 'uint64?',
|
'hostWait' => 'uint64?',
|
||||||
|
'hookWait' => 'uint64?',
|
||||||
),
|
),
|
||||||
self::CONFIG_KEY_SCHEMA => array(
|
self::CONFIG_KEY_SCHEMA => array(
|
||||||
'key_repository' => array(
|
'key_repository' => array(
|
||||||
|
|
|
@ -0,0 +1,99 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
final class PhabricatorRepositorySyncEvent
|
||||||
|
extends PhabricatorRepositoryDAO
|
||||||
|
implements PhabricatorPolicyInterface {
|
||||||
|
|
||||||
|
protected $repositoryPHID;
|
||||||
|
protected $epoch;
|
||||||
|
protected $devicePHID;
|
||||||
|
protected $fromDevicePHID;
|
||||||
|
protected $deviceVersion;
|
||||||
|
protected $fromDeviceVersion;
|
||||||
|
protected $resultType;
|
||||||
|
protected $resultCode;
|
||||||
|
protected $syncWait;
|
||||||
|
protected $properties = array();
|
||||||
|
|
||||||
|
private $repository = self::ATTACHABLE;
|
||||||
|
|
||||||
|
const RESULT_SYNC = 'sync';
|
||||||
|
const RESULT_ERROR = 'error';
|
||||||
|
const RESULT_TIMEOUT = 'timeout';
|
||||||
|
const RESULT_EXCEPTION = 'exception';
|
||||||
|
|
||||||
|
public static function initializeNewEvent() {
|
||||||
|
return new self();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function getConfiguration() {
|
||||||
|
return array(
|
||||||
|
self::CONFIG_AUX_PHID => true,
|
||||||
|
self::CONFIG_TIMESTAMPS => false,
|
||||||
|
self::CONFIG_SERIALIZATION => array(
|
||||||
|
'properties' => self::SERIALIZATION_JSON,
|
||||||
|
),
|
||||||
|
self::CONFIG_COLUMN_SCHEMA => array(
|
||||||
|
'deviceVersion' => 'uint32?',
|
||||||
|
'fromDeviceVersion' => 'uint32?',
|
||||||
|
'resultType' => 'text32',
|
||||||
|
'resultCode' => 'uint32',
|
||||||
|
'syncWait' => 'uint64',
|
||||||
|
),
|
||||||
|
self::CONFIG_KEY_SCHEMA => array(
|
||||||
|
'key_repository' => array(
|
||||||
|
'columns' => array('repositoryPHID'),
|
||||||
|
),
|
||||||
|
'key_epoch' => array(
|
||||||
|
'columns' => array('epoch'),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
) + parent::getConfiguration();
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getPHIDType() {
|
||||||
|
return PhabricatorRepositorySyncEventPHIDType::TYPECONST;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function attachRepository(PhabricatorRepository $repository) {
|
||||||
|
$this->repository = $repository;
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getRepository() {
|
||||||
|
return $this->assertAttached($this->repository);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function setProperty($key, $value) {
|
||||||
|
$this->properties[$key] = $value;
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getProperty($key, $default = null) {
|
||||||
|
return idx($this->properties, $key, $default);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* -( PhabricatorPolicyInterface )----------------------------------------- */
|
||||||
|
|
||||||
|
|
||||||
|
public function getCapabilities() {
|
||||||
|
return array(
|
||||||
|
PhabricatorPolicyCapability::CAN_VIEW,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getPolicy($capability) {
|
||||||
|
return $this->getRepository()->getPolicy($capability);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function hasAutomaticCapability($capability, PhabricatorUser $viewer) {
|
||||||
|
return $this->getRepository()->hasAutomaticCapability($capability, $viewer);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function describeAutomaticCapability($capability) {
|
||||||
|
return pht(
|
||||||
|
"A repository's sync events are visible to users who can see the ".
|
||||||
|
"repository.");
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -7,6 +7,10 @@ final class PhabricatorSlowvoteVoteController
|
||||||
$viewer = $request->getViewer();
|
$viewer = $request->getViewer();
|
||||||
$id = $request->getURIData('id');
|
$id = $request->getURIData('id');
|
||||||
|
|
||||||
|
if (!$request->isFormPost()) {
|
||||||
|
return id(new Aphront404Response());
|
||||||
|
}
|
||||||
|
|
||||||
$poll = id(new PhabricatorSlowvoteQuery())
|
$poll = id(new PhabricatorSlowvoteQuery())
|
||||||
->setViewer($viewer)
|
->setViewer($viewer)
|
||||||
->withIDs(array($id))
|
->withIDs(array($id))
|
||||||
|
@ -16,69 +20,36 @@ final class PhabricatorSlowvoteVoteController
|
||||||
if (!$poll) {
|
if (!$poll) {
|
||||||
return new Aphront404Response();
|
return new Aphront404Response();
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($poll->getIsClosed()) {
|
if ($poll->getIsClosed()) {
|
||||||
return new Aphront400Response();
|
return new Aphront400Response();
|
||||||
}
|
}
|
||||||
|
|
||||||
$options = $poll->getOptions();
|
$options = $poll->getOptions();
|
||||||
$viewer_choices = $poll->getViewerChoices($viewer);
|
$options = mpull($options, null, 'getID');
|
||||||
|
|
||||||
$old_votes = mpull($viewer_choices, null, 'getOptionID');
|
$old_votes = $poll->getViewerChoices($viewer);
|
||||||
|
$old_votes = mpull($old_votes, null, 'getOptionID');
|
||||||
if ($request->isAjax()) {
|
|
||||||
$vote = $request->getInt('vote');
|
|
||||||
$votes = array_keys($old_votes);
|
|
||||||
$votes = array_fuse($votes);
|
|
||||||
|
|
||||||
if ($poll->getMethod() == PhabricatorSlowvotePoll::METHOD_PLURALITY) {
|
|
||||||
if (idx($votes, $vote, false)) {
|
|
||||||
$votes = array();
|
|
||||||
} else {
|
|
||||||
$votes = array($vote);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if (idx($votes, $vote, false)) {
|
|
||||||
unset($votes[$vote]);
|
|
||||||
} else {
|
|
||||||
$votes[$vote] = $vote;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
$this->updateVotes($viewer, $poll, $old_votes, $votes);
|
|
||||||
|
|
||||||
$updated_choices = id(new PhabricatorSlowvoteChoice())->loadAllWhere(
|
|
||||||
'pollID = %d AND authorPHID = %s',
|
|
||||||
$poll->getID(),
|
|
||||||
$viewer->getPHID());
|
|
||||||
|
|
||||||
$embed = id(new SlowvoteEmbedView())
|
|
||||||
->setPoll($poll)
|
|
||||||
->setOptions($options)
|
|
||||||
->setViewerChoices($updated_choices);
|
|
||||||
|
|
||||||
return id(new AphrontAjaxResponse())
|
|
||||||
->setContent(array(
|
|
||||||
'pollID' => $poll->getID(),
|
|
||||||
'contentHTML' => $embed->render(),
|
|
||||||
));
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!$request->isFormPost()) {
|
|
||||||
return id(new Aphront404Response());
|
|
||||||
}
|
|
||||||
|
|
||||||
$votes = $request->getArr('vote');
|
$votes = $request->getArr('vote');
|
||||||
$votes = array_fuse($votes);
|
$votes = array_fuse($votes);
|
||||||
|
|
||||||
$this->updateVotes($viewer, $poll, $old_votes, $votes);
|
$method = $poll->getMethod();
|
||||||
|
$is_plurality = ($method == PhabricatorSlowvotePoll::METHOD_PLURALITY);
|
||||||
|
|
||||||
return id(new AphrontRedirectResponse())->setURI('/V'.$poll->getID());
|
if ($is_plurality && count($votes) > 1) {
|
||||||
}
|
throw new Exception(
|
||||||
|
pht('In this poll, you may only vote for one option.'));
|
||||||
|
}
|
||||||
|
|
||||||
private function updateVotes($viewer, $poll, $old_votes, $votes) {
|
foreach ($votes as $vote) {
|
||||||
if (!empty($votes) && count($votes) > 1 &&
|
if (!isset($options[$vote])) {
|
||||||
$poll->getMethod() == PhabricatorSlowvotePoll::METHOD_PLURALITY) {
|
throw new Exception(
|
||||||
return id(new Aphront400Response());
|
pht(
|
||||||
|
'Option ("%s") is not a valid poll option. You may only '.
|
||||||
|
'vote for valid options.',
|
||||||
|
$vote));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
foreach ($old_votes as $old_vote) {
|
foreach ($old_votes as $old_vote) {
|
||||||
|
@ -98,6 +69,9 @@ final class PhabricatorSlowvoteVoteController
|
||||||
->setOptionID($vote)
|
->setOptionID($vote)
|
||||||
->save();
|
->save();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return id(new AphrontRedirectResponse())
|
||||||
|
->setURI($poll->getURI());
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -39,12 +39,6 @@ final class SlowvoteEmbedView extends AphrontView {
|
||||||
}
|
}
|
||||||
|
|
||||||
require_celerity_resource('phabricator-slowvote-css');
|
require_celerity_resource('phabricator-slowvote-css');
|
||||||
require_celerity_resource('javelin-behavior-slowvote-embed');
|
|
||||||
|
|
||||||
$config = array(
|
|
||||||
'pollID' => $poll->getID(),
|
|
||||||
);
|
|
||||||
Javelin::initBehavior('slowvote-embed', $config);
|
|
||||||
|
|
||||||
$user_choices = $poll->getViewerChoices($this->getUser());
|
$user_choices = $poll->getViewerChoices($this->getUser());
|
||||||
$user_choices = mpull($user_choices, 'getOptionID', 'getOptionID');
|
$user_choices = mpull($user_choices, 'getOptionID', 'getOptionID');
|
||||||
|
|
|
@ -414,28 +414,24 @@ present on the leaders but not present on the followers by examining the
|
||||||
push logs.
|
push logs.
|
||||||
|
|
||||||
If you are comfortable discarding these changes, you can instruct Phabricator
|
If you are comfortable discarding these changes, you can instruct Phabricator
|
||||||
that it can forget about the leaders in two ways: disable the service bindings
|
that it can forget about the leaders by doing this:
|
||||||
to all of the leader devices so they are no longer part of the cluster, or use
|
|
||||||
`bin/repository thaw` to `--demote` the leaders explicitly.
|
|
||||||
|
|
||||||
If you do this, **you will lose data**. Either action will discard any changes
|
- Disable the service bindings to all of the leader devices so they are no
|
||||||
on the affected leaders which have not replicated to other devices in the
|
longer part of the cluster.
|
||||||
cluster.
|
- Then, use `bin/repository thaw` to `--demote` the leaders explicitly.
|
||||||
|
|
||||||
To remove a device from the cluster, disable all of the bindings to it
|
To demote a device, run this command:
|
||||||
in Almanac, using the web UI.
|
|
||||||
|
|
||||||
{icon exclamation-triangle, color="red"} Any data which is only present on
|
|
||||||
the disabled device will be lost.
|
|
||||||
|
|
||||||
To demote a device without removing it from the cluster, run this command:
|
|
||||||
|
|
||||||
```
|
```
|
||||||
phabricator/ $ ./bin/repository thaw rXYZ --demote repo002.corp.net
|
phabricator/ $ ./bin/repository thaw rXYZ --demote repo002.corp.net
|
||||||
```
|
```
|
||||||
|
|
||||||
{icon exclamation-triangle, color="red"} Any data which is only present on
|
{icon exclamation-triangle, color="red"} Any data which is only present on
|
||||||
**this** device will be lost.
|
the demoted device will be lost.
|
||||||
|
|
||||||
|
If you do this, **you will lose unreplicated data**. You will discard any
|
||||||
|
changes on the affected leaders which have not replicated to other devices
|
||||||
|
in the cluster.
|
||||||
|
|
||||||
|
|
||||||
Ambiguous Leaders
|
Ambiguous Leaders
|
||||||
|
|
|
@ -133,7 +133,7 @@ tricky because of shell escaping. The easiest way to do it is to use the
|
||||||
Then set the value like this:
|
Then set the value like this:
|
||||||
|
|
||||||
```
|
```
|
||||||
phabricator/ $ ./bin/config set --stdin < mailers.json
|
phabricator/ $ ./bin/config set --stdin cluster.mailers < mailers.json
|
||||||
```
|
```
|
||||||
|
|
||||||
For alternatives and more information on configuration, see
|
For alternatives and more information on configuration, see
|
||||||
|
|
|
@ -169,8 +169,7 @@ final class PhabricatorWorkerActiveTask extends PhabricatorWorkerTask {
|
||||||
|
|
||||||
$t_start = microtime(true);
|
$t_start = microtime(true);
|
||||||
$worker->executeTask();
|
$worker->executeTask();
|
||||||
$t_end = microtime(true);
|
$duration = phutil_microseconds_since($t_start);
|
||||||
$duration = (int)(1000000 * ($t_end - $t_start));
|
|
||||||
|
|
||||||
$result = $this->archiveTask(
|
$result = $this->archiveTask(
|
||||||
PhabricatorWorkerArchiveTask::RESULT_SUCCESS,
|
PhabricatorWorkerArchiveTask::RESULT_SUCCESS,
|
||||||
|
|
|
@ -3,23 +3,23 @@
|
||||||
final class QueryFormattingTestCase extends PhabricatorTestCase {
|
final class QueryFormattingTestCase extends PhabricatorTestCase {
|
||||||
|
|
||||||
public function testQueryFormatting() {
|
public function testQueryFormatting() {
|
||||||
$conn_r = id(new PhabricatorUser())->establishConnection('r');
|
$conn = id(new PhabricatorUser())->establishConnection('r');
|
||||||
|
|
||||||
$this->assertEqual(
|
$this->assertEqual(
|
||||||
'NULL',
|
'NULL',
|
||||||
qsprintf($conn_r, '%nd', null));
|
qsprintf($conn, '%nd', null));
|
||||||
|
|
||||||
$this->assertEqual(
|
$this->assertEqual(
|
||||||
'0',
|
'0',
|
||||||
qsprintf($conn_r, '%nd', 0));
|
qsprintf($conn, '%nd', 0));
|
||||||
|
|
||||||
$this->assertEqual(
|
$this->assertEqual(
|
||||||
'0',
|
'0',
|
||||||
qsprintf($conn_r, '%d', 0));
|
qsprintf($conn, '%d', 0));
|
||||||
|
|
||||||
$raised = null;
|
$raised = null;
|
||||||
try {
|
try {
|
||||||
qsprintf($conn_r, '%d', 'derp');
|
qsprintf($conn, '%d', 'derp');
|
||||||
} catch (Exception $ex) {
|
} catch (Exception $ex) {
|
||||||
$raised = $ex;
|
$raised = $ex;
|
||||||
}
|
}
|
||||||
|
@ -29,27 +29,40 @@ final class QueryFormattingTestCase extends PhabricatorTestCase {
|
||||||
|
|
||||||
$this->assertEqual(
|
$this->assertEqual(
|
||||||
"'<S>'",
|
"'<S>'",
|
||||||
qsprintf($conn_r, '%s', null));
|
qsprintf($conn, '%s', null));
|
||||||
|
|
||||||
$this->assertEqual(
|
$this->assertEqual(
|
||||||
'NULL',
|
'NULL',
|
||||||
qsprintf($conn_r, '%ns', null));
|
qsprintf($conn, '%ns', null));
|
||||||
|
|
||||||
$this->assertEqual(
|
$this->assertEqual(
|
||||||
"'<S>', '<S>'",
|
"'<S>', '<S>'",
|
||||||
qsprintf($conn_r, '%Ls', array('x', 'y')));
|
qsprintf($conn, '%Ls', array('x', 'y')));
|
||||||
|
|
||||||
$this->assertEqual(
|
$this->assertEqual(
|
||||||
"'<B>'",
|
"'<B>'",
|
||||||
qsprintf($conn_r, '%B', null));
|
qsprintf($conn, '%B', null));
|
||||||
|
|
||||||
$this->assertEqual(
|
$this->assertEqual(
|
||||||
'NULL',
|
'NULL',
|
||||||
qsprintf($conn_r, '%nB', null));
|
qsprintf($conn, '%nB', null));
|
||||||
|
|
||||||
$this->assertEqual(
|
$this->assertEqual(
|
||||||
"'<B>', '<B>'",
|
"'<B>', '<B>'",
|
||||||
qsprintf($conn_r, '%LB', array('x', 'y')));
|
qsprintf($conn, '%LB', array('x', 'y')));
|
||||||
|
|
||||||
|
$this->assertEqual(
|
||||||
|
'<C>',
|
||||||
|
qsprintf($conn, '%T', 'x'));
|
||||||
|
|
||||||
|
$this->assertEqual(
|
||||||
|
'<C>',
|
||||||
|
qsprintf($conn, '%C', 'y'));
|
||||||
|
|
||||||
|
$this->assertEqual(
|
||||||
|
'<C>.<C>',
|
||||||
|
qsprintf($conn, '%R', new AphrontDatabaseTableRef('x', 'y')));
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -162,7 +162,8 @@
|
||||||
* @task xaction Managing Transactions
|
* @task xaction Managing Transactions
|
||||||
* @task isolate Isolation for Unit Testing
|
* @task isolate Isolation for Unit Testing
|
||||||
*/
|
*/
|
||||||
abstract class LiskDAO extends Phobject {
|
abstract class LiskDAO extends Phobject
|
||||||
|
implements AphrontDatabaseTableRefInterface {
|
||||||
|
|
||||||
const CONFIG_IDS = 'id-mechanism';
|
const CONFIG_IDS = 'id-mechanism';
|
||||||
const CONFIG_TIMESTAMPS = 'timestamps';
|
const CONFIG_TIMESTAMPS = 'timestamps';
|
||||||
|
@ -235,8 +236,11 @@ abstract class LiskDAO extends Phobject {
|
||||||
* @return string Connection namespace for cache
|
* @return string Connection namespace for cache
|
||||||
* @task conn
|
* @task conn
|
||||||
*/
|
*/
|
||||||
abstract protected function getConnectionNamespace();
|
protected function getConnectionNamespace() {
|
||||||
|
return $this->getDatabaseName();
|
||||||
|
}
|
||||||
|
|
||||||
|
abstract protected function getDatabaseName();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get an existing, cached connection for this object.
|
* Get an existing, cached connection for this object.
|
||||||
|
@ -525,8 +529,8 @@ abstract class LiskDAO extends Phobject {
|
||||||
$args = func_get_args();
|
$args = func_get_args();
|
||||||
$args = array_slice($args, 1);
|
$args = array_slice($args, 1);
|
||||||
|
|
||||||
$pattern = 'SELECT * FROM %T WHERE '.$pattern.' %Q';
|
$pattern = 'SELECT * FROM %R WHERE '.$pattern.' %Q';
|
||||||
array_unshift($args, $this->getTableName());
|
array_unshift($args, $this);
|
||||||
array_push($args, $lock_clause);
|
array_push($args, $lock_clause);
|
||||||
array_unshift($args, $pattern);
|
array_unshift($args, $pattern);
|
||||||
|
|
||||||
|
@ -1150,8 +1154,8 @@ abstract class LiskDAO extends Phobject {
|
||||||
|
|
||||||
$id = $this->getID();
|
$id = $this->getID();
|
||||||
$conn->query(
|
$conn->query(
|
||||||
'UPDATE %T SET %Q WHERE %C = '.(is_int($id) ? '%d' : '%s'),
|
'UPDATE %R SET %Q WHERE %C = '.(is_int($id) ? '%d' : '%s'),
|
||||||
$this->getTableName(),
|
$this,
|
||||||
$map,
|
$map,
|
||||||
$this->getIDKeyForUse(),
|
$this->getIDKeyForUse(),
|
||||||
$id);
|
$id);
|
||||||
|
@ -1178,8 +1182,8 @@ abstract class LiskDAO extends Phobject {
|
||||||
|
|
||||||
$conn = $this->establishConnection('w');
|
$conn = $this->establishConnection('w');
|
||||||
$conn->query(
|
$conn->query(
|
||||||
'DELETE FROM %T WHERE %C = %d',
|
'DELETE FROM %R WHERE %C = %d',
|
||||||
$this->getTableName(),
|
$this,
|
||||||
$this->getIDKeyForUse(),
|
$this->getIDKeyForUse(),
|
||||||
$this->getID());
|
$this->getID());
|
||||||
|
|
||||||
|
@ -1255,9 +1259,9 @@ abstract class LiskDAO extends Phobject {
|
||||||
$data = implode(', ', $data);
|
$data = implode(', ', $data);
|
||||||
|
|
||||||
$conn->query(
|
$conn->query(
|
||||||
'%Q INTO %T (%LC) VALUES (%Q)',
|
'%Q INTO %R (%LC) VALUES (%Q)',
|
||||||
$mode,
|
$mode,
|
||||||
$this->getTableName(),
|
$this,
|
||||||
$columns,
|
$columns,
|
||||||
$data);
|
$data);
|
||||||
|
|
||||||
|
@ -2019,4 +2023,17 @@ abstract class LiskDAO extends Phobject {
|
||||||
->getMaximumByteLengthForDataType($data_type);
|
->getMaximumByteLengthForDataType($data_type);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* -( AphrontDatabaseTableRefInterface )----------------------------------- */
|
||||||
|
|
||||||
|
|
||||||
|
public function getAphrontRefDatabaseName() {
|
||||||
|
return $this->getDatabaseName();
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getAphrontRefTableName() {
|
||||||
|
return $this->getTableName();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -187,11 +187,10 @@ abstract class PhabricatorLiskDAO extends LiskDAO {
|
||||||
*/
|
*/
|
||||||
abstract public function getApplicationName();
|
abstract public function getApplicationName();
|
||||||
|
|
||||||
protected function getConnectionNamespace() {
|
protected function getDatabaseName() {
|
||||||
return self::getStorageNamespace().'_'.$this->getApplicationName();
|
return self::getStorageNamespace().'_'.$this->getApplicationName();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Break a list of escaped SQL statement fragments (e.g., VALUES lists for
|
* Break a list of escaped SQL statement fragments (e.g., VALUES lists for
|
||||||
* INSERT, previously built with @{function:qsprintf}) into chunks which will
|
* INSERT, previously built with @{function:qsprintf}) into chunks which will
|
||||||
|
|
|
@ -22,7 +22,7 @@ final class LiskIsolationTestDAO extends LiskDAO {
|
||||||
'resource!'));
|
'resource!'));
|
||||||
}
|
}
|
||||||
|
|
||||||
protected function getConnectionNamespace() {
|
protected function getDatabaseName() {
|
||||||
return 'test';
|
return 'test';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -64,6 +64,8 @@ final class PhabricatorStartup {
|
||||||
* @task info
|
* @task info
|
||||||
*/
|
*/
|
||||||
public static function getMicrosecondsSinceStart() {
|
public static function getMicrosecondsSinceStart() {
|
||||||
|
// This is the same as "phutil_microseconds_since()", but we may not have
|
||||||
|
// loaded libphutil yet.
|
||||||
return (int)(1000000 * (microtime(true) - self::getStartTime()));
|
return (int)(1000000 * (microtime(true) - self::getStartTime()));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,43 +0,0 @@
|
||||||
/**
|
|
||||||
* @provides javelin-behavior-slowvote-embed
|
|
||||||
* @requires javelin-behavior
|
|
||||||
* javelin-request
|
|
||||||
* javelin-stratcom
|
|
||||||
* javelin-dom
|
|
||||||
*/
|
|
||||||
JX.behavior('slowvote-embed', function() {
|
|
||||||
JX.Stratcom.listen(
|
|
||||||
['click'],
|
|
||||||
'slowvote-option',
|
|
||||||
function(e) {
|
|
||||||
if (!e.isNormalMouseEvent()) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
e.kill();
|
|
||||||
|
|
||||||
var pollID = e.getNodeData('slowvote-embed').pollID;
|
|
||||||
var voteURI = '/vote/' + pollID + '/';
|
|
||||||
|
|
||||||
var request = new JX.Request(voteURI, function(r) {
|
|
||||||
var updated_poll = JX.$H(r.contentHTML);
|
|
||||||
var root = JX.$('phabricator-standard-page');
|
|
||||||
|
|
||||||
var polls = JX.DOM.scry(root, 'div', 'slowvote-embed');
|
|
||||||
|
|
||||||
for(var i = 0; i < polls.length; i++) {
|
|
||||||
var data = JX.Stratcom.getData(polls[i]);
|
|
||||||
|
|
||||||
if (data.pollID == pollID) {
|
|
||||||
JX.DOM.replace(polls[i], updated_poll);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
});
|
|
||||||
|
|
||||||
request.addData({vote: e.getNodeData('slowvote-option').optionID});
|
|
||||||
request.send();
|
|
||||||
|
|
||||||
});
|
|
||||||
|
|
||||||
});
|
|
Loading…
Add table
Reference in a new issue