From da40f807410634ac8220bfe47649cdabfe6a65f4 Mon Sep 17 00:00:00 2001 From: epriestley Date: Tue, 6 Nov 2018 16:57:55 -0800 Subject: [PATCH 01/43] Update PhabricatorLiskDAO::chunkSQL() for new %Q semantics Summary: Ref T13217. This method is slightly tricky: - We can't safely return a string: return an array instead. - It no longer makes sense to accept glue. All callers use `', '` as glue anyway, so hard-code that. Then convert all callsites. Test Plan: Browsed around, saw fewer "unsafe" errors in error log. Reviewers: amckinley Reviewed By: amckinley Subscribers: yelirekim, PHID-OPKG-gm6ozazyms6q6i22gyam Maniphest Tasks: T13217 Differential Revision: https://secure.phabricator.com/D19784 --- .../sql/autopatches/20140805.boardcol.2.php | 2 +- .../20130820.file-mailkey-populate.php | 4 +-- .../sql/patches/20131106.diffphid.2.mig.php | 4 +-- .../PhabricatorKeyValueDatabaseCache.php | 2 +- .../PhabricatorCalendarNotificationEngine.php | 2 +- ...iffusionUpdateCoverageConduitAPIMethod.php | 4 +-- .../publisher/DivinerLivePublisher.php | 4 +-- .../fact/daemon/PhabricatorFactDaemon.php | 2 +- .../fact/storage/PhabricatorFactDimension.php | 2 +- .../editor/ManiphestTransactionEditor.php | 2 +- .../people/storage/PhabricatorUserCache.php | 2 +- .../project/storage/PhabricatorProject.php | 2 +- ...torRepositoryManagementParentsWorkflow.php | 4 +-- ...atorRepositoryCommitChangeParserWorker.php | 2 +- ...abricatorFerretFulltextEngineExtension.php | 2 +- ...bricatorSearchManagementNgramsWorkflow.php | 2 +- .../engine/PhabricatorSystemActionEngine.php | 2 +- .../field/PhabricatorCustomFieldList.php | 2 +- .../__tests__/QueryFormattingTestCase.php | 24 +++++++------- .../storage/lisk/PhabricatorLiskDAO.php | 17 ++++------ .../lisk/__tests__/LiskChunkTestCase.php | 32 ++++++++++--------- 21 files changed, 59 insertions(+), 60 deletions(-) diff --git a/resources/sql/autopatches/20140805.boardcol.2.php b/resources/sql/autopatches/20140805.boardcol.2.php index 317de4e370..40d6c46ec2 100644 --- a/resources/sql/autopatches/20140805.boardcol.2.php +++ b/resources/sql/autopatches/20140805.boardcol.2.php @@ -45,7 +45,7 @@ foreach (PhabricatorLiskDAO::chunkSQL($sql) as $chunk) { queryfx( $conn_w, 'INSERT INTO %T (boardPHID, columnPHID, objectPHID, sequence) - VALUES %Q', + VALUES %LQ', id(new PhabricatorProjectColumnPosition())->getTableName(), $chunk); } diff --git a/resources/sql/patches/20130820.file-mailkey-populate.php b/resources/sql/patches/20130820.file-mailkey-populate.php index ba4d6d1606..0db10bef58 100644 --- a/resources/sql/patches/20130820.file-mailkey-populate.php +++ b/resources/sql/patches/20130820.file-mailkey-populate.php @@ -22,12 +22,12 @@ foreach (new LiskRawMigrationIterator($conn_w, 'file') as $row) { } if ($sql) { - foreach (PhabricatorLiskDAO::chunkSQL($sql, ', ') as $chunk) { + foreach (PhabricatorLiskDAO::chunkSQL($sql) as $chunk) { queryfx( $conn_w, 'INSERT INTO %T (id, mailKey, phid, byteSize, storageEngine, storageFormat, - storageHandle, dateCreated, dateModified, metadata) VALUES %Q '. + storageHandle, dateCreated, dateModified, metadata) VALUES %LQ '. 'ON DUPLICATE KEY UPDATE mailKey = VALUES(mailKey)', $table_name, $chunk); diff --git a/resources/sql/patches/20131106.diffphid.2.mig.php b/resources/sql/patches/20131106.diffphid.2.mig.php index 67fd14aad0..7976c910eb 100644 --- a/resources/sql/patches/20131106.diffphid.2.mig.php +++ b/resources/sql/patches/20131106.diffphid.2.mig.php @@ -34,10 +34,10 @@ foreach ($chunk_iter as $chunk) { continue; } - foreach (PhabricatorLiskDAO::chunkSQL($sql, ', ') as $sql_chunk) { + foreach (PhabricatorLiskDAO::chunkSQL($sql) as $sql_chunk) { queryfx( $conn_w, - 'INSERT IGNORE INTO %T (id, phid) VALUES %Q + 'INSERT IGNORE INTO %T (id, phid) VALUES %LQ ON DUPLICATE KEY UPDATE phid = VALUES(phid)', $diff_table->getTableName(), $sql_chunk); diff --git a/src/applications/cache/PhabricatorKeyValueDatabaseCache.php b/src/applications/cache/PhabricatorKeyValueDatabaseCache.php index c6a52024fe..0b4609074a 100644 --- a/src/applications/cache/PhabricatorKeyValueDatabaseCache.php +++ b/src/applications/cache/PhabricatorKeyValueDatabaseCache.php @@ -38,7 +38,7 @@ final class PhabricatorKeyValueDatabaseCache $conn_w, 'INSERT INTO %T (cacheKeyHash, cacheKey, cacheFormat, cacheData, - cacheCreated, cacheExpires) VALUES %Q + cacheCreated, cacheExpires) VALUES %LQ ON DUPLICATE KEY UPDATE cacheKey = VALUES(cacheKey), cacheFormat = VALUES(cacheFormat), diff --git a/src/applications/calendar/notifications/PhabricatorCalendarNotificationEngine.php b/src/applications/calendar/notifications/PhabricatorCalendarNotificationEngine.php index 59d8476c87..cf7fc30554 100644 --- a/src/applications/calendar/notifications/PhabricatorCalendarNotificationEngine.php +++ b/src/applications/calendar/notifications/PhabricatorCalendarNotificationEngine.php @@ -245,7 +245,7 @@ final class PhabricatorCalendarNotificationEngine $conn, 'INSERT IGNORE INTO %T (eventPHID, targetPHID, utcInitialEpoch, didNotifyEpoch) - VALUES %Q', + VALUES %LQ', $table->getTableName(), $chunk); } diff --git a/src/applications/diffusion/conduit/DiffusionUpdateCoverageConduitAPIMethod.php b/src/applications/diffusion/conduit/DiffusionUpdateCoverageConduitAPIMethod.php index be0d2c4faa..256d023345 100644 --- a/src/applications/diffusion/conduit/DiffusionUpdateCoverageConduitAPIMethod.php +++ b/src/applications/diffusion/conduit/DiffusionUpdateCoverageConduitAPIMethod.php @@ -106,8 +106,8 @@ final class DiffusionUpdateCoverageConduitAPIMethod foreach (PhabricatorLiskDAO::chunkSQL($sql) as $chunk) { queryfx( $conn, - 'INSERT INTO %T (branchID, pathID, commitID, coverage) VALUES %Q'. - ' ON DUPLICATE KEY UPDATE coverage=VALUES(coverage)', + 'INSERT INTO %T (branchID, pathID, commitID, coverage) VALUES %LQ'. + ' ON DUPLICATE KEY UPDATE coverage = VALUES(coverage)', $table_name, $chunk); } diff --git a/src/applications/diviner/publisher/DivinerLivePublisher.php b/src/applications/diviner/publisher/DivinerLivePublisher.php index 80f3bd7a1e..e9e1848d6e 100644 --- a/src/applications/diviner/publisher/DivinerLivePublisher.php +++ b/src/applications/diviner/publisher/DivinerLivePublisher.php @@ -101,11 +101,11 @@ final class DivinerLivePublisher extends DivinerPublisher { $strings[] = qsprintf($conn_w, '%s', $hash); } - foreach (PhabricatorLiskDAO::chunkSQL($strings, ', ') as $chunk) { + foreach (PhabricatorLiskDAO::chunkSQL($strings) as $chunk) { queryfx( $conn_w, 'UPDATE %T SET graphHash = NULL, nodeHash = NULL - WHERE graphHash IN (%Q)', + WHERE graphHash IN (%LQ)', $symbol_table->getTableName(), $chunk); } diff --git a/src/applications/fact/daemon/PhabricatorFactDaemon.php b/src/applications/fact/daemon/PhabricatorFactDaemon.php index 3fa6c6f0ff..57813b3021 100644 --- a/src/applications/fact/daemon/PhabricatorFactDaemon.php +++ b/src/applications/fact/daemon/PhabricatorFactDaemon.php @@ -189,7 +189,7 @@ final class PhabricatorFactDaemon extends PhabricatorDaemon { $conn, 'INSERT INTO %T (keyID, objectID, dimensionID, value, epoch) - VALUES %Q', + VALUES %LQ', $table_name, $chunk); } diff --git a/src/applications/fact/storage/PhabricatorFactDimension.php b/src/applications/fact/storage/PhabricatorFactDimension.php index 9c05121c9c..5644da331e 100644 --- a/src/applications/fact/storage/PhabricatorFactDimension.php +++ b/src/applications/fact/storage/PhabricatorFactDimension.php @@ -75,7 +75,7 @@ abstract class PhabricatorFactDimension extends PhabricatorFactDAO { foreach (PhabricatorLiskDAO::chunkSQL($sql) as $chunk) { queryfx( $conn, - 'INSERT IGNORE INTO %T (%C) VALUES %Q', + 'INSERT IGNORE INTO %T (%C) VALUES %LQ', $this->getTableName(), $column, $chunk); diff --git a/src/applications/maniphest/editor/ManiphestTransactionEditor.php b/src/applications/maniphest/editor/ManiphestTransactionEditor.php index 66c523573f..19fd57b0d2 100644 --- a/src/applications/maniphest/editor/ManiphestTransactionEditor.php +++ b/src/applications/maniphest/editor/ManiphestTransactionEditor.php @@ -531,7 +531,7 @@ final class ManiphestTransactionEditor foreach (PhabricatorLiskDAO::chunkSQL($sql) as $chunk) { queryfx( $conn, - 'INSERT INTO %T (id, %Q, subpriority) VALUES %Q + 'INSERT INTO %T (id, %Q, subpriority) VALUES %LQ ON DUPLICATE KEY UPDATE subpriority = VALUES(subpriority)', $task->getTableName(), implode(', ', array_keys($extra_columns)), diff --git a/src/applications/people/storage/PhabricatorUserCache.php b/src/applications/people/storage/PhabricatorUserCache.php index 2afb06a437..71b3e4816b 100644 --- a/src/applications/people/storage/PhabricatorUserCache.php +++ b/src/applications/people/storage/PhabricatorUserCache.php @@ -85,7 +85,7 @@ final class PhabricatorUserCache extends PhabricatorUserDAO { queryfx( $conn_w, 'INSERT INTO %T (userPHID, cacheIndex, cacheKey, cacheData, cacheType) - VALUES %Q + VALUES %LQ ON DUPLICATE KEY UPDATE cacheData = VALUES(cacheData), cacheType = VALUES(cacheType)', diff --git a/src/applications/project/storage/PhabricatorProject.php b/src/applications/project/storage/PhabricatorProject.php index a049354ad7..f134b2c633 100644 --- a/src/applications/project/storage/PhabricatorProject.php +++ b/src/applications/project/storage/PhabricatorProject.php @@ -467,7 +467,7 @@ final class PhabricatorProject extends PhabricatorProjectDAO foreach (PhabricatorLiskDAO::chunkSQL($sql) as $chunk) { queryfx( $conn_w, - 'INSERT INTO %T (projectID, token) VALUES %Q', + 'INSERT INTO %T (projectID, token) VALUES %LQ', $table, $chunk); } diff --git a/src/applications/repository/management/PhabricatorRepositoryManagementParentsWorkflow.php b/src/applications/repository/management/PhabricatorRepositoryManagementParentsWorkflow.php index 671287c3d6..e201ef40fc 100644 --- a/src/applications/repository/management/PhabricatorRepositoryManagementParentsWorkflow.php +++ b/src/applications/repository/management/PhabricatorRepositoryManagementParentsWorkflow.php @@ -161,7 +161,7 @@ final class PhabricatorRepositoryManagementParentsWorkflow foreach (PhabricatorLiskDAO::chunkSQL($delete_sql) as $chunk) { queryfx( $conn_w, - 'DELETE FROM %T WHERE childCommitID IN (%Q)', + 'DELETE FROM %T WHERE childCommitID IN (%LQ)', PhabricatorRepository::TABLE_PARENTS, $chunk); } @@ -169,7 +169,7 @@ final class PhabricatorRepositoryManagementParentsWorkflow foreach (PhabricatorLiskDAO::chunkSQL($insert_sql) as $chunk) { queryfx( $conn_w, - 'INSERT INTO %T (childCommitID, parentCommitID) VALUES %Q', + 'INSERT INTO %T (childCommitID, parentCommitID) VALUES %LQ', PhabricatorRepository::TABLE_PARENTS, $chunk); } diff --git a/src/applications/repository/worker/commitchangeparser/PhabricatorRepositoryCommitChangeParserWorker.php b/src/applications/repository/worker/commitchangeparser/PhabricatorRepositoryCommitChangeParserWorker.php index 6d79742a32..afbf42af97 100644 --- a/src/applications/repository/worker/commitchangeparser/PhabricatorRepositoryCommitChangeParserWorker.php +++ b/src/applications/repository/worker/commitchangeparser/PhabricatorRepositoryCommitChangeParserWorker.php @@ -151,7 +151,7 @@ abstract class PhabricatorRepositoryCommitChangeParserWorker 'INSERT INTO %T (repositoryID, pathID, commitID, targetPathID, targetCommitID, changeType, fileType, isDirect, commitSequence) - VALUES %Q', + VALUES %LQ', PhabricatorRepository::TABLE_PATHCHANGE, $chunk); } diff --git a/src/applications/search/engineextension/PhabricatorFerretFulltextEngineExtension.php b/src/applications/search/engineextension/PhabricatorFerretFulltextEngineExtension.php index fe50518f3f..8647bc83eb 100644 --- a/src/applications/search/engineextension/PhabricatorFerretFulltextEngineExtension.php +++ b/src/applications/search/engineextension/PhabricatorFerretFulltextEngineExtension.php @@ -201,7 +201,7 @@ final class PhabricatorFerretFulltextEngineExtension foreach (PhabricatorLiskDAO::chunkSQL($sql) as $chunk) { queryfx( $conn, - 'INSERT INTO %T (documentID, ngram) VALUES %Q', + 'INSERT INTO %T (documentID, ngram) VALUES %LQ', $engine->getNgramsTableName(), $chunk); } diff --git a/src/applications/search/management/PhabricatorSearchManagementNgramsWorkflow.php b/src/applications/search/management/PhabricatorSearchManagementNgramsWorkflow.php index 1a1124b4d8..3d87378849 100644 --- a/src/applications/search/management/PhabricatorSearchManagementNgramsWorkflow.php +++ b/src/applications/search/management/PhabricatorSearchManagementNgramsWorkflow.php @@ -123,7 +123,7 @@ final class PhabricatorSearchManagementNgramsWorkflow queryfx( $conn, 'INSERT IGNORE INTO %T (ngram, needsCollection) - VALUES %Q', + VALUES %LQ', $engine->getCommonNgramsTableName(), $chunk); } diff --git a/src/applications/system/engine/PhabricatorSystemActionEngine.php b/src/applications/system/engine/PhabricatorSystemActionEngine.php index b6a8e26711..6b8352a29e 100644 --- a/src/applications/system/engine/PhabricatorSystemActionEngine.php +++ b/src/applications/system/engine/PhabricatorSystemActionEngine.php @@ -153,7 +153,7 @@ final class PhabricatorSystemActionEngine extends Phobject { queryfx( $conn_w, 'INSERT INTO %T (actorHash, actorIdentity, action, score, epoch) - VALUES %Q', + VALUES %LQ', $log->getTableName(), $chunk); } diff --git a/src/infrastructure/customfield/field/PhabricatorCustomFieldList.php b/src/infrastructure/customfield/field/PhabricatorCustomFieldList.php index efe12e68b8..8ae5529f95 100644 --- a/src/infrastructure/customfield/field/PhabricatorCustomFieldList.php +++ b/src/infrastructure/customfield/field/PhabricatorCustomFieldList.php @@ -303,7 +303,7 @@ final class PhabricatorCustomFieldList extends Phobject { foreach (PhabricatorLiskDAO::chunkSQL($sql_list) as $chunk) { queryfx( $conn_w, - 'INSERT INTO %T (objectPHID, indexKey, indexValue) VALUES %Q', + 'INSERT INTO %T (objectPHID, indexKey, indexValue) VALUES %LQ', $table, $chunk); } diff --git a/src/infrastructure/storage/__tests__/QueryFormattingTestCase.php b/src/infrastructure/storage/__tests__/QueryFormattingTestCase.php index 53d16a7948..cff7390949 100644 --- a/src/infrastructure/storage/__tests__/QueryFormattingTestCase.php +++ b/src/infrastructure/storage/__tests__/QueryFormattingTestCase.php @@ -7,15 +7,15 @@ final class QueryFormattingTestCase extends PhabricatorTestCase { $this->assertEqual( 'NULL', - qsprintf($conn, '%nd', null)); + (string)qsprintf($conn, '%nd', null)); $this->assertEqual( '0', - qsprintf($conn, '%nd', 0)); + (string)qsprintf($conn, '%nd', 0)); $this->assertEqual( '0', - qsprintf($conn, '%d', 0)); + (string)qsprintf($conn, '%d', 0)); $raised = null; try { @@ -29,39 +29,39 @@ final class QueryFormattingTestCase extends PhabricatorTestCase { $this->assertEqual( "''", - qsprintf($conn, '%s', null)); + (string)qsprintf($conn, '%s', null)); $this->assertEqual( 'NULL', - qsprintf($conn, '%ns', null)); + (string)qsprintf($conn, '%ns', null)); $this->assertEqual( "'', ''", - qsprintf($conn, '%Ls', array('x', 'y'))); + (string)qsprintf($conn, '%Ls', array('x', 'y'))); $this->assertEqual( "''", - qsprintf($conn, '%B', null)); + (string)qsprintf($conn, '%B', null)); $this->assertEqual( 'NULL', - qsprintf($conn, '%nB', null)); + (string)qsprintf($conn, '%nB', null)); $this->assertEqual( "'', ''", - qsprintf($conn, '%LB', array('x', 'y'))); + (string)qsprintf($conn, '%LB', array('x', 'y'))); $this->assertEqual( '', - qsprintf($conn, '%T', 'x')); + (string)qsprintf($conn, '%T', 'x')); $this->assertEqual( '', - qsprintf($conn, '%C', 'y')); + (string)qsprintf($conn, '%C', 'y')); $this->assertEqual( '.', - qsprintf($conn, '%R', new AphrontDatabaseTableRef('x', 'y'))); + (string)qsprintf($conn, '%R', new AphrontDatabaseTableRef('x', 'y'))); } diff --git a/src/infrastructure/storage/lisk/PhabricatorLiskDAO.php b/src/infrastructure/storage/lisk/PhabricatorLiskDAO.php index b300efbf4b..e47d701df9 100644 --- a/src/infrastructure/storage/lisk/PhabricatorLiskDAO.php +++ b/src/infrastructure/storage/lisk/PhabricatorLiskDAO.php @@ -196,14 +196,11 @@ abstract class PhabricatorLiskDAO extends LiskDAO { * INSERT, previously built with @{function:qsprintf}) into chunks which will * fit under the MySQL 'max_allowed_packet' limit. * - * Chunks are glued together with `$glue`, by default ", ". - * * If a statement is too large to fit within the limit, it is broken into * its own chunk (but might fail when the query executes). */ public static function chunkSQL( array $fragments, - $glue = ', ', $limit = null) { if ($limit === null) { @@ -216,9 +213,13 @@ abstract class PhabricatorLiskDAO extends LiskDAO { $chunk = array(); $len = 0; - $glue_len = strlen($glue); + $glue_len = strlen(', '); foreach ($fragments as $fragment) { - $this_len = strlen($fragment); + if ($fragment instanceof PhutilQueryString) { + $this_len = strlen($fragment->getUnmaskedString()); + } else { + $this_len = strlen($fragment); + } if ($chunk) { // Chunks after the first also imply glue. @@ -232,7 +233,7 @@ abstract class PhabricatorLiskDAO extends LiskDAO { if ($chunk) { $result[] = $chunk; } - $len = strlen($fragment); + $len = ($this_len - $glue_len); $chunk = array($fragment); } } @@ -241,10 +242,6 @@ abstract class PhabricatorLiskDAO extends LiskDAO { $result[] = $chunk; } - foreach ($result as $key => $fragment_list) { - $result[$key] = implode($glue, $fragment_list); - } - return $result; } diff --git a/src/infrastructure/storage/lisk/__tests__/LiskChunkTestCase.php b/src/infrastructure/storage/lisk/__tests__/LiskChunkTestCase.php index 8ce7608a8b..66ffdb17bc 100644 --- a/src/infrastructure/storage/lisk/__tests__/LiskChunkTestCase.php +++ b/src/infrastructure/storage/lisk/__tests__/LiskChunkTestCase.php @@ -15,13 +15,15 @@ final class LiskChunkTestCase extends PhabricatorTestCase { $this->assertEqual( array( - 'aa', - 'bb', - 'ccc', - 'dd', - 'e', + array('a'), + array('a'), + array('b'), + array('b'), + array('ccc'), + array('dd'), + array('e'), ), - PhabricatorLiskDAO::chunkSQL($fragments, '', 2)); + PhabricatorLiskDAO::chunkSQL($fragments, 2)); $fragments = array( @@ -37,11 +39,11 @@ final class LiskChunkTestCase extends PhabricatorTestCase { $this->assertEqual( array( - 'a, a, a', - 'XX, a, a', - 'a, a', + array('a', 'a', 'a'), + array('XX', 'a', 'a'), + array('a', 'a'), ), - PhabricatorLiskDAO::chunkSQL($fragments, ', ', 8)); + PhabricatorLiskDAO::chunkSQL($fragments, 8)); $fragments = array( @@ -55,12 +57,12 @@ final class LiskChunkTestCase extends PhabricatorTestCase { $this->assertEqual( array( - 'xxxxxxxxxx', - 'yyyyyyyyyy', - 'a, b, c', - 'zzzzzzzzzz', + array('xxxxxxxxxx'), + array('yyyyyyyyyy'), + array('a', 'b', 'c'), + array('zzzzzzzzzz'), ), - PhabricatorLiskDAO::chunkSQL($fragments, ', ', 8)); + PhabricatorLiskDAO::chunkSQL($fragments, 8)); } } From e26c4bddab330040e6616821ae409fab834c27ed Mon Sep 17 00:00:00 2001 From: epriestley Date: Thu, 1 Nov 2018 20:15:23 -0700 Subject: [PATCH 02/43] Replace magical "branch" behavior in "diffusion.branchquery" with an explicit "patterns" Summary: See PHI958. Ref T13210. Previously, see PHI720. The use case for the magic in PHI720 involves multiple patterns, and no parameter can be passed to `branch` that will result in multiple patterns being passed to `git`. Replace the implicit magic with an explicit `patterns` parameter. This whole thing is a bit shaky but probably isn't hurting anything. Test Plan: - Ran query with no `patterns`. - Ran query with invalid `patterns`, got readable error. - Ran query with various valid `patterns` (plain branch name, globs with "?" and "*"), got sensible results. Reviewers: amckinley Reviewed By: amckinley Maniphest Tasks: T13210 Differential Revision: https://secure.phabricator.com/D19771 --- .../DiffusionBranchQueryConduitAPIMethod.php | 25 +++++++++++-------- 1 file changed, 14 insertions(+), 11 deletions(-) diff --git a/src/applications/diffusion/conduit/DiffusionBranchQueryConduitAPIMethod.php b/src/applications/diffusion/conduit/DiffusionBranchQueryConduitAPIMethod.php index 62878c0291..ac2c223c1e 100644 --- a/src/applications/diffusion/conduit/DiffusionBranchQueryConduitAPIMethod.php +++ b/src/applications/diffusion/conduit/DiffusionBranchQueryConduitAPIMethod.php @@ -21,6 +21,7 @@ final class DiffusionBranchQueryConduitAPIMethod 'limit' => 'optional int', 'offset' => 'optional int', 'contains' => 'optional string', + 'patterns' => 'optional list', ); } @@ -31,15 +32,17 @@ final class DiffusionBranchQueryConduitAPIMethod $contains = $request->getValue('contains'); if (strlen($contains)) { - // See PHI720. If the standard "branch" field is provided, use it - // as the "pattern" argument to "git branch ..." to let callers test - // for reachability from a particular branch head. - $pattern = $request->getValue('branch'); - if (strlen($pattern)) { - $pattern_argv = array($pattern); - } else { - $pattern_argv = array(); - } + // See PHI958 (and, earlier, PHI720). If "patterns" are provided, pass + // them to "git branch ..." to let callers test for reachability from + // particular branch heads. + $patterns_argv = $request->getValue('patterns', array()); + PhutilTypeSpec::checkMap( + array( + 'patterns' => $patterns_argv, + ), + array( + 'patterns' => 'list', + )); // NOTE: We can't use DiffusionLowLevelGitRefQuery here because // `git for-each-ref` does not support `--contains`. @@ -47,14 +50,14 @@ final class DiffusionBranchQueryConduitAPIMethod list($stdout) = $repository->execxLocalCommand( 'branch --verbose --no-abbrev --contains %s -- %Ls', $contains, - $pattern_argv); + $patterns_argv); $ref_map = DiffusionGitBranch::parseLocalBranchOutput( $stdout); } else { list($stdout) = $repository->execxLocalCommand( 'branch -r --verbose --no-abbrev --contains %s -- %Ls', $contains, - $pattern_argv); + $patterns_argv); $ref_map = DiffusionGitBranch::parseRemoteBranchOutput( $stdout, DiffusionGitBranch::DEFAULT_GIT_REMOTE); From 64b52b9952dd1ea098e1daf4260506568f40bfc9 Mon Sep 17 00:00:00 2001 From: epriestley Date: Wed, 7 Nov 2018 02:38:09 -0800 Subject: [PATCH 03/43] Make SELECT construction in PolicyAwareQuery safer Summary: Depends on D19784. Ref T13217. Reduce uses of unsafe `%Q` in SELECT construction. Test Plan: This reduces the number of safety warnings when loading Phabricator home from ~900 to ~800. Reviewers: amckinley Reviewed By: amckinley Maniphest Tasks: T13217 Differential Revision: https://secure.phabricator.com/D19785 --- .../query/PhabricatorRepositoryQuery.php | 4 ++-- src/infrastructure/query/PhabricatorQuery.php | 21 ++++++------------- ...PhabricatorCursorPagedPolicyAwareQuery.php | 4 ++-- 3 files changed, 10 insertions(+), 19 deletions(-) diff --git a/src/applications/repository/query/PhabricatorRepositoryQuery.php b/src/applications/repository/query/PhabricatorRepositoryQuery.php index 035693ff75..4175ffc2d5 100644 --- a/src/applications/repository/query/PhabricatorRepositoryQuery.php +++ b/src/applications/repository/query/PhabricatorRepositoryQuery.php @@ -491,10 +491,10 @@ final class PhabricatorRepositoryQuery protected function buildSelectClauseParts(AphrontDatabaseConnection $conn) { $parts = parent::buildSelectClauseParts($conn); - $parts[] = 'r.*'; + $parts[] = qsprintf($conn, 'r.*'); if ($this->shouldJoinSummaryTable()) { - $parts[] = 's.*'; + $parts[] = qsprintf($conn, 's.*'); } return $parts; diff --git a/src/infrastructure/query/PhabricatorQuery.php b/src/infrastructure/query/PhabricatorQuery.php index 5893297dbc..1dfe14b6f2 100644 --- a/src/infrastructure/query/PhabricatorQuery.php +++ b/src/infrastructure/query/PhabricatorQuery.php @@ -41,25 +41,16 @@ abstract class PhabricatorQuery extends Phobject { /** * @task format */ - protected function formatSelectClause(array $parts) { + protected function formatSelectClause( + AphrontDatabaseConnection $conn, + array $parts) { + $parts = $this->flattenSubclause($parts); if (!$parts) { - throw new Exception(pht('Can not build empty select clause!')); + throw new Exception(pht('Can not build empty SELECT clause!')); } - return 'SELECT '.$this->formatSelectSubclause($parts); - } - - - /** - * @task format - */ - protected function formatSelectSubclause(array $parts) { - $parts = $this->flattenSubclause($parts); - if (!$parts) { - return null; - } - return implode(', ', $parts); + return qsprintf($conn, 'SELECT %LQ', $parts); } diff --git a/src/infrastructure/query/policy/PhabricatorCursorPagedPolicyAwareQuery.php b/src/infrastructure/query/policy/PhabricatorCursorPagedPolicyAwareQuery.php index 1784a8ce17..cd101a61d9 100644 --- a/src/infrastructure/query/policy/PhabricatorCursorPagedPolicyAwareQuery.php +++ b/src/infrastructure/query/policy/PhabricatorCursorPagedPolicyAwareQuery.php @@ -277,7 +277,7 @@ abstract class PhabricatorCursorPagedPolicyAwareQuery */ protected function buildSelectClause(AphrontDatabaseConnection $conn) { $parts = $this->buildSelectClauseParts($conn); - return $this->formatSelectClause($parts); + return $this->formatSelectClause($conn, $parts); } @@ -291,7 +291,7 @@ abstract class PhabricatorCursorPagedPolicyAwareQuery if ($alias) { $select[] = qsprintf($conn, '%T.*', $alias); } else { - $select[] = '*'; + $select[] = qsprintf($conn, '*'); } $select[] = $this->buildEdgeLogicSelectClause($conn); From 98690ee326ce314dfee8a7f27bd8007d61ecfbf0 Mon Sep 17 00:00:00 2001 From: epriestley Date: Wed, 7 Nov 2018 02:48:26 -0800 Subject: [PATCH 04/43] Update many Phabricator queries for new %Q query semantics Summary: Depends on D19785. Ref T13217. This converts many of the most common clause construction pathways to the new %Q / %LQ / %LO / %LA / %LJ semantics. Test Plan: Browsed around a bunch, saw fewer warnings and no obvious behavioral errors. The transformations here are generally mechanical (although I did them by hand). Reviewers: amckinley Reviewed By: amckinley Subscribers: hach-que Maniphest Tasks: T13217 Differential Revision: https://secure.phabricator.com/D19789 --- .../auth/query/PhabricatorAuthInviteQuery.php | 16 +++--- .../PhabricatorAuthProviderConfigQuery.php | 20 +++---- .../query/PhabricatorAuthSessionQuery.php | 22 +++---- .../PhabricatorCalendarEventInviteeQuery.php | 16 +++--- .../query/PhabricatorCalendarEventQuery.php | 4 -- .../query/PhabricatorChatLogChannelQuery.php | 10 ++-- .../chatlog/query/PhabricatorChatLogQuery.php | 14 ++--- .../query/PhabricatorConfigEntryQuery.php | 14 ++--- .../query/ConpherenceFulltextQuery.php | 10 ++-- .../ConpherenceParticipantCountQuery.php | 2 +- .../query/ConpherenceParticipantQuery.php | 2 +- .../query/PhabricatorDaemonLogQuery.php | 17 +++--- .../query/DifferentialInlineCommentQuery.php | 20 +++---- .../query/DifferentialRevisionQuery.php | 57 ++++++++++--------- .../query/DiffusionLintCountQuery.php | 8 +-- .../diffusion/query/DiffusionSymbolQuery.php | 16 +++--- .../diviner/query/DivinerAtomQuery.php | 38 ++++++------- .../diviner/query/DivinerBookQuery.php | 18 +++--- .../files/query/PhabricatorFileChunkQuery.php | 16 +++--- .../flag/query/PhabricatorFlagQuery.php | 14 ++--- .../fund/query/FundBackerQuery.php | 16 +++--- .../herald/query/HeraldRuleQuery.php | 22 +++---- .../herald/query/HeraldTranscriptQuery.php | 12 ++-- .../query/LegalpadDocumentSignatureQuery.php | 20 +++---- .../PhabricatorOAuthServerClientQuery.php | 12 ++-- .../phlux/query/PhluxVariableQuery.php | 12 ++-- .../pholio/query/PholioImageQuery.php | 14 ++--- .../phortune/query/PhortuneAccountQuery.php | 2 +- .../phortune/query/PhortuneCartQuery.php | 2 +- .../phortune/query/PhortuneChargeQuery.php | 2 +- .../phortune/query/PhortuneMerchantQuery.php | 2 +- .../PhortunePaymentProviderConfigQuery.php | 2 +- .../phortune/query/PhortuneProductQuery.php | 4 +- .../phortune/query/PhortunePurchaseQuery.php | 2 +- .../query/PhortuneSubscriptionQuery.php | 2 +- .../query/PhragmentFragmentQuery.php | 16 +++--- .../query/PhragmentFragmentVersionQuery.php | 16 +++--- .../query/PhragmentSnapshotChildQuery.php | 14 ++--- .../query/PhragmentSnapshotQuery.php | 22 +++---- .../query/PhrequentUserTimeQuery.php | 2 +- .../releeph/query/ReleephBranchQuery.php | 14 ++--- .../releeph/query/ReleephProductQuery.php | 14 ++--- .../releeph/query/ReleephRequestQuery.php | 18 +++--- .../query/PhabricatorSavedQueryQuery.php | 12 ++-- .../query/PhabricatorTokenCountQuery.php | 6 +- ...atorApplicationTransactionCommentQuery.php | 22 +++---- .../query/PhabricatorWorkerLeaseQuery.php | 36 +++++++----- .../query/PhabricatorWorkerTaskQuery.php | 18 +++--- .../query/PhabricatorWorkerTriggerQuery.php | 16 +++--- .../edges/query/PhabricatorEdgeQuery.php | 10 ++-- .../query/PhabricatorOffsetPagedQuery.php | 10 ++-- src/infrastructure/query/PhabricatorQuery.php | 52 ++++++----------- ...PhabricatorCursorPagedPolicyAwareQuery.php | 49 ++++++++-------- src/infrastructure/storage/lisk/LiskDAO.php | 17 +++--- 54 files changed, 407 insertions(+), 417 deletions(-) diff --git a/src/applications/auth/query/PhabricatorAuthInviteQuery.php b/src/applications/auth/query/PhabricatorAuthInviteQuery.php index 1ae617db65..55b325d603 100644 --- a/src/applications/auth/query/PhabricatorAuthInviteQuery.php +++ b/src/applications/auth/query/PhabricatorAuthInviteQuery.php @@ -59,26 +59,26 @@ final class PhabricatorAuthInviteQuery return $invites; } - protected function buildWhereClause(AphrontDatabaseConnection $conn_r) { + protected function buildWhereClause(AphrontDatabaseConnection $conn) { $where = array(); if ($this->ids !== null) { $where[] = qsprintf( - $conn_r, + $conn, 'id IN (%Ld)', $this->ids); } if ($this->phids !== null) { $where[] = qsprintf( - $conn_r, + $conn, 'phid IN (%Ls)', $this->phids); } if ($this->emailAddresses !== null) { $where[] = qsprintf( - $conn_r, + $conn, 'emailAddress IN (%Ls)', $this->emailAddresses); } @@ -90,21 +90,21 @@ final class PhabricatorAuthInviteQuery } $where[] = qsprintf( - $conn_r, + $conn, 'verificationHash IN (%Ls)', $hashes); } if ($this->authorPHIDs !== null) { $where[] = qsprintf( - $conn_r, + $conn, 'authorPHID IN (%Ls)', $this->authorPHIDs); } - $where[] = $this->buildPagingClause($conn_r); + $where[] = $this->buildPagingClause($conn); - return $this->formatWhereClause($where); + return $this->formatWhereClause($conn, $where); } public function getQueryApplicationClass() { diff --git a/src/applications/auth/query/PhabricatorAuthProviderConfigQuery.php b/src/applications/auth/query/PhabricatorAuthProviderConfigQuery.php index 44e5913290..626c80348f 100644 --- a/src/applications/auth/query/PhabricatorAuthProviderConfigQuery.php +++ b/src/applications/auth/query/PhabricatorAuthProviderConfigQuery.php @@ -54,26 +54,26 @@ final class PhabricatorAuthProviderConfigQuery return $table->loadAllFromArray($data); } - protected function buildWhereClause(AphrontDatabaseConnection $conn_r) { + protected function buildWhereClause(AphrontDatabaseConnection $conn) { $where = array(); - if ($this->ids) { + if ($this->ids !== null) { $where[] = qsprintf( - $conn_r, + $conn, 'id IN (%Ld)', $this->ids); } - if ($this->phids) { + if ($this->phids !== null) { $where[] = qsprintf( - $conn_r, + $conn, 'phid IN (%Ls)', $this->phids); } - if ($this->providerClasses) { + if ($this->providerClasses !== null) { $where[] = qsprintf( - $conn_r, + $conn, 'providerClass IN (%Ls)', $this->providerClasses); } @@ -84,16 +84,16 @@ final class PhabricatorAuthProviderConfigQuery break; case self::STATUS_ENABLED: $where[] = qsprintf( - $conn_r, + $conn, 'isEnabled = 1'); break; default: throw new Exception(pht("Unknown status '%s'!", $status)); } - $where[] = $this->buildPagingClause($conn_r); + $where[] = $this->buildPagingClause($conn); - return $this->formatWhereClause($where); + return $this->formatWhereClause($conn, $where); } public function getQueryApplicationClass() { diff --git a/src/applications/auth/query/PhabricatorAuthSessionQuery.php b/src/applications/auth/query/PhabricatorAuthSessionQuery.php index dea95dd450..25928e72c1 100644 --- a/src/applications/auth/query/PhabricatorAuthSessionQuery.php +++ b/src/applications/auth/query/PhabricatorAuthSessionQuery.php @@ -65,44 +65,44 @@ final class PhabricatorAuthSessionQuery return $sessions; } - protected function buildWhereClause(AphrontDatabaseConnection $conn_r) { + protected function buildWhereClause(AphrontDatabaseConnection $conn) { $where = array(); - if ($this->ids) { + if ($this->ids !== null) { $where[] = qsprintf( - $conn_r, + $conn, 'id IN (%Ld)', $this->ids); } - if ($this->identityPHIDs) { + if ($this->identityPHIDs !== null) { $where[] = qsprintf( - $conn_r, + $conn, 'userPHID IN (%Ls)', $this->identityPHIDs); } - if ($this->sessionKeys) { + if ($this->sessionKeys !== null) { $hashes = array(); foreach ($this->sessionKeys as $session_key) { $hashes[] = PhabricatorHash::weakDigest($session_key); } $where[] = qsprintf( - $conn_r, + $conn, 'sessionKey IN (%Ls)', $hashes); } - if ($this->sessionTypes) { + if ($this->sessionTypes !== null) { $where[] = qsprintf( - $conn_r, + $conn, 'type IN (%Ls)', $this->sessionTypes); } - $where[] = $this->buildPagingClause($conn_r); + $where[] = $this->buildPagingClause($conn); - return $this->formatWhereClause($where); + return $this->formatWhereClause($conn, $where); } public function getQueryApplicationClass() { diff --git a/src/applications/calendar/query/PhabricatorCalendarEventInviteeQuery.php b/src/applications/calendar/query/PhabricatorCalendarEventInviteeQuery.php index 5ffa2a8aab..683d6cd918 100644 --- a/src/applications/calendar/query/PhabricatorCalendarEventInviteeQuery.php +++ b/src/applications/calendar/query/PhabricatorCalendarEventInviteeQuery.php @@ -49,47 +49,47 @@ final class PhabricatorCalendarEventInviteeQuery return $table->loadAllFromArray($data); } - protected function buildWhereClause(AphrontDatabaseConnection $conn_r) { + protected function buildWhereClause(AphrontDatabaseConnection $conn) { $where = array(); if ($this->ids !== null) { $where[] = qsprintf( - $conn_r, + $conn, 'id IN (%Ld)', $this->ids); } if ($this->eventPHIDs !== null) { $where[] = qsprintf( - $conn_r, + $conn, 'eventPHID IN (%Ls)', $this->eventPHIDs); } if ($this->inviteePHIDs !== null) { $where[] = qsprintf( - $conn_r, + $conn, 'inviteePHID IN (%Ls)', $this->inviteePHIDs); } if ($this->inviterPHIDs !== null) { $where[] = qsprintf( - $conn_r, + $conn, 'inviterPHID IN (%Ls)', $this->inviterPHIDs); } if ($this->statuses !== null) { $where[] = qsprintf( - $conn_r, + $conn, 'status = %d', $this->statuses); } - $where[] = $this->buildPagingClause($conn_r); + $where[] = $this->buildPagingClause($conn); - return $this->formatWhereClause($where); + return $this->formatWhereClause($conn, $where); } public function getQueryApplicationClass() { diff --git a/src/applications/calendar/query/PhabricatorCalendarEventQuery.php b/src/applications/calendar/query/PhabricatorCalendarEventQuery.php index 9b7189cfdf..2be76a631f 100644 --- a/src/applications/calendar/query/PhabricatorCalendarEventQuery.php +++ b/src/applications/calendar/query/PhabricatorCalendarEventQuery.php @@ -509,10 +509,6 @@ final class PhabricatorCalendarEventQuery return parent::shouldGroupQueryResultRows(); } - protected function getApplicationSearchObjectPHIDColumn() { - return 'event.phid'; - } - public function getQueryApplicationClass() { return 'PhabricatorCalendarApplication'; } diff --git a/src/applications/chatlog/query/PhabricatorChatLogChannelQuery.php b/src/applications/chatlog/query/PhabricatorChatLogChannelQuery.php index 2aded5c11b..a13514eec7 100644 --- a/src/applications/chatlog/query/PhabricatorChatLogChannelQuery.php +++ b/src/applications/chatlog/query/PhabricatorChatLogChannelQuery.php @@ -33,14 +33,14 @@ final class PhabricatorChatLogChannelQuery return $logs; } - protected function buildWhereClause(AphrontDatabaseConnection $conn_r) { + protected function buildWhereClause(AphrontDatabaseConnection $conn) { $where = array(); - $where[] = $this->buildPagingClause($conn_r); + $where[] = $this->buildPagingClause($conn); if ($this->channelIDs) { $where[] = qsprintf( - $conn_r, + $conn, 'id IN (%Ld)', $this->channelIDs); @@ -48,12 +48,12 @@ final class PhabricatorChatLogChannelQuery if ($this->channels) { $where[] = qsprintf( - $conn_r, + $conn, 'channelName IN (%Ls)', $this->channels); } - return $this->formatWhereClause($where); + return $this->formatWhereClause($conn, $where); } public function getQueryApplicationClass() { diff --git a/src/applications/chatlog/query/PhabricatorChatLogQuery.php b/src/applications/chatlog/query/PhabricatorChatLogQuery.php index d174fdac90..88cf6da7e3 100644 --- a/src/applications/chatlog/query/PhabricatorChatLogQuery.php +++ b/src/applications/chatlog/query/PhabricatorChatLogQuery.php @@ -55,26 +55,26 @@ final class PhabricatorChatLogQuery return $events; } - protected function buildWhereClause(AphrontDatabaseConnection $conn_r) { + protected function buildWhereClause(AphrontDatabaseConnection $conn) { $where = array(); - $where[] = $this->buildPagingClause($conn_r); + $where[] = $this->buildPagingClause($conn); - if ($this->maximumEpoch) { + if ($this->maximumEpoch !== null) { $where[] = qsprintf( - $conn_r, + $conn, 'epoch <= %d', $this->maximumEpoch); } - if ($this->channelIDs) { + if ($this->channelIDs !== null) { $where[] = qsprintf( - $conn_r, + $conn, 'channelID IN (%Ld)', $this->channelIDs); } - return $this->formatWhereClause($where); + return $this->formatWhereClause($conn, $where); } public function getQueryApplicationClass() { diff --git a/src/applications/config/query/PhabricatorConfigEntryQuery.php b/src/applications/config/query/PhabricatorConfigEntryQuery.php index 56bf15a267..f46fdb7d1e 100644 --- a/src/applications/config/query/PhabricatorConfigEntryQuery.php +++ b/src/applications/config/query/PhabricatorConfigEntryQuery.php @@ -31,26 +31,26 @@ final class PhabricatorConfigEntryQuery return $table->loadAllFromArray($data); } - protected function buildWhereClause(AphrontDatabaseConnection $conn_r) { + protected function buildWhereClause(AphrontDatabaseConnection $conn) { $where = array(); - if ($this->ids) { + if ($this->ids !== null) { $where[] = qsprintf( - $conn_r, + $conn, 'id IN (%Ld)', $this->ids); } - if ($this->phids) { + if ($this->phids !== null) { $where[] = qsprintf( - $conn_r, + $conn, 'phid IN (%Ls)', $this->phids); } - $where[] = $this->buildPagingClause($conn_r); + $where[] = $this->buildPagingClause($conn); - return $this->formatWhereClause($where); + return $this->formatWhereClause($conn, $where); } public function getQueryApplicationClass() { diff --git a/src/applications/conpherence/query/ConpherenceFulltextQuery.php b/src/applications/conpherence/query/ConpherenceFulltextQuery.php index ba734049f8..99ee4f559d 100644 --- a/src/applications/conpherence/query/ConpherenceFulltextQuery.php +++ b/src/applications/conpherence/query/ConpherenceFulltextQuery.php @@ -38,19 +38,19 @@ final class ConpherenceFulltextQuery return $rows; } - protected function buildWhereClause(AphrontDatabaseConnection $conn_r) { + protected function buildWhereClause(AphrontDatabaseConnection $conn) { $where = array(); if ($this->threadPHIDs !== null) { $where[] = qsprintf( - $conn_r, + $conn, 'i.threadPHID IN (%Ls)', $this->threadPHIDs); } if ($this->previousTransactionPHIDs !== null) { $where[] = qsprintf( - $conn_r, + $conn, 'i.previousTransactionPHID IN (%Ls)', $this->previousTransactionPHIDs); } @@ -61,12 +61,12 @@ final class ConpherenceFulltextQuery $compiled_query = $compiler->compileQuery($tokens); $where[] = qsprintf( - $conn_r, + $conn, 'MATCH(i.corpus) AGAINST (%s IN BOOLEAN MODE)', $compiled_query); } - return $this->formatWhereClause($where); + return $this->formatWhereClause($conn, $where); } private function buildOrderByClause(AphrontDatabaseConnection $conn_r) { diff --git a/src/applications/conpherence/query/ConpherenceParticipantCountQuery.php b/src/applications/conpherence/query/ConpherenceParticipantCountQuery.php index 268af0ccf1..c9ac9f76c1 100644 --- a/src/applications/conpherence/query/ConpherenceParticipantCountQuery.php +++ b/src/applications/conpherence/query/ConpherenceParticipantCountQuery.php @@ -57,7 +57,7 @@ final class ConpherenceParticipantCountQuery } } - return $this->formatWhereClause($where); + return $this->formatWhereClause($conn, $where); } private function buildGroupByClause(AphrontDatabaseConnection $conn) { diff --git a/src/applications/conpherence/query/ConpherenceParticipantQuery.php b/src/applications/conpherence/query/ConpherenceParticipantQuery.php index fb4c3eff0f..0316d5e2c2 100644 --- a/src/applications/conpherence/query/ConpherenceParticipantQuery.php +++ b/src/applications/conpherence/query/ConpherenceParticipantQuery.php @@ -38,7 +38,7 @@ final class ConpherenceParticipantQuery extends PhabricatorOffsetPagedQuery { $this->participantPHIDs); } - return $this->formatWhereClause($where); + return $this->formatWhereClause($conn, $where); } private function buildOrderClause(AphrontDatabaseConnection $conn) { diff --git a/src/applications/daemon/query/PhabricatorDaemonLogQuery.php b/src/applications/daemon/query/PhabricatorDaemonLogQuery.php index 961c1cfc61..2c5b6baa3b 100644 --- a/src/applications/daemon/query/PhabricatorDaemonLogQuery.php +++ b/src/applications/daemon/query/PhabricatorDaemonLogQuery.php @@ -124,46 +124,47 @@ final class PhabricatorDaemonLogQuery return $daemons; } - protected function buildWhereClause(AphrontDatabaseConnection $conn_r) { + protected function buildWhereClause(AphrontDatabaseConnection $conn) { $where = array(); if ($this->ids !== null) { $where[] = qsprintf( - $conn_r, + $conn, 'id IN (%Ld)', $this->ids); } if ($this->notIDs !== null) { $where[] = qsprintf( - $conn_r, + $conn, 'id NOT IN (%Ld)', $this->notIDs); } if ($this->getStatusConstants()) { $where[] = qsprintf( - $conn_r, + $conn, 'status IN (%Ls)', $this->getStatusConstants()); } if ($this->daemonClasses !== null) { $where[] = qsprintf( - $conn_r, + $conn, 'daemon IN (%Ls)', $this->daemonClasses); } if ($this->daemonIDs !== null) { $where[] = qsprintf( - $conn_r, + $conn, 'daemonID IN (%Ls)', $this->daemonIDs); } - $where[] = $this->buildPagingClause($conn_r); - return $this->formatWhereClause($where); + $where[] = $this->buildPagingClause($conn); + + return $this->formatWhereClause($conn, $where); } private function getStatusConstants() { diff --git a/src/applications/differential/query/DifferentialInlineCommentQuery.php b/src/applications/differential/query/DifferentialInlineCommentQuery.php index 3f8ea62e14..38549cd933 100644 --- a/src/applications/differential/query/DifferentialInlineCommentQuery.php +++ b/src/applications/differential/query/DifferentialInlineCommentQuery.php @@ -107,31 +107,31 @@ final class DifferentialInlineCommentQuery return head($this->execute()); } - protected function buildWhereClause(AphrontDatabaseConnection $conn_r) { + protected function buildWhereClause(AphrontDatabaseConnection $conn) { $where = array(); // Only find inline comments. $where[] = qsprintf( - $conn_r, + $conn, 'changesetID IS NOT NULL'); if ($this->ids !== null) { $where[] = qsprintf( - $conn_r, + $conn, 'id IN (%Ld)', $this->ids); } if ($this->phids !== null) { $where[] = qsprintf( - $conn_r, + $conn, 'phid IN (%Ls)', $this->phids); } if ($this->revisionPHIDs !== null) { $where[] = qsprintf( - $conn_r, + $conn, 'revisionPHID IN (%Ls)', $this->revisionPHIDs); } @@ -139,28 +139,28 @@ final class DifferentialInlineCommentQuery if ($this->drafts === null) { if ($this->deletedDrafts) { $where[] = qsprintf( - $conn_r, + $conn, '(authorPHID = %s) OR (transactionPHID IS NOT NULL)', $this->getViewer()->getPHID()); } else { $where[] = qsprintf( - $conn_r, + $conn, '(authorPHID = %s AND isDeleted = 0) OR (transactionPHID IS NOT NULL)', $this->getViewer()->getPHID()); } } else if ($this->drafts) { $where[] = qsprintf( - $conn_r, + $conn, '(authorPHID = %s AND isDeleted = 0) AND (transactionPHID IS NULL)', $this->getViewer()->getPHID()); } else { $where[] = qsprintf( - $conn_r, + $conn, 'transactionPHID IS NOT NULL'); } - return $this->formatWhereClause($where); + return $this->formatWhereClause($conn, $where); } public function adjustInlinesForChangesets( diff --git a/src/applications/differential/query/DifferentialRevisionQuery.php b/src/applications/differential/query/DifferentialRevisionQuery.php index bc93de2db2..d24106328b 100644 --- a/src/applications/differential/query/DifferentialRevisionQuery.php +++ b/src/applications/differential/query/DifferentialRevisionQuery.php @@ -542,26 +542,26 @@ final class DifferentialRevisionQuery /** * @task internal */ - private function buildJoinsClause($conn_r) { + private function buildJoinsClause(AphrontDatabaseConnection $conn) { $joins = array(); if ($this->pathIDs) { $path_table = new DifferentialAffectedPath(); $joins[] = qsprintf( - $conn_r, + $conn, 'JOIN %T p ON p.revisionID = r.id', $path_table->getTableName()); } if ($this->commitHashes) { $joins[] = qsprintf( - $conn_r, + $conn, 'JOIN %T hash_rel ON hash_rel.revisionID = r.id', ArcanistDifferentialRevisionHash::TABLE_NAME); } if ($this->ccs) { $joins[] = qsprintf( - $conn_r, + $conn, 'JOIN %T e_ccs ON e_ccs.src = r.phid '. 'AND e_ccs.type = %s '. 'AND e_ccs.dst in (%Ls)', @@ -572,7 +572,7 @@ final class DifferentialRevisionQuery if ($this->reviewers) { $joins[] = qsprintf( - $conn_r, + $conn, 'JOIN %T reviewer ON reviewer.revisionPHID = r.phid AND reviewer.reviewerStatus != %s AND reviewer.reviewerPHID in (%Ls)', @@ -583,7 +583,7 @@ final class DifferentialRevisionQuery if ($this->draftAuthors) { $joins[] = qsprintf( - $conn_r, + $conn, 'JOIN %T has_draft ON has_draft.srcPHID = r.phid AND has_draft.type = %s AND has_draft.dstPHID IN (%Ls)', @@ -594,21 +594,21 @@ final class DifferentialRevisionQuery if ($this->commitPHIDs) { $joins[] = qsprintf( - $conn_r, + $conn, 'JOIN %T commits ON commits.revisionID = r.id', DifferentialRevision::TABLE_COMMIT); } - $joins[] = $this->buildJoinClauseParts($conn_r); + $joins[] = $this->buildJoinClauseParts($conn); - return $this->formatJoinClause($joins); + return $this->formatJoinClause($conn, $joins); } /** * @task internal */ - protected function buildWhereClause(AphrontDatabaseConnection $conn_r) { + protected function buildWhereClause(AphrontDatabaseConnection $conn) { $where = array(); if ($this->pathIDs) { @@ -616,32 +616,32 @@ final class DifferentialRevisionQuery $repo_info = igroup($this->pathIDs, 'repositoryID'); foreach ($repo_info as $repository_id => $paths) { $path_clauses[] = qsprintf( - $conn_r, + $conn, '(p.repositoryID = %d AND p.pathID IN (%Ld))', $repository_id, ipull($paths, 'pathID')); } - $path_clauses = '('.implode(' OR ', $path_clauses).')'; + $path_clauses = qsprintf($conn, '%LO', $path_clauses); $where[] = $path_clauses; } if ($this->authors) { $where[] = qsprintf( - $conn_r, + $conn, 'r.authorPHID IN (%Ls)', $this->authors); } if ($this->revIDs) { $where[] = qsprintf( - $conn_r, + $conn, 'r.id IN (%Ld)', $this->revIDs); } if ($this->repositoryPHIDs) { $where[] = qsprintf( - $conn_r, + $conn, 'r.repositoryPHID IN (%Ls)', $this->repositoryPHIDs); } @@ -651,67 +651,67 @@ final class DifferentialRevisionQuery foreach ($this->commitHashes as $info) { list($type, $hash) = $info; $hash_clauses[] = qsprintf( - $conn_r, + $conn, '(hash_rel.type = %s AND hash_rel.hash = %s)', $type, $hash); } - $hash_clauses = '('.implode(' OR ', $hash_clauses).')'; + $hash_clauses = qsprintf($conn, '%LO', $hash_clauses); $where[] = $hash_clauses; } if ($this->commitPHIDs) { $where[] = qsprintf( - $conn_r, + $conn, 'commits.commitPHID IN (%Ls)', $this->commitPHIDs); } if ($this->phids) { $where[] = qsprintf( - $conn_r, + $conn, 'r.phid IN (%Ls)', $this->phids); } if ($this->branches) { $where[] = qsprintf( - $conn_r, + $conn, 'r.branchName in (%Ls)', $this->branches); } if ($this->updatedEpochMin !== null) { $where[] = qsprintf( - $conn_r, + $conn, 'r.dateModified >= %d', $this->updatedEpochMin); } if ($this->updatedEpochMax !== null) { $where[] = qsprintf( - $conn_r, + $conn, 'r.dateModified <= %d', $this->updatedEpochMax); } if ($this->createdEpochMin !== null) { $where[] = qsprintf( - $conn_r, + $conn, 'r.dateCreated >= %d', $this->createdEpochMin); } if ($this->createdEpochMax !== null) { $where[] = qsprintf( - $conn_r, + $conn, 'r.dateCreated <= %d', $this->createdEpochMax); } if ($this->statuses !== null) { $where[] = qsprintf( - $conn_r, + $conn, 'r.status in (%Ls)', $this->statuses); } @@ -725,13 +725,14 @@ final class DifferentialRevisionQuery DifferentialLegacyQuery::STATUS_CLOSED); } $where[] = qsprintf( - $conn_r, + $conn, 'r.status in (%Ls)', $statuses); } - $where[] = $this->buildWhereClauseParts($conn_r); - return $this->formatWhereClause($where); + $where[] = $this->buildWhereClauseParts($conn); + + return $this->formatWhereClause($conn, $where); } diff --git a/src/applications/diffusion/query/DiffusionLintCountQuery.php b/src/applications/diffusion/query/DiffusionLintCountQuery.php index 4441ccf3ef..2c1a2a9867 100644 --- a/src/applications/diffusion/query/DiffusionLintCountQuery.php +++ b/src/applications/diffusion/query/DiffusionLintCountQuery.php @@ -70,7 +70,7 @@ final class DiffusionLintCountQuery extends PhabricatorQuery { } protected function buildCustomWhereClause( - AphrontDatabaseConnection $conn_r, + AphrontDatabaseConnection $conn, $part) { $where = array(); @@ -79,19 +79,19 @@ final class DiffusionLintCountQuery extends PhabricatorQuery { if ($this->codes !== null) { $where[] = qsprintf( - $conn_r, + $conn, 'code IN (%Ls)', $this->codes); } if ($this->branchIDs !== null) { $where[] = qsprintf( - $conn_r, + $conn, 'branchID IN (%Ld)', $this->branchIDs); } - return $this->formatWhereClause($where); + return $this->formatWhereClause($conn, $where); } private function processPaths() { diff --git a/src/applications/diffusion/query/DiffusionSymbolQuery.php b/src/applications/diffusion/query/DiffusionSymbolQuery.php index 0e08b7b32d..3415fbbefa 100644 --- a/src/applications/diffusion/query/DiffusionSymbolQuery.php +++ b/src/applications/diffusion/query/DiffusionSymbolQuery.php @@ -192,52 +192,52 @@ final class DiffusionSymbolQuery extends PhabricatorOffsetPagedQuery { /** * @task internal */ - protected function buildWhereClause(AphrontDatabaseConnection $conn_r) { + protected function buildWhereClause(AphrontDatabaseConnection $conn) { $where = array(); if (isset($this->context)) { $where[] = qsprintf( - $conn_r, + $conn, 'symbolContext = %s', $this->context); } if ($this->name) { $where[] = qsprintf( - $conn_r, + $conn, 'symbolName = %s', $this->name); } if ($this->namePrefix) { $where[] = qsprintf( - $conn_r, + $conn, 'symbolName LIKE %>', $this->namePrefix); } if ($this->repositoryPHIDs) { $where[] = qsprintf( - $conn_r, + $conn, 'repositoryPHID IN (%Ls)', $this->repositoryPHIDs); } if ($this->language) { $where[] = qsprintf( - $conn_r, + $conn, 'symbolLanguage = %s', $this->language); } if ($this->type) { $where[] = qsprintf( - $conn_r, + $conn, 'symbolType = %s', $this->type); } - return $this->formatWhereClause($where); + return $this->formatWhereClause($conn, $where); } diff --git a/src/applications/diviner/query/DivinerAtomQuery.php b/src/applications/diviner/query/DivinerAtomQuery.php index 5f4900d4dd..65a5634008 100644 --- a/src/applications/diviner/query/DivinerAtomQuery.php +++ b/src/applications/diviner/query/DivinerAtomQuery.php @@ -299,40 +299,40 @@ final class DivinerAtomQuery extends PhabricatorCursorPagedPolicyAwareQuery { return $atoms; } - protected function buildWhereClause(AphrontDatabaseConnection $conn_r) { + protected function buildWhereClause(AphrontDatabaseConnection $conn) { $where = array(); if ($this->ids) { $where[] = qsprintf( - $conn_r, + $conn, 'id IN (%Ld)', $this->ids); } if ($this->phids) { $where[] = qsprintf( - $conn_r, + $conn, 'phid IN (%Ls)', $this->phids); } if ($this->bookPHIDs) { $where[] = qsprintf( - $conn_r, + $conn, 'bookPHID IN (%Ls)', $this->bookPHIDs); } if ($this->types) { $where[] = qsprintf( - $conn_r, + $conn, 'type IN (%Ls)', $this->types); } if ($this->names) { $where[] = qsprintf( - $conn_r, + $conn, 'name IN (%Ls)', $this->names); } @@ -347,7 +347,7 @@ final class DivinerAtomQuery extends PhabricatorCursorPagedPolicyAwareQuery { } $where[] = qsprintf( - $conn_r, + $conn, 'titleSlugHash in (%Ls)', $hashes); } @@ -366,46 +366,46 @@ final class DivinerAtomQuery extends PhabricatorCursorPagedPolicyAwareQuery { if ($contexts && $with_null) { $where[] = qsprintf( - $conn_r, + $conn, 'context IN (%Ls) OR context IS NULL', $contexts); } else if ($contexts) { $where[] = qsprintf( - $conn_r, + $conn, 'context IN (%Ls)', $contexts); } else if ($with_null) { $where[] = qsprintf( - $conn_r, + $conn, 'context IS NULL'); } } if ($this->indexes) { $where[] = qsprintf( - $conn_r, + $conn, 'atomIndex IN (%Ld)', $this->indexes); } if ($this->isDocumentable !== null) { $where[] = qsprintf( - $conn_r, + $conn, 'isDocumentable = %d', (int)$this->isDocumentable); } if ($this->isGhost !== null) { if ($this->isGhost) { - $where[] = qsprintf($conn_r, 'graphHash IS NULL'); + $where[] = qsprintf($conn, 'graphHash IS NULL'); } else { - $where[] = qsprintf($conn_r, 'graphHash IS NOT NULL'); + $where[] = qsprintf($conn, 'graphHash IS NOT NULL'); } } if ($this->nodeHashes) { $where[] = qsprintf( - $conn_r, + $conn, 'nodeHash IN (%Ls)', $this->nodeHashes); } @@ -415,21 +415,21 @@ final class DivinerAtomQuery extends PhabricatorCursorPagedPolicyAwareQuery { // the column has binary collation. Eventually, this should move into // fulltext. $where[] = qsprintf( - $conn_r, + $conn, 'CONVERT(name USING utf8) LIKE %~', $this->nameContains); } if ($this->repositoryPHIDs) { $where[] = qsprintf( - $conn_r, + $conn, 'repositoryPHID IN (%Ls)', $this->repositoryPHIDs); } - $where[] = $this->buildPagingClause($conn_r); + $where[] = $this->buildPagingClause($conn); - return $this->formatWhereClause($where); + return $this->formatWhereClause($conn, $where); } /** diff --git a/src/applications/diviner/query/DivinerBookQuery.php b/src/applications/diviner/query/DivinerBookQuery.php index 8e6726ef5c..d540d971b0 100644 --- a/src/applications/diviner/query/DivinerBookQuery.php +++ b/src/applications/diviner/query/DivinerBookQuery.php @@ -116,54 +116,54 @@ final class DivinerBookQuery extends PhabricatorCursorPagedPolicyAwareQuery { return $books; } - protected function buildWhereClause(AphrontDatabaseConnection $conn_r) { + protected function buildWhereClause(AphrontDatabaseConnection $conn) { $where = array(); if ($this->ids) { $where[] = qsprintf( - $conn_r, + $conn, 'id IN (%Ld)', $this->ids); } if ($this->phids) { $where[] = qsprintf( - $conn_r, + $conn, 'phid IN (%Ls)', $this->phids); } if (strlen($this->nameLike)) { $where[] = qsprintf( - $conn_r, + $conn, 'name LIKE %~', $this->nameLike); } if ($this->names !== null) { $where[] = qsprintf( - $conn_r, + $conn, 'name IN (%Ls)', $this->names); } if (strlen($this->namePrefix)) { $where[] = qsprintf( - $conn_r, + $conn, 'name LIKE %>', $this->namePrefix); } if ($this->repositoryPHIDs !== null) { $where[] = qsprintf( - $conn_r, + $conn, 'repositoryPHID IN (%Ls)', $this->repositoryPHIDs); } - $where[] = $this->buildPagingClause($conn_r); + $where[] = $this->buildPagingClause($conn); - return $this->formatWhereClause($where); + return $this->formatWhereClause($conn, $where); } public function getQueryApplicationClass() { diff --git a/src/applications/files/query/PhabricatorFileChunkQuery.php b/src/applications/files/query/PhabricatorFileChunkQuery.php index b6fda13103..4398860569 100644 --- a/src/applications/files/query/PhabricatorFileChunkQuery.php +++ b/src/applications/files/query/PhabricatorFileChunkQuery.php @@ -86,26 +86,26 @@ final class PhabricatorFileChunkQuery return $chunks; } - protected function buildWhereClause(AphrontDatabaseConnection $conn_r) { + protected function buildWhereClause(AphrontDatabaseConnection $conn) { $where = array(); if ($this->chunkHandles !== null) { $where[] = qsprintf( - $conn_r, + $conn, 'chunkHandle IN (%Ls)', $this->chunkHandles); } if ($this->rangeStart !== null) { $where[] = qsprintf( - $conn_r, + $conn, 'byteEnd > %d', $this->rangeStart); } if ($this->rangeEnd !== null) { $where[] = qsprintf( - $conn_r, + $conn, 'byteStart < %d', $this->rangeEnd); } @@ -113,18 +113,18 @@ final class PhabricatorFileChunkQuery if ($this->isComplete !== null) { if ($this->isComplete) { $where[] = qsprintf( - $conn_r, + $conn, 'dataFilePHID IS NOT NULL'); } else { $where[] = qsprintf( - $conn_r, + $conn, 'dataFilePHID IS NULL'); } } - $where[] = $this->buildPagingClause($conn_r); + $where[] = $this->buildPagingClause($conn); - return $this->formatWhereClause($where); + return $this->formatWhereClause($conn, $where); } public function getQueryApplicationClass() { diff --git a/src/applications/flag/query/PhabricatorFlagQuery.php b/src/applications/flag/query/PhabricatorFlagQuery.php index ff154a512b..3418f10746 100644 --- a/src/applications/flag/query/PhabricatorFlagQuery.php +++ b/src/applications/flag/query/PhabricatorFlagQuery.php @@ -123,40 +123,40 @@ final class PhabricatorFlagQuery return $flags; } - protected function buildWhereClause(AphrontDatabaseConnection $conn_r) { + protected function buildWhereClause(AphrontDatabaseConnection $conn) { $where = array(); if ($this->ownerPHIDs) { $where[] = qsprintf( - $conn_r, + $conn, 'flag.ownerPHID IN (%Ls)', $this->ownerPHIDs); } if ($this->types) { $where[] = qsprintf( - $conn_r, + $conn, 'flag.type IN (%Ls)', $this->types); } if ($this->objectPHIDs) { $where[] = qsprintf( - $conn_r, + $conn, 'flag.objectPHID IN (%Ls)', $this->objectPHIDs); } if ($this->colors) { $where[] = qsprintf( - $conn_r, + $conn, 'flag.color IN (%Ld)', $this->colors); } - $where[] = $this->buildPagingClause($conn_r); + $where[] = $this->buildPagingClause($conn); - return $this->formatWhereClause($where); + return $this->formatWhereClause($conn, $where); } public function getQueryApplicationClass() { diff --git a/src/applications/fund/query/FundBackerQuery.php b/src/applications/fund/query/FundBackerQuery.php index 5f7406f5ad..2d6e13dfd9 100644 --- a/src/applications/fund/query/FundBackerQuery.php +++ b/src/applications/fund/query/FundBackerQuery.php @@ -68,47 +68,47 @@ final class FundBackerQuery return $backers; } - protected function buildWhereClause(AphrontDatabaseConnection $conn_r) { + protected function buildWhereClause(AphrontDatabaseConnection $conn) { $where = array(); - $where[] = $this->buildPagingClause($conn_r); + $where[] = $this->buildPagingClause($conn); if ($this->ids !== null) { $where[] = qsprintf( - $conn_r, + $conn, 'id IN (%Ld)', $this->ids); } if ($this->phids !== null) { $where[] = qsprintf( - $conn_r, + $conn, 'phid IN (%Ls)', $this->phids); } if ($this->initiativePHIDs !== null) { $where[] = qsprintf( - $conn_r, + $conn, 'initiativePHID IN (%Ls)', $this->initiativePHIDs); } if ($this->backerPHIDs !== null) { $where[] = qsprintf( - $conn_r, + $conn, 'backerPHID IN (%Ls)', $this->backerPHIDs); } if ($this->statuses !== null) { $where[] = qsprintf( - $conn_r, + $conn, 'status IN (%Ls)', $this->statuses); } - return $this->formatWhereClause($where); + return $this->formatWhereClause($conn, $where); } public function getQueryApplicationClass() { diff --git a/src/applications/herald/query/HeraldRuleQuery.php b/src/applications/herald/query/HeraldRuleQuery.php index 88df18db24..9673bd0810 100644 --- a/src/applications/herald/query/HeraldRuleQuery.php +++ b/src/applications/herald/query/HeraldRuleQuery.php @@ -175,68 +175,68 @@ final class HeraldRuleQuery extends PhabricatorCursorPagedPolicyAwareQuery { return $rules; } - protected function buildWhereClause(AphrontDatabaseConnection $conn_r) { + protected function buildWhereClause(AphrontDatabaseConnection $conn) { $where = array(); if ($this->ids) { $where[] = qsprintf( - $conn_r, + $conn, 'rule.id IN (%Ld)', $this->ids); } if ($this->phids) { $where[] = qsprintf( - $conn_r, + $conn, 'rule.phid IN (%Ls)', $this->phids); } if ($this->authorPHIDs) { $where[] = qsprintf( - $conn_r, + $conn, 'rule.authorPHID IN (%Ls)', $this->authorPHIDs); } if ($this->ruleTypes) { $where[] = qsprintf( - $conn_r, + $conn, 'rule.ruleType IN (%Ls)', $this->ruleTypes); } if ($this->contentTypes) { $where[] = qsprintf( - $conn_r, + $conn, 'rule.contentType IN (%Ls)', $this->contentTypes); } if ($this->disabled !== null) { $where[] = qsprintf( - $conn_r, + $conn, 'rule.isDisabled = %d', (int)$this->disabled); } if ($this->datasourceQuery) { $where[] = qsprintf( - $conn_r, + $conn, 'rule.name LIKE %>', $this->datasourceQuery); } if ($this->triggerObjectPHIDs) { $where[] = qsprintf( - $conn_r, + $conn, 'rule.triggerObjectPHID IN (%Ls)', $this->triggerObjectPHIDs); } - $where[] = $this->buildPagingClause($conn_r); + $where[] = $this->buildPagingClause($conn); - return $this->formatWhereClause($where); + return $this->formatWhereClause($conn, $where); } private function validateRuleAuthors(array $rules) { diff --git a/src/applications/herald/query/HeraldTranscriptQuery.php b/src/applications/herald/query/HeraldTranscriptQuery.php index 7d0fdfc59e..5308e05ee1 100644 --- a/src/applications/herald/query/HeraldTranscriptQuery.php +++ b/src/applications/herald/query/HeraldTranscriptQuery.php @@ -91,33 +91,33 @@ final class HeraldTranscriptQuery return $transcripts; } - protected function buildWhereClause(AphrontDatabaseConnection $conn_r) { + protected function buildWhereClause(AphrontDatabaseConnection $conn) { $where = array(); if ($this->ids) { $where[] = qsprintf( - $conn_r, + $conn, 'id IN (%Ld)', $this->ids); } if ($this->phids) { $where[] = qsprintf( - $conn_r, + $conn, 'phid IN (%Ls)', $this->phids); } if ($this->objectPHIDs) { $where[] = qsprintf( - $conn_r, + $conn, 'objectPHID in (%Ls)', $this->objectPHIDs); } - $where[] = $this->buildPagingClause($conn_r); + $where[] = $this->buildPagingClause($conn); - return $this->formatWhereClause($where); + return $this->formatWhereClause($conn, $where); } public function getQueryApplicationClass() { diff --git a/src/applications/legalpad/query/LegalpadDocumentSignatureQuery.php b/src/applications/legalpad/query/LegalpadDocumentSignatureQuery.php index 452ea6759b..c310dd3d64 100644 --- a/src/applications/legalpad/query/LegalpadDocumentSignatureQuery.php +++ b/src/applications/legalpad/query/LegalpadDocumentSignatureQuery.php @@ -86,61 +86,61 @@ final class LegalpadDocumentSignatureQuery return $signatures; } - protected function buildWhereClause(AphrontDatabaseConnection $conn_r) { + protected function buildWhereClause(AphrontDatabaseConnection $conn) { $where = array(); - $where[] = $this->buildPagingClause($conn_r); + $where[] = $this->buildPagingClause($conn); if ($this->ids !== null) { $where[] = qsprintf( - $conn_r, + $conn, 'id IN (%Ld)', $this->ids); } if ($this->documentPHIDs !== null) { $where[] = qsprintf( - $conn_r, + $conn, 'documentPHID IN (%Ls)', $this->documentPHIDs); } if ($this->signerPHIDs !== null) { $where[] = qsprintf( - $conn_r, + $conn, 'signerPHID IN (%Ls)', $this->signerPHIDs); } if ($this->documentVersions !== null) { $where[] = qsprintf( - $conn_r, + $conn, 'documentVersion IN (%Ld)', $this->documentVersions); } if ($this->secretKeys !== null) { $where[] = qsprintf( - $conn_r, + $conn, 'secretKey IN (%Ls)', $this->secretKeys); } if ($this->nameContains !== null) { $where[] = qsprintf( - $conn_r, + $conn, 'signerName LIKE %~', $this->nameContains); } if ($this->emailContains !== null) { $where[] = qsprintf( - $conn_r, + $conn, 'signerEmail LIKE %~', $this->emailContains); } - return $this->formatWhereClause($where); + return $this->formatWhereClause($conn, $where); } public function getQueryApplicationClass() { diff --git a/src/applications/oauthserver/query/PhabricatorOAuthServerClientQuery.php b/src/applications/oauthserver/query/PhabricatorOAuthServerClientQuery.php index b5da9ecd5f..46fc514824 100644 --- a/src/applications/oauthserver/query/PhabricatorOAuthServerClientQuery.php +++ b/src/applications/oauthserver/query/PhabricatorOAuthServerClientQuery.php @@ -37,33 +37,33 @@ final class PhabricatorOAuthServerClientQuery return $table->loadAllFromArray($data); } - protected function buildWhereClause(AphrontDatabaseConnection $conn_r) { + protected function buildWhereClause(AphrontDatabaseConnection $conn) { $where = array(); if ($this->ids) { $where[] = qsprintf( - $conn_r, + $conn, 'id IN (%Ld)', $this->ids); } if ($this->phids) { $where[] = qsprintf( - $conn_r, + $conn, 'phid IN (%Ls)', $this->phids); } if ($this->creatorPHIDs) { $where[] = qsprintf( - $conn_r, + $conn, 'creatorPHID IN (%Ls)', $this->creatorPHIDs); } - $where[] = $this->buildPagingClause($conn_r); + $where[] = $this->buildPagingClause($conn); - return $this->formatWhereClause($where); + return $this->formatWhereClause($conn, $where); } public function getQueryApplicationClass() { diff --git a/src/applications/phlux/query/PhluxVariableQuery.php b/src/applications/phlux/query/PhluxVariableQuery.php index 82072485d4..75abd044d0 100644 --- a/src/applications/phlux/query/PhluxVariableQuery.php +++ b/src/applications/phlux/query/PhluxVariableQuery.php @@ -37,33 +37,33 @@ final class PhluxVariableQuery return $table->loadAllFromArray($rows); } - protected function buildWhereClause(AphrontDatabaseConnection $conn_r) { + protected function buildWhereClause(AphrontDatabaseConnection $conn) { $where = array(); if ($this->ids !== null) { $where[] = qsprintf( - $conn_r, + $conn, 'id IN (%Ld)', $this->ids); } if ($this->keys !== null) { $where[] = qsprintf( - $conn_r, + $conn, 'variableKey IN (%Ls)', $this->keys); } if ($this->phids !== null) { $where[] = qsprintf( - $conn_r, + $conn, 'phid IN (%Ls)', $this->phids); } - $where[] = $this->buildPagingClause($conn_r); + $where[] = $this->buildPagingClause($conn); - return $this->formatWhereClause($where); + return $this->formatWhereClause($conn, $where); } protected function getDefaultOrderVector() { diff --git a/src/applications/pholio/query/PholioImageQuery.php b/src/applications/pholio/query/PholioImageQuery.php index 8c722660cb..79ffdc56d7 100644 --- a/src/applications/pholio/query/PholioImageQuery.php +++ b/src/applications/pholio/query/PholioImageQuery.php @@ -61,40 +61,40 @@ final class PholioImageQuery return $images; } - protected function buildWhereClause(AphrontDatabaseConnection $conn_r) { + protected function buildWhereClause(AphrontDatabaseConnection $conn) { $where = array(); - $where[] = $this->buildPagingClause($conn_r); + $where[] = $this->buildPagingClause($conn); if ($this->ids) { $where[] = qsprintf( - $conn_r, + $conn, 'id IN (%Ld)', $this->ids); } if ($this->phids) { $where[] = qsprintf( - $conn_r, + $conn, 'phid IN (%Ls)', $this->phids); } if ($this->mockIDs) { $where[] = qsprintf( - $conn_r, + $conn, 'mockID IN (%Ld)', $this->mockIDs); } if ($this->obsolete !== null) { $where[] = qsprintf( - $conn_r, + $conn, 'isObsolete = %d', $this->obsolete); } - return $this->formatWhereClause($where); + return $this->formatWhereClause($conn, $where); } protected function willFilterPage(array $images) { diff --git a/src/applications/phortune/query/PhortuneAccountQuery.php b/src/applications/phortune/query/PhortuneAccountQuery.php index c91e5f0111..4ada4f2845 100644 --- a/src/applications/phortune/query/PhortuneAccountQuery.php +++ b/src/applications/phortune/query/PhortuneAccountQuery.php @@ -99,7 +99,7 @@ final class PhortuneAccountQuery $this->memberPHIDs); } - return $this->formatWhereClause($where); + return $this->formatWhereClause($conn, $where); } protected function buildJoinClause(AphrontDatabaseConnection $conn) { diff --git a/src/applications/phortune/query/PhortuneCartQuery.php b/src/applications/phortune/query/PhortuneCartQuery.php index 5009d3a685..0b3325b932 100644 --- a/src/applications/phortune/query/PhortuneCartQuery.php +++ b/src/applications/phortune/query/PhortuneCartQuery.php @@ -213,7 +213,7 @@ final class PhortuneCartQuery } } - return $this->formatWhereClause($where); + return $this->formatWhereClause($conn, $where); } public function getQueryApplicationClass() { diff --git a/src/applications/phortune/query/PhortuneChargeQuery.php b/src/applications/phortune/query/PhortuneChargeQuery.php index 6b11cbe95e..a7eda9d6a6 100644 --- a/src/applications/phortune/query/PhortuneChargeQuery.php +++ b/src/applications/phortune/query/PhortuneChargeQuery.php @@ -134,7 +134,7 @@ final class PhortuneChargeQuery $this->statuses); } - return $this->formatWhereClause($where); + return $this->formatWhereClause($conn, $where); } public function getQueryApplicationClass() { diff --git a/src/applications/phortune/query/PhortuneMerchantQuery.php b/src/applications/phortune/query/PhortuneMerchantQuery.php index c91267b73c..033621a3f1 100644 --- a/src/applications/phortune/query/PhortuneMerchantQuery.php +++ b/src/applications/phortune/query/PhortuneMerchantQuery.php @@ -114,7 +114,7 @@ final class PhortuneMerchantQuery $where[] = $this->buildPagingClause($conn); - return $this->formatWhereClause($where); + return $this->formatWhereClause($conn, $where); } protected function buildJoinClause(AphrontDatabaseConnection $conn) { diff --git a/src/applications/phortune/query/PhortunePaymentProviderConfigQuery.php b/src/applications/phortune/query/PhortunePaymentProviderConfigQuery.php index d80b0e90d0..a850acec28 100644 --- a/src/applications/phortune/query/PhortunePaymentProviderConfigQuery.php +++ b/src/applications/phortune/query/PhortunePaymentProviderConfigQuery.php @@ -85,7 +85,7 @@ final class PhortunePaymentProviderConfigQuery $where[] = $this->buildPagingClause($conn); - return $this->formatWhereClause($where); + return $this->formatWhereClause($conn, $where); } public function getQueryApplicationClass() { diff --git a/src/applications/phortune/query/PhortuneProductQuery.php b/src/applications/phortune/query/PhortuneProductQuery.php index 99ba535585..30701d4e7b 100644 --- a/src/applications/phortune/query/PhortuneProductQuery.php +++ b/src/applications/phortune/query/PhortuneProductQuery.php @@ -105,12 +105,12 @@ final class PhortuneProductQuery PhabricatorHash::digestForIndex($ref)); } } - $where[] = implode(' OR ', $sql); + $where[] = qsprintf($conn, '%LO', $sql); } $where[] = $this->buildPagingClause($conn); - return $this->formatWhereClause($where); + return $this->formatWhereClause($conn, $where); } public function getQueryApplicationClass() { diff --git a/src/applications/phortune/query/PhortunePurchaseQuery.php b/src/applications/phortune/query/PhortunePurchaseQuery.php index 6e9e599240..275537c351 100644 --- a/src/applications/phortune/query/PhortunePurchaseQuery.php +++ b/src/applications/phortune/query/PhortunePurchaseQuery.php @@ -100,7 +100,7 @@ final class PhortunePurchaseQuery $this->cartPHIDs); } - return $this->formatWhereClause($where); + return $this->formatWhereClause($conn, $where); } public function getQueryApplicationClass() { diff --git a/src/applications/phortune/query/PhortuneSubscriptionQuery.php b/src/applications/phortune/query/PhortuneSubscriptionQuery.php index e8b39c0fee..6919e6a169 100644 --- a/src/applications/phortune/query/PhortuneSubscriptionQuery.php +++ b/src/applications/phortune/query/PhortuneSubscriptionQuery.php @@ -182,7 +182,7 @@ final class PhortuneSubscriptionQuery $this->statuses); } - return $this->formatWhereClause($where); + return $this->formatWhereClause($conn, $where); } public function getQueryApplicationClass() { diff --git a/src/applications/phragment/query/PhragmentFragmentQuery.php b/src/applications/phragment/query/PhragmentFragmentQuery.php index d6dc67609d..56444217db 100644 --- a/src/applications/phragment/query/PhragmentFragmentQuery.php +++ b/src/applications/phragment/query/PhragmentFragmentQuery.php @@ -55,47 +55,47 @@ final class PhragmentFragmentQuery return $table->loadAllFromArray($data); } - protected function buildWhereClause(AphrontDatabaseConnection $conn_r) { + protected function buildWhereClause(AphrontDatabaseConnection $conn) { $where = array(); if ($this->ids) { $where[] = qsprintf( - $conn_r, + $conn, 'id IN (%Ld)', $this->ids); } if ($this->phids) { $where[] = qsprintf( - $conn_r, + $conn, 'phid IN (%Ls)', $this->phids); } if ($this->paths) { $where[] = qsprintf( - $conn_r, + $conn, 'path IN (%Ls)', $this->paths); } if ($this->leadingPath) { $where[] = qsprintf( - $conn_r, + $conn, 'path LIKE %>', $this->leadingPath); } if ($this->depths) { $where[] = qsprintf( - $conn_r, + $conn, 'depth IN (%Ld)', $this->depths); } - $where[] = $this->buildPagingClause($conn_r); + $where[] = $this->buildPagingClause($conn); - return $this->formatWhereClause($where); + return $this->formatWhereClause($conn, $where); } protected function didFilterPage(array $page) { diff --git a/src/applications/phragment/query/PhragmentFragmentVersionQuery.php b/src/applications/phragment/query/PhragmentFragmentVersionQuery.php index a874a8be1a..e95c3260a8 100644 --- a/src/applications/phragment/query/PhragmentFragmentVersionQuery.php +++ b/src/applications/phragment/query/PhragmentFragmentVersionQuery.php @@ -49,47 +49,47 @@ final class PhragmentFragmentVersionQuery return $table->loadAllFromArray($data); } - protected function buildWhereClause(AphrontDatabaseConnection $conn_r) { + protected function buildWhereClause(AphrontDatabaseConnection $conn) { $where = array(); if ($this->ids) { $where[] = qsprintf( - $conn_r, + $conn, 'id IN (%Ld)', $this->ids); } if ($this->phids) { $where[] = qsprintf( - $conn_r, + $conn, 'phid IN (%Ls)', $this->phids); } if ($this->fragmentPHIDs) { $where[] = qsprintf( - $conn_r, + $conn, 'fragmentPHID IN (%Ls)', $this->fragmentPHIDs); } if ($this->sequences) { $where[] = qsprintf( - $conn_r, + $conn, 'sequence IN (%Ld)', $this->sequences); } if ($this->sequenceBefore !== null) { $where[] = qsprintf( - $conn_r, + $conn, 'sequence < %d', $this->sequenceBefore); } - $where[] = $this->buildPagingClause($conn_r); + $where[] = $this->buildPagingClause($conn); - return $this->formatWhereClause($where); + return $this->formatWhereClause($conn, $where); } protected function willFilterPage(array $page) { diff --git a/src/applications/phragment/query/PhragmentSnapshotChildQuery.php b/src/applications/phragment/query/PhragmentSnapshotChildQuery.php index 032fb0c49c..faa3493499 100644 --- a/src/applications/phragment/query/PhragmentSnapshotChildQuery.php +++ b/src/applications/phragment/query/PhragmentSnapshotChildQuery.php @@ -55,40 +55,40 @@ final class PhragmentSnapshotChildQuery return $table->loadAllFromArray($data); } - protected function buildWhereClause(AphrontDatabaseConnection $conn_r) { + protected function buildWhereClause(AphrontDatabaseConnection $conn) { $where = array(); if ($this->ids) { $where[] = qsprintf( - $conn_r, + $conn, 'id IN (%Ld)', $this->ids); } if ($this->snapshotPHIDs) { $where[] = qsprintf( - $conn_r, + $conn, 'snapshotPHID IN (%Ls)', $this->snapshotPHIDs); } if ($this->fragmentPHIDs) { $where[] = qsprintf( - $conn_r, + $conn, 'fragmentPHID IN (%Ls)', $this->fragmentPHIDs); } if ($this->fragmentVersionPHIDs) { $where[] = qsprintf( - $conn_r, + $conn, 'fragmentVersionPHID IN (%Ls)', $this->fragmentVersionPHIDs); } - $where[] = $this->buildPagingClause($conn_r); + $where[] = $this->buildPagingClause($conn); - return $this->formatWhereClause($where); + return $this->formatWhereClause($conn, $where); } protected function willFilterPage(array $page) { diff --git a/src/applications/phragment/query/PhragmentSnapshotQuery.php b/src/applications/phragment/query/PhragmentSnapshotQuery.php index d6f9d3422f..a4805650fc 100644 --- a/src/applications/phragment/query/PhragmentSnapshotQuery.php +++ b/src/applications/phragment/query/PhragmentSnapshotQuery.php @@ -43,40 +43,40 @@ final class PhragmentSnapshotQuery return $table->loadAllFromArray($data); } - protected function buildWhereClause(AphrontDatabaseConnection $conn_r) { + protected function buildWhereClause(AphrontDatabaseConnection $conn) { $where = array(); - if ($this->ids) { + if ($this->ids !== null) { $where[] = qsprintf( - $conn_r, + $conn, 'id IN (%Ld)', $this->ids); } - if ($this->phids) { + if ($this->phids !== null) { $where[] = qsprintf( - $conn_r, + $conn, 'phid IN (%Ls)', $this->phids); } - if ($this->primaryFragmentPHIDs) { + if ($this->primaryFragmentPHIDs !== null) { $where[] = qsprintf( - $conn_r, + $conn, 'primaryFragmentPHID IN (%Ls)', $this->primaryFragmentPHIDs); } - if ($this->names) { + if ($this->names !== null) { $where[] = qsprintf( - $conn_r, + $conn, 'name IN (%Ls)', $this->names); } - $where[] = $this->buildPagingClause($conn_r); + $where[] = $this->buildPagingClause($conn); - return $this->formatWhereClause($where); + return $this->formatWhereClause($conn, $where); } protected function willFilterPage(array $page) { diff --git a/src/applications/phrequent/query/PhrequentUserTimeQuery.php b/src/applications/phrequent/query/PhrequentUserTimeQuery.php index d0d1160df0..7fb77f0f76 100644 --- a/src/applications/phrequent/query/PhrequentUserTimeQuery.php +++ b/src/applications/phrequent/query/PhrequentUserTimeQuery.php @@ -116,7 +116,7 @@ final class PhrequentUserTimeQuery $where[] = $this->buildPagingClause($conn); - return $this->formatWhereClause($where); + return $this->formatWhereClause($conn, $where); } public function getOrderableColumns() { diff --git a/src/applications/releeph/query/ReleephBranchQuery.php b/src/applications/releeph/query/ReleephBranchQuery.php index 9d7c884012..97e47bdcaf 100644 --- a/src/applications/releeph/query/ReleephBranchQuery.php +++ b/src/applications/releeph/query/ReleephBranchQuery.php @@ -103,26 +103,26 @@ final class ReleephBranchQuery return $branches; } - protected function buildWhereClause(AphrontDatabaseConnection $conn_r) { + protected function buildWhereClause(AphrontDatabaseConnection $conn) { $where = array(); if ($this->ids !== null) { $where[] = qsprintf( - $conn_r, + $conn, 'id IN (%Ld)', $this->ids); } if ($this->phids !== null) { $where[] = qsprintf( - $conn_r, + $conn, 'phid IN (%Ls)', $this->phids); } if ($this->productIDs !== null) { $where[] = qsprintf( - $conn_r, + $conn, 'releephProjectID IN (%Ld)', $this->productIDs); } @@ -133,16 +133,16 @@ final class ReleephBranchQuery break; case self::STATUS_OPEN: $where[] = qsprintf( - $conn_r, + $conn, 'isActive = 1'); break; default: throw new Exception(pht("Unknown status constant '%s'!", $status)); } - $where[] = $this->buildPagingClause($conn_r); + $where[] = $this->buildPagingClause($conn); - return $this->formatWhereClause($where); + return $this->formatWhereClause($conn, $where); } public function getQueryApplicationClass() { diff --git a/src/applications/releeph/query/ReleephProductQuery.php b/src/applications/releeph/query/ReleephProductQuery.php index acfc39c1c2..c039950379 100644 --- a/src/applications/releeph/query/ReleephProductQuery.php +++ b/src/applications/releeph/query/ReleephProductQuery.php @@ -83,40 +83,40 @@ final class ReleephProductQuery return $projects; } - protected function buildWhereClause(AphrontDatabaseConnection $conn_r) { + protected function buildWhereClause(AphrontDatabaseConnection $conn) { $where = array(); if ($this->active !== null) { $where[] = qsprintf( - $conn_r, + $conn, 'isActive = %d', (int)$this->active); } if ($this->ids !== null) { $where[] = qsprintf( - $conn_r, + $conn, 'id IN (%Ls)', $this->ids); } if ($this->phids !== null) { $where[] = qsprintf( - $conn_r, + $conn, 'phid IN (%Ls)', $this->phids); } if ($this->repositoryPHIDs !== null) { $where[] = qsprintf( - $conn_r, + $conn, 'repositoryPHID IN (%Ls)', $this->repositoryPHIDs); } - $where[] = $this->buildPagingClause($conn_r); + $where[] = $this->buildPagingClause($conn); - return $this->formatWhereClause($where); + return $this->formatWhereClause($conn, $where); } public function getOrderableColumns() { diff --git a/src/applications/releeph/query/ReleephRequestQuery.php b/src/applications/releeph/query/ReleephRequestQuery.php index 7d4f19e624..3042260387 100644 --- a/src/applications/releeph/query/ReleephRequestQuery.php +++ b/src/applications/releeph/query/ReleephRequestQuery.php @@ -147,54 +147,54 @@ final class ReleephRequestQuery return $requests; } - protected function buildWhereClause(AphrontDatabaseConnection $conn_r) { + protected function buildWhereClause(AphrontDatabaseConnection $conn) { $where = array(); if ($this->ids !== null) { $where[] = qsprintf( - $conn_r, + $conn, 'id IN (%Ld)', $this->ids); } if ($this->phids !== null) { $where[] = qsprintf( - $conn_r, + $conn, 'phid IN (%Ls)', $this->phids); } if ($this->branchIDs !== null) { $where[] = qsprintf( - $conn_r, + $conn, 'branchID IN (%Ld)', $this->branchIDs); } if ($this->requestedCommitPHIDs !== null) { $where[] = qsprintf( - $conn_r, + $conn, 'requestCommitPHID IN (%Ls)', $this->requestedCommitPHIDs); } if ($this->requestorPHIDs !== null) { $where[] = qsprintf( - $conn_r, + $conn, 'requestUserPHID IN (%Ls)', $this->requestorPHIDs); } if ($this->requestedObjectPHIDs !== null) { $where[] = qsprintf( - $conn_r, + $conn, 'requestedObjectPHID IN (%Ls)', $this->requestedObjectPHIDs); } - $where[] = $this->buildPagingClause($conn_r); + $where[] = $this->buildPagingClause($conn); - return $this->formatWhereClause($where); + return $this->formatWhereClause($conn, $where); } private function getKeepStatusConstants() { diff --git a/src/applications/search/query/PhabricatorSavedQueryQuery.php b/src/applications/search/query/PhabricatorSavedQueryQuery.php index 623b001662..765c751940 100644 --- a/src/applications/search/query/PhabricatorSavedQueryQuery.php +++ b/src/applications/search/query/PhabricatorSavedQueryQuery.php @@ -37,33 +37,33 @@ final class PhabricatorSavedQueryQuery return $table->loadAllFromArray($data); } - protected function buildWhereClause(AphrontDatabaseConnection $conn_r) { + protected function buildWhereClause(AphrontDatabaseConnection $conn) { $where = array(); if ($this->ids !== null) { $where[] = qsprintf( - $conn_r, + $conn, 'id IN (%Ld)', $this->ids); } if ($this->engineClassNames !== null) { $where[] = qsprintf( - $conn_r, + $conn, 'engineClassName IN (%Ls)', $this->engineClassNames); } if ($this->queryKeys !== null) { $where[] = qsprintf( - $conn_r, + $conn, 'queryKey IN (%Ls)', $this->queryKeys); } - $where[] = $this->buildPagingClause($conn_r); + $where[] = $this->buildPagingClause($conn); - return $this->formatWhereClause($where); + return $this->formatWhereClause($conn, $where); } public function getQueryApplicationClass() { diff --git a/src/applications/tokens/query/PhabricatorTokenCountQuery.php b/src/applications/tokens/query/PhabricatorTokenCountQuery.php index 64333715fd..c4694af607 100644 --- a/src/applications/tokens/query/PhabricatorTokenCountQuery.php +++ b/src/applications/tokens/query/PhabricatorTokenCountQuery.php @@ -24,17 +24,17 @@ final class PhabricatorTokenCountQuery return ipull($rows, 'tokenCount', 'objectPHID'); } - protected function buildWhereClause(AphrontDatabaseConnection $conn_r) { + protected function buildWhereClause(AphrontDatabaseConnection $conn) { $where = array(); if ($this->objectPHIDs) { $where[] = qsprintf( - $conn_r, + $conn, 'objectPHID IN (%Ls)', $this->objectPHIDs); } - return $this->formatWhereClause($where); + return $this->formatWhereClause($conn, $where); } } diff --git a/src/applications/transactions/query/PhabricatorApplicationTransactionCommentQuery.php b/src/applications/transactions/query/PhabricatorApplicationTransactionCommentQuery.php index f79de81ba3..4ca56101fc 100644 --- a/src/applications/transactions/query/PhabricatorApplicationTransactionCommentQuery.php +++ b/src/applications/transactions/query/PhabricatorApplicationTransactionCommentQuery.php @@ -57,46 +57,48 @@ abstract class PhabricatorApplicationTransactionCommentQuery return $table->loadAllFromArray($data); } - protected function buildWhereClause(AphrontDatabaseConnection $conn_r) { - return $this->formatWhereClause($this->buildWhereClauseComponents($conn_r)); + protected function buildWhereClause(AphrontDatabaseConnection $conn) { + return $this->formatWhereClause( + $conn, + $this->buildWhereClauseComponents($conn)); } protected function buildWhereClauseComponents( - AphrontDatabaseConnection $conn_r) { + AphrontDatabaseConnection $conn) { $where = array(); if ($this->ids !== null) { $where[] = qsprintf( - $conn_r, + $conn, 'xcomment.id IN (%Ld)', $this->ids); } if ($this->phids !== null) { $where[] = qsprintf( - $conn_r, + $conn, 'xcomment.phid IN (%Ls)', $this->phids); } if ($this->authorPHIDs !== null) { $where[] = qsprintf( - $conn_r, + $conn, 'xcomment.authorPHID IN (%Ls)', $this->authorPHIDs); } if ($this->transactionPHIDs !== null) { $where[] = qsprintf( - $conn_r, + $conn, 'xcomment.transactionPHID IN (%Ls)', $this->transactionPHIDs); } if ($this->isDeleted !== null) { $where[] = qsprintf( - $conn_r, + $conn, 'xcomment.isDeleted = %d', (int)$this->isDeleted); } @@ -104,11 +106,11 @@ abstract class PhabricatorApplicationTransactionCommentQuery if ($this->hasTransaction !== null) { if ($this->hasTransaction) { $where[] = qsprintf( - $conn_r, + $conn, 'xcomment.transactionPHID IS NOT NULL'); } else { $where[] = qsprintf( - $conn_r, + $conn, 'xcomment.transactionPHID IS NULL'); } } diff --git a/src/infrastructure/daemon/workers/query/PhabricatorWorkerLeaseQuery.php b/src/infrastructure/daemon/workers/query/PhabricatorWorkerLeaseQuery.php index bb70f03d88..0163143ae7 100644 --- a/src/infrastructure/daemon/workers/query/PhabricatorWorkerLeaseQuery.php +++ b/src/infrastructure/daemon/workers/query/PhabricatorWorkerLeaseQuery.php @@ -209,39 +209,47 @@ final class PhabricatorWorkerLeaseQuery extends PhabricatorQuery { } protected function buildCustomWhereClause( - AphrontDatabaseConnection $conn_w, + AphrontDatabaseConnection $conn, $phase) { $where = array(); switch ($phase) { case self::PHASE_LEASED: - $where[] = 'leaseOwner IS NOT NULL'; - $where[] = 'leaseExpires >= UNIX_TIMESTAMP()'; + $where[] = qsprintf( + $conn, + 'leaseOwner IS NOT NULL'); + $where[] = qsprintf( + $conn, + 'leaseExpires >= UNIX_TIMESTAMP()'); break; case self::PHASE_UNLEASED: - $where[] = 'leaseOwner IS NULL'; + $where[] = qsprintf( + $conn, + 'leaseOwner IS NULL'); break; case self::PHASE_EXPIRED: - $where[] = 'leaseExpires < UNIX_TIMESTAMP()'; + $where[] = qsprintf( + $conn, + 'leaseExpires < UNIX_TIMESTAMP()'); break; default: throw new Exception(pht("Unknown phase '%s'!", $phase)); } if ($this->ids !== null) { - $where[] = qsprintf($conn_w, 'id IN (%Ld)', $this->ids); + $where[] = qsprintf($conn, 'id IN (%Ld)', $this->ids); } if ($this->objectPHIDs !== null) { - $where[] = qsprintf($conn_w, 'objectPHID IN (%Ls)', $this->objectPHIDs); + $where[] = qsprintf($conn, 'objectPHID IN (%Ls)', $this->objectPHIDs); } - return $this->formatWhereClause($where); + return $this->formatWhereClause($conn, $where); } private function buildUpdateWhereClause( - AphrontDatabaseConnection $conn_w, + AphrontDatabaseConnection $conn, $phase, array $rows) { @@ -257,25 +265,25 @@ final class PhabricatorWorkerLeaseQuery extends PhabricatorQuery { 'Trying to lease tasks selected in the leased phase! This is '. 'intended to be impossible.')); case self::PHASE_UNLEASED: - $where[] = qsprintf($conn_w, 'leaseOwner IS NULL'); - $where[] = qsprintf($conn_w, 'id IN (%Ld)', ipull($rows, 'id')); + $where[] = qsprintf($conn, 'leaseOwner IS NULL'); + $where[] = qsprintf($conn, 'id IN (%Ld)', ipull($rows, 'id')); break; case self::PHASE_EXPIRED: $in = array(); foreach ($rows as $row) { $in[] = qsprintf( - $conn_w, + $conn, '(id = %d AND leaseOwner = %s)', $row['id'], $row['leaseOwner']); } - $where[] = qsprintf($conn_w, '(%Q)', implode(' OR ', $in)); + $where[] = qsprintf($conn, '%LO', $in); break; default: throw new Exception(pht('Unknown phase "%s"!', $phase)); } - return $this->formatWhereClause($where); + return $this->formatWhereClause($conn, $where); } private function buildOrderClause(AphrontDatabaseConnection $conn_w, $phase) { diff --git a/src/infrastructure/daemon/workers/query/PhabricatorWorkerTaskQuery.php b/src/infrastructure/daemon/workers/query/PhabricatorWorkerTaskQuery.php index ae6e2cc442..fa9a521d0f 100644 --- a/src/infrastructure/daemon/workers/query/PhabricatorWorkerTaskQuery.php +++ b/src/infrastructure/daemon/workers/query/PhabricatorWorkerTaskQuery.php @@ -48,59 +48,59 @@ abstract class PhabricatorWorkerTaskQuery return $this; } - protected function buildWhereClause(AphrontDatabaseConnection $conn_r) { + protected function buildWhereClause(AphrontDatabaseConnection $conn) { $where = array(); if ($this->ids !== null) { $where[] = qsprintf( - $conn_r, + $conn, 'id in (%Ld)', $this->ids); } if ($this->objectPHIDs !== null) { $where[] = qsprintf( - $conn_r, + $conn, 'objectPHID IN (%Ls)', $this->objectPHIDs); } if ($this->dateModifiedSince !== null) { $where[] = qsprintf( - $conn_r, + $conn, 'dateModified > %d', $this->dateModifiedSince); } if ($this->dateCreatedBefore !== null) { $where[] = qsprintf( - $conn_r, + $conn, 'dateCreated < %d', $this->dateCreatedBefore); } if ($this->classNames !== null) { $where[] = qsprintf( - $conn_r, + $conn, 'taskClass IN (%Ls)', $this->classNames); } if ($this->minFailureCount !== null) { $where[] = qsprintf( - $conn_r, + $conn, 'failureCount >= %d', $this->minFailureCount); } if ($this->maxFailureCount !== null) { $where[] = qsprintf( - $conn_r, + $conn, 'failureCount <= %d', $this->maxFailureCount); } - return $this->formatWhereClause($where); + return $this->formatWhereClause($conn, $where); } protected function buildOrderClause(AphrontDatabaseConnection $conn_r) { diff --git a/src/infrastructure/daemon/workers/query/PhabricatorWorkerTriggerQuery.php b/src/infrastructure/daemon/workers/query/PhabricatorWorkerTriggerQuery.php index a8dc5061e7..931b09f1f9 100644 --- a/src/infrastructure/daemon/workers/query/PhabricatorWorkerTriggerQuery.php +++ b/src/infrastructure/daemon/workers/query/PhabricatorWorkerTriggerQuery.php @@ -160,52 +160,52 @@ final class PhabricatorWorkerTriggerQuery return implode(' ', $joins); } - protected function buildWhereClause(AphrontDatabaseConnection $conn_r) { + protected function buildWhereClause(AphrontDatabaseConnection $conn) { $where = array(); if ($this->ids !== null) { $where[] = qsprintf( - $conn_r, + $conn, 't.id IN (%Ld)', $this->ids); } if ($this->phids !== null) { $where[] = qsprintf( - $conn_r, + $conn, 't.phid IN (%Ls)', $this->phids); } if ($this->versionMin !== null) { $where[] = qsprintf( - $conn_r, + $conn, 't.triggerVersion >= %d', $this->versionMin); } if ($this->versionMax !== null) { $where[] = qsprintf( - $conn_r, + $conn, 't.triggerVersion <= %d', $this->versionMax); } if ($this->nextEpochMin !== null) { $where[] = qsprintf( - $conn_r, + $conn, 'e.nextEventEpoch >= %d', $this->nextEpochMin); } if ($this->nextEpochMax !== null) { $where[] = qsprintf( - $conn_r, + $conn, 'e.nextEventEpoch <= %d', $this->nextEpochMax); } - return $this->formatWhereClause($where); + return $this->formatWhereClause($conn, $where); } private function buildOrderClause(AphrontDatabaseConnection $conn_r) { diff --git a/src/infrastructure/edges/query/PhabricatorEdgeQuery.php b/src/infrastructure/edges/query/PhabricatorEdgeQuery.php index 2dfceb7fbc..edd78c868e 100644 --- a/src/infrastructure/edges/query/PhabricatorEdgeQuery.php +++ b/src/infrastructure/edges/query/PhabricatorEdgeQuery.php @@ -290,19 +290,19 @@ final class PhabricatorEdgeQuery extends PhabricatorQuery { /** * @task internal */ - protected function buildWhereClause(AphrontDatabaseConnection $conn_r) { + protected function buildWhereClause(AphrontDatabaseConnection $conn) { $where = array(); if ($this->sourcePHIDs) { $where[] = qsprintf( - $conn_r, + $conn, 'edge.src IN (%Ls)', $this->sourcePHIDs); } if ($this->edgeTypes) { $where[] = qsprintf( - $conn_r, + $conn, 'edge.type IN (%Ls)', $this->edgeTypes); } @@ -310,12 +310,12 @@ final class PhabricatorEdgeQuery extends PhabricatorQuery { if ($this->destPHIDs) { // potentially complain if $this->edgeType was not set $where[] = qsprintf( - $conn_r, + $conn, 'edge.dst IN (%Ls)', $this->destPHIDs); } - return $this->formatWhereClause($where); + return $this->formatWhereClause($conn, $where); } diff --git a/src/infrastructure/query/PhabricatorOffsetPagedQuery.php b/src/infrastructure/query/PhabricatorOffsetPagedQuery.php index ef97a4ebe4..fd9ea18e3f 100644 --- a/src/infrastructure/query/PhabricatorOffsetPagedQuery.php +++ b/src/infrastructure/query/PhabricatorOffsetPagedQuery.php @@ -27,15 +27,15 @@ abstract class PhabricatorOffsetPagedQuery extends PhabricatorQuery { return $this->limit; } - protected function buildLimitClause(AphrontDatabaseConnection $conn_r) { + protected function buildLimitClause(AphrontDatabaseConnection $conn) { if ($this->limit && $this->offset) { - return qsprintf($conn_r, 'LIMIT %d, %d', $this->offset, $this->limit); + return qsprintf($conn, 'LIMIT %d, %d', $this->offset, $this->limit); } else if ($this->limit) { - return qsprintf($conn_r, 'LIMIT %d', $this->limit); + return qsprintf($conn, 'LIMIT %d', $this->limit); } else if ($this->offset) { - return qsprintf($conn_r, 'LIMIT %d, %d', $this->offset, PHP_INT_MAX); + return qsprintf($conn, 'LIMIT %d, %d', $this->offset, PHP_INT_MAX); } else { - return ''; + return qsprintf($conn, ''); } } diff --git a/src/infrastructure/query/PhabricatorQuery.php b/src/infrastructure/query/PhabricatorQuery.php index 1dfe14b6f2..4315ef79ae 100644 --- a/src/infrastructure/query/PhabricatorQuery.php +++ b/src/infrastructure/query/PhabricatorQuery.php @@ -15,28 +15,19 @@ abstract class PhabricatorQuery extends Phobject { /** * @task format */ - protected function formatWhereClause(array $parts) { + protected function formatWhereClause( + AphrontDatabaseConnection $conn, + array $parts) { + $parts = $this->flattenSubclause($parts); if (!$parts) { - return ''; + return qsprintf($conn, ''); } - return 'WHERE '.$this->formatWhereSubclause($parts); + return qsprintf($conn, 'WHERE %LA', $parts); } - /** - * @task format - */ - protected function formatWhereSubclause(array $parts) { - $parts = $this->flattenSubclause($parts); - if (!$parts) { - return null; - } - - return '('.implode(') AND (', $parts).')'; - } - /** * @task format @@ -57,39 +48,32 @@ abstract class PhabricatorQuery extends Phobject { /** * @task format */ - protected function formatJoinClause(array $parts) { + protected function formatJoinClause( + AphrontDatabaseConnection $conn, + array $parts) { + $parts = $this->flattenSubclause($parts); if (!$parts) { - return ''; + return qsprintf($conn, ''); } - return implode(' ', $parts); + return qsprintf($conn, '%LJ', $parts); } /** * @task format */ - protected function formatHavingClause(array $parts) { + protected function formatHavingClause( + AphrontDatabaseConnection $conn, + array $parts) { + $parts = $this->flattenSubclause($parts); if (!$parts) { - return ''; + return qsprintf($conn, ''); } - return 'HAVING '.$this->formatHavingSubclause($parts); - } - - - /** - * @task format - */ - protected function formatHavingSubclause(array $parts) { - $parts = $this->flattenSubclause($parts); - if (!$parts) { - return null; - } - - return '('.implode(') AND (', $parts).')'; + return qsprintf($conn, 'HAVING %LA', $parts); } diff --git a/src/infrastructure/query/policy/PhabricatorCursorPagedPolicyAwareQuery.php b/src/infrastructure/query/policy/PhabricatorCursorPagedPolicyAwareQuery.php index cd101a61d9..49ab55ef4f 100644 --- a/src/infrastructure/query/policy/PhabricatorCursorPagedPolicyAwareQuery.php +++ b/src/infrastructure/query/policy/PhabricatorCursorPagedPolicyAwareQuery.php @@ -195,15 +195,15 @@ abstract class PhabricatorCursorPagedPolicyAwareQuery } } - final protected function buildLimitClause(AphrontDatabaseConnection $conn_r) { + final protected function buildLimitClause(AphrontDatabaseConnection $conn) { if ($this->shouldLimitResults()) { $limit = $this->getRawResultLimit(); if ($limit) { - return qsprintf($conn_r, 'LIMIT %d', $limit); + return qsprintf($conn, 'LIMIT %d', $limit); } } - return ''; + return qsprintf($conn, ''); } protected function shouldLimitResults() { @@ -306,7 +306,7 @@ abstract class PhabricatorCursorPagedPolicyAwareQuery */ protected function buildJoinClause(AphrontDatabaseConnection $conn) { $joins = $this->buildJoinClauseParts($conn); - return $this->formatJoinClause($joins); + return $this->formatJoinClause($conn, $joins); } @@ -328,7 +328,7 @@ abstract class PhabricatorCursorPagedPolicyAwareQuery */ protected function buildWhereClause(AphrontDatabaseConnection $conn) { $where = $this->buildWhereClauseParts($conn); - return $this->formatWhereClause($where); + return $this->formatWhereClause($conn, $where); } @@ -352,7 +352,7 @@ abstract class PhabricatorCursorPagedPolicyAwareQuery */ protected function buildHavingClause(AphrontDatabaseConnection $conn) { $having = $this->buildHavingClauseParts($conn); - return $this->formatHavingClause($having); + return $this->formatHavingClause($conn, $having); } @@ -371,13 +371,13 @@ abstract class PhabricatorCursorPagedPolicyAwareQuery */ protected function buildGroupClause(AphrontDatabaseConnection $conn) { if (!$this->shouldGroupQueryResultRows()) { - return ''; + return qsprintf($conn, ''); } return qsprintf( $conn, 'GROUP BY %Q', - $this->getApplicationSearchObjectPHIDColumn()); + $this->getApplicationSearchObjectPHIDColumn($conn)); } @@ -1134,7 +1134,7 @@ abstract class PhabricatorCursorPagedPolicyAwareQuery } } - return qsprintf($conn, 'ORDER BY %Q', implode(', ', $sql)); + return qsprintf($conn, 'ORDER BY %LQ', $sql); } @@ -1244,17 +1244,18 @@ abstract class PhabricatorCursorPagedPolicyAwareQuery * See @{method:getPrimaryTableAlias} if the column needs to be qualified with * a table alias. * - * @return string Column name. + * @param AphrontDatabaseConnection Connection executing queries. + * @return PhutilQueryString Column name. * @task appsearch */ - protected function getApplicationSearchObjectPHIDColumn() { - if ($this->getPrimaryTableAlias()) { - $prefix = $this->getPrimaryTableAlias().'.'; - } else { - $prefix = ''; - } + protected function getApplicationSearchObjectPHIDColumn( + AphrontDatabaseConnection $conn) { - return $prefix.'phid'; + if ($this->getPrimaryTableAlias()) { + return qsprintf($conn, '%T.phid', $this->getPrimaryTableAlias()); + } else { + return qsprintf($conn, 'phid'); + } } @@ -1308,15 +1309,15 @@ abstract class PhabricatorCursorPagedPolicyAwareQuery * @task appsearch */ protected function buildApplicationSearchGroupClause( - AphrontDatabaseConnection $conn_r) { + AphrontDatabaseConnection $conn) { if ($this->getApplicationSearchMayJoinMultipleRows()) { return qsprintf( - $conn_r, + $conn, 'GROUP BY %Q', $this->getApplicationSearchObjectPHIDColumn()); } else { - return ''; + return qsprintf($conn, ''); } } @@ -1410,7 +1411,7 @@ abstract class PhabricatorCursorPagedPolicyAwareQuery } } - $phid_column = $this->getApplicationSearchObjectPHIDColumn(); + $phid_column = $this->getApplicationSearchObjectPHIDColumn($conn); $orderable = $this->getOrderableColumns(); $vector = $this->getOrderVector(); @@ -2373,7 +2374,7 @@ abstract class PhabricatorCursorPagedPolicyAwareQuery */ public function buildEdgeLogicJoinClause(AphrontDatabaseConnection $conn) { $edge_table = PhabricatorEdgeConfig::TABLE_NAME_EDGE; - $phid_column = $this->getApplicationSearchObjectPHIDColumn(); + $phid_column = $this->getApplicationSearchObjectPHIDColumn($conn); $joins = array(); foreach ($this->edgeLogicConstraints as $type => $constraints) { @@ -2531,9 +2532,7 @@ abstract class PhabricatorCursorPagedPolicyAwareQuery } if ($full && $null) { - $full = $this->formatWhereSubclause($full); - $null = $this->formatWhereSubclause($null); - $where[] = qsprintf($conn, '(%Q OR %Q)', $full, $null); + $where[] = qsprintf($conn, '(%LA OR %LA)', $full, $null); } else if ($full) { foreach ($full as $condition) { $where[] = $condition; diff --git a/src/infrastructure/storage/lisk/LiskDAO.php b/src/infrastructure/storage/lisk/LiskDAO.php index 583d8226f6..03dbf51961 100644 --- a/src/infrastructure/storage/lisk/LiskDAO.php +++ b/src/infrastructure/storage/lisk/LiskDAO.php @@ -517,13 +517,14 @@ abstract class LiskDAO extends Phobject protected function loadRawDataWhere($pattern /* , $args... */) { - $connection = $this->establishConnection('r'); + $conn = $this->establishConnection('r'); - $lock_clause = ''; - if ($connection->isReadLocking()) { - $lock_clause = 'FOR UPDATE'; - } else if ($connection->isWriteLocking()) { - $lock_clause = 'LOCK IN SHARE MODE'; + if ($conn->isReadLocking()) { + $lock_clause = qsprintf($conn, 'FOR UPDATE'); + } else if ($conn->isWriteLocking()) { + $lock_clause = qsprintf($conn, 'LOCK IN SHARE MODE'); + } else { + $lock_clause = qsprintf($conn, ''); } $args = func_get_args(); @@ -534,9 +535,7 @@ abstract class LiskDAO extends Phobject array_push($args, $lock_clause); array_unshift($args, $pattern); - return call_user_func_array( - array($connection, 'queryData'), - $args); + return call_user_func_array(array($conn, 'queryData'), $args); } From 2f10d4adebf9519492f6b01001d8776ed8dc3850 Mon Sep 17 00:00:00 2001 From: epriestley Date: Wed, 7 Nov 2018 03:57:28 -0800 Subject: [PATCH 05/43] Continue making application fixes to Phabricator for changes to %Q semantics Summary: Depends on D19789. Ref T13217. Continue updating things to use the new %Q-flavored conversions instead of smushing a bunch of strings together. Test Plan: Browsed around, far fewer errors. These changes are largely mechanical in nature. Reviewers: amckinley Reviewed By: amckinley Maniphest Tasks: T13217 Differential Revision: https://secure.phabricator.com/D19790 --- .../engine/PhabricatorAuthSessionEngine.php | 8 +-- .../auth/query/PhabricatorAuthSSHKeyQuery.php | 2 +- .../query/PhabricatorCalendarEventQuery.php | 1 - .../query/DifferentialRevisionQuery.php | 32 ++++++++---- .../editor/ManiphestTransactionEditor.php | 49 ++++++++++--------- .../maniphest/query/ManiphestTaskQuery.php | 16 +++--- .../people/storage/PhabricatorUser.php | 4 +- .../cluster/PhabricatorDatabaseRef.php | 10 +++- .../edges/editor/PhabricatorEdgeEditor.php | 10 ++-- .../edges/query/PhabricatorEdgeQuery.php | 6 +-- ...PhabricatorCursorPagedPolicyAwareQuery.php | 45 +++++++++-------- src/infrastructure/storage/lisk/LiskDAO.php | 22 +++++++-- 12 files changed, 120 insertions(+), 85 deletions(-) diff --git a/src/applications/auth/engine/PhabricatorAuthSessionEngine.php b/src/applications/auth/engine/PhabricatorAuthSessionEngine.php index 6e40cdde98..76c0b310a3 100644 --- a/src/applications/auth/engine/PhabricatorAuthSessionEngine.php +++ b/src/applications/auth/engine/PhabricatorAuthSessionEngine.php @@ -809,15 +809,15 @@ final class PhabricatorAuthSessionEngine extends Phobject { } if ($cache_selects) { - $cache_selects = ', '.implode(', ', $cache_selects); + $cache_selects = qsprintf($conn, ', %LQ', $cache_selects); } else { - $cache_selects = ''; + $cache_selects = qsprintf($conn, ''); } if ($cache_joins) { - $cache_joins = implode(' ', $cache_joins); + $cache_joins = qsprintf($conn, '%LJ', $cache_joins); } else { - $cache_joins = ''; + $cache_joins = qsprintf($conn, ''); } return array($cache_selects, $cache_joins, $cache_map, $types_map); diff --git a/src/applications/auth/query/PhabricatorAuthSSHKeyQuery.php b/src/applications/auth/query/PhabricatorAuthSSHKeyQuery.php index d8474085b2..3a310ed173 100644 --- a/src/applications/auth/query/PhabricatorAuthSSHKeyQuery.php +++ b/src/applications/auth/query/PhabricatorAuthSSHKeyQuery.php @@ -111,7 +111,7 @@ final class PhabricatorAuthSSHKeyQuery $key->getType(), $key->getHash()); } - $where[] = implode(' OR ', $sql); + $where[] = qsprintf($conn, '%LO', $sql); } if ($this->isActive !== null) { diff --git a/src/applications/calendar/query/PhabricatorCalendarEventQuery.php b/src/applications/calendar/query/PhabricatorCalendarEventQuery.php index 2be76a631f..c35622fc0e 100644 --- a/src/applications/calendar/query/PhabricatorCalendarEventQuery.php +++ b/src/applications/calendar/query/PhabricatorCalendarEventQuery.php @@ -513,7 +513,6 @@ final class PhabricatorCalendarEventQuery return 'PhabricatorCalendarApplication'; } - protected function willFilterPage(array $events) { $instance_of_event_phids = array(); $recurring_events = array(); diff --git a/src/applications/differential/query/DifferentialRevisionQuery.php b/src/applications/differential/query/DifferentialRevisionQuery.php index d24106328b..fdd4904bee 100644 --- a/src/applications/differential/query/DifferentialRevisionQuery.php +++ b/src/applications/differential/query/DifferentialRevisionQuery.php @@ -453,7 +453,7 @@ final class DifferentialRevisionQuery private function loadData() { $table = $this->newResultObject(); - $conn_r = $table->establishConnection('r'); + $conn = $table->establishConnection('r'); $selects = array(); @@ -469,13 +469,13 @@ final class DifferentialRevisionQuery $this->authors = array_merge($basic_authors, $this->responsibles); $this->reviewers = $basic_reviewers; - $selects[] = $this->buildSelectStatement($conn_r); + $selects[] = $this->buildSelectStatement($conn); // Build the query where the responsible users are reviewers, or // projects they are members of are reviewers. $this->authors = $basic_authors; $this->reviewers = array_merge($basic_reviewers, $this->responsibles); - $selects[] = $this->buildSelectStatement($conn_r); + $selects[] = $this->buildSelectStatement($conn); // Put everything back like it was. $this->authors = $basic_authors; @@ -486,21 +486,35 @@ final class DifferentialRevisionQuery throw $ex; } } else { - $selects[] = $this->buildSelectStatement($conn_r); + $selects[] = $this->buildSelectStatement($conn); } if (count($selects) > 1) { + $unions = null; + foreach ($selects as $select) { + if (!$unions) { + $unions = $select; + continue; + } + + $unions = qsprintf( + $conn, + '%Q UNION DISTINCT %Q', + $unions, + $select); + } + $query = qsprintf( - $conn_r, + $conn, '%Q %Q %Q', - implode(' UNION DISTINCT ', $selects), - $this->buildOrderClause($conn_r, true), - $this->buildLimitClause($conn_r)); + $unions, + $this->buildOrderClause($conn, true), + $this->buildLimitClause($conn)); } else { $query = head($selects); } - return queryfx_all($conn_r, '%Q', $query); + return queryfx_all($conn, '%Q', $query); } private function buildSelectStatement(AphrontDatabaseConnection $conn_r) { diff --git a/src/applications/maniphest/editor/ManiphestTransactionEditor.php b/src/applications/maniphest/editor/ManiphestTransactionEditor.php index 19fd57b0d2..4b6c7707f6 100644 --- a/src/applications/maniphest/editor/ManiphestTransactionEditor.php +++ b/src/applications/maniphest/editor/ManiphestTransactionEditor.php @@ -471,27 +471,28 @@ final class ManiphestTransactionEditor // be worth evaluating is to use "CASE". Another approach is to disable // strict mode for this query. - $extra_columns = array( - 'phid' => '""', - 'authorPHID' => '""', - 'status' => '""', - 'priority' => 0, - 'title' => '""', - 'description' => '""', - 'dateCreated' => 0, - 'dateModified' => 0, - 'mailKey' => '""', - 'viewPolicy' => '""', - 'editPolicy' => '""', - 'ownerOrdering' => '""', - 'spacePHID' => '""', - 'bridgedObjectPHID' => '""', - 'properties' => '""', - 'points' => 0, - 'subtype' => '""', - ); + $default_str = qsprintf($conn, '%s', ''); + $default_int = qsprintf($conn, '%d', 0); - $defaults = implode(', ', $extra_columns); + $extra_columns = array( + 'phid' => $default_str, + 'authorPHID' => $default_str, + 'status' => $default_str, + 'priority' => $default_int, + 'title' => $default_str, + 'description' => $default_str, + 'dateCreated' => $default_int, + 'dateModified' => $default_int, + 'mailKey' => $default_str, + 'viewPolicy' => $default_str, + 'editPolicy' => $default_str, + 'ownerOrdering' => $default_str, + 'spacePHID' => $default_str, + 'bridgedObjectPHID' => $default_str, + 'properties' => $default_str, + 'points' => $default_int, + 'subtype' => $default_str, + ); $sql = array(); $offset = 0; @@ -520,9 +521,9 @@ final class ManiphestTransactionEditor $sql[] = qsprintf( $conn, - '(%d, %Q, %f)', + '(%d, %LQ, %f)', $id, - $defaults, + $extra_columns, $subpriority); $offset++; @@ -531,10 +532,10 @@ final class ManiphestTransactionEditor foreach (PhabricatorLiskDAO::chunkSQL($sql) as $chunk) { queryfx( $conn, - 'INSERT INTO %T (id, %Q, subpriority) VALUES %LQ + 'INSERT INTO %T (id, %LC, subpriority) VALUES %LQ ON DUPLICATE KEY UPDATE subpriority = VALUES(subpriority)', $task->getTableName(), - implode(', ', array_keys($extra_columns)), + array_keys($extra_columns), $chunk); } diff --git a/src/applications/maniphest/query/ManiphestTaskQuery.php b/src/applications/maniphest/query/ManiphestTaskQuery.php index ccf93c4a2d..d1932a7597 100644 --- a/src/applications/maniphest/query/ManiphestTaskQuery.php +++ b/src/applications/maniphest/query/ManiphestTaskQuery.php @@ -237,7 +237,7 @@ final class ManiphestTaskQuery extends PhabricatorCursorPagedPolicyAwareQuery { $where = $this->buildWhereClause($conn); - $group_column = ''; + $group_column = qsprintf($conn, ''); switch ($this->groupBy) { case self::GROUP_PROJECT: $group_column = qsprintf( @@ -601,10 +601,10 @@ final class ManiphestTaskQuery extends PhabricatorCursorPagedPolicyAwareQuery { } if (!$subclause) { - return ''; + return qsprintf($conn, ''); } - return '('.implode(') OR (', $subclause).')'; + return qsprintf($conn, '%LO', $subclause); } protected function buildJoinClauseParts(AphrontDatabaseConnection $conn) { @@ -736,7 +736,7 @@ final class ManiphestTaskQuery extends PhabricatorCursorPagedPolicyAwareQuery { return $joins; } - protected function buildGroupClause(AphrontDatabaseConnection $conn_r) { + protected function buildGroupClause(AphrontDatabaseConnection $conn) { $joined_multiple_rows = ($this->hasOpenParents !== null) || ($this->hasOpenSubtasks !== null) || @@ -750,13 +750,13 @@ final class ManiphestTaskQuery extends PhabricatorCursorPagedPolicyAwareQuery { // task IDs. if ($joined_multiple_rows) { if ($joined_project_name) { - return 'GROUP BY task.phid, projectGroup.dst'; + return qsprintf($conn, 'GROUP BY task.phid, projectGroup.dst'); } else { - return 'GROUP BY task.phid'; + return qsprintf($conn, 'GROUP BY task.phid'); } - } else { - return ''; } + + return qsprintf($conn, ''); } diff --git a/src/applications/people/storage/PhabricatorUser.php b/src/applications/people/storage/PhabricatorUser.php index e2a0f9a2a2..216e451517 100644 --- a/src/applications/people/storage/PhabricatorUser.php +++ b/src/applications/people/storage/PhabricatorUser.php @@ -663,9 +663,9 @@ final class PhabricatorUser if ($sql) { queryfx( $conn_w, - 'INSERT INTO %T (userID, token) VALUES %Q', + 'INSERT INTO %T (userID, token) VALUES %LQ', $table, - implode(', ', $sql)); + $sql); } } diff --git a/src/infrastructure/cluster/PhabricatorDatabaseRef.php b/src/infrastructure/cluster/PhabricatorDatabaseRef.php index 7dc55427ca..89435b5869 100644 --- a/src/infrastructure/cluster/PhabricatorDatabaseRef.php +++ b/src/infrastructure/cluster/PhabricatorDatabaseRef.php @@ -557,8 +557,14 @@ final class PhabricatorDatabaseRef $conn = $this->newManagementConnection(); try { - $value = queryfx_one($conn, 'SELECT @@%Q', $key); - $value = $value['@@'.$key]; + $value = queryfx_one($conn, 'SELECT @@%C', $key); + + // NOTE: Although MySQL allows us to escape configuration values as if + // they are column names, the escaping is included in the column name + // of the return value: if we select "@@`x`", we get back a column named + // "@@`x`", not "@@x" as we might expect. + $value = head($value); + } catch (AphrontQueryException $ex) { $value = null; } diff --git a/src/infrastructure/edges/editor/PhabricatorEdgeEditor.php b/src/infrastructure/edges/editor/PhabricatorEdgeEditor.php index 700a11ea36..588b5267b4 100644 --- a/src/infrastructure/edges/editor/PhabricatorEdgeEditor.php +++ b/src/infrastructure/edges/editor/PhabricatorEdgeEditor.php @@ -275,13 +275,13 @@ final class PhabricatorEdgeEditor extends Phobject { $conn_w->openTransaction(); $this->openTransactions[] = $conn_w; - foreach (array_chunk($sql, 256) as $chunk) { + foreach (PhabricatorLiskDAO::chunkSQL($sql) as $chunk) { queryfx( $conn_w, 'INSERT INTO %T (src, type, dst, dateCreated, seq, dataID) - VALUES %Q ON DUPLICATE KEY UPDATE dataID = VALUES(dataID)', + VALUES %LQ ON DUPLICATE KEY UPDATE dataID = VALUES(dataID)', PhabricatorEdgeConfig::TABLE_NAME_EDGE, - implode(', ', $chunk)); + $chunk); } } } @@ -320,9 +320,9 @@ final class PhabricatorEdgeEditor extends Phobject { foreach (array_chunk($sql, 256) as $chunk) { queryfx( $conn_w, - 'DELETE FROM %T WHERE (%Q)', + 'DELETE FROM %T WHERE %LO', PhabricatorEdgeConfig::TABLE_NAME_EDGE, - implode(' OR ', $chunk)); + $chunk); } } } diff --git a/src/infrastructure/edges/query/PhabricatorEdgeQuery.php b/src/infrastructure/edges/query/PhabricatorEdgeQuery.php index edd78c868e..6519c47724 100644 --- a/src/infrastructure/edges/query/PhabricatorEdgeQuery.php +++ b/src/infrastructure/edges/query/PhabricatorEdgeQuery.php @@ -322,11 +322,11 @@ final class PhabricatorEdgeQuery extends PhabricatorQuery { /** * @task internal */ - private function buildOrderClause($conn_r) { + private function buildOrderClause(AphrontDatabaseConnection $conn) { if ($this->order == self::ORDER_NEWEST_FIRST) { - return 'ORDER BY edge.dateCreated DESC, edge.seq DESC'; + return qsprintf($conn, 'ORDER BY edge.dateCreated DESC, edge.seq DESC'); } else { - return 'ORDER BY edge.dateCreated ASC, edge.seq ASC'; + return qsprintf($conn, 'ORDER BY edge.dateCreated ASC, edge.seq ASC'); } } diff --git a/src/infrastructure/query/policy/PhabricatorCursorPagedPolicyAwareQuery.php b/src/infrastructure/query/policy/PhabricatorCursorPagedPolicyAwareQuery.php index 49ab55ef4f..162132d508 100644 --- a/src/infrastructure/query/policy/PhabricatorCursorPagedPolicyAwareQuery.php +++ b/src/infrastructure/query/policy/PhabricatorCursorPagedPolicyAwareQuery.php @@ -123,12 +123,19 @@ abstract class PhabricatorCursorPagedPolicyAwareQuery AphrontDatabaseConnection $conn, $table_name) { + $table_alias = $this->getPrimaryTableAlias(); + if ($table_alias === null) { + $table_alias = qsprintf($conn, ''); + } else { + $table_alias = qsprintf($conn, '%T', $table_alias); + } + return qsprintf( $conn, '%Q FROM %T %Q %Q %Q %Q %Q %Q %Q', $this->buildSelectClause($conn), $table_name, - (string)$this->getPrimaryTableAlias(), + $table_alias, $this->buildJoinClause($conn), $this->buildWhereClause($conn), $this->buildGroupClause($conn), @@ -425,7 +432,7 @@ abstract class PhabricatorCursorPagedPolicyAwareQuery } else { // No paging is being applied to this query so we do not need to // construct a paging clause. - return ''; + return qsprintf($conn, ''); } $keys = array(); @@ -655,24 +662,16 @@ abstract class PhabricatorCursorPagedPolicyAwareQuery $conn, '%Q %Q %Q', $field, - $reverse ? '>' : '<', + $reverse ? qsprintf($conn, '>') : qsprintf($conn, '<'), $value); } if ($parts) { - if (count($parts) > 1) { - $clause[] = '('.implode(') OR (', $parts).')'; - } else { - $clause[] = head($parts); - } + $clause[] = qsprintf($conn, '%LO', $parts); } if ($clause) { - if (count($clause) > 1) { - $clauses[] = '('.implode(') AND (', $clause).')'; - } else { - $clauses[] = head($clause); - } + $clauses[] = qsprintf($conn, '%LA', $clause); } if ($value === null) { @@ -689,7 +688,11 @@ abstract class PhabricatorCursorPagedPolicyAwareQuery } } - return '('.implode(') OR (', $clauses).')'; + if ($clauses) { + return qsprintf($conn, '%LO', $clauses); + } + + return qsprintf($conn, ''); } @@ -1315,7 +1318,7 @@ abstract class PhabricatorCursorPagedPolicyAwareQuery return qsprintf( $conn, 'GROUP BY %Q', - $this->getApplicationSearchObjectPHIDColumn()); + $this->getApplicationSearchObjectPHIDColumn($conn)); } else { return qsprintf($conn, ''); } @@ -1339,16 +1342,16 @@ abstract class PhabricatorCursorPagedPolicyAwareQuery $alias = $constraint['alias']; $index = $constraint['index']; $cond = $constraint['cond']; - $phid_column = $this->getApplicationSearchObjectPHIDColumn(); + $phid_column = $this->getApplicationSearchObjectPHIDColumn($conn); switch ($cond) { case '=': // Figure out whether we need to do a LEFT JOIN or not. We need to // LEFT JOIN if we're going to select "IS NULL" rows. - $join_type = 'JOIN'; + $join_type = qsprintf($conn, 'JOIN'); foreach ($constraint['constraints'] as $query_constraint) { $op = $query_constraint->getOperator(); if ($op === PhabricatorQueryConstraint::OPERATOR_NULL) { - $join_type = 'LEFT JOIN'; + $join_type = qsprintf($conn, 'LEFT JOIN'); break; } } @@ -2437,9 +2440,9 @@ abstract class PhabricatorCursorPagedPolicyAwareQuery // this to a LEFT join. We'll use WHERE to select matching rows // later. if ($has_null) { - $join_type = 'LEFT'; + $join_type = qsprintf($conn, 'LEFT'); } else { - $join_type = ''; + $join_type = qsprintf($conn, ''); } $joins[] = qsprintf( @@ -2912,7 +2915,7 @@ abstract class PhabricatorCursorPagedPolicyAwareQuery if ($alias) { $col = qsprintf($conn, '%T.spacePHID', $alias); } else { - $col = 'spacePHID'; + $col = qsprintf($conn, 'spacePHID'); } if ($space_phids && $include_null) { diff --git a/src/infrastructure/storage/lisk/LiskDAO.php b/src/infrastructure/storage/lisk/LiskDAO.php index 03dbf51961..1095aadf59 100644 --- a/src/infrastructure/storage/lisk/LiskDAO.php +++ b/src/infrastructure/storage/lisk/LiskDAO.php @@ -1149,11 +1149,10 @@ abstract class LiskDAO extends Phobject $map[$key] = qsprintf($conn, '%C = %ns', $key, $value); } } - $map = implode(', ', $map); $id = $this->getID(); $conn->query( - 'UPDATE %R SET %Q WHERE %C = '.(is_int($id) ? '%d' : '%s'), + 'UPDATE %R SET %LQ WHERE %C = '.(is_int($id) ? '%d' : '%s'), $this, $map, $this->getIDKeyForUse(), @@ -1255,11 +1254,24 @@ abstract class LiskDAO extends Phobject $parameter_exception); } } - $data = implode(', ', $data); + + switch ($mode) { + case 'INSERT': + $verb = qsprintf($conn, 'INSERT'); + break; + case 'REPLACE': + $verb = qsprintf($conn, 'REPLACE'); + break; + default: + throw new Exception( + pht( + 'Insert mode verb "%s" is not recognized, use INSERT or REPLACE.', + $mode)); + } $conn->query( - '%Q INTO %R (%LC) VALUES (%Q)', - $mode, + '%Q INTO %R (%LC) VALUES (%LQ)', + $verb, $this, $columns, $data); From 86fd2041484fe600c6004170e101e8d1d1f17de2 Mon Sep 17 00:00:00 2001 From: epriestley Date: Tue, 13 Nov 2018 09:32:52 -0800 Subject: [PATCH 06/43] Fix all query warnings in "arc unit --everything" Summary: Ref T13216. Ref T13217. Depends on D19800. This fixes all of the remaining query warnings that pop up when you run "arc unit --everything". There's likely still quite a bit of stuff lurking around, but hopefully this covers a big set of the most common queries. Test Plan: Ran `arc unit --everything`. Before change: lots of query warnings. After change: no query warnings. Reviewers: amckinley Reviewed By: amckinley Maniphest Tasks: T13217, T13216 Differential Revision: https://secure.phabricator.com/D19801 --- .../schema/PhabricatorConfigSchemaQuery.php | 4 +- .../diffusion/query/DiffusionCommitQuery.php | 2 +- .../diffusion/query/DiffusionPathQuery.php | 14 ++-- .../files/query/PhabricatorFileQuery.php | 2 +- .../people/storage/PhabricatorUser.php | 7 +- .../phortune/query/PhortuneAccountQuery.php | 44 +++++------- .../engine/PhabricatorBoardLayoutEngine.php | 4 +- .../project/query/PhabricatorProjectQuery.php | 2 +- .../query/PhabricatorRepositoryQuery.php | 6 +- ...atorRepositoryCommitChangeParserWorker.php | 4 +- ...rRepositorySvnCommitChangeParserWorker.php | 4 +- ...PhabricatorCursorPagedPolicyAwareQuery.php | 2 +- .../storage/lisk/LiskDAOSet.php | 11 ++- .../PhabricatorStorageManagementAPI.php | 4 +- .../PhabricatorStorageManagementWorkflow.php | 70 ++++++++++++++++--- 15 files changed, 121 insertions(+), 59 deletions(-) diff --git a/src/applications/config/schema/PhabricatorConfigSchemaQuery.php b/src/applications/config/schema/PhabricatorConfigSchemaQuery.php index 2cb7763110..4a93396671 100644 --- a/src/applications/config/schema/PhabricatorConfigSchemaQuery.php +++ b/src/applications/config/schema/PhabricatorConfigSchemaQuery.php @@ -115,8 +115,8 @@ final class PhabricatorConfigSchemaQuery extends Phobject { 'SELECT TABLE_SCHEMA, TABLE_NAME, COLUMN_NAME, CHARACTER_SET_NAME, COLLATION_NAME, COLUMN_TYPE, IS_NULLABLE, EXTRA FROM INFORMATION_SCHEMA.COLUMNS - WHERE (%Q)', - '('.implode(') OR (', $sql).')'); + WHERE %LO', + $sql); $column_info = igroup($column_info, 'TABLE_SCHEMA'); } else { $column_info = array(); diff --git a/src/applications/diffusion/query/DiffusionCommitQuery.php b/src/applications/diffusion/query/DiffusionCommitQuery.php index f2d0920b3a..cf8c25baf1 100644 --- a/src/applications/diffusion/query/DiffusionCommitQuery.php +++ b/src/applications/diffusion/query/DiffusionCommitQuery.php @@ -696,7 +696,7 @@ final class DiffusionCommitQuery pht('No commit identifiers.')); } - $where[] = '('.implode(' OR ', $sql).')'; + $where[] = qsprintf($conn, '%LO', $sql); } if ($this->auditIDs !== null) { diff --git a/src/applications/diffusion/query/DiffusionPathQuery.php b/src/applications/diffusion/query/DiffusionPathQuery.php index 45dc978ec1..0783643dc0 100644 --- a/src/applications/diffusion/query/DiffusionPathQuery.php +++ b/src/applications/diffusion/query/DiffusionPathQuery.php @@ -10,12 +10,12 @@ final class DiffusionPathQuery extends Phobject { } public function execute() { - $conn_r = id(new PhabricatorRepository())->establishConnection('r'); + $conn = id(new PhabricatorRepository())->establishConnection('r'); - $where = $this->buildWhereClause($conn_r); + $where = $this->buildWhereClause($conn); $results = queryfx_all( - $conn_r, + $conn, 'SELECT * FROM %T %Q', PhabricatorRepository::TABLE_PATH, $where); @@ -23,20 +23,20 @@ final class DiffusionPathQuery extends Phobject { return ipull($results, null, 'id'); } - protected function buildWhereClause(AphrontDatabaseConnection $conn_r) { + protected function buildWhereClause(AphrontDatabaseConnection $conn) { $where = array(); if ($this->pathIDs) { $where[] = qsprintf( - $conn_r, + $conn, 'id IN (%Ld)', $this->pathIDs); } if ($where) { - return 'WHERE ('.implode(') AND (', $where).')'; + return qsprintf($conn, 'WHERE %LA', $where); } else { - return ''; + return qsprintf($conn, ''); } } diff --git a/src/applications/files/query/PhabricatorFileQuery.php b/src/applications/files/query/PhabricatorFileQuery.php index 9f1659a7f3..4205ab5c5d 100644 --- a/src/applications/files/query/PhabricatorFileQuery.php +++ b/src/applications/files/query/PhabricatorFileQuery.php @@ -391,7 +391,7 @@ final class PhabricatorFileQuery $transform['transform']); } } - $where[] = qsprintf($conn, '(%Q)', implode(') OR (', $clauses)); + $where[] = qsprintf($conn, '%LO', $clauses); } if ($this->dateCreatedAfter !== null) { diff --git a/src/applications/people/storage/PhabricatorUser.php b/src/applications/people/storage/PhabricatorUser.php index 216e451517..651d1b75a6 100644 --- a/src/applications/people/storage/PhabricatorUser.php +++ b/src/applications/people/storage/PhabricatorUser.php @@ -458,11 +458,14 @@ final class PhabricatorUser } public function loadPrimaryEmail() { + $email = new PhabricatorUserEmail(); + $conn = $email->establishConnection('r'); + return $this->loadOneRelative( - new PhabricatorUserEmail(), + $email, 'userPHID', 'getPHID', - '(isPrimary = 1)'); + qsprintf($conn, '(isPrimary = 1)')); } diff --git a/src/applications/phortune/query/PhortuneAccountQuery.php b/src/applications/phortune/query/PhortuneAccountQuery.php index 4ada4f2845..ee8291218c 100644 --- a/src/applications/phortune/query/PhortuneAccountQuery.php +++ b/src/applications/phortune/query/PhortuneAccountQuery.php @@ -42,20 +42,12 @@ final class PhortuneAccountQuery return $this; } + public function newResultObject() { + return new PhortuneAccount(); + } + protected function loadPage() { - $table = new PhortuneAccount(); - $conn = $table->establishConnection('r'); - - $rows = queryfx_all( - $conn, - 'SELECT a.* FROM %T a %Q %Q %Q %Q', - $table->getTableName(), - $this->buildJoinClause($conn), - $this->buildWhereClause($conn), - $this->buildOrderClause($conn), - $this->buildLimitClause($conn)); - - return $table->loadAllFromArray($rows); + return $this->loadStandardPage($this->newResultObject()); } protected function willFilterPage(array $accounts) { @@ -73,39 +65,37 @@ final class PhortuneAccountQuery return $accounts; } - protected function buildWhereClause(AphrontDatabaseConnection $conn) { - $where = array(); + protected function buildWhereClauseParts(AphrontDatabaseConnection $conn) { + $where = parent::buildWhereClauseParts($conn); - $where[] = $this->buildPagingClause($conn); - - if ($this->ids) { + if ($this->ids !== null) { $where[] = qsprintf( $conn, 'a.id IN (%Ld)', $this->ids); } - if ($this->phids) { + if ($this->phids !== null) { $where[] = qsprintf( $conn, 'a.phid IN (%Ls)', $this->phids); } - if ($this->memberPHIDs) { + if ($this->memberPHIDs !== null) { $where[] = qsprintf( $conn, 'm.dst IN (%Ls)', $this->memberPHIDs); } - return $this->formatWhereClause($conn, $where); + return $where; } - protected function buildJoinClause(AphrontDatabaseConnection $conn) { - $joins = array(); + protected function buildJoinClauseParts(AphrontDatabaseConnection $conn) { + $joins = parent::buildJoinClauseParts($conn); - if ($this->memberPHIDs) { + if ($this->memberPHIDs !== null) { $joins[] = qsprintf( $conn, 'LEFT JOIN %T m ON a.phid = m.src AND m.type = %d', @@ -113,11 +103,15 @@ final class PhortuneAccountQuery PhortuneAccountHasMemberEdgeType::EDGECONST); } - return implode(' ', $joins); + return $joins; } public function getQueryApplicationClass() { return 'PhabricatorPhortuneApplication'; } + protected function getPrimaryTableAlias() { + return 'a'; + } + } diff --git a/src/applications/project/engine/PhabricatorBoardLayoutEngine.php b/src/applications/project/engine/PhabricatorBoardLayoutEngine.php index 248677fbec..863f8c0e8c 100644 --- a/src/applications/project/engine/PhabricatorBoardLayoutEngine.php +++ b/src/applications/project/engine/PhabricatorBoardLayoutEngine.php @@ -297,9 +297,9 @@ final class PhabricatorBoardLayoutEngine extends Phobject { queryfx( $conn_w, 'INSERT INTO %T (id, sequence, boardPHID, columnPHID, objectPHID) - VALUES %Q ON DUPLICATE KEY UPDATE sequence = VALUES(sequence)', + VALUES %LQ ON DUPLICATE KEY UPDATE sequence = VALUES(sequence)', $table->getTableName(), - implode(', ', $pairs)); + $pairs); } foreach ($adds as $position) { diff --git a/src/applications/project/query/PhabricatorProjectQuery.php b/src/applications/project/query/PhabricatorProjectQuery.php index 98713078fb..e3a02edbd0 100644 --- a/src/applications/project/query/PhabricatorProjectQuery.php +++ b/src/applications/project/query/PhabricatorProjectQuery.php @@ -557,7 +557,7 @@ final class PhabricatorProjectQuery $ancestor_path['projectDepth']); } - $where[] = '('.implode(' OR ', $sql).')'; + $where[] = qsprintf($conn, '%LO', $sql); $where[] = qsprintf( $conn, diff --git a/src/applications/repository/query/PhabricatorRepositoryQuery.php b/src/applications/repository/query/PhabricatorRepositoryQuery.php index 4175ffc2d5..56c62cc8fb 100644 --- a/src/applications/repository/query/PhabricatorRepositoryQuery.php +++ b/src/applications/repository/query/PhabricatorRepositoryQuery.php @@ -513,8 +513,8 @@ final class PhabricatorRepositoryQuery if ($this->shouldJoinURITable()) { $joins[] = qsprintf( $conn, - 'LEFT JOIN %T uri ON r.phid = uri.repositoryPHID', - id(new PhabricatorRepositoryURIIndex())->getTableName()); + 'LEFT JOIN %R uri ON r.phid = uri.repositoryPHID', + new PhabricatorRepositoryURIIndex()); } return $joins; @@ -639,7 +639,7 @@ final class PhabricatorRepositoryQuery $this->slugIdentifiers); } - $where = array('('.implode(' OR ', $identifier_clause).')'); + $where[] = qsprintf($conn, '%LO', $identifier_clause); } if ($this->types) { diff --git a/src/applications/repository/worker/commitchangeparser/PhabricatorRepositoryCommitChangeParserWorker.php b/src/applications/repository/worker/commitchangeparser/PhabricatorRepositoryCommitChangeParserWorker.php index afbf42af97..b544228561 100644 --- a/src/applications/repository/worker/commitchangeparser/PhabricatorRepositoryCommitChangeParserWorker.php +++ b/src/applications/repository/worker/commitchangeparser/PhabricatorRepositoryCommitChangeParserWorker.php @@ -70,9 +70,9 @@ abstract class PhabricatorRepositoryCommitChangeParserWorker } queryfx( $conn_w, - 'INSERT IGNORE INTO %T (path, pathHash) VALUES %Q', + 'INSERT IGNORE INTO %T (path, pathHash) VALUES %LQ', PhabricatorRepository::TABLE_PATH, - implode(', ', $sql)); + $sql); } $result_map += self::lookupPaths($missing_paths); } diff --git a/src/applications/repository/worker/commitchangeparser/PhabricatorRepositorySvnCommitChangeParserWorker.php b/src/applications/repository/worker/commitchangeparser/PhabricatorRepositorySvnCommitChangeParserWorker.php index 6725ddb1d1..6be2251f1e 100644 --- a/src/applications/repository/worker/commitchangeparser/PhabricatorRepositorySvnCommitChangeParserWorker.php +++ b/src/applications/repository/worker/commitchangeparser/PhabricatorRepositorySvnCommitChangeParserWorker.php @@ -463,9 +463,9 @@ final class PhabricatorRepositorySvnCommitChangeParserWorker $conn_w, 'INSERT INTO %T (repositoryID, parentID, svnCommit, pathID, existed, fileType) - VALUES %Q', + VALUES %LQ', PhabricatorRepository::TABLE_FILESYSTEM, - implode(', ', $sql_chunk)); + $sql_chunk); } } diff --git a/src/infrastructure/query/policy/PhabricatorCursorPagedPolicyAwareQuery.php b/src/infrastructure/query/policy/PhabricatorCursorPagedPolicyAwareQuery.php index 162132d508..2d91f5e608 100644 --- a/src/infrastructure/query/policy/PhabricatorCursorPagedPolicyAwareQuery.php +++ b/src/infrastructure/query/policy/PhabricatorCursorPagedPolicyAwareQuery.php @@ -2353,7 +2353,7 @@ abstract class PhabricatorCursorPagedPolicyAwareQuery (array)$constraint->getValue(), $idx++); } - $parts = implode(', ', $parts); + $parts = qsprintf($conn, '%LQ', $parts); $select[] = qsprintf( $conn, diff --git a/src/infrastructure/storage/lisk/LiskDAOSet.php b/src/infrastructure/storage/lisk/LiskDAOSet.php index e1bc1e50d1..90eba708ea 100644 --- a/src/infrastructure/storage/lisk/LiskDAOSet.php +++ b/src/infrastructure/storage/lisk/LiskDAOSet.php @@ -77,11 +77,20 @@ final class LiskDAOSet extends Phobject { } else { $set = new LiskDAOSet(); $this->subsets[] = $set; + + $conn = $object->establishConnection('r'); + + if (strlen($where)) { + $where_clause = qsprintf($conn, 'AND %Q', $where); + } else { + $where_clause = qsprintf($conn, ''); + } + $relatives = $object->putInSet($set)->loadAllWhere( '%C IN (%Ls) %Q', $foreign_column, $ids, - ($where != '' ? 'AND '.$where : '')); + $where_clause); $relatives = mgroup($relatives, 'get'.$foreign_column); } } diff --git a/src/infrastructure/storage/management/PhabricatorStorageManagementAPI.php b/src/infrastructure/storage/management/PhabricatorStorageManagementAPI.php index 19d7a98d42..e66ba784f7 100644 --- a/src/infrastructure/storage/management/PhabricatorStorageManagementAPI.php +++ b/src/infrastructure/storage/management/PhabricatorStorageManagementAPI.php @@ -265,7 +265,9 @@ final class PhabricatorStorageManagementAPI extends Phobject { } try { - queryfx($conn, '%Q', $query); + // NOTE: We're using the unsafe "%Z" conversion here. There's no + // avoiding it since we're executing raw text files full of SQL. + queryfx($conn, '%Z', $query); } catch (AphrontAccessDeniedQueryException $ex) { throw new PhutilProxyException( pht( diff --git a/src/infrastructure/storage/management/workflow/PhabricatorStorageManagementWorkflow.php b/src/infrastructure/storage/management/workflow/PhabricatorStorageManagementWorkflow.php index 5b9459cfb8..5bc83972dd 100644 --- a/src/infrastructure/storage/management/workflow/PhabricatorStorageManagementWorkflow.php +++ b/src/infrastructure/storage/management/workflow/PhabricatorStorageManagementWorkflow.php @@ -357,22 +357,76 @@ abstract class PhabricatorStorageManagementWorkflow } if ($adjust['charset']) { + switch ($adjust['charset']) { + case 'binary': + $charset_value = qsprintf($conn, 'binary'); + break; + case 'utf8': + $charset_value = qsprintf($conn, 'utf8'); + break; + case 'utf8mb4': + $charset_value = qsprintf($conn, 'utf8mb4'); + break; + default: + throw new Exception( + pht( + 'Unsupported character set "%s".', + $adjust['charset'])); + } + + switch ($adjust['collation']) { + case 'binary': + $collation_value = qsprintf($conn, 'binary'); + break; + case 'utf8_general_ci': + $collation_value = qsprintf($conn, 'utf8_general_ci'); + break; + case 'utf8mb4_bin': + $collation_value = qsprintf($conn, 'utf8mb4_bin'); + break; + case 'utf8mb4_unicode_ci': + $collation_value = qsprintf($conn, 'utf8mb4_unicode_ci'); + break; + default: + throw new Exception( + pht( + 'Unsupported collation set "%s".', + $adjust['collation'])); + } + $parts[] = qsprintf( $conn, 'CHARACTER SET %Q COLLATE %Q', - $adjust['charset'], - $adjust['collation']); + $charset_value, + $collation_value); } + if ($parts) { + $parts = qsprintf($conn, '%LJ', $parts); + } else { + $parts = qsprintf($conn, ''); + } + + if ($adjust['nullable']) { + $nullable = qsprintf($conn, 'NULL'); + } else { + $nullable = qsprintf($conn, 'NOT NULL'); + } + + // TODO: We're using "%Z" here for the column type, which is + // technically unsafe. It would be nice to be able to use "%Q" + // instead, but this requires a fair amount of legwork to + // enumerate all column types. + queryfx( $conn, - 'ALTER TABLE %T.%T MODIFY %T %Q %Q %Q', + 'ALTER TABLE %T.%T MODIFY %T %Z %Q %Q', $adjust['database'], $adjust['table'], $adjust['name'], $adjust['type'], - implode(' ', $parts), - $adjust['nullable'] ? 'NULL' : 'NOT NULL'); + $parts, + $nullable); } break; case 'key': @@ -395,7 +449,7 @@ abstract class PhabricatorStorageManagementWorkflow // Different keys need different creation syntax. Notable // special cases are primary keys and fulltext keys. if ($adjust['name'] == 'PRIMARY') { - $key_name = 'PRIMARY KEY'; + $key_name = qsprintf($conn, 'PRIMARY KEY'); } else if ($adjust['indexType'] == 'FULLTEXT') { $key_name = qsprintf($conn, 'FULLTEXT %T', $adjust['name']); } else { @@ -414,11 +468,11 @@ abstract class PhabricatorStorageManagementWorkflow queryfx( $conn, - 'ALTER TABLE %T.%T ADD %Q (%Q)', + 'ALTER TABLE %T.%T ADD %Q (%LK)', $adjust['database'], $adjust['table'], $key_name, - implode(', ', $adjust['columns'])); + $adjust['columns']); } break; default: From 96f9b0917e21f84995d99571d6006627590d9c00 Mon Sep 17 00:00:00 2001 From: epriestley Date: Wed, 14 Nov 2018 09:41:07 -0800 Subject: [PATCH 07/43] Improve performance of two recent commit migrations Summary: Ref T13216. See PHI959. These two recent migrations can be expressed more efficiently: - When updating commit audit statuses, the field isn't JSON encoded or anything so we can just issue several bulk UPDATEs. - When inserting mail keys, we can batch them in groups of 100. Test Plan: Used `bin/storage upgrade -f --apply phabricator:...` to reapply patches. Saw equivalent behavior and faster runtimes. Reviewers: amckinley Reviewed By: amckinley Subscribers: PHID-OPKG-gm6ozazyms6q6i22gyam Maniphest Tasks: T13216 Differential Revision: https://secure.phabricator.com/D19802 --- .../autopatches/20180910.audit.03.status.php | 16 +++------- .../autopatches/20180914.audit.01.mailkey.php | 32 ++++++++++++------- 2 files changed, 25 insertions(+), 23 deletions(-) diff --git a/resources/sql/autopatches/20180910.audit.03.status.php b/resources/sql/autopatches/20180910.audit.03.status.php index b7a0bf9f58..ab42d196f0 100644 --- a/resources/sql/autopatches/20180910.audit.03.status.php +++ b/resources/sql/autopatches/20180910.audit.03.status.php @@ -12,17 +12,11 @@ $status_map = array( 5 => 'needs-verification', ); -foreach (new LiskMigrationIterator($table) as $commit) { - $status = $commit->getAuditStatus(); - - if (!isset($status_map[$status])) { - continue; - } - +foreach ($status_map as $old_status => $new_status) { queryfx( $conn, - 'UPDATE %T SET auditStatus = %s WHERE id = %d', - $table->getTableName(), - $status_map[$status], - $commit->getID()); + 'UPDATE %R SET auditStatus = %s WHERE auditStatus = %s', + $table, + $new_status, + $old_status); } diff --git a/resources/sql/autopatches/20180914.audit.01.mailkey.php b/resources/sql/autopatches/20180914.audit.01.mailkey.php index de8419a3c4..60926857ee 100644 --- a/resources/sql/autopatches/20180914.audit.01.mailkey.php +++ b/resources/sql/autopatches/20180914.audit.01.mailkey.php @@ -8,19 +8,27 @@ $properties_table = new PhabricatorMetaMTAMailProperties(); $conn = $properties_table->establishConnection('w'); $iterator = new LiskRawMigrationIterator($commit_conn, $commit_name); -foreach ($iterator as $commit) { +$chunks = new PhutilChunkedIterator($iterator, 100); +foreach ($chunks as $chunk) { + $sql = array(); + foreach ($chunk as $commit) { + $sql[] = qsprintf( + $conn, + '(%s, %s, %d, %d)', + $commit['phid'], + phutil_json_encode( + array( + 'mailKey' => $commit['mailKey'], + )), + PhabricatorTime::getNow(), + PhabricatorTime::getNow()); + } + queryfx( $conn, - 'INSERT IGNORE INTO %T + 'INSERT IGNORE INTO %R (objectPHID, mailProperties, dateCreated, dateModified) - VALUES - (%s, %s, %d, %d)', - $properties_table->getTableName(), - $commit['phid'], - phutil_json_encode( - array( - 'mailKey' => $commit['mailKey'], - )), - PhabricatorTime::getNow(), - PhabricatorTime::getNow()); + VALUES %LQ', + $properties_table, + $sql); } From e57bfbf421f44adb8f5bfbb6f5c062c250a637ac Mon Sep 17 00:00:00 2001 From: epriestley Date: Thu, 15 Nov 2018 08:19:29 -0800 Subject: [PATCH 08/43] Pull some debugging code back out of "master" See D19778. This is a workaround for T13179 that landed by mistake. --- src/applications/almanac/util/AlmanacKeys.php | 2 +- .../drydock/interface/command/DrydockSSHCommandInterface.php | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/applications/almanac/util/AlmanacKeys.php b/src/applications/almanac/util/AlmanacKeys.php index 83a4f4021b..7ed9098e97 100644 --- a/src/applications/almanac/util/AlmanacKeys.php +++ b/src/applications/almanac/util/AlmanacKeys.php @@ -63,7 +63,7 @@ final class AlmanacKeys extends Phobject { // protocol does not have a mechanism like a "Host" header. $username = PhabricatorEnv::getEnvConfig('cluster.instance'); if (strlen($username)) { -// return $username; + return $username; } $username = PhabricatorEnv::getEnvConfig('diffusion.ssh-user'); diff --git a/src/applications/drydock/interface/command/DrydockSSHCommandInterface.php b/src/applications/drydock/interface/command/DrydockSSHCommandInterface.php index 7ebd95938b..1aab14b57b 100644 --- a/src/applications/drydock/interface/command/DrydockSSHCommandInterface.php +++ b/src/applications/drydock/interface/command/DrydockSSHCommandInterface.php @@ -30,8 +30,8 @@ final class DrydockSSHCommandInterface extends DrydockCommandInterface { $full_command = call_user_func_array('csprintf', $argv); $flags = array(); - // $flags[] = '-o'; - // $flags[] = 'LogLevel=quiet'; + $flags[] = '-o'; + $flags[] = 'LogLevel=quiet'; $flags[] = '-o'; $flags[] = 'StrictHostKeyChecking=no'; From ea6d2afa868571d26dd740a24af7f6278f7382be Mon Sep 17 00:00:00 2001 From: epriestley Date: Thu, 15 Nov 2018 04:03:47 -0800 Subject: [PATCH 09/43] Fix flickering tooltips in Chrome when the tip container overlaps the triggering element Summary: Fixes T8440. See that task for discussion. Ref T13216. See PHI976. Test Plan: In Chrome, hovered a timestamp and moved the mouse up to the "overlap" area (see T8440). Before: flickered like crazy. After: no flickering. (I couldn't reproduce the original issue in modern Firefox or Safari.) Reviewers: amckinley, avivey Reviewed By: avivey Maniphest Tasks: T8440, T13216 Differential Revision: https://secure.phabricator.com/D19808 --- resources/celerity/map.php | 6 +++--- webroot/rsrc/css/aphront/tooltip.css | 6 ++++++ 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/resources/celerity/map.php b/resources/celerity/map.php index f812e17517..ee4a670dab 100644 --- a/resources/celerity/map.php +++ b/resources/celerity/map.php @@ -9,7 +9,7 @@ return array( 'names' => array( 'conpherence.pkg.css' => 'e68cf1fa', 'conpherence.pkg.js' => '15191c65', - 'core.pkg.css' => '2574c199', + 'core.pkg.css' => 'cff4ff6f', 'core.pkg.js' => 'b5a949ca', 'differential.pkg.css' => '06dc617c', 'differential.pkg.js' => 'c1cfa143', @@ -32,7 +32,7 @@ return array( 'rsrc/css/aphront/phabricator-nav-view.css' => '694d7723', 'rsrc/css/aphront/table-view.css' => '8c9bbafe', 'rsrc/css/aphront/tokenizer.css' => '15d5ff71', - 'rsrc/css/aphront/tooltip.css' => '173b9431', + 'rsrc/css/aphront/tooltip.css' => 'cb1397a4', 'rsrc/css/aphront/typeahead-browse.css' => 'f2818435', 'rsrc/css/aphront/typeahead.css' => 'a4a21016', 'rsrc/css/application/almanac/almanac.css' => 'dbb9b3af', @@ -521,7 +521,7 @@ return array( 'aphront-panel-view-css' => '8427b78d', 'aphront-table-view-css' => '8c9bbafe', 'aphront-tokenizer-control-css' => '15d5ff71', - 'aphront-tooltip-css' => '173b9431', + 'aphront-tooltip-css' => 'cb1397a4', 'aphront-typeahead-control-css' => 'a4a21016', 'application-search-view-css' => '787f5b76', 'auth-css' => '0877ed6e', diff --git a/webroot/rsrc/css/aphront/tooltip.css b/webroot/rsrc/css/aphront/tooltip.css index e6ff46cbed..754e647d0a 100644 --- a/webroot/rsrc/css/aphront/tooltip.css +++ b/webroot/rsrc/css/aphront/tooltip.css @@ -5,6 +5,12 @@ .jx-tooltip-container { position: absolute; padding: 5px; + + /* In Chrome, moving the cursor into the empty space next to the "caret" on + the caret side of the tooltip can cause the tooltip to flicker rapidly + because the cursor hits the container. To stop this, prevent cursor + events on the container. See T8440. */ + pointer-events: none; } .jx-tooltip-appear { From bbd292b9b34e4ea9f29c423f045b4b20632df6cf Mon Sep 17 00:00:00 2001 From: epriestley Date: Wed, 14 Nov 2018 11:22:54 -0800 Subject: [PATCH 10/43] Modernize the Herald rule search engine Summary: Ref T13216. Update the Herald Rule SearchEngine and Query to use a more modern style. Test Plan: Ran various rule queries in the UI, got sensible results Reviewers: amckinley Reviewed By: amckinley Maniphest Tasks: T13216 Differential Revision: https://secure.phabricator.com/D19803 --- .../herald/query/HeraldRuleQuery.php | 43 +++-- .../herald/query/HeraldRuleSearchEngine.php | 148 ++++++------------ 2 files changed, 68 insertions(+), 123 deletions(-) diff --git a/src/applications/herald/query/HeraldRuleQuery.php b/src/applications/herald/query/HeraldRuleQuery.php index 9673bd0810..4fd48d3109 100644 --- a/src/applications/herald/query/HeraldRuleQuery.php +++ b/src/applications/herald/query/HeraldRuleQuery.php @@ -70,19 +70,12 @@ final class HeraldRuleQuery extends PhabricatorCursorPagedPolicyAwareQuery { return $this; } + public function newResultObject() { + return new HeraldRule(); + } + protected function loadPage() { - $table = new HeraldRule(); - $conn_r = $table->establishConnection('r'); - - $data = queryfx_all( - $conn_r, - 'SELECT rule.* FROM %T rule %Q %Q %Q', - $table->getTableName(), - $this->buildWhereClause($conn_r), - $this->buildOrderClause($conn_r), - $this->buildLimitClause($conn_r)); - - return $table->loadAllFromArray($data); + return $this->loadStandardPage($this->newResultObject()); } protected function willFilterPage(array $rules) { @@ -175,38 +168,38 @@ final class HeraldRuleQuery extends PhabricatorCursorPagedPolicyAwareQuery { return $rules; } - protected function buildWhereClause(AphrontDatabaseConnection $conn) { - $where = array(); + protected function buildWhereClauseParts(AphrontDatabaseConnection $conn) { + $where = parent::buildWhereClauseParts($conn); - if ($this->ids) { + if ($this->ids !== null) { $where[] = qsprintf( $conn, 'rule.id IN (%Ld)', $this->ids); } - if ($this->phids) { + if ($this->phids !== null) { $where[] = qsprintf( $conn, 'rule.phid IN (%Ls)', $this->phids); } - if ($this->authorPHIDs) { + if ($this->authorPHIDs !== null) { $where[] = qsprintf( $conn, 'rule.authorPHID IN (%Ls)', $this->authorPHIDs); } - if ($this->ruleTypes) { + if ($this->ruleTypes !== null) { $where[] = qsprintf( $conn, 'rule.ruleType IN (%Ls)', $this->ruleTypes); } - if ($this->contentTypes) { + if ($this->contentTypes !== null) { $where[] = qsprintf( $conn, 'rule.contentType IN (%Ls)', @@ -220,23 +213,21 @@ final class HeraldRuleQuery extends PhabricatorCursorPagedPolicyAwareQuery { (int)$this->disabled); } - if ($this->datasourceQuery) { + if ($this->datasourceQuery !== null) { $where[] = qsprintf( $conn, 'rule.name LIKE %>', $this->datasourceQuery); } - if ($this->triggerObjectPHIDs) { + if ($this->triggerObjectPHIDs !== null) { $where[] = qsprintf( $conn, 'rule.triggerObjectPHID IN (%Ls)', $this->triggerObjectPHIDs); } - $where[] = $this->buildPagingClause($conn); - - return $this->formatWhereClause($conn, $where); + return $where; } private function validateRuleAuthors(array $rules) { @@ -281,4 +272,8 @@ final class HeraldRuleQuery extends PhabricatorCursorPagedPolicyAwareQuery { return 'PhabricatorHeraldApplication'; } + protected function getPrimaryTableAlias() { + return 'rule'; + } + } diff --git a/src/applications/herald/query/HeraldRuleSearchEngine.php b/src/applications/herald/query/HeraldRuleSearchEngine.php index 3b8196c13a..3a6da6145a 100644 --- a/src/applications/herald/query/HeraldRuleSearchEngine.php +++ b/src/applications/herald/query/HeraldRuleSearchEngine.php @@ -10,90 +10,68 @@ final class HeraldRuleSearchEngine extends PhabricatorApplicationSearchEngine { return 'PhabricatorHeraldApplication'; } - public function buildSavedQueryFromRequest(AphrontRequest $request) { - $saved = new PhabricatorSavedQuery(); - - $saved->setParameter( - 'authorPHIDs', - $this->readUsersFromRequest($request, 'authors')); - - $saved->setParameter('contentType', $request->getStr('contentType')); - $saved->setParameter('ruleType', $request->getStr('ruleType')); - $saved->setParameter( - 'disabled', - $this->readBoolFromRequest($request, 'disabled')); - - return $saved; + public function newQuery() { + return new HeraldRuleQuery(); } - public function buildQueryFromSavedQuery(PhabricatorSavedQuery $saved) { - $query = id(new HeraldRuleQuery()); + protected function buildCustomSearchFields() { + $viewer = $this->requireViewer(); - $author_phids = $saved->getParameter('authorPHIDs'); - if ($author_phids) { - $query->withAuthorPHIDs($author_phids); + $rule_types = HeraldRuleTypeConfig::getRuleTypeMap(); + $content_types = HeraldAdapter::getEnabledAdapterMap($viewer); + + return array( + id(new PhabricatorUsersSearchField()) + ->setLabel(pht('Authors')) + ->setKey('authorPHIDs') + ->setAliases(array('author', 'authors', 'authorPHID')) + ->setDescription( + pht('Search for rules with given authors.')), + id(new PhabricatorSearchCheckboxesField()) + ->setKey('ruleTypes') + ->setAliases(array('ruleType')) + ->setLabel(pht('Rule Type')) + ->setDescription( + pht('Search for rules of given types.')) + ->setOptions($rule_types), + id(new PhabricatorSearchCheckboxesField()) + ->setKey('contentTypes') + ->setLabel(pht('Content Type')) + ->setDescription( + pht('Search for rules affecting given types of content.')) + ->setOptions($content_types), + id(new PhabricatorSearchThreeStateField()) + ->setLabel(pht('Rule Status')) + ->setKey('disabled') + ->setOptions( + pht('(Show All)'), + pht('Show Only Disabled Rules'), + pht('Show Only Enabled Rules')), + ); + } + + protected function buildQueryFromParameters(array $map) { + $query = $this->newQuery(); + + if ($map['authorPHIDs']) { + $query->withAuthorPHIDs($map['authorPHIDs']); } - $content_type = $saved->getParameter('contentType'); - $content_type = idx($this->getContentTypeValues(), $content_type); - if ($content_type) { - $query->withContentTypes(array($content_type)); + if ($map['contentTypes']) { + $query->withContentTypes($map['contentTypes']); } - $rule_type = $saved->getParameter('ruleType'); - $rule_type = idx($this->getRuleTypeValues(), $rule_type); - if ($rule_type) { - $query->withRuleTypes(array($rule_type)); + if ($map['ruleTypes']) { + $query->withRuleTypes($map['ruleTypes']); } - $disabled = $saved->getParameter('disabled'); - if ($disabled !== null) { - $query->withDisabled($disabled); + if ($map['disabled'] !== null) { + $query->withDisabled($map['disabled']); } return $query; } - public function buildSearchForm( - AphrontFormView $form, - PhabricatorSavedQuery $saved_query) { - - $author_phids = $saved_query->getParameter('authorPHIDs', array()); - $content_type = $saved_query->getParameter('contentType'); - $rule_type = $saved_query->getParameter('ruleType'); - - $form - ->appendControl( - id(new AphrontFormTokenizerControl()) - ->setDatasource(new PhabricatorPeopleDatasource()) - ->setName('authors') - ->setLabel(pht('Authors')) - ->setValue($author_phids)) - ->appendChild( - id(new AphrontFormSelectControl()) - ->setName('contentType') - ->setLabel(pht('Content Type')) - ->setValue($content_type) - ->setOptions($this->getContentTypeOptions())) - ->appendChild( - id(new AphrontFormSelectControl()) - ->setName('ruleType') - ->setLabel(pht('Rule Type')) - ->setValue($rule_type) - ->setOptions($this->getRuleTypeOptions())) - ->appendChild( - id(new AphrontFormSelectControl()) - ->setName('disabled') - ->setLabel(pht('Rule Status')) - ->setValue($this->getBoolFromQuery($saved_query, 'disabled')) - ->setOptions( - array( - '' => pht('Show Enabled and Disabled Rules'), - 'false' => pht('Show Only Enabled Rules'), - 'true' => pht('Show Only Disabled Rules'), - ))); - } - protected function getURI($path) { return '/herald/'.$path; } @@ -131,35 +109,6 @@ final class HeraldRuleSearchEngine extends PhabricatorApplicationSearchEngine { return parent::buildSavedQueryFromBuiltin($query_key); } - private function getContentTypeOptions() { - return array( - '' => pht('(All Content Types)'), - ) + HeraldAdapter::getEnabledAdapterMap($this->requireViewer()); - } - - private function getContentTypeValues() { - return array_fuse( - array_keys( - HeraldAdapter::getEnabledAdapterMap($this->requireViewer()))); - } - - private function getRuleTypeOptions() { - return array( - '' => pht('(All Rule Types)'), - ) + HeraldRuleTypeConfig::getRuleTypeMap(); - } - - private function getRuleTypeValues() { - return array_fuse(array_keys(HeraldRuleTypeConfig::getRuleTypeMap())); - } - - protected function getRequiredHandlePHIDsForResultList( - array $rules, - PhabricatorSavedQuery $query) { - - return mpull($rules, 'getAuthorPHID'); - } - protected function renderResultList( array $rules, PhabricatorSavedQuery $query, @@ -167,6 +116,7 @@ final class HeraldRuleSearchEngine extends PhabricatorApplicationSearchEngine { assert_instances_of($rules, 'HeraldRule'); $viewer = $this->requireViewer(); + $handles = $viewer->loadHandles(mpull($rules, 'getAuthorPHID')); $content_type_map = HeraldAdapter::getEnabledAdapterMap($viewer); From 8a8123c9db3bc53bcd93e3e8e79e90607b08f47c Mon Sep 17 00:00:00 2001 From: epriestley Date: Wed, 14 Nov 2018 14:50:40 -0800 Subject: [PATCH 11/43] Replace the primary "Disabled/Enabled" Herald Rule filter with "Active/Inactive", considering author status Summary: Ref T13216. See PHI947. In Herald, Personal rules do not run if their author's account is disabled. This isn't communicated very clearly in the UI, and the way the SearchEngine/Query are set up isn't great. Define "active" as "rule will actually run", which specifically means "rule is enabled, and has a valid (non-disabled) author if it needs one". Change the meaning of the "Active" default filter from "rule is enabled" to "rule is enabled, and has a valid author if it needs one". Refine the status badge on the view controller to show this "invalid author" state. Tweak the language for "Disable/Enable" to be more consistent -- we currently call it "disabled" in some cases and "archived" in others. Test Plan: - Disabled a user account and saw their personal rules behave properly with the new filters/options/view controller. - Disabled/enabled a rule, saw consistent text. Reviewers: amckinley Reviewed By: amckinley Maniphest Tasks: T13216 Differential Revision: https://secure.phabricator.com/D19805 --- .../controller/HeraldDisableController.php | 8 ++--- .../controller/HeraldRuleViewController.php | 18 ++++------ .../herald/query/HeraldRuleQuery.php | 36 ++++++++++++++++++- .../herald/query/HeraldRuleSearchEngine.php | 22 ++++++++++-- 4 files changed, 65 insertions(+), 19 deletions(-) diff --git a/src/applications/herald/controller/HeraldDisableController.php b/src/applications/herald/controller/HeraldDisableController.php index bdbefa55ea..def87049f7 100644 --- a/src/applications/herald/controller/HeraldDisableController.php +++ b/src/applications/herald/controller/HeraldDisableController.php @@ -44,13 +44,13 @@ final class HeraldDisableController extends HeraldController { } if ($is_disable) { - $title = pht('Really archive this rule?'); + $title = pht('Really disable this rule?'); $body = pht('This rule will no longer activate.'); - $button = pht('Archive Rule'); + $button = pht('Disable Rule'); } else { - $title = pht('Really activate this rule?'); + $title = pht('Really enable this rule?'); $body = pht('This rule will become active again.'); - $button = pht('Activate Rule'); + $button = pht('Enable Rule'); } $dialog = id(new AphrontDialogView()) diff --git a/src/applications/herald/controller/HeraldRuleViewController.php b/src/applications/herald/controller/HeraldRuleViewController.php index 9e696c23c4..70a2d20224 100644 --- a/src/applications/herald/controller/HeraldRuleViewController.php +++ b/src/applications/herald/controller/HeraldRuleViewController.php @@ -14,6 +14,7 @@ final class HeraldRuleViewController extends HeraldController { ->setViewer($viewer) ->withIDs(array($id)) ->needConditionsAndActions(true) + ->needValidateAuthors(true) ->executeOne(); if (!$rule) { return new Aphront404Response(); @@ -26,15 +27,11 @@ final class HeraldRuleViewController extends HeraldController { ->setHeaderIcon('fa-bullhorn'); if ($rule->getIsDisabled()) { - $header->setStatus( - 'fa-ban', - 'red', - pht('Archived')); + $header->setStatus('fa-ban', 'red', pht('Disabled')); + } else if (!$rule->hasValidAuthor()) { + $header->setStatus('fa-user', 'red', pht('Author Not Active')); } else { - $header->setStatus( - 'fa-check', - 'bluegrey', - pht('Active')); + $header->setStatus('fa-check', 'bluegrey', pht('Active')); } $curtain = $this->buildCurtain($rule); @@ -90,16 +87,15 @@ final class HeraldRuleViewController extends HeraldController { if ($rule->getIsDisabled()) { $disable_uri = "disable/{$id}/enable/"; $disable_icon = 'fa-check'; - $disable_name = pht('Activate Rule'); + $disable_name = pht('Enable Rule'); } else { $disable_uri = "disable/{$id}/disable/"; $disable_icon = 'fa-ban'; - $disable_name = pht('Archive Rule'); + $disable_name = pht('Disable Rule'); } $curtain->addAction( id(new PhabricatorActionView()) - ->setName(pht('Disable Rule')) ->setHref($this->getApplicationURI($disable_uri)) ->setIcon($disable_icon) ->setName($disable_name) diff --git a/src/applications/herald/query/HeraldRuleQuery.php b/src/applications/herald/query/HeraldRuleQuery.php index 4fd48d3109..e6dba43c7a 100644 --- a/src/applications/herald/query/HeraldRuleQuery.php +++ b/src/applications/herald/query/HeraldRuleQuery.php @@ -8,6 +8,7 @@ final class HeraldRuleQuery extends PhabricatorCursorPagedPolicyAwareQuery { private $ruleTypes; private $contentTypes; private $disabled; + private $active; private $datasourceQuery; private $triggerObjectPHIDs; @@ -45,6 +46,11 @@ final class HeraldRuleQuery extends PhabricatorCursorPagedPolicyAwareQuery { return $this; } + public function withActive($active) { + $this->active = $active; + return $this; + } + public function withDatasourceQuery($query) { $this->datasourceQuery = $query; return $this; @@ -92,10 +98,31 @@ final class HeraldRuleQuery extends PhabricatorCursorPagedPolicyAwareQuery { } } - if ($this->needValidateAuthors) { + if ($this->needValidateAuthors || ($this->active !== null)) { $this->validateRuleAuthors($rules); } + if ($this->active !== null) { + $need_active = (bool)$this->active; + foreach ($rules as $key => $rule) { + if ($rule->getIsDisabled()) { + $is_active = false; + } else if (!$rule->hasValidAuthor()) { + $is_active = false; + } else { + $is_active = true; + } + + if ($is_active != $need_active) { + unset($rules[$key]); + } + } + } + + if (!$rules) { + return array(); + } + if ($this->needConditionsAndActions) { $conditions = id(new HeraldCondition())->loadAllWhere( 'ruleID IN (%Ld)', @@ -213,6 +240,13 @@ final class HeraldRuleQuery extends PhabricatorCursorPagedPolicyAwareQuery { (int)$this->disabled); } + if ($this->active !== null) { + $where[] = qsprintf( + $conn, + 'rule.isDisabled = %d', + (int)(!$this->active)); + } + if ($this->datasourceQuery !== null) { $where[] = qsprintf( $conn, diff --git a/src/applications/herald/query/HeraldRuleSearchEngine.php b/src/applications/herald/query/HeraldRuleSearchEngine.php index 3a6da6145a..47a6832731 100644 --- a/src/applications/herald/query/HeraldRuleSearchEngine.php +++ b/src/applications/herald/query/HeraldRuleSearchEngine.php @@ -11,7 +11,8 @@ final class HeraldRuleSearchEngine extends PhabricatorApplicationSearchEngine { } public function newQuery() { - return new HeraldRuleQuery(); + return id(new HeraldRuleQuery()) + ->needValidateAuthors(true); } protected function buildCustomSearchFields() { @@ -41,7 +42,14 @@ final class HeraldRuleSearchEngine extends PhabricatorApplicationSearchEngine { pht('Search for rules affecting given types of content.')) ->setOptions($content_types), id(new PhabricatorSearchThreeStateField()) - ->setLabel(pht('Rule Status')) + ->setLabel(pht('Active Rules')) + ->setKey('active') + ->setOptions( + pht('(Show All)'), + pht('Show Only Active Rules'), + pht('Show Only Inactive Rules')), + id(new PhabricatorSearchThreeStateField()) + ->setLabel(pht('Disabled Rules')) ->setKey('disabled') ->setOptions( pht('(Show All)'), @@ -69,6 +77,10 @@ final class HeraldRuleSearchEngine extends PhabricatorApplicationSearchEngine { $query->withDisabled($map['disabled']); } + if ($map['active'] !== null) { + $query->withActive($map['active']); + } + return $query; } @@ -99,7 +111,8 @@ final class HeraldRuleSearchEngine extends PhabricatorApplicationSearchEngine { case 'all': return $query; case 'active': - return $query->setParameter('disabled', false); + return $query + ->setParameter('active', true); case 'authored': return $query ->setParameter('authorPHIDs', array($viewer_phid)) @@ -145,6 +158,9 @@ final class HeraldRuleSearchEngine extends PhabricatorApplicationSearchEngine { if ($rule->getIsDisabled()) { $item->setDisabled(true); $item->addIcon('fa-lock grey', pht('Disabled')); + } else if (!$rule->hasValidAuthor()) { + $item->setDisabled(true); + $item->addIcon('fa-user grey', pht('Author Not Active')); } $content_type_name = idx($content_type_map, $rule->getContentType()); From 533e4e13b35a285e5d5188358ee082348cb1a7f8 Mon Sep 17 00:00:00 2001 From: epriestley Date: Wed, 14 Nov 2018 15:23:07 -0800 Subject: [PATCH 12/43] Add a `bin/herald test ...` for doing test runs via the CLI Summary: Ref T13216. See D19666. It's currently tricky to profile Herald test runs since you have to submit a form and repeating them is a bit of a mess. Provide a simple CLI wrapper so we can use `--xprofile`. This is also maybe nice-to-have if we're ever debugging anything here. Test Plan: Ran `bin/herald test --object ... --type ...` and got a sensible looking transcript in the UI. Reviewers: amckinley Reviewed By: amckinley Maniphest Tasks: T13216 Differential Revision: https://secure.phabricator.com/D19806 --- bin/herald | 1 + scripts/setup/manage_herald.php | 21 +++ src/__phutil_library_map__.php | 4 + .../management/HeraldManagementWorkflow.php | 4 + .../HeraldTestManagementWorkflow.php | 139 ++++++++++++++++++ .../herald/query/HeraldTranscriptQuery.php | 33 ++--- 6 files changed, 185 insertions(+), 17 deletions(-) create mode 120000 bin/herald create mode 100755 scripts/setup/manage_herald.php create mode 100644 src/applications/herald/management/HeraldManagementWorkflow.php create mode 100644 src/applications/herald/management/HeraldTestManagementWorkflow.php diff --git a/bin/herald b/bin/herald new file mode 120000 index 0000000000..53f039ddf1 --- /dev/null +++ b/bin/herald @@ -0,0 +1 @@ +../scripts/setup/manage_herald.php \ No newline at end of file diff --git a/scripts/setup/manage_herald.php b/scripts/setup/manage_herald.php new file mode 100755 index 0000000000..4ebd94f820 --- /dev/null +++ b/scripts/setup/manage_herald.php @@ -0,0 +1,21 @@ +#!/usr/bin/env php +setTagline(pht('manage Herald')); +$args->setSynopsis(<<parseStandardArguments(); + +$workflows = id(new PhutilClassMapQuery()) + ->setAncestorClass('HeraldManagementWorkflow') + ->execute(); +$workflows[] = new PhutilHelpArgumentWorkflow(); +$args->parseWorkflows($workflows); diff --git a/src/__phutil_library_map__.php b/src/__phutil_library_map__.php index 4c784571b5..578a90d09c 100644 --- a/src/__phutil_library_map__.php +++ b/src/__phutil_library_map__.php @@ -1488,6 +1488,7 @@ phutil_register_library_map(array( 'HeraldInvalidConditionException' => 'applications/herald/engine/exception/HeraldInvalidConditionException.php', 'HeraldMailableState' => 'applications/herald/state/HeraldMailableState.php', 'HeraldManageGlobalRulesCapability' => 'applications/herald/capability/HeraldManageGlobalRulesCapability.php', + 'HeraldManagementWorkflow' => 'applications/herald/management/HeraldManagementWorkflow.php', 'HeraldManiphestTaskAdapter' => 'applications/maniphest/herald/HeraldManiphestTaskAdapter.php', 'HeraldNewController' => 'applications/herald/controller/HeraldNewController.php', 'HeraldNewObjectField' => 'applications/herald/field/HeraldNewObjectField.php', @@ -1537,6 +1538,7 @@ phutil_register_library_map(array( 'HeraldSupportActionGroup' => 'applications/herald/action/HeraldSupportActionGroup.php', 'HeraldSupportFieldGroup' => 'applications/herald/field/HeraldSupportFieldGroup.php', 'HeraldTestConsoleController' => 'applications/herald/controller/HeraldTestConsoleController.php', + 'HeraldTestManagementWorkflow' => 'applications/herald/management/HeraldTestManagementWorkflow.php', 'HeraldTextFieldValue' => 'applications/herald/value/HeraldTextFieldValue.php', 'HeraldTokenizerFieldValue' => 'applications/herald/value/HeraldTokenizerFieldValue.php', 'HeraldTransactionQuery' => 'applications/herald/query/HeraldTransactionQuery.php', @@ -6975,6 +6977,7 @@ phutil_register_library_map(array( 'HeraldInvalidConditionException' => 'Exception', 'HeraldMailableState' => 'HeraldState', 'HeraldManageGlobalRulesCapability' => 'PhabricatorPolicyCapability', + 'HeraldManagementWorkflow' => 'PhabricatorManagementWorkflow', 'HeraldManiphestTaskAdapter' => 'HeraldAdapter', 'HeraldNewController' => 'HeraldController', 'HeraldNewObjectField' => 'HeraldField', @@ -7031,6 +7034,7 @@ phutil_register_library_map(array( 'HeraldSupportActionGroup' => 'HeraldActionGroup', 'HeraldSupportFieldGroup' => 'HeraldFieldGroup', 'HeraldTestConsoleController' => 'HeraldController', + 'HeraldTestManagementWorkflow' => 'HeraldManagementWorkflow', 'HeraldTextFieldValue' => 'HeraldFieldValue', 'HeraldTokenizerFieldValue' => 'HeraldFieldValue', 'HeraldTransactionQuery' => 'PhabricatorApplicationTransactionQuery', diff --git a/src/applications/herald/management/HeraldManagementWorkflow.php b/src/applications/herald/management/HeraldManagementWorkflow.php new file mode 100644 index 0000000000..50559ec3bc --- /dev/null +++ b/src/applications/herald/management/HeraldManagementWorkflow.php @@ -0,0 +1,4 @@ +setName('test') + ->setExamples('**test** --object __object__ --type __type__') + ->setSynopsis( + pht( + 'Test content rules for an object. Executes a dry run, like the '. + 'web UI test console.')) + ->setArguments( + array( + array( + 'name' => 'object', + 'param' => 'object', + 'help' => pht('Run rules on this object.'), + ), + array( + 'name' => 'type', + 'param' => 'type', + 'help' => pht('Run rules for this content type.'), + ), + )); + } + + public function execute(PhutilArgumentParser $args) { + $viewer = $this->getViewer(); + + $object_name = $args->getArg('object'); + if (!strlen($object_name)) { + throw new PhutilArgumentUsageException( + pht('Specify an object to test rules for with "--object".')); + } + + $objects = id(new PhabricatorObjectQuery()) + ->setViewer($viewer) + ->withNames(array($object_name)) + ->execute(); + if (!$objects) { + throw new PhutilArgumentUsageException( + pht( + 'Unable to load specified object ("%s").', + $object_name)); + } + $object = head($objects); + + $adapters = HeraldAdapter::getAllAdapters(); + + $can_select = array(); + $display_adapters = array(); + foreach ($adapters as $key => $adapter) { + if (!$adapter->isTestAdapterForObject($object)) { + continue; + } + + if (!$adapter->isAvailableToUser($viewer)) { + continue; + } + + $display_adapters[$key] = $adapter; + + if ($adapter->canCreateTestAdapterForObject($object)) { + $can_select[$key] = $adapter; + } + } + + + $content_type = $args->getArg('type'); + if (!strlen($content_type)) { + throw new PhutilArgumentUsageException( + pht( + 'Specify a content type to run rules for. For this object, valid '. + 'content types are: %s.', + implode(', ', array_keys($can_select)))); + } + + if (!isset($can_select[$content_type])) { + if (!isset($display_adapters[$content_type])) { + throw new PhutilArgumentUsageException( + pht( + 'Specify a content type to run rules for. The specified content '. + 'type ("%s") is not valid. For this object, valid content types '. + 'are: %s.', + $content_type, + implode(', ', array_keys($can_select)))); + } else { + throw new PhutilArgumentUsageException( + pht( + 'The specified content type ("%s") does not support dry runs. '. + 'Choose a testable content type. For this object, valid content '. + 'types are: %s.', + $content_type, + implode(', ', array_keys($can_select)))); + } + } + + $adapter = $can_select[$content_type]->newTestAdapter( + $viewer, + $object); + + $content_source = $this->newContentSource(); + + $adapter + ->setContentSource($content_source) + ->setIsNewObject(false) + ->setViewer($viewer); + + $rules = id(new HeraldRuleQuery()) + ->setViewer($viewer) + ->withContentTypes(array($adapter->getAdapterContentType())) + ->withDisabled(false) + ->needConditionsAndActions(true) + ->needAppliedToPHIDs(array($object->getPHID())) + ->needValidateAuthors(true) + ->execute(); + + $engine = id(new HeraldEngine()) + ->setDryRun(true); + + $effects = $engine->applyRules($rules, $adapter); + $engine->applyEffects($effects, $adapter, $rules); + + $xscript = $engine->getTranscript(); + + $uri = '/herald/transcript/'.$xscript->getID().'/'; + $uri = PhabricatorEnv::getProductionURI($uri); + + echo tsprintf( + "%s\n\n __%s__\n\n", + pht('Test run complete. Transcript:'), + $uri); + + return 0; + } + +} diff --git a/src/applications/herald/query/HeraldTranscriptQuery.php b/src/applications/herald/query/HeraldTranscriptQuery.php index 5308e05ee1..71789e07c5 100644 --- a/src/applications/herald/query/HeraldTranscriptQuery.php +++ b/src/applications/herald/query/HeraldTranscriptQuery.php @@ -30,36 +30,35 @@ final class HeraldTranscriptQuery protected function loadPage() { $transcript = new HeraldTranscript(); - $conn_r = $transcript->establishConnection('r'); + $conn = $transcript->establishConnection('r'); // NOTE: Transcripts include a potentially enormous amount of serialized // data, so we're loading only some of the fields here if the caller asked // for partial records. if ($this->needPartialRecords) { - $fields = implode( - ', ', - array( - 'id', - 'phid', - 'objectPHID', - 'time', - 'duration', - 'dryRun', - 'host', - )); + $fields = array( + 'id', + 'phid', + 'objectPHID', + 'time', + 'duration', + 'dryRun', + 'host', + ); + $fields = qsprintf($conn, '%LC', $fields); } else { - $fields = '*'; + $fields = qsprintf($conn, '*'); } $rows = queryfx_all( - $conn_r, + $conn, 'SELECT %Q FROM %T t %Q %Q %Q', $fields, $transcript->getTableName(), - $this->buildWhereClause($conn_r), - $this->buildOrderClause($conn_r), - $this->buildLimitClause($conn_r)); + $this->buildWhereClause($conn), + $this->buildOrderClause($conn), + $this->buildLimitClause($conn)); $transcripts = $transcript->loadAllFromArray($rows); From ec452e548a0d258060e6ada71403f3785b67eda1 Mon Sep 17 00:00:00 2001 From: epriestley Date: Thu, 15 Nov 2018 04:15:43 -0800 Subject: [PATCH 13/43] Improve text overflow behavior for hovercards with (for example) long package names Summary: See PHI977. Ref T13216. Some text, like long package names, may overflow hovercards. Add overflow CSS behaviors to remedy this. Test Plan: Before: {F6012699} After: {F6012700} (You can use `/search/hovercard/` to render hovercards in a handy standalone way.) Reviewers: amckinley Reviewed By: amckinley Maniphest Tasks: T13216 Differential Revision: https://secure.phabricator.com/D19809 --- resources/celerity/map.php | 4 ++-- webroot/rsrc/css/phui/phui-hovercard.css | 2 ++ 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/resources/celerity/map.php b/resources/celerity/map.php index ee4a670dab..e147d89128 100644 --- a/resources/celerity/map.php +++ b/resources/celerity/map.php @@ -155,7 +155,7 @@ return array( 'rsrc/css/phui/phui-form.css' => '7aaa04e3', 'rsrc/css/phui/phui-head-thing.css' => 'fd311e5f', 'rsrc/css/phui/phui-header-view.css' => '1ba8b707', - 'rsrc/css/phui/phui-hovercard.css' => 'f0592bcf', + 'rsrc/css/phui/phui-hovercard.css' => '4a484541', 'rsrc/css/phui/phui-icon-set-selector.css' => '87db8fee', 'rsrc/css/phui/phui-icon.css' => 'cf24ceec', 'rsrc/css/phui/phui-image-mask.css' => 'a8498f9c', @@ -821,7 +821,7 @@ return array( 'phui-head-thing-view-css' => 'fd311e5f', 'phui-header-view-css' => '1ba8b707', 'phui-hovercard' => '1bd28176', - 'phui-hovercard-view-css' => 'f0592bcf', + 'phui-hovercard-view-css' => '4a484541', 'phui-icon-set-selector-css' => '87db8fee', 'phui-icon-view-css' => 'cf24ceec', 'phui-image-mask-css' => 'a8498f9c', diff --git a/webroot/rsrc/css/phui/phui-hovercard.css b/webroot/rsrc/css/phui/phui-hovercard.css index 1f01362b6f..876b0b6e53 100644 --- a/webroot/rsrc/css/phui/phui-hovercard.css +++ b/webroot/rsrc/css/phui/phui-hovercard.css @@ -54,6 +54,8 @@ .phui-hovercard-body-item { margin: 4px 0 0 0; + overflow: hidden; + text-overflow: ellipsis; } .phui-hovercard-body-header { From 44c32839a68cd8f98fa38a1e321cd5526b213d92 Mon Sep 17 00:00:00 2001 From: epriestley Date: Thu, 15 Nov 2018 05:02:29 -0800 Subject: [PATCH 14/43] When you "Request Review" of a draft revision, change the button text from "Submit Quietly" to "Publish Revision" Summary: See PHI975. Ref T13216. Ref T2543. Previously, see D19204 and PHI433. When you're acting on a draft revision, we change the button text to "Submit Quietly" as a hint that your actions don't generate notifications yet. However, this isn't accurate when one of your actions is "Request Review", which causes the revision to publish. Allow actions to override the submit button text, and make the "Request Review" action change the button text to "Publish Revision". The alternative change I considered was to remove the word "Quietly" in all cases. I'm not //thrilled// about how complex this change is to adjust one word, but the various pieces are all fairly clean individually. I'm not sure we'll ever be able to use it for anything else, but I do suspect that the word "Quietly" was the change in D19204 with the largest effect by far (see T10000). Test Plan: - Created a draft revision. Saw "Submit Quietly" text. - Added a "Request Review" action, saw it change to "Publish Revision". - Reloaded page, saw stack saved and "Publish Revision". - Removed action, saw "Submit Quietly". - Repeated on a non-draft revision, button stayed put as "Submit". - Submitted the various actions, saw them have the desired effects. Reviewers: amckinley Reviewed By: amckinley Maniphest Tasks: T13216, T2543 Differential Revision: https://secure.phabricator.com/D19810 --- resources/celerity/map.php | 22 +++++++++---------- .../DifferentialRevisionActionTransaction.php | 8 +++++++ ...entialRevisionRequestReviewTransaction.php | 13 +++++++++++ .../query/PhabricatorOwnersPackageQuery.php | 2 +- .../PhabricatorEditEngineCommentAction.php | 10 +++++++++ .../editfield/PhabricatorApplyEditField.php | 20 +++++++++++++---- ...catorApplicationTransactionCommentView.php | 4 +++- .../form/control/AphrontFormSubmitControl.php | 16 +++++++++++++- .../transactions/behavior-comment-actions.js | 22 +++++++++++++++++++ 9 files changed, 99 insertions(+), 18 deletions(-) diff --git a/resources/celerity/map.php b/resources/celerity/map.php index e147d89128..7d6e766c44 100644 --- a/resources/celerity/map.php +++ b/resources/celerity/map.php @@ -422,7 +422,7 @@ return array( '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-queries.js' => 'e9581f08', - 'rsrc/js/application/transactions/behavior-comment-actions.js' => '038bf27f', + 'rsrc/js/application/transactions/behavior-comment-actions.js' => '59e27e74', 'rsrc/js/application/transactions/behavior-reorder-configs.js' => 'd7a74243', 'rsrc/js/application/transactions/behavior-reorder-fields.js' => 'b59e1e96', 'rsrc/js/application/transactions/behavior-show-older-transactions.js' => '8f29b364', @@ -574,7 +574,7 @@ return array( 'javelin-behavior-bulk-job-reload' => 'edf8a145', 'javelin-behavior-calendar-month-view' => 'fe33e256', 'javelin-behavior-choose-control' => '327a00d1', - 'javelin-behavior-comment-actions' => '038bf27f', + 'javelin-behavior-comment-actions' => '59e27e74', 'javelin-behavior-config-reorder-fields' => 'b6993408', 'javelin-behavior-conpherence-menu' => '4047cd35', 'javelin-behavior-conpherence-participant-pane' => 'd057e45a', @@ -903,15 +903,6 @@ return array( 'javelin-behavior', 'javelin-uri', ), - '038bf27f' => array( - 'javelin-behavior', - 'javelin-stratcom', - 'javelin-workflow', - 'javelin-dom', - 'phuix-form-control-view', - 'phuix-icon-view', - 'javelin-behavior-phabricator-gesture', - ), '040fce04' => array( 'javelin-behavior', 'javelin-request', @@ -1319,6 +1310,15 @@ return array( 'javelin-vector', 'javelin-dom', ), + '59e27e74' => array( + 'javelin-behavior', + 'javelin-stratcom', + 'javelin-workflow', + 'javelin-dom', + 'phuix-form-control-view', + 'phuix-icon-view', + 'javelin-behavior-phabricator-gesture', + ), '5c54cbf3' => array( 'javelin-behavior', 'javelin-stratcom', diff --git a/src/applications/differential/xaction/DifferentialRevisionActionTransaction.php b/src/applications/differential/xaction/DifferentialRevisionActionTransaction.php index cf2b557764..99dc8bebc6 100644 --- a/src/applications/differential/xaction/DifferentialRevisionActionTransaction.php +++ b/src/applications/differential/xaction/DifferentialRevisionActionTransaction.php @@ -57,6 +57,11 @@ abstract class DifferentialRevisionActionTransaction return null; } + protected function getRevisionActionSubmitButtonText( + DifferentialRevision $revision) { + return null; + } + public static function loadAllActions() { return id(new PhutilClassMapQuery()) ->setAncestorClass(__CLASS__) @@ -110,6 +115,9 @@ abstract class DifferentialRevisionActionTransaction $group_key = $this->getRevisionActionGroupKey(); $field->setCommentActionGroupKey($group_key); + $button_text = $this->getRevisionActionSubmitButtonText($revision); + $field->setActionSubmitButtonText($button_text); + // Currently, every revision action conflicts with every other // revision action: for example, you can not simultaneously Accept and // Reject a revision. diff --git a/src/applications/differential/xaction/DifferentialRevisionRequestReviewTransaction.php b/src/applications/differential/xaction/DifferentialRevisionRequestReviewTransaction.php index a3d2699c74..169e41dec5 100644 --- a/src/applications/differential/xaction/DifferentialRevisionRequestReviewTransaction.php +++ b/src/applications/differential/xaction/DifferentialRevisionRequestReviewTransaction.php @@ -19,6 +19,19 @@ final class DifferentialRevisionRequestReviewTransaction } } + protected function getRevisionActionSubmitButtonText( + DifferentialRevision $revision) { + + // See PHI975. When the action stack will promote the revision out of + // draft, change the button text from "Submit Quietly". + if ($revision->isDraft()) { + return pht('Publish Revision'); + } + + return null; + } + + public function getColor() { return 'sky'; } diff --git a/src/applications/owners/query/PhabricatorOwnersPackageQuery.php b/src/applications/owners/query/PhabricatorOwnersPackageQuery.php index ff0665b344..6d6ccb2ed2 100644 --- a/src/applications/owners/query/PhabricatorOwnersPackageQuery.php +++ b/src/applications/owners/query/PhabricatorOwnersPackageQuery.php @@ -228,7 +228,7 @@ final class PhabricatorOwnersPackageQuery $repository_phid, $indexes); } - $where[] = implode(' OR ', $clauses); + $where[] = qsprintf($conn, '%LO', $clauses); } return $where; diff --git a/src/applications/transactions/commentaction/PhabricatorEditEngineCommentAction.php b/src/applications/transactions/commentaction/PhabricatorEditEngineCommentAction.php index 4d3b9ca5df..d363e5a14e 100644 --- a/src/applications/transactions/commentaction/PhabricatorEditEngineCommentAction.php +++ b/src/applications/transactions/commentaction/PhabricatorEditEngineCommentAction.php @@ -9,6 +9,7 @@ abstract class PhabricatorEditEngineCommentAction extends Phobject { private $order; private $groupKey; private $conflictKey; + private $submitButtonText; abstract public function getPHUIXControlType(); abstract public function getPHUIXControlSpecification(); @@ -81,4 +82,13 @@ abstract class PhabricatorEditEngineCommentAction extends Phobject { return $this->initialValue; } + public function setSubmitButtonText($text) { + $this->submitButtonText = $text; + return $this; + } + + public function getSubmitButtonText() { + return $this->submitButtonText; + } + } diff --git a/src/applications/transactions/editfield/PhabricatorApplyEditField.php b/src/applications/transactions/editfield/PhabricatorApplyEditField.php index d349767f94..02d4cad31f 100644 --- a/src/applications/transactions/editfield/PhabricatorApplyEditField.php +++ b/src/applications/transactions/editfield/PhabricatorApplyEditField.php @@ -5,6 +5,7 @@ final class PhabricatorApplyEditField private $actionDescription; private $actionConflictKey; + private $actionSubmitButtonText; private $options; protected function newControl() { @@ -29,6 +30,15 @@ final class PhabricatorApplyEditField return $this->actionConflictKey; } + public function setActionSubmitButtonText($text) { + $this->actionSubmitButtonText = $text; + return $this; + } + + public function getActionSubmitButtonText() { + return $this->actionSubmitButtonText; + } + public function setOptions(array $options) { $this->options = $options; return $this; @@ -59,14 +69,16 @@ final class PhabricatorApplyEditField protected function newCommentAction() { $options = $this->getOptions(); if ($options) { - return id(new PhabricatorEditEngineCheckboxesCommentAction()) - ->setConflictKey($this->getActionConflictKey()) + $action = id(new PhabricatorEditEngineCheckboxesCommentAction()) ->setOptions($options); } else { - return id(new PhabricatorEditEngineStaticCommentAction()) - ->setConflictKey($this->getActionConflictKey()) + $action = id(new PhabricatorEditEngineStaticCommentAction()) ->setDescription($this->getActionDescription()); } + + return $action + ->setConflictKey($this->getActionConflictKey()) + ->setSubmitButtonText($this->getActionSubmitButtonText()); } } diff --git a/src/applications/transactions/view/PhabricatorApplicationTransactionCommentView.php b/src/applications/transactions/view/PhabricatorApplicationTransactionCommentView.php index c0f514c904..227854c79f 100644 --- a/src/applications/transactions/view/PhabricatorApplicationTransactionCommentView.php +++ b/src/applications/transactions/view/PhabricatorApplicationTransactionCommentView.php @@ -329,7 +329,6 @@ class PhabricatorApplicationTransactionCommentView extends AphrontView { $key = $comment_action->getKey(); $label = $comment_action->getLabel(); - $action_map[$key] = array( 'key' => $key, 'label' => $label, @@ -339,6 +338,7 @@ class PhabricatorApplicationTransactionCommentView extends AphrontView { 'groupKey' => $comment_action->getGroupKey(), 'conflictKey' => $comment_action->getConflictKey(), 'auralLabel' => pht('Remove Action: %s', $label), + 'buttonText' => $comment_action->getSubmitButtonText(), ); $type_map[$key] = $comment_action; @@ -404,6 +404,7 @@ class PhabricatorApplicationTransactionCommentView extends AphrontView { 'showPreview' => $this->getShowPreview(), 'actionURI' => $this->getAction(), 'drafts' => $draft_keys, + 'defaultButtonText' => $this->getSubmitButtonName(), )); } @@ -426,6 +427,7 @@ class PhabricatorApplicationTransactionCommentView extends AphrontView { id(new AphrontFormSubmitControl()) ->addClass('phui-comment-fullwidth-control') ->addClass('phui-comment-submit-control') + ->addSigil('submit-transactions') ->setValue($this->getSubmitButtonName())); return $form; diff --git a/src/view/form/control/AphrontFormSubmitControl.php b/src/view/form/control/AphrontFormSubmitControl.php index c2bf716848..7e948bd135 100644 --- a/src/view/form/control/AphrontFormSubmitControl.php +++ b/src/view/form/control/AphrontFormSubmitControl.php @@ -3,6 +3,7 @@ final class AphrontFormSubmitControl extends AphrontFormControl { private $buttons = array(); + private $sigils = array(); public function addCancelButton($href, $label = null) { if (!$label) { @@ -22,6 +23,11 @@ final class AphrontFormSubmitControl extends AphrontFormControl { return $this; } + public function addSigil($sigil) { + $this->sigils[] = $sigil; + return $this; + } + protected function getCustomControlClass() { return 'aphront-form-control-submit'; } @@ -29,11 +35,19 @@ final class AphrontFormSubmitControl extends AphrontFormControl { protected function renderInput() { $submit_button = null; if ($this->getValue()) { - $submit_button = phutil_tag( + + if ($this->sigils) { + $sigils = $this->sigils; + } else { + $sigils = null; + } + + $submit_button = javelin_tag( 'button', array( 'type' => 'submit', 'name' => '__submit__', + 'sigil' => $sigils, 'disabled' => $this->getDisabled() ? 'disabled' : null, ), $this->getValue()); diff --git a/webroot/rsrc/js/application/transactions/behavior-comment-actions.js b/webroot/rsrc/js/application/transactions/behavior-comment-actions.js index 9cec374f6b..ab962592c7 100644 --- a/webroot/rsrc/js/application/transactions/behavior-comment-actions.js +++ b/webroot/rsrc/js/application/transactions/behavior-comment-actions.js @@ -43,6 +43,22 @@ JX.behavior('comment-actions', function(config) { return null; } + function redraw() { + // If any of the stacked actions specify that they change the label for + // the "Submit" button, update the button text. Otherwise, return it to + // the default text. + var button_text = config.defaultButtonText; + for (var k in rows) { + var action = action_map[k]; + if (action.buttonText) { + button_text = action.buttonText; + } + } + + var button_node = JX.DOM.find(form_node, 'button', 'submit-transactions'); + JX.DOM.setContent(button_node, button_text); + } + function remove_action(key) { var row = rows[key]; if (row) { @@ -50,6 +66,8 @@ JX.behavior('comment-actions', function(config) { row.option.disabled = false; delete rows[key]; } + + redraw(); } function serialize_actions() { @@ -90,6 +108,8 @@ JX.behavior('comment-actions', function(config) { control = add_row(option); } + + redraw(); } function onresponse(response) { @@ -209,6 +229,8 @@ JX.behavior('comment-actions', function(config) { place_node.parentNode.insertBefore(node, place_node); + redraw(); + force_preview(); return control; From b2e91d2205393cd3c7b64addc46be6a190a4700e Mon Sep 17 00:00:00 2001 From: epriestley Date: Wed, 14 Nov 2018 15:54:04 -0800 Subject: [PATCH 15/43] Move the "container updated" message for Buildables that build Diffs outside of the transaction Summary: Ref T13216. See PHI970. Ref T13054. See some discussion in T13216. When a Harbormaster Buildable object is first created for a Diff, it has no `containerPHID` since the revision has not yet been created. We later (after creating a revision) send the Buildable a message telling it that we've added a container and it should re-link the container object. Currently, we send this message in `applyExternalEffects()`, which runs inside the Differential transaction. If Harbormaster races quickly enough, it can read the `Diff` object before the transaction commits, and not see the container update. Add a `didCommitTransaction()` callback after the transactions commit, then move the message code there instead. Test Plan: - See T13216 for substantial evidence that this change is on the right track. - Before change: added `sleep(15)`, reproduced the issue reliably. - After change: unable to reproduce issue even with `sleep(15)` (the `containerPHID` always populates correctly). Reviewers: amckinley Reviewed By: amckinley Maniphest Tasks: T13216, T13054 Differential Revision: https://secure.phabricator.com/D19807 --- .../DifferentialRevisionUpdateTransaction.php | 8 ++++++- ...habricatorApplicationTransactionEditor.php | 21 +++++++++++++++++++ .../PhabricatorModularTransactionType.php | 4 ++++ .../query/PhabricatorWorkerTaskQuery.php | 16 +++++++------- .../query/PhabricatorWorkerTriggerQuery.php | 10 ++++++--- 5 files changed, 47 insertions(+), 12 deletions(-) diff --git a/src/applications/differential/xaction/DifferentialRevisionUpdateTransaction.php b/src/applications/differential/xaction/DifferentialRevisionUpdateTransaction.php index bf1e6de870..33bbaceb7e 100644 --- a/src/applications/differential/xaction/DifferentialRevisionUpdateTransaction.php +++ b/src/applications/differential/xaction/DifferentialRevisionUpdateTransaction.php @@ -57,6 +57,12 @@ final class DifferentialRevisionUpdateTransaction // Harbormaster. See discussion in T8650. $diff->setRevisionID($object->getID()); $diff->save(); + } + + public function didCommitTransaction($object, $value) { + $editor = $this->getEditor(); + $diff = $editor->requireDiff($value); + $omnipotent = PhabricatorUser::getOmnipotentUser(); // If there are any outstanding buildables for this diff, tell // Harbormaster that their containers need to be updated. This is @@ -64,7 +70,7 @@ final class DifferentialRevisionUpdateTransaction // and unit results. $buildables = id(new HarbormasterBuildableQuery()) - ->setViewer(PhabricatorUser::getOmnipotentUser()) + ->setViewer($omnipotent) ->withManualBuildables(false) ->withBuildablePHIDs(array($diff->getPHID())) ->execute(); diff --git a/src/applications/transactions/editor/PhabricatorApplicationTransactionEditor.php b/src/applications/transactions/editor/PhabricatorApplicationTransactionEditor.php index b4dd09ed10..a77b7f84a4 100644 --- a/src/applications/transactions/editor/PhabricatorApplicationTransactionEditor.php +++ b/src/applications/transactions/editor/PhabricatorApplicationTransactionEditor.php @@ -869,6 +869,24 @@ abstract class PhabricatorApplicationTransactionEditor return $xactions; } + final protected function didCommitTransactions( + PhabricatorLiskDAO $object, + array $xactions) { + + foreach ($xactions as $xaction) { + $type = $xaction->getTransactionType(); + + $xtype = $this->getModularTransactionType($type); + if (!$xtype) { + continue; + } + + $xtype = clone $xtype; + $xtype->setStorage($xaction); + $xtype->didCommitTransaction($object, $xaction->getNewValue()); + } + } + public function setContentSource(PhabricatorContentSource $content_source) { $this->contentSource = $content_source; return $this; @@ -1106,6 +1124,9 @@ abstract class PhabricatorApplicationTransactionEditor $object->saveTransaction(); $transaction_open = false; } + + $this->didCommitTransactions($object, $xactions); + } catch (Exception $ex) { if ($read_locking) { $object->endReadLocking(); diff --git a/src/applications/transactions/storage/PhabricatorModularTransactionType.php b/src/applications/transactions/storage/PhabricatorModularTransactionType.php index 3d81e02c38..c5ad835750 100644 --- a/src/applications/transactions/storage/PhabricatorModularTransactionType.php +++ b/src/applications/transactions/storage/PhabricatorModularTransactionType.php @@ -35,6 +35,10 @@ abstract class PhabricatorModularTransactionType return; } + public function didCommitTransaction($object, $value) { + return; + } + public function getTransactionHasEffect($object, $old, $new) { return ($old !== $new); } diff --git a/src/infrastructure/daemon/workers/query/PhabricatorWorkerTaskQuery.php b/src/infrastructure/daemon/workers/query/PhabricatorWorkerTaskQuery.php index fa9a521d0f..3797de50f9 100644 --- a/src/infrastructure/daemon/workers/query/PhabricatorWorkerTaskQuery.php +++ b/src/infrastructure/daemon/workers/query/PhabricatorWorkerTaskQuery.php @@ -103,26 +103,26 @@ abstract class PhabricatorWorkerTaskQuery return $this->formatWhereClause($conn, $where); } - protected function buildOrderClause(AphrontDatabaseConnection $conn_r) { + protected function buildOrderClause(AphrontDatabaseConnection $conn) { // NOTE: The garbage collector executes this query with a date constraint, // and the query is inefficient if we don't use the same key for ordering. // See T9808 for discussion. if ($this->dateCreatedBefore) { - return qsprintf($conn_r, 'ORDER BY dateCreated DESC, id DESC'); + return qsprintf($conn, 'ORDER BY dateCreated DESC, id DESC'); } else if ($this->dateModifiedSince) { - return qsprintf($conn_r, 'ORDER BY dateModified DESC, id DESC'); + return qsprintf($conn, 'ORDER BY dateModified DESC, id DESC'); } else { - return qsprintf($conn_r, 'ORDER BY id DESC'); + return qsprintf($conn, 'ORDER BY id DESC'); } } - protected function buildLimitClause(AphrontDatabaseConnection $conn_r) { - $clause = ''; + protected function buildLimitClause(AphrontDatabaseConnection $conn) { if ($this->limit) { - $clause = qsprintf($conn_r, 'LIMIT %d', $this->limit); + return qsprintf($conn, 'LIMIT %d', $this->limit); + } else { + return qsprintf($conn, ''); } - return $clause; } } diff --git a/src/infrastructure/daemon/workers/query/PhabricatorWorkerTriggerQuery.php b/src/infrastructure/daemon/workers/query/PhabricatorWorkerTriggerQuery.php index 931b09f1f9..87c16a48c5 100644 --- a/src/infrastructure/daemon/workers/query/PhabricatorWorkerTriggerQuery.php +++ b/src/infrastructure/daemon/workers/query/PhabricatorWorkerTriggerQuery.php @@ -145,19 +145,23 @@ final class PhabricatorWorkerTriggerQuery return $triggers; } - protected function buildJoinClause(AphrontDatabaseConnection $conn_r) { + protected function buildJoinClause(AphrontDatabaseConnection $conn) { $joins = array(); if (($this->nextEpochMin !== null) || ($this->nextEpochMax !== null) || ($this->order == self::ORDER_EXECUTION)) { $joins[] = qsprintf( - $conn_r, + $conn, 'JOIN %T e ON e.triggerID = t.id', id(new PhabricatorWorkerTriggerEvent())->getTableName()); } - return implode(' ', $joins); + if ($joins) { + return qsprintf($conn, '%LJ', $joins); + } else { + return qsprintf($conn, ''); + } } protected function buildWhereClause(AphrontDatabaseConnection $conn) { From 49483bdb4823ed66e2afaa2b642c8c6f2853b41c Mon Sep 17 00:00:00 2001 From: epriestley Date: Thu, 15 Nov 2018 05:28:46 -0800 Subject: [PATCH 16/43] Use "%P" to protect session key hashes in SessionEngine queries from DarkConsole Summary: Ref T6960. Ref T13217. Ref T13216. Depends on D19811. Use the recently-introduced "%P" conversion ("Password/Secret") to load sessions in SessionEngine. This secret isn't critical to protect (it's the //hash// of the actual secret and not useful to attackers on its own) but it shows up on every page in DarkConsole and is an obvious case where `%P` is a more appropriate conversion. Test Plan: Note "*********" in the middle of the output here, instead of a session key hash: {F6012805} Reviewers: amckinley Reviewed By: amckinley Maniphest Tasks: T13217, T13216, T6960 Differential Revision: https://secure.phabricator.com/D19812 --- src/applications/auth/engine/PhabricatorAuthSessionEngine.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/applications/auth/engine/PhabricatorAuthSessionEngine.php b/src/applications/auth/engine/PhabricatorAuthSessionEngine.php index 76c0b310a3..98ec8b744a 100644 --- a/src/applications/auth/engine/PhabricatorAuthSessionEngine.php +++ b/src/applications/auth/engine/PhabricatorAuthSessionEngine.php @@ -127,12 +127,12 @@ final class PhabricatorAuthSessionEngine extends Phobject { u.* %Q FROM %T u JOIN %T s ON u.phid = s.userPHID - AND s.type = %s AND s.sessionKey = %s %Q', + AND s.type = %s AND s.sessionKey = %P %Q', $cache_selects, $user_table->getTableName(), $session_table->getTableName(), $session_type, - $session_key, + new PhutilOpaqueEnvelope($session_key), $cache_joins); if (!$info) { From 933462b4873b4830c9b057e3a15d1aa622835744 Mon Sep 17 00:00:00 2001 From: epriestley Date: Thu, 15 Nov 2018 05:53:34 -0800 Subject: [PATCH 17/43] Continue cleaning up queries in the wake of changes to "%Q" Summary: Depends on D19810. Ref T13217. Ref T13216. I mostly used `grep implode | grep OR` and `grep implode | grep AND` to find these -- not totally exhaustive but should be a big chunk of the callsites that are missing `%LO` / `%LA`. Test Plan: These are tricky to test exhaustively, but I made an attempt to hit most of them: - Browsed Almanac interfaces. - Created/browsed Calendar events. - Enabled/disabled/showed the lock log. - Browsed repositories. - Loaded Facts UI. - Poked at Multimeter. - Used typeahead for users and projects. - Browsed Phriction. - Ran various fulltext searches. Not sure these are reachable: - All the lint stuff might be dead/unreachable/nonfunctional? Reviewers: amckinley Reviewed By: amckinley Subscribers: yelirekim Maniphest Tasks: T13217, T13216 Differential Revision: https://secure.phabricator.com/D19814 --- .../almanac/query/AlmanacInterfaceQuery.php | 2 +- .../query/PhabricatorCalendarEventQuery.php | 4 +-- .../PhabricatorLockLogManagementWorkflow.php | 4 +-- .../diffusion/DiffusionLintSaveRunner.php | 8 ++--- .../DiffusionBrowseQueryConduitAPIMethod.php | 4 +-- .../controller/DiffusionLintController.php | 12 +++---- .../query/DiffusionCachedResolveRefsQuery.php | 4 +-- .../query/PhabricatorFactDatapointQuery.php | 4 +-- .../controller/MultimeterSampleController.php | 8 ++--- .../query/PhabricatorPackagesQuery.php | 2 +- .../people/query/PhabricatorPeopleQuery.php | 2 +- .../query/PhrequentUserTimeQuery.php | 4 +-- .../query/PhrictionDocumentQuery.php | 8 +++-- .../project/query/PhabricatorProjectQuery.php | 2 +- ...PhabricatorCursorPagedPolicyAwareQuery.php | 35 +++++++++++++------ 15 files changed, 59 insertions(+), 44 deletions(-) diff --git a/src/applications/almanac/query/AlmanacInterfaceQuery.php b/src/applications/almanac/query/AlmanacInterfaceQuery.php index bb6fc2f9d9..d5886761c1 100644 --- a/src/applications/almanac/query/AlmanacInterfaceQuery.php +++ b/src/applications/almanac/query/AlmanacInterfaceQuery.php @@ -121,7 +121,7 @@ final class AlmanacInterfaceQuery $address->getAddress(), $address->getPort()); } - $where[] = implode(' OR ', $parts); + $where[] = qsprintf($conn, '%LO', $parts); } return $where; diff --git a/src/applications/calendar/query/PhabricatorCalendarEventQuery.php b/src/applications/calendar/query/PhabricatorCalendarEventQuery.php index c35622fc0e..fc1399fdb3 100644 --- a/src/applications/calendar/query/PhabricatorCalendarEventQuery.php +++ b/src/applications/calendar/query/PhabricatorCalendarEventQuery.php @@ -444,8 +444,8 @@ final class PhabricatorCalendarEventQuery $where[] = qsprintf( $conn, - '%Q', - implode(' OR ', $sql)); + '%LO', + $sql); } if ($this->isStub !== null) { diff --git a/src/applications/daemon/management/PhabricatorLockLogManagementWorkflow.php b/src/applications/daemon/management/PhabricatorLockLogManagementWorkflow.php index 5602bda309..8bf9be9423 100644 --- a/src/applications/daemon/management/PhabricatorLockLogManagementWorkflow.php +++ b/src/applications/daemon/management/PhabricatorLockLogManagementWorkflow.php @@ -107,9 +107,9 @@ final class PhabricatorLockLogManagementWorkflow } if (!$parts) { - $constraint = '1 = 1'; + $constraint = qsprintf($conn, '1 = 1'); } else { - $constraint = '('.implode(') AND (', $parts).')'; + $constraint = qsprintf($conn, '%LA', $parts); } $logs = $table->loadAllWhere( diff --git a/src/applications/diffusion/DiffusionLintSaveRunner.php b/src/applications/diffusion/DiffusionLintSaveRunner.php index 980234f95d..a1a2fe7d32 100644 --- a/src/applications/diffusion/DiffusionLintSaveRunner.php +++ b/src/applications/diffusion/DiffusionLintSaveRunner.php @@ -229,9 +229,9 @@ final class DiffusionLintSaveRunner extends Phobject { $this->conn, 'INSERT INTO %T (branchID, path, line, code, severity, name, description) - VALUES %Q', + VALUES %LQ', PhabricatorRepository::TABLE_LINTMESSAGE, - implode(', ', $values)); + $values); } $this->conn->saveTransaction(); @@ -295,10 +295,10 @@ final class DiffusionLintSaveRunner extends Phobject { } queryfx( $this->conn, - 'UPDATE %T SET authorPHID = %s WHERE %Q', + 'UPDATE %T SET authorPHID = %s WHERE %LO', PhabricatorRepository::TABLE_LINTMESSAGE, $author, - implode(' OR ', $where)); + $where); } $this->conn->saveTransaction(); diff --git a/src/applications/diffusion/conduit/DiffusionBrowseQueryConduitAPIMethod.php b/src/applications/diffusion/conduit/DiffusionBrowseQueryConduitAPIMethod.php index 2e272d69a7..fe99471b0c 100644 --- a/src/applications/diffusion/conduit/DiffusionBrowseQueryConduitAPIMethod.php +++ b/src/applications/diffusion/conduit/DiffusionBrowseQueryConduitAPIMethod.php @@ -451,13 +451,13 @@ final class DiffusionBrowseQueryConduitAPIMethod WHERE repositoryID = %d AND parentID = %d AND existed = 1 - AND (%Q) + AND (%LO) ORDER BY pathName', PhabricatorRepository::TABLE_FILESYSTEM, PhabricatorRepository::TABLE_PATH, $repository->getID(), $path_id, - implode(' OR ', $sql)); + $sql); $loadable_commits = array(); foreach ($browse as $key => $file) { diff --git a/src/applications/diffusion/controller/DiffusionLintController.php b/src/applications/diffusion/controller/DiffusionLintController.php index af3ed571ce..704435882d 100644 --- a/src/applications/diffusion/controller/DiffusionLintController.php +++ b/src/applications/diffusion/controller/DiffusionLintController.php @@ -276,13 +276,13 @@ final class DiffusionLintController extends DiffusionController { array_keys($branch), $path->getPath()); if ($path->getExcluded()) { - $where[] = 'NOT '.$condition; + $where[] = qsprintf($conn, 'NOT %Q', $condition); } else { $or[] = $condition; } } } - $where[] = '('.implode(' OR ', $or).')'; + $where[] = qsprintf($conn, '%LO', $or); } return queryfx_all( @@ -296,11 +296,11 @@ final class DiffusionLintController extends DiffusionController { COUNT(DISTINCT path) AS files, COUNT(*) AS n FROM %T - WHERE %Q + WHERE %LA GROUP BY branchID, code ORDER BY n DESC', PhabricatorRepository::TABLE_LINTMESSAGE, - implode(' AND ', $where)); + $where); } protected function buildActionView(DiffusionRequest $drequest) { @@ -526,10 +526,10 @@ final class DiffusionLintController extends DiffusionController { $conn, 'SELECT * FROM %T - WHERE %Q + WHERE %LA ORDER BY path, code, line LIMIT %d OFFSET %d', PhabricatorRepository::TABLE_LINTMESSAGE, - implode(' AND ', $where), + $where, $limit, $offset); } diff --git a/src/applications/diffusion/query/DiffusionCachedResolveRefsQuery.php b/src/applications/diffusion/query/DiffusionCachedResolveRefsQuery.php index 0217c97d58..1666056d63 100644 --- a/src/applications/diffusion/query/DiffusionCachedResolveRefsQuery.php +++ b/src/applications/diffusion/query/DiffusionCachedResolveRefsQuery.php @@ -81,10 +81,10 @@ final class DiffusionCachedResolveRefsQuery $commits = queryfx_all( $conn_r, 'SELECT commitIdentifier FROM %T - WHERE repositoryID = %s AND %Q', + WHERE repositoryID = %s AND %LO', id(new PhabricatorRepositoryCommit())->getTableName(), $repository->getID(), - implode(' OR ', $prefixes)); + $prefixes); foreach ($commits as $commit) { $hash = $commit['commitIdentifier']; diff --git a/src/applications/fact/query/PhabricatorFactDatapointQuery.php b/src/applications/fact/query/PhabricatorFactDatapointQuery.php index fee7c86d12..20945dbbd0 100644 --- a/src/applications/fact/query/PhabricatorFactDatapointQuery.php +++ b/src/applications/fact/query/PhabricatorFactDatapointQuery.php @@ -158,7 +158,7 @@ final class PhabricatorFactDatapointQuery extends Phobject { $this->dimensionMap); } - $where = '('.implode(') AND (', $where).')'; + $where = qsprintf($conn, '%LA', $where); if ($this->limit) { $limit = qsprintf( @@ -166,7 +166,7 @@ final class PhabricatorFactDatapointQuery extends Phobject { 'LIMIT %d', $this->limit); } else { - $limit = ''; + $limit = qsprintf($conn, ''); } return queryfx_all( diff --git a/src/applications/multimeter/controller/MultimeterSampleController.php b/src/applications/multimeter/controller/MultimeterSampleController.php index 0023038185..da09641d22 100644 --- a/src/applications/multimeter/controller/MultimeterSampleController.php +++ b/src/applications/multimeter/controller/MultimeterSampleController.php @@ -52,8 +52,6 @@ final class MultimeterSampleController extends MultimeterController { } } - $where = '('.implode(') AND (', $where).')'; - $data = queryfx_all( $conn, 'SELECT *, @@ -61,13 +59,13 @@ final class MultimeterSampleController extends MultimeterController { SUM(sampleRate * resourceCost) AS totalCost, SUM(sampleRate * resourceCost) / SUM(sampleRate) AS averageCost FROM %T - WHERE %Q - GROUP BY %Q + WHERE %LA + GROUP BY %LC ORDER BY totalCost DESC, MAX(id) DESC LIMIT 100', $table->getTableName(), $where, - implode(', ', array_select_keys($group_map, $group))); + array_select_keys($group_map, $group)); $this->loadDimensions($data); $phids = array(); diff --git a/src/applications/packages/query/PhabricatorPackagesQuery.php b/src/applications/packages/query/PhabricatorPackagesQuery.php index 58b90599eb..e7e882bdad 100644 --- a/src/applications/packages/query/PhabricatorPackagesQuery.php +++ b/src/applications/packages/query/PhabricatorPackagesQuery.php @@ -32,7 +32,7 @@ abstract class PhabricatorPackagesQuery throw new PhabricatorEmptyQueryException(); } - return implode(' OR ', $parts); + return qsprintf($conn, '%LO', $parts); } } diff --git a/src/applications/people/query/PhabricatorPeopleQuery.php b/src/applications/people/query/PhabricatorPeopleQuery.php index 35ff480833..131dc7c588 100644 --- a/src/applications/people/query/PhabricatorPeopleQuery.php +++ b/src/applications/people/query/PhabricatorPeopleQuery.php @@ -268,7 +268,7 @@ final class PhabricatorPeopleQuery 'user.username LIKE %>', $name_prefix); } - $where[] = '('.implode(' OR ', $parts).')'; + $where[] = qsprintf($conn, '%LO', $parts); } if ($this->emails !== null) { diff --git a/src/applications/phrequent/query/PhrequentUserTimeQuery.php b/src/applications/phrequent/query/PhrequentUserTimeQuery.php index 7fb77f0f76..cf5122c020 100644 --- a/src/applications/phrequent/query/PhrequentUserTimeQuery.php +++ b/src/applications/phrequent/query/PhrequentUserTimeQuery.php @@ -177,9 +177,9 @@ final class PhrequentUserTimeQuery $preempting_events = queryfx_all( $conn_r, - 'SELECT * FROM %T WHERE %Q ORDER BY dateStarted ASC, id ASC', + 'SELECT * FROM %T WHERE %LO ORDER BY dateStarted ASC, id ASC', $usertime->getTableName(), - implode(' OR ', $preempt)); + $preempt); $preempting_events = $usertime->loadAllFromArray($preempting_events); $preempting_events = mgroup($preempting_events, 'getUserPHID'); diff --git a/src/applications/phriction/query/PhrictionDocumentQuery.php b/src/applications/phriction/query/PhrictionDocumentQuery.php index 8a0fa325ae..5f508ad804 100644 --- a/src/applications/phriction/query/PhrictionDocumentQuery.php +++ b/src/applications/phriction/query/PhrictionDocumentQuery.php @@ -309,10 +309,14 @@ final class PhrictionDocumentQuery $max); } - $path_clauses[] = '('.implode(') AND (', $parts).')'; + if ($parts) { + $path_clauses[] = qsprintf($conn, '%LA', $parts); + } } - $where[] = '('.implode(') OR (', $path_clauses).')'; + if ($path_clauses) { + $where[] = qsprintf($conn, '%LO', $path_clauses); + } } return $where; diff --git a/src/applications/project/query/PhabricatorProjectQuery.php b/src/applications/project/query/PhabricatorProjectQuery.php index e3a02edbd0..9b051c00dd 100644 --- a/src/applications/project/query/PhabricatorProjectQuery.php +++ b/src/applications/project/query/PhabricatorProjectQuery.php @@ -514,7 +514,7 @@ final class PhabricatorProjectQuery 'name LIKE %>', $name_prefix); } - $where[] = '('.implode(' OR ', $parts).')'; + $where[] = qsprintf($conn, '%LO', $parts); } if ($this->icons !== null) { diff --git a/src/infrastructure/query/policy/PhabricatorCursorPagedPolicyAwareQuery.php b/src/infrastructure/query/policy/PhabricatorCursorPagedPolicyAwareQuery.php index 2d91f5e608..931840e8fb 100644 --- a/src/infrastructure/query/policy/PhabricatorCursorPagedPolicyAwareQuery.php +++ b/src/infrastructure/query/policy/PhabricatorCursorPagedPolicyAwareQuery.php @@ -1440,7 +1440,11 @@ abstract class PhabricatorCursorPagedPolicyAwareQuery $key); } - return implode(' ', $joins); + if ($joins) { + return qsprintf($conn, '%LJ', $joins); + } else { + return qsprintf($conn, ''); + } } /** @@ -1516,7 +1520,7 @@ abstract class PhabricatorCursorPagedPolicyAwareQuery } if ($constraint_parts) { - $where[] = '('.implode(') OR (', $constraint_parts).')'; + $where[] = qsprintf($conn, '%LO', $constraint_parts); } break; } @@ -1670,7 +1674,7 @@ abstract class PhabricatorCursorPagedPolicyAwareQuery } if (!$this->ferretEngine) { - $select[] = '0 _ft_rank'; + $select[] = qsprintf($conn, '0 _ft_rank'); return $select; } @@ -1736,12 +1740,21 @@ abstract class PhabricatorCursorPagedPolicyAwareQuery } } - $parts[] = '0'; + $parts[] = qsprintf($conn, '%d', 0); + + $sum = array_shift($parts); + foreach ($parts as $part) { + $sum = qsprintf( + $conn, + '%Q + %Q', + $sum, + $part); + } $select[] = qsprintf( $conn, '%Q _ft_rank', - implode(' + ', $parts)); + $sum); return $select; } @@ -2031,20 +2044,20 @@ abstract class PhabricatorCursorPagedPolicyAwareQuery if ($is_not) { $where[] = qsprintf( $conn, - '(%Q)', - implode(' AND ', $term_constraints)); + '%LA', + $term_constraints); } else if ($is_quoted) { $where[] = qsprintf( $conn, - '(%T.rawCorpus LIKE %~ AND (%Q))', + '(%T.rawCorpus LIKE %~ AND %LO)', $table_alias, $value, - implode(' OR ', $term_constraints)); + $term_constraints); } else { $where[] = qsprintf( $conn, - '(%Q)', - implode(' OR ', $term_constraints)); + '%LO', + $term_constraints); } } From cb033673b6eb3dc8330d2ddea0fd358eae3b939a Mon Sep 17 00:00:00 2001 From: epriestley Date: Thu, 15 Nov 2018 08:11:49 -0800 Subject: [PATCH 18/43] Unify intracluster sync and Drydock working copy construction timeouts as a repository "copy time limit" Summary: Depends on D19814. Ref T13216. See PHI885. For various eldritch reasons, `git fetch` can hang. Although we'd probably like to fix this with `git fetch --require-sustained-network-transfer-rate=512KB/5s` or similar, that flag doesn't exist and we don't have a reasonable way to build it. Short of that, move toward formalizing a repository "copy time limit": the longest amount of time anything may spend trying to make a copy of this repository. This grows out of the existing intracluster sync limit, which is effectively the same thing. Here, apply it to `git clone` and `git fetch` in Drydock working copy construction, too. A future change may make it configurable. Test Plan: - Set the limit to 0.001. - Tried to build and lease working copies, got sensible timeout errors (see D19815). ``` Lease activation failed: [CommandException] Command killed by timeout after running for more than 0.001 seconds. COMMAND ssh '-o' 'LogLevel=quiet' '-o' 'StrictHostKeyChecking=no' '-o' 'UserKnownHostsFile=/dev/null' '-o' 'BatchMode=yes' -l '********' -p '2222' -i '********' '127.0.0.1' -- '(cd '\''/var/drydock/workingcopy-163/repo/spellbook/'\'' && git clean -d --force && git fetch && git reset --hard)' ``` Reviewers: amckinley Reviewed By: amckinley Subscribers: yelirekim, PHID-OPKG-gm6ozazyms6q6i22gyam Maniphest Tasks: T13216 Differential Revision: https://secure.phabricator.com/D19816 --- .../protocol/DiffusionCommandEngine.php | 3 +- ...dockWorkingCopyBlueprintImplementation.php | 34 +++++++++++++++---- .../drydock/storage/DrydockSlotLock.php | 4 +-- .../storage/PhabricatorRepository.php | 13 +++++++ .../storage/PhabricatorRepositoryURIIndex.php | 10 +++--- 5 files changed, 50 insertions(+), 14 deletions(-) diff --git a/src/applications/diffusion/protocol/DiffusionCommandEngine.php b/src/applications/diffusion/protocol/DiffusionCommandEngine.php index d7da62b11d..9d52482b23 100644 --- a/src/applications/diffusion/protocol/DiffusionCommandEngine.php +++ b/src/applications/diffusion/protocol/DiffusionCommandEngine.php @@ -138,7 +138,8 @@ abstract class DiffusionCommandEngine extends Phobject { // See T13108. By default, don't let any cluster command run indefinitely // to try to avoid cases where `git fetch` hangs for some reason and we're // left sitting with a held lock forever. - $future->setTimeout(phutil_units('15 minutes in seconds')); + $repository = $this->getRepository(); + $future->setTimeout($repository->getCopyTimeLimit()); return $future; } diff --git a/src/applications/drydock/blueprint/DrydockWorkingCopyBlueprintImplementation.php b/src/applications/drydock/blueprint/DrydockWorkingCopyBlueprintImplementation.php index c1672f99fb..eb3a361ea8 100644 --- a/src/applications/drydock/blueprint/DrydockWorkingCopyBlueprintImplementation.php +++ b/src/applications/drydock/blueprint/DrydockWorkingCopyBlueprintImplementation.php @@ -173,6 +173,7 @@ final class DrydockWorkingCopyBlueprintImplementation $map = $resource->getAttribute('repositories.map'); + $futures = array(); $repositories = $this->loadRepositories(ipull($map, 'phid')); foreach ($map as $directory => $spec) { // TODO: Validate directory isn't goofy like "/etc" or "../../lol" @@ -181,11 +182,18 @@ final class DrydockWorkingCopyBlueprintImplementation $repository = $repositories[$spec['phid']]; $path = "{$root}/repo/{$directory}/"; - // TODO: Run these in parallel? - $interface->execx( + $future = $interface->getExecFuture( 'git clone -- %s %s', (string)$repository->getCloneURIObject(), $path); + + $future->setTimeout($repository->getCopyTimeLimit()); + + $futures[$directory] = $future; + } + + foreach (new FutureIterator($futures) as $key => $future) { + $future->resolvex(); } $resource @@ -240,8 +248,12 @@ final class DrydockWorkingCopyBlueprintImplementation $map = $lease->getAttribute('repositories.map'); $root = $resource->getAttribute('workingcopy.root'); + $repositories = $this->loadRepositories(ipull($map, 'phid')); + $default = null; foreach ($map as $directory => $spec) { + $repository = $repositories[$spec['phid']]; + $interface->pushWorkingDirectory("{$root}/repo/{$directory}/"); $cmd = array(); @@ -271,7 +283,9 @@ final class DrydockWorkingCopyBlueprintImplementation $arg[] = $branch; } - $this->execxv($interface, $cmd, $arg); + $this->newExecvFuture($interface, $cmd, $arg) + ->setTimeout($repository->getCopyTimeLimit()) + ->resolvex(); if (idx($spec, 'default')) { $default = $directory; @@ -295,7 +309,9 @@ final class DrydockWorkingCopyBlueprintImplementation $arg[] = $ref_ref; try { - $this->execxv($interface, $cmd, $arg); + $this->newExecvFuture($interface, $cmd, $arg) + ->setTimeout($repository->getCopyTimeLimit()) + ->resolvex(); } catch (CommandException $ex) { $display_command = csprintf( 'git fetch %R %R', @@ -509,12 +525,18 @@ final class DrydockWorkingCopyBlueprintImplementation DrydockCommandInterface $interface, array $commands, array $arguments) { + return $this->newExecvFuture($interface, $commands, $arguments)->resolvex(); + } + + private function newExecvFuture( + DrydockCommandInterface $interface, + array $commands, + array $arguments) { $commands = implode(' && ', $commands); $argv = array_merge(array($commands), $arguments); - return call_user_func_array(array($interface, 'execx'), $argv); + return call_user_func_array(array($interface, 'getExecFuture'), $argv); } - } diff --git a/src/applications/drydock/storage/DrydockSlotLock.php b/src/applications/drydock/storage/DrydockSlotLock.php index 7e4e320946..7a9bce8b3f 100644 --- a/src/applications/drydock/storage/DrydockSlotLock.php +++ b/src/applications/drydock/storage/DrydockSlotLock.php @@ -140,9 +140,9 @@ final class DrydockSlotLock extends DrydockDAO { try { queryfx( $conn_w, - 'INSERT INTO %T (ownerPHID, lockIndex, lockKey) VALUES %Q', + 'INSERT INTO %T (ownerPHID, lockIndex, lockKey) VALUES %LQ', $table->getTableName(), - implode(', ', $sql)); + $sql); } catch (AphrontDuplicateKeyQueryException $ex) { // Try to improve the readability of the exception. We might miss on // this query if the lock has already been released, but most of the diff --git a/src/applications/repository/storage/PhabricatorRepository.php b/src/applications/repository/storage/PhabricatorRepository.php index 52824c2aa4..2b0d63cf47 100644 --- a/src/applications/repository/storage/PhabricatorRepository.php +++ b/src/applications/repository/storage/PhabricatorRepository.php @@ -1889,6 +1889,19 @@ final class PhabricatorRepository extends PhabricatorRepositoryDAO } + /** + * Time limit for cloning or copying this repository. + * + * This limit is used to timeout operations like `git clone` or `git fetch` + * when doing intracluster synchronization, building working copies, etc. + * + * @return int Maximum number of seconds to spend copying this repository. + */ + public function getCopyTimeLimit() { + return phutil_units('15 minutes in seconds'); + } + + /** * Retrieve the service URI for the device hosting this repository. * diff --git a/src/applications/repository/storage/PhabricatorRepositoryURIIndex.php b/src/applications/repository/storage/PhabricatorRepositoryURIIndex.php index 2cfa0c4471..3ef41f8089 100644 --- a/src/applications/repository/storage/PhabricatorRepositoryURIIndex.php +++ b/src/applications/repository/storage/PhabricatorRepositoryURIIndex.php @@ -48,16 +48,16 @@ final class PhabricatorRepositoryURIIndex queryfx( $conn_w, - 'DELETE FROM %T WHERE repositoryPHID = %s', - $table->getTableName(), + 'DELETE FROM %R WHERE repositoryPHID = %s', + $table, $repository_phid); if ($sql) { queryfx( $conn_w, - 'INSERT INTO %T (repositoryPHID, repositoryURI) VALUES %Q', - $table->getTableName(), - implode(', ', $sql)); + 'INSERT INTO %R (repositoryPHID, repositoryURI) VALUES %LQ', + $table, + $sql); } $table->saveTransaction(); From 9481b9eff111168db57ae25cfa6a4b7a3967711c Mon Sep 17 00:00:00 2001 From: epriestley Date: Sat, 17 Nov 2018 10:39:25 -0800 Subject: [PATCH 19/43] Allow "Can Configure Application" permissions to be configured Summary: Ref T13216. See PHI980. Currently, each application in {nav Applications > X > Configure} has a "Can Configure Application" permission which is hard-coded to "Administrators". There's no technical reason for this, there just hasn't been a great use case for unlocking it. I think when I originally wrote it our protections against locking yourself out of things weren't that great (i.e., it was easier to set the policy to something that prevented you from editing it after the new policy took effect). Our protections are better now. The major goal here is to let installs open up Custom Forms for given applications (mostly Maniphest) to more users, but the other options mostly go hand-in-hand with that. Also, in developer mode, include stack traces for policy exceptions. This makes debugging weird stuff (like the indirect Config application errors here) easier. Test Plan: - Granted "Can Configure Application" for Maniphest to all users. - Edited custom forms as a non-administrator. - Configured Maniphest as a non-administrator. - Installed/uninstalled Maniphest as a non-administrator. - Tried to lock myself out (got an error message). {F6015721} Reviewers: amckinley, joshuaspence Reviewed By: joshuaspence Subscribers: joshuaspence Maniphest Tasks: T13216 Differential Revision: https://secure.phabricator.com/D19822 --- .../PhabricatorPolicyRequestExceptionHandler.php | 12 ++++++++++++ src/applications/base/PhabricatorApplication.php | 2 +- .../config/editor/PhabricatorConfigEditor.php | 7 ++++++- ...habricatorApplicationPolicyChangeTransaction.php | 13 +++++++++++-- .../PhabricatorApplicationUninstallTransaction.php | 8 ++++++-- 5 files changed, 36 insertions(+), 6 deletions(-) diff --git a/src/aphront/handler/PhabricatorPolicyRequestExceptionHandler.php b/src/aphront/handler/PhabricatorPolicyRequestExceptionHandler.php index cb1dea3533..442cb08f66 100644 --- a/src/aphront/handler/PhabricatorPolicyRequestExceptionHandler.php +++ b/src/aphront/handler/PhabricatorPolicyRequestExceptionHandler.php @@ -83,6 +83,18 @@ final class PhabricatorPolicyRequestExceptionHandler $dialog->appendList($list); } + // If the install is in developer mode, include a stack trace for the + // exception. When debugging things, it isn't always obvious where a + // policy exception came from and this can make it easier to hunt down + // bugs or improve ambiguous/confusing messaging. + + $is_developer = PhabricatorEnv::getEnvConfig('phabricator.developer-mode'); + if ($is_developer) { + $dialog->appendChild( + id(new AphrontStackTraceView()) + ->setTrace($throwable->getTrace())); + } + if ($request->isAjax()) { $dialog->addCancelButton('/', pht('Close')); } else { diff --git a/src/applications/base/PhabricatorApplication.php b/src/applications/base/PhabricatorApplication.php index e276e035e4..1cabeb0709 100644 --- a/src/applications/base/PhabricatorApplication.php +++ b/src/applications/base/PhabricatorApplication.php @@ -545,7 +545,7 @@ abstract class PhabricatorApplication case PhabricatorPolicyCapability::CAN_VIEW: return $this->canUninstall(); case PhabricatorPolicyCapability::CAN_EDIT: - return false; + return true; default: $spec = $this->getCustomCapabilitySpecification($capability); return idx($spec, 'edit', true); diff --git a/src/applications/config/editor/PhabricatorConfigEditor.php b/src/applications/config/editor/PhabricatorConfigEditor.php index f776c3ec0c..deccf1ef5c 100644 --- a/src/applications/config/editor/PhabricatorConfigEditor.php +++ b/src/applications/config/editor/PhabricatorConfigEditor.php @@ -118,7 +118,8 @@ final class PhabricatorConfigEditor PhabricatorUser $user, PhabricatorConfigEntry $config_entry, $value, - PhabricatorContentSource $source) { + PhabricatorContentSource $source, + $acting_as_phid = null) { $xaction = id(new PhabricatorConfigTransaction()) ->setTransactionType(PhabricatorConfigTransaction::TYPE_EDIT) @@ -133,6 +134,10 @@ final class PhabricatorConfigEditor ->setContinueOnNoEffect(true) ->setContentSource($source); + if ($acting_as_phid) { + $editor->setActingAsPHID($acting_as_phid); + } + $editor->applyTransactions($config_entry, array($xaction)); } diff --git a/src/applications/meta/xactions/PhabricatorApplicationPolicyChangeTransaction.php b/src/applications/meta/xactions/PhabricatorApplicationPolicyChangeTransaction.php index 91b5c73249..59dfc09486 100644 --- a/src/applications/meta/xactions/PhabricatorApplicationPolicyChangeTransaction.php +++ b/src/applications/meta/xactions/PhabricatorApplicationPolicyChangeTransaction.php @@ -35,11 +35,20 @@ final class PhabricatorApplicationPolicyChangeTransaction $editor = $this->getEditor(); $content_source = $editor->getContentSource(); + + // NOTE: We allow applications to have custom edit policies, but they are + // currently stored in the Config application. The ability to edit Config + // values is always restricted to administrators, today. Empower this + // particular edit to punch through possible stricter policies, so normal + // users can change application configuration if the application allows + // them to do so. + PhabricatorConfigEditor::storeNewValue( - $user, + PhabricatorUser::getOmnipotentUser(), $config_entry, $current_value, - $content_source); + $content_source, + $user->getPHID()); } public function getTitle() { diff --git a/src/applications/meta/xactions/PhabricatorApplicationUninstallTransaction.php b/src/applications/meta/xactions/PhabricatorApplicationUninstallTransaction.php index 12fbc8ebd4..b76e63b5c0 100644 --- a/src/applications/meta/xactions/PhabricatorApplicationUninstallTransaction.php +++ b/src/applications/meta/xactions/PhabricatorApplicationUninstallTransaction.php @@ -43,11 +43,15 @@ final class PhabricatorApplicationUninstallTransaction $editor = $this->getEditor(); $content_source = $editor->getContentSource(); + + // Today, changing config requires "Administrator", but "Can Edit" on + // applications to let you uninstall them may be granted to any user. PhabricatorConfigEditor::storeNewValue( - $user, + PhabricatorUser::getOmnipotentUser(), $config_entry, $list, - $content_source); + $content_source, + $user->getPHID()); } public function getTitle() { From 4967cd6ab941c466dfaf79db297e68e95ebdd1fc Mon Sep 17 00:00:00 2001 From: epriestley Date: Fri, 16 Nov 2018 17:18:33 -0800 Subject: [PATCH 20/43] Fix some "%Q" behavior in PhortuneMerchantQuery Summary: Ref T13217. This older query does some manual joins; update it for more modern joins. Test Plan: Ran `instances/` unit tests and got a clean result, browsed Phortune merchants. Reviewers: amckinley Reviewed By: amckinley Maniphest Tasks: T13217 Differential Revision: https://secure.phabricator.com/D19820 --- .../phortune/query/PhortuneMerchantQuery.php | 36 ++++++++----------- 1 file changed, 15 insertions(+), 21 deletions(-) diff --git a/src/applications/phortune/query/PhortuneMerchantQuery.php b/src/applications/phortune/query/PhortuneMerchantQuery.php index 033621a3f1..b6cab7dbc2 100644 --- a/src/applications/phortune/query/PhortuneMerchantQuery.php +++ b/src/applications/phortune/query/PhortuneMerchantQuery.php @@ -28,20 +28,12 @@ final class PhortuneMerchantQuery return $this; } + public function newResultObject() { + return new PhortuneMerchant(); + } + protected function loadPage() { - $table = new PhortuneMerchant(); - $conn = $table->establishConnection('r'); - - $rows = queryfx_all( - $conn, - 'SELECT m.* FROM %T m %Q %Q %Q %Q', - $table->getTableName(), - $this->buildJoinClause($conn), - $this->buildWhereClause($conn), - $this->buildOrderClause($conn), - $this->buildLimitClause($conn)); - - return $table->loadAllFromArray($rows); + return $this->loadStandardPage($this->newResultObject()); } protected function willFilterPage(array $merchants) { @@ -88,8 +80,8 @@ final class PhortuneMerchantQuery return $merchants; } - protected function buildWhereClause(AphrontDatabaseConnection $conn) { - $where = array(); + protected function buildWhereClauseParts(AphrontDatabaseConnection $conn) { + $where = parent::buildWhereClauseParts($conn); if ($this->ids !== null) { $where[] = qsprintf( @@ -112,13 +104,11 @@ final class PhortuneMerchantQuery $this->memberPHIDs); } - $where[] = $this->buildPagingClause($conn); - - return $this->formatWhereClause($conn, $where); + return $where; } - protected function buildJoinClause(AphrontDatabaseConnection $conn) { - $joins = array(); + protected function buildJoinClauseParts(AphrontDatabaseConnection $conn) { + $joins = parent::buildJoinClauseParts($conn); if ($this->memberPHIDs !== null) { $joins[] = qsprintf( @@ -128,11 +118,15 @@ final class PhortuneMerchantQuery PhortuneMerchantHasMemberEdgeType::EDGECONST); } - return implode(' ', $joins); + return $joins; } public function getQueryApplicationClass() { return 'PhabricatorPhortuneApplication'; } + protected function getPrimaryTableAlias() { + return 'm'; + } + } From ab14f49ef87d2b744cbd11cecedad270f4a3f58f Mon Sep 17 00:00:00 2001 From: epriestley Date: Thu, 15 Nov 2018 05:39:36 -0800 Subject: [PATCH 21/43] On the Diffusion cluster status page, improve device sort order Summary: Ref T13216. See PHI943. When you have a large number of cluster bindings for a repository, the UI sorting can be a bit hard to manage. One install that regularly cycles repository cluster devices had a couple dozen older disabled bindings, with the enabled bindings intermingled. Sort the UI: - enabled devices come first; - in each group, sort by name. Test Plan: Mixed disabled/enabled bindings, loaded {nav Diffusion > Repository > Storage} page with clustering configured. Before: relatively unhelpful sort order. After: more intuitive sort order. Reviewers: amckinley Reviewed By: amckinley Maniphest Tasks: T13216 Differential Revision: https://secure.phabricator.com/D19813 --- ...fusionRepositoryStorageManagementPanel.php | 33 ++++++++++++++----- 1 file changed, 25 insertions(+), 8 deletions(-) diff --git a/src/applications/diffusion/management/DiffusionRepositoryStorageManagementPanel.php b/src/applications/diffusion/management/DiffusionRepositoryStorageManagementPanel.php index a39dfaa632..0a15a62417 100644 --- a/src/applications/diffusion/management/DiffusionRepositoryStorageManagementPanel.php +++ b/src/applications/diffusion/management/DiffusionRepositoryStorageManagementPanel.php @@ -99,15 +99,20 @@ final class DiffusionRepositoryStorageManagementPanel $versions = mpull($versions, null, 'getDevicePHID'); - foreach ($bindings as $binding_group) { - $all_disabled = true; - foreach ($binding_group as $binding) { - if (!$binding->getIsDisabled()) { - $all_disabled = false; - break; - } - } + // List enabled devices first, then sort devices in each group by name. + $sort = array(); + foreach ($bindings as $key => $binding_group) { + $all_disabled = $this->isDisabledGroup($binding_group); + $sort[$key] = id(new PhutilSortVector()) + ->addInt($all_disabled ? 1 : 0) + ->addString(head($binding_group)->getDevice()->getName()); + } + $sort = msortv($sort, 'getSelf'); + $bindings = array_select_keys($bindings, array_keys($sort)) + $bindings; + + foreach ($bindings as $binding_group) { + $all_disabled = $this->isDisabledGroup($binding_group); $any_binding = head($binding_group); if ($all_disabled) { @@ -228,4 +233,16 @@ final class DiffusionRepositoryStorageManagementPanel return $this->newBox(pht('Cluster Status'), $table); } + private function isDisabledGroup(array $binding_group) { + assert_instances_of($binding_group, 'AlmanacBinding'); + + foreach ($binding_group as $binding) { + if (!$binding->getIsDisabled()) { + return false; + } + } + + return true; + } + } From a0d4b6da4b6017f3fee38e57c70467073d6ce767 Mon Sep 17 00:00:00 2001 From: epriestley Date: Fri, 16 Nov 2018 09:57:54 -0800 Subject: [PATCH 22/43] Support (but do not actually enable) a maximum file size limit for Git repositories Summary: Depends on D19816. Ref T13216. See PHI908. See PHI750. In a few cases, users have pushed multi-gigabyte files full of various things that probably shouldn't be version controlled. This tends to create various headaches. Add support for limiting the maximum size of any object. Specifically, we: - list all the objects each commit touches; - check their size after the commit applies; - if it's over the limit, reject the commit. This change doesn't actually hook the limit up (the limit is always "0", i.e. unlimited), and doesn't have Mercurial or SVN support. The actual parser bit would probably be better in some other `Query/Parser` class eventually, too. But it at least roughly works. Test Plan: Changed the hard-coded limit to other values, tried to push stuff, got sensible results: ``` $ echo pew >> magic_missile.txt && git commit -am pew && git push [master 98d07af] pew 1 file changed, 1 insertion(+) # Push received by "local.phacility.net", forwarding to cluster host. # Acquiring write lock for repository "spellbook"... # Acquired write lock immediately. # Acquiring read lock for repository "spellbook" on device "local.phacility.net"... # Acquired read lock immediately. # Device "local.phacility.net" is already a cluster leader and does not need to be synchronized. # Ready to receive on cluster host "local.phacility.net". Counting objects: 49, done. Delta compression using up to 8 threads. Compressing objects: 100% (48/48), done. Writing objects: 100% (49/49), 3.44 KiB | 1.72 MiB/s, done. Total 49 (delta 30), reused 0 (delta 0) remote: +---------------------------------------------------------------+ remote: | * * * PUSH REJECTED BY EVIL DRAGON BUREAUCRATS * * * | remote: +---------------------------------------------------------------+ remote: \ remote: \ ^ /^ remote: \ / \ // \ remote: \ |\___/| / \// .\ remote: \ /V V \__ / // | \ \ *----* remote: / / \/_/ // | \ \ \ | remote: @___@` \/_ // | \ \ \/\ \ remote: 0/0/| \/_ // | \ \ \ \ remote: 0/0/0/0/| \/// | \ \ | | remote: 0/0/0/0/0/_|_ / ( // | \ _\ | / remote: 0/0/0/0/0/0/`/,_ _ _/ ) ; -. | _ _\.-~ / / remote: ,-} _ *-.|.-~-. .~ ~ remote: * \__/ `/\ / ~-. _ .-~ / remote: \____(Oo) *. } { / remote: ( (..) .----~-.\ \-` .~ remote: //___\\ \ DENIED! ///.----..< \ _ -~ remote: // \\ ///-._ _ _ _ _ _ _{^ - - - - ~ remote: remote: remote: OVERSIZED FILE remote: This repository ("spellbook") is configured with a maximum individual file size limit, but you are pushing a change ("98d07af863e799509e7c3a639404d216f9fc79c7") which causes the size of a file ("magic_missile.txt") to exceed the limit. The commit makes the file 317 bytes long, but the limit for this repository is 1 bytes. remote: # Released cluster write lock. To ssh://local.phacility.com/source/spellbook.git ! [remote rejected] master -> master (pre-receive hook declined) error: failed to push some refs to 'ssh://epriestley@local.phacility.com/source/spellbook.git' ``` Reviewers: amckinley Reviewed By: amckinley Subscribers: joshuaspence Maniphest Tasks: T13216 Differential Revision: https://secure.phabricator.com/D19817 --- .../engine/DiffusionCommitHookEngine.php | 143 ++++++++++++++++++ .../storage/PhabricatorRepositoryPushLog.php | 4 + 2 files changed, 147 insertions(+) diff --git a/src/applications/diffusion/engine/DiffusionCommitHookEngine.php b/src/applications/diffusion/engine/DiffusionCommitHookEngine.php index 236e289ec0..80736e0e1b 100644 --- a/src/applications/diffusion/engine/DiffusionCommitHookEngine.php +++ b/src/applications/diffusion/engine/DiffusionCommitHookEngine.php @@ -164,6 +164,16 @@ final class DiffusionCommitHookEngine extends Phobject { $this->applyHeraldRefRules($ref_updates); } + try { + if (!$is_initial_import) { + $this->rejectOversizedFiles($content_updates); + } + } catch (DiffusionCommitHookRejectException $ex) { + // If we're rejecting oversized files, flag everything. + $this->rejectCode = PhabricatorRepositoryPushLog::REJECT_OVERSIZED; + throw $ex; + } + try { if (!$is_initial_import) { $this->rejectEnormousChanges($content_updates); @@ -1255,6 +1265,139 @@ final class DiffusionCommitHookEngine extends Phobject { return $changesets; } + private function rejectOversizedFiles(array $content_updates) { + $repository = $this->getRepository(); + + // TODO: Allow repositories to be configured for a maximum filesize. + $limit = 0; + + if (!$limit) { + return; + } + + foreach ($content_updates as $update) { + $identifier = $update->getRefNew(); + + $sizes = $this->loadFileSizesForCommit($identifier); + foreach ($sizes as $path => $size) { + if ($size <= $limit) { + continue; + } + + $message = pht( + 'OVERSIZED FILE'. + "\n". + 'This repository ("%s") is configured with a maximum individual '. + 'file size limit, but you are pushing a change ("%s") which causes '. + 'the size of a file ("%s") to exceed the limit. The commit makes '. + 'the file %s bytes long, but the limit for this repository is '. + '%s bytes.', + $repository->getDisplayName(), + $identifier, + $path, + new PhutilNumber($size), + new PhutilNumber($limit)); + + throw new DiffusionCommitHookRejectException($message); + } + } + } + + public function loadFileSizesForCommit($identifier) { + $repository = $this->getRepository(); + $vcs = $repository->getVersionControlSystem(); + + $path_sizes = array(); + + switch ($vcs) { + case PhabricatorRepositoryType::REPOSITORY_TYPE_GIT: + list($paths_raw) = $repository->execxLocalCommand( + 'diff-tree -z -r --no-commit-id %s --', + $identifier); + + // With "-z" we get "\0\0" for each line. Group the + // delimited text into ", " pairs. + $paths_raw = trim($paths_raw, "\0"); + $paths_raw = explode("\0", $paths_raw); + if (count($paths_raw) % 2) { + throw new Exception( + pht( + 'Unexpected number of output lines from "git diff-tree" when '. + 'processing commit ("%s"): got %s lines, expected an even '. + 'number.', + $identifier, + phutil_count($paths_raw))); + } + $paths_raw = array_chunk($paths_raw, 2); + + $paths = array(); + foreach ($paths_raw as $path_raw) { + list($fields, $pathname) = $path_raw; + $fields = explode(' ', $fields); + + // Fields are: + // + // :100644 100644 aaaa bbbb M + // + // [0] Old file mode. + // [1] New file mode. + // [2] Old object hash. + // [3] New object hash. + // [4] Change mode. + + $paths[] = array( + 'path' => $pathname, + 'newHash' => $fields[3], + ); + } + + if ($paths) { + $check_paths = array(); + foreach ($paths as $path) { + if ($path['newHash'] === self::EMPTY_HASH) { + $path_sizes[$path['path']] = 0; + continue; + } + $check_paths[$path['newHash']][] = $path['path']; + } + + if ($check_paths) { + $future = $repository->getLocalCommandFuture( + 'cat-file --batch-check=%s', + '%(objectsize)') + ->write(implode("\n", array_keys($check_paths))); + + list($sizes) = $future->resolvex(); + $sizes = trim($sizes); + $sizes = phutil_split_lines($sizes, false); + if (count($sizes) !== count($check_paths)) { + throw new Exception( + pht( + 'Unexpected number of output lines from "git cat-file" when '. + 'processing commit ("%s"): got %s lines, expected %s.', + $identifier, + phutil_count($sizes), + phutil_count($check_paths))); + } + + foreach ($check_paths as $object_hash => $path_names) { + $object_size = (int)array_shift($sizes); + foreach ($path_names as $path_name) { + $path_sizes[$path_name] = $object_size; + } + } + } + } + break; + default: + throw new Exception( + pht( + 'File size limits are not supported for this VCS.')); + } + + return $path_sizes; + } + public function loadCommitRefForCommit($identifier) { $repository = $this->getRepository(); $vcs = $repository->getVersionControlSystem(); diff --git a/src/applications/repository/storage/PhabricatorRepositoryPushLog.php b/src/applications/repository/storage/PhabricatorRepositoryPushLog.php index c2d3456da6..fa08ff0394 100644 --- a/src/applications/repository/storage/PhabricatorRepositoryPushLog.php +++ b/src/applications/repository/storage/PhabricatorRepositoryPushLog.php @@ -24,6 +24,7 @@ final class PhabricatorRepositoryPushLog const CHANGEFLAG_REWRITE = 8; const CHANGEFLAG_DANGEROUS = 16; const CHANGEFLAG_ENORMOUS = 32; + const CHANGEFLAG_OVERSIZED = 64; const REJECT_ACCEPT = 0; const REJECT_DANGEROUS = 1; @@ -31,6 +32,7 @@ final class PhabricatorRepositoryPushLog const REJECT_EXTERNAL = 3; const REJECT_BROKEN = 4; const REJECT_ENORMOUS = 5; + const REJECT_OVERSIZED = 6; protected $repositoryPHID; protected $epoch; @@ -63,6 +65,7 @@ final class PhabricatorRepositoryPushLog self::CHANGEFLAG_REWRITE => pht('Rewrite'), self::CHANGEFLAG_DANGEROUS => pht('Dangerous'), self::CHANGEFLAG_ENORMOUS => pht('Enormous'), + self::CHANGEFLAG_OVERSIZED => pht('Oversized'), ); } @@ -74,6 +77,7 @@ final class PhabricatorRepositoryPushLog self::REJECT_EXTERNAL => pht('Rejected: External Hook'), self::REJECT_BROKEN => pht('Rejected: Broken'), self::REJECT_ENORMOUS => pht('Rejected: Enormous'), + self::REJECT_OVERSIZED => pht('Rejected: Oversized File'), ); } From 43cf4edfb16b42a14a6d809f89677bd7d81a6b7c Mon Sep 17 00:00:00 2001 From: epriestley Date: Thu, 11 Oct 2018 13:34:17 -0700 Subject: [PATCH 23/43] When waiting for long-running Harbormaster futures to resolve, close idle database connections Summary: Ref T13216. See PHI916. Harbormaster builds may be long-running, particularly if they effectively wrap `ssh ... ./run-huge-build.sh`. If we spend more than a few seconds waiting for futures to resolve, close idle database connections. The general goal here is to reduce the held connection load for installs with a very large number of test runners. Test Plan: Added debugging code to `phlog()` closures, saw connections closed while running builds. Reviewers: amckinley Reviewed By: amckinley Subscribers: PHID-OPKG-gm6ozazyms6q6i22gyam Maniphest Tasks: T13216 Differential Revision: https://secure.phabricator.com/D19824 --- .../feed/query/PhabricatorFeedQuery.php | 4 +-- .../HarbormasterBuildStepImplementation.php | 28 ++++++++++++++++--- .../search/index/PhabricatorIndexEngine.php | 4 +-- .../search/ngrams/PhabricatorSearchNgrams.php | 4 +-- src/infrastructure/storage/lisk/LiskDAO.php | 17 +++++++++++ 5 files changed, 47 insertions(+), 10 deletions(-) diff --git a/src/applications/feed/query/PhabricatorFeedQuery.php b/src/applications/feed/query/PhabricatorFeedQuery.php index ae1da3aba0..18a5d748d5 100644 --- a/src/applications/feed/query/PhabricatorFeedQuery.php +++ b/src/applications/feed/query/PhabricatorFeedQuery.php @@ -78,8 +78,8 @@ final class PhabricatorFeedQuery $where[] = qsprintf( $conn, - 'ref.chronologicalKey IN (%Q)', - implode(', ', $keys)); + 'ref.chronologicalKey IN (%LQ)', + $keys); } // NOTE: We may not have 64-bit PHP, so do the shifts in MySQL instead. diff --git a/src/applications/harbormaster/step/HarbormasterBuildStepImplementation.php b/src/applications/harbormaster/step/HarbormasterBuildStepImplementation.php index e0dfe05cc0..3bbb72cd95 100644 --- a/src/applications/harbormaster/step/HarbormasterBuildStepImplementation.php +++ b/src/applications/harbormaster/step/HarbormasterBuildStepImplementation.php @@ -253,12 +253,32 @@ abstract class HarbormasterBuildStepImplementation extends Phobject { HarbormasterBuildTarget $target, array $futures) { + $did_close = false; + $wait_start = PhabricatorTime::getNow(); + $futures = new FutureIterator($futures); foreach ($futures->setUpdateInterval(5) as $key => $future) { - if ($future === null) { - $build->reload(); - if ($this->shouldAbort($build, $target)) { - throw new HarbormasterBuildAbortedException(); + if ($future !== null) { + continue; + } + + $build->reload(); + if ($this->shouldAbort($build, $target)) { + throw new HarbormasterBuildAbortedException(); + } + + // See PHI916. If we're waiting on a remote system for a while, clean + // up database connections to reduce the cost of having a large number + // of processes babysitting an `ssh ... ./run-huge-build.sh` process on + // a build host. + if (!$did_close) { + $now = PhabricatorTime::getNow(); + $elapsed = ($now - $wait_start); + $idle_limit = 5; + + if ($elapsed >= $idle_limit) { + LiskDAO::closeIdleConnections(); + $did_close = true; } } } diff --git a/src/applications/search/index/PhabricatorIndexEngine.php b/src/applications/search/index/PhabricatorIndexEngine.php index 1dde3ce9ab..1e1781f169 100644 --- a/src/applications/search/index/PhabricatorIndexEngine.php +++ b/src/applications/search/index/PhabricatorIndexEngine.php @@ -141,10 +141,10 @@ final class PhabricatorIndexEngine extends Phobject { queryfx( $conn_w, 'INSERT INTO %T (objectPHID, extensionKey, version) - VALUES %Q + VALUES %LQ ON DUPLICATE KEY UPDATE version = VALUES(version)', $table->getTableName(), - implode(', ', $sql)); + $sql); } } diff --git a/src/applications/search/ngrams/PhabricatorSearchNgrams.php b/src/applications/search/ngrams/PhabricatorSearchNgrams.php index 9ff8157e9e..fc55af5eb0 100644 --- a/src/applications/search/ngrams/PhabricatorSearchNgrams.php +++ b/src/applications/search/ngrams/PhabricatorSearchNgrams.php @@ -102,9 +102,9 @@ abstract class PhabricatorSearchNgrams if ($sql) { queryfx( $conn_w, - 'INSERT INTO %T (objectID, ngram) VALUES %Q', + 'INSERT INTO %T (objectID, ngram) VALUES %LQ', $this->getTableName(), - implode(', ', $sql)); + $sql); } return $this; diff --git a/src/infrastructure/storage/lisk/LiskDAO.php b/src/infrastructure/storage/lisk/LiskDAO.php index 1095aadf59..aa12f8e614 100644 --- a/src/infrastructure/storage/lisk/LiskDAO.php +++ b/src/infrastructure/storage/lisk/LiskDAO.php @@ -1652,6 +1652,11 @@ abstract class LiskDAO extends Phobject $now = PhabricatorTime::getNow(); foreach ($connections as $key => $connection) { + // If the connection is not idle, never consider it inactive. + if (!$connection->isIdle()) { + continue; + } + $last_active = $connection->getLastActiveEpoch(); $idle_duration = ($now - $last_active); @@ -1672,6 +1677,18 @@ abstract class LiskDAO extends Phobject } } + public static function closeIdleConnections() { + $connections = self::$connections; + + foreach ($connections as $key => $connection) { + if (!$connection->isIdle()) { + continue; + } + + self::closeConnection($key); + } + } + private static function closeConnection($key) { if (empty(self::$connections[$key])) { throw new Exception( From 45e93c8f1d1a04018a5d62b1f73d264fa200e057 Mon Sep 17 00:00:00 2001 From: epriestley Date: Wed, 21 Nov 2018 08:46:48 -0800 Subject: [PATCH 24/43] Expose "identifiers" as a query constraint for Commit search Summary: Ref T13216. See PHI984. The CommitSearchEngine (and, by extension, `diffusion.commit.search`) currently do not support identifier search, but this is a reasonable capability to provide. Test Plan: Testing that a commit exists on `master`: {F6020742} Same commit is not on `stable`: {F6020743} Reviewers: amckinley Reviewed By: amckinley Maniphest Tasks: T13216 Differential Revision: https://secure.phabricator.com/D19825 --- .../audit/query/PhabricatorCommitSearchEngine.php | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/src/applications/audit/query/PhabricatorCommitSearchEngine.php b/src/applications/audit/query/PhabricatorCommitSearchEngine.php index ee5105f193..e286573e93 100644 --- a/src/applications/audit/query/PhabricatorCommitSearchEngine.php +++ b/src/applications/audit/query/PhabricatorCommitSearchEngine.php @@ -58,6 +58,10 @@ final class PhabricatorCommitSearchEngine $query->withAncestorsOf($map['ancestorsOf']); } + if ($map['identifiers']) { + $query->withIdentifiers($map['identifiers']); + } + return $query; } @@ -130,6 +134,15 @@ final class PhabricatorCommitSearchEngine pht( 'Find commits which are ancestors of a particular ref, '. 'like "master".')), + id(new PhabricatorSearchStringListField()) + ->setLabel(pht('Identifiers')) + ->setKey('identifiers') + ->setDescription( + pht( + 'Find commits with particular identifiers (usually, hashes). '. + 'Supports full or partial identifiers (like "abcd12340987..." or '. + '"abcd1234") and qualified or unqualified identifiers (like '. + '"rXabcd1234" or "abcd1234").')), ); } From 03f249baf349cfb1223480600b7b84755b202673 Mon Sep 17 00:00:00 2001 From: epriestley Date: Wed, 21 Nov 2018 11:51:47 -0800 Subject: [PATCH 25/43] Remove rendering support for very old Repository transactions Summary: Depends on D19827. Ref T13221. Ref T13216. To prepare Repositories for a move to ModularTransactions, throw away some very old transaction rendering code. This will cause these very old transactions (none of which have been written since at least April 2016) to render "epriestley edited this repository." instead of "epriestley changed the SSH login for this repository from X to Y." These edits were generally obsoleted by repository URIs, Passphrase credentials, and general modernization. Test Plan: Grepped for all constants, got no hits. Reviewers: amckinley Reviewed By: amckinley Maniphest Tasks: T13221, T13216 Differential Revision: https://secure.phabricator.com/D19828 --- .../editor/PhabricatorRepositoryEditor.php | 7 -- .../PhabricatorRepositoryTransaction.php | 110 ------------------ 2 files changed, 117 deletions(-) diff --git a/src/applications/repository/editor/PhabricatorRepositoryEditor.php b/src/applications/repository/editor/PhabricatorRepositoryEditor.php index 882c1a11fa..5cfa1da290 100644 --- a/src/applications/repository/editor/PhabricatorRepositoryEditor.php +++ b/src/applications/repository/editor/PhabricatorRepositoryEditor.php @@ -22,7 +22,6 @@ final class PhabricatorRepositoryEditor $types[] = PhabricatorRepositoryTransaction::TYPE_DEFAULT_BRANCH; $types[] = PhabricatorRepositoryTransaction::TYPE_TRACK_ONLY; $types[] = PhabricatorRepositoryTransaction::TYPE_AUTOCLOSE_ONLY; - $types[] = PhabricatorRepositoryTransaction::TYPE_UUID; $types[] = PhabricatorRepositoryTransaction::TYPE_SVN_SUBPATH; $types[] = PhabricatorRepositoryTransaction::TYPE_NOTIFY; $types[] = PhabricatorRepositoryTransaction::TYPE_AUTOCLOSE; @@ -65,8 +64,6 @@ final class PhabricatorRepositoryEditor return array_keys($object->getDetail('branch-filter', array())); case PhabricatorRepositoryTransaction::TYPE_AUTOCLOSE_ONLY: return array_keys($object->getDetail('close-commits-filter', array())); - case PhabricatorRepositoryTransaction::TYPE_UUID: - return $object->getUUID(); case PhabricatorRepositoryTransaction::TYPE_SVN_SUBPATH: return $object->getDetail('svn-subpath'); case PhabricatorRepositoryTransaction::TYPE_NOTIFY: @@ -108,7 +105,6 @@ final class PhabricatorRepositoryEditor case PhabricatorRepositoryTransaction::TYPE_DEFAULT_BRANCH: case PhabricatorRepositoryTransaction::TYPE_TRACK_ONLY: case PhabricatorRepositoryTransaction::TYPE_AUTOCLOSE_ONLY: - case PhabricatorRepositoryTransaction::TYPE_UUID: case PhabricatorRepositoryTransaction::TYPE_SVN_SUBPATH: case PhabricatorRepositoryTransaction::TYPE_VCS: case PhabricatorRepositoryTransaction::TYPE_PUSH_POLICY: @@ -171,9 +167,6 @@ final class PhabricatorRepositoryEditor 'close-commits-filter', array_fill_keys($xaction->getNewValue(), true)); break; - case PhabricatorRepositoryTransaction::TYPE_UUID: - $object->setUUID($xaction->getNewValue()); - break; case PhabricatorRepositoryTransaction::TYPE_SVN_SUBPATH: $object->setDetail('svn-subpath', $xaction->getNewValue()); break; diff --git a/src/applications/repository/storage/PhabricatorRepositoryTransaction.php b/src/applications/repository/storage/PhabricatorRepositoryTransaction.php index 866800161e..57f554349c 100644 --- a/src/applications/repository/storage/PhabricatorRepositoryTransaction.php +++ b/src/applications/repository/storage/PhabricatorRepositoryTransaction.php @@ -25,20 +25,6 @@ final class PhabricatorRepositoryTransaction const TYPE_AUTOMATION_BLUEPRINTS = 'repo:automation-blueprints'; const TYPE_CALLSIGN = 'repo:callsign'; - // TODO: Clean up these legacy transaction types. - const TYPE_SSH_LOGIN = 'repo:ssh-login'; - const TYPE_SSH_KEY = 'repo:ssh-key'; - const TYPE_SSH_KEYFILE = 'repo:ssh-keyfile'; - const TYPE_HTTP_LOGIN = 'repo:http-login'; - const TYPE_HTTP_PASS = 'repo:http-pass'; - const TYPE_CREDENTIAL = 'repo:credential'; - const TYPE_PROTOCOL_HTTP = 'repo:serve-http'; - const TYPE_PROTOCOL_SSH = 'repo:serve-ssh'; - const TYPE_HOSTING = 'repo:hosting'; - const TYPE_LOCAL_PATH = 'repo:local-path'; - const TYPE_REMOTE_URI = 'repo:remote-uri'; - const TYPE_UUID = 'repo:uuid'; - public function getApplicationName() { return 'repository'; } @@ -86,15 +72,6 @@ final class PhabricatorRepositoryTransaction $new = $this->getNewValue(); switch ($this->getTransactionType()) { - case self::TYPE_REMOTE_URI: - case self::TYPE_SSH_LOGIN: - case self::TYPE_SSH_KEY: - case self::TYPE_SSH_KEYFILE: - case self::TYPE_HTTP_LOGIN: - case self::TYPE_HTTP_PASS: - // Hide null vs empty string changes. - return (!strlen($old) && !strlen($new)); - case self::TYPE_LOCAL_PATH: case self::TYPE_NAME: // Hide these on create, they aren't interesting and we have an // explicit "create" transaction. @@ -233,25 +210,6 @@ final class PhabricatorRepositoryTransaction implode(', ', $new)); } break; - case self::TYPE_UUID: - if (!strlen($new)) { - return pht( - '%s removed "%s" as the repository UUID.', - $this->renderHandleLink($author_phid), - $old); - } else if (!strlen($old)) { - return pht( - '%s set the repository UUID to "%s".', - $this->renderHandleLink($author_phid), - $new); - } else { - return pht( - '%s changed the repository UUID from "%s" to "%s".', - $this->renderHandleLink($author_phid), - $old, - $new); - } - break; case self::TYPE_SVN_SUBPATH: if (!strlen($new)) { return pht( @@ -293,74 +251,6 @@ final class PhabricatorRepositoryTransaction $this->renderHandleLink($author_phid)); } break; - case self::TYPE_REMOTE_URI: - if (!strlen($old)) { - return pht( - '%s set the remote URI for this repository to "%s".', - $this->renderHandleLink($author_phid), - $new); - } else if (!strlen($new)) { - return pht( - '%s removed the remote URI for this repository.', - $this->renderHandleLink($author_phid)); - } else { - return pht( - '%s changed the remote URI for this repository from "%s" to "%s".', - $this->renderHandleLink($author_phid), - $old, - $new); - } - break; - case self::TYPE_SSH_LOGIN: - return pht( - '%s updated the SSH login for this repository.', - $this->renderHandleLink($author_phid)); - case self::TYPE_SSH_KEY: - return pht( - '%s updated the SSH key for this repository.', - $this->renderHandleLink($author_phid)); - case self::TYPE_SSH_KEYFILE: - return pht( - '%s updated the SSH keyfile for this repository.', - $this->renderHandleLink($author_phid)); - case self::TYPE_HTTP_LOGIN: - return pht( - '%s updated the HTTP login for this repository.', - $this->renderHandleLink($author_phid)); - case self::TYPE_HTTP_PASS: - return pht( - '%s updated the HTTP password for this repository.', - $this->renderHandleLink($author_phid)); - case self::TYPE_LOCAL_PATH: - return pht( - '%s changed the local path from "%s" to "%s".', - $this->renderHandleLink($author_phid), - $old, - $new); - case self::TYPE_HOSTING: - if ($new) { - return pht( - '%s changed this repository to be hosted on Phabricator.', - $this->renderHandleLink($author_phid)); - } else { - return pht( - '%s changed this repository to track a remote elsewhere.', - $this->renderHandleLink($author_phid)); - } - case self::TYPE_PROTOCOL_HTTP: - return pht( - '%s changed the availability of this repository over HTTP from '. - '"%s" to "%s".', - $this->renderHandleLink($author_phid), - $old, - $new); - case self::TYPE_PROTOCOL_SSH: - return pht( - '%s changed the availability of this repository over SSH from '. - '"%s" to "%s".', - $this->renderHandleLink($author_phid), - $old, - $new); case self::TYPE_PUSH_POLICY: return pht( '%s changed the push policy of this repository from "%s" to "%s".', From 433a7321ff2585aec3ebbb23f59c0392f7e34e7e Mon Sep 17 00:00:00 2001 From: John Linahan Date: Mon, 26 Nov 2018 14:16:57 +0000 Subject: [PATCH 26/43] Add harbormaster.buildable.search API Method Summary: This revision adds a Conduit search method for buildables. It exposes: * `objectPHID` * `containerPHID` * `buildableStatus` * `isManual` Test Plan: Use the API Console to run searches. Example: ``` { "data": [ { "id": 2, "type": "HMBB", "phid": "PHID-HMBB-m4k5lodx6naq22576a7d", "fields": { "objectPHID": "PHID-DIFF-vzvgqqcyscpd7ta4osy2", "containerPHID": "PHID-DREV-vsivs5276c7vtgpmssn2", "buildableStatus": { "value": "passed" }, "isManual": true, "dateCreated": 1542407155, "dateModified": 1542407156, "policy": { "view": "users", "edit": "users" } }, "attachments": {} }, { "id": 1, "type": "HMBB", "phid": "PHID-HMBB-opxfl4auoz3ey5klplrx", "fields": { "objectPHID": "PHID-DIFF-vzvgqqcyscpd7ta4osy2", "containerPHID": null, "buildableStatus": { "value": "passed" }, "isManual": false, "dateCreated": 1542406968, "dateModified": 1542406968, "policy": { "view": "users", "edit": "users" } }, "attachments": {} } ], "maps": {}, "query": { "queryKey": null }, "cursor": { "limit": 100, "after": null, "before": null, "order": null } } ``` Reviewers: #blessed_reviewers, epriestley Reviewed By: #blessed_reviewers, epriestley Subscribers: Korvin, O14 ATC Monitoring Differential Revision: https://secure.phabricator.com/D19818 --- src/__phutil_library_map__.php | 3 ++ .../HarbormasterBuildableSearchAPIMethod.php | 18 ++++++++ .../storage/HarbormasterBuildable.php | 41 +++++++++++++++++++ 3 files changed, 62 insertions(+) create mode 100644 src/applications/harbormaster/conduit/HarbormasterBuildableSearchAPIMethod.php diff --git a/src/__phutil_library_map__.php b/src/__phutil_library_map__.php index 578a90d09c..e02178bc28 100644 --- a/src/__phutil_library_map__.php +++ b/src/__phutil_library_map__.php @@ -1367,6 +1367,7 @@ phutil_register_library_map(array( 'HarbormasterBuildableListController' => 'applications/harbormaster/controller/HarbormasterBuildableListController.php', 'HarbormasterBuildablePHIDType' => 'applications/harbormaster/phid/HarbormasterBuildablePHIDType.php', 'HarbormasterBuildableQuery' => 'applications/harbormaster/query/HarbormasterBuildableQuery.php', + 'HarbormasterBuildableSearchAPIMethod' => 'applications/harbormaster/conduit/HarbormasterBuildableSearchAPIMethod.php', 'HarbormasterBuildableSearchEngine' => 'applications/harbormaster/query/HarbormasterBuildableSearchEngine.php', 'HarbormasterBuildableStatus' => 'applications/harbormaster/constants/HarbormasterBuildableStatus.php', 'HarbormasterBuildableTransaction' => 'applications/harbormaster/storage/HarbormasterBuildableTransaction.php', @@ -6845,6 +6846,7 @@ phutil_register_library_map(array( 'PhabricatorApplicationTransactionInterface', 'PhabricatorPolicyInterface', 'HarbormasterBuildableInterface', + 'PhabricatorConduitResultInterface', 'PhabricatorDestructibleInterface', ), 'HarbormasterBuildableActionController' => 'HarbormasterController', @@ -6852,6 +6854,7 @@ phutil_register_library_map(array( 'HarbormasterBuildableListController' => 'HarbormasterController', 'HarbormasterBuildablePHIDType' => 'PhabricatorPHIDType', 'HarbormasterBuildableQuery' => 'PhabricatorCursorPagedPolicyAwareQuery', + 'HarbormasterBuildableSearchAPIMethod' => 'PhabricatorSearchEngineAPIMethod', 'HarbormasterBuildableSearchEngine' => 'PhabricatorApplicationSearchEngine', 'HarbormasterBuildableStatus' => 'Phobject', 'HarbormasterBuildableTransaction' => 'PhabricatorApplicationTransaction', diff --git a/src/applications/harbormaster/conduit/HarbormasterBuildableSearchAPIMethod.php b/src/applications/harbormaster/conduit/HarbormasterBuildableSearchAPIMethod.php new file mode 100644 index 0000000000..28471976e2 --- /dev/null +++ b/src/applications/harbormaster/conduit/HarbormasterBuildableSearchAPIMethod.php @@ -0,0 +1,18 @@ +setKey('objectPHID') + ->setType('phid') + ->setDescription(pht('PHID of the object that is built.')), + id(new PhabricatorConduitSearchFieldSpecification()) + ->setKey('containerPHID') + ->setType('phid') + ->setDescription(pht('PHID of the object containing this buildable.')), + id(new PhabricatorConduitSearchFieldSpecification()) + ->setKey('buildableStatus') + ->setType('map') + ->setDescription(pht('The current status of this buildable.')), + id(new PhabricatorConduitSearchFieldSpecification()) + ->setKey('isManual') + ->setType('bool') + ->setDescription(pht('True if this is a manual buildable.')), + ); + } + + public function getFieldValuesForConduit() { + return array( + 'objectPHID' => $this->getBuildablePHID(), + 'containerPHID' => $this->getContainerPHID(), + 'buildableStatus' => array( + 'value' => $this->getBuildableStatus(), + ), + 'isManual' => (bool)$this->getIsManualBuildable(), + ); + } + + public function getConduitSearchAttachments() { + return array(); + } + + /* -( PhabricatorDestructibleInterface )----------------------------------- */ From 97e7ef0f015c082d40c654154cd453abb81db760 Mon Sep 17 00:00:00 2001 From: epriestley Date: Mon, 26 Nov 2018 06:00:59 -0800 Subject: [PATCH 27/43] When the last rejecting reviewer resigns from a revision, return it to "Needs Review" Summary: Ref T13216. Fixes T12920. See PHI911. If you reject a revision and then resign from it, it stays in "Needs Revision". There's some arguable motivation for this, but it's inconsistent with how "Accept" works (if the last accepting reviewer resigns, we kick you out of "Accepted"). Make it consistent. Test Plan: - As the only reviewer: requested changes to a revision, then resigned. - Before: revision stays in "Needs Revision". - After: revision moves back to "Needs Review". Reviewers: amckinley Reviewed By: amckinley Maniphest Tasks: T13216, T12920 Differential Revision: https://secure.phabricator.com/D19840 --- .../differential/editor/DifferentialTransactionEditor.php | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/applications/differential/editor/DifferentialTransactionEditor.php b/src/applications/differential/editor/DifferentialTransactionEditor.php index 5bc33dabe1..7fa4cd61e2 100644 --- a/src/applications/differential/editor/DifferentialTransactionEditor.php +++ b/src/applications/differential/editor/DifferentialTransactionEditor.php @@ -451,6 +451,11 @@ final class DifferentialTransactionEditor // conditions for acceptance. This usually happens after an accepting // reviewer resigns or is removed. $new_status = DifferentialRevisionStatus::NEEDS_REVIEW; + } else if ($was_revision) { + // This revision was "Needs Revision", but no longer has any rejecting + // reviewers. This usually happens after the last rejecting reviewer + // resigns or is removed. Put the revision back in "Needs Review". + $new_status = DifferentialRevisionStatus::NEEDS_REVIEW; } if ($new_status === null) { From bcc90d8c6b8c0c556d9105e669cdba9db4d44370 Mon Sep 17 00:00:00 2001 From: epriestley Date: Sun, 25 Nov 2018 15:39:15 -0800 Subject: [PATCH 28/43] Fix an off-by-one error affecting mail rendering of inlines on the final line of a file Summary: Depends on D19837. Ref T13216. See PHI985. There's an off-by-one error here between how inline comments store "length" and how context rendering treats "length". We need to add 1 to the length, but currently do it a little too early. Do it slightly later so that inlines on the final line of a file render properly. Test Plan: Left an inline on the final line of a new file, saw it render properly in HTML mail. Reviewers: amckinley Reviewed By: amckinley Maniphest Tasks: T13216 Differential Revision: https://secure.phabricator.com/D19838 --- .../mail/DifferentialInlineCommentMailView.php | 2 +- .../differential/parser/DifferentialChangesetParser.php | 9 ++++++++- src/applications/feed/PhabricatorFeedStoryPublisher.php | 8 ++++---- 3 files changed, 13 insertions(+), 6 deletions(-) diff --git a/src/applications/differential/mail/DifferentialInlineCommentMailView.php b/src/applications/differential/mail/DifferentialInlineCommentMailView.php index 0bc914914b..c1430320e3 100644 --- a/src/applications/differential/mail/DifferentialInlineCommentMailView.php +++ b/src/applications/differential/mail/DifferentialInlineCommentMailView.php @@ -361,7 +361,7 @@ final class DifferentialInlineCommentMailView return $parser->render( $start - $context, - $length + 1 + (2 * $context), + $length + (2 * $context), array()); } diff --git a/src/applications/differential/parser/DifferentialChangesetParser.php b/src/applications/differential/parser/DifferentialChangesetParser.php index 596ea0e426..e214aa16a4 100644 --- a/src/applications/differential/parser/DifferentialChangesetParser.php +++ b/src/applications/differential/parser/DifferentialChangesetParser.php @@ -874,9 +874,16 @@ final class DifferentialChangesetParser extends Phobject { $offset_map = $this->old; } + // NOTE: Inline comments use zero-based lengths. For example, a comment + // that starts and ends on line 123 has length 0. Rendering considers + // this range to have length 1. Probably both should agree, but that + // ship likely sailed long ago. Tweak things here to get the two systems + // to agree. See PHI985, where this affected mail rendering of inline + // comments left on the final line of a file. + $range_end = $this->getOffset($offset_map, $range_start + $range_len); $range_start = $this->getOffset($offset_map, $range_start); - $range_len = ($range_end - $range_start); + $range_len = ($range_end - $range_start) + 1; } $render_pch = $this->shouldRenderPropertyChangeHeader($this->changeset); diff --git a/src/applications/feed/PhabricatorFeedStoryPublisher.php b/src/applications/feed/PhabricatorFeedStoryPublisher.php index 47dde8c98a..70d9a2f61c 100644 --- a/src/applications/feed/PhabricatorFeedStoryPublisher.php +++ b/src/applications/feed/PhabricatorFeedStoryPublisher.php @@ -133,9 +133,9 @@ final class PhabricatorFeedStoryPublisher extends Phobject { queryfx( $conn, - 'INSERT INTO %T (objectPHID, chronologicalKey) VALUES %Q', + 'INSERT INTO %T (objectPHID, chronologicalKey) VALUES %LQ', $ref->getTableName(), - implode(', ', $sql)); + $sql); } $subscribed_phids = $this->subscribedPHIDs; @@ -191,9 +191,9 @@ final class PhabricatorFeedStoryPublisher extends Phobject { $conn, 'INSERT INTO %T '. '(primaryObjectPHID, userPHID, chronologicalKey, hasViewed) '. - 'VALUES %Q', + 'VALUES %LQ', $notif->getTableName(), - implode(', ', $sql)); + $sql); } PhabricatorUserCache::clearCaches( From 9473f60a362f7d38fb0b26b0bdbf31a000148341 Mon Sep 17 00:00:00 2001 From: epriestley Date: Sat, 24 Nov 2018 06:50:53 -0800 Subject: [PATCH 29/43] Allow "Abandoned" revisions to be commandeered Summary: Ref T13216. See PHI985. You currently can't commandeer an abandoned revision, but this workflow is perfectly fine. The caution here is just around weird use cases where, e.g., users want to reopen a revision to add a revert to it. These workflows tend to create problems so we try to guide users away from them. Test Plan: {F6026841} Reviewers: amckinley Reviewed By: amckinley Maniphest Tasks: T13216 Differential Revision: https://secure.phabricator.com/D19835 --- .../DifferentialRevisionCommandeerTransaction.php | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/src/applications/differential/xaction/DifferentialRevisionCommandeerTransaction.php b/src/applications/differential/xaction/DifferentialRevisionCommandeerTransaction.php index 5aafa536f3..a296597bc7 100644 --- a/src/applications/differential/xaction/DifferentialRevisionCommandeerTransaction.php +++ b/src/applications/differential/xaction/DifferentialRevisionCommandeerTransaction.php @@ -59,11 +59,21 @@ final class DifferentialRevisionCommandeerTransaction } protected function validateAction($object, PhabricatorUser $viewer) { - if ($object->isClosed()) { + // If a revision has already landed, we generally want to discourage + // reopening and reusing it since this tends to create a big mess (users + // should create a new revision instead). Thus, we stop you from + // commandeering closed revisions. + + // See PHI985. If the revision was abandoned, there's no peril in allowing + // the commandeer since the change (likely) never actually landed. So + // it's okay to commandeer abandoned revisions. + + if ($object->isClosed() && !$object->isAbandoned()) { throw new Exception( pht( 'You can not commandeer this revision because it has already '. - 'been closed. You can only commandeer open revisions.')); + 'been closed. You can only commandeer open or abandoned '. + 'revisions.')); } if ($this->isViewerRevisionAuthor($object, $viewer)) { From 5343b1f898d4e310e49221f5351ce86fc4686b72 Mon Sep 17 00:00:00 2001 From: epriestley Date: Sat, 24 Nov 2018 06:30:02 -0800 Subject: [PATCH 30/43] Remove defunct "metamta.herald.show-hints" Config option Summary: Ref T13216. See PHI985. This config option once controlled adding a Herald transcript link to email. However, this was never implemented in a generic way and was removed from revisions in D8459 and from commits in D10705. No one has noticed or asked for this option for several years, so this is probably a good opportunity to simplify the software and reduce the total amount of configuration. If we did want to pursue this in the future, I'd generally prefer to make it part of the mail detail page (`/mail/detail/12345/`) anyway. Test Plan: Grepped for `metamta.herald.show-hints` and `addHeraldSection()`, got no hits for either. Reviewers: amckinley Reviewed By: amckinley Maniphest Tasks: T13216 Differential Revision: https://secure.phabricator.com/D19833 --- .../PhabricatorMetaMTAConfigOptions.php | 16 ------------ .../view/PhabricatorMetaMTAMailBody.php | 18 ------------- .../PhabricatorMetaMTAMailBodyTestCase.php | 25 ++----------------- 3 files changed, 2 insertions(+), 57 deletions(-) diff --git a/src/applications/config/option/PhabricatorMetaMTAConfigOptions.php b/src/applications/config/option/PhabricatorMetaMTAConfigOptions.php index 8a236a883b..ad8a62d184 100644 --- a/src/applications/config/option/PhabricatorMetaMTAConfigOptions.php +++ b/src/applications/config/option/PhabricatorMetaMTAConfigOptions.php @@ -81,14 +81,6 @@ of each approach are: - Required if private reply-to addresses are configured. - Mail messages are sent in the language of user preference. -EODOC -)); - - $herald_hints_description = $this->deformat(pht(<<setLocked(true) ->setDescription(pht('Domain used for reply email addresses.')) ->addExample('phabricator.example.com', ''), - $this->newOption('metamta.herald.show-hints', 'bool', true) - ->setBoolOptions( - array( - pht('Show Herald Hints'), - pht('No Herald Hints'), - )) - ->setSummary(pht('Show hints about Herald rules in email.')) - ->setDescription($herald_hints_description), $this->newOption('metamta.recipients.show-hints', 'bool', true) ->setBoolOptions( array( diff --git a/src/applications/metamta/view/PhabricatorMetaMTAMailBody.php b/src/applications/metamta/view/PhabricatorMetaMTAMailBody.php index 6aaf4fb052..778e05b052 100644 --- a/src/applications/metamta/view/PhabricatorMetaMTAMailBody.php +++ b/src/applications/metamta/view/PhabricatorMetaMTAMailBody.php @@ -151,24 +151,6 @@ final class PhabricatorMetaMTAMailBody extends Phobject { return $this; } - /** - * Add a Herald section with a rule management URI and a transcript URI. - * - * @param string URI to rule transcripts. - * @return this - * @task compose - */ - public function addHeraldSection($xscript_uri) { - if (!PhabricatorEnv::getEnvConfig('metamta.herald.show-hints')) { - return $this; - } - - $this->addLinkSection( - pht('WHY DID I GET THIS EMAIL?'), - PhabricatorEnv::getProductionURI($xscript_uri)); - - return $this; - } /** * Add an attachment. diff --git a/src/applications/metamta/view/__tests__/PhabricatorMetaMTAMailBodyTestCase.php b/src/applications/metamta/view/__tests__/PhabricatorMetaMTAMailBodyTestCase.php index e8415c7675..5753802996 100644 --- a/src/applications/metamta/view/__tests__/PhabricatorMetaMTAMailBodyTestCase.php +++ b/src/applications/metamta/view/__tests__/PhabricatorMetaMTAMailBodyTestCase.php @@ -10,36 +10,15 @@ HEADER bass trout -WHY DID I GET THIS EMAIL? - http://test.com/xscript/ - EOTEXT; - $this->assertEmail($expect, true); + $this->assertEmail($expect); } - public function testBodyRenderNoHerald() { - $expect = <<assertEmail($expect, false); - } - - private function assertEmail($expect, $herald_hints) { - $env = PhabricatorEnv::beginScopedEnv(); - $env->overrideEnvConfig('phabricator.production-uri', 'http://test.com/'); - $env->overrideEnvConfig('metamta.herald.show-hints', $herald_hints); - + private function assertEmail($expect) { $body = new PhabricatorMetaMTAMailBody(); $body->addRawSection('salmon'); $body->addTextSection('HEADER', "bass\ntrout\n"); - $body->addHeraldSection('/xscript/'); $this->assertEqual($expect, $body->render()); } From 8b550ce2cd8fa29c854a46d8b46b6b516a0f70a0 Mon Sep 17 00:00:00 2001 From: epriestley Date: Sat, 24 Nov 2018 06:59:19 -0800 Subject: [PATCH 31/43] Don't allow the middle mouse button to start an inline comment Summary: Ref T13216. See PHI985. When you click a line number to start an inline comment, we intend to initiate the action only if you used the left mouse button (desktop) or a touch (tablet/device). We currently have a `not right` condition for doing this, but it only excludes right clicks, not middle clicks (or other nth-button clicks). The `not right` condition was sligthly easier to write, but use an `is left` condition instead of a `not right` condition. Test Plan: - In Safari, Firefox and Chrome: - Used left click to start an inline. - Used middle click to do nothing (previously: started an inline). Reviewers: amckinley Reviewed By: amckinley Maniphest Tasks: T13216 Differential Revision: https://secure.phabricator.com/D19836 --- resources/celerity/map.php | 26 +++++++++---------- webroot/rsrc/externals/javelin/core/Event.js | 14 ++++++++++ .../js/application/diff/DiffChangesetList.js | 5 +++- 3 files changed, 31 insertions(+), 14 deletions(-) diff --git a/resources/celerity/map.php b/resources/celerity/map.php index 7d6e766c44..9e3e7c6061 100644 --- a/resources/celerity/map.php +++ b/resources/celerity/map.php @@ -10,9 +10,9 @@ return array( 'conpherence.pkg.css' => 'e68cf1fa', 'conpherence.pkg.js' => '15191c65', 'core.pkg.css' => 'cff4ff6f', - 'core.pkg.js' => 'b5a949ca', + 'core.pkg.js' => '4bde473b', 'differential.pkg.css' => '06dc617c', - 'differential.pkg.js' => 'c1cfa143', + 'differential.pkg.js' => 'ef0b989b', 'diffusion.pkg.css' => 'a2d17c7d', 'diffusion.pkg.js' => '6134c5a1', 'maniphest.pkg.css' => '4845691a', @@ -207,7 +207,7 @@ return array( 'rsrc/externals/font/lato/lato-regular.ttf' => 'e270165b', 'rsrc/externals/font/lato/lato-regular.woff' => '13d39fe2', 'rsrc/externals/font/lato/lato-regular.woff2' => '57a9f742', - 'rsrc/externals/javelin/core/Event.js' => '2ee659ce', + 'rsrc/externals/javelin/core/Event.js' => 'ef7e057f', 'rsrc/externals/javelin/core/Stratcom.js' => '327f418a', 'rsrc/externals/javelin/core/__tests__/event-stop-and-kill.js' => '717554e4', 'rsrc/externals/javelin/core/__tests__/install.js' => 'c432ee85', @@ -373,7 +373,7 @@ return array( 'rsrc/js/application/dashboard/behavior-dashboard-query-panel-select.js' => '453c5375', 'rsrc/js/application/dashboard/behavior-dashboard-tab-panel.js' => 'd4eecc63', 'rsrc/js/application/diff/DiffChangeset.js' => 'b49b59d6', - 'rsrc/js/application/diff/DiffChangesetList.js' => 'e0b984b5', + 'rsrc/js/application/diff/DiffChangesetList.js' => '0a84bcc1', 'rsrc/js/application/diff/DiffInline.js' => 'e83d28f3', 'rsrc/js/application/diff/behavior-preview-link.js' => '051c7832', 'rsrc/js/application/differential/behavior-comment-preview.js' => '51c5ad07', @@ -688,7 +688,7 @@ return array( 'javelin-diffusion-locate-file-source' => '00676f00', 'javelin-dom' => '4976858c', 'javelin-dynval' => 'f6555212', - 'javelin-event' => '2ee659ce', + 'javelin-event' => 'ef7e057f', 'javelin-fx' => '54b612ba', 'javelin-history' => 'd4505101', 'javelin-install' => '05270951', @@ -750,7 +750,7 @@ return array( 'phabricator-darkmessage' => 'c48cccdd', 'phabricator-dashboard-css' => 'fe5b1869', 'phabricator-diff-changeset' => 'b49b59d6', - 'phabricator-diff-changeset-list' => 'e0b984b5', + 'phabricator-diff-changeset-list' => '0a84bcc1', 'phabricator-diff-inline' => 'e83d28f3', 'phabricator-drag-and-drop-file-upload' => '58dea2fa', 'phabricator-draggable-list' => 'bea6e7f4', @@ -944,6 +944,10 @@ return array( 'javelin-dom', 'javelin-router', ), + '0a84bcc1' => array( + 'javelin-install', + 'phuix-button-view', + ), '0f764c35' => array( 'javelin-install', 'javelin-util', @@ -1049,9 +1053,6 @@ return array( 'javelin-install', 'javelin-event', ), - '2ee659ce' => array( - 'javelin-install', - ), '31420f77' => array( 'javelin-behavior', ), @@ -2009,10 +2010,6 @@ return array( 'phuix-icon-view', 'phabricator-prefab', ), - 'e0b984b5' => array( - 'javelin-install', - 'phuix-button-view', - ), 'e1d25dfb' => array( 'javelin-behavior', 'javelin-stratcom', @@ -2094,6 +2091,9 @@ return array( 'javelin-behavior', 'javelin-uri', ), + 'ef7e057f' => array( + 'javelin-install', + ), 'efe49472' => array( 'javelin-install', 'javelin-util', diff --git a/webroot/rsrc/externals/javelin/core/Event.js b/webroot/rsrc/externals/javelin/core/Event.js index d4e04be7e2..787a453c4d 100644 --- a/webroot/rsrc/externals/javelin/core/Event.js +++ b/webroot/rsrc/externals/javelin/core/Event.js @@ -133,6 +133,20 @@ JX.install('Event', { return r.which == 3 || r.button == 2; }, + + /** + * Get whether the mouse button associated with the mouse event is the + * left-side button in a browser-agnostic way. + * + * @return bool + * @task info + */ + isLeftButton: function() { + var r = this.getRawEvent(); + return (r.which == 1 || r.button == 0); + }, + + /** * Determine if a mouse event is a normal event (left mouse button, no * modifier keys). diff --git a/webroot/rsrc/js/application/diff/DiffChangesetList.js b/webroot/rsrc/js/application/diff/DiffChangesetList.js index a95267c0a3..5ba43a7e6d 100644 --- a/webroot/rsrc/js/application/diff/DiffChangesetList.js +++ b/webroot/rsrc/js/application/diff/DiffChangesetList.js @@ -1261,7 +1261,10 @@ JX.install('DiffChangesetList', { _onrangedown: function(e) { // NOTE: We're allowing "mousedown" from a touch event through so users // can leave inlines on a single line. - if (e.isRightButton()) { + + // See PHI985. We want to exclude both right-mouse and middle-mouse + // clicks from continuing. + if (!e.isLeftButton()) { return; } From 88189f723f05897ebae02c092ce0b0d9079cf2fa Mon Sep 17 00:00:00 2001 From: epriestley Date: Sun, 25 Nov 2018 13:35:16 -0800 Subject: [PATCH 32/43] Make a Feed query construction less clever/sneaky for new qsprintf() semantics Summary: Ref T13216. Ref T13217. Currently, we build this query in a weird way so we end up with `(1, 2, 3)` on both 32-bit and 64-bit systems. I can't reproduce the string-vs-int MySQL key issue on any system I have access to, so just simplify this and format as `('1', '2', '3')` instead. The issue this is working around is that MySQL would (I think?) sometimes appear to do something goofy and miss the key if you formatted the query with strings. I never really nailed this down and could have either been mistaken about it or it could be fixed in all modern versions of MySQL. Until we have better evidence to the contrary, assume MySQL is smart enough to handle this sensibly now. Test Plan: Ran daemons with Feed publish workers, no longer received query warnings. Reviewers: amckinley Reviewed By: amckinley Maniphest Tasks: T13217, T13216 Differential Revision: https://secure.phabricator.com/D19837 --- .../feed/query/PhabricatorFeedQuery.php | 22 +++++++++---------- 1 file changed, 10 insertions(+), 12 deletions(-) diff --git a/src/applications/feed/query/PhabricatorFeedQuery.php b/src/applications/feed/query/PhabricatorFeedQuery.php index 18a5d748d5..ce472a6026 100644 --- a/src/applications/feed/query/PhabricatorFeedQuery.php +++ b/src/applications/feed/query/PhabricatorFeedQuery.php @@ -64,22 +64,20 @@ final class PhabricatorFeedQuery } if ($this->chronologicalKeys !== null) { - // NOTE: We want to use integers in the query so we can take advantage - // of keys, but can't use %d on 32-bit systems. Make sure all the keys - // are integers and then format them raw. + // NOTE: We can't use "%d" to format these large integers on 32-bit + // systems. Historically, we formatted these into integers in an + // awkward way because MySQL could sometimes (?) fail to use the proper + // keys if the values were formatted as strings instead of integers. - $keys = $this->chronologicalKeys; - foreach ($keys as $key) { - if (!ctype_digit($key)) { - throw new Exception( - pht("Key '%s' is not a valid chronological key!", $key)); - } - } + // After the "qsprintf()" update to use PhutilQueryString, we can no + // longer do this in a sneaky way. However, the MySQL key issue also + // no longer appears to reproduce across several systems. So: just use + // strings until problems turn up? $where[] = qsprintf( $conn, - 'ref.chronologicalKey IN (%LQ)', - $keys); + 'ref.chronologicalKey IN (%Ls)', + $this->chronologicalKeys); } // NOTE: We may not have 64-bit PHP, so do the shifts in MySQL instead. From 2e8a5e843f95700c4c3953750d95a3b0533c2464 Mon Sep 17 00:00:00 2001 From: epriestley Date: Sat, 24 Nov 2018 06:20:04 -0800 Subject: [PATCH 33/43] Recover when cookies are disabled in Firefox and accessing localStorage throws Summary: Ref T13216. See PHI985. If you disable cookies in Firefox, accessing `window.localStorage` throws an exception. Currently, this pretty much kills all scripts on the page. Instead, catch and ignore this, as though `window.localStorage` was not defined. Test Plan: - Set Firefox to "no cookies". - Loaded any page while logged out. - Before: JS fatal early in the stack. - After: page loads and JS works. Reviewers: amckinley Reviewed By: amckinley Maniphest Tasks: T13216 Differential Revision: https://secure.phabricator.com/D19832 --- resources/celerity/map.php | 4 ++-- webroot/rsrc/externals/javelin/core/init.js | 9 +++++++-- 2 files changed, 9 insertions(+), 4 deletions(-) diff --git a/resources/celerity/map.php b/resources/celerity/map.php index 9e3e7c6061..e5eb50bb58 100644 --- a/resources/celerity/map.php +++ b/resources/celerity/map.php @@ -213,7 +213,7 @@ return array( 'rsrc/externals/javelin/core/__tests__/install.js' => 'c432ee85', 'rsrc/externals/javelin/core/__tests__/stratcom.js' => '88bf7313', 'rsrc/externals/javelin/core/__tests__/util.js' => 'e251703d', - 'rsrc/externals/javelin/core/init.js' => '638a4e2b', + 'rsrc/externals/javelin/core/init.js' => '8d83d2a1', 'rsrc/externals/javelin/core/init_node.js' => 'c234aded', 'rsrc/externals/javelin/core/install.js' => '05270951', 'rsrc/externals/javelin/core/util.js' => '93cc50d6', @@ -694,7 +694,7 @@ return array( 'javelin-install' => '05270951', 'javelin-json' => '69adf288', 'javelin-leader' => '7f243deb', - 'javelin-magical-init' => '638a4e2b', + 'javelin-magical-init' => '8d83d2a1', 'javelin-mask' => '8a41885b', 'javelin-quicksand' => '6b8ef10b', 'javelin-reactor' => '2b8de964', diff --git a/webroot/rsrc/externals/javelin/core/init.js b/webroot/rsrc/externals/javelin/core/init.js index fb10b0f4c3..61c9f13d6d 100644 --- a/webroot/rsrc/externals/javelin/core/init.js +++ b/webroot/rsrc/externals/javelin/core/init.js @@ -214,8 +214,13 @@ 'mouseup' ]; - if (window.localStorage) { - window_events.push('storage'); + try { + if (window.localStorage) { + window_events.push('storage'); + } + } catch (storage_exception) { + // See PHI985. In Firefox, accessing "window.localStorage" may throw an + // exception if cookies are disabled. } for (ii = 0; ii < window_events.length; ++ii) { From bb369c7b711f6eb5f6b3ca9a0c8b85a088b05dc1 Mon Sep 17 00:00:00 2001 From: epriestley Date: Wed, 21 Nov 2018 11:07:20 -0800 Subject: [PATCH 34/43] Convert the "Repository Management" UI to a full-width, Phortune-style UI Summary: Ref T13216. I want to add some new management options to repositories (e.g., filesize limit, clone timeouts). Before adding new stuff here, update the UI to a full-width, Phortune-style UI. This partially reverts D18523. About a year ago, several UIs got converted to fixed-width (repository management, config, settings, instance management in SAAS). I didn't think these were good changes and have never really gotten used to them. The rationale wasn't clear to me and these changes just felt like "be more like GitHub". I think usability is significantly worse, e.g. actions are now hidden inside button menus instead of immediately visible. Phortune also got converted less dramatically to a full-width-with-menu UI, which I like much better. Adjust repository management to use that UI style instead of the fixed-width style. Test Plan: {F6020884} Viewed every panel, including the Subversion panel. Reviewers: amckinley Reviewed By: amckinley Maniphest Tasks: T13216 Differential Revision: https://secure.phabricator.com/D19826 --- ...fusionRepositoryManagePanelsController.php | 8 ++- ...fusionRepositoryActionsManagementPanel.php | 56 ++++++++++----- ...ionRepositoryAutomationManagementPanel.php | 72 +++++++++++-------- ...ffusionRepositoryBasicsManagementPanel.php | 56 +++++++-------- ...usionRepositoryBranchesManagementPanel.php | 54 +++++++++----- .../DiffusionRepositoryManagementPanel.php | 49 ++++++++----- ...usionRepositoryPoliciesManagementPanel.php | 72 ++++++++++++++----- ...fusionRepositoryStagingManagementPanel.php | 52 +++++++++----- ...fusionRepositoryStorageManagementPanel.php | 37 +++++++--- ...ionRepositorySubversionManagementPanel.php | 51 ++++++++----- ...fusionRepositorySymbolsManagementPanel.php | 53 +++++++++----- ...DiffusionRepositoryURIsManagementPanel.php | 55 ++++++++------ 12 files changed, 405 insertions(+), 210 deletions(-) diff --git a/src/applications/diffusion/controller/DiffusionRepositoryManagePanelsController.php b/src/applications/diffusion/controller/DiffusionRepositoryManagePanelsController.php index 7871c5192a..312c8402d3 100644 --- a/src/applications/diffusion/controller/DiffusionRepositoryManagePanelsController.php +++ b/src/applications/diffusion/controller/DiffusionRepositoryManagePanelsController.php @@ -68,13 +68,17 @@ final class DiffusionRepositoryManagePanelsController $view = id(new PHUITwoColumnView()) ->setHeader($header) - ->setNavigation($nav) - ->setFixed(true) ->setMainColumn($content); + $curtain = $panel->buildManagementPanelCurtain(); + if ($curtain) { + $view->setCurtain($curtain); + } + return $this->newPage() ->setTitle($title) ->setCrumbs($crumbs) + ->setNavigation($nav) ->appendChild($view); } diff --git a/src/applications/diffusion/management/DiffusionRepositoryActionsManagementPanel.php b/src/applications/diffusion/management/DiffusionRepositoryActionsManagementPanel.php index a5805fd0cc..eb1b98dba7 100644 --- a/src/applications/diffusion/management/DiffusionRepositoryActionsManagementPanel.php +++ b/src/applications/diffusion/management/DiffusionRepositoryActionsManagementPanel.php @@ -14,7 +14,20 @@ final class DiffusionRepositoryActionsManagementPanel } public function getManagementPanelIcon() { - return 'fa-flash'; + $repository = $this->getRepository(); + + $has_any = + $repository->getDetail('herald-disabled') || + $repository->getDetail('disable-autoclose'); + + // NOTE: Any value here really means something is disabled, so try to + // hint that a little bit with the icon. + + if ($has_any) { + return 'fa-flash'; + } else { + return 'fa-flash grey'; + } } protected function getEditEngineFieldKeys() { @@ -24,6 +37,30 @@ final class DiffusionRepositoryActionsManagementPanel ); } + public function buildManagementPanelCurtain() { + $repository = $this->getRepository(); + $viewer = $this->getViewer(); + $action_list = $this->newActionList(); + + $can_edit = PhabricatorPolicyFilter::hasCapability( + $viewer, + $repository, + PhabricatorPolicyCapability::CAN_EDIT); + + $actions_uri = $this->getEditPageURI(); + + $action_list->addAction( + id(new PhabricatorActionView()) + ->setIcon('fa-pencil') + ->setName(pht('Edit Actions')) + ->setHref($actions_uri) + ->setDisabled(!$can_edit) + ->setWorkflow(!$can_edit)); + + return $this->newCurtainView() + ->setActionList($action_list); + } + public function buildManagementPanelContent() { $repository = $this->getRepository(); $viewer = $this->getViewer(); @@ -43,22 +80,7 @@ final class DiffusionRepositoryActionsManagementPanel $autoclose = phutil_tag('em', array(), $autoclose); $view->addProperty(pht('Autoclose'), $autoclose); - $can_edit = PhabricatorPolicyFilter::hasCapability( - $viewer, - $repository, - PhabricatorPolicyCapability::CAN_EDIT); - - $actions_uri = $this->getEditPageURI(); - - $button = id(new PHUIButtonView()) - ->setTag('a') - ->setIcon('fa-pencil') - ->setText(pht('Edit')) - ->setHref($actions_uri) - ->setDisabled(!$can_edit) - ->setWorkflow(!$can_edit); - - return $this->newBox(pht('Actions'), $view, array($button)); + return $this->newBox(pht('Actions'), $view); } } diff --git a/src/applications/diffusion/management/DiffusionRepositoryAutomationManagementPanel.php b/src/applications/diffusion/management/DiffusionRepositoryAutomationManagementPanel.php index 22afd5a53b..0ac03bd2cd 100644 --- a/src/applications/diffusion/management/DiffusionRepositoryAutomationManagementPanel.php +++ b/src/applications/diffusion/management/DiffusionRepositoryAutomationManagementPanel.php @@ -27,18 +27,60 @@ final class DiffusionRepositoryAutomationManagementPanel public function getManagementPanelIcon() { $repository = $this->getRepository(); + if (!$repository->canPerformAutomation()) { + return 'fa-truck grey'; + } + $blueprint_phids = $repository->getAutomationBlueprintPHIDs(); + if (!$blueprint_phids) { + return 'fa-truck grey'; + } $is_authorized = DrydockAuthorizationQuery::isFullyAuthorized( $repository->getPHID(), $blueprint_phids); if (!$is_authorized) { - return 'fa-exclamation-triangle'; + return 'fa-exclamation-triangle yellow'; } return 'fa-truck'; } + public function buildManagementPanelCurtain() { + $repository = $this->getRepository(); + $viewer = $this->getViewer(); + $action_list = $this->newActionList(); + + $can_edit = PhabricatorPolicyFilter::hasCapability( + $viewer, + $repository, + PhabricatorPolicyCapability::CAN_EDIT); + + $can_test = $can_edit && $repository->canPerformAutomation(); + + $automation_uri = $this->getEditPageURI(); + $test_uri = $repository->getPathURI('edit/testautomation/'); + + $action_list->addAction( + id(new PhabricatorActionView()) + ->setIcon('fa-pencil') + ->setName(pht('Edit Automation')) + ->setHref($automation_uri) + ->setDisabled(!$can_edit) + ->setWorkflow(!$can_edit)); + + $action_list->addAction( + id(new PhabricatorActionView()) + ->setIcon('fa-gamepad') + ->setName(pht('Test Configuration')) + ->setWorkflow(true) + ->setDisabled(!$can_test) + ->setHref($test_uri)); + + return $this->newCurtainView() + ->setActionList($action_list); + } + public function buildManagementPanelContent() { $repository = $this->getRepository(); $viewer = $this->getViewer(); @@ -58,33 +100,7 @@ final class DiffusionRepositoryAutomationManagementPanel $view->addProperty(pht('Automation'), $blueprint_view); - $can_edit = PhabricatorPolicyFilter::hasCapability( - $viewer, - $repository, - PhabricatorPolicyCapability::CAN_EDIT); - - $can_test = $can_edit && $repository->canPerformAutomation(); - - $automation_uri = $this->getEditPageURI(); - $test_uri = $repository->getPathURI('edit/testautomation/'); - - $edit = id(new PHUIButtonView()) - ->setTag('a') - ->setIcon('fa-pencil') - ->setText(pht('Edit')) - ->setHref($automation_uri) - ->setDisabled(!$can_edit) - ->setWorkflow(!$can_edit); - - $test = id(new PHUIButtonView()) - ->setTag('a') - ->setIcon('fa-gamepad') - ->setText(pht('Test Config')) - ->setWorkflow(true) - ->setDisabled(!$can_test) - ->setHref($test_uri); - - return $this->newBox(pht('Automation'), $view, array($edit, $test)); + return $this->newBox(pht('Automation'), $view); } } diff --git a/src/applications/diffusion/management/DiffusionRepositoryBasicsManagementPanel.php b/src/applications/diffusion/management/DiffusionRepositoryBasicsManagementPanel.php index af5e9b2881..bcf3ad6d74 100644 --- a/src/applications/diffusion/management/DiffusionRepositoryBasicsManagementPanel.php +++ b/src/applications/diffusion/management/DiffusionRepositoryBasicsManagementPanel.php @@ -27,9 +27,10 @@ final class DiffusionRepositoryBasicsManagementPanel ); } - private function buildActionMenu() { + public function buildManagementPanelCurtain() { $repository = $this->getRepository(); $viewer = $this->getViewer(); + $action_list = id(new PhabricatorActionListView()) ->setViewer($viewer); @@ -44,27 +45,34 @@ final class DiffusionRepositoryBasicsManagementPanel $encoding_uri = $this->getEditPageURI('encoding'); $dangerous_uri = $repository->getPathURI('edit/dangerous/'); $enormous_uri = $repository->getPathURI('edit/enormous/'); + $update_uri = $repository->getPathURI('edit/update/'); if ($repository->isTracked()) { + $activate_icon = 'fa-ban'; $activate_label = pht('Deactivate Repository'); } else { + $activate_icon = 'fa-check'; $activate_label = pht('Activate Repository'); } $should_dangerous = $repository->shouldAllowDangerousChanges(); if ($should_dangerous) { + $dangerous_icon = 'fa-shield'; $dangerous_name = pht('Prevent Dangerous Changes'); $can_dangerous = $can_edit; } else { + $dangerous_icon = 'fa-exclamation-triangle'; $dangerous_name = pht('Allow Dangerous Changes'); $can_dangerous = ($can_edit && $repository->canAllowDangerousChanges()); } $should_enormous = $repository->shouldAllowEnormousChanges(); if ($should_enormous) { + $enormous_icon = 'fa-shield'; $enormous_name = pht('Prevent Enormous Changes'); $can_enormous = $can_edit; } else { + $enormous_icon = 'fa-exclamation-triangle'; $enormous_name = pht('Allow Enormous Changes'); $can_enormous = ($can_edit && $repository->canAllowEnormousChanges()); } @@ -73,12 +81,14 @@ final class DiffusionRepositoryBasicsManagementPanel id(new PhabricatorActionView()) ->setName(pht('Edit Basic Information')) ->setHref($edit_uri) + ->setIcon('fa-pencil') ->setDisabled(!$can_edit) ->setWorkflow(!$can_edit)); $action_list->addAction( id(new PhabricatorActionView()) ->setName(pht('Edit Text Encoding')) + ->setIcon('fa-text-width') ->setHref($encoding_uri) ->setDisabled(!$can_edit) ->setWorkflow(!$can_edit)); @@ -87,6 +97,7 @@ final class DiffusionRepositoryBasicsManagementPanel id(new PhabricatorActionView()) ->setName($dangerous_name) ->setHref($dangerous_uri) + ->setIcon($dangerous_icon) ->setDisabled(!$can_dangerous) ->setWorkflow(true)); @@ -94,16 +105,26 @@ final class DiffusionRepositoryBasicsManagementPanel id(new PhabricatorActionView()) ->setName($enormous_name) ->setHref($enormous_uri) + ->setIcon($enormous_icon) ->setDisabled(!$can_enormous) ->setWorkflow(true)); $action_list->addAction( id(new PhabricatorActionView()) - ->setHref($activate_uri) ->setName($activate_label) + ->setHref($activate_uri) + ->setIcon($activate_icon) ->setDisabled(!$can_edit) ->setWorkflow(true)); + $action_list->addAction( + id(new PhabricatorActionView()) + ->setName(pht('Update Now')) + ->setHref($update_uri) + ->setIcon('fa-refresh') + ->setWorkflow(true) + ->setDisabled(!$can_edit)); + $action_list->addAction( id(new PhabricatorActionView()) ->setType(PhabricatorActionView::TYPE_DIVIDER)); @@ -112,25 +133,18 @@ final class DiffusionRepositoryBasicsManagementPanel id(new PhabricatorActionView()) ->setName(pht('Delete Repository')) ->setHref($delete_uri) + ->setIcon('fa-times') ->setColor(PhabricatorActionView::RED) ->setDisabled(true) ->setWorkflow(true)); - return $action_list; + return $this->newCurtainView() + ->setActionList($action_list); } public function buildManagementPanelContent() { - - $button = id(new PHUIButtonView()) - ->setTag('a') - ->setText(pht('Actions')) - ->setHref('#') - ->setIcon('fa-bars') - ->addClass('phui-mobile-menu') - ->setDropdownMenu($this->buildActionMenu()); - $basics = $this->buildBasics(); - $basics = $this->newBox(pht('Properties'), $basics, array($button)); + $basics = $this->newBox(pht('Properties'), $basics); $repository = $this->getRepository(); $is_new = $repository->isNewlyInitialized(); @@ -254,7 +268,6 @@ final class DiffusionRepositoryBasicsManagementPanel private function buildStatus() { $repository = $this->getRepository(); $viewer = $this->getViewer(); - $update_uri = $repository->getPathURI('edit/update/'); $view = id(new PHUIPropertyListView()) ->setViewer($viewer); @@ -274,20 +287,7 @@ final class DiffusionRepositoryBasicsManagementPanel $view->addTextContent($raw_error); } - $can_edit = PhabricatorPolicyFilter::hasCapability( - $viewer, - $repository, - PhabricatorPolicyCapability::CAN_EDIT); - - $button = id(new PHUIButtonView()) - ->setTag('a') - ->setIcon('fa-refresh') - ->setText(pht('Update Now')) - ->setWorkflow(true) - ->setDisabled(!$can_edit) - ->setHref($update_uri); - - return $this->newBox(pht('Status'), $view, array($button)); + return $this->newBox(pht('Status'), $view); } private function buildRepositoryUpdateInterval( diff --git a/src/applications/diffusion/management/DiffusionRepositoryBranchesManagementPanel.php b/src/applications/diffusion/management/DiffusionRepositoryBranchesManagementPanel.php index 7c7f4bcf63..e3ace3a5a5 100644 --- a/src/applications/diffusion/management/DiffusionRepositoryBranchesManagementPanel.php +++ b/src/applications/diffusion/management/DiffusionRepositoryBranchesManagementPanel.php @@ -19,7 +19,18 @@ final class DiffusionRepositoryBranchesManagementPanel } public function getManagementPanelIcon() { - return 'fa-code-fork'; + $repository = $this->getRepository(); + + $has_any = + $repository->getDetail('default-branch') || + $repository->getDetail('branch-filter') || + $repository->getDetail('close-commits-filter'); + + if ($has_any) { + return 'fa-code-fork'; + } else { + return 'fa-code-fork grey'; + } } protected function getEditEngineFieldKeys() { @@ -30,6 +41,30 @@ final class DiffusionRepositoryBranchesManagementPanel ); } + public function buildManagementPanelCurtain() { + $repository = $this->getRepository(); + $viewer = $this->getViewer(); + $action_list = $this->newActionList(); + + $can_edit = PhabricatorPolicyFilter::hasCapability( + $viewer, + $repository, + PhabricatorPolicyCapability::CAN_EDIT); + + $branches_uri = $this->getEditPageURI(); + + $action_list->addAction( + id(new PhabricatorActionView()) + ->setIcon('fa-pencil') + ->setName(pht('Edit Branches')) + ->setHref($branches_uri) + ->setDisabled(!$can_edit) + ->setWorkflow(!$can_edit)); + + return $this->newCurtainView() + ->setActionList($action_list); + } + public function buildManagementPanelContent() { $repository = $this->getRepository(); $viewer = $this->getViewer(); @@ -61,22 +96,7 @@ final class DiffusionRepositoryBranchesManagementPanel $view->addProperty(pht('Autoclose Only'), $autoclose_only); - $can_edit = PhabricatorPolicyFilter::hasCapability( - $viewer, - $repository, - PhabricatorPolicyCapability::CAN_EDIT); - - $branches_uri = $this->getEditPageURI(); - - $button = id(new PHUIButtonView()) - ->setTag('a') - ->setIcon('fa-pencil') - ->setText(pht('Edit')) - ->setHref($branches_uri) - ->setDisabled(!$can_edit) - ->setWorkflow(!$can_edit); - - $content[] = $this->newBox(pht('Branches'), $view, array($button)); + $content[] = $this->newBox(pht('Branches'), $view); // Branch Autoclose Table if (!$repository->isImporting()) { diff --git a/src/applications/diffusion/management/DiffusionRepositoryManagementPanel.php b/src/applications/diffusion/management/DiffusionRepositoryManagementPanel.php index 282fa1b7b1..6f3d209e59 100644 --- a/src/applications/diffusion/management/DiffusionRepositoryManagementPanel.php +++ b/src/applications/diffusion/management/DiffusionRepositoryManagementPanel.php @@ -41,6 +41,7 @@ abstract class DiffusionRepositoryManagementPanel abstract public function getManagementPanelLabel(); abstract public function getManagementPanelOrder(); abstract public function buildManagementPanelContent(); + public function buildManagementPanelCurtain() { return null; } public function getManagementPanelIcon() { return 'fa-pencil'; @@ -63,22 +64,6 @@ abstract class DiffusionRepositoryManagementPanel ->execute(); } - final protected function newBox($header_text, $body, $button = array()) { - $header = id(new PHUIHeaderView()) - ->setHeader($header_text); - - foreach ($button as $link) { - $header->addActionLink($link); - } - - $view = id(new PHUIObjectBoxView()) - ->setHeader($header) - ->setBackground(PHUIObjectBoxView::WHITE_CONFIG) - ->appendChild($body); - - return $view; - } - final protected function newTimeline() { return $this->controller->newTimeline($this->getRepository()); } @@ -124,4 +109,36 @@ abstract class DiffusionRepositoryManagementPanel return $this->getPanelURI(); } + final protected function newActionList() { + $viewer = $this->getViewer(); + $action_id = celerity_generate_unique_node_id(); + + return id(new PhabricatorActionListView()) + ->setViewer($viewer) + ->setID($action_id); + } + + final protected function newCurtainView() { + $viewer = $this->getViewer(); + + return id(new PHUICurtainView()) + ->setViewer($viewer); + } + + final protected function newBox($header_text, $body) { + $viewer = $this->getViewer(); + + $header = id(new PHUIHeaderView()) + ->setViewer($viewer) + ->setHeader($header_text); + + $view = id(new PHUIObjectBoxView()) + ->setViewer($viewer) + ->setHeader($header) + ->setBackground(PHUIObjectBoxView::BLUE_PROPERTY) + ->appendChild($body); + + return $view; + } + } diff --git a/src/applications/diffusion/management/DiffusionRepositoryPoliciesManagementPanel.php b/src/applications/diffusion/management/DiffusionRepositoryPoliciesManagementPanel.php index 3da6ea80dc..7cada92fed 100644 --- a/src/applications/diffusion/management/DiffusionRepositoryPoliciesManagementPanel.php +++ b/src/applications/diffusion/management/DiffusionRepositoryPoliciesManagementPanel.php @@ -14,7 +14,35 @@ final class DiffusionRepositoryPoliciesManagementPanel } public function getManagementPanelIcon() { - return 'fa-lock'; + $viewer = $this->getViewer(); + $repository = $this->getRepository(); + + $can_view = PhabricatorPolicyCapability::CAN_VIEW; + $can_edit = PhabricatorPolicyCapability::CAN_EDIT; + $can_push = DiffusionPushCapability::CAPABILITY; + + $actual_values = array( + 'spacePHID' => $repository->getSpacePHID(), + 'view' => $repository->getPolicy($can_view), + 'edit' => $repository->getPolicy($can_edit), + 'push' => $repository->getPolicy($can_push), + ); + + $default = PhabricatorRepository::initializeNewRepository( + $viewer); + + $default_values = array( + 'spacePHID' => $default->getSpacePHID(), + 'view' => $default->getPolicy($can_view), + 'edit' => $default->getPolicy($can_edit), + 'push' => $default->getPolicy($can_push), + ); + + if ($actual_values === $default_values) { + return 'fa-lock grey'; + } else { + return 'fa-lock'; + } } protected function getEditEngineFieldKeys() { @@ -26,6 +54,31 @@ final class DiffusionRepositoryPoliciesManagementPanel ); } + public function buildManagementPanelCurtain() { + $repository = $this->getRepository(); + $viewer = $this->getViewer(); + $action_list = $this->newActionList(); + + $can_edit = PhabricatorPolicyFilter::hasCapability( + $viewer, + $repository, + PhabricatorPolicyCapability::CAN_EDIT); + + $edit_uri = $this->getEditPageURI(); + + $action_list->addAction( + id(new PhabricatorActionView()) + ->setName(pht('Edit Policies')) + ->setHref($edit_uri) + ->setIcon('fa-pencil') + ->setDisabled(!$can_edit) + ->setWorkflow(!$can_edit)); + + return $this->newCurtainView() + ->setActionList($action_list); + } + + public function buildManagementPanelContent() { $repository = $this->getRepository(); $viewer = $this->getViewer(); @@ -58,22 +111,7 @@ final class DiffusionRepositoryPoliciesManagementPanel : phutil_tag('em', array(), pht('Not a Hosted Repository')); $view->addProperty(pht('Pushable By'), $pushable); - $can_edit = PhabricatorPolicyFilter::hasCapability( - $viewer, - $repository, - PhabricatorPolicyCapability::CAN_EDIT); - - $edit_uri = $this->getEditPageURI(); - - $button = id(new PHUIButtonView()) - ->setTag('a') - ->setIcon('fa-pencil') - ->setText(pht('Edit')) - ->setHref($edit_uri) - ->setDisabled(!$can_edit) - ->setWorkflow(!$can_edit); - - return $this->newBox(pht('Policies'), $view, array($button)); + return $this->newBox(pht('Policies'), $view); } } diff --git a/src/applications/diffusion/management/DiffusionRepositoryStagingManagementPanel.php b/src/applications/diffusion/management/DiffusionRepositoryStagingManagementPanel.php index ebd970f032..33e5f7842a 100644 --- a/src/applications/diffusion/management/DiffusionRepositoryStagingManagementPanel.php +++ b/src/applications/diffusion/management/DiffusionRepositoryStagingManagementPanel.php @@ -18,9 +18,16 @@ final class DiffusionRepositoryStagingManagementPanel return $repository->isGit(); } - public function getManagementPanelIcon() { - return 'fa-upload'; + $repository = $this->getRepository(); + + $staging_uri = $repository->getStagingURI(); + + if ($staging_uri) { + return 'fa-upload'; + } else { + return 'fa-upload grey'; + } } protected function getEditEngineFieldKeys() { @@ -29,6 +36,30 @@ final class DiffusionRepositoryStagingManagementPanel ); } + public function buildManagementPanelCurtain() { + $repository = $this->getRepository(); + $viewer = $this->getViewer(); + $action_list = $this->newActionList(); + + $can_edit = PhabricatorPolicyFilter::hasCapability( + $viewer, + $repository, + PhabricatorPolicyCapability::CAN_EDIT); + + $staging_uri = $this->getEditPageURI(); + + $action_list->addAction( + id(new PhabricatorActionView()) + ->setIcon('fa-pencil') + ->setName(pht('Edit Staging')) + ->setHref($staging_uri) + ->setDisabled(!$can_edit) + ->setWorkflow(!$can_edit)); + + return $this->newCurtainView() + ->setActionList($action_list); + } + public function buildManagementPanelContent() { $repository = $this->getRepository(); $viewer = $this->getViewer(); @@ -43,22 +74,7 @@ final class DiffusionRepositoryStagingManagementPanel $view->addProperty(pht('Staging Area URI'), $staging_uri); - $can_edit = PhabricatorPolicyFilter::hasCapability( - $viewer, - $repository, - PhabricatorPolicyCapability::CAN_EDIT); - - $staging_uri = $this->getEditPageURI(); - - $button = id(new PHUIButtonView()) - ->setTag('a') - ->setIcon('fa-pencil') - ->setText(pht('Edit')) - ->setHref($staging_uri) - ->setDisabled(!$can_edit) - ->setWorkflow(!$can_edit); - - return $this->newBox(pht('Staging Area'), $view, array($button)); + return $this->newBox(pht('Staging Area'), $view); } } diff --git a/src/applications/diffusion/management/DiffusionRepositoryStorageManagementPanel.php b/src/applications/diffusion/management/DiffusionRepositoryStorageManagementPanel.php index 0a15a62417..0f09b3dc22 100644 --- a/src/applications/diffusion/management/DiffusionRepositoryStorageManagementPanel.php +++ b/src/applications/diffusion/management/DiffusionRepositoryStorageManagementPanel.php @@ -14,7 +14,32 @@ final class DiffusionRepositoryStorageManagementPanel } public function getManagementPanelIcon() { - return 'fa-database'; + $repository = $this->getRepository(); + + if ($repository->getAlmanacServicePHID()) { + return 'fa-sitemap'; + } else if ($repository->isHosted()) { + return 'fa-database'; + } else { + return 'fa-download'; + } + } + + public function buildManagementPanelCurtain() { + $repository = $this->getRepository(); + $viewer = $this->getViewer(); + $action_list = $this->newActionList(); + + $doc_href = PhabricatorEnv::getDoclink('Cluster: Repositories'); + + $action_list->addAction( + id(new PhabricatorActionView()) + ->setIcon('fa-book') + ->setHref($doc_href) + ->setName(pht('Cluster Documentation'))); + + return $this->newCurtainView() + ->setActionList($action_list); } public function buildManagementPanelContent() { @@ -47,15 +72,7 @@ final class DiffusionRepositoryStorageManagementPanel $view->addProperty(pht('Storage Path'), $storage_path); $view->addProperty(pht('Storage Cluster'), $storage_service); - $doc_href = PhabricatorEnv::getDoclink('Cluster: Repositories'); - - $button = id(new PHUIButtonView()) - ->setTag('a') - ->setIcon('fa-book') - ->setHref($doc_href) - ->setText(pht('Help')); - - return $this->newBox(pht('Storage'), $view, array($button)); + return $this->newBox(pht('Storage'), $view); } private function buildClusterStatusPanel() { diff --git a/src/applications/diffusion/management/DiffusionRepositorySubversionManagementPanel.php b/src/applications/diffusion/management/DiffusionRepositorySubversionManagementPanel.php index cd6e2c5be0..f87a5d9b85 100644 --- a/src/applications/diffusion/management/DiffusionRepositorySubversionManagementPanel.php +++ b/src/applications/diffusion/management/DiffusionRepositorySubversionManagementPanel.php @@ -19,7 +19,15 @@ final class DiffusionRepositorySubversionManagementPanel } public function getManagementPanelIcon() { - return 'fa-folder'; + $repository = $this->getRepository(); + + $has_any = (bool)$repository->getDetail('svn-subpath'); + + if ($has_any) { + return 'fa-folder'; + } else { + return 'fa-folder grey'; + } } protected function getEditEngineFieldKeys() { @@ -28,6 +36,30 @@ final class DiffusionRepositorySubversionManagementPanel ); } + public function buildManagementPanelCurtain() { + $repository = $this->getRepository(); + $viewer = $this->getViewer(); + $action_list = $this->newActionList(); + + $can_edit = PhabricatorPolicyFilter::hasCapability( + $viewer, + $repository, + PhabricatorPolicyCapability::CAN_EDIT); + + $subversion_uri = $this->getEditPageURI(); + + $action_list->addAction( + id(new PhabricatorActionView()) + ->setIcon('fa-pencil') + ->setName(pht('Edit Properties')) + ->setHref($subversion_uri) + ->setDisabled(!$can_edit) + ->setWorkflow(!$can_edit)); + + return $this->newCurtainView($action_list) + ->setActionList($action_list); + } + public function buildManagementPanelContent() { $repository = $this->getRepository(); $viewer = $this->getViewer(); @@ -40,22 +72,7 @@ final class DiffusionRepositorySubversionManagementPanel phutil_tag('em', array(), pht('Import Entire Repository'))); $view->addProperty(pht('Import Only'), $default_branch); - $can_edit = PhabricatorPolicyFilter::hasCapability( - $viewer, - $repository, - PhabricatorPolicyCapability::CAN_EDIT); - - $subversion_uri = $this->getEditPageURI(); - - $button = id(new PHUIButtonView()) - ->setTag('a') - ->setIcon('fa-pencil') - ->setText(pht('Edit')) - ->setHref($subversion_uri) - ->setDisabled(!$can_edit) - ->setWorkflow(!$can_edit); - - return $this->newBox(pht('Subversion'), $view, array($button)); + return $this->newBox(pht('Subversion'), $view); } } diff --git a/src/applications/diffusion/management/DiffusionRepositorySymbolsManagementPanel.php b/src/applications/diffusion/management/DiffusionRepositorySymbolsManagementPanel.php index f64fbf8d6e..6970861cff 100644 --- a/src/applications/diffusion/management/DiffusionRepositorySymbolsManagementPanel.php +++ b/src/applications/diffusion/management/DiffusionRepositorySymbolsManagementPanel.php @@ -14,7 +14,17 @@ final class DiffusionRepositorySymbolsManagementPanel } public function getManagementPanelIcon() { - return 'fa-bullseye'; + $repository = $this->getRepository(); + + $has_any = + $repository->getSymbolLanguages() || + $repository->getSymbolSources(); + + if ($has_any) { + return 'fa-link'; + } else { + return 'fa-link grey'; + } } protected function getEditEngineFieldKeys() { @@ -24,6 +34,30 @@ final class DiffusionRepositorySymbolsManagementPanel ); } + public function buildManagementPanelCurtain() { + $repository = $this->getRepository(); + $viewer = $this->getViewer(); + $action_list = $this->newActionList(); + + $can_edit = PhabricatorPolicyFilter::hasCapability( + $viewer, + $repository, + PhabricatorPolicyCapability::CAN_EDIT); + + $symbols_uri = $this->getEditPageURI(); + + $action_list->addAction( + id(new PhabricatorActionView()) + ->setIcon('fa-pencil') + ->setName(pht('Edit Symbols')) + ->setHref($symbols_uri) + ->setDisabled(!$can_edit) + ->setWorkflow(!$can_edit)); + + return $this->newCurtainView() + ->setActionList($action_list); + } + public function buildManagementPanelContent() { $repository = $this->getRepository(); $viewer = $this->getViewer(); @@ -47,22 +81,7 @@ final class DiffusionRepositorySymbolsManagementPanel } $view->addProperty(pht('Uses Symbols From'), $sources); - $can_edit = PhabricatorPolicyFilter::hasCapability( - $viewer, - $repository, - PhabricatorPolicyCapability::CAN_EDIT); - - $symbols_uri = $this->getEditPageURI(); - - $button = id(new PHUIButtonView()) - ->setTag('a') - ->setIcon('fa-pencil') - ->setText(pht('Edit')) - ->setHref($symbols_uri) - ->setDisabled(!$can_edit) - ->setWorkflow(!$can_edit); - - return $this->newBox(pht('Symbols'), $view, array($button)); + return $this->newBox(pht('Symbols'), $view); } } diff --git a/src/applications/diffusion/management/DiffusionRepositoryURIsManagementPanel.php b/src/applications/diffusion/management/DiffusionRepositoryURIsManagementPanel.php index 220bf4e2c1..a795c955a7 100644 --- a/src/applications/diffusion/management/DiffusionRepositoryURIsManagementPanel.php +++ b/src/applications/diffusion/management/DiffusionRepositoryURIsManagementPanel.php @@ -17,6 +17,36 @@ final class DiffusionRepositoryURIsManagementPanel return 400; } + public function buildManagementPanelCurtain() { + $repository = $this->getRepository(); + $viewer = $this->getViewer(); + $action_list = $this->newActionList(); + + $can_edit = PhabricatorPolicyFilter::hasCapability( + $viewer, + $repository, + PhabricatorPolicyCapability::CAN_EDIT); + + $doc_href = PhabricatorEnv::getDoclink('Diffusion User Guide: URIs'); + $add_href = $repository->getPathURI('uri/edit/'); + + $action_list->addAction( + id(new PhabricatorActionView()) + ->setIcon('fa-plus') + ->setHref($add_href) + ->setDisabled(!$can_edit) + ->setName(pht('Add New URI'))); + + $action_list->addAction( + id(new PhabricatorActionView()) + ->setIcon('fa-book') + ->setHref($doc_href) + ->setName(pht('URI Documentation'))); + + return $this->newCurtainView() + ->setActionList($action_list); + } + public function buildManagementPanelContent() { $repository = $this->getRepository(); $viewer = $this->getViewer(); @@ -122,30 +152,9 @@ final class DiffusionRepositoryURIsManagementPanel ->setSeverity(PHUIInfoView::SEVERITY_NOTICE) ->setErrors($messages); - $can_edit = PhabricatorPolicyFilter::hasCapability( - $viewer, - $repository, - PhabricatorPolicyCapability::CAN_EDIT); + $box = $this->newBox(pht('Repository URIs'), $table); - $doc_href = PhabricatorEnv::getDoclink('Diffusion User Guide: URIs'); - $add_href = $repository->getPathURI('uri/edit/'); - - $add = id(new PHUIButtonView()) - ->setTag('a') - ->setIcon('fa-plus') - ->setHref($add_href) - ->setDisabled(!$can_edit) - ->setText(pht('New URI')); - - $help = id(new PHUIButtonView()) - ->setTag('a') - ->setIcon('fa-book') - ->setHref($doc_href) - ->setText(pht('Help')); - - $box = $this->newBox(pht('Repository URIs'), $table, array($add, $help)); - - return array($box, $info_view); + return array($info_view, $box); } } From 1d0b99e1f8348ca222498551423154a0c37878fb Mon Sep 17 00:00:00 2001 From: epriestley Date: Tue, 27 Nov 2018 05:14:45 -0800 Subject: [PATCH 35/43] Allow applications to require a High Security token without doing a session upgrade Summary: Ref T13222. See PHI873. Currently, when applications prompt users to enter MFA, their session upgrades as a side effect. In some cases (like managing your email addresses) it makes sense to upgrade your session for a little while since it's common to make multiple edits in sequence (add a new address, make it primary, remove an old address). We generally want MFA to stay out of the way and not feel annoying. In other cases, we don't expect multiple high-security actions in a row. Notably, PHI873 looks at more "one-shot" use cases where a prompt is answering a specific workflow. We already have at least one of these in the upstream: answering an MFA prompt when signing a Legalpad document. Introduce a "token" workflow (in contrast to the existing "session") workflow that just does a one-shot prompt without upgrading your session statefully. Then, make Legalpad use this new workflow. Note that this workflow has a significant problem: if the form submission is invalid for some other reason, we re-prompt you on resubmit. In Legalpad, this workflow looks like: - Forget to check the "I agree" checkbox. - Submit the form. - Get prompted for MFA. - Answer MFA prompt. - Get dumped back to the form with an error. - When you fix the error and submit again, you have to do another MFA check. This isn't a fatal flaw in Legalpad, but would become a problem with wider adoption. I'll work on fixing this (so the MFA token sticks to the form) in the next set of changes. Roughly, this is headed toward "MFA sticks to the form/workflow" instead of "MFA sticks to the user/session". Test Plan: - Signed a legalpad document with MFA enabled. - Was prompted for MFA. - Session no longer upgraded (no purple "session in high security" badge). - Submitted form with error, answered MFA, fixed error, submitted form again. - Bad behavior: got re-prompted for MFA. In the future, MFA should stick to the form. Reviewers: amckinley Reviewed By: amckinley Maniphest Tasks: T13222 Differential Revision: https://secure.phabricator.com/D19843 --- .../engine/PhabricatorAuthSessionEngine.php | 56 +++++++++++++++++++ .../LegalpadDocumentSignController.php | 11 ++-- .../legalpad/storage/LegalpadDocument.php | 2 +- 3 files changed, 63 insertions(+), 6 deletions(-) diff --git a/src/applications/auth/engine/PhabricatorAuthSessionEngine.php b/src/applications/auth/engine/PhabricatorAuthSessionEngine.php index 98ec8b744a..4ce86e8f97 100644 --- a/src/applications/auth/engine/PhabricatorAuthSessionEngine.php +++ b/src/applications/auth/engine/PhabricatorAuthSessionEngine.php @@ -345,6 +345,33 @@ final class PhabricatorAuthSessionEngine extends Phobject { /* -( High Security )------------------------------------------------------ */ + /** + * Require the user respond to a high security (MFA) check. + * + * This method differs from @{method:requireHighSecuritySession} in that it + * does not upgrade the user's session as a side effect. This method is + * appropriate for one-time checks. + * + * @param PhabricatorUser User whose session needs to be in high security. + * @param AphrontReqeust Current request. + * @param string URI to return the user to if they cancel. + * @return PhabricatorAuthHighSecurityToken Security token. + * @task hisec + */ + public function requireHighSecurityToken( + PhabricatorUser $viewer, + AphrontRequest $request, + $cancel_uri) { + + return $this->newHighSecurityToken( + $viewer, + $request, + $cancel_uri, + false, + false); + } + + /** * Require high security, or prompt the user to enter high security. * @@ -352,6 +379,11 @@ final class PhabricatorAuthSessionEngine extends Phobject { * token. Otherwise, it will throw an exception which will eventually * be converted into a multi-factor authentication workflow. * + * This method upgrades the user's session to high security for a short + * period of time, and is appropriate if you anticipate they may need to + * take multiple high security actions. To perform a one-time check instead, + * use @{method:requireHighSecurityToken}. + * * @param PhabricatorUser User whose session needs to be in high security. * @param AphrontReqeust Current request. * @param string URI to return the user to if they cancel. @@ -367,11 +399,30 @@ final class PhabricatorAuthSessionEngine extends Phobject { $cancel_uri, $jump_into_hisec = false) { + return $this->newHighSecurityToken( + $viewer, + $request, + $cancel_uri, + false, + true); + } + + private function newHighSecurityToken( + PhabricatorUser $viewer, + AphrontRequest $request, + $cancel_uri, + $jump_into_hisec, + $upgrade_session) { + if (!$viewer->hasSession()) { throw new Exception( pht('Requiring a high-security session from a user with no session!')); } + // TODO: If a user answers a "requireHighSecurityToken()" prompt and hits + // a "requireHighSecuritySession()" prompt a short time later, the one-shot + // token should be good enough to upgrade the session. + $session = $viewer->getSession(); // Check if the session is already in high security mode. @@ -441,6 +492,11 @@ final class PhabricatorAuthSessionEngine extends Phobject { return $this->issueHighSecurityToken($session, true); } + // If we aren't upgrading the session itself, just issue a token. + if (!$upgrade_session) { + return $this->issueHighSecurityToken($session, true); + } + $until = time() + phutil_units('15 minutes in seconds'); $session->setHighSecurityUntil($until); diff --git a/src/applications/legalpad/controller/LegalpadDocumentSignController.php b/src/applications/legalpad/controller/LegalpadDocumentSignController.php index 1748dba452..8ef35e3493 100644 --- a/src/applications/legalpad/controller/LegalpadDocumentSignController.php +++ b/src/applications/legalpad/controller/LegalpadDocumentSignController.php @@ -149,15 +149,16 @@ final class LegalpadDocumentSignController extends LegalpadController { } $errors = array(); + $hisec_token = null; if ($request->isFormOrHisecPost() && !$has_signed) { // Require two-factor auth to sign legal documents. if ($viewer->isLoggedIn()) { - $engine = new PhabricatorAuthSessionEngine(); - $engine->requireHighSecuritySession( - $viewer, - $request, - '/'.$document->getMonogram()); + $hisec_token = id(new PhabricatorAuthSessionEngine()) + ->requireHighSecurityToken( + $viewer, + $request, + $document->getURI()); } list($form_data, $errors, $field_errors) = $this->readSignatureForm( diff --git a/src/applications/legalpad/storage/LegalpadDocument.php b/src/applications/legalpad/storage/LegalpadDocument.php index 55d1ef9fa9..004802de39 100644 --- a/src/applications/legalpad/storage/LegalpadDocument.php +++ b/src/applications/legalpad/storage/LegalpadDocument.php @@ -120,7 +120,7 @@ final class LegalpadDocument extends LegalpadDAO return 'L'.$this->getID(); } - public function getViewURI() { + public function getURI() { return '/'.$this->getMonogram(); } From 2f11001f6e3c2bf6ceeac7684860d5a0cd0f01de Mon Sep 17 00:00:00 2001 From: epriestley Date: Mon, 26 Nov 2018 15:44:48 -0800 Subject: [PATCH 36/43] Allow "Change Subtype" to be selected from the comment action stack Summary: Ref T13222. See PHI683. Currently, you can "Change subtype..." via Conduit and the bulk editor, but not via the comment action stack or edit forms. In PHI683 an install is doing this often enough that they'd like it to become a first-class action. I've generally been cautious about pushing this action to become a first-class action (there are some inevitable rough edges and I don't want to add too much complexity if there isn't a use case for it) but since we have evidence that users would find it useful and nothing has exploded yet, I'm comfortable taking another step forward. Currently, `EditEngine` has this sort of weird `setIsConduitOnly()` method. This actually means more like "this doesn't show up on forms". Make it better align with that. In particular, a "conduit only" field can already show up in the bulk editor, which is goofy. Change this to `setIsFormField()` and convert/simplify existing callsites. Test Plan: There are a lot of ways to reach EditEngine so this probably isn't entirely exhaustive, but I think I got pretty much anything which is likely to break: - Searched for `setIsConduitOnly()` and `getIsConduitOnly()`, converted all callsites to `setIsFormField()`. - Searched for `setIsLockable()`, `setIsReorderable()` and `setIsDefaultable()` and aligned these calls to intent where applicable. - Created an Almanac binding. - Edited an Almanac binding. - Created an Almanac service. - Edited an Almanac service. - Edited a binding property. - Deleted a binding property. - Created and edited a badge. - Awarded and revoked a badge. - Created and edited an event. - Made an event recurring. - Created and edited a Conpherence thread. - Edited and updated the diff for a revision. - Created and edited a repository. - Created and disabled repository URIs. - Created and edited a blueprint. - Created and edited tasks. - Created a paste, edited/archived a paste. - Created/edited/archived a package. - Created/edited a project. - Made comments. - Moved tasks on workboards via comment action stack. - Changed task subtype via comment action stack. Reviewers: amckinley Reviewed By: amckinley Subscribers: PHID-OPKG-gm6ozazyms6q6i22gyam Maniphest Tasks: T13222 Differential Revision: https://secure.phabricator.com/D19842 --- .../editor/AlmanacBindingEditEngine.php | 6 ++--- .../editor/AlmanacInterfaceEditEngine.php | 2 +- .../editor/AlmanacServiceEditEngine.php | 2 +- .../AlmanacPropertiesEditEngineExtension.php | 4 ++-- .../editor/PhabricatorBadgesEditEngine.php | 4 ++-- .../PhabricatorCalendarEventEditEngine.php | 4 ++-- .../PhabricatorCalendarExportEditEngine.php | 2 +- .../PhabricatorCalendarImportEditEngine.php | 6 ++--- .../editor/ConpherenceEditEngine.php | 6 ++--- .../editor/DifferentialRevisionEditEngine.php | 6 ++--- .../editor/DiffusionRepositoryEditEngine.php | 8 +++---- .../editor/DiffusionURIEditEngine.php | 6 ++--- .../editor/DrydockBlueprintEditEngine.php | 2 +- .../maniphest/editor/ManiphestEditEngine.php | 8 +++---- .../PhabricatorOwnersPackageEditEngine.php | 4 ++-- .../editor/PhabricatorPasteEditEngine.php | 2 +- .../editor/PhabricatorUserEditEngine.php | 2 +- .../phame/editor/PhameBlogEditEngine.php | 2 +- .../engine/PhabricatorProjectEditEngine.php | 4 ++-- ...tEngineConfigurationDefaultsController.php | 5 +++++ ...rEditEngineConfigurationLockController.php | 4 ++++ ...itEngineConfigurationReorderController.php | 4 ++++ .../editengine/PhabricatorEditEngine.php | 4 ++++ .../editfield/PhabricatorEditField.php | 22 +++++++++---------- .../PhabricatorCommentEditEngineExtension.php | 5 +---- .../PhabricatorSubtypeEditEngineExtension.php | 14 ++++++------ .../PhabricatorEditEngineConfiguration.php | 5 +++++ .../PhabricatorCustomFieldEditField.php | 2 +- .../field/PhabricatorCustomField.php | 8 +++---- 29 files changed, 85 insertions(+), 68 deletions(-) diff --git a/src/applications/almanac/editor/AlmanacBindingEditEngine.php b/src/applications/almanac/editor/AlmanacBindingEditEngine.php index 5146578fff..66db7fcbab 100644 --- a/src/applications/almanac/editor/AlmanacBindingEditEngine.php +++ b/src/applications/almanac/editor/AlmanacBindingEditEngine.php @@ -136,7 +136,7 @@ final class AlmanacBindingEditEngine id(new PhabricatorTextEditField()) ->setKey('service') ->setLabel(pht('Service')) - ->setIsConduitOnly(true) + ->setIsFormField(false) ->setTransactionType( AlmanacBindingServiceTransaction::TRANSACTIONTYPE) ->setDescription(pht('Service to create a binding for.')) @@ -146,7 +146,7 @@ final class AlmanacBindingEditEngine id(new PhabricatorTextEditField()) ->setKey('interface') ->setLabel(pht('Interface')) - ->setIsConduitOnly(true) + ->setIsFormField(false) ->setTransactionType( AlmanacBindingInterfaceTransaction::TRANSACTIONTYPE) ->setDescription(pht('Interface to bind the service to.')) @@ -156,7 +156,7 @@ final class AlmanacBindingEditEngine id(new PhabricatorBoolEditField()) ->setKey('disabled') ->setLabel(pht('Disabled')) - ->setIsConduitOnly(true) + ->setIsFormField(false) ->setTransactionType( AlmanacBindingDisableTransaction::TRANSACTIONTYPE) ->setDescription(pht('Disable or enable the binding.')) diff --git a/src/applications/almanac/editor/AlmanacInterfaceEditEngine.php b/src/applications/almanac/editor/AlmanacInterfaceEditEngine.php index 30c371d6f9..ca57113bf2 100644 --- a/src/applications/almanac/editor/AlmanacInterfaceEditEngine.php +++ b/src/applications/almanac/editor/AlmanacInterfaceEditEngine.php @@ -150,7 +150,7 @@ final class AlmanacInterfaceEditEngine id(new PhabricatorTextEditField()) ->setKey('device') ->setLabel(pht('Device')) - ->setIsConduitOnly(true) + ->setIsFormField(false) ->setTransactionType( AlmanacInterfaceDeviceTransaction::TRANSACTIONTYPE) ->setDescription(pht('When creating an interface, set the device.')) diff --git a/src/applications/almanac/editor/AlmanacServiceEditEngine.php b/src/applications/almanac/editor/AlmanacServiceEditEngine.php index 00e54962b3..b63075543f 100644 --- a/src/applications/almanac/editor/AlmanacServiceEditEngine.php +++ b/src/applications/almanac/editor/AlmanacServiceEditEngine.php @@ -136,7 +136,7 @@ final class AlmanacServiceEditEngine id(new PhabricatorTextEditField()) ->setKey('type') ->setLabel(pht('Type')) - ->setIsConduitOnly(true) + ->setIsFormField(false) ->setTransactionType( AlmanacServiceTypeTransaction::TRANSACTIONTYPE) ->setDescription(pht('When creating a service, set the type.')) diff --git a/src/applications/almanac/engineextension/AlmanacPropertiesEditEngineExtension.php b/src/applications/almanac/engineextension/AlmanacPropertiesEditEngineExtension.php index 965c193f40..425ee04b3c 100644 --- a/src/applications/almanac/engineextension/AlmanacPropertiesEditEngineExtension.php +++ b/src/applications/almanac/engineextension/AlmanacPropertiesEditEngineExtension.php @@ -30,14 +30,14 @@ final class AlmanacPropertiesEditEngineExtension ->setConduitDescription( pht('Pass a map of values to set one or more properties.')) ->setConduitTypeDescription(pht('Map of property names to values.')) - ->setIsConduitOnly(true), + ->setIsFormField(false), id(new AlmanacDeletePropertyEditField()) ->setKey('property.delete') ->setTransactionType($object->getAlmanacPropertyDeleteTransactionType()) ->setConduitDescription( pht('Pass a list of property names to delete properties.')) ->setConduitTypeDescription(pht('List of property names.')) - ->setIsConduitOnly(true), + ->setIsFormField(false), ); } diff --git a/src/applications/badges/editor/PhabricatorBadgesEditEngine.php b/src/applications/badges/editor/PhabricatorBadgesEditEngine.php index e7a441343c..721184852c 100644 --- a/src/applications/badges/editor/PhabricatorBadgesEditEngine.php +++ b/src/applications/badges/editor/PhabricatorBadgesEditEngine.php @@ -126,7 +126,7 @@ final class PhabricatorBadgesEditEngine ->setValue($object->getDescription()), id(new PhabricatorUsersEditField()) ->setKey('award') - ->setIsConduitOnly(true) + ->setIsFormField(false) ->setDescription(pht('New badge award recipients.')) ->setConduitTypeDescription(pht('New badge award recipients.')) ->setTransactionType( @@ -134,7 +134,7 @@ final class PhabricatorBadgesEditEngine ->setLabel(pht('Award Recipients')), id(new PhabricatorUsersEditField()) ->setKey('revoke') - ->setIsConduitOnly(true) + ->setIsFormField(false) ->setDescription(pht('Revoke badge award recipients.')) ->setConduitTypeDescription(pht('Revoke badge award recipients.')) ->setTransactionType( diff --git a/src/applications/calendar/editor/PhabricatorCalendarEventEditEngine.php b/src/applications/calendar/editor/PhabricatorCalendarEventEditEngine.php index a9eab9376a..1b23b13fbf 100644 --- a/src/applications/calendar/editor/PhabricatorCalendarEventEditEngine.php +++ b/src/applications/calendar/editor/PhabricatorCalendarEventEditEngine.php @@ -148,7 +148,7 @@ final class PhabricatorCalendarEventEditEngine ->setDescription(pht('Cancel the event.')) ->setTransactionType( PhabricatorCalendarEventCancelTransaction::TRANSACTIONTYPE) - ->setIsConduitOnly(true) + ->setIsFormField(false) ->setConduitDescription(pht('Cancel or restore the event.')) ->setConduitTypeDescription(pht('True to cancel the event.')) ->setValue($object->getIsCancelled()), @@ -161,7 +161,7 @@ final class PhabricatorCalendarEventEditEngine ->setDescription(pht('Host of the event.')) ->setTransactionType( PhabricatorCalendarEventHostTransaction::TRANSACTIONTYPE) - ->setIsConduitOnly($this->getIsCreate()) + ->setIsFormField(!$this->getIsCreate()) ->setConduitDescription(pht('Change the host of the event.')) ->setConduitTypeDescription(pht('New event host.')) ->setSingleValue($object->getHostPHID()), diff --git a/src/applications/calendar/editor/PhabricatorCalendarExportEditEngine.php b/src/applications/calendar/editor/PhabricatorCalendarExportEditEngine.php index 8cd35e7323..bc8fc360c6 100644 --- a/src/applications/calendar/editor/PhabricatorCalendarExportEditEngine.php +++ b/src/applications/calendar/editor/PhabricatorCalendarExportEditEngine.php @@ -99,7 +99,7 @@ final class PhabricatorCalendarExportEditEngine ->setDescription(pht('Disable the export.')) ->setTransactionType( PhabricatorCalendarExportDisableTransaction::TRANSACTIONTYPE) - ->setIsConduitOnly(true) + ->setIsFormField(false) ->setConduitDescription(pht('Disable or restore the export.')) ->setConduitTypeDescription(pht('True to cancel the export.')) ->setValue($object->getIsDisabled()), diff --git a/src/applications/calendar/editor/PhabricatorCalendarImportEditEngine.php b/src/applications/calendar/editor/PhabricatorCalendarImportEditEngine.php index 90b4962ca9..7be3969671 100644 --- a/src/applications/calendar/editor/PhabricatorCalendarImportEditEngine.php +++ b/src/applications/calendar/editor/PhabricatorCalendarImportEditEngine.php @@ -101,7 +101,7 @@ final class PhabricatorCalendarImportEditEngine ->setDescription(pht('Disable the import.')) ->setTransactionType( PhabricatorCalendarImportDisableTransaction::TRANSACTIONTYPE) - ->setIsConduitOnly(true) + ->setIsFormField(false) ->setConduitDescription(pht('Disable or restore the import.')) ->setConduitTypeDescription(pht('True to cancel the import.')) ->setValue($object->getIsDisabled()), @@ -111,7 +111,7 @@ final class PhabricatorCalendarImportEditEngine ->setDescription(pht('Delete all events from this source.')) ->setTransactionType( PhabricatorCalendarImportDisableTransaction::TRANSACTIONTYPE) - ->setIsConduitOnly(true) + ->setIsFormField(false) ->setConduitDescription(pht('Disable or restore the import.')) ->setConduitTypeDescription(pht('True to delete imported events.')) ->setValue(false), @@ -121,7 +121,7 @@ final class PhabricatorCalendarImportEditEngine ->setDescription(pht('Reload events imported from this source.')) ->setTransactionType( PhabricatorCalendarImportDisableTransaction::TRANSACTIONTYPE) - ->setIsConduitOnly(true) + ->setIsFormField(false) ->setConduitDescription(pht('Disable or restore the import.')) ->setConduitTypeDescription(pht('True to reload the import.')) ->setValue(false), diff --git a/src/applications/conpherence/editor/ConpherenceEditEngine.php b/src/applications/conpherence/editor/ConpherenceEditEngine.php index 91cd8fd081..f5f850e637 100644 --- a/src/applications/conpherence/editor/ConpherenceEditEngine.php +++ b/src/applications/conpherence/editor/ConpherenceEditEngine.php @@ -76,8 +76,8 @@ final class ConpherenceEditEngine $initial_phids = $participant_phids; } - // Only show participants on create or conduit, not edit - $conduit_only = !$this->getIsCreate(); + // Only show participants on create or conduit, not edit. + $show_participants = (bool)$this->getIsCreate(); return array( id(new PhabricatorTextEditField()) @@ -103,7 +103,7 @@ final class ConpherenceEditEngine ->setKey('participants') ->setValue($participant_phids) ->setInitialValue($initial_phids) - ->setIsConduitOnly($conduit_only) + ->setIsFormField($show_participants) ->setAliases(array('users', 'members', 'participants', 'userPHID')) ->setDescription(pht('Room participants.')) ->setUseEdgeTransactions(true) diff --git a/src/applications/differential/editor/DifferentialRevisionEditEngine.php b/src/applications/differential/editor/DifferentialRevisionEditEngine.php index 0404bd6201..ffae7fab16 100644 --- a/src/applications/differential/editor/DifferentialRevisionEditEngine.php +++ b/src/applications/differential/editor/DifferentialRevisionEditEngine.php @@ -138,7 +138,7 @@ final class DifferentialRevisionEditEngine DifferentialRevisionUpdateTransaction::TRANSACTIONTYPE) ->setHandleParameterType(new AphrontPHIDListHTTPParameterType()) ->setSingleValue($diff_phid) - ->setIsConduitOnly(!$diff) + ->setIsFormField((bool)$diff) ->setIsReorderable(false) ->setIsDefaultable(false) ->setIsInvisible(true) @@ -225,7 +225,7 @@ final class DifferentialRevisionEditEngine $fields[] = id(new PhabricatorHandlesEditField()) ->setKey('tasks') ->setUseEdgeTransactions(true) - ->setIsConduitOnly(true) + ->setIsFormField(false) ->setTransactionType(PhabricatorTransactions::TYPE_EDGE) ->setMetadataValue( 'edge:type', @@ -245,7 +245,7 @@ final class DifferentialRevisionEditEngine $fields[] = id(new PhabricatorBoolEditField()) ->setKey('draft') ->setLabel(pht('Hold as Draft')) - ->setIsConduitOnly(true) + ->setIsFormField(false) ->setOptions( pht('Autosubmit Once Builds Finish'), pht('Hold as Draft')) diff --git a/src/applications/diffusion/editor/DiffusionRepositoryEditEngine.php b/src/applications/diffusion/editor/DiffusionRepositoryEditEngine.php index dcb79a6c86..79b97e44be 100644 --- a/src/applications/diffusion/editor/DiffusionRepositoryEditEngine.php +++ b/src/applications/diffusion/editor/DiffusionRepositoryEditEngine.php @@ -244,7 +244,7 @@ final class DiffusionRepositoryEditEngine ->setKey('vcs') ->setLabel(pht('Version Control System')) ->setTransactionType(PhabricatorRepositoryTransaction::TYPE_VCS) - ->setIsConduitOnly(true) + ->setIsFormField(false) ->setIsCopyable(true) ->setOptions(PhabricatorRepositoryType::getAllRepositoryTypes()) ->setDescription(pht('Underlying repository version control system.')) @@ -300,7 +300,7 @@ final class DiffusionRepositoryEditEngine ->setKey('allowDangerousChanges') ->setLabel(pht('Allow Dangerous Changes')) ->setIsCopyable(true) - ->setIsConduitOnly(true) + ->setIsFormField(false) ->setOptions( pht('Prevent Dangerous Changes'), pht('Allow Dangerous Changes')) @@ -313,7 +313,7 @@ final class DiffusionRepositoryEditEngine ->setKey('allowEnormousChanges') ->setLabel(pht('Allow Enormous Changes')) ->setIsCopyable(true) - ->setIsConduitOnly(true) + ->setIsFormField(false) ->setOptions( pht('Prevent Enormous Changes'), pht('Allow Enormous Changes')) @@ -326,7 +326,7 @@ final class DiffusionRepositoryEditEngine ->setKey('status') ->setLabel(pht('Status')) ->setTransactionType(PhabricatorRepositoryTransaction::TYPE_ACTIVATE) - ->setIsConduitOnly(true) + ->setIsFormField(false) ->setOptions(PhabricatorRepository::getStatusNameMap()) ->setDescription(pht('Active or inactive status.')) ->setConduitDescription(pht('Active or deactivate the repository.')) diff --git a/src/applications/diffusion/editor/DiffusionURIEditEngine.php b/src/applications/diffusion/editor/DiffusionURIEditEngine.php index fdc91cff5f..dbc1238153 100644 --- a/src/applications/diffusion/editor/DiffusionURIEditEngine.php +++ b/src/applications/diffusion/editor/DiffusionURIEditEngine.php @@ -150,7 +150,7 @@ final class DiffusionURIEditEngine ->setAliases(array('repositoryPHID')) ->setLabel(pht('Repository')) ->setIsRequired(true) - ->setIsConduitOnly(true) + ->setIsFormField(false) ->setTransactionType( PhabricatorRepositoryURITransaction::TYPE_REPOSITORY) ->setDescription(pht('The repository this URI is associated with.')) @@ -195,7 +195,7 @@ final class DiffusionURIEditEngine ->setKey('credential') ->setAliases(array('credentialPHID')) ->setLabel(pht('Credential')) - ->setIsConduitOnly(true) + ->setIsFormField(false) ->setTransactionType( PhabricatorRepositoryURITransaction::TYPE_CREDENTIAL) ->setDescription( @@ -206,7 +206,7 @@ final class DiffusionURIEditEngine id(new PhabricatorBoolEditField()) ->setKey('disable') ->setLabel(pht('Disabled')) - ->setIsConduitOnly(true) + ->setIsFormField(false) ->setTransactionType(PhabricatorRepositoryURITransaction::TYPE_DISABLE) ->setDescription(pht('Active status of the URI.')) ->setConduitDescription(pht('Disable or activate the URI.')) diff --git a/src/applications/drydock/editor/DrydockBlueprintEditEngine.php b/src/applications/drydock/editor/DrydockBlueprintEditEngine.php index 6bd1366ef0..a3b6fdaf75 100644 --- a/src/applications/drydock/editor/DrydockBlueprintEditEngine.php +++ b/src/applications/drydock/editor/DrydockBlueprintEditEngine.php @@ -153,7 +153,7 @@ final class DrydockBlueprintEditEngine id(new PhabricatorTextEditField()) ->setKey('type') ->setLabel(pht('Type')) - ->setIsConduitOnly(true) + ->setIsFormField(false) ->setTransactionType( DrydockBlueprintTypeTransaction::TRANSACTIONTYPE) ->setDescription(pht('When creating a blueprint, set the type.')) diff --git a/src/applications/maniphest/editor/ManiphestEditEngine.php b/src/applications/maniphest/editor/ManiphestEditEngine.php index c270104034..0b0b4d6758 100644 --- a/src/applications/maniphest/editor/ManiphestEditEngine.php +++ b/src/applications/maniphest/editor/ManiphestEditEngine.php @@ -169,9 +169,7 @@ EODOCS ->setConduitDocumentation($column_documentation) ->setAliases(array('columnPHID', 'columns', 'columnPHIDs')) ->setTransactionType(PhabricatorTransactions::TYPE_COLUMNS) - ->setIsReorderable(false) - ->setIsDefaultable(false) - ->setIsLockable(false) + ->setIsFormField(false) ->setCommentActionLabel(pht('Move on Workboard')) ->setCommentActionOrder(2000) ->setColumnMap($column_map), @@ -291,7 +289,7 @@ EODOCS ->setConduitDescription(pht('Change the parents of this task.')) ->setConduitTypeDescription(pht('List of parent task PHIDs.')) ->setUseEdgeTransactions(true) - ->setIsConduitOnly(true) + ->setIsFormField(false) ->setTransactionType(PhabricatorTransactions::TYPE_EDGE) ->setMetadataValue('edge:type', $parent_type) ->setValue($parent_phids); @@ -303,7 +301,7 @@ EODOCS ->setConduitDescription(pht('Change the subtasks of this task.')) ->setConduitTypeDescription(pht('List of subtask PHIDs.')) ->setUseEdgeTransactions(true) - ->setIsConduitOnly(true) + ->setIsFormField(false) ->setTransactionType(PhabricatorTransactions::TYPE_EDGE) ->setMetadataValue('edge:type', $subtask_type) ->setValue($parent_phids); diff --git a/src/applications/owners/editor/PhabricatorOwnersPackageEditEngine.php b/src/applications/owners/editor/PhabricatorOwnersPackageEditEngine.php index d2eee5685e..044cb8beda 100644 --- a/src/applications/owners/editor/PhabricatorOwnersPackageEditEngine.php +++ b/src/applications/owners/editor/PhabricatorOwnersPackageEditEngine.php @@ -159,7 +159,7 @@ EOTEXT ->setDescription(pht('Archive or enable the package.')) ->setTransactionType( PhabricatorOwnersPackageStatusTransaction::TRANSACTIONTYPE) - ->setIsConduitOnly(true) + ->setIsFormField(false) ->setValue($object->getStatus()) ->setOptions($object->getStatusNameMap()), id(new PhabricatorCheckboxesEditField()) @@ -176,7 +176,7 @@ EOTEXT id(new PhabricatorConduitEditField()) ->setKey('paths.set') ->setLabel(pht('Paths')) - ->setIsConduitOnly(true) + ->setIsFormField(false) ->setTransactionType( PhabricatorOwnersPackagePathsTransaction::TRANSACTIONTYPE) ->setConduitDescription( diff --git a/src/applications/paste/editor/PhabricatorPasteEditEngine.php b/src/applications/paste/editor/PhabricatorPasteEditEngine.php index 5578a7c9f6..146565e87e 100644 --- a/src/applications/paste/editor/PhabricatorPasteEditEngine.php +++ b/src/applications/paste/editor/PhabricatorPasteEditEngine.php @@ -104,7 +104,7 @@ final class PhabricatorPasteEditEngine ->setLabel(pht('Status')) ->setTransactionType( PhabricatorPasteStatusTransaction::TRANSACTIONTYPE) - ->setIsConduitOnly(true) + ->setIsFormField(false) ->setOptions(PhabricatorPaste::getStatusNameMap()) ->setDescription(pht('Active or archived status.')) ->setConduitDescription(pht('Active or archive the paste.')) diff --git a/src/applications/people/editor/PhabricatorUserEditEngine.php b/src/applications/people/editor/PhabricatorUserEditEngine.php index c547426b12..c4c1abf1e3 100644 --- a/src/applications/people/editor/PhabricatorUserEditEngine.php +++ b/src/applications/people/editor/PhabricatorUserEditEngine.php @@ -71,7 +71,7 @@ final class PhabricatorUserEditEngine ->setLabel(pht('Disabled')) ->setDescription(pht('Disable the user.')) ->setTransactionType(PhabricatorUserDisableTransaction::TRANSACTIONTYPE) - ->setIsConduitOnly(true) + ->setIsFormField(false) ->setConduitDescription(pht('Disable or enable the user.')) ->setConduitTypeDescription(pht('True to disable the user.')) ->setValue($object->getIsDisabled()), diff --git a/src/applications/phame/editor/PhameBlogEditEngine.php b/src/applications/phame/editor/PhameBlogEditEngine.php index 9b6be308c4..e11dec6847 100644 --- a/src/applications/phame/editor/PhameBlogEditEngine.php +++ b/src/applications/phame/editor/PhameBlogEditEngine.php @@ -126,7 +126,7 @@ final class PhameBlogEditEngine ->setKey('status') ->setLabel(pht('Status')) ->setTransactionType(PhameBlogStatusTransaction::TRANSACTIONTYPE) - ->setIsConduitOnly(true) + ->setIsFormField(false) ->setOptions(PhameBlog::getStatusNameMap()) ->setDescription(pht('Active or archived status.')) ->setConduitDescription(pht('Active or archive the blog.')) diff --git a/src/applications/project/engine/PhabricatorProjectEditEngine.php b/src/applications/project/engine/PhabricatorProjectEditEngine.php index 5eb6b49b87..1c84932656 100644 --- a/src/applications/project/engine/PhabricatorProjectEditEngine.php +++ b/src/applications/project/engine/PhabricatorProjectEditEngine.php @@ -287,13 +287,13 @@ final class PhabricatorProjectEditEngine // Show this on the web UI when creating a project, but not when editing // one. It is always available via Conduit. - $conduit_only = !$this->getIsCreate(); + $show_field = (bool)$this->getIsCreate(); $members_field = id(new PhabricatorUsersEditField()) ->setKey('members') ->setAliases(array('memberPHIDs')) ->setLabel(pht('Initial Members')) - ->setIsConduitOnly($conduit_only) + ->setIsFormField($show_field) ->setUseEdgeTransactions(true) ->setTransactionType(PhabricatorTransactions::TYPE_EDGE) ->setMetadataValue( diff --git a/src/applications/transactions/controller/PhabricatorEditEngineConfigurationDefaultsController.php b/src/applications/transactions/controller/PhabricatorEditEngineConfigurationDefaultsController.php index 340431dd19..f7361d50cf 100644 --- a/src/applications/transactions/controller/PhabricatorEditEngineConfigurationDefaultsController.php +++ b/src/applications/transactions/controller/PhabricatorEditEngineConfigurationDefaultsController.php @@ -30,6 +30,11 @@ final class PhabricatorEditEngineConfigurationDefaultsController $fields = $engine->getFieldsForConfig($config); foreach ($fields as $key => $field) { + if (!$field->getIsFormField()) { + unset($fields[$key]); + continue; + } + if (!$field->getIsDefaultable()) { unset($fields[$key]); continue; diff --git a/src/applications/transactions/controller/PhabricatorEditEngineConfigurationLockController.php b/src/applications/transactions/controller/PhabricatorEditEngineConfigurationLockController.php index 790eaccb47..34b099b9f0 100644 --- a/src/applications/transactions/controller/PhabricatorEditEngineConfigurationLockController.php +++ b/src/applications/transactions/controller/PhabricatorEditEngineConfigurationLockController.php @@ -79,6 +79,10 @@ EOTEXT ); foreach ($fields as $field) { + if (!$field->getIsFormField()) { + continue; + } + if (!$field->getIsLockable()) { continue; } diff --git a/src/applications/transactions/controller/PhabricatorEditEngineConfigurationReorderController.php b/src/applications/transactions/controller/PhabricatorEditEngineConfigurationReorderController.php index 15eb9530fd..6ff36cdfa4 100644 --- a/src/applications/transactions/controller/PhabricatorEditEngineConfigurationReorderController.php +++ b/src/applications/transactions/controller/PhabricatorEditEngineConfigurationReorderController.php @@ -62,6 +62,10 @@ final class PhabricatorEditEngineConfigurationReorderController $key_order = array(); foreach ($fields as $field) { + if (!$field->getIsFormField()) { + continue; + } + if (!$field->getIsReorderable()) { continue; } diff --git a/src/applications/transactions/editengine/PhabricatorEditEngine.php b/src/applications/transactions/editengine/PhabricatorEditEngine.php index 579b53a989..4e189df164 100644 --- a/src/applications/transactions/editengine/PhabricatorEditEngine.php +++ b/src/applications/transactions/editengine/PhabricatorEditEngine.php @@ -1289,6 +1289,10 @@ abstract class PhabricatorEditEngine } foreach ($fields as $field) { + if (!$field->getIsFormField()) { + continue; + } + $field->appendToForm($form); } diff --git a/src/applications/transactions/editfield/PhabricatorEditField.php b/src/applications/transactions/editfield/PhabricatorEditField.php index 07bf3589a8..7eafbc60cf 100644 --- a/src/applications/transactions/editfield/PhabricatorEditField.php +++ b/src/applications/transactions/editfield/PhabricatorEditField.php @@ -44,7 +44,7 @@ abstract class PhabricatorEditField extends Phobject { private $isDefaultable = true; private $isLockable = true; private $isCopyable = false; - private $isConduitOnly = false; + private $isFormField = true; private $conduitEditTypes; private $bulkEditTypes; @@ -139,13 +139,13 @@ abstract class PhabricatorEditField extends Phobject { return $this->isReorderable; } - public function setIsConduitOnly($is_conduit_only) { - $this->isConduitOnly = $is_conduit_only; + public function setIsFormField($is_form_field) { + $this->isFormField = $is_form_field; return $this; } - public function getIsConduitOnly() { - return $this->isConduitOnly; + public function getIsFormField() { + return $this->isFormField; } public function setDescription($description) { @@ -336,7 +336,7 @@ abstract class PhabricatorEditField extends Phobject { } protected function buildControl() { - if ($this->getIsConduitOnly()) { + if (!$this->getIsFormField()) { return null; } @@ -637,7 +637,7 @@ abstract class PhabricatorEditField extends Phobject { } final public function getHTTPParameterType() { - if ($this->getIsConduitOnly()) { + if (!$this->getIsFormField()) { return null; } @@ -840,7 +840,7 @@ abstract class PhabricatorEditField extends Phobject { } public function shouldGenerateTransactionsFromSubmit() { - if ($this->getIsConduitOnly()) { + if (!$this->getIsFormField()) { return false; } @@ -853,7 +853,7 @@ abstract class PhabricatorEditField extends Phobject { } public function shouldReadValueFromRequest() { - if ($this->getIsConduitOnly()) { + if (!$this->getIsFormField()) { return false; } @@ -869,7 +869,7 @@ abstract class PhabricatorEditField extends Phobject { } public function shouldReadValueFromSubmit() { - if ($this->getIsConduitOnly()) { + if (!$this->getIsFormField()) { return false; } @@ -885,7 +885,7 @@ abstract class PhabricatorEditField extends Phobject { } public function shouldGenerateTransactionsFromComment() { - if ($this->getIsConduitOnly()) { + if (!$this->getCommentActionLabel()) { return false; } diff --git a/src/applications/transactions/engineextension/PhabricatorCommentEditEngineExtension.php b/src/applications/transactions/engineextension/PhabricatorCommentEditEngineExtension.php index c5da130b68..0d20533798 100644 --- a/src/applications/transactions/engineextension/PhabricatorCommentEditEngineExtension.php +++ b/src/applications/transactions/engineextension/PhabricatorCommentEditEngineExtension.php @@ -57,10 +57,7 @@ final class PhabricatorCommentEditEngineExtension ->setBulkEditLabel(pht('Add comment')) ->setBulkEditGroupKey('comments') ->setAliases(array('comments')) - ->setIsHidden(true) - ->setIsReorderable(false) - ->setIsDefaultable(false) - ->setIsLockable(false) + ->setIsFormField(false) ->setCanApplyWithoutEditCapability($is_interact) ->setTransactionType($comment_type) ->setConduitDescription(pht('Make comments.')) diff --git a/src/applications/transactions/engineextension/PhabricatorSubtypeEditEngineExtension.php b/src/applications/transactions/engineextension/PhabricatorSubtypeEditEngineExtension.php index 338702478c..7d32545416 100644 --- a/src/applications/transactions/engineextension/PhabricatorSubtypeEditEngineExtension.php +++ b/src/applications/transactions/engineextension/PhabricatorSubtypeEditEngineExtension.php @@ -36,20 +36,20 @@ final class PhabricatorSubtypeEditEngineExtension $subtype_field = id(new PhabricatorSelectEditField()) ->setKey(self::EDITKEY) ->setLabel(pht('Subtype')) - ->setIsConduitOnly(true) - ->setIsHidden(true) - ->setIsReorderable(false) - ->setIsDefaultable(false) - ->setIsLockable(false) + ->setIsFormField(false) ->setTransactionType($subtype_type) ->setConduitDescription(pht('Change the object subtype.')) ->setConduitTypeDescription(pht('New object subtype key.')) ->setValue($object->getEditEngineSubtype()) ->setOptions($options); - // If subtypes are configured, enable changing them from the bulk editor. + // If subtypes are configured, enable changing them from the bulk editor + // and comment action stack. if (count($map) > 1) { - $subtype_field->setBulkEditLabel(pht('Change subtype to')); + $subtype_field + ->setBulkEditLabel(pht('Change subtype to')) + ->setCommentActionLabel(pht('Change Subtype')) + ->setCommentActionOrder(3000); } return array( diff --git a/src/applications/transactions/storage/PhabricatorEditEngineConfiguration.php b/src/applications/transactions/storage/PhabricatorEditEngineConfiguration.php index ada979c45c..3a1c8ec60b 100644 --- a/src/applications/transactions/storage/PhabricatorEditEngineConfiguration.php +++ b/src/applications/transactions/storage/PhabricatorEditEngineConfiguration.php @@ -139,9 +139,14 @@ final class PhabricatorEditEngineConfiguration $values = $this->getProperty('defaults', array()); foreach ($fields as $key => $field) { + if (!$field->getIsFormField()) { + continue; + } + if (!$field->getIsDefaultable()) { continue; } + if ($is_new) { if (array_key_exists($key, $values)) { $field->readDefaultValueFromConfiguration($values[$key]); diff --git a/src/infrastructure/customfield/editor/PhabricatorCustomFieldEditField.php b/src/infrastructure/customfield/editor/PhabricatorCustomFieldEditField.php index 27b8276c85..1a63e040e2 100644 --- a/src/infrastructure/customfield/editor/PhabricatorCustomFieldEditField.php +++ b/src/infrastructure/customfield/editor/PhabricatorCustomFieldEditField.php @@ -59,7 +59,7 @@ final class PhabricatorCustomFieldEditField } protected function buildControl() { - if ($this->getIsConduitOnly()) { + if (!$this->getIsFormField()) { return null; } diff --git a/src/infrastructure/customfield/field/PhabricatorCustomField.php b/src/infrastructure/customfield/field/PhabricatorCustomField.php index 36db8f239b..d7df3c5b78 100644 --- a/src/infrastructure/customfield/field/PhabricatorCustomField.php +++ b/src/infrastructure/customfield/field/PhabricatorCustomField.php @@ -1145,10 +1145,10 @@ abstract class PhabricatorCustomField extends Phobject { return $this->proxy->newStandardEditField(); } - if (!$this->shouldAppearInEditView()) { - $conduit_only = true; + if ($this->shouldAppearInEditView()) { + $form_field = true; } else { - $conduit_only = false; + $form_field = false; } $bulk_label = $this->getBulkEditLabel(); @@ -1160,7 +1160,7 @@ abstract class PhabricatorCustomField extends Phobject { ->setBulkEditLabel($bulk_label) ->setDescription($this->getFieldDescription()) ->setTransactionType($this->getApplicationTransactionType()) - ->setIsConduitOnly($conduit_only) + ->setIsFormField($form_field) ->setValue($this->getNewValueForApplicationTransactions()); } From 01c7be059dd6fd806dd2f1fcea7f0d602b6ca43c Mon Sep 17 00:00:00 2001 From: epriestley Date: Mon, 26 Nov 2018 06:25:22 -0800 Subject: [PATCH 37/43] Add support for "harbormaster.target.search" Summary: Ref T13222. See PHI986. See PHI896. Harbormaster build targets don't currently have a modern "*.search" API, but there's no reason not to provide one (even if some of the use cases are a little bit questionable). Test Plan: {F6032423} Reviewers: amckinley Reviewed By: amckinley Subscribers: PHID-OPKG-gm6ozazyms6q6i22gyam Maniphest Tasks: T13222 Differential Revision: https://secure.phabricator.com/D19841 --- src/__phutil_library_map__.php | 5 ++ .../HarbormasterTargetSearchAPIMethod.php | 18 ++++ .../HarbormasterBuildTargetSearchEngine.php | 73 ++++++++++++++++ .../storage/build/HarbormasterBuildTarget.php | 84 ++++++++++++++++++- 4 files changed, 179 insertions(+), 1 deletion(-) create mode 100644 src/applications/harbormaster/conduit/HarbormasterTargetSearchAPIMethod.php create mode 100644 src/applications/harbormaster/query/HarbormasterBuildTargetSearchEngine.php diff --git a/src/__phutil_library_map__.php b/src/__phutil_library_map__.php index e02178bc28..467a934211 100644 --- a/src/__phutil_library_map__.php +++ b/src/__phutil_library_map__.php @@ -1353,6 +1353,7 @@ phutil_register_library_map(array( 'HarbormasterBuildTarget' => 'applications/harbormaster/storage/build/HarbormasterBuildTarget.php', 'HarbormasterBuildTargetPHIDType' => 'applications/harbormaster/phid/HarbormasterBuildTargetPHIDType.php', 'HarbormasterBuildTargetQuery' => 'applications/harbormaster/query/HarbormasterBuildTargetQuery.php', + 'HarbormasterBuildTargetSearchEngine' => 'applications/harbormaster/query/HarbormasterBuildTargetSearchEngine.php', 'HarbormasterBuildTransaction' => 'applications/harbormaster/storage/HarbormasterBuildTransaction.php', 'HarbormasterBuildTransactionEditor' => 'applications/harbormaster/editor/HarbormasterBuildTransactionEditor.php', 'HarbormasterBuildTransactionQuery' => 'applications/harbormaster/query/HarbormasterBuildTransactionQuery.php', @@ -1433,6 +1434,7 @@ phutil_register_library_map(array( 'HarbormasterStepEditController' => 'applications/harbormaster/controller/HarbormasterStepEditController.php', 'HarbormasterStepViewController' => 'applications/harbormaster/controller/HarbormasterStepViewController.php', 'HarbormasterTargetEngine' => 'applications/harbormaster/engine/HarbormasterTargetEngine.php', + 'HarbormasterTargetSearchAPIMethod' => 'applications/harbormaster/conduit/HarbormasterTargetSearchAPIMethod.php', 'HarbormasterTargetWorker' => 'applications/harbormaster/worker/HarbormasterTargetWorker.php', 'HarbormasterTestBuildStepGroup' => 'applications/harbormaster/stepgroup/HarbormasterTestBuildStepGroup.php', 'HarbormasterThrowExceptionBuildStep' => 'applications/harbormaster/step/HarbormasterThrowExceptionBuildStep.php', @@ -6832,9 +6834,11 @@ phutil_register_library_map(array( 'HarbormasterDAO', 'PhabricatorPolicyInterface', 'PhabricatorDestructibleInterface', + 'PhabricatorConduitResultInterface', ), 'HarbormasterBuildTargetPHIDType' => 'PhabricatorPHIDType', 'HarbormasterBuildTargetQuery' => 'PhabricatorCursorPagedPolicyAwareQuery', + 'HarbormasterBuildTargetSearchEngine' => 'PhabricatorApplicationSearchEngine', 'HarbormasterBuildTransaction' => 'PhabricatorApplicationTransaction', 'HarbormasterBuildTransactionEditor' => 'PhabricatorApplicationTransactionEditor', 'HarbormasterBuildTransactionQuery' => 'PhabricatorApplicationTransactionQuery', @@ -6918,6 +6922,7 @@ phutil_register_library_map(array( 'HarbormasterStepEditController' => 'HarbormasterPlanController', 'HarbormasterStepViewController' => 'HarbormasterPlanController', 'HarbormasterTargetEngine' => 'Phobject', + 'HarbormasterTargetSearchAPIMethod' => 'PhabricatorSearchEngineAPIMethod', 'HarbormasterTargetWorker' => 'HarbormasterWorker', 'HarbormasterTestBuildStepGroup' => 'HarbormasterBuildStepGroup', 'HarbormasterThrowExceptionBuildStep' => 'HarbormasterBuildStepImplementation', diff --git a/src/applications/harbormaster/conduit/HarbormasterTargetSearchAPIMethod.php b/src/applications/harbormaster/conduit/HarbormasterTargetSearchAPIMethod.php new file mode 100644 index 0000000000..011ca2a47b --- /dev/null +++ b/src/applications/harbormaster/conduit/HarbormasterTargetSearchAPIMethod.php @@ -0,0 +1,18 @@ +setLabel(pht('Builds')) + ->setKey('buildPHIDs') + ->setAliases(array('build', 'builds', 'buildPHID')) + ->setDescription( + pht('Search for targets of a given build.')) + ->setDatasource(new HarbormasterBuildPlanDatasource()), + ); + } + + protected function buildQueryFromParameters(array $map) { + $query = $this->newQuery(); + + if ($map['buildPHIDs']) { + $query->withBuildPHIDs($map['buildPHIDs']); + } + + return $query; + } + + protected function getURI($path) { + return '/harbormaster/target/'.$path; + } + + protected function getBuiltinQueryNames() { + return array( + 'all' => pht('All Targets'), + ); + } + + 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 $builds, + PhabricatorSavedQuery $query, + array $handles) { + assert_instances_of($builds, 'HarbormasterBuildTarget'); + + // Currently, this only supports the "harbormaster.target.search" + // API method. + throw new PhutilMethodNotImplementedException(); + } + +} diff --git a/src/applications/harbormaster/storage/build/HarbormasterBuildTarget.php b/src/applications/harbormaster/storage/build/HarbormasterBuildTarget.php index b559a66198..30b1bd79e4 100644 --- a/src/applications/harbormaster/storage/build/HarbormasterBuildTarget.php +++ b/src/applications/harbormaster/storage/build/HarbormasterBuildTarget.php @@ -4,7 +4,8 @@ final class HarbormasterBuildTarget extends HarbormasterDAO implements PhabricatorPolicyInterface, - PhabricatorDestructibleInterface { + PhabricatorDestructibleInterface, + PhabricatorConduitResultInterface { protected $name; protected $buildPHID; @@ -412,4 +413,85 @@ final class HarbormasterBuildTarget } +/* -( PhabricatorConduitResultInterface )---------------------------------- */ + + + public function getFieldSpecificationsForConduit() { + return array( + id(new PhabricatorConduitSearchFieldSpecification()) + ->setKey('name') + ->setType('string') + ->setDescription(pht('The name of the build target.')), + id(new PhabricatorConduitSearchFieldSpecification()) + ->setKey('buildPHID') + ->setType('phid') + ->setDescription(pht('The build the target is associated with.')), + id(new PhabricatorConduitSearchFieldSpecification()) + ->setKey('buildStepPHID') + ->setType('phid') + ->setDescription(pht('The build step the target runs.')), + id(new PhabricatorConduitSearchFieldSpecification()) + ->setKey('status') + ->setType('map') + ->setDescription(pht('Status for the build target.')), + id(new PhabricatorConduitSearchFieldSpecification()) + ->setKey('epochStarted') + ->setType('epoch?') + ->setDescription( + pht( + 'Epoch timestamp for target start, if the target '. + 'has started.')), + id(new PhabricatorConduitSearchFieldSpecification()) + ->setKey('epochCompleted') + ->setType('epoch?') + ->setDescription( + pht( + 'Epoch timestamp for target completion, if the target '. + 'has completed.')), + id(new PhabricatorConduitSearchFieldSpecification()) + ->setKey('buildGeneration') + ->setType('int') + ->setDescription( + pht( + 'Build generation this target belongs to. When builds '. + 'restart, a new generation with new targets is created.')), + ); + } + + public function getFieldValuesForConduit() { + $status = $this->getTargetStatus(); + + $epoch_started = $this->getDateStarted(); + if ($epoch_started) { + $epoch_started = (int)$epoch_started; + } else { + $epoch_started = null; + } + + $epoch_completed = $this->getDateCompleted(); + if ($epoch_completed) { + $epoch_completed = (int)$epoch_completed; + } else { + $epoch_completed = null; + } + + return array( + 'name' => $this->getName(), + 'buildPHID' => $this->getBuildPHID(), + 'buildStepPHID' => $this->getBuildStepPHID(), + 'status' => array( + 'value' => $status, + 'name' => self::getBuildTargetStatusName($status), + ), + 'epochStarted' => $epoch_started, + 'epochCompleted' => $epoch_completed, + 'buildGeneration' => (int)$this->getBuildGeneration(), + ); + } + + public function getConduitSearchAttachments() { + return array(); + } + + } From c457d23a1d30f81ff96f1fa0c0c90550a1d49b53 Mon Sep 17 00:00:00 2001 From: epriestley Date: Sat, 24 Nov 2018 06:41:34 -0800 Subject: [PATCH 38/43] Tailor the "no reviewers on this revision" warnings to handle the case where all reviewers have resigned Summary: Ref T13216. See PHI985. We currently use a banner to warn you when a revision has no reviewers or only disabled users, but since the changes to track "Resign" more explicilty we'll no longer warn you if everyone has resigned. (Previously, they'd no longer be reviewers, so you'd end up with the "no reviewers are assigned" warning if everyone resigned.) This can still interact slightly oddly with some states (e.g., only a package or project reviewer) but I'd like to wait for T731 to tighten those cases up, and they're more advanced/unusual. Test Plan: {F6026832} {F6026833} {F6026834} Reviewers: amckinley Reviewed By: amckinley Maniphest Tasks: T13216 Differential Revision: https://secure.phabricator.com/D19834 --- .../DifferentialReviewersField.php | 25 +++++++++++++++---- 1 file changed, 20 insertions(+), 5 deletions(-) diff --git a/src/applications/differential/customfield/DifferentialReviewersField.php b/src/applications/differential/customfield/DifferentialReviewersField.php index f835854e2f..31b175b09d 100644 --- a/src/applications/differential/customfield/DifferentialReviewersField.php +++ b/src/applications/differential/customfield/DifferentialReviewersField.php @@ -72,20 +72,35 @@ final class DifferentialReviewersField return array(); } + $all_resigned = true; + $all_disabled = true; + $any_reviewers = false; + foreach ($this->getValue() as $reviewer) { - if (!$handles[$reviewer->getReviewerPHID()]->isDisabled()) { - return array(); + $reviewer_phid = $reviewer->getReviewerPHID(); + + $any_reviewers = true; + + if (!$handles[$reviewer_phid]->isDisabled()) { + $all_disabled = false; + } + + if (!$reviewer->isResigned()) { + $all_resigned = false; } } $warnings = array(); - if ($this->getValue()) { + if (!$any_reviewers) { + $warnings[] = pht( + 'This revision needs review, but there are no reviewers specified.'); + } else if ($all_disabled) { $warnings[] = pht( 'This revision needs review, but all specified reviewers are '. 'disabled or inactive.'); - } else { + } else if ($all_resigned) { $warnings[] = pht( - 'This revision needs review, but there are no reviewers specified.'); + 'This revision needs review, but all reviewers have resigned.'); } return $warnings; From c25d2a399d68ecbfbb23c508bc7e3c18b1f94c57 Mon Sep 17 00:00:00 2001 From: epriestley Date: Wed, 21 Nov 2018 11:35:08 -0800 Subject: [PATCH 39/43] Separate the repository management UI into sections Summary: Depends on D19826. Ref T13216. We have a fair number of options here; add some groups so the "Build" stuff can go in a little subcategory and such. Test Plan: {F6020896} Reviewers: amckinley Reviewed By: amckinley Maniphest Tasks: T13216 Differential Revision: https://secure.phabricator.com/D19827 --- src/__phutil_library_map__.php | 10 ++++ ...fusionRepositoryManagePanelsController.php | 49 ++++++++++++++----- ...ionRepositoryAutomationManagementPanel.php | 4 ++ ...onRepositoryManagementBuildsPanelGroup.php | 16 ++++++ ...sitoryManagementIntegrationsPanelGroup.php | 16 ++++++ ...sionRepositoryManagementMainPanelGroup.php | 16 ++++++ ...ionRepositoryManagementOtherPanelGroup.php | 16 ++++++ .../DiffusionRepositoryManagementPanel.php | 4 +- ...iffusionRepositoryManagementPanelGroup.php | 21 ++++++++ ...fusionRepositoryStagingManagementPanel.php | 4 ++ ...fusionRepositorySymbolsManagementPanel.php | 4 ++ 11 files changed, 146 insertions(+), 14 deletions(-) create mode 100644 src/applications/diffusion/management/DiffusionRepositoryManagementBuildsPanelGroup.php create mode 100644 src/applications/diffusion/management/DiffusionRepositoryManagementIntegrationsPanelGroup.php create mode 100644 src/applications/diffusion/management/DiffusionRepositoryManagementMainPanelGroup.php create mode 100644 src/applications/diffusion/management/DiffusionRepositoryManagementOtherPanelGroup.php create mode 100644 src/applications/diffusion/management/DiffusionRepositoryManagementPanelGroup.php diff --git a/src/__phutil_library_map__.php b/src/__phutil_library_map__.php index 467a934211..5ec0932b3b 100644 --- a/src/__phutil_library_map__.php +++ b/src/__phutil_library_map__.php @@ -952,7 +952,12 @@ phutil_register_library_map(array( 'DiffusionRepositoryListController' => 'applications/diffusion/controller/DiffusionRepositoryListController.php', 'DiffusionRepositoryManageController' => 'applications/diffusion/controller/DiffusionRepositoryManageController.php', 'DiffusionRepositoryManagePanelsController' => 'applications/diffusion/controller/DiffusionRepositoryManagePanelsController.php', + 'DiffusionRepositoryManagementBuildsPanelGroup' => 'applications/diffusion/management/DiffusionRepositoryManagementBuildsPanelGroup.php', + 'DiffusionRepositoryManagementIntegrationsPanelGroup' => 'applications/diffusion/management/DiffusionRepositoryManagementIntegrationsPanelGroup.php', + 'DiffusionRepositoryManagementMainPanelGroup' => 'applications/diffusion/management/DiffusionRepositoryManagementMainPanelGroup.php', + 'DiffusionRepositoryManagementOtherPanelGroup' => 'applications/diffusion/management/DiffusionRepositoryManagementOtherPanelGroup.php', 'DiffusionRepositoryManagementPanel' => 'applications/diffusion/management/DiffusionRepositoryManagementPanel.php', + 'DiffusionRepositoryManagementPanelGroup' => 'applications/diffusion/management/DiffusionRepositoryManagementPanelGroup.php', 'DiffusionRepositoryPath' => 'applications/diffusion/data/DiffusionRepositoryPath.php', 'DiffusionRepositoryPoliciesManagementPanel' => 'applications/diffusion/management/DiffusionRepositoryPoliciesManagementPanel.php', 'DiffusionRepositoryProfilePictureController' => 'applications/diffusion/controller/DiffusionRepositoryProfilePictureController.php', @@ -6335,7 +6340,12 @@ phutil_register_library_map(array( 'DiffusionRepositoryListController' => 'DiffusionController', 'DiffusionRepositoryManageController' => 'DiffusionController', 'DiffusionRepositoryManagePanelsController' => 'DiffusionRepositoryManageController', + 'DiffusionRepositoryManagementBuildsPanelGroup' => 'DiffusionRepositoryManagementPanelGroup', + 'DiffusionRepositoryManagementIntegrationsPanelGroup' => 'DiffusionRepositoryManagementPanelGroup', + 'DiffusionRepositoryManagementMainPanelGroup' => 'DiffusionRepositoryManagementPanelGroup', + 'DiffusionRepositoryManagementOtherPanelGroup' => 'DiffusionRepositoryManagementPanelGroup', 'DiffusionRepositoryManagementPanel' => 'Phobject', + 'DiffusionRepositoryManagementPanelGroup' => 'Phobject', 'DiffusionRepositoryPath' => 'Phobject', 'DiffusionRepositoryPoliciesManagementPanel' => 'DiffusionRepositoryManagementPanel', 'DiffusionRepositoryProfilePictureController' => 'DiffusionController', diff --git a/src/applications/diffusion/controller/DiffusionRepositoryManagePanelsController.php b/src/applications/diffusion/controller/DiffusionRepositoryManagePanelsController.php index 312c8402d3..dfb84cc3f0 100644 --- a/src/applications/diffusion/controller/DiffusionRepositoryManagePanelsController.php +++ b/src/applications/diffusion/controller/DiffusionRepositoryManagePanelsController.php @@ -93,20 +93,45 @@ final class DiffusionRepositoryManagePanelsController $nav = id(new AphrontSideNavFilterView()) ->setBaseURI($base_uri); - foreach ($panels as $panel) { - $key = $panel->getManagementPanelKey(); - $label = $panel->getManagementPanelLabel(); - $icon = $panel->getManagementPanelIcon(); - $href = $panel->getPanelNavigationURI(); + $groups = DiffusionRepositoryManagementPanelGroup::getAllPanelGroups(); + $panel_groups = mgroup($panels, 'getManagementPanelGroupKey'); + $other_key = DiffusionRepositoryManagementOtherPanelGroup::PANELGROUPKEY; - $item = id(new PHUIListItemView()) - ->setKey($key) - ->setName($label) - ->setType(PHUIListItemView::TYPE_LINK) - ->setHref($href) - ->setIcon($icon); + foreach ($groups as $group_key => $group) { + // If this is the "Other" group, include everything else that isn't in + // some actual group. + if ($group_key === $other_key) { + $group_panels = array_mergev($panel_groups); + $panel_groups = array(); + } else { + $group_panels = idx($panel_groups, $group_key); + unset($panel_groups[$group_key]); + } - $nav->addMenuItem($item); + if (!$group_panels) { + continue; + } + + $label = $group->getManagementPanelGroupLabel(); + if ($label) { + $nav->addLabel($label); + } + + foreach ($group_panels as $panel) { + $key = $panel->getManagementPanelKey(); + $label = $panel->getManagementPanelLabel(); + $icon = $panel->getManagementPanelIcon(); + $href = $panel->getPanelNavigationURI(); + + $item = id(new PHUIListItemView()) + ->setKey($key) + ->setName($label) + ->setType(PHUIListItemView::TYPE_LINK) + ->setHref($href) + ->setIcon($icon); + + $nav->addMenuItem($item); + } } $nav->selectFilter($selected); diff --git a/src/applications/diffusion/management/DiffusionRepositoryAutomationManagementPanel.php b/src/applications/diffusion/management/DiffusionRepositoryAutomationManagementPanel.php index 0ac03bd2cd..ac0c1251bb 100644 --- a/src/applications/diffusion/management/DiffusionRepositoryAutomationManagementPanel.php +++ b/src/applications/diffusion/management/DiffusionRepositoryAutomationManagementPanel.php @@ -13,6 +13,10 @@ final class DiffusionRepositoryAutomationManagementPanel return 800; } + public function getManagementPanelGroupKey() { + return DiffusionRepositoryManagementBuildsPanelGroup::PANELGROUPKEY; + } + public function shouldEnableForRepository( PhabricatorRepository $repository) { return $repository->isGit(); diff --git a/src/applications/diffusion/management/DiffusionRepositoryManagementBuildsPanelGroup.php b/src/applications/diffusion/management/DiffusionRepositoryManagementBuildsPanelGroup.php new file mode 100644 index 0000000000..e80901b494 --- /dev/null +++ b/src/applications/diffusion/management/DiffusionRepositoryManagementBuildsPanelGroup.php @@ -0,0 +1,16 @@ +getPhobjectClassConstant('PANELGROUPKEY'); + } + + abstract public function getManagementPanelGroupOrder(); + abstract public function getManagementPanelGroupLabel(); + + public static function getAllPanelGroups() { + return id(new PhutilClassMapQuery()) + ->setAncestorClass(__CLASS__) + ->setUniqueMethod('getManagementPanelGroupKey') + ->setSortMethod('getManagementPanelGroupOrder') + ->execute(); + } + +} diff --git a/src/applications/diffusion/management/DiffusionRepositoryStagingManagementPanel.php b/src/applications/diffusion/management/DiffusionRepositoryStagingManagementPanel.php index 33e5f7842a..2d92e08dfa 100644 --- a/src/applications/diffusion/management/DiffusionRepositoryStagingManagementPanel.php +++ b/src/applications/diffusion/management/DiffusionRepositoryStagingManagementPanel.php @@ -13,6 +13,10 @@ final class DiffusionRepositoryStagingManagementPanel return 700; } + public function getManagementPanelGroupKey() { + return DiffusionRepositoryManagementBuildsPanelGroup::PANELGROUPKEY; + } + public function shouldEnableForRepository( PhabricatorRepository $repository) { return $repository->isGit(); diff --git a/src/applications/diffusion/management/DiffusionRepositorySymbolsManagementPanel.php b/src/applications/diffusion/management/DiffusionRepositorySymbolsManagementPanel.php index 6970861cff..63a02dcf61 100644 --- a/src/applications/diffusion/management/DiffusionRepositorySymbolsManagementPanel.php +++ b/src/applications/diffusion/management/DiffusionRepositorySymbolsManagementPanel.php @@ -13,6 +13,10 @@ final class DiffusionRepositorySymbolsManagementPanel return 900; } + public function getManagementPanelGroupKey() { + return DiffusionRepositoryManagementIntegrationsPanelGroup::PANELGROUPKEY; + } + public function getManagementPanelIcon() { $repository = $this->getRepository(); From fd12b37d16f6b031ba3561fa90a9a3da59dfb79d Mon Sep 17 00:00:00 2001 From: epriestley Date: Wed, 21 Nov 2018 12:21:57 -0800 Subject: [PATCH 40/43] Modularize Repository transactions Summary: Depends on D19828. Ref T13216. Before adding new transactions to repositories (filesize limit, copy time limit, etc) modularize the existing transactions. Test Plan: - Created repository. - Edited callsign (invalid, valid, duplicate, add, remove). - Edited short name (invaild, valid, duplicate, add, remove). - Edited description (add, remove). - Edited encoding (invalid, valid, remove). - Allowed/denied dangerous changes. - Allowed/denied enormous chagnes. - Activated, deactivated, reactivated. - Changed tags. - Changed push policy. - Changed default branch (add, remove). - Changed track only: add, remove, invalid function, invalid regex. - Changed autoclose only: add, remove, invalid function, invalid regex. - Changed publish/notify. - Changed autoclose. - Changed staging area (add, remove, invalid). - Changed blueprints (add, remove). - Changed symbols (add, remove). - Grepped for `PhabricatorRepositoryTransaction::TYPE_`. - Reviewed transaction history: {F6021036} Reviewers: amckinley Reviewed By: amckinley Maniphest Tasks: T13216 Differential Revision: https://secure.phabricator.com/D19829 --- src/__phutil_library_map__.php | 46 +- ...fusionRepositoryEditActivateController.php | 3 +- ...usionRepositoryEditDangerousController.php | 3 +- ...fusionRepositoryEditEnormousController.php | 3 +- .../editor/DiffusionRepositoryEditEngine.php | 50 +- ...atorApplicationPolicyChangeTransaction.php | 10 +- .../editor/PhabricatorRepositoryEditor.php | 520 ------------------ ...RepositoryManagementClusterizeWorkflow.php | 3 +- .../PhabricatorRepositoryTransaction.php | 401 +------------- ...abricatorRepositoryActivateTransaction.php | 61 ++ ...atorRepositoryAutocloseOnlyTransaction.php | 42 ++ ...bricatorRepositoryAutocloseTransaction.php | 34 ++ ...ricatorRepositoryBlueprintsTransaction.php | 81 +++ ...abricatorRepositoryCallsignTransaction.php | 90 +++ ...bricatorRepositoryDangerousTransaction.php | 30 + ...atorRepositoryDefaultBranchTransaction.php | 39 ++ ...icatorRepositoryDescriptionTransaction.php | 49 ++ ...abricatorRepositoryEncodingTransaction.php | 68 +++ ...abricatorRepositoryEnormousTransaction.php | 30 + .../PhabricatorRepositoryNameTransaction.php | 35 ++ ...PhabricatorRepositoryNotifyTransaction.php | 34 ++ ...ricatorRepositoryPushPolicyTransaction.php | 24 + ...ricatorRepositorySVNSubpathTransaction.php | 39 ++ ...habricatorRepositoryServiceTransaction.php | 59 ++ .../PhabricatorRepositorySlugTransaction.php | 88 +++ ...ricatorRepositoryStagingURITransaction.php | 68 +++ ...orRepositorySymbolLanguagesTransaction.php | 39 ++ ...atorRepositorySymbolSourcesTransaction.php | 78 +++ ...bricatorRepositoryTrackOnlyTransaction.php | 42 ++ .../PhabricatorRepositoryTransactionType.php | 49 ++ .../PhabricatorRepositoryVCSTransaction.php | 66 +++ ...rApplicationTransactionValueController.php | 2 +- .../PhabricatorModularTransactionType.php | 29 + 33 files changed, 1266 insertions(+), 949 deletions(-) create mode 100644 src/applications/repository/xaction/PhabricatorRepositoryActivateTransaction.php create mode 100644 src/applications/repository/xaction/PhabricatorRepositoryAutocloseOnlyTransaction.php create mode 100644 src/applications/repository/xaction/PhabricatorRepositoryAutocloseTransaction.php create mode 100644 src/applications/repository/xaction/PhabricatorRepositoryBlueprintsTransaction.php create mode 100644 src/applications/repository/xaction/PhabricatorRepositoryCallsignTransaction.php create mode 100644 src/applications/repository/xaction/PhabricatorRepositoryDangerousTransaction.php create mode 100644 src/applications/repository/xaction/PhabricatorRepositoryDefaultBranchTransaction.php create mode 100644 src/applications/repository/xaction/PhabricatorRepositoryDescriptionTransaction.php create mode 100644 src/applications/repository/xaction/PhabricatorRepositoryEncodingTransaction.php create mode 100644 src/applications/repository/xaction/PhabricatorRepositoryEnormousTransaction.php create mode 100644 src/applications/repository/xaction/PhabricatorRepositoryNameTransaction.php create mode 100644 src/applications/repository/xaction/PhabricatorRepositoryNotifyTransaction.php create mode 100644 src/applications/repository/xaction/PhabricatorRepositoryPushPolicyTransaction.php create mode 100644 src/applications/repository/xaction/PhabricatorRepositorySVNSubpathTransaction.php create mode 100644 src/applications/repository/xaction/PhabricatorRepositoryServiceTransaction.php create mode 100644 src/applications/repository/xaction/PhabricatorRepositorySlugTransaction.php create mode 100644 src/applications/repository/xaction/PhabricatorRepositoryStagingURITransaction.php create mode 100644 src/applications/repository/xaction/PhabricatorRepositorySymbolLanguagesTransaction.php create mode 100644 src/applications/repository/xaction/PhabricatorRepositorySymbolSourcesTransaction.php create mode 100644 src/applications/repository/xaction/PhabricatorRepositoryTrackOnlyTransaction.php create mode 100644 src/applications/repository/xaction/PhabricatorRepositoryTransactionType.php create mode 100644 src/applications/repository/xaction/PhabricatorRepositoryVCSTransaction.php diff --git a/src/__phutil_library_map__.php b/src/__phutil_library_map__.php index 5ec0932b3b..77012e8a64 100644 --- a/src/__phutil_library_map__.php +++ b/src/__phutil_library_map__.php @@ -4084,8 +4084,13 @@ phutil_register_library_map(array( 'PhabricatorRemarkupUIExample' => 'applications/uiexample/examples/PhabricatorRemarkupUIExample.php', 'PhabricatorRepositoriesSetupCheck' => 'applications/config/check/PhabricatorRepositoriesSetupCheck.php', 'PhabricatorRepository' => 'applications/repository/storage/PhabricatorRepository.php', + 'PhabricatorRepositoryActivateTransaction' => 'applications/repository/xaction/PhabricatorRepositoryActivateTransaction.php', 'PhabricatorRepositoryAuditRequest' => 'applications/repository/storage/PhabricatorRepositoryAuditRequest.php', + 'PhabricatorRepositoryAutocloseOnlyTransaction' => 'applications/repository/xaction/PhabricatorRepositoryAutocloseOnlyTransaction.php', + 'PhabricatorRepositoryAutocloseTransaction' => 'applications/repository/xaction/PhabricatorRepositoryAutocloseTransaction.php', + 'PhabricatorRepositoryBlueprintsTransaction' => 'applications/repository/xaction/PhabricatorRepositoryBlueprintsTransaction.php', 'PhabricatorRepositoryBranch' => 'applications/repository/storage/PhabricatorRepositoryBranch.php', + 'PhabricatorRepositoryCallsignTransaction' => 'applications/repository/xaction/PhabricatorRepositoryCallsignTransaction.php', 'PhabricatorRepositoryCommit' => 'applications/repository/storage/PhabricatorRepositoryCommit.php', 'PhabricatorRepositoryCommitChangeParserWorker' => 'applications/repository/worker/commitchangeparser/PhabricatorRepositoryCommitChangeParserWorker.php', 'PhabricatorRepositoryCommitData' => 'applications/repository/storage/PhabricatorRepositoryCommitData.php', @@ -4099,10 +4104,15 @@ phutil_register_library_map(array( 'PhabricatorRepositoryCommitTestCase' => 'applications/repository/storage/__tests__/PhabricatorRepositoryCommitTestCase.php', 'PhabricatorRepositoryConfigOptions' => 'applications/repository/config/PhabricatorRepositoryConfigOptions.php', 'PhabricatorRepositoryDAO' => 'applications/repository/storage/PhabricatorRepositoryDAO.php', + 'PhabricatorRepositoryDangerousTransaction' => 'applications/repository/xaction/PhabricatorRepositoryDangerousTransaction.php', + 'PhabricatorRepositoryDefaultBranchTransaction' => 'applications/repository/xaction/PhabricatorRepositoryDefaultBranchTransaction.php', + 'PhabricatorRepositoryDescriptionTransaction' => 'applications/repository/xaction/PhabricatorRepositoryDescriptionTransaction.php', 'PhabricatorRepositoryDestructibleCodex' => 'applications/repository/codex/PhabricatorRepositoryDestructibleCodex.php', 'PhabricatorRepositoryDiscoveryEngine' => 'applications/repository/engine/PhabricatorRepositoryDiscoveryEngine.php', 'PhabricatorRepositoryEditor' => 'applications/repository/editor/PhabricatorRepositoryEditor.php', + 'PhabricatorRepositoryEncodingTransaction' => 'applications/repository/xaction/PhabricatorRepositoryEncodingTransaction.php', 'PhabricatorRepositoryEngine' => 'applications/repository/engine/PhabricatorRepositoryEngine.php', + 'PhabricatorRepositoryEnormousTransaction' => 'applications/repository/xaction/PhabricatorRepositoryEnormousTransaction.php', 'PhabricatorRepositoryFerretEngine' => 'applications/repository/search/PhabricatorRepositoryFerretEngine.php', 'PhabricatorRepositoryFulltextEngine' => 'applications/repository/search/PhabricatorRepositoryFulltextEngine.php', 'PhabricatorRepositoryGitCommitChangeParserWorker' => 'applications/repository/worker/commitchangeparser/PhabricatorRepositoryGitCommitChangeParserWorker.php', @@ -4146,6 +4156,8 @@ phutil_register_library_map(array( 'PhabricatorRepositoryMercurialCommitMessageParserWorker' => 'applications/repository/worker/commitmessageparser/PhabricatorRepositoryMercurialCommitMessageParserWorker.php', 'PhabricatorRepositoryMirror' => 'applications/repository/storage/PhabricatorRepositoryMirror.php', 'PhabricatorRepositoryMirrorEngine' => 'applications/repository/engine/PhabricatorRepositoryMirrorEngine.php', + 'PhabricatorRepositoryNameTransaction' => 'applications/repository/xaction/PhabricatorRepositoryNameTransaction.php', + 'PhabricatorRepositoryNotifyTransaction' => 'applications/repository/xaction/PhabricatorRepositoryNotifyTransaction.php', 'PhabricatorRepositoryOldRef' => 'applications/repository/storage/PhabricatorRepositoryOldRef.php', 'PhabricatorRepositoryParsedChange' => 'applications/repository/data/PhabricatorRepositoryParsedChange.php', 'PhabricatorRepositoryPullEngine' => 'applications/repository/engine/PhabricatorRepositoryPullEngine.php', @@ -4162,6 +4174,7 @@ phutil_register_library_map(array( 'PhabricatorRepositoryPushLogQuery' => 'applications/repository/query/PhabricatorRepositoryPushLogQuery.php', 'PhabricatorRepositoryPushLogSearchEngine' => 'applications/repository/query/PhabricatorRepositoryPushLogSearchEngine.php', 'PhabricatorRepositoryPushMailWorker' => 'applications/repository/worker/PhabricatorRepositoryPushMailWorker.php', + 'PhabricatorRepositoryPushPolicyTransaction' => 'applications/repository/xaction/PhabricatorRepositoryPushPolicyTransaction.php', 'PhabricatorRepositoryPushReplyHandler' => 'applications/repository/mail/PhabricatorRepositoryPushReplyHandler.php', 'PhabricatorRepositoryQuery' => 'applications/repository/query/PhabricatorRepositoryQuery.php', 'PhabricatorRepositoryRefCursor' => 'applications/repository/storage/PhabricatorRepositoryRefCursor.php', @@ -4170,18 +4183,26 @@ phutil_register_library_map(array( 'PhabricatorRepositoryRefEngine' => 'applications/repository/engine/PhabricatorRepositoryRefEngine.php', 'PhabricatorRepositoryRefPosition' => 'applications/repository/storage/PhabricatorRepositoryRefPosition.php', 'PhabricatorRepositoryRepositoryPHIDType' => 'applications/repository/phid/PhabricatorRepositoryRepositoryPHIDType.php', + 'PhabricatorRepositorySVNSubpathTransaction' => 'applications/repository/xaction/PhabricatorRepositorySVNSubpathTransaction.php', 'PhabricatorRepositorySchemaSpec' => 'applications/repository/storage/PhabricatorRepositorySchemaSpec.php', 'PhabricatorRepositorySearchEngine' => 'applications/repository/query/PhabricatorRepositorySearchEngine.php', + 'PhabricatorRepositoryServiceTransaction' => 'applications/repository/xaction/PhabricatorRepositoryServiceTransaction.php', + 'PhabricatorRepositorySlugTransaction' => 'applications/repository/xaction/PhabricatorRepositorySlugTransaction.php', + 'PhabricatorRepositoryStagingURITransaction' => 'applications/repository/xaction/PhabricatorRepositoryStagingURITransaction.php', 'PhabricatorRepositoryStatusMessage' => 'applications/repository/storage/PhabricatorRepositoryStatusMessage.php', 'PhabricatorRepositorySvnCommitChangeParserWorker' => 'applications/repository/worker/commitchangeparser/PhabricatorRepositorySvnCommitChangeParserWorker.php', 'PhabricatorRepositorySvnCommitMessageParserWorker' => 'applications/repository/worker/commitmessageparser/PhabricatorRepositorySvnCommitMessageParserWorker.php', 'PhabricatorRepositorySymbol' => 'applications/repository/storage/PhabricatorRepositorySymbol.php', + 'PhabricatorRepositorySymbolLanguagesTransaction' => 'applications/repository/xaction/PhabricatorRepositorySymbolLanguagesTransaction.php', + 'PhabricatorRepositorySymbolSourcesTransaction' => 'applications/repository/xaction/PhabricatorRepositorySymbolSourcesTransaction.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', + 'PhabricatorRepositoryTrackOnlyTransaction' => 'applications/repository/xaction/PhabricatorRepositoryTrackOnlyTransaction.php', 'PhabricatorRepositoryTransaction' => 'applications/repository/storage/PhabricatorRepositoryTransaction.php', 'PhabricatorRepositoryTransactionQuery' => 'applications/repository/query/PhabricatorRepositoryTransactionQuery.php', + 'PhabricatorRepositoryTransactionType' => 'applications/repository/xaction/PhabricatorRepositoryTransactionType.php', 'PhabricatorRepositoryType' => 'applications/repository/constants/PhabricatorRepositoryType.php', 'PhabricatorRepositoryURI' => 'applications/repository/storage/PhabricatorRepositoryURI.php', 'PhabricatorRepositoryURIIndex' => 'applications/repository/storage/PhabricatorRepositoryURIIndex.php', @@ -4192,6 +4213,7 @@ phutil_register_library_map(array( 'PhabricatorRepositoryURITestCase' => 'applications/repository/storage/__tests__/PhabricatorRepositoryURITestCase.php', 'PhabricatorRepositoryURITransaction' => 'applications/repository/storage/PhabricatorRepositoryURITransaction.php', 'PhabricatorRepositoryURITransactionQuery' => 'applications/repository/query/PhabricatorRepositoryURITransactionQuery.php', + 'PhabricatorRepositoryVCSTransaction' => 'applications/repository/xaction/PhabricatorRepositoryVCSTransaction.php', 'PhabricatorRepositoryWorkingCopyVersion' => 'applications/repository/storage/PhabricatorRepositoryWorkingCopyVersion.php', 'PhabricatorRequestExceptionHandler' => 'aphront/handler/PhabricatorRequestExceptionHandler.php', 'PhabricatorResourceSite' => 'aphront/site/PhabricatorResourceSite.php', @@ -10004,11 +10026,16 @@ phutil_register_library_map(array( 'PhabricatorFulltextInterface', 'PhabricatorFerretInterface', ), + 'PhabricatorRepositoryActivateTransaction' => 'PhabricatorRepositoryTransactionType', 'PhabricatorRepositoryAuditRequest' => array( 'PhabricatorRepositoryDAO', 'PhabricatorPolicyInterface', ), + 'PhabricatorRepositoryAutocloseOnlyTransaction' => 'PhabricatorRepositoryTransactionType', + 'PhabricatorRepositoryAutocloseTransaction' => 'PhabricatorRepositoryTransactionType', + 'PhabricatorRepositoryBlueprintsTransaction' => 'PhabricatorRepositoryTransactionType', 'PhabricatorRepositoryBranch' => 'PhabricatorRepositoryDAO', + 'PhabricatorRepositoryCallsignTransaction' => 'PhabricatorRepositoryTransactionType', 'PhabricatorRepositoryCommit' => array( 'PhabricatorRepositoryDAO', 'PhabricatorPolicyInterface', @@ -10042,10 +10069,15 @@ phutil_register_library_map(array( 'PhabricatorRepositoryCommitTestCase' => 'PhabricatorTestCase', 'PhabricatorRepositoryConfigOptions' => 'PhabricatorApplicationConfigOptions', 'PhabricatorRepositoryDAO' => 'PhabricatorLiskDAO', + 'PhabricatorRepositoryDangerousTransaction' => 'PhabricatorRepositoryTransactionType', + 'PhabricatorRepositoryDefaultBranchTransaction' => 'PhabricatorRepositoryTransactionType', + 'PhabricatorRepositoryDescriptionTransaction' => 'PhabricatorRepositoryTransactionType', 'PhabricatorRepositoryDestructibleCodex' => 'PhabricatorDestructibleCodex', 'PhabricatorRepositoryDiscoveryEngine' => 'PhabricatorRepositoryEngine', 'PhabricatorRepositoryEditor' => 'PhabricatorApplicationTransactionEditor', + 'PhabricatorRepositoryEncodingTransaction' => 'PhabricatorRepositoryTransactionType', 'PhabricatorRepositoryEngine' => 'Phobject', + 'PhabricatorRepositoryEnormousTransaction' => 'PhabricatorRepositoryTransactionType', 'PhabricatorRepositoryFerretEngine' => 'PhabricatorFerretEngine', 'PhabricatorRepositoryFulltextEngine' => 'PhabricatorFulltextEngine', 'PhabricatorRepositoryGitCommitChangeParserWorker' => 'PhabricatorRepositoryCommitChangeParserWorker', @@ -10097,6 +10129,8 @@ phutil_register_library_map(array( 'PhabricatorRepositoryMercurialCommitMessageParserWorker' => 'PhabricatorRepositoryCommitMessageParserWorker', 'PhabricatorRepositoryMirror' => 'PhabricatorRepositoryDAO', 'PhabricatorRepositoryMirrorEngine' => 'PhabricatorRepositoryEngine', + 'PhabricatorRepositoryNameTransaction' => 'PhabricatorRepositoryTransactionType', + 'PhabricatorRepositoryNotifyTransaction' => 'PhabricatorRepositoryTransactionType', 'PhabricatorRepositoryOldRef' => array( 'PhabricatorRepositoryDAO', 'PhabricatorPolicyInterface', @@ -10125,6 +10159,7 @@ phutil_register_library_map(array( 'PhabricatorRepositoryPushLogQuery' => 'PhabricatorCursorPagedPolicyAwareQuery', 'PhabricatorRepositoryPushLogSearchEngine' => 'PhabricatorApplicationSearchEngine', 'PhabricatorRepositoryPushMailWorker' => 'PhabricatorWorker', + 'PhabricatorRepositoryPushPolicyTransaction' => 'PhabricatorRepositoryTransactionType', 'PhabricatorRepositoryPushReplyHandler' => 'PhabricatorMailReplyHandler', 'PhabricatorRepositoryQuery' => 'PhabricatorCursorPagedPolicyAwareQuery', 'PhabricatorRepositoryRefCursor' => array( @@ -10136,12 +10171,18 @@ phutil_register_library_map(array( 'PhabricatorRepositoryRefEngine' => 'PhabricatorRepositoryEngine', 'PhabricatorRepositoryRefPosition' => 'PhabricatorRepositoryDAO', 'PhabricatorRepositoryRepositoryPHIDType' => 'PhabricatorPHIDType', + 'PhabricatorRepositorySVNSubpathTransaction' => 'PhabricatorRepositoryTransactionType', 'PhabricatorRepositorySchemaSpec' => 'PhabricatorConfigSchemaSpec', 'PhabricatorRepositorySearchEngine' => 'PhabricatorApplicationSearchEngine', + 'PhabricatorRepositoryServiceTransaction' => 'PhabricatorRepositoryTransactionType', + 'PhabricatorRepositorySlugTransaction' => 'PhabricatorRepositoryTransactionType', + 'PhabricatorRepositoryStagingURITransaction' => 'PhabricatorRepositoryTransactionType', 'PhabricatorRepositoryStatusMessage' => 'PhabricatorRepositoryDAO', 'PhabricatorRepositorySvnCommitChangeParserWorker' => 'PhabricatorRepositoryCommitChangeParserWorker', 'PhabricatorRepositorySvnCommitMessageParserWorker' => 'PhabricatorRepositoryCommitMessageParserWorker', 'PhabricatorRepositorySymbol' => 'PhabricatorRepositoryDAO', + 'PhabricatorRepositorySymbolLanguagesTransaction' => 'PhabricatorRepositoryTransactionType', + 'PhabricatorRepositorySymbolSourcesTransaction' => 'PhabricatorRepositoryTransactionType', 'PhabricatorRepositorySyncEvent' => array( 'PhabricatorRepositoryDAO', 'PhabricatorPolicyInterface', @@ -10149,8 +10190,10 @@ phutil_register_library_map(array( 'PhabricatorRepositorySyncEventPHIDType' => 'PhabricatorPHIDType', 'PhabricatorRepositorySyncEventQuery' => 'PhabricatorCursorPagedPolicyAwareQuery', 'PhabricatorRepositoryTestCase' => 'PhabricatorTestCase', - 'PhabricatorRepositoryTransaction' => 'PhabricatorApplicationTransaction', + 'PhabricatorRepositoryTrackOnlyTransaction' => 'PhabricatorRepositoryTransactionType', + 'PhabricatorRepositoryTransaction' => 'PhabricatorModularTransaction', 'PhabricatorRepositoryTransactionQuery' => 'PhabricatorApplicationTransactionQuery', + 'PhabricatorRepositoryTransactionType' => 'PhabricatorModularTransactionType', 'PhabricatorRepositoryType' => 'Phobject', 'PhabricatorRepositoryURI' => array( 'PhabricatorRepositoryDAO', @@ -10167,6 +10210,7 @@ phutil_register_library_map(array( 'PhabricatorRepositoryURITestCase' => 'PhabricatorTestCase', 'PhabricatorRepositoryURITransaction' => 'PhabricatorApplicationTransaction', 'PhabricatorRepositoryURITransactionQuery' => 'PhabricatorApplicationTransactionQuery', + 'PhabricatorRepositoryVCSTransaction' => 'PhabricatorRepositoryTransactionType', 'PhabricatorRepositoryWorkingCopyVersion' => 'PhabricatorRepositoryDAO', 'PhabricatorRequestExceptionHandler' => 'AphrontRequestExceptionHandler', 'PhabricatorResourceSite' => 'PhabricatorSite', diff --git a/src/applications/diffusion/controller/DiffusionRepositoryEditActivateController.php b/src/applications/diffusion/controller/DiffusionRepositoryEditActivateController.php index b383333fbc..7e2d06982d 100644 --- a/src/applications/diffusion/controller/DiffusionRepositoryEditActivateController.php +++ b/src/applications/diffusion/controller/DiffusionRepositoryEditActivateController.php @@ -25,7 +25,8 @@ final class DiffusionRepositoryEditActivateController } $xaction = id(new PhabricatorRepositoryTransaction()) - ->setTransactionType(PhabricatorRepositoryTransaction::TYPE_ACTIVATE) + ->setTransactionType( + PhabricatorRepositoryActivateTransaction::TRANSACTIONTYPE) ->setNewValue($new_status); $editor = id(new PhabricatorRepositoryEditor()) diff --git a/src/applications/diffusion/controller/DiffusionRepositoryEditDangerousController.php b/src/applications/diffusion/controller/DiffusionRepositoryEditDangerousController.php index 1088733cfc..9503a9e386 100644 --- a/src/applications/diffusion/controller/DiffusionRepositoryEditDangerousController.php +++ b/src/applications/diffusion/controller/DiffusionRepositoryEditDangerousController.php @@ -30,7 +30,8 @@ final class DiffusionRepositoryEditDangerousController if ($request->isFormPost()) { $xaction = id(new PhabricatorRepositoryTransaction()) - ->setTransactionType(PhabricatorRepositoryTransaction::TYPE_DANGEROUS) + ->setTransactionType( + PhabricatorRepositoryDangerousTransaction::TRANSACTIONTYPE) ->setNewValue(!$repository->shouldAllowDangerousChanges()); $editor = id(new PhabricatorRepositoryEditor()) diff --git a/src/applications/diffusion/controller/DiffusionRepositoryEditEnormousController.php b/src/applications/diffusion/controller/DiffusionRepositoryEditEnormousController.php index d4eeb118d7..11a3ee3736 100644 --- a/src/applications/diffusion/controller/DiffusionRepositoryEditEnormousController.php +++ b/src/applications/diffusion/controller/DiffusionRepositoryEditEnormousController.php @@ -30,7 +30,8 @@ final class DiffusionRepositoryEditEnormousController if ($request->isFormPost()) { $xaction = id(new PhabricatorRepositoryTransaction()) - ->setTransactionType(PhabricatorRepositoryTransaction::TYPE_ENORMOUS) + ->setTransactionType( + PhabricatorRepositoryEnormousTransaction::TRANSACTIONTYPE) ->setNewValue(!$repository->shouldAllowEnormousChanges()); $editor = id(new PhabricatorRepositoryEditor()) diff --git a/src/applications/diffusion/editor/DiffusionRepositoryEditEngine.php b/src/applications/diffusion/editor/DiffusionRepositoryEditEngine.php index 79b97e44be..78baeba71a 100644 --- a/src/applications/diffusion/editor/DiffusionRepositoryEditEngine.php +++ b/src/applications/diffusion/editor/DiffusionRepositoryEditEngine.php @@ -243,7 +243,8 @@ final class DiffusionRepositoryEditEngine id(new PhabricatorSelectEditField()) ->setKey('vcs') ->setLabel(pht('Version Control System')) - ->setTransactionType(PhabricatorRepositoryTransaction::TYPE_VCS) + ->setTransactionType( + PhabricatorRepositoryVCSTransaction::TRANSACTIONTYPE) ->setIsFormField(false) ->setIsCopyable(true) ->setOptions(PhabricatorRepositoryType::getAllRepositoryTypes()) @@ -258,7 +259,8 @@ final class DiffusionRepositoryEditEngine ->setKey('name') ->setLabel(pht('Name')) ->setIsRequired(true) - ->setTransactionType(PhabricatorRepositoryTransaction::TYPE_NAME) + ->setTransactionType( + PhabricatorRepositoryNameTransaction::TRANSACTIONTYPE) ->setDescription(pht('The repository name.')) ->setConduitDescription(pht('Rename the repository.')) ->setConduitTypeDescription(pht('New repository name.')) @@ -266,7 +268,8 @@ final class DiffusionRepositoryEditEngine id(new PhabricatorTextEditField()) ->setKey('callsign') ->setLabel(pht('Callsign')) - ->setTransactionType(PhabricatorRepositoryTransaction::TYPE_CALLSIGN) + ->setTransactionType( + PhabricatorRepositoryCallsignTransaction::TRANSACTIONTYPE) ->setDescription(pht('The repository callsign.')) ->setConduitDescription(pht('Change the repository callsign.')) ->setConduitTypeDescription(pht('New repository callsign.')) @@ -274,7 +277,8 @@ final class DiffusionRepositoryEditEngine id(new PhabricatorTextEditField()) ->setKey('shortName') ->setLabel(pht('Short Name')) - ->setTransactionType(PhabricatorRepositoryTransaction::TYPE_SLUG) + ->setTransactionType( + PhabricatorRepositorySlugTransaction::TRANSACTIONTYPE) ->setDescription(pht('Short, unique repository name.')) ->setConduitDescription(pht('Change the repository short name.')) ->setConduitTypeDescription(pht('New short name for the repository.')) @@ -282,7 +286,8 @@ final class DiffusionRepositoryEditEngine id(new PhabricatorRemarkupEditField()) ->setKey('description') ->setLabel(pht('Description')) - ->setTransactionType(PhabricatorRepositoryTransaction::TYPE_DESCRIPTION) + ->setTransactionType( + PhabricatorRepositoryDescriptionTransaction::TRANSACTIONTYPE) ->setDescription(pht('Repository description.')) ->setConduitDescription(pht('Change the repository description.')) ->setConduitTypeDescription(pht('New repository description.')) @@ -291,7 +296,8 @@ final class DiffusionRepositoryEditEngine ->setKey('encoding') ->setLabel(pht('Text Encoding')) ->setIsCopyable(true) - ->setTransactionType(PhabricatorRepositoryTransaction::TYPE_ENCODING) + ->setTransactionType( + PhabricatorRepositoryEncodingTransaction::TRANSACTIONTYPE) ->setDescription(pht('Default text encoding.')) ->setConduitDescription(pht('Change the default text encoding.')) ->setConduitTypeDescription(pht('New text encoding.')) @@ -304,7 +310,8 @@ final class DiffusionRepositoryEditEngine ->setOptions( pht('Prevent Dangerous Changes'), pht('Allow Dangerous Changes')) - ->setTransactionType(PhabricatorRepositoryTransaction::TYPE_DANGEROUS) + ->setTransactionType( + PhabricatorRepositoryDangerousTransaction::TRANSACTIONTYPE) ->setDescription(pht('Permit dangerous changes to be made.')) ->setConduitDescription(pht('Allow or prevent dangerous changes.')) ->setConduitTypeDescription(pht('New protection setting.')) @@ -317,7 +324,8 @@ final class DiffusionRepositoryEditEngine ->setOptions( pht('Prevent Enormous Changes'), pht('Allow Enormous Changes')) - ->setTransactionType(PhabricatorRepositoryTransaction::TYPE_ENORMOUS) + ->setTransactionType( + PhabricatorRepositoryEnormousTransaction::TRANSACTIONTYPE) ->setDescription(pht('Permit enormous changes to be made.')) ->setConduitDescription(pht('Allow or prevent enormous changes.')) ->setConduitTypeDescription(pht('New protection setting.')) @@ -325,7 +333,8 @@ final class DiffusionRepositoryEditEngine id(new PhabricatorSelectEditField()) ->setKey('status') ->setLabel(pht('Status')) - ->setTransactionType(PhabricatorRepositoryTransaction::TYPE_ACTIVATE) + ->setTransactionType( + PhabricatorRepositoryActivateTransaction::TRANSACTIONTYPE) ->setIsFormField(false) ->setOptions(PhabricatorRepository::getStatusNameMap()) ->setDescription(pht('Active or inactive status.')) @@ -336,7 +345,7 @@ final class DiffusionRepositoryEditEngine ->setKey('defaultBranch') ->setLabel(pht('Default Branch')) ->setTransactionType( - PhabricatorRepositoryTransaction::TYPE_DEFAULT_BRANCH) + PhabricatorRepositoryDefaultBranchTransaction::TRANSACTIONTYPE) ->setIsCopyable(true) ->setDescription(pht('Default branch name.')) ->setConduitDescription(pht('Set the default branch name.')) @@ -347,7 +356,7 @@ final class DiffusionRepositoryEditEngine ->setKey('trackOnly') ->setLabel(pht('Track Only')) ->setTransactionType( - PhabricatorRepositoryTransaction::TYPE_TRACK_ONLY) + PhabricatorRepositoryTrackOnlyTransaction::TRANSACTIONTYPE) ->setIsCopyable(true) ->setDescription(pht('Track only these branches.')) ->setConduitDescription(pht('Set the tracked branches.')) @@ -358,7 +367,7 @@ final class DiffusionRepositoryEditEngine ->setKey('autocloseOnly') ->setLabel(pht('Autoclose Only')) ->setTransactionType( - PhabricatorRepositoryTransaction::TYPE_AUTOCLOSE_ONLY) + PhabricatorRepositoryAutocloseOnlyTransaction::TRANSACTIONTYPE) ->setIsCopyable(true) ->setDescription(pht('Autoclose commits on only these branches.')) ->setConduitDescription(pht('Set the autoclose branches.')) @@ -368,7 +377,7 @@ final class DiffusionRepositoryEditEngine ->setKey('importOnly') ->setLabel(pht('Import Only')) ->setTransactionType( - PhabricatorRepositoryTransaction::TYPE_SVN_SUBPATH) + PhabricatorRepositorySVNSubpathTransaction::TRANSACTIONTYPE) ->setIsCopyable(true) ->setDescription(pht('Subpath to selectively import.')) ->setConduitDescription(pht('Set the subpath to import.')) @@ -379,7 +388,7 @@ final class DiffusionRepositoryEditEngine ->setKey('stagingAreaURI') ->setLabel(pht('Staging Area URI')) ->setTransactionType( - PhabricatorRepositoryTransaction::TYPE_STAGING_URI) + PhabricatorRepositoryStagingURITransaction::TRANSACTIONTYPE) ->setIsCopyable(true) ->setDescription(pht('Staging area URI.')) ->setConduitDescription(pht('Set the staging area URI.')) @@ -390,7 +399,7 @@ final class DiffusionRepositoryEditEngine ->setKey('automationBlueprintPHIDs') ->setLabel(pht('Use Blueprints')) ->setTransactionType( - PhabricatorRepositoryTransaction::TYPE_AUTOMATION_BLUEPRINTS) + PhabricatorRepositoryBlueprintsTransaction::TRANSACTIONTYPE) ->setIsCopyable(true) ->setDatasource(new DrydockBlueprintDatasource()) ->setDescription(pht('Automation blueprints.')) @@ -402,7 +411,7 @@ final class DiffusionRepositoryEditEngine ->setKey('symbolLanguages') ->setLabel(pht('Languages')) ->setTransactionType( - PhabricatorRepositoryTransaction::TYPE_SYMBOLS_LANGUAGE) + PhabricatorRepositorySymbolLanguagesTransaction::TRANSACTIONTYPE) ->setIsCopyable(true) ->setDescription( pht('Languages which define symbols in this repository.')) @@ -415,7 +424,7 @@ final class DiffusionRepositoryEditEngine ->setKey('symbolRepositoryPHIDs') ->setLabel(pht('Uses Symbols From')) ->setTransactionType( - PhabricatorRepositoryTransaction::TYPE_SYMBOLS_SOURCES) + PhabricatorRepositorySymbolSourcesTransaction::TRANSACTIONTYPE) ->setIsCopyable(true) ->setDatasource(new DiffusionRepositoryDatasource()) ->setDescription(pht('Repositories to link symbols from.')) @@ -426,7 +435,7 @@ final class DiffusionRepositoryEditEngine ->setKey('publish') ->setLabel(pht('Publish/Notify')) ->setTransactionType( - PhabricatorRepositoryTransaction::TYPE_NOTIFY) + PhabricatorRepositoryNotifyTransaction::TRANSACTIONTYPE) ->setIsCopyable(true) ->setOptions( pht('Disable Notifications, Feed, and Herald'), @@ -439,7 +448,7 @@ final class DiffusionRepositoryEditEngine ->setKey('autoclose') ->setLabel(pht('Autoclose')) ->setTransactionType( - PhabricatorRepositoryTransaction::TYPE_AUTOCLOSE) + PhabricatorRepositoryAutocloseTransaction::TRANSACTIONTYPE) ->setIsCopyable(true) ->setOptions( pht('Disable Autoclose'), @@ -455,7 +464,8 @@ final class DiffusionRepositoryEditEngine ->setIsCopyable(true) ->setCapability(DiffusionPushCapability::CAPABILITY) ->setPolicies($policies) - ->setTransactionType(PhabricatorRepositoryTransaction::TYPE_PUSH_POLICY) + ->setTransactionType( + PhabricatorRepositoryPushPolicyTransaction::TRANSACTIONTYPE) ->setDescription( pht('Controls who can push changes to the repository.')) ->setConduitDescription( diff --git a/src/applications/meta/xactions/PhabricatorApplicationPolicyChangeTransaction.php b/src/applications/meta/xactions/PhabricatorApplicationPolicyChangeTransaction.php index 59dfc09486..5364a3d4fa 100644 --- a/src/applications/meta/xactions/PhabricatorApplicationPolicyChangeTransaction.php +++ b/src/applications/meta/xactions/PhabricatorApplicationPolicyChangeTransaction.php @@ -52,8 +52,8 @@ final class PhabricatorApplicationPolicyChangeTransaction } public function getTitle() { - $old = $this->renderPolicy($this->getOldValue()); - $new = $this->renderPolicy($this->getNewValue()); + $old = $this->renderApplicationPolicy($this->getOldValue()); + $new = $this->renderApplicationPolicy($this->getNewValue()); return pht( '%s changed the "%s" policy from "%s" to "%s".', @@ -64,8 +64,8 @@ final class PhabricatorApplicationPolicyChangeTransaction } public function getTitleForFeed() { - $old = $this->renderPolicy($this->getOldValue()); - $new = $this->renderPolicy($this->getNewValue()); + $old = $this->renderApplicationPolicy($this->getOldValue()); + $new = $this->renderApplicationPolicy($this->getNewValue()); return pht( '%s changed the "%s" policy for application %s from "%s" to "%s".', @@ -165,7 +165,7 @@ final class PhabricatorApplicationPolicyChangeTransaction return $errors; } - private function renderPolicy($name) { + private function renderApplicationPolicy($name) { $policies = $this->getAllPolicies(); if (empty($policies[$name])) { // Not a standard policy, check for a custom policy. diff --git a/src/applications/repository/editor/PhabricatorRepositoryEditor.php b/src/applications/repository/editor/PhabricatorRepositoryEditor.php index 5cfa1da290..0955d54b98 100644 --- a/src/applications/repository/editor/PhabricatorRepositoryEditor.php +++ b/src/applications/repository/editor/PhabricatorRepositoryEditor.php @@ -14,28 +14,6 @@ final class PhabricatorRepositoryEditor public function getTransactionTypes() { $types = parent::getTransactionTypes(); - $types[] = PhabricatorRepositoryTransaction::TYPE_VCS; - $types[] = PhabricatorRepositoryTransaction::TYPE_ACTIVATE; - $types[] = PhabricatorRepositoryTransaction::TYPE_NAME; - $types[] = PhabricatorRepositoryTransaction::TYPE_DESCRIPTION; - $types[] = PhabricatorRepositoryTransaction::TYPE_ENCODING; - $types[] = PhabricatorRepositoryTransaction::TYPE_DEFAULT_BRANCH; - $types[] = PhabricatorRepositoryTransaction::TYPE_TRACK_ONLY; - $types[] = PhabricatorRepositoryTransaction::TYPE_AUTOCLOSE_ONLY; - $types[] = PhabricatorRepositoryTransaction::TYPE_SVN_SUBPATH; - $types[] = PhabricatorRepositoryTransaction::TYPE_NOTIFY; - $types[] = PhabricatorRepositoryTransaction::TYPE_AUTOCLOSE; - $types[] = PhabricatorRepositoryTransaction::TYPE_PUSH_POLICY; - $types[] = PhabricatorRepositoryTransaction::TYPE_DANGEROUS; - $types[] = PhabricatorRepositoryTransaction::TYPE_ENORMOUS; - $types[] = PhabricatorRepositoryTransaction::TYPE_SLUG; - $types[] = PhabricatorRepositoryTransaction::TYPE_SERVICE; - $types[] = PhabricatorRepositoryTransaction::TYPE_SYMBOLS_LANGUAGE; - $types[] = PhabricatorRepositoryTransaction::TYPE_SYMBOLS_SOURCES; - $types[] = PhabricatorRepositoryTransaction::TYPE_STAGING_URI; - $types[] = PhabricatorRepositoryTransaction::TYPE_AUTOMATION_BLUEPRINTS; - $types[] = PhabricatorRepositoryTransaction::TYPE_CALLSIGN; - $types[] = PhabricatorTransactions::TYPE_EDGE; $types[] = PhabricatorTransactions::TYPE_VIEW_POLICY; $types[] = PhabricatorTransactions::TYPE_EDIT_POLICY; @@ -43,504 +21,6 @@ final class PhabricatorRepositoryEditor return $types; } - protected function getCustomTransactionOldValue( - PhabricatorLiskDAO $object, - PhabricatorApplicationTransaction $xaction) { - - switch ($xaction->getTransactionType()) { - case PhabricatorRepositoryTransaction::TYPE_VCS: - return $object->getVersionControlSystem(); - case PhabricatorRepositoryTransaction::TYPE_ACTIVATE: - return $object->isTracked(); - case PhabricatorRepositoryTransaction::TYPE_NAME: - return $object->getName(); - case PhabricatorRepositoryTransaction::TYPE_DESCRIPTION: - return $object->getDetail('description'); - case PhabricatorRepositoryTransaction::TYPE_ENCODING: - return $object->getDetail('encoding'); - case PhabricatorRepositoryTransaction::TYPE_DEFAULT_BRANCH: - return $object->getDetail('default-branch'); - case PhabricatorRepositoryTransaction::TYPE_TRACK_ONLY: - return array_keys($object->getDetail('branch-filter', array())); - case PhabricatorRepositoryTransaction::TYPE_AUTOCLOSE_ONLY: - return array_keys($object->getDetail('close-commits-filter', array())); - case PhabricatorRepositoryTransaction::TYPE_SVN_SUBPATH: - return $object->getDetail('svn-subpath'); - case PhabricatorRepositoryTransaction::TYPE_NOTIFY: - return (int)!$object->getDetail('herald-disabled'); - case PhabricatorRepositoryTransaction::TYPE_AUTOCLOSE: - return (int)!$object->getDetail('disable-autoclose'); - case PhabricatorRepositoryTransaction::TYPE_PUSH_POLICY: - return $object->getPushPolicy(); - case PhabricatorRepositoryTransaction::TYPE_DANGEROUS: - return $object->shouldAllowDangerousChanges(); - case PhabricatorRepositoryTransaction::TYPE_ENORMOUS: - return $object->shouldAllowEnormousChanges(); - case PhabricatorRepositoryTransaction::TYPE_SLUG: - return $object->getRepositorySlug(); - case PhabricatorRepositoryTransaction::TYPE_SERVICE: - return $object->getAlmanacServicePHID(); - case PhabricatorRepositoryTransaction::TYPE_SYMBOLS_LANGUAGE: - return $object->getSymbolLanguages(); - case PhabricatorRepositoryTransaction::TYPE_SYMBOLS_SOURCES: - return $object->getSymbolSources(); - case PhabricatorRepositoryTransaction::TYPE_STAGING_URI: - return $object->getDetail('staging-uri'); - case PhabricatorRepositoryTransaction::TYPE_AUTOMATION_BLUEPRINTS: - return $object->getDetail('automation.blueprintPHIDs', array()); - case PhabricatorRepositoryTransaction::TYPE_CALLSIGN: - return $object->getCallsign(); - } - } - - protected function getCustomTransactionNewValue( - PhabricatorLiskDAO $object, - PhabricatorApplicationTransaction $xaction) { - - switch ($xaction->getTransactionType()) { - case PhabricatorRepositoryTransaction::TYPE_ACTIVATE: - case PhabricatorRepositoryTransaction::TYPE_NAME: - case PhabricatorRepositoryTransaction::TYPE_DESCRIPTION: - case PhabricatorRepositoryTransaction::TYPE_ENCODING: - case PhabricatorRepositoryTransaction::TYPE_DEFAULT_BRANCH: - case PhabricatorRepositoryTransaction::TYPE_TRACK_ONLY: - case PhabricatorRepositoryTransaction::TYPE_AUTOCLOSE_ONLY: - case PhabricatorRepositoryTransaction::TYPE_SVN_SUBPATH: - case PhabricatorRepositoryTransaction::TYPE_VCS: - case PhabricatorRepositoryTransaction::TYPE_PUSH_POLICY: - case PhabricatorRepositoryTransaction::TYPE_DANGEROUS: - case PhabricatorRepositoryTransaction::TYPE_ENORMOUS: - case PhabricatorRepositoryTransaction::TYPE_SERVICE: - case PhabricatorRepositoryTransaction::TYPE_SYMBOLS_LANGUAGE: - case PhabricatorRepositoryTransaction::TYPE_SYMBOLS_SOURCES: - case PhabricatorRepositoryTransaction::TYPE_STAGING_URI: - case PhabricatorRepositoryTransaction::TYPE_AUTOMATION_BLUEPRINTS: - return $xaction->getNewValue(); - case PhabricatorRepositoryTransaction::TYPE_SLUG: - case PhabricatorRepositoryTransaction::TYPE_CALLSIGN: - $name = $xaction->getNewValue(); - if (strlen($name)) { - return $name; - } - return null; - case PhabricatorRepositoryTransaction::TYPE_NOTIFY: - case PhabricatorRepositoryTransaction::TYPE_AUTOCLOSE: - return (int)$xaction->getNewValue(); - } - } - - protected function applyCustomInternalTransaction( - PhabricatorLiskDAO $object, - PhabricatorApplicationTransaction $xaction) { - - switch ($xaction->getTransactionType()) { - case PhabricatorRepositoryTransaction::TYPE_VCS: - $object->setVersionControlSystem($xaction->getNewValue()); - break; - case PhabricatorRepositoryTransaction::TYPE_ACTIVATE: - $active = $xaction->getNewValue(); - - // The first time a repository is activated, clear the "new repository" - // flag so we stop showing setup hints. - if ($active) { - $object->setDetail('newly-initialized', false); - } - - $object->setDetail('tracking-enabled', $active); - break; - case PhabricatorRepositoryTransaction::TYPE_NAME: - $object->setName($xaction->getNewValue()); - break; - case PhabricatorRepositoryTransaction::TYPE_DESCRIPTION: - $object->setDetail('description', $xaction->getNewValue()); - break; - case PhabricatorRepositoryTransaction::TYPE_DEFAULT_BRANCH: - $object->setDetail('default-branch', $xaction->getNewValue()); - break; - case PhabricatorRepositoryTransaction::TYPE_TRACK_ONLY: - $object->setDetail( - 'branch-filter', - array_fill_keys($xaction->getNewValue(), true)); - break; - case PhabricatorRepositoryTransaction::TYPE_AUTOCLOSE_ONLY: - $object->setDetail( - 'close-commits-filter', - array_fill_keys($xaction->getNewValue(), true)); - break; - case PhabricatorRepositoryTransaction::TYPE_SVN_SUBPATH: - $object->setDetail('svn-subpath', $xaction->getNewValue()); - break; - case PhabricatorRepositoryTransaction::TYPE_NOTIFY: - $object->setDetail('herald-disabled', (int)!$xaction->getNewValue()); - break; - case PhabricatorRepositoryTransaction::TYPE_AUTOCLOSE: - $object->setDetail('disable-autoclose', (int)!$xaction->getNewValue()); - break; - case PhabricatorRepositoryTransaction::TYPE_PUSH_POLICY: - return $object->setPushPolicy($xaction->getNewValue()); - case PhabricatorRepositoryTransaction::TYPE_DANGEROUS: - $object->setDetail('allow-dangerous-changes', $xaction->getNewValue()); - return; - case PhabricatorRepositoryTransaction::TYPE_ENORMOUS: - $object->setDetail('allow-enormous-changes', $xaction->getNewValue()); - return; - case PhabricatorRepositoryTransaction::TYPE_SLUG: - $object->setRepositorySlug($xaction->getNewValue()); - return; - case PhabricatorRepositoryTransaction::TYPE_SERVICE: - $object->setAlmanacServicePHID($xaction->getNewValue()); - return; - case PhabricatorRepositoryTransaction::TYPE_SYMBOLS_LANGUAGE: - $object->setDetail('symbol-languages', $xaction->getNewValue()); - return; - case PhabricatorRepositoryTransaction::TYPE_SYMBOLS_SOURCES: - $object->setDetail('symbol-sources', $xaction->getNewValue()); - return; - case PhabricatorRepositoryTransaction::TYPE_STAGING_URI: - $object->setDetail('staging-uri', $xaction->getNewValue()); - return; - case PhabricatorRepositoryTransaction::TYPE_AUTOMATION_BLUEPRINTS: - $object->setDetail( - 'automation.blueprintPHIDs', - $xaction->getNewValue()); - return; - case PhabricatorRepositoryTransaction::TYPE_CALLSIGN: - $object->setCallsign($xaction->getNewValue()); - return; - case PhabricatorRepositoryTransaction::TYPE_ENCODING: - $object->setDetail('encoding', $xaction->getNewValue()); - break; - } - } - - protected function applyCustomExternalTransaction( - PhabricatorLiskDAO $object, - PhabricatorApplicationTransaction $xaction) { - - switch ($xaction->getTransactionType()) { - case PhabricatorRepositoryTransaction::TYPE_AUTOMATION_BLUEPRINTS: - DrydockAuthorization::applyAuthorizationChanges( - $this->getActor(), - $object->getPHID(), - $xaction->getOldValue(), - $xaction->getNewValue()); - break; - } - - } - - protected function validateTransaction( - PhabricatorLiskDAO $object, - $type, - array $xactions) { - - $errors = parent::validateTransaction($object, $type, $xactions); - - switch ($type) { - case PhabricatorRepositoryTransaction::TYPE_AUTOCLOSE_ONLY: - case PhabricatorRepositoryTransaction::TYPE_TRACK_ONLY: - foreach ($xactions as $xaction) { - foreach ($xaction->getNewValue() as $pattern) { - // Check for invalid regular expressions. - $regexp = PhabricatorRepository::extractBranchRegexp($pattern); - if ($regexp !== null) { - $ok = @preg_match($regexp, ''); - if ($ok === false) { - $error = new PhabricatorApplicationTransactionValidationError( - $type, - pht('Invalid'), - pht( - 'Expression "%s" is not a valid regular expression. Note '. - 'that you must include delimiters.', - $regexp), - $xaction); - $errors[] = $error; - continue; - } - } - - // Check for formatting mistakes like `regex(...)` instead of - // `regexp(...)`. - $matches = null; - if (preg_match('/^([^(]+)\\(.*\\)\z/', $pattern, $matches)) { - switch ($matches[1]) { - case 'regexp': - break; - default: - $error = new PhabricatorApplicationTransactionValidationError( - $type, - pht('Invalid'), - pht( - 'Matching function "%s(...)" is not recognized. Valid '. - 'functions are: regexp(...).', - $matches[1]), - $xaction); - $errors[] = $error; - break; - } - } - } - } - break; - - case PhabricatorRepositoryTransaction::TYPE_AUTOMATION_BLUEPRINTS: - foreach ($xactions as $xaction) { - $old = nonempty($xaction->getOldValue(), array()); - $new = nonempty($xaction->getNewValue(), array()); - - $add = array_diff($new, $old); - - $invalid = PhabricatorObjectQuery::loadInvalidPHIDsForViewer( - $this->getActor(), - $add); - if ($invalid) { - $errors[] = new PhabricatorApplicationTransactionValidationError( - $type, - pht('Invalid'), - pht( - 'Some of the selected automation blueprints are invalid '. - 'or restricted: %s.', - implode(', ', $invalid)), - $xaction); - } - } - break; - - case PhabricatorRepositoryTransaction::TYPE_VCS: - $vcs_map = PhabricatorRepositoryType::getAllRepositoryTypes(); - $current_vcs = $object->getVersionControlSystem(); - - if (!$this->getIsNewObject()) { - foreach ($xactions as $xaction) { - if ($xaction->getNewValue() == $current_vcs) { - continue; - } - - $errors[] = new PhabricatorApplicationTransactionValidationError( - $type, - pht('Immutable'), - pht( - 'You can not change the version control system an existing '. - 'repository uses. It can only be set when a repository is '. - 'first created.'), - $xaction); - } - } else { - $value = $object->getVersionControlSystem(); - foreach ($xactions as $xaction) { - $value = $xaction->getNewValue(); - - if (empty($vcs_map[$value])) { - $errors[] = new PhabricatorApplicationTransactionValidationError( - $type, - pht('Invalid'), - pht( - 'Specified version control system must be a VCS '. - 'recognized by Phabricator: %s.', - implode(', ', array_keys($vcs_map))), - $xaction); - } - } - - if (!strlen($value)) { - $error = new PhabricatorApplicationTransactionValidationError( - $type, - pht('Required'), - pht( - 'When creating a repository, you must specify a valid '. - 'underlying version control system: %s.', - implode(', ', array_keys($vcs_map))), - nonempty(last($xactions), null)); - $error->setIsMissingFieldError(true); - $errors[] = $error; - } - } - break; - - case PhabricatorRepositoryTransaction::TYPE_NAME: - $missing = $this->validateIsEmptyTextField( - $object->getName(), - $xactions); - - if ($missing) { - $error = new PhabricatorApplicationTransactionValidationError( - $type, - pht('Required'), - pht('Repository name is required.'), - nonempty(last($xactions), null)); - - $error->setIsMissingFieldError(true); - $errors[] = $error; - } - break; - - case PhabricatorRepositoryTransaction::TYPE_ACTIVATE: - $status_map = PhabricatorRepository::getStatusMap(); - foreach ($xactions as $xaction) { - $status = $xaction->getNewValue(); - if (empty($status_map[$status])) { - $errors[] = new PhabricatorApplicationTransactionValidationError( - $type, - pht('Invalid'), - pht( - 'Repository status "%s" is not valid.', - $status), - $xaction); - } - } - break; - - case PhabricatorRepositoryTransaction::TYPE_ENCODING: - foreach ($xactions as $xaction) { - // Make sure the encoding is valid by converting to UTF-8. This tests - // that the user has mbstring installed, and also that they didn't - // type a garbage encoding name. Note that we're converting from - // UTF-8 to the target encoding, because mbstring is fine with - // converting from a nonsense encoding. - $encoding = $xaction->getNewValue(); - if (!strlen($encoding)) { - continue; - } - - try { - phutil_utf8_convert('.', $encoding, 'UTF-8'); - } catch (Exception $ex) { - $errors[] = new PhabricatorApplicationTransactionValidationError( - $type, - pht('Invalid'), - pht( - 'Repository encoding "%s" is not valid: %s', - $encoding, - $ex->getMessage()), - $xaction); - } - } - break; - - case PhabricatorRepositoryTransaction::TYPE_SLUG: - foreach ($xactions as $xaction) { - $old = $xaction->getOldValue(); - $new = $xaction->getNewValue(); - - if (!strlen($new)) { - continue; - } - - if ($new === $old) { - continue; - } - - try { - PhabricatorRepository::assertValidRepositorySlug($new); - } catch (Exception $ex) { - $errors[] = new PhabricatorApplicationTransactionValidationError( - $type, - pht('Invalid'), - $ex->getMessage(), - $xaction); - continue; - } - - $other = id(new PhabricatorRepositoryQuery()) - ->setViewer(PhabricatorUser::getOmnipotentUser()) - ->withSlugs(array($new)) - ->executeOne(); - if ($other && ($other->getID() !== $object->getID())) { - $errors[] = new PhabricatorApplicationTransactionValidationError( - $type, - pht('Duplicate'), - pht( - 'The selected repository short name is already in use by '. - 'another repository. Choose a unique short name.'), - $xaction); - continue; - } - } - break; - - case PhabricatorRepositoryTransaction::TYPE_CALLSIGN: - foreach ($xactions as $xaction) { - $old = $xaction->getOldValue(); - $new = $xaction->getNewValue(); - - if (!strlen($new)) { - continue; - } - - if ($new === $old) { - continue; - } - - try { - PhabricatorRepository::assertValidCallsign($new); - } catch (Exception $ex) { - $errors[] = new PhabricatorApplicationTransactionValidationError( - $type, - pht('Invalid'), - $ex->getMessage(), - $xaction); - continue; - } - - $other = id(new PhabricatorRepositoryQuery()) - ->setViewer(PhabricatorUser::getOmnipotentUser()) - ->withCallsigns(array($new)) - ->executeOne(); - if ($other && ($other->getID() !== $object->getID())) { - $errors[] = new PhabricatorApplicationTransactionValidationError( - $type, - pht('Duplicate'), - pht( - 'The selected callsign ("%s") is already in use by another '. - 'repository. Choose a unique callsign.', - $new), - $xaction); - continue; - } - } - break; - - case PhabricatorRepositoryTransaction::TYPE_SYMBOLS_SOURCES: - foreach ($xactions as $xaction) { - $old = $object->getSymbolSources(); - $new = $xaction->getNewValue(); - - // If the viewer is adding new repositories, make sure they are - // valid and visible. - $add = array_diff($new, $old); - if (!$add) { - continue; - } - - $repositories = id(new PhabricatorRepositoryQuery()) - ->setViewer($this->getActor()) - ->withPHIDs($add) - ->execute(); - $repositories = mpull($repositories, null, 'getPHID'); - - foreach ($add as $phid) { - if (isset($repositories[$phid])) { - continue; - } - - $errors[] = new PhabricatorApplicationTransactionValidationError( - $type, - pht('Invalid'), - pht( - 'Repository ("%s") does not exist, or you do not have '. - 'permission to see it.', - $phid), - $xaction); - break; - } - } - break; - - } - - return $errors; - } - protected function didCatchDuplicateKeyException( PhabricatorLiskDAO $object, array $xactions, diff --git a/src/applications/repository/management/PhabricatorRepositoryManagementClusterizeWorkflow.php b/src/applications/repository/management/PhabricatorRepositoryManagementClusterizeWorkflow.php index 7178564067..2e2c3fcc92 100644 --- a/src/applications/repository/management/PhabricatorRepositoryManagementClusterizeWorkflow.php +++ b/src/applications/repository/management/PhabricatorRepositoryManagementClusterizeWorkflow.php @@ -117,7 +117,8 @@ final class PhabricatorRepositoryManagementClusterizeWorkflow $xactions = array(); $xactions[] = id(new PhabricatorRepositoryTransaction()) - ->setTransactionType(PhabricatorRepositoryTransaction::TYPE_SERVICE) + ->setTransactionType( + PhabricatorRepositoryServiceTransaction::TRANSACTIONTYPE) ->setNewValue($service_phid); id(new PhabricatorRepositoryEditor()) diff --git a/src/applications/repository/storage/PhabricatorRepositoryTransaction.php b/src/applications/repository/storage/PhabricatorRepositoryTransaction.php index 57f554349c..85c354ba67 100644 --- a/src/applications/repository/storage/PhabricatorRepositoryTransaction.php +++ b/src/applications/repository/storage/PhabricatorRepositoryTransaction.php @@ -1,29 +1,7 @@ getOldValue(); - $new = $this->getNewValue(); - - switch ($this->getTransactionType()) { - case self::TYPE_PUSH_POLICY: - case self::TYPE_SERVICE: - if ($old) { - $phids[] = $old; - } - if ($new) { - $phids[] = $new; - } - break; - case self::TYPE_SYMBOLS_SOURCES: - case self::TYPE_AUTOMATION_BLUEPRINTS: - if ($old) { - $phids = array_merge($phids, $old); - } - if ($new) { - $phids = array_merge($phids, $new); - } - break; - } - - return $phids; - } - - public function shouldHide() { - $old = $this->getOldValue(); - $new = $this->getNewValue(); - - switch ($this->getTransactionType()) { - case self::TYPE_NAME: - // Hide these on create, they aren't interesting and we have an - // explicit "create" transaction. - if (!strlen($old)) { - return true; - } - break; - } - - return parent::shouldHide(); - } - - public function getIcon() { - switch ($this->getTransactionType()) { - case self::TYPE_VCS: - return 'fa-plus'; - } - return parent::getIcon(); - } - - public function getColor() { - switch ($this->getTransactionType()) { - case self::TYPE_VCS: - return 'green'; - } - return parent::getIcon(); - } - - public function getTitle() { - $author_phid = $this->getAuthorPHID(); - - $old = $this->getOldValue(); - $new = $this->getNewValue(); - - switch ($this->getTransactionType()) { - case self::TYPE_VCS: - return pht( - '%s created this repository.', - $this->renderHandleLink($author_phid)); - case self::TYPE_ACTIVATE: - // TODO: Old versions of this transaction use a boolean value, but - // should be migrated. - $is_deactivate = - (!$new) || - ($new == PhabricatorRepository::STATUS_INACTIVE); - - if (!$is_deactivate) { - return pht( - '%s activated this repository.', - $this->renderHandleLink($author_phid)); - } else { - return pht( - '%s deactivated this repository.', - $this->renderHandleLink($author_phid)); - } - case self::TYPE_NAME: - return pht( - '%s renamed this repository from "%s" to "%s".', - $this->renderHandleLink($author_phid), - $old, - $new); - case self::TYPE_DESCRIPTION: - return pht( - '%s updated the description of this repository.', - $this->renderHandleLink($author_phid)); - case self::TYPE_ENCODING: - if (strlen($old) && !strlen($new)) { - return pht( - '%s removed the "%s" encoding configured for this repository.', - $this->renderHandleLink($author_phid), - $old); - } else if (strlen($new) && !strlen($old)) { - return pht( - '%s set the encoding for this repository to "%s".', - $this->renderHandleLink($author_phid), - $new); - } else { - return pht( - '%s changed the repository encoding from "%s" to "%s".', - $this->renderHandleLink($author_phid), - $old, - $new); - } - case self::TYPE_DEFAULT_BRANCH: - if (!strlen($new)) { - return pht( - '%s removed "%s" as the default branch.', - $this->renderHandleLink($author_phid), - $old); - } else if (!strlen($old)) { - return pht( - '%s set the default branch to "%s".', - $this->renderHandleLink($author_phid), - $new); - } else { - return pht( - '%s changed the default branch from "%s" to "%s".', - $this->renderHandleLink($author_phid), - $old, - $new); - } - break; - case self::TYPE_TRACK_ONLY: - if (!$new) { - return pht( - '%s set this repository to track all branches.', - $this->renderHandleLink($author_phid)); - } else if (!$old) { - return pht( - '%s set this repository to track branches: %s.', - $this->renderHandleLink($author_phid), - implode(', ', $new)); - } else { - return pht( - '%s changed track branches from "%s" to "%s".', - $this->renderHandleLink($author_phid), - implode(', ', $old), - implode(', ', $new)); - } - break; - case self::TYPE_AUTOCLOSE_ONLY: - if (!$new) { - return pht( - '%s set this repository to autoclose on all branches.', - $this->renderHandleLink($author_phid)); - } else if (!$old) { - return pht( - '%s set this repository to autoclose on branches: %s.', - $this->renderHandleLink($author_phid), - implode(', ', $new)); - } else { - return pht( - '%s changed autoclose branches from "%s" to "%s".', - $this->renderHandleLink($author_phid), - implode(', ', $old), - implode(', ', $new)); - } - break; - case self::TYPE_SVN_SUBPATH: - if (!strlen($new)) { - return pht( - '%s removed "%s" as the Import Only path.', - $this->renderHandleLink($author_phid), - $old); - } else if (!strlen($old)) { - return pht( - '%s set the repository to import only "%s".', - $this->renderHandleLink($author_phid), - $new); - } else { - return pht( - '%s changed the import path from "%s" to "%s".', - $this->renderHandleLink($author_phid), - $old, - $new); - } - break; - case self::TYPE_NOTIFY: - if ($new) { - return pht( - '%s enabled notifications and publishing for this repository.', - $this->renderHandleLink($author_phid)); - } else { - return pht( - '%s disabled notifications and publishing for this repository.', - $this->renderHandleLink($author_phid)); - } - break; - case self::TYPE_AUTOCLOSE: - if ($new) { - return pht( - '%s enabled autoclose for this repository.', - $this->renderHandleLink($author_phid)); - } else { - return pht( - '%s disabled autoclose for this repository.', - $this->renderHandleLink($author_phid)); - } - break; - case self::TYPE_PUSH_POLICY: - return pht( - '%s changed the push policy of this repository from "%s" to "%s".', - $this->renderHandleLink($author_phid), - $this->renderPolicyName($old, 'old'), - $this->renderPolicyName($new, 'new')); - case self::TYPE_DANGEROUS: - if ($new) { - return pht( - '%s disabled protection against dangerous changes.', - $this->renderHandleLink($author_phid)); - } else { - return pht( - '%s enabled protection against dangerous changes.', - $this->renderHandleLink($author_phid)); - } - case self::TYPE_ENORMOUS: - if ($new) { - return pht( - '%s disabled protection against enormous changes.', - $this->renderHandleLink($author_phid)); - } else { - return pht( - '%s enabled protection against enormous changes.', - $this->renderHandleLink($author_phid)); - } - case self::TYPE_SLUG: - if (strlen($old) && !strlen($new)) { - return pht( - '%s removed the short name of this repository.', - $this->renderHandleLink($author_phid)); - } else if (strlen($new) && !strlen($old)) { - return pht( - '%s set the short name of this repository to "%s".', - $this->renderHandleLink($author_phid), - $new); - } else { - return pht( - '%s changed the short name of this repository from "%s" to "%s".', - $this->renderHandleLink($author_phid), - $old, - $new); - } - case self::TYPE_SERVICE: - if (strlen($old) && !strlen($new)) { - return pht( - '%s moved storage for this repository from %s to local.', - $this->renderHandleLink($author_phid), - $this->renderHandleLink($old)); - } else if (!strlen($old) && strlen($new)) { - // TODO: Possibly, we should distinguish between automatic assignment - // on creation vs explicit adjustment. - return pht( - '%s set storage for this repository to %s.', - $this->renderHandleLink($author_phid), - $this->renderHandleLink($new)); - } else { - return pht( - '%s moved storage for this repository from %s to %s.', - $this->renderHandleLink($author_phid), - $this->renderHandleLink($old), - $this->renderHandleLink($new)); - } - case self::TYPE_SYMBOLS_SOURCES: - return pht( - '%s changed symbol sources from %s to %s.', - $this->renderHandleLink($author_phid), - empty($old) ? pht('None') : $this->renderHandleList($old), - empty($new) ? pht('None') : $this->renderHandleList($new)); - - case self::TYPE_SYMBOLS_LANGUAGE: - return pht('%s changed indexed languages from %s to %s.', - $this->renderHandleLink($author_phid), - $old ? implode(', ', $old) : pht('Any'), - $new ? implode(', ', $new) : pht('Any')); - - case self::TYPE_STAGING_URI: - if (!$old) { - return pht( - '%s set "%s" as the staging area for this repository.', - $this->renderHandleLink($author_phid), - $new); - } else if (!$new) { - return pht( - '%s removed "%s" as the staging area for this repository.', - $this->renderHandleLink($author_phid), - $old); - } else { - return pht( - '%s changed the staging area for this repository from '. - '"%s" to "%s".', - $this->renderHandleLink($author_phid), - $old, - $new); - } - - case self::TYPE_AUTOMATION_BLUEPRINTS: - $add = array_diff($new, $old); - $rem = array_diff($old, $new); - - if ($add && $rem) { - return pht( - '%s changed %s automation blueprint(s), '. - 'added %s: %s; removed %s: %s.', - $this->renderHandleLink($author_phid), - new PhutilNumber(count($add) + count($rem)), - new PhutilNumber(count($add)), - $this->renderHandleList($add), - new PhutilNumber(count($rem)), - $this->renderHandleList($rem)); - } else if ($add) { - return pht( - '%s added %s automation blueprint(s): %s.', - $this->renderHandleLink($author_phid), - new PhutilNumber(count($add)), - $this->renderHandleList($add)); - } else { - return pht( - '%s removed %s automation blueprint(s): %s.', - $this->renderHandleLink($author_phid), - new PhutilNumber(count($rem)), - $this->renderHandleList($rem)); - } - - case self::TYPE_CALLSIGN: - if ($old === null) { - return pht( - '%s set the callsign for this repository to "%s".', - $this->renderHandleLink($author_phid), - $new); - } else if ($new === null) { - return pht( - '%s removed the callsign ("%s") for this repository.', - $this->renderHandleLink($author_phid), - $old); - } else { - return pht( - '%s changed the callsign for this repository from "%s" to "%s".', - $this->renderHandleLink($author_phid), - $old, - $new); - } - - } - - return parent::getTitle(); - } - - public function hasChangeDetails() { - switch ($this->getTransactionType()) { - case self::TYPE_DESCRIPTION: - return true; - } - return parent::hasChangeDetails(); - } - - public function renderChangeDetails(PhabricatorUser $viewer) { - return $this->renderTextCorpusChangeDetails( - $viewer, - $this->getOldValue(), - $this->getNewValue()); + public function getBaseTransactionClass() { + return 'PhabricatorRepositoryTransactionType'; } } diff --git a/src/applications/repository/xaction/PhabricatorRepositoryActivateTransaction.php b/src/applications/repository/xaction/PhabricatorRepositoryActivateTransaction.php new file mode 100644 index 0000000000..31298c55aa --- /dev/null +++ b/src/applications/repository/xaction/PhabricatorRepositoryActivateTransaction.php @@ -0,0 +1,61 @@ +isTracked(); + } + + public function applyInternalEffects($object, $value) { + // The first time a repository is activated, clear the "new repository" + // flag so we stop showing setup hints. + if ($value) { + $object->setDetail('newly-initialized', false); + } + + $object->setDetail('tracking-enabled', $value); + } + + public function getTitle() { + $new = $this->getNewValue(); + + // TODO: Old versions of this transaction use a boolean value, but + // should be migrated. + $is_deactivate = + (!$new) || + ($new == PhabricatorRepository::STATUS_INACTIVE); + + if (!$is_deactivate) { + return pht( + '%s activated this repository.', + $this->renderAuthor()); + } else { + return pht( + '%s deactivated this repository.', + $this->renderAuthor()); + } + } + + public function validateTransactions($object, array $xactions) { + $errors = array(); + + $status_map = PhabricatorRepository::getStatusMap(); + foreach ($xactions as $xaction) { + $status = $xaction->getNewValue(); + if (empty($status_map[$status])) { + $errors[] = $this->newInvalidError( + pht( + 'Repository status "%s" is not valid. Valid statuses are: %s.', + $status, + implode(', ', array_keys($status_map))), + $xaction); + } + } + + return $errors; + } + +} diff --git a/src/applications/repository/xaction/PhabricatorRepositoryAutocloseOnlyTransaction.php b/src/applications/repository/xaction/PhabricatorRepositoryAutocloseOnlyTransaction.php new file mode 100644 index 0000000000..feacc73c44 --- /dev/null +++ b/src/applications/repository/xaction/PhabricatorRepositoryAutocloseOnlyTransaction.php @@ -0,0 +1,42 @@ +getDetail('close-commits-filter', array())); + } + + public function applyInternalEffects($object, $value) { + $object->setDetail('close-commits-filter', array_fill_keys($value, true)); + } + + public function getTitle() { + $old = $this->getOldValue(); + $new = $this->getNewValue(); + + if (!$new) { + return pht( + '%s set this repository to autoclose on all branches.', + $this->renderAuthor()); + } else if (!$old) { + return pht( + '%s set this repository to autoclose on branches: %s.', + $this->renderAuthor(), + $this->renderValue(implode(', ', $new))); + } else { + return pht( + '%s changed autoclose branches from %s to %s.', + $this->renderAuthor(), + $this->renderValue(implode(', ', $old)), + $this->renderValue(implode(', ', $new))); + } + } + + public function validateTransactions($object, array $xactions) { + return $this->validateRefList($object, $xactions); + } + +} diff --git a/src/applications/repository/xaction/PhabricatorRepositoryAutocloseTransaction.php b/src/applications/repository/xaction/PhabricatorRepositoryAutocloseTransaction.php new file mode 100644 index 0000000000..6ab677fb4a --- /dev/null +++ b/src/applications/repository/xaction/PhabricatorRepositoryAutocloseTransaction.php @@ -0,0 +1,34 @@ +getDetail('disable-autoclose'); + } + + public function generateNewValue($object, $value) { + return (int)$value; + } + + public function applyInternalEffects($object, $value) { + $object->setDetail('disable-autoclose', (int)!$value); + } + + public function getTitle() { + $new = $this->getNewValue(); + + if ($new) { + return pht( + '%s enabled autoclose for this repository.', + $this->renderAuthor()); + } else { + return pht( + '%s disabled autoclose for this repository.', + $this->renderAuthor()); + } + } + +} diff --git a/src/applications/repository/xaction/PhabricatorRepositoryBlueprintsTransaction.php b/src/applications/repository/xaction/PhabricatorRepositoryBlueprintsTransaction.php new file mode 100644 index 0000000000..aae164d706 --- /dev/null +++ b/src/applications/repository/xaction/PhabricatorRepositoryBlueprintsTransaction.php @@ -0,0 +1,81 @@ +getDetail('automation.blueprintPHIDs', array()); + } + + public function applyInternalEffects($object, $value) { + $object->setDetail('automation.blueprintPHIDs', $value); + } + + public function applyExternalEffects($object, $value) { + DrydockAuthorization::applyAuthorizationChanges( + $this->getActor(), + $object->getPHID(), + $this->getOldValue(), + $this->getNewValue()); + } + + public function getTitle() { + $old = $this->getOldValue(); + $new = $this->getNewValue(); + + $add = array_diff($new, $old); + $rem = array_diff($old, $new); + + if ($add && $rem) { + return pht( + '%s changed %s automation blueprint(s), '. + 'added %s: %s; removed %s: %s.', + $this->renderAuthor(), + new PhutilNumber(count($add) + count($rem)), + new PhutilNumber(count($add)), + $this->renderHandleList($add), + new PhutilNumber(count($rem)), + $this->renderHandleList($rem)); + } else if ($add) { + return pht( + '%s added %s automation blueprint(s): %s.', + $this->renderAuthor(), + new PhutilNumber(count($add)), + $this->renderHandleList($add)); + } else { + return pht( + '%s removed %s automation blueprint(s): %s.', + $this->renderAuthor(), + new PhutilNumber(count($rem)), + $this->renderHandleList($rem)); + } + } + + public function validateTransactions($object, array $xactions) { + $errors = array(); + + foreach ($xactions as $xaction) { + $old = nonempty($xaction->getOldValue(), array()); + $new = nonempty($xaction->getNewValue(), array()); + + $add = array_diff($new, $old); + + $invalid = PhabricatorObjectQuery::loadInvalidPHIDsForViewer( + $this->getActor(), + $add); + if ($invalid) { + $errors[] = $this->newInvalidError( + pht( + 'Some of the selected automation blueprints are invalid '. + 'or restricted: %s.', + implode(', ', $invalid)), + $xaction); + } + } + + return $errors; + } + +} diff --git a/src/applications/repository/xaction/PhabricatorRepositoryCallsignTransaction.php b/src/applications/repository/xaction/PhabricatorRepositoryCallsignTransaction.php new file mode 100644 index 0000000000..ab13c6a571 --- /dev/null +++ b/src/applications/repository/xaction/PhabricatorRepositoryCallsignTransaction.php @@ -0,0 +1,90 @@ +getCallsign(); + } + + public function generateNewValue($object, $value) { + if (strlen($value)) { + return $value; + } + + return null; + } + + public function applyInternalEffects($object, $value) { + $object->setCallsign($value); + } + + public function getTitle() { + $old = $this->getOldValue(); + $new = $this->getNewValue(); + + if (!strlen($old)) { + return pht( + '%s set the callsign for this repository to %s.', + $this->renderAuthor(), + $this->renderNewValue()); + } else if (!strlen($new)) { + return pht( + '%s removed the callsign (%s) for this repository.', + $this->renderAuthor(), + $this->renderOldValue()); + } else { + return pht( + '%s changed the callsign for this repository from %s to %s.', + $this->renderAuthor(), + $this->renderOldValue(), + $this->renderNewValue()); + } + } + + public function validateTransactions($object, array $xactions) { + $errors = array(); + + foreach ($xactions as $xaction) { + $old = $xaction->getOldValue(); + $new = $xaction->getNewValue(); + + if (!strlen($new)) { + continue; + } + + if ($new === $old) { + continue; + } + + try { + PhabricatorRepository::assertValidCallsign($new); + } catch (Exception $ex) { + $errors[] = $this->newInvalidError( + $ex->getMessage(), + $xaction); + continue; + } + + $other = id(new PhabricatorRepositoryQuery()) + ->setViewer(PhabricatorUser::getOmnipotentUser()) + ->withCallsigns(array($new)) + ->executeOne(); + if ($other && ($other->getID() !== $object->getID())) { + $errors[] = $this->newError( + pht('Duplicate'), + pht( + 'The selected callsign ("%s") is already in use by another '. + 'repository. Choose a unique callsign.', + $new), + $xaction); + continue; + } + } + + return $errors; + } + +} diff --git a/src/applications/repository/xaction/PhabricatorRepositoryDangerousTransaction.php b/src/applications/repository/xaction/PhabricatorRepositoryDangerousTransaction.php new file mode 100644 index 0000000000..218e1d1182 --- /dev/null +++ b/src/applications/repository/xaction/PhabricatorRepositoryDangerousTransaction.php @@ -0,0 +1,30 @@ +shouldAllowDangerousChanges(); + } + + public function applyInternalEffects($object, $value) { + $object->setDetail('allow-dangerous-changes', $value); + } + + public function getTitle() { + $new = $this->getNewValue(); + + if ($new) { + return pht( + '%s disabled protection against dangerous changes.', + $this->renderAuthor()); + } else { + return pht( + '%s enabled protection against dangerous changes.', + $this->renderAuthor()); + } + } + +} diff --git a/src/applications/repository/xaction/PhabricatorRepositoryDefaultBranchTransaction.php b/src/applications/repository/xaction/PhabricatorRepositoryDefaultBranchTransaction.php new file mode 100644 index 0000000000..23eebf60c8 --- /dev/null +++ b/src/applications/repository/xaction/PhabricatorRepositoryDefaultBranchTransaction.php @@ -0,0 +1,39 @@ +getDetail('default-branch'); + } + + public function applyInternalEffects($object, $value) { + $object->setDetail('default-branch', $value); + } + + public function getTitle() { + $old = $this->getOldValue(); + $new = $this->getNewValue(); + + if (!strlen($new)) { + return pht( + '%s removed %s as the default branch.', + $this->renderAuthor(), + $this->renderOldValue()); + } else if (!strlen($old)) { + return pht( + '%s set the default branch to %s.', + $this->renderAuthor(), + $this->renderNewValue()); + } else { + return pht( + '%s changed the default branch from %s to %s.', + $this->renderAuthor(), + $this->renderOldValue(), + $this->renderNewValue()); + } + } + +} diff --git a/src/applications/repository/xaction/PhabricatorRepositoryDescriptionTransaction.php b/src/applications/repository/xaction/PhabricatorRepositoryDescriptionTransaction.php new file mode 100644 index 0000000000..9e86d80d06 --- /dev/null +++ b/src/applications/repository/xaction/PhabricatorRepositoryDescriptionTransaction.php @@ -0,0 +1,49 @@ +getDetail('description'); + } + + public function applyInternalEffects($object, $value) { + $object->setDetail('description', $value); + } + + public function getTitle() { + return pht( + '%s updated the description for this repository.', + $this->renderAuthor()); + } + + public function hasChangeDetailView() { + return true; + } + + public function getMailDiffSectionHeader() { + return pht('CHANGES TO REPOSITORY DESCRIPTION'); + } + + public function newChangeDetailView() { + $viewer = $this->getViewer(); + + return id(new PhabricatorApplicationTransactionTextDiffDetailView()) + ->setViewer($viewer) + ->setOldText($this->getOldValue()) + ->setNewText($this->getNewValue()); + } + + public function newRemarkupChanges() { + $changes = array(); + + $changes[] = $this->newRemarkupChange() + ->setOldValue($this->getOldValue()) + ->setNewValue($this->getNewValue()); + + return $changes; + } + +} diff --git a/src/applications/repository/xaction/PhabricatorRepositoryEncodingTransaction.php b/src/applications/repository/xaction/PhabricatorRepositoryEncodingTransaction.php new file mode 100644 index 0000000000..ef129da832 --- /dev/null +++ b/src/applications/repository/xaction/PhabricatorRepositoryEncodingTransaction.php @@ -0,0 +1,68 @@ +getDetail('encoding'); + } + + public function applyInternalEffects($object, $value) { + $object->setDetail('encoding', $value); + } + + public function getTitle() { + $old = $this->getOldValue(); + $new = $this->getNewValue(); + + if (strlen($old) && !strlen($new)) { + return pht( + '%s removed the %s encoding configured for this repository.', + $this->renderAuthor(), + $this->renderOldValue()); + } else if (strlen($new) && !strlen($old)) { + return pht( + '%s set the encoding for this repository to %s.', + $this->renderAuthor(), + $this->renderNewValue()); + } else { + return pht( + '%s changed the repository encoding from %s to %s.', + $this->renderAuthor(), + $this->renderOldValue(), + $this->renderNewValue()); + } + } + + public function validateTransactions($object, array $xactions) { + $errors = array(); + + foreach ($xactions as $xaction) { + // Make sure the encoding is valid by converting to UTF-8. This tests + // that the user has mbstring installed, and also that they didn't + // type a garbage encoding name. Note that we're converting from + // UTF-8 to the target encoding, because mbstring is fine with + // converting from a nonsense encoding. + $encoding = $xaction->getNewValue(); + if (!strlen($encoding)) { + continue; + } + + try { + phutil_utf8_convert('.', $encoding, 'UTF-8'); + } catch (Exception $ex) { + $errors[] = $this->newInvalidError( + pht( + 'Repository encoding "%s" is not valid: %s', + $encoding, + $ex->getMessage()), + $xaction); + } + } + + return $errors; + } + +} diff --git a/src/applications/repository/xaction/PhabricatorRepositoryEnormousTransaction.php b/src/applications/repository/xaction/PhabricatorRepositoryEnormousTransaction.php new file mode 100644 index 0000000000..aae18ba8ff --- /dev/null +++ b/src/applications/repository/xaction/PhabricatorRepositoryEnormousTransaction.php @@ -0,0 +1,30 @@ +shouldAllowEnormousChanges(); + } + + public function applyInternalEffects($object, $value) { + $object->setDetail('allow-enormous-changes', $value); + } + + public function getTitle() { + $new = $this->getNewValue(); + + if ($new) { + return pht( + '%s disabled protection against enormous changes.', + $this->renderAuthor()); + } else { + return pht( + '%s enabled protection against enormous changes.', + $this->renderAuthor()); + } + } + +} diff --git a/src/applications/repository/xaction/PhabricatorRepositoryNameTransaction.php b/src/applications/repository/xaction/PhabricatorRepositoryNameTransaction.php new file mode 100644 index 0000000000..c168037c84 --- /dev/null +++ b/src/applications/repository/xaction/PhabricatorRepositoryNameTransaction.php @@ -0,0 +1,35 @@ +getName(); + } + + public function applyInternalEffects($object, $value) { + $object->setName($value); + } + + public function getTitle() { + return pht( + '%s renamed this repository from %s to %s.', + $this->renderAuthor(), + $this->renderOldValue(), + $this->renderNewValue()); + } + + public function validateTransactions($object, array $xactions) { + $errors = array(); + + if ($this->isEmptyTextTransaction($object->getName(), $xactions)) { + $errors[] = $this->newRequiredError( + pht('Repositories must have a name.')); + } + + return $errors; + } + +} diff --git a/src/applications/repository/xaction/PhabricatorRepositoryNotifyTransaction.php b/src/applications/repository/xaction/PhabricatorRepositoryNotifyTransaction.php new file mode 100644 index 0000000000..4ffe1918ff --- /dev/null +++ b/src/applications/repository/xaction/PhabricatorRepositoryNotifyTransaction.php @@ -0,0 +1,34 @@ +getDetail('herald-disabled'); + } + + public function generateNewValue($object, $value) { + return (int)$value; + } + + public function applyInternalEffects($object, $value) { + $object->setDetail('herald-disabled', (int)!$value); + } + + public function getTitle() { + $new = $this->getNewValue(); + + if ($new) { + return pht( + '%s enabled notifications and publishing for this repository.', + $this->renderAuthor()); + } else { + return pht( + '%s disabled notifications and publishing for this repository.', + $this->renderAuthor()); + } + } + +} diff --git a/src/applications/repository/xaction/PhabricatorRepositoryPushPolicyTransaction.php b/src/applications/repository/xaction/PhabricatorRepositoryPushPolicyTransaction.php new file mode 100644 index 0000000000..1bd8ce9e47 --- /dev/null +++ b/src/applications/repository/xaction/PhabricatorRepositoryPushPolicyTransaction.php @@ -0,0 +1,24 @@ +getPushPolicy(); + } + + public function applyInternalEffects($object, $value) { + $object->setPushPolicy($value); + } + + public function getTitle() { + return pht( + '%s changed the push policy of this repository from %s to %s.', + $this->renderAuthor(), + $this->renderOldPolicy(), + $this->renderNewPolicy()); + } + +} diff --git a/src/applications/repository/xaction/PhabricatorRepositorySVNSubpathTransaction.php b/src/applications/repository/xaction/PhabricatorRepositorySVNSubpathTransaction.php new file mode 100644 index 0000000000..10de2d9980 --- /dev/null +++ b/src/applications/repository/xaction/PhabricatorRepositorySVNSubpathTransaction.php @@ -0,0 +1,39 @@ +getDetail('svn-subpath'); + } + + public function applyInternalEffects($object, $value) { + $object->setDetail('svn-subpath', $value); + } + + public function getTitle() { + $old = $this->getOldValue(); + $new = $this->getNewValue(); + + if (!strlen($new)) { + return pht( + '%s removed %s as the "Import Only" path.', + $this->renderAuthor(), + $this->renderOldValue()); + } else if (!strlen($old)) { + return pht( + '%s set the repository "Import Only" path to %s.', + $this->renderAuthor(), + $this->renderNewValue()); + } else { + return pht( + '%s changed the "Import Only" path from %s to %s.', + $this->renderAuthor(), + $this->renderOldValue(), + $this->renderNewValue()); + } + } + +} diff --git a/src/applications/repository/xaction/PhabricatorRepositoryServiceTransaction.php b/src/applications/repository/xaction/PhabricatorRepositoryServiceTransaction.php new file mode 100644 index 0000000000..f6d89a61f5 --- /dev/null +++ b/src/applications/repository/xaction/PhabricatorRepositoryServiceTransaction.php @@ -0,0 +1,59 @@ +getAlmanacServicePHID(); + } + + public function generateNewValue($object, $value) { + if (strlen($value)) { + return $value; + } + + return null; + } + + public function applyInternalEffects($object, $value) { + $object->setAlmanacServicePHID($value); + } + + public function getTitle() { + $old = $this->getOldValue(); + $new = $this->getOldValue(); + + if (strlen($old) && !strlen($new)) { + return pht( + '%s moved storage for this repository from %s to local.', + $this->renderAuthor(), + $this->renderOldHandle()); + } else if (!strlen($old) && strlen($new)) { + // TODO: Possibly, we should distinguish between automatic assignment + // on creation vs explicit adjustment. + return pht( + '%s set storage for this repository to %s.', + $this->renderAuthor(), + $this->renderNewHandle()); + } else { + return pht( + '%s moved storage for this repository from %s to %s.', + $this->renderAuthor(), + $this->renderOldHandle(), + $this->renderNewHandle()); + } + } + + public function validateTransactions($object, array $xactions) { + $errors = array(); + + // TODO: This could use some validation, values should be valid Almanac + // services of appropriate types. It's only reachable via the CLI so it's + // difficult to get wrong in practice. + + return $errors; + } + +} diff --git a/src/applications/repository/xaction/PhabricatorRepositorySlugTransaction.php b/src/applications/repository/xaction/PhabricatorRepositorySlugTransaction.php new file mode 100644 index 0000000000..79e8f7121e --- /dev/null +++ b/src/applications/repository/xaction/PhabricatorRepositorySlugTransaction.php @@ -0,0 +1,88 @@ +getRepositorySlug(); + } + + public function generateNewValue($object, $value) { + if (strlen($value)) { + return $value; + } + + return null; + } + + public function applyInternalEffects($object, $value) { + $object->setRepositorySlug($value); + } + + public function getTitle() { + $old = $this->getOldValue(); + $new = $this->getNewValue(); + + if (strlen($old) && !strlen($new)) { + return pht( + '%s removed the short name of this repository.', + $this->renderAuthor()); + } else if (strlen($new) && !strlen($old)) { + return pht( + '%s set the short name of this repository to %s.', + $this->renderAuthor(), + $this->renderNewValue()); + } else { + return pht( + '%s changed the short name of this repository from %s to %s.', + $this->renderAuthor(), + $this->renderOldValue(), + $this->renderNewValue()); + } + } + + public function validateTransactions($object, array $xactions) { + $errors = array(); + + foreach ($xactions as $xaction) { + $old = $xaction->getOldValue(); + $new = $xaction->getNewValue(); + + if (!strlen($new)) { + continue; + } + + if ($new === $old) { + continue; + } + + try { + PhabricatorRepository::assertValidRepositorySlug($new); + } catch (Exception $ex) { + $errors[] = $this->newInvalidError( + $ex->getMessage(), + $xaction); + continue; + } + + $other = id(new PhabricatorRepositoryQuery()) + ->setViewer(PhabricatorUser::getOmnipotentUser()) + ->withSlugs(array($new)) + ->executeOne(); + if ($other && ($other->getID() !== $object->getID())) { + $errors[] = $this->newError( + pht('Duplicate'), + pht( + 'The selected repository short name is already in use by '. + 'another repository. Choose a unique short name.'), + $xaction); + continue; + } + } + + return $errors; + } + +} diff --git a/src/applications/repository/xaction/PhabricatorRepositoryStagingURITransaction.php b/src/applications/repository/xaction/PhabricatorRepositoryStagingURITransaction.php new file mode 100644 index 0000000000..4297d5e244 --- /dev/null +++ b/src/applications/repository/xaction/PhabricatorRepositoryStagingURITransaction.php @@ -0,0 +1,68 @@ +getDetail('staging-uri'); + } + + public function applyInternalEffects($object, $value) { + $object->setDetail('staging-uri', $value); + } + + public function getTitle() { + $old = $this->getOldValue(); + $new = $this->getNewValue(); + + if (!strlen($old)) { + return pht( + '%s set %s as the staging area for this repository.', + $this->renderAuthor(), + $this->renderNewValue()); + } else if (!strlen($new)) { + return pht( + '%s removed %s as the staging area for this repository.', + $this->renderAuthor(), + $this->renderOldValue()); + } else { + return pht( + '%s changed the staging area for this repository from '. + '%s to %s.', + $this->renderAuthor(), + $this->renderOldValue(), + $this->renderNewValue()); + } + } + + public function validateTransactions($object, array $xactions) { + $errors = array(); + + $old = $this->generateOldValue($object); + foreach ($xactions as $xaction) { + $new = $xaction->getNewValue(); + + if (!strlen($new)) { + continue; + } + + if ($new === $old) { + continue; + } + + try { + PhabricatorRepository::assertValidRemoteURI($new); + } catch (Exception $ex) { + $errors[] = $this->newInvalidError( + $ex->getMessage(), + $xaction); + continue; + } + } + + return $errors; + } + +} diff --git a/src/applications/repository/xaction/PhabricatorRepositorySymbolLanguagesTransaction.php b/src/applications/repository/xaction/PhabricatorRepositorySymbolLanguagesTransaction.php new file mode 100644 index 0000000000..18653c78fd --- /dev/null +++ b/src/applications/repository/xaction/PhabricatorRepositorySymbolLanguagesTransaction.php @@ -0,0 +1,39 @@ +getSymbolLanguages(); + } + + public function applyInternalEffects($object, $value) { + $object->setDetail('symbol-languages', $value); + } + + public function getTitle() { + $old = $this->getOldValue(); + $new = $this->getNewValue(); + + if ($old) { + $display_old = implode(', ', $old); + } else { + $display_old = pht('Any'); + } + + if ($new) { + $display_new = implode(', ', $new); + } else { + $display_new = pht('Any'); + } + + return pht( + '%s changed indexed languages from %s to %s.', + $this->renderAuthor(), + $this->renderValue($display_old), + $this->renderValue($display_new)); + } + +} diff --git a/src/applications/repository/xaction/PhabricatorRepositorySymbolSourcesTransaction.php b/src/applications/repository/xaction/PhabricatorRepositorySymbolSourcesTransaction.php new file mode 100644 index 0000000000..781fad9d99 --- /dev/null +++ b/src/applications/repository/xaction/PhabricatorRepositorySymbolSourcesTransaction.php @@ -0,0 +1,78 @@ +getSymbolSources(); + } + + public function applyInternalEffects($object, $value) { + $object->setDetail('symbol-sources', $value); + } + + public function getTitle() { + $old = $this->getOldValue(); + $new = $this->getNewValue(); + + if ($old) { + $display_old = $this->renderHandleList($old); + } else { + $display_old = $this->renderValue(pht('None')); + } + + if ($new) { + $display_new = $this->renderHandleList($new); + } else { + $display_new = $this->renderValue(pht('None')); + } + + return pht( + '%s changed symbol sources from %s to %s.', + $this->renderAuthor(), + $display_old, + $display_new); + } + + public function validateTransactions($object, array $xactions) { + $errors = array(); + + foreach ($xactions as $xaction) { + $old = $object->getSymbolSources(); + $new = $xaction->getNewValue(); + + // If the viewer is adding new repositories, make sure they are + // valid and visible. + $add = array_diff($new, $old); + if (!$add) { + continue; + } + + $repositories = id(new PhabricatorRepositoryQuery()) + ->setViewer($this->getActor()) + ->withPHIDs($add) + ->execute(); + $repositories = mpull($repositories, null, 'getPHID'); + + foreach ($add as $phid) { + if (isset($repositories[$phid])) { + continue; + } + + $errors[] = $this->newInvalidError( + pht( + 'Repository ("%s") does not exist, or you do not have '. + 'permission to see it.', + $phid), + $xaction); + break; + } + } + + return $errors; + } + + +} diff --git a/src/applications/repository/xaction/PhabricatorRepositoryTrackOnlyTransaction.php b/src/applications/repository/xaction/PhabricatorRepositoryTrackOnlyTransaction.php new file mode 100644 index 0000000000..20ff1baf08 --- /dev/null +++ b/src/applications/repository/xaction/PhabricatorRepositoryTrackOnlyTransaction.php @@ -0,0 +1,42 @@ +getDetail('branch-filter', array())); + } + + public function applyInternalEffects($object, $value) { + $object->setDetail('branch-filter', array_fill_keys($value, true)); + } + + public function getTitle() { + $old = $this->getOldValue(); + $new = $this->getNewValue(); + + if (!$new) { + return pht( + '%s set this repository to track all branches.', + $this->renderAuthor()); + } else if (!$old) { + return pht( + '%s set this repository to track branches: %s.', + $this->renderAuthor(), + $this->renderValue(implode(', ', $new))); + } else { + return pht( + '%s changed tracked branches from %s to %s.', + $this->renderAuthor(), + $this->renderValue(implode(', ', $old)), + $this->renderValue(implode(', ', $new))); + } + } + + public function validateTransactions($object, array $xactions) { + return $this->validateRefList($object, $xactions); + } + +} diff --git a/src/applications/repository/xaction/PhabricatorRepositoryTransactionType.php b/src/applications/repository/xaction/PhabricatorRepositoryTransactionType.php new file mode 100644 index 0000000000..6a2bd44feb --- /dev/null +++ b/src/applications/repository/xaction/PhabricatorRepositoryTransactionType.php @@ -0,0 +1,49 @@ +getNewValue() as $pattern) { + // Check for invalid regular expressions. + $regexp = PhabricatorRepository::extractBranchRegexp($pattern); + if ($regexp !== null) { + $ok = @preg_match($regexp, ''); + if ($ok === false) { + $errors[] = $this->newInvalidError( + pht( + 'Expression "%s" is not a valid regular expression. Note '. + 'that you must include delimiters.', + $regexp), + $xaction); + continue; + } + } + + // Check for formatting mistakes like `regex(...)` instead of + // `regexp(...)`. + $matches = null; + if (preg_match('/^([^(]+)\\(.*\\)\z/', $pattern, $matches)) { + switch ($matches[1]) { + case 'regexp': + break; + default: + $errors[] = $this->newInvalidError( + pht( + 'Matching function "%s(...)" is not recognized. Valid '. + 'functions are: regexp(...).', + $matches[1]), + $xaction); + break; + } + } + } + } + + return $errors; + } + +} diff --git a/src/applications/repository/xaction/PhabricatorRepositoryVCSTransaction.php b/src/applications/repository/xaction/PhabricatorRepositoryVCSTransaction.php new file mode 100644 index 0000000000..d5944d5425 --- /dev/null +++ b/src/applications/repository/xaction/PhabricatorRepositoryVCSTransaction.php @@ -0,0 +1,66 @@ +getVersionControlSystem(); + } + + public function applyInternalEffects($object, $value) { + $object->setVersionControlSystem($value); + } + + public function validateTransactions($object, array $xactions) { + $errors = array(); + + $vcs_map = PhabricatorRepositoryType::getAllRepositoryTypes(); + $current_vcs = $object->getVersionControlSystem(); + + if (!$this->isNewObject()) { + foreach ($xactions as $xaction) { + if ($xaction->getNewValue() == $current_vcs) { + continue; + } + + $errors[] = $this->newInvalidError( + pht( + 'You can not change the version control system an existing '. + 'repository uses. It can only be set when a repository is '. + 'first created.'), + $xaction); + } + + return $errors; + } + + $value = $object->getVersionControlSystem(); + + foreach ($xactions as $xaction) { + $value = $xaction->getNewValue(); + + if (isset($vcs_map[$value])) { + continue; + } + + $errors[] = $this->newInvalidError( + pht( + 'Specified version control system must be a VCS '. + 'recognized by Phabricator. Valid systems are: %s.', + implode(', ', array_keys($vcs_map))), + $xaction); + } + + if ($value === null) { + $errors[] = $this->newRequiredError( + pht( + 'When creating a repository, you must specify a valid '. + 'underlying version control system. Valid systems are: %s.', + implode(', ', array_keys($vcs_map)))); + } + + return $errors; + } +} diff --git a/src/applications/transactions/controller/PhabricatorApplicationTransactionValueController.php b/src/applications/transactions/controller/PhabricatorApplicationTransactionValueController.php index 553bfa3e10..bef6fef5a8 100644 --- a/src/applications/transactions/controller/PhabricatorApplicationTransactionValueController.php +++ b/src/applications/transactions/controller/PhabricatorApplicationTransactionValueController.php @@ -32,7 +32,7 @@ final class PhabricatorApplicationTransactionValueController case PhabricatorTransactions::TYPE_VIEW_POLICY: case PhabricatorTransactions::TYPE_EDIT_POLICY: case PhabricatorTransactions::TYPE_JOIN_POLICY: - case PhabricatorRepositoryTransaction::TYPE_PUSH_POLICY: + case PhabricatorRepositoryPushPolicyTransaction::TRANSACTIONTYPE: break; default: return new Aphront404Response(); diff --git a/src/applications/transactions/storage/PhabricatorModularTransactionType.php b/src/applications/transactions/storage/PhabricatorModularTransactionType.php index c5ad835750..ca82af1c66 100644 --- a/src/applications/transactions/storage/PhabricatorModularTransactionType.php +++ b/src/applications/transactions/storage/PhabricatorModularTransactionType.php @@ -199,6 +199,35 @@ abstract class PhabricatorModularTransactionType return $this->renderHandle($this->getNewValue()); } + final protected function renderOldPolicy() { + return $this->renderPolicy($this->getOldValue(), 'old'); + } + + final protected function renderNewPolicy() { + return $this->renderPolicy($this->getNewValue(), 'new'); + } + + final protected function renderPolicy($phid, $mode) { + $viewer = $this->getViewer(); + $handles = $viewer->loadHandles(array($phid)); + + $policy = PhabricatorPolicy::newFromPolicyAndHandle( + $phid, + $handles[$phid]); + + if ($this->isTextMode()) { + return $this->renderValue($policy->getFullName()); + } + + $storage = $this->getStorage(); + if ($policy->getType() == PhabricatorPolicyType::TYPE_CUSTOM) { + $policy->setHref('/transactions/'.$mode.'/'.$storage->getPHID().'/'); + $policy->setWorkflow(true); + } + + return $this->renderValue($policy->renderDescription()); + } + final protected function renderHandleList(array $phids) { $viewer = $this->getViewer(); $display = $viewer->renderHandleList($phids) From c6fc05ee0941f3b417075c9f17c1f2ded0e31042 Mon Sep 17 00:00:00 2001 From: epriestley Date: Wed, 21 Nov 2018 16:05:31 -0800 Subject: [PATCH 41/43] Pull Git filesize logic into a separate LowLevel query and use more Iterators Summary: Depends on D19829. Ref T13216. See PHI908. The current implementation is kind of a lot to live in `CommitHookEngine` and will likely fail if `git diff-tree` produces more than 2GB of output. Pull it out and make it slightly more robust against enormous commits. It's probably limited by this, now: ``` implode("\n", $every_path) ``` We could replace that with some `PhutilReverseRopeSource` primitive or something but since we don't have one of those and it seems unlikely that we'll hit this case in practice, I left it here for now with just the easy stuff converted to be stream-oriented. Test Plan: Used this script to test the query against various commits, got good results: ``` setViewer($viewer) ->withCallsigns(array('P')) ->executeOne(); var_dump( id(new DiffusionLowLevelFilesizeQuery()) ->setRepository($repository) ->withIdentifier($argv[1]) ->execute()); ``` Used this to find large commits in history and pull filesizes (worked great, although our largest commit only touches a couple thousand paths): ``` for hash in `git log --format=%H`; do echo -n $hash; echo -n ' '; git diff-tree -r --no-commit-id $hash | wc -l | awk '{print $1}'; done | awk '{print $2 " " $1}' | sort -n ``` Reviewers: amckinley Reviewed By: amckinley Maniphest Tasks: T13216 Differential Revision: https://secure.phabricator.com/D19830 --- src/__phutil_library_map__.php | 2 + .../engine/DiffusionCommitHookEngine.php | 94 +------------ .../DiffusionLowLevelFilesizeQuery.php | 125 ++++++++++++++++++ 3 files changed, 131 insertions(+), 90 deletions(-) create mode 100644 src/applications/diffusion/query/lowlevel/DiffusionLowLevelFilesizeQuery.php diff --git a/src/__phutil_library_map__.php b/src/__phutil_library_map__.php index 77012e8a64..89d8a6bfac 100644 --- a/src/__phutil_library_map__.php +++ b/src/__phutil_library_map__.php @@ -839,6 +839,7 @@ phutil_register_library_map(array( 'DiffusionLookSoonConduitAPIMethod' => 'applications/diffusion/conduit/DiffusionLookSoonConduitAPIMethod.php', 'DiffusionLowLevelCommitFieldsQuery' => 'applications/diffusion/query/lowlevel/DiffusionLowLevelCommitFieldsQuery.php', 'DiffusionLowLevelCommitQuery' => 'applications/diffusion/query/lowlevel/DiffusionLowLevelCommitQuery.php', + 'DiffusionLowLevelFilesizeQuery' => 'applications/diffusion/query/lowlevel/DiffusionLowLevelFilesizeQuery.php', 'DiffusionLowLevelGitRefQuery' => 'applications/diffusion/query/lowlevel/DiffusionLowLevelGitRefQuery.php', 'DiffusionLowLevelMercurialBranchesQuery' => 'applications/diffusion/query/lowlevel/DiffusionLowLevelMercurialBranchesQuery.php', 'DiffusionLowLevelMercurialPathsQuery' => 'applications/diffusion/query/lowlevel/DiffusionLowLevelMercurialPathsQuery.php', @@ -6250,6 +6251,7 @@ phutil_register_library_map(array( 'DiffusionLookSoonConduitAPIMethod' => 'DiffusionConduitAPIMethod', 'DiffusionLowLevelCommitFieldsQuery' => 'DiffusionLowLevelQuery', 'DiffusionLowLevelCommitQuery' => 'DiffusionLowLevelQuery', + 'DiffusionLowLevelFilesizeQuery' => 'DiffusionLowLevelQuery', 'DiffusionLowLevelGitRefQuery' => 'DiffusionLowLevelQuery', 'DiffusionLowLevelMercurialBranchesQuery' => 'DiffusionLowLevelQuery', 'DiffusionLowLevelMercurialPathsQuery' => 'DiffusionLowLevelQuery', diff --git a/src/applications/diffusion/engine/DiffusionCommitHookEngine.php b/src/applications/diffusion/engine/DiffusionCommitHookEngine.php index 80736e0e1b..07d598cfa5 100644 --- a/src/applications/diffusion/engine/DiffusionCommitHookEngine.php +++ b/src/applications/diffusion/engine/DiffusionCommitHookEngine.php @@ -1305,97 +1305,11 @@ final class DiffusionCommitHookEngine extends Phobject { public function loadFileSizesForCommit($identifier) { $repository = $this->getRepository(); - $vcs = $repository->getVersionControlSystem(); - $path_sizes = array(); - - switch ($vcs) { - case PhabricatorRepositoryType::REPOSITORY_TYPE_GIT: - list($paths_raw) = $repository->execxLocalCommand( - 'diff-tree -z -r --no-commit-id %s --', - $identifier); - - // With "-z" we get "\0\0" for each line. Group the - // delimited text into ", " pairs. - $paths_raw = trim($paths_raw, "\0"); - $paths_raw = explode("\0", $paths_raw); - if (count($paths_raw) % 2) { - throw new Exception( - pht( - 'Unexpected number of output lines from "git diff-tree" when '. - 'processing commit ("%s"): got %s lines, expected an even '. - 'number.', - $identifier, - phutil_count($paths_raw))); - } - $paths_raw = array_chunk($paths_raw, 2); - - $paths = array(); - foreach ($paths_raw as $path_raw) { - list($fields, $pathname) = $path_raw; - $fields = explode(' ', $fields); - - // Fields are: - // - // :100644 100644 aaaa bbbb M - // - // [0] Old file mode. - // [1] New file mode. - // [2] Old object hash. - // [3] New object hash. - // [4] Change mode. - - $paths[] = array( - 'path' => $pathname, - 'newHash' => $fields[3], - ); - } - - if ($paths) { - $check_paths = array(); - foreach ($paths as $path) { - if ($path['newHash'] === self::EMPTY_HASH) { - $path_sizes[$path['path']] = 0; - continue; - } - $check_paths[$path['newHash']][] = $path['path']; - } - - if ($check_paths) { - $future = $repository->getLocalCommandFuture( - 'cat-file --batch-check=%s', - '%(objectsize)') - ->write(implode("\n", array_keys($check_paths))); - - list($sizes) = $future->resolvex(); - $sizes = trim($sizes); - $sizes = phutil_split_lines($sizes, false); - if (count($sizes) !== count($check_paths)) { - throw new Exception( - pht( - 'Unexpected number of output lines from "git cat-file" when '. - 'processing commit ("%s"): got %s lines, expected %s.', - $identifier, - phutil_count($sizes), - phutil_count($check_paths))); - } - - foreach ($check_paths as $object_hash => $path_names) { - $object_size = (int)array_shift($sizes); - foreach ($path_names as $path_name) { - $path_sizes[$path_name] = $object_size; - } - } - } - } - break; - default: - throw new Exception( - pht( - 'File size limits are not supported for this VCS.')); - } - - return $path_sizes; + return id(new DiffusionLowLevelFilesizeQuery()) + ->setRepository($repository) + ->withIdentifier($identifier) + ->execute(); } public function loadCommitRefForCommit($identifier) { diff --git a/src/applications/diffusion/query/lowlevel/DiffusionLowLevelFilesizeQuery.php b/src/applications/diffusion/query/lowlevel/DiffusionLowLevelFilesizeQuery.php new file mode 100644 index 0000000000..9d97a134ca --- /dev/null +++ b/src/applications/diffusion/query/lowlevel/DiffusionLowLevelFilesizeQuery.php @@ -0,0 +1,125 @@ +identifier = $identifier; + return $this; + } + + protected function executeQuery() { + if (!strlen($this->identifier)) { + throw new PhutilInvalidStateException('withIdentifier'); + } + + $type = $this->getRepository()->getVersionControlSystem(); + switch ($type) { + case PhabricatorRepositoryType::REPOSITORY_TYPE_GIT: + $result = $this->loadGitFilesizes(); + break; + default: + throw new Exception(pht('Unsupported repository type "%s"!', $type)); + } + + return $result; + } + + private function loadGitFilesizes() { + $repository = $this->getRepository(); + $identifier = $this->identifier; + + $paths_future = $repository->getLocalCommandFuture( + 'diff-tree -z -r --no-commit-id %s --', + $identifier); + + // With "-z" we get "\0\0" for each line. Process the + // delimited text as ", " pairs. + + $path_lines = id(new LinesOfALargeExecFuture($paths_future)) + ->setDelimiter("\0"); + + $paths = array(); + + $path_pairs = new PhutilChunkedIterator($path_lines, 2); + foreach ($path_pairs as $path_pair) { + if (count($path_pair) != 2) { + throw new Exception( + pht( + 'Unexpected number of output lines from "git diff-tree" when '. + 'processing commit ("%s"): expected an even number of lines.', + $identifier)); + } + + list($fields, $pathname) = array_values($path_pair); + $fields = explode(' ', $fields); + + // Fields are: + // + // :100644 100644 aaaa bbbb M + // + // [0] Old file mode. + // [1] New file mode. + // [2] Old object hash. + // [3] New object hash. + // [4] Change mode. + + $paths[] = array( + 'path' => $pathname, + 'newHash' => $fields[3], + ); + } + + $path_sizes = array(); + + if (!$paths) { + return $path_sizes; + } + + $check_paths = array(); + foreach ($paths as $path) { + if ($path['newHash'] === DiffusionCommitHookEngine::EMPTY_HASH) { + $path_sizes[$path['path']] = 0; + continue; + } + $check_paths[$path['newHash']][] = $path['path']; + } + + if (!$check_paths) { + return $path_sizes; + } + + $future = $repository->getLocalCommandFuture( + 'cat-file --batch-check=%s', + '%(objectsize)'); + + $future->write(implode("\n", array_keys($check_paths))); + + $size_lines = id(new LinesOfALargeExecFuture($future)) + ->setDelimiter("\n"); + foreach ($size_lines as $line) { + $object_size = (int)$line; + + $object_hash = head_key($check_paths); + $path_names = $check_paths[$object_hash]; + unset($check_paths[$object_hash]); + + foreach ($path_names as $path_name) { + $path_sizes[$path_name] = $object_size; + } + } + + if ($check_paths) { + throw new Exception( + pht( + 'Unexpected number of output lines from "git cat-file" when '. + 'processing commit ("%s").', + $identifier)); + } + + return $path_sizes; + } + +} From c86c5749bafb30e3dbe6961a64077fdccfd5a72a Mon Sep 17 00:00:00 2001 From: epriestley Date: Wed, 21 Nov 2018 16:48:00 -0800 Subject: [PATCH 42/43] Make the repository "Filesize Limit" and "Clone/Fetch Timeout" configurable in the UI Summary: Depends on D19830. Ref T13216. See PHI908. See PHI750. See PHI885. Allow users to configure a filesize limit, and allow them to adjust the clone/fetch timeout. Test Plan: {F6021356} - Configured a filesize limit and pushed, hit it. Made the limit larger and pushed, change went through. Reviewers: amckinley Reviewed By: amckinley Subscribers: yelirekim, PHID-OPKG-gm6ozazyms6q6i22gyam Maniphest Tasks: T13216 Differential Revision: https://secure.phabricator.com/D19831 --- src/__phutil_library_map__.php | 6 ++ .../editor/DiffusionRepositoryEditEngine.php | 19 ++++ .../engine/DiffusionCommitHookEngine.php | 4 +- ...ffusionRepositoryLimitsManagementPanel.php | 101 ++++++++++++++++++ .../protocol/DiffusionCommandEngine.php | 2 +- ...dockWorkingCopyBlueprintImplementation.php | 6 +- .../storage/PhabricatorRepository.php | 24 +++++ ...atorRepositoryCopyTimeLimitTransaction.php | 77 +++++++++++++ ...atorRepositoryFilesizeLimitTransaction.php | 78 ++++++++++++++ .../user/userguide/diffusion_managing.diviner | 69 +++++++----- 10 files changed, 352 insertions(+), 34 deletions(-) create mode 100644 src/applications/diffusion/management/DiffusionRepositoryLimitsManagementPanel.php create mode 100644 src/applications/repository/xaction/PhabricatorRepositoryCopyTimeLimitTransaction.php create mode 100644 src/applications/repository/xaction/PhabricatorRepositoryFilesizeLimitTransaction.php diff --git a/src/__phutil_library_map__.php b/src/__phutil_library_map__.php index 89d8a6bfac..2b6732b7d5 100644 --- a/src/__phutil_library_map__.php +++ b/src/__phutil_library_map__.php @@ -950,6 +950,7 @@ phutil_register_library_map(array( 'DiffusionRepositoryHistoryManagementPanel' => 'applications/diffusion/management/DiffusionRepositoryHistoryManagementPanel.php', 'DiffusionRepositoryIdentityEditor' => 'applications/diffusion/editor/DiffusionRepositoryIdentityEditor.php', 'DiffusionRepositoryIdentitySearchEngine' => 'applications/diffusion/query/DiffusionRepositoryIdentitySearchEngine.php', + 'DiffusionRepositoryLimitsManagementPanel' => 'applications/diffusion/management/DiffusionRepositoryLimitsManagementPanel.php', 'DiffusionRepositoryListController' => 'applications/diffusion/controller/DiffusionRepositoryListController.php', 'DiffusionRepositoryManageController' => 'applications/diffusion/controller/DiffusionRepositoryManageController.php', 'DiffusionRepositoryManagePanelsController' => 'applications/diffusion/controller/DiffusionRepositoryManagePanelsController.php', @@ -4104,6 +4105,7 @@ phutil_register_library_map(array( 'PhabricatorRepositoryCommitRef' => 'applications/repository/engine/PhabricatorRepositoryCommitRef.php', 'PhabricatorRepositoryCommitTestCase' => 'applications/repository/storage/__tests__/PhabricatorRepositoryCommitTestCase.php', 'PhabricatorRepositoryConfigOptions' => 'applications/repository/config/PhabricatorRepositoryConfigOptions.php', + 'PhabricatorRepositoryCopyTimeLimitTransaction' => 'applications/repository/xaction/PhabricatorRepositoryCopyTimeLimitTransaction.php', 'PhabricatorRepositoryDAO' => 'applications/repository/storage/PhabricatorRepositoryDAO.php', 'PhabricatorRepositoryDangerousTransaction' => 'applications/repository/xaction/PhabricatorRepositoryDangerousTransaction.php', 'PhabricatorRepositoryDefaultBranchTransaction' => 'applications/repository/xaction/PhabricatorRepositoryDefaultBranchTransaction.php', @@ -4115,6 +4117,7 @@ phutil_register_library_map(array( 'PhabricatorRepositoryEngine' => 'applications/repository/engine/PhabricatorRepositoryEngine.php', 'PhabricatorRepositoryEnormousTransaction' => 'applications/repository/xaction/PhabricatorRepositoryEnormousTransaction.php', 'PhabricatorRepositoryFerretEngine' => 'applications/repository/search/PhabricatorRepositoryFerretEngine.php', + 'PhabricatorRepositoryFilesizeLimitTransaction' => 'applications/repository/xaction/PhabricatorRepositoryFilesizeLimitTransaction.php', 'PhabricatorRepositoryFulltextEngine' => 'applications/repository/search/PhabricatorRepositoryFulltextEngine.php', 'PhabricatorRepositoryGitCommitChangeParserWorker' => 'applications/repository/worker/commitchangeparser/PhabricatorRepositoryGitCommitChangeParserWorker.php', 'PhabricatorRepositoryGitCommitMessageParserWorker' => 'applications/repository/worker/commitmessageparser/PhabricatorRepositoryGitCommitMessageParserWorker.php', @@ -6361,6 +6364,7 @@ phutil_register_library_map(array( 'DiffusionRepositoryHistoryManagementPanel' => 'DiffusionRepositoryManagementPanel', 'DiffusionRepositoryIdentityEditor' => 'PhabricatorApplicationTransactionEditor', 'DiffusionRepositoryIdentitySearchEngine' => 'PhabricatorApplicationSearchEngine', + 'DiffusionRepositoryLimitsManagementPanel' => 'DiffusionRepositoryManagementPanel', 'DiffusionRepositoryListController' => 'DiffusionController', 'DiffusionRepositoryManageController' => 'DiffusionController', 'DiffusionRepositoryManagePanelsController' => 'DiffusionRepositoryManageController', @@ -10070,6 +10074,7 @@ phutil_register_library_map(array( 'PhabricatorRepositoryCommitRef' => 'Phobject', 'PhabricatorRepositoryCommitTestCase' => 'PhabricatorTestCase', 'PhabricatorRepositoryConfigOptions' => 'PhabricatorApplicationConfigOptions', + 'PhabricatorRepositoryCopyTimeLimitTransaction' => 'PhabricatorRepositoryTransactionType', 'PhabricatorRepositoryDAO' => 'PhabricatorLiskDAO', 'PhabricatorRepositoryDangerousTransaction' => 'PhabricatorRepositoryTransactionType', 'PhabricatorRepositoryDefaultBranchTransaction' => 'PhabricatorRepositoryTransactionType', @@ -10081,6 +10086,7 @@ phutil_register_library_map(array( 'PhabricatorRepositoryEngine' => 'Phobject', 'PhabricatorRepositoryEnormousTransaction' => 'PhabricatorRepositoryTransactionType', 'PhabricatorRepositoryFerretEngine' => 'PhabricatorFerretEngine', + 'PhabricatorRepositoryFilesizeLimitTransaction' => 'PhabricatorRepositoryTransactionType', 'PhabricatorRepositoryFulltextEngine' => 'PhabricatorFulltextEngine', 'PhabricatorRepositoryGitCommitChangeParserWorker' => 'PhabricatorRepositoryCommitChangeParserWorker', 'PhabricatorRepositoryGitCommitMessageParserWorker' => 'PhabricatorRepositoryCommitMessageParserWorker', diff --git a/src/applications/diffusion/editor/DiffusionRepositoryEditEngine.php b/src/applications/diffusion/editor/DiffusionRepositoryEditEngine.php index 78baeba71a..355a67480c 100644 --- a/src/applications/diffusion/editor/DiffusionRepositoryEditEngine.php +++ b/src/applications/diffusion/editor/DiffusionRepositoryEditEngine.php @@ -472,6 +472,25 @@ final class DiffusionRepositoryEditEngine pht('Change the push policy of the repository.')) ->setConduitTypeDescription(pht('New policy PHID or constant.')) ->setValue($object->getPolicy(DiffusionPushCapability::CAPABILITY)), + id(new PhabricatorTextEditField()) + ->setKey('filesizeLimit') + ->setLabel(pht('Filesize Limit')) + ->setTransactionType( + PhabricatorRepositoryFilesizeLimitTransaction::TRANSACTIONTYPE) + ->setDescription(pht('Maximum permitted file size.')) + ->setConduitDescription(pht('Change the filesize limit.')) + ->setConduitTypeDescription(pht('New repository filesize limit.')) + ->setValue($object->getFilesizeLimit()), + id(new PhabricatorTextEditField()) + ->setKey('copyTimeLimit') + ->setLabel(pht('Clone/Fetch Timeout')) + ->setTransactionType( + PhabricatorRepositoryCopyTimeLimitTransaction::TRANSACTIONTYPE) + ->setDescription( + pht('Maximum permitted duration of internal clone/fetch.')) + ->setConduitDescription(pht('Change the copy time limit.')) + ->setConduitTypeDescription(pht('New repository copy time limit.')) + ->setValue($object->getCopyTimeLimit()), ); } diff --git a/src/applications/diffusion/engine/DiffusionCommitHookEngine.php b/src/applications/diffusion/engine/DiffusionCommitHookEngine.php index 07d598cfa5..17f037471a 100644 --- a/src/applications/diffusion/engine/DiffusionCommitHookEngine.php +++ b/src/applications/diffusion/engine/DiffusionCommitHookEngine.php @@ -1268,9 +1268,7 @@ final class DiffusionCommitHookEngine extends Phobject { private function rejectOversizedFiles(array $content_updates) { $repository = $this->getRepository(); - // TODO: Allow repositories to be configured for a maximum filesize. - $limit = 0; - + $limit = $repository->getFilesizeLimit(); if (!$limit) { return; } diff --git a/src/applications/diffusion/management/DiffusionRepositoryLimitsManagementPanel.php b/src/applications/diffusion/management/DiffusionRepositoryLimitsManagementPanel.php new file mode 100644 index 0000000000..861e1a22fb --- /dev/null +++ b/src/applications/diffusion/management/DiffusionRepositoryLimitsManagementPanel.php @@ -0,0 +1,101 @@ +isGit(); + } + + public function getManagementPanelIcon() { + $repository = $this->getRepository(); + + $any_limit = false; + + if ($repository->getFilesizeLimit()) { + $any_limit = true; + } + + if ($any_limit) { + return 'fa-signal'; + } else { + return 'fa-signal grey'; + } + } + + protected function getEditEngineFieldKeys() { + return array( + 'filesizeLimit', + 'copyTimeLimit', + ); + } + + public function buildManagementPanelCurtain() { + $repository = $this->getRepository(); + $viewer = $this->getViewer(); + $action_list = $this->newActionList(); + + $can_edit = PhabricatorPolicyFilter::hasCapability( + $viewer, + $repository, + PhabricatorPolicyCapability::CAN_EDIT); + + $limits_uri = $this->getEditPageURI(); + + $action_list->addAction( + id(new PhabricatorActionView()) + ->setIcon('fa-pencil') + ->setName(pht('Edit Limits')) + ->setHref($limits_uri) + ->setDisabled(!$can_edit) + ->setWorkflow(!$can_edit)); + + return $this->newCurtainView() + ->setActionList($action_list); + } + + public function buildManagementPanelContent() { + $repository = $this->getRepository(); + $viewer = $this->getViewer(); + + $view = id(new PHUIPropertyListView()) + ->setViewer($viewer); + + $byte_limit = $repository->getFilesizeLimit(); + if ($byte_limit) { + $filesize_display = pht('%s Bytes', new PhutilNumber($byte_limit)); + } else { + $filesize_display = pht('Unlimited'); + $filesize_display = phutil_tag('em', array(), $filesize_display); + } + + $view->addProperty(pht('Filesize Limit'), $filesize_display); + + $copy_limit = $repository->getCopyTimeLimit(); + if ($copy_limit) { + $copy_display = pht('%s Seconds', new PhutilNumber($copy_limit)); + } else { + $copy_default = $repository->getDefaultCopyTimeLimit(); + $copy_display = pht( + 'Default (%s Seconds)', + new PhutilNumber($copy_default)); + $copy_display = phutil_tag('em', array(), $copy_display); + } + + $view->addProperty(pht('Clone/Fetch Timeout'), $copy_display); + + return $this->newBox(pht('Limits'), $view); + } + +} diff --git a/src/applications/diffusion/protocol/DiffusionCommandEngine.php b/src/applications/diffusion/protocol/DiffusionCommandEngine.php index 9d52482b23..a0fe568168 100644 --- a/src/applications/diffusion/protocol/DiffusionCommandEngine.php +++ b/src/applications/diffusion/protocol/DiffusionCommandEngine.php @@ -139,7 +139,7 @@ abstract class DiffusionCommandEngine extends Phobject { // to try to avoid cases where `git fetch` hangs for some reason and we're // left sitting with a held lock forever. $repository = $this->getRepository(); - $future->setTimeout($repository->getCopyTimeLimit()); + $future->setTimeout($repository->getEffectiveCopyTimeLimit()); return $future; } diff --git a/src/applications/drydock/blueprint/DrydockWorkingCopyBlueprintImplementation.php b/src/applications/drydock/blueprint/DrydockWorkingCopyBlueprintImplementation.php index eb3a361ea8..d82f5c2c15 100644 --- a/src/applications/drydock/blueprint/DrydockWorkingCopyBlueprintImplementation.php +++ b/src/applications/drydock/blueprint/DrydockWorkingCopyBlueprintImplementation.php @@ -187,7 +187,7 @@ final class DrydockWorkingCopyBlueprintImplementation (string)$repository->getCloneURIObject(), $path); - $future->setTimeout($repository->getCopyTimeLimit()); + $future->setTimeout($repository->getEffectiveCopyTimeLimit()); $futures[$directory] = $future; } @@ -284,7 +284,7 @@ final class DrydockWorkingCopyBlueprintImplementation } $this->newExecvFuture($interface, $cmd, $arg) - ->setTimeout($repository->getCopyTimeLimit()) + ->setTimeout($repository->getEffectiveCopyTimeLimit()) ->resolvex(); if (idx($spec, 'default')) { @@ -310,7 +310,7 @@ final class DrydockWorkingCopyBlueprintImplementation try { $this->newExecvFuture($interface, $cmd, $arg) - ->setTimeout($repository->getCopyTimeLimit()) + ->setTimeout($repository->getEffectiveCopyTimeLimit()) ->resolvex(); } catch (CommandException $ex) { $display_command = csprintf( diff --git a/src/applications/repository/storage/PhabricatorRepository.php b/src/applications/repository/storage/PhabricatorRepository.php index 2b0d63cf47..83c5b411e4 100644 --- a/src/applications/repository/storage/PhabricatorRepository.php +++ b/src/applications/repository/storage/PhabricatorRepository.php @@ -1898,9 +1898,33 @@ final class PhabricatorRepository extends PhabricatorRepositoryDAO * @return int Maximum number of seconds to spend copying this repository. */ public function getCopyTimeLimit() { + return $this->getDetail('limit.copy'); + } + + public function setCopyTimeLimit($limit) { + return $this->setDetail('limit.copy', $limit); + } + + public function getDefaultCopyTimeLimit() { return phutil_units('15 minutes in seconds'); } + public function getEffectiveCopyTimeLimit() { + $limit = $this->getCopyTimeLimit(); + if ($limit) { + return $limit; + } + + return $this->getDefaultCopyTimeLimit(); + } + + public function getFilesizeLimit() { + return $this->getDetail('limit.filesize'); + } + + public function setFilesizeLimit($limit) { + return $this->setDetail('limit.filesize', $limit); + } /** * Retrieve the service URI for the device hosting this repository. diff --git a/src/applications/repository/xaction/PhabricatorRepositoryCopyTimeLimitTransaction.php b/src/applications/repository/xaction/PhabricatorRepositoryCopyTimeLimitTransaction.php new file mode 100644 index 0000000000..6be3466ae7 --- /dev/null +++ b/src/applications/repository/xaction/PhabricatorRepositoryCopyTimeLimitTransaction.php @@ -0,0 +1,77 @@ +getCopyTimeLimit(); + } + + public function generateNewValue($object, $value) { + if (!strlen($value)) { + return null; + } + + $value = (int)$value; + if (!$value) { + return null; + } + + return $value; + } + + public function applyInternalEffects($object, $value) { + $object->setCopyTimeLimit($value); + } + + public function getTitle() { + $old = $this->getOldValue(); + $new = $this->getNewValue(); + + if ($old && $new) { + return pht( + '%s changed the copy time limit for this repository from %s seconds '. + 'to %s seconds.', + $this->renderAuthor(), + $this->renderOldValue(), + $this->renderNewValue()); + } else if ($new) { + return pht( + '%s set the copy time limit for this repository to %s seconds.', + $this->renderAuthor(), + $this->renderNewValue()); + } else { + return pht( + '%s reset the copy time limit (%s seconds) for this repository '. + 'to the default value.', + $this->renderAuthor(), + $this->renderOldValue()); + } + } + + public function validateTransactions($object, array $xactions) { + $errors = array(); + + foreach ($xactions as $xaction) { + $new = $xaction->getNewValue(); + + if (!strlen($new)) { + continue; + } + + if (!preg_match('/^\d+\z/', $new)) { + $errors[] = $this->newInvalidError( + pht( + 'Unable to parse copy time limit, specify a positive number '. + 'of seconds.'), + $xaction); + continue; + } + } + + return $errors; + } + +} diff --git a/src/applications/repository/xaction/PhabricatorRepositoryFilesizeLimitTransaction.php b/src/applications/repository/xaction/PhabricatorRepositoryFilesizeLimitTransaction.php new file mode 100644 index 0000000000..6bf74cc757 --- /dev/null +++ b/src/applications/repository/xaction/PhabricatorRepositoryFilesizeLimitTransaction.php @@ -0,0 +1,78 @@ +getFilesizeLimit(); + } + + public function generateNewValue($object, $value) { + if (!strlen($value)) { + return null; + } + + $value = phutil_parse_bytes($value); + if (!$value) { + return null; + } + + return $value; + } + + public function applyInternalEffects($object, $value) { + $object->setFilesizeLimit($value); + } + + public function getTitle() { + $old = $this->getOldValue(); + $new = $this->getNewValue(); + + if ($old && $new) { + return pht( + '%s changed the filesize limit for this repository from %s bytes to '. + '%s bytes.', + $this->renderAuthor(), + $this->renderOldValue(), + $this->renderNewValue()); + } else if ($new) { + return pht( + '%s set the filesize limit for this repository to %s bytes.', + $this->renderAuthor(), + $this->renderNewValue()); + } else { + return pht( + '%s removed the filesize limit (%s bytes) for this repository.', + $this->renderAuthor(), + $this->renderOldValue()); + } + } + + public function validateTransactions($object, array $xactions) { + $errors = array(); + + foreach ($xactions as $xaction) { + $new = $xaction->getNewValue(); + + if (!strlen($new)) { + continue; + } + + try { + $value = phutil_parse_bytes($new); + } catch (Exception $ex) { + $errors[] = $this->newInvalidError( + pht( + 'Unable to parse filesize limit: %s', + $ex->getMessage()), + $xaction); + continue; + } + } + + return $errors; + } + +} diff --git a/src/docs/user/userguide/diffusion_managing.diviner b/src/docs/user/userguide/diffusion_managing.diviner index cc03433532..432b4de13d 100644 --- a/src/docs/user/userguide/diffusion_managing.diviner +++ b/src/docs/user/userguide/diffusion_managing.diviner @@ -236,37 +236,20 @@ fetch from, serve from, and push to. These options are covered in detail in @{article:Diffusion User Guide: URIs}. -Staging Area -============ - -The **Staging Area** panel configures staging areas, used to make proposed -changes available to build and continuous integration systems. - -For more details, see @{article:Harbormaster User Guide}. - - -Automation -========== - -The **Automation** panel configures support for allowing Phabricator to make -writes directly to the repository, so that it can perform operations like -automatically landing revisions from the web UI. - -For details on repository automation, see -@{article:Drydock User Guide: Repository Automation}. - - -Symbols +Limits ====== -The **Symbols** panel allows you to customize how symbols (like class and -function names) are linked when viewing code in the repository, and when -viewing revisions which propose code changes to the repository. +The **Limits** panel allows you to configure limits and timeouts. -To take advantage of this feature, you need to do additional work to build -symbol indexes. For details on configuring and populating symbol indexes, see -@{article:User Guide: Symbol Indexes}. +**Filesize Limit**: Allows you to set a maximum filesize for any file in the +repository. If a commit creates a larger file (or modifies an existing file so +it becomes too large) it will be rejected. This option only applies to hosted +repositories. +**Clone/Fetch Timeout**: Configure the internal timeout for creating copies +of this repository during operations like intracluster synchronization and +Drydock working copy construction. This timeout does not affect external +users. Branches ======== @@ -311,6 +294,38 @@ revisions and tasks. If you don't want Phabricator to close objects when it discovers new commits, disable **Autoclose** for the repository. +Staging Area +============ + +The **Staging Area** panel configures staging areas, used to make proposed +changes available to build and continuous integration systems. + +For more details, see @{article:Harbormaster User Guide}. + + +Automation +========== + +The **Automation** panel configures support for allowing Phabricator to make +writes directly to the repository, so that it can perform operations like +automatically landing revisions from the web UI. + +For details on repository automation, see +@{article:Drydock User Guide: Repository Automation}. + + +Symbols +====== + +The **Symbols** panel allows you to customize how symbols (like class and +function names) are linked when viewing code in the repository, and when +viewing revisions which propose code changes to the repository. + +To take advantage of this feature, you need to do additional work to build +symbol indexes. For details on configuring and populating symbol indexes, see +@{article:User Guide: Symbol Indexes}. + + Repository Identifiers and Names ================================ From 9bfe558587aa55d8f0cf037640e3cfee6d642593 Mon Sep 17 00:00:00 2001 From: epriestley Date: Mon, 26 Nov 2018 05:11:45 -0800 Subject: [PATCH 43/43] Add a "touched paths" limit to repositories, limiting the maximum number of paths any commit may touch Summary: Depends on D19831. Ref T13216. See PHI908. Allegedly, a user copied a large repository into itself and then pushed it. Great backup strategy, but it can create headaches for administrators. Allow a "maximum paths you can touch with one commit" limit to be configured, to make it harder for users to make this push this kind of commit by accident. If you actually intended to do this, you can work around this by breaking your commit into pieces (or temporarily removing the limit). This isn't a security/policy sort of option, it's just a guard against silly mistakes. Test Plan: Set limit to 2, tried to push 3 files, got rejected. Raised limit, pushed changes successfully. Reviewers: amckinley Reviewed By: amckinley Maniphest Tasks: T13216 Differential Revision: https://secure.phabricator.com/D19839 --- src/__phutil_library_map__.php | 2 + .../editor/DiffusionRepositoryEditEngine.php | 9 +++ .../engine/DiffusionCommitHookEngine.php | 55 +++++++++++++- ...ffusionRepositoryLimitsManagementPanel.php | 11 +++ .../storage/PhabricatorRepository.php | 8 ++ .../storage/PhabricatorRepositoryPushLog.php | 4 + ...ricatorRepositoryTouchLimitTransaction.php | 76 +++++++++++++++++++ .../user/userguide/diffusion_managing.diviner | 27 +++++++ 8 files changed, 190 insertions(+), 2 deletions(-) create mode 100644 src/applications/repository/xaction/PhabricatorRepositoryTouchLimitTransaction.php diff --git a/src/__phutil_library_map__.php b/src/__phutil_library_map__.php index 2b6732b7d5..e4ffb74b0f 100644 --- a/src/__phutil_library_map__.php +++ b/src/__phutil_library_map__.php @@ -4203,6 +4203,7 @@ phutil_register_library_map(array( 'PhabricatorRepositorySyncEventPHIDType' => 'applications/repository/phid/PhabricatorRepositorySyncEventPHIDType.php', 'PhabricatorRepositorySyncEventQuery' => 'applications/repository/query/PhabricatorRepositorySyncEventQuery.php', 'PhabricatorRepositoryTestCase' => 'applications/repository/storage/__tests__/PhabricatorRepositoryTestCase.php', + 'PhabricatorRepositoryTouchLimitTransaction' => 'applications/repository/xaction/PhabricatorRepositoryTouchLimitTransaction.php', 'PhabricatorRepositoryTrackOnlyTransaction' => 'applications/repository/xaction/PhabricatorRepositoryTrackOnlyTransaction.php', 'PhabricatorRepositoryTransaction' => 'applications/repository/storage/PhabricatorRepositoryTransaction.php', 'PhabricatorRepositoryTransactionQuery' => 'applications/repository/query/PhabricatorRepositoryTransactionQuery.php', @@ -10198,6 +10199,7 @@ phutil_register_library_map(array( 'PhabricatorRepositorySyncEventPHIDType' => 'PhabricatorPHIDType', 'PhabricatorRepositorySyncEventQuery' => 'PhabricatorCursorPagedPolicyAwareQuery', 'PhabricatorRepositoryTestCase' => 'PhabricatorTestCase', + 'PhabricatorRepositoryTouchLimitTransaction' => 'PhabricatorRepositoryTransactionType', 'PhabricatorRepositoryTrackOnlyTransaction' => 'PhabricatorRepositoryTransactionType', 'PhabricatorRepositoryTransaction' => 'PhabricatorModularTransaction', 'PhabricatorRepositoryTransactionQuery' => 'PhabricatorApplicationTransactionQuery', diff --git a/src/applications/diffusion/editor/DiffusionRepositoryEditEngine.php b/src/applications/diffusion/editor/DiffusionRepositoryEditEngine.php index 355a67480c..29dc588b45 100644 --- a/src/applications/diffusion/editor/DiffusionRepositoryEditEngine.php +++ b/src/applications/diffusion/editor/DiffusionRepositoryEditEngine.php @@ -491,6 +491,15 @@ final class DiffusionRepositoryEditEngine ->setConduitDescription(pht('Change the copy time limit.')) ->setConduitTypeDescription(pht('New repository copy time limit.')) ->setValue($object->getCopyTimeLimit()), + id(new PhabricatorTextEditField()) + ->setKey('touchLimit') + ->setLabel(pht('Touched Paths Limit')) + ->setTransactionType( + PhabricatorRepositoryTouchLimitTransaction::TRANSACTIONTYPE) + ->setDescription(pht('Maximum permitted paths touched per commit.')) + ->setConduitDescription(pht('Change the touch limit.')) + ->setConduitTypeDescription(pht('New repository touch limit.')) + ->setValue($object->getTouchLimit()), ); } diff --git a/src/applications/diffusion/engine/DiffusionCommitHookEngine.php b/src/applications/diffusion/engine/DiffusionCommitHookEngine.php index 17f037471a..6856b8676d 100644 --- a/src/applications/diffusion/engine/DiffusionCommitHookEngine.php +++ b/src/applications/diffusion/engine/DiffusionCommitHookEngine.php @@ -39,6 +39,7 @@ final class DiffusionCommitHookEngine extends Phobject { private $emailPHIDs = array(); private $changesets = array(); private $changesetsSize = 0; + private $filesizeCache = array(); /* -( Config )------------------------------------------------------------- */ @@ -174,6 +175,15 @@ final class DiffusionCommitHookEngine extends Phobject { throw $ex; } + try { + if (!$is_initial_import) { + $this->rejectCommitsAffectingTooManyPaths($content_updates); + } + } catch (DiffusionCommitHookRejectException $ex) { + $this->rejectCode = PhabricatorRepositoryPushLog::REJECT_TOUCHES; + throw $ex; + } + try { if (!$is_initial_import) { $this->rejectEnormousChanges($content_updates); @@ -1276,7 +1286,8 @@ final class DiffusionCommitHookEngine extends Phobject { foreach ($content_updates as $update) { $identifier = $update->getRefNew(); - $sizes = $this->loadFileSizesForCommit($identifier); + $sizes = $this->getFileSizesForCommit($identifier); + foreach ($sizes as $path => $size) { if ($size <= $limit) { continue; @@ -1301,7 +1312,47 @@ final class DiffusionCommitHookEngine extends Phobject { } } - public function loadFileSizesForCommit($identifier) { + private function rejectCommitsAffectingTooManyPaths(array $content_updates) { + $repository = $this->getRepository(); + + $limit = $repository->getTouchLimit(); + if (!$limit) { + return; + } + + foreach ($content_updates as $update) { + $identifier = $update->getRefNew(); + + $sizes = $this->getFileSizesForCommit($identifier); + if (count($sizes) > $limit) { + $message = pht( + 'COMMIT AFFECTS TOO MANY PATHS'. + "\n". + 'This repository ("%s") is configured with a touched files limit '. + 'that caps the maximum number of paths any single commit may '. + 'affect. You are pushing a change ("%s") which exceeds this '. + 'limit: it affects %s paths, but the largest number of paths any '. + 'commit may affect is %s paths.', + $repository->getDisplayName(), + $identifier, + phutil_count($sizes), + new PhutilNumber($limit)); + + throw new DiffusionCommitHookRejectException($message); + } + } + } + + public function getFileSizesForCommit($identifier) { + if (!isset($this->filesizeCache[$identifier])) { + $file_sizes = $this->loadFileSizesForCommit($identifier); + $this->filesizeCache[$identifier] = $file_sizes; + } + + return $this->filesizeCache[$identifier]; + } + + private function loadFileSizesForCommit($identifier) { $repository = $this->getRepository(); return id(new DiffusionLowLevelFilesizeQuery()) diff --git a/src/applications/diffusion/management/DiffusionRepositoryLimitsManagementPanel.php b/src/applications/diffusion/management/DiffusionRepositoryLimitsManagementPanel.php index 861e1a22fb..b57ed5cf56 100644 --- a/src/applications/diffusion/management/DiffusionRepositoryLimitsManagementPanel.php +++ b/src/applications/diffusion/management/DiffusionRepositoryLimitsManagementPanel.php @@ -38,6 +38,7 @@ final class DiffusionRepositoryLimitsManagementPanel return array( 'filesizeLimit', 'copyTimeLimit', + 'touchLimit', ); } @@ -95,6 +96,16 @@ final class DiffusionRepositoryLimitsManagementPanel $view->addProperty(pht('Clone/Fetch Timeout'), $copy_display); + $touch_limit = $repository->getTouchLimit(); + if ($touch_limit) { + $touch_display = pht('%s Paths', new PhutilNumber($touch_limit)); + } else { + $touch_display = pht('Unlimited'); + $touch_display = phutil_tag('em', array(), $touch_display); + } + + $view->addProperty(pht('Touched Paths Limit'), $touch_display); + return $this->newBox(pht('Limits'), $view); } diff --git a/src/applications/repository/storage/PhabricatorRepository.php b/src/applications/repository/storage/PhabricatorRepository.php index 83c5b411e4..3b7918cd59 100644 --- a/src/applications/repository/storage/PhabricatorRepository.php +++ b/src/applications/repository/storage/PhabricatorRepository.php @@ -1926,6 +1926,14 @@ final class PhabricatorRepository extends PhabricatorRepositoryDAO return $this->setDetail('limit.filesize', $limit); } + public function getTouchLimit() { + return $this->getDetail('limit.touch'); + } + + public function setTouchLimit($limit) { + return $this->setDetail('limit.touch', $limit); + } + /** * Retrieve the service URI for the device hosting this repository. * diff --git a/src/applications/repository/storage/PhabricatorRepositoryPushLog.php b/src/applications/repository/storage/PhabricatorRepositoryPushLog.php index fa08ff0394..cfb5402c2a 100644 --- a/src/applications/repository/storage/PhabricatorRepositoryPushLog.php +++ b/src/applications/repository/storage/PhabricatorRepositoryPushLog.php @@ -25,6 +25,7 @@ final class PhabricatorRepositoryPushLog const CHANGEFLAG_DANGEROUS = 16; const CHANGEFLAG_ENORMOUS = 32; const CHANGEFLAG_OVERSIZED = 64; + const CHANGEFLAG_TOUCHES = 128; const REJECT_ACCEPT = 0; const REJECT_DANGEROUS = 1; @@ -33,6 +34,7 @@ final class PhabricatorRepositoryPushLog const REJECT_BROKEN = 4; const REJECT_ENORMOUS = 5; const REJECT_OVERSIZED = 6; + const REJECT_TOUCHES = 7; protected $repositoryPHID; protected $epoch; @@ -66,6 +68,7 @@ final class PhabricatorRepositoryPushLog self::CHANGEFLAG_DANGEROUS => pht('Dangerous'), self::CHANGEFLAG_ENORMOUS => pht('Enormous'), self::CHANGEFLAG_OVERSIZED => pht('Oversized'), + self::CHANGEFLAG_TOUCHES => pht('Touches Too Many Paths'), ); } @@ -78,6 +81,7 @@ final class PhabricatorRepositoryPushLog self::REJECT_BROKEN => pht('Rejected: Broken'), self::REJECT_ENORMOUS => pht('Rejected: Enormous'), self::REJECT_OVERSIZED => pht('Rejected: Oversized File'), + self::REJECT_TOUCHES => pht('Rejected: Touches Too Many Paths'), ); } diff --git a/src/applications/repository/xaction/PhabricatorRepositoryTouchLimitTransaction.php b/src/applications/repository/xaction/PhabricatorRepositoryTouchLimitTransaction.php new file mode 100644 index 0000000000..e3052d0894 --- /dev/null +++ b/src/applications/repository/xaction/PhabricatorRepositoryTouchLimitTransaction.php @@ -0,0 +1,76 @@ +getTouchLimit(); + } + + public function generateNewValue($object, $value) { + if (!strlen($value)) { + return null; + } + + $value = (int)$value; + if (!$value) { + return null; + } + + return $value; + } + + public function applyInternalEffects($object, $value) { + $object->setTouchLimit($value); + } + + public function getTitle() { + $old = $this->getOldValue(); + $new = $this->getNewValue(); + + if ($old && $new) { + return pht( + '%s changed the touch limit for this repository from %s paths to '. + '%s paths.', + $this->renderAuthor(), + $this->renderOldValue(), + $this->renderNewValue()); + } else if ($new) { + return pht( + '%s set the touch limit for this repository to %s paths.', + $this->renderAuthor(), + $this->renderNewValue()); + } else { + return pht( + '%s removed the touch limit (%s paths) for this repository.', + $this->renderAuthor(), + $this->renderOldValue()); + } + } + + public function validateTransactions($object, array $xactions) { + $errors = array(); + + foreach ($xactions as $xaction) { + $new = $xaction->getNewValue(); + + if (!strlen($new)) { + continue; + } + + if (!preg_match('/^\d+\z/', $new)) { + $errors[] = $this->newInvalidError( + pht( + 'Unable to parse touch limit, specify a positive number of '. + 'paths.'), + $xaction); + continue; + } + } + + return $errors; + } + +} diff --git a/src/docs/user/userguide/diffusion_managing.diviner b/src/docs/user/userguide/diffusion_managing.diviner index 432b4de13d..a671b0f09e 100644 --- a/src/docs/user/userguide/diffusion_managing.diviner +++ b/src/docs/user/userguide/diffusion_managing.diviner @@ -246,11 +246,38 @@ repository. If a commit creates a larger file (or modifies an existing file so it becomes too large) it will be rejected. This option only applies to hosted repositories. +This limit is primarily intended to make it more difficult to accidentally push +very large files that shouldn't be version controlled (like logs, binaries, +machine learning data, or media assets). Pushing huge datafiles by mistake can +make the repository unwieldy by dramatically increasing how much data must be +transferred over the network to clone it, and simply reverting the changes +doesn't reduce the impact of this kind of mistake. + **Clone/Fetch Timeout**: Configure the internal timeout for creating copies of this repository during operations like intracluster synchronization and Drydock working copy construction. This timeout does not affect external users. +**Touch Limit**: Apply a limit to the maximum number of paths that any commit +may touch. If a commit affects more paths than this limit, it will be rejected. +This option only applies to hosted repositories. Users may work around this +limit by breaking the commit into several smaller commits which each affect +fewer paths. + +This limit is intended to offer a guard rail against users making silly +mistakes that create obviously mistaken changes, like copying an entire +repository into itself and pushing the result. This kind of change can take +some effort to clean up if it becomes part of repository history. + +Note that if you move a file, both the old and new locations count as touched +paths. You should generally configure this limit to be more than twice the +number of files you anticipate any user ever legitimately wanting to move in +a single commit. For example, a limit of `20000` will let users move up to +10,000 files in a single commit, but will reject users mistakenly trying to +push a copy of another repository or a directory with a million logfiles or +whatever other kind of creative nonsense they manage to dream up. + + Branches ========