Merge branch 'master' into blender-tweaks

This commit is contained in:
2017-12-06 16:58:17 +01:00
123 changed files with 2590 additions and 629 deletions

View File

@@ -9,11 +9,11 @@ return array(
'names' => array( 'names' => array(
'conpherence.pkg.css' => 'e68cf1fa', 'conpherence.pkg.css' => 'e68cf1fa',
'conpherence.pkg.js' => '15191c65', 'conpherence.pkg.js' => '15191c65',
'core.pkg.css' => '16369829', 'core.pkg.css' => '8ccb0ba3',
'core.pkg.js' => '4c79d74f', 'core.pkg.js' => '4c79d74f',
'darkconsole.pkg.js' => '1f9a31bc', 'darkconsole.pkg.js' => '1f9a31bc',
'differential.pkg.css' => '45951e9e', 'differential.pkg.css' => '45951e9e',
'differential.pkg.js' => 'b71b8c5d', 'differential.pkg.js' => '500a75c5',
'diffusion.pkg.css' => 'a2d17c7d', 'diffusion.pkg.css' => 'a2d17c7d',
'diffusion.pkg.js' => '6134c5a1', 'diffusion.pkg.js' => '6134c5a1',
'favicon.ico' => '4d48ee79', 'favicon.ico' => '4d48ee79',
@@ -73,7 +73,7 @@ return array(
'rsrc/css/application/diffusion/diffusion-icons.css' => '0c15255e', 'rsrc/css/application/diffusion/diffusion-icons.css' => '0c15255e',
'rsrc/css/application/diffusion/diffusion-readme.css' => '419dd5b6', 'rsrc/css/application/diffusion/diffusion-readme.css' => '419dd5b6',
'rsrc/css/application/diffusion/diffusion-repository.css' => 'ee6f20ec', 'rsrc/css/application/diffusion/diffusion-repository.css' => 'ee6f20ec',
'rsrc/css/application/diffusion/diffusion-source.css' => '69ac9399', 'rsrc/css/application/diffusion/diffusion-source.css' => '5f35a3bd',
'rsrc/css/application/diffusion/diffusion.css' => '45727264', 'rsrc/css/application/diffusion/diffusion.css' => '45727264',
'rsrc/css/application/feed/feed.css' => 'ecd4ec57', 'rsrc/css/application/feed/feed.css' => 'ecd4ec57',
'rsrc/css/application/files/global-drag-and-drop.css' => 'b556a948', 'rsrc/css/application/files/global-drag-and-drop.css' => 'b556a948',
@@ -176,7 +176,7 @@ return array(
'rsrc/css/phui/phui-spacing.css' => '042804d6', 'rsrc/css/phui/phui-spacing.css' => '042804d6',
'rsrc/css/phui/phui-status.css' => 'd5263e49', 'rsrc/css/phui/phui-status.css' => 'd5263e49',
'rsrc/css/phui/phui-tag-view.css' => 'b4719c50', 'rsrc/css/phui/phui-tag-view.css' => 'b4719c50',
'rsrc/css/phui/phui-timeline-view.css' => 'f21db7ca', 'rsrc/css/phui/phui-timeline-view.css' => 'e2ef62b1',
'rsrc/css/phui/phui-two-column-view.css' => '44ec4951', 'rsrc/css/phui/phui-two-column-view.css' => '44ec4951',
'rsrc/css/phui/workboards/phui-workboard-color.css' => '783cdff5', 'rsrc/css/phui/workboards/phui-workboard-color.css' => '783cdff5',
'rsrc/css/phui/workboards/phui-workboard.css' => '3bc85455', 'rsrc/css/phui/workboards/phui-workboard.css' => '3bc85455',
@@ -398,7 +398,7 @@ return array(
'rsrc/js/application/dashboard/behavior-dashboard-query-panel-select.js' => '453c5375', 'rsrc/js/application/dashboard/behavior-dashboard-query-panel-select.js' => '453c5375',
'rsrc/js/application/dashboard/behavior-dashboard-tab-panel.js' => 'd4eecc63', 'rsrc/js/application/dashboard/behavior-dashboard-tab-panel.js' => 'd4eecc63',
'rsrc/js/application/diff/DiffChangeset.js' => '99abf4cd', 'rsrc/js/application/diff/DiffChangeset.js' => '99abf4cd',
'rsrc/js/application/diff/DiffChangesetList.js' => '8f1cd52c', 'rsrc/js/application/diff/DiffChangesetList.js' => '3b77efdd',
'rsrc/js/application/diff/DiffInline.js' => 'e83d28f3', 'rsrc/js/application/diff/DiffInline.js' => 'e83d28f3',
'rsrc/js/application/diff/behavior-preview-link.js' => '051c7832', 'rsrc/js/application/diff/behavior-preview-link.js' => '051c7832',
'rsrc/js/application/differential/behavior-comment-preview.js' => '51c5ad07', 'rsrc/js/application/differential/behavior-comment-preview.js' => '51c5ad07',
@@ -446,7 +446,7 @@ return array(
'rsrc/js/application/releeph/releeph-preview-branch.js' => 'b2b4fbaf', 'rsrc/js/application/releeph/releeph-preview-branch.js' => 'b2b4fbaf',
'rsrc/js/application/releeph/releeph-request-state-change.js' => 'a0b57eb8', 'rsrc/js/application/releeph/releeph-request-state-change.js' => 'a0b57eb8',
'rsrc/js/application/releeph/releeph-request-typeahead.js' => 'de2e896f', 'rsrc/js/application/releeph/releeph-request-typeahead.js' => 'de2e896f',
'rsrc/js/application/repository/repository-crossreference.js' => 'e5339c43', 'rsrc/js/application/repository/repository-crossreference.js' => '7fe9bc12',
'rsrc/js/application/search/behavior-reorder-profile-menu-items.js' => 'e2e0a072', 'rsrc/js/application/search/behavior-reorder-profile-menu-items.js' => 'e2e0a072',
'rsrc/js/application/search/behavior-reorder-queries.js' => 'e9581f08', 'rsrc/js/application/search/behavior-reorder-queries.js' => 'e9581f08',
'rsrc/js/application/slowvote/behavior-slowvote-embed.js' => '887ad43f', 'rsrc/js/application/slowvote/behavior-slowvote-embed.js' => '887ad43f',
@@ -574,7 +574,7 @@ return array(
'diffusion-icons-css' => '0c15255e', 'diffusion-icons-css' => '0c15255e',
'diffusion-readme-css' => '419dd5b6', 'diffusion-readme-css' => '419dd5b6',
'diffusion-repository-css' => 'ee6f20ec', 'diffusion-repository-css' => 'ee6f20ec',
'diffusion-source-css' => '69ac9399', 'diffusion-source-css' => '5f35a3bd',
'diviner-shared-css' => '896f1d43', 'diviner-shared-css' => '896f1d43',
'font-fontawesome' => 'e838e088', 'font-fontawesome' => 'e838e088',
'font-lato' => 'c7ccd872', 'font-lato' => 'c7ccd872',
@@ -692,7 +692,7 @@ return array(
'javelin-behavior-reorder-applications' => '76b9fc3e', 'javelin-behavior-reorder-applications' => '76b9fc3e',
'javelin-behavior-reorder-columns' => 'e1d25dfb', 'javelin-behavior-reorder-columns' => 'e1d25dfb',
'javelin-behavior-reorder-profile-menu-items' => 'e2e0a072', 'javelin-behavior-reorder-profile-menu-items' => 'e2e0a072',
'javelin-behavior-repository-crossreference' => 'e5339c43', 'javelin-behavior-repository-crossreference' => '7fe9bc12',
'javelin-behavior-scrollbar' => '834a1173', 'javelin-behavior-scrollbar' => '834a1173',
'javelin-behavior-search-reorder-queries' => 'e9581f08', 'javelin-behavior-search-reorder-queries' => 'e9581f08',
'javelin-behavior-select-content' => 'bf5374ef', 'javelin-behavior-select-content' => 'bf5374ef',
@@ -777,7 +777,7 @@ return array(
'phabricator-darkmessage' => 'c48cccdd', 'phabricator-darkmessage' => 'c48cccdd',
'phabricator-dashboard-css' => 'fe5b1869', 'phabricator-dashboard-css' => 'fe5b1869',
'phabricator-diff-changeset' => '99abf4cd', 'phabricator-diff-changeset' => '99abf4cd',
'phabricator-diff-changeset-list' => '8f1cd52c', 'phabricator-diff-changeset-list' => '3b77efdd',
'phabricator-diff-inline' => 'e83d28f3', 'phabricator-diff-inline' => 'e83d28f3',
'phabricator-drag-and-drop-file-upload' => '58dea2fa', 'phabricator-drag-and-drop-file-upload' => '58dea2fa',
'phabricator-draggable-list' => 'bea6e7f4', 'phabricator-draggable-list' => 'bea6e7f4',
@@ -874,7 +874,7 @@ return array(
'phui-status-list-view-css' => 'd5263e49', 'phui-status-list-view-css' => 'd5263e49',
'phui-tag-view-css' => 'b4719c50', 'phui-tag-view-css' => 'b4719c50',
'phui-theme-css' => '9f261c6b', 'phui-theme-css' => '9f261c6b',
'phui-timeline-view-css' => 'f21db7ca', 'phui-timeline-view-css' => 'e2ef62b1',
'phui-two-column-view-css' => '44ec4951', 'phui-two-column-view-css' => '44ec4951',
'phui-workboard-color-css' => '783cdff5', 'phui-workboard-color-css' => '783cdff5',
'phui-workboard-view-css' => '3bc85455', 'phui-workboard-view-css' => '3bc85455',
@@ -1137,6 +1137,10 @@ return array(
'javelin-dom', 'javelin-dom',
'javelin-magical-init', 'javelin-magical-init',
), ),
'3b77efdd' => array(
'javelin-install',
'phuix-button-view',
),
'3cb0b2fc' => array( '3cb0b2fc' => array(
'javelin-behavior', 'javelin-behavior',
'javelin-dom', 'javelin-dom',
@@ -1549,6 +1553,12 @@ return array(
'7f243deb' => array( '7f243deb' => array(
'javelin-install', 'javelin-install',
), ),
'7fe9bc12' => array(
'javelin-behavior',
'javelin-dom',
'javelin-stratcom',
'javelin-uri',
),
'834a1173' => array( '834a1173' => array(
'javelin-behavior', 'javelin-behavior',
'javelin-scrollbar', 'javelin-scrollbar',
@@ -1607,10 +1617,6 @@ return array(
'8e1baf68' => array( '8e1baf68' => array(
'phui-button-css', 'phui-button-css',
), ),
'8f1cd52c' => array(
'javelin-install',
'phuix-button-view',
),
'8f29b364' => array( '8f29b364' => array(
'javelin-behavior', 'javelin-behavior',
'javelin-stratcom', 'javelin-stratcom',
@@ -2076,12 +2082,6 @@ return array(
'javelin-behavior', 'javelin-behavior',
'javelin-dom', 'javelin-dom',
), ),
'e5339c43' => array(
'javelin-behavior',
'javelin-dom',
'javelin-stratcom',
'javelin-uri',
),
'e5822781' => array( 'e5822781' => array(
'javelin-behavior', 'javelin-behavior',
'javelin-dom', 'javelin-dom',

View File

@@ -0,0 +1,9 @@
CREATE TABLE {$NAMESPACE}_ponder.ponder_question_fdocument (
id INT UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY,
objectPHID VARBINARY(64) NOT NULL,
isClosed BOOL NOT NULL,
authorPHID VARBINARY(64),
ownerPHID VARBINARY(64),
epochCreated INT UNSIGNED NOT NULL,
epochModified INT UNSIGNED NOT NULL
) ENGINE=InnoDB, COLLATE {$COLLATE_TEXT};

View File

@@ -0,0 +1,8 @@
CREATE TABLE {$NAMESPACE}_ponder.ponder_question_ffield (
id INT UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY,
documentID INT UNSIGNED NOT NULL,
fieldKey VARCHAR(4) NOT NULL COLLATE {$COLLATE_TEXT},
rawCorpus LONGTEXT NOT NULL COLLATE {$COLLATE_SORT},
termCorpus LONGTEXT NOT NULL COLLATE {$COLLATE_SORT},
normalCorpus LONGTEXT NOT NULL COLLATE {$COLLATE_SORT}
) ENGINE=InnoDB, COLLATE {$COLLATE_TEXT};

View File

@@ -0,0 +1,5 @@
CREATE TABLE {$NAMESPACE}_ponder.ponder_question_fngrams (
id INT UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY,
documentID INT UNSIGNED NOT NULL,
ngram CHAR(3) NOT NULL COLLATE {$COLLATE_TEXT}
) ENGINE=InnoDB, COLLATE {$COLLATE_TEXT};

View File

@@ -0,0 +1,7 @@
CREATE TABLE {$NAMESPACE}_ponder.ponder_question_fngrams_common (
id INT UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY,
ngram CHAR(3) NOT NULL COLLATE {$COLLATE_TEXT},
needsCollection BOOL NOT NULL,
UNIQUE KEY `key_ngram` (ngram),
KEY `key_collect` (needsCollection)
) ENGINE=InnoDB, COLLATE {$COLLATE_TEXT};

View File

@@ -0,0 +1,11 @@
<?php
$table = new PonderQuestion();
foreach (new LiskMigrationIterator($table) as $question) {
PhabricatorSearchWorker::queueDocumentForIndexing(
$question->getPHID(),
array(
'force' => true,
));
}

View File

@@ -0,0 +1,2 @@
ALTER TABLE {$NAMESPACE}_differential.differential_revision
ADD activeDiffPHID VARBINARY(64) NOT NULL;

View File

@@ -0,0 +1,24 @@
<?php
$table = new DifferentialRevision();
$conn = $table->establishConnection('w');
$diff_table = new DifferentialDiff();
foreach (new LiskMigrationIterator($table) as $revision) {
$revision_id = $revision->getID();
$diff_row = queryfx_one(
$conn,
'SELECT phid FROM %T WHERE revisionID = %d ORDER BY id DESC LIMIT 1',
$diff_table->getTableName(),
$revision_id);
if ($diff_row) {
queryfx(
$conn,
'UPDATE %T SET activeDiffPHID = %s WHERE id = %d',
$table->getTableName(),
$diff_row['phid'],
$revision_id);
}
}

View File

@@ -35,16 +35,23 @@ $args->parse(
$parser = new MimeMailParser(); $parser = new MimeMailParser();
$parser->setText(file_get_contents('php://stdin')); $parser->setText(file_get_contents('php://stdin'));
$text_body = $parser->getMessageBody('text'); $content = array();
foreach (array('text', 'html') as $part) {
$part_body = $parser->getMessageBody($part);
$text_body_headers = $parser->getMessageBodyHeaders('text'); if (strlen($part_body) && !phutil_is_utf8($part_body)) {
$content_type = idx($text_body_headers, 'content-type'); $part_headers = $parser->getMessageBodyHeaders($part);
if ( if (!is_array($part_headers)) {
!phutil_is_utf8($text_body) && $part_headers = array();
(preg_match('/charset="(.*?)"/', $content_type, $matches) || }
preg_match('/charset=(\S+)/', $content_type, $matches)) $content_type = idx($part_headers, 'content-type');
) { if (preg_match('/charset="(.*?)"/', $content_type, $matches) ||
$text_body = phutil_utf8_convert($text_body, 'UTF-8', $matches[1]); preg_match('/charset=(\S+)/', $content_type, $matches)) {
$part_body = phutil_utf8_convert($part_body, 'UTF-8', $matches[1]);
}
}
$content[$part] = $part_body;
} }
$headers = $parser->getHeaders(); $headers = $parser->getHeaders();
@@ -57,10 +64,7 @@ if ($args->getArg('process-duplicates')) {
$received = new PhabricatorMetaMTAReceivedMail(); $received = new PhabricatorMetaMTAReceivedMail();
$received->setHeaders($headers); $received->setHeaders($headers);
$received->setBodies(array( $received->setBodies($content);
'text' => $text_body,
'html' => $parser->getMessageBody('html'),
));
$attachments = array(); $attachments = array();
foreach ($parser->getAttachments() as $attachment) { foreach ($parser->getAttachments() as $attachment) {

View File

@@ -439,11 +439,14 @@ phutil_register_library_map(array(
'DifferentialDiffQuery' => 'applications/differential/query/DifferentialDiffQuery.php', 'DifferentialDiffQuery' => 'applications/differential/query/DifferentialDiffQuery.php',
'DifferentialDiffRepositoryHeraldField' => 'applications/differential/herald/DifferentialDiffRepositoryHeraldField.php', 'DifferentialDiffRepositoryHeraldField' => 'applications/differential/herald/DifferentialDiffRepositoryHeraldField.php',
'DifferentialDiffRepositoryProjectsHeraldField' => 'applications/differential/herald/DifferentialDiffRepositoryProjectsHeraldField.php', 'DifferentialDiffRepositoryProjectsHeraldField' => 'applications/differential/herald/DifferentialDiffRepositoryProjectsHeraldField.php',
'DifferentialDiffSearchConduitAPIMethod' => 'applications/differential/conduit/DifferentialDiffSearchConduitAPIMethod.php',
'DifferentialDiffSearchEngine' => 'applications/differential/query/DifferentialDiffSearchEngine.php',
'DifferentialDiffTestCase' => 'applications/differential/storage/__tests__/DifferentialDiffTestCase.php', 'DifferentialDiffTestCase' => 'applications/differential/storage/__tests__/DifferentialDiffTestCase.php',
'DifferentialDiffTransaction' => 'applications/differential/storage/DifferentialDiffTransaction.php', 'DifferentialDiffTransaction' => 'applications/differential/storage/DifferentialDiffTransaction.php',
'DifferentialDiffTransactionQuery' => 'applications/differential/query/DifferentialDiffTransactionQuery.php', 'DifferentialDiffTransactionQuery' => 'applications/differential/query/DifferentialDiffTransactionQuery.php',
'DifferentialDiffViewController' => 'applications/differential/controller/DifferentialDiffViewController.php', 'DifferentialDiffViewController' => 'applications/differential/controller/DifferentialDiffViewController.php',
'DifferentialDoorkeeperRevisionFeedStoryPublisher' => 'applications/differential/doorkeeper/DifferentialDoorkeeperRevisionFeedStoryPublisher.php', 'DifferentialDoorkeeperRevisionFeedStoryPublisher' => 'applications/differential/doorkeeper/DifferentialDoorkeeperRevisionFeedStoryPublisher.php',
'DifferentialDraftField' => 'applications/differential/customfield/DifferentialDraftField.php',
'DifferentialExactUserFunctionDatasource' => 'applications/differential/typeahead/DifferentialExactUserFunctionDatasource.php', 'DifferentialExactUserFunctionDatasource' => 'applications/differential/typeahead/DifferentialExactUserFunctionDatasource.php',
'DifferentialFieldParseException' => 'applications/differential/exception/DifferentialFieldParseException.php', 'DifferentialFieldParseException' => 'applications/differential/exception/DifferentialFieldParseException.php',
'DifferentialFieldValidationException' => 'applications/differential/exception/DifferentialFieldValidationException.php', 'DifferentialFieldValidationException' => 'applications/differential/exception/DifferentialFieldValidationException.php',
@@ -457,6 +460,7 @@ phutil_register_library_map(array(
'DifferentialGetWorkingCopy' => 'applications/differential/DifferentialGetWorkingCopy.php', 'DifferentialGetWorkingCopy' => 'applications/differential/DifferentialGetWorkingCopy.php',
'DifferentialGitSVNIDCommitMessageField' => 'applications/differential/field/DifferentialGitSVNIDCommitMessageField.php', 'DifferentialGitSVNIDCommitMessageField' => 'applications/differential/field/DifferentialGitSVNIDCommitMessageField.php',
'DifferentialHarbormasterField' => 'applications/differential/customfield/DifferentialHarbormasterField.php', 'DifferentialHarbormasterField' => 'applications/differential/customfield/DifferentialHarbormasterField.php',
'DifferentialHeraldStateReasons' => 'applications/differential/herald/DifferentialHeraldStateReasons.php',
'DifferentialHiddenComment' => 'applications/differential/storage/DifferentialHiddenComment.php', 'DifferentialHiddenComment' => 'applications/differential/storage/DifferentialHiddenComment.php',
'DifferentialHostField' => 'applications/differential/customfield/DifferentialHostField.php', 'DifferentialHostField' => 'applications/differential/customfield/DifferentialHostField.php',
'DifferentialHovercardEngineExtension' => 'applications/differential/engineextension/DifferentialHovercardEngineExtension.php', 'DifferentialHovercardEngineExtension' => 'applications/differential/engineextension/DifferentialHovercardEngineExtension.php',
@@ -546,6 +550,7 @@ phutil_register_library_map(array(
'DifferentialRevisionHasTaskRelationship' => 'applications/differential/relationships/DifferentialRevisionHasTaskRelationship.php', 'DifferentialRevisionHasTaskRelationship' => 'applications/differential/relationships/DifferentialRevisionHasTaskRelationship.php',
'DifferentialRevisionHeraldField' => 'applications/differential/herald/DifferentialRevisionHeraldField.php', 'DifferentialRevisionHeraldField' => 'applications/differential/herald/DifferentialRevisionHeraldField.php',
'DifferentialRevisionHeraldFieldGroup' => 'applications/differential/herald/DifferentialRevisionHeraldFieldGroup.php', 'DifferentialRevisionHeraldFieldGroup' => 'applications/differential/herald/DifferentialRevisionHeraldFieldGroup.php',
'DifferentialRevisionHoldDraftTransaction' => 'applications/differential/xaction/DifferentialRevisionHoldDraftTransaction.php',
'DifferentialRevisionIDCommitMessageField' => 'applications/differential/field/DifferentialRevisionIDCommitMessageField.php', 'DifferentialRevisionIDCommitMessageField' => 'applications/differential/field/DifferentialRevisionIDCommitMessageField.php',
'DifferentialRevisionInlineTransaction' => 'applications/differential/xaction/DifferentialRevisionInlineTransaction.php', 'DifferentialRevisionInlineTransaction' => 'applications/differential/xaction/DifferentialRevisionInlineTransaction.php',
'DifferentialRevisionInlinesController' => 'applications/differential/controller/DifferentialRevisionInlinesController.php', 'DifferentialRevisionInlinesController' => 'applications/differential/controller/DifferentialRevisionInlinesController.php',
@@ -589,6 +594,7 @@ phutil_register_library_map(array(
'DifferentialRevisionUpdateHistoryView' => 'applications/differential/view/DifferentialRevisionUpdateHistoryView.php', 'DifferentialRevisionUpdateHistoryView' => 'applications/differential/view/DifferentialRevisionUpdateHistoryView.php',
'DifferentialRevisionViewController' => 'applications/differential/controller/DifferentialRevisionViewController.php', 'DifferentialRevisionViewController' => 'applications/differential/controller/DifferentialRevisionViewController.php',
'DifferentialRevisionVoidTransaction' => 'applications/differential/xaction/DifferentialRevisionVoidTransaction.php', 'DifferentialRevisionVoidTransaction' => 'applications/differential/xaction/DifferentialRevisionVoidTransaction.php',
'DifferentialRevisionWrongStateTransaction' => 'applications/differential/xaction/DifferentialRevisionWrongStateTransaction.php',
'DifferentialSchemaSpec' => 'applications/differential/storage/DifferentialSchemaSpec.php', 'DifferentialSchemaSpec' => 'applications/differential/storage/DifferentialSchemaSpec.php',
'DifferentialSetDiffPropertyConduitAPIMethod' => 'applications/differential/conduit/DifferentialSetDiffPropertyConduitAPIMethod.php', 'DifferentialSetDiffPropertyConduitAPIMethod' => 'applications/differential/conduit/DifferentialSetDiffPropertyConduitAPIMethod.php',
'DifferentialStoredCustomField' => 'applications/differential/customfield/DifferentialStoredCustomField.php', 'DifferentialStoredCustomField' => 'applications/differential/customfield/DifferentialStoredCustomField.php',
@@ -763,6 +769,7 @@ phutil_register_library_map(array(
'DiffusionMercurialBlameQuery' => 'applications/diffusion/query/blame/DiffusionMercurialBlameQuery.php', 'DiffusionMercurialBlameQuery' => 'applications/diffusion/query/blame/DiffusionMercurialBlameQuery.php',
'DiffusionMercurialCommandEngine' => 'applications/diffusion/protocol/DiffusionMercurialCommandEngine.php', 'DiffusionMercurialCommandEngine' => 'applications/diffusion/protocol/DiffusionMercurialCommandEngine.php',
'DiffusionMercurialFileContentQuery' => 'applications/diffusion/query/filecontent/DiffusionMercurialFileContentQuery.php', 'DiffusionMercurialFileContentQuery' => 'applications/diffusion/query/filecontent/DiffusionMercurialFileContentQuery.php',
'DiffusionMercurialFlagInjectionException' => 'applications/diffusion/exception/DiffusionMercurialFlagInjectionException.php',
'DiffusionMercurialRawDiffQuery' => 'applications/diffusion/query/rawdiff/DiffusionMercurialRawDiffQuery.php', 'DiffusionMercurialRawDiffQuery' => 'applications/diffusion/query/rawdiff/DiffusionMercurialRawDiffQuery.php',
'DiffusionMercurialRequest' => 'applications/diffusion/request/DiffusionMercurialRequest.php', 'DiffusionMercurialRequest' => 'applications/diffusion/request/DiffusionMercurialRequest.php',
'DiffusionMercurialResponse' => 'applications/diffusion/response/DiffusionMercurialResponse.php', 'DiffusionMercurialResponse' => 'applications/diffusion/response/DiffusionMercurialResponse.php',
@@ -825,6 +832,7 @@ phutil_register_library_map(array(
'DiffusionQueryCommitsConduitAPIMethod' => 'applications/diffusion/conduit/DiffusionQueryCommitsConduitAPIMethod.php', 'DiffusionQueryCommitsConduitAPIMethod' => 'applications/diffusion/conduit/DiffusionQueryCommitsConduitAPIMethod.php',
'DiffusionQueryConduitAPIMethod' => 'applications/diffusion/conduit/DiffusionQueryConduitAPIMethod.php', 'DiffusionQueryConduitAPIMethod' => 'applications/diffusion/conduit/DiffusionQueryConduitAPIMethod.php',
'DiffusionQueryPathsConduitAPIMethod' => 'applications/diffusion/conduit/DiffusionQueryPathsConduitAPIMethod.php', 'DiffusionQueryPathsConduitAPIMethod' => 'applications/diffusion/conduit/DiffusionQueryPathsConduitAPIMethod.php',
'DiffusionQuickSearchEngineExtension' => 'applications/diffusion/engineextension/DiffusionQuickSearchEngineExtension.php',
'DiffusionRawDiffQuery' => 'applications/diffusion/query/rawdiff/DiffusionRawDiffQuery.php', 'DiffusionRawDiffQuery' => 'applications/diffusion/query/rawdiff/DiffusionRawDiffQuery.php',
'DiffusionRawDiffQueryConduitAPIMethod' => 'applications/diffusion/conduit/DiffusionRawDiffQueryConduitAPIMethod.php', 'DiffusionRawDiffQueryConduitAPIMethod' => 'applications/diffusion/conduit/DiffusionRawDiffQueryConduitAPIMethod.php',
'DiffusionReadmeView' => 'applications/diffusion/view/DiffusionReadmeView.php', 'DiffusionReadmeView' => 'applications/diffusion/view/DiffusionReadmeView.php',
@@ -1327,6 +1335,7 @@ phutil_register_library_map(array(
'HeraldApplicationActionGroup' => 'applications/herald/action/HeraldApplicationActionGroup.php', 'HeraldApplicationActionGroup' => 'applications/herald/action/HeraldApplicationActionGroup.php',
'HeraldApplyTranscript' => 'applications/herald/storage/transcript/HeraldApplyTranscript.php', 'HeraldApplyTranscript' => 'applications/herald/storage/transcript/HeraldApplyTranscript.php',
'HeraldBasicFieldGroup' => 'applications/herald/field/HeraldBasicFieldGroup.php', 'HeraldBasicFieldGroup' => 'applications/herald/field/HeraldBasicFieldGroup.php',
'HeraldBuildableState' => 'applications/herald/state/HeraldBuildableState.php',
'HeraldCommitAdapter' => 'applications/diffusion/herald/HeraldCommitAdapter.php', 'HeraldCommitAdapter' => 'applications/diffusion/herald/HeraldCommitAdapter.php',
'HeraldCondition' => 'applications/herald/storage/HeraldCondition.php', 'HeraldCondition' => 'applications/herald/storage/HeraldCondition.php',
'HeraldConditionTranscript' => 'applications/herald/storage/transcript/HeraldConditionTranscript.php', 'HeraldConditionTranscript' => 'applications/herald/storage/transcript/HeraldConditionTranscript.php',
@@ -1350,6 +1359,7 @@ phutil_register_library_map(array(
'HeraldGroup' => 'applications/herald/group/HeraldGroup.php', 'HeraldGroup' => 'applications/herald/group/HeraldGroup.php',
'HeraldInvalidActionException' => 'applications/herald/engine/exception/HeraldInvalidActionException.php', 'HeraldInvalidActionException' => 'applications/herald/engine/exception/HeraldInvalidActionException.php',
'HeraldInvalidConditionException' => 'applications/herald/engine/exception/HeraldInvalidConditionException.php', 'HeraldInvalidConditionException' => 'applications/herald/engine/exception/HeraldInvalidConditionException.php',
'HeraldMailableState' => 'applications/herald/state/HeraldMailableState.php',
'HeraldManageGlobalRulesCapability' => 'applications/herald/capability/HeraldManageGlobalRulesCapability.php', 'HeraldManageGlobalRulesCapability' => 'applications/herald/capability/HeraldManageGlobalRulesCapability.php',
'HeraldManiphestTaskAdapter' => 'applications/maniphest/herald/HeraldManiphestTaskAdapter.php', 'HeraldManiphestTaskAdapter' => 'applications/maniphest/herald/HeraldManiphestTaskAdapter.php',
'HeraldNewController' => 'applications/herald/controller/HeraldNewController.php', 'HeraldNewController' => 'applications/herald/controller/HeraldNewController.php',
@@ -1387,6 +1397,8 @@ phutil_register_library_map(array(
'HeraldSchemaSpec' => 'applications/herald/storage/HeraldSchemaSpec.php', 'HeraldSchemaSpec' => 'applications/herald/storage/HeraldSchemaSpec.php',
'HeraldSelectFieldValue' => 'applications/herald/value/HeraldSelectFieldValue.php', 'HeraldSelectFieldValue' => 'applications/herald/value/HeraldSelectFieldValue.php',
'HeraldSpaceField' => 'applications/spaces/herald/HeraldSpaceField.php', 'HeraldSpaceField' => 'applications/spaces/herald/HeraldSpaceField.php',
'HeraldState' => 'applications/herald/state/HeraldState.php',
'HeraldStateReasons' => 'applications/herald/state/HeraldStateReasons.php',
'HeraldSubscribersField' => 'applications/subscriptions/herald/HeraldSubscribersField.php', 'HeraldSubscribersField' => 'applications/subscriptions/herald/HeraldSubscribersField.php',
'HeraldSupportActionGroup' => 'applications/herald/action/HeraldSupportActionGroup.php', 'HeraldSupportActionGroup' => 'applications/herald/action/HeraldSupportActionGroup.php',
'HeraldSupportFieldGroup' => 'applications/herald/field/HeraldSupportFieldGroup.php', 'HeraldSupportFieldGroup' => 'applications/herald/field/HeraldSupportFieldGroup.php',
@@ -2529,6 +2541,8 @@ phutil_register_library_map(array(
'PhabricatorCustomFieldEditField' => 'infrastructure/customfield/editor/PhabricatorCustomFieldEditField.php', 'PhabricatorCustomFieldEditField' => 'infrastructure/customfield/editor/PhabricatorCustomFieldEditField.php',
'PhabricatorCustomFieldEditType' => 'infrastructure/customfield/editor/PhabricatorCustomFieldEditType.php', 'PhabricatorCustomFieldEditType' => 'infrastructure/customfield/editor/PhabricatorCustomFieldEditType.php',
'PhabricatorCustomFieldFulltextEngineExtension' => 'infrastructure/customfield/engineextension/PhabricatorCustomFieldFulltextEngineExtension.php', 'PhabricatorCustomFieldFulltextEngineExtension' => 'infrastructure/customfield/engineextension/PhabricatorCustomFieldFulltextEngineExtension.php',
'PhabricatorCustomFieldHeraldAction' => 'infrastructure/customfield/herald/PhabricatorCustomFieldHeraldAction.php',
'PhabricatorCustomFieldHeraldActionGroup' => 'infrastructure/customfield/herald/PhabricatorCustomFieldHeraldActionGroup.php',
'PhabricatorCustomFieldHeraldField' => 'infrastructure/customfield/herald/PhabricatorCustomFieldHeraldField.php', 'PhabricatorCustomFieldHeraldField' => 'infrastructure/customfield/herald/PhabricatorCustomFieldHeraldField.php',
'PhabricatorCustomFieldHeraldFieldGroup' => 'infrastructure/customfield/herald/PhabricatorCustomFieldHeraldFieldGroup.php', 'PhabricatorCustomFieldHeraldFieldGroup' => 'infrastructure/customfield/herald/PhabricatorCustomFieldHeraldFieldGroup.php',
'PhabricatorCustomFieldImplementationIncompleteException' => 'infrastructure/customfield/exception/PhabricatorCustomFieldImplementationIncompleteException.php', 'PhabricatorCustomFieldImplementationIncompleteException' => 'infrastructure/customfield/exception/PhabricatorCustomFieldImplementationIncompleteException.php',
@@ -3194,6 +3208,7 @@ phutil_register_library_map(array(
'PhabricatorMetronomicTriggerClock' => 'infrastructure/daemon/workers/clock/PhabricatorMetronomicTriggerClock.php', 'PhabricatorMetronomicTriggerClock' => 'infrastructure/daemon/workers/clock/PhabricatorMetronomicTriggerClock.php',
'PhabricatorModularTransaction' => 'applications/transactions/storage/PhabricatorModularTransaction.php', 'PhabricatorModularTransaction' => 'applications/transactions/storage/PhabricatorModularTransaction.php',
'PhabricatorModularTransactionType' => 'applications/transactions/storage/PhabricatorModularTransactionType.php', 'PhabricatorModularTransactionType' => 'applications/transactions/storage/PhabricatorModularTransactionType.php',
'PhabricatorMonogramQuickSearchEngineExtension' => 'applications/typeahead/engineextension/PhabricatorMonogramQuickSearchEngineExtension.php',
'PhabricatorMonospacedFontSetting' => 'applications/settings/setting/PhabricatorMonospacedFontSetting.php', 'PhabricatorMonospacedFontSetting' => 'applications/settings/setting/PhabricatorMonospacedFontSetting.php',
'PhabricatorMonospacedTextareasSetting' => 'applications/settings/setting/PhabricatorMonospacedTextareasSetting.php', 'PhabricatorMonospacedTextareasSetting' => 'applications/settings/setting/PhabricatorMonospacedTextareasSetting.php',
'PhabricatorMotivatorProfileMenuItem' => 'applications/search/menuitem/PhabricatorMotivatorProfileMenuItem.php', 'PhabricatorMotivatorProfileMenuItem' => 'applications/search/menuitem/PhabricatorMotivatorProfileMenuItem.php',
@@ -3517,6 +3532,7 @@ phutil_register_library_map(array(
'PhabricatorPeopleProfileTasksController' => 'applications/people/controller/PhabricatorPeopleProfileTasksController.php', 'PhabricatorPeopleProfileTasksController' => 'applications/people/controller/PhabricatorPeopleProfileTasksController.php',
'PhabricatorPeopleProfileViewController' => 'applications/people/controller/PhabricatorPeopleProfileViewController.php', 'PhabricatorPeopleProfileViewController' => 'applications/people/controller/PhabricatorPeopleProfileViewController.php',
'PhabricatorPeopleQuery' => 'applications/people/query/PhabricatorPeopleQuery.php', 'PhabricatorPeopleQuery' => 'applications/people/query/PhabricatorPeopleQuery.php',
'PhabricatorPeopleQuickSearchEngineExtension' => 'applications/people/engineextension/PhabricatorPeopleQuickSearchEngineExtension.php',
'PhabricatorPeopleRenameController' => 'applications/people/controller/PhabricatorPeopleRenameController.php', 'PhabricatorPeopleRenameController' => 'applications/people/controller/PhabricatorPeopleRenameController.php',
'PhabricatorPeopleRevisionsProfileMenuItem' => 'applications/people/menuitem/PhabricatorPeopleRevisionsProfileMenuItem.php', 'PhabricatorPeopleRevisionsProfileMenuItem' => 'applications/people/menuitem/PhabricatorPeopleRevisionsProfileMenuItem.php',
'PhabricatorPeopleSearchEngine' => 'applications/people/query/PhabricatorPeopleSearchEngine.php', 'PhabricatorPeopleSearchEngine' => 'applications/people/query/PhabricatorPeopleSearchEngine.php',
@@ -3777,6 +3793,9 @@ phutil_register_library_map(array(
'PhabricatorQueryOrderItem' => 'infrastructure/query/order/PhabricatorQueryOrderItem.php', 'PhabricatorQueryOrderItem' => 'infrastructure/query/order/PhabricatorQueryOrderItem.php',
'PhabricatorQueryOrderTestCase' => 'infrastructure/query/order/__tests__/PhabricatorQueryOrderTestCase.php', 'PhabricatorQueryOrderTestCase' => 'infrastructure/query/order/__tests__/PhabricatorQueryOrderTestCase.php',
'PhabricatorQueryOrderVector' => 'infrastructure/query/order/PhabricatorQueryOrderVector.php', 'PhabricatorQueryOrderVector' => 'infrastructure/query/order/PhabricatorQueryOrderVector.php',
'PhabricatorQuickSearchApplicationEngineExtension' => 'applications/meta/engineextension/PhabricatorQuickSearchApplicationEngineExtension.php',
'PhabricatorQuickSearchEngine' => 'applications/search/engine/PhabricatorQuickSearchEngine.php',
'PhabricatorQuickSearchEngineExtension' => 'applications/search/engineextension/PhabricatorQuickSearchEngineExtension.php',
'PhabricatorRateLimitRequestExceptionHandler' => 'aphront/handler/PhabricatorRateLimitRequestExceptionHandler.php', 'PhabricatorRateLimitRequestExceptionHandler' => 'aphront/handler/PhabricatorRateLimitRequestExceptionHandler.php',
'PhabricatorRecaptchaConfigOptions' => 'applications/config/option/PhabricatorRecaptchaConfigOptions.php', 'PhabricatorRecaptchaConfigOptions' => 'applications/config/option/PhabricatorRecaptchaConfigOptions.php',
'PhabricatorRedirectController' => 'applications/base/controller/PhabricatorRedirectController.php', 'PhabricatorRedirectController' => 'applications/base/controller/PhabricatorRedirectController.php',
@@ -4778,6 +4797,7 @@ phutil_register_library_map(array(
'PonderQuestionEditController' => 'applications/ponder/controller/PonderQuestionEditController.php', 'PonderQuestionEditController' => 'applications/ponder/controller/PonderQuestionEditController.php',
'PonderQuestionEditEngine' => 'applications/ponder/editor/PonderQuestionEditEngine.php', 'PonderQuestionEditEngine' => 'applications/ponder/editor/PonderQuestionEditEngine.php',
'PonderQuestionEditor' => 'applications/ponder/editor/PonderQuestionEditor.php', 'PonderQuestionEditor' => 'applications/ponder/editor/PonderQuestionEditor.php',
'PonderQuestionFerretEngine' => 'applications/ponder/search/PonderQuestionFerretEngine.php',
'PonderQuestionFulltextEngine' => 'applications/ponder/search/PonderQuestionFulltextEngine.php', 'PonderQuestionFulltextEngine' => 'applications/ponder/search/PonderQuestionFulltextEngine.php',
'PonderQuestionHistoryController' => 'applications/ponder/controller/PonderQuestionHistoryController.php', 'PonderQuestionHistoryController' => 'applications/ponder/controller/PonderQuestionHistoryController.php',
'PonderQuestionListController' => 'applications/ponder/controller/PonderQuestionListController.php', 'PonderQuestionListController' => 'applications/ponder/controller/PonderQuestionListController.php',
@@ -4809,6 +4829,7 @@ phutil_register_library_map(array(
'ProjectDefaultViewCapability' => 'applications/project/capability/ProjectDefaultViewCapability.php', 'ProjectDefaultViewCapability' => 'applications/project/capability/ProjectDefaultViewCapability.php',
'ProjectEditConduitAPIMethod' => 'applications/project/conduit/ProjectEditConduitAPIMethod.php', 'ProjectEditConduitAPIMethod' => 'applications/project/conduit/ProjectEditConduitAPIMethod.php',
'ProjectQueryConduitAPIMethod' => 'applications/project/conduit/ProjectQueryConduitAPIMethod.php', 'ProjectQueryConduitAPIMethod' => 'applications/project/conduit/ProjectQueryConduitAPIMethod.php',
'ProjectQuickSearchEngineExtension' => 'applications/project/engineextension/ProjectQuickSearchEngineExtension.php',
'ProjectRemarkupRule' => 'applications/project/remarkup/ProjectRemarkupRule.php', 'ProjectRemarkupRule' => 'applications/project/remarkup/ProjectRemarkupRule.php',
'ProjectRemarkupRuleTestCase' => 'applications/project/remarkup/__tests__/ProjectRemarkupRuleTestCase.php', 'ProjectRemarkupRuleTestCase' => 'applications/project/remarkup/__tests__/ProjectRemarkupRuleTestCase.php',
'ProjectReplyHandler' => 'applications/project/mail/ProjectReplyHandler.php', 'ProjectReplyHandler' => 'applications/project/mail/ProjectReplyHandler.php',
@@ -5433,6 +5454,7 @@ phutil_register_library_map(array(
'HarbormasterBuildkiteBuildableInterface', 'HarbormasterBuildkiteBuildableInterface',
'PhabricatorApplicationTransactionInterface', 'PhabricatorApplicationTransactionInterface',
'PhabricatorDestructibleInterface', 'PhabricatorDestructibleInterface',
'PhabricatorConduitResultInterface',
), ),
'DifferentialDiffAffectedFilesHeraldField' => 'DifferentialDiffHeraldField', 'DifferentialDiffAffectedFilesHeraldField' => 'DifferentialDiffHeraldField',
'DifferentialDiffAuthorHeraldField' => 'DifferentialDiffHeraldField', 'DifferentialDiffAuthorHeraldField' => 'DifferentialDiffHeraldField',
@@ -5451,11 +5473,14 @@ phutil_register_library_map(array(
'DifferentialDiffQuery' => 'PhabricatorCursorPagedPolicyAwareQuery', 'DifferentialDiffQuery' => 'PhabricatorCursorPagedPolicyAwareQuery',
'DifferentialDiffRepositoryHeraldField' => 'DifferentialDiffHeraldField', 'DifferentialDiffRepositoryHeraldField' => 'DifferentialDiffHeraldField',
'DifferentialDiffRepositoryProjectsHeraldField' => 'DifferentialDiffHeraldField', 'DifferentialDiffRepositoryProjectsHeraldField' => 'DifferentialDiffHeraldField',
'DifferentialDiffSearchConduitAPIMethod' => 'PhabricatorSearchEngineAPIMethod',
'DifferentialDiffSearchEngine' => 'PhabricatorApplicationSearchEngine',
'DifferentialDiffTestCase' => 'PhutilTestCase', 'DifferentialDiffTestCase' => 'PhutilTestCase',
'DifferentialDiffTransaction' => 'PhabricatorApplicationTransaction', 'DifferentialDiffTransaction' => 'PhabricatorApplicationTransaction',
'DifferentialDiffTransactionQuery' => 'PhabricatorApplicationTransactionQuery', 'DifferentialDiffTransactionQuery' => 'PhabricatorApplicationTransactionQuery',
'DifferentialDiffViewController' => 'DifferentialController', 'DifferentialDiffViewController' => 'DifferentialController',
'DifferentialDoorkeeperRevisionFeedStoryPublisher' => 'DoorkeeperFeedStoryPublisher', 'DifferentialDoorkeeperRevisionFeedStoryPublisher' => 'DoorkeeperFeedStoryPublisher',
'DifferentialDraftField' => 'DifferentialCoreCustomField',
'DifferentialExactUserFunctionDatasource' => 'PhabricatorTypeaheadCompositeDatasource', 'DifferentialExactUserFunctionDatasource' => 'PhabricatorTypeaheadCompositeDatasource',
'DifferentialFieldParseException' => 'Exception', 'DifferentialFieldParseException' => 'Exception',
'DifferentialFieldValidationException' => 'Exception', 'DifferentialFieldValidationException' => 'Exception',
@@ -5469,6 +5494,7 @@ phutil_register_library_map(array(
'DifferentialGetWorkingCopy' => 'Phobject', 'DifferentialGetWorkingCopy' => 'Phobject',
'DifferentialGitSVNIDCommitMessageField' => 'DifferentialCommitMessageField', 'DifferentialGitSVNIDCommitMessageField' => 'DifferentialCommitMessageField',
'DifferentialHarbormasterField' => 'DifferentialCustomField', 'DifferentialHarbormasterField' => 'DifferentialCustomField',
'DifferentialHeraldStateReasons' => 'HeraldStateReasons',
'DifferentialHiddenComment' => 'DifferentialDAO', 'DifferentialHiddenComment' => 'DifferentialDAO',
'DifferentialHostField' => 'DifferentialCustomField', 'DifferentialHostField' => 'DifferentialCustomField',
'DifferentialHovercardEngineExtension' => 'PhabricatorHovercardEngineExtension', 'DifferentialHovercardEngineExtension' => 'PhabricatorHovercardEngineExtension',
@@ -5583,6 +5609,7 @@ phutil_register_library_map(array(
'DifferentialRevisionHasTaskRelationship' => 'DifferentialRevisionRelationship', 'DifferentialRevisionHasTaskRelationship' => 'DifferentialRevisionRelationship',
'DifferentialRevisionHeraldField' => 'HeraldField', 'DifferentialRevisionHeraldField' => 'HeraldField',
'DifferentialRevisionHeraldFieldGroup' => 'HeraldFieldGroup', 'DifferentialRevisionHeraldFieldGroup' => 'HeraldFieldGroup',
'DifferentialRevisionHoldDraftTransaction' => 'DifferentialRevisionTransactionType',
'DifferentialRevisionIDCommitMessageField' => 'DifferentialCommitMessageField', 'DifferentialRevisionIDCommitMessageField' => 'DifferentialCommitMessageField',
'DifferentialRevisionInlineTransaction' => 'PhabricatorModularTransactionType', 'DifferentialRevisionInlineTransaction' => 'PhabricatorModularTransactionType',
'DifferentialRevisionInlinesController' => 'DifferentialController', 'DifferentialRevisionInlinesController' => 'DifferentialController',
@@ -5626,6 +5653,7 @@ phutil_register_library_map(array(
'DifferentialRevisionUpdateHistoryView' => 'AphrontView', 'DifferentialRevisionUpdateHistoryView' => 'AphrontView',
'DifferentialRevisionViewController' => 'DifferentialController', 'DifferentialRevisionViewController' => 'DifferentialController',
'DifferentialRevisionVoidTransaction' => 'DifferentialRevisionTransactionType', 'DifferentialRevisionVoidTransaction' => 'DifferentialRevisionTransactionType',
'DifferentialRevisionWrongStateTransaction' => 'DifferentialRevisionTransactionType',
'DifferentialSchemaSpec' => 'PhabricatorConfigSchemaSpec', 'DifferentialSchemaSpec' => 'PhabricatorConfigSchemaSpec',
'DifferentialSetDiffPropertyConduitAPIMethod' => 'DifferentialConduitAPIMethod', 'DifferentialSetDiffPropertyConduitAPIMethod' => 'DifferentialConduitAPIMethod',
'DifferentialStoredCustomField' => 'DifferentialCustomField', 'DifferentialStoredCustomField' => 'DifferentialCustomField',
@@ -5803,6 +5831,7 @@ phutil_register_library_map(array(
'DiffusionMercurialBlameQuery' => 'DiffusionBlameQuery', 'DiffusionMercurialBlameQuery' => 'DiffusionBlameQuery',
'DiffusionMercurialCommandEngine' => 'DiffusionCommandEngine', 'DiffusionMercurialCommandEngine' => 'DiffusionCommandEngine',
'DiffusionMercurialFileContentQuery' => 'DiffusionFileContentQuery', 'DiffusionMercurialFileContentQuery' => 'DiffusionFileContentQuery',
'DiffusionMercurialFlagInjectionException' => 'Exception',
'DiffusionMercurialRawDiffQuery' => 'DiffusionRawDiffQuery', 'DiffusionMercurialRawDiffQuery' => 'DiffusionRawDiffQuery',
'DiffusionMercurialRequest' => 'DiffusionRequest', 'DiffusionMercurialRequest' => 'DiffusionRequest',
'DiffusionMercurialResponse' => 'AphrontResponse', 'DiffusionMercurialResponse' => 'AphrontResponse',
@@ -5865,6 +5894,7 @@ phutil_register_library_map(array(
'DiffusionQueryCommitsConduitAPIMethod' => 'DiffusionConduitAPIMethod', 'DiffusionQueryCommitsConduitAPIMethod' => 'DiffusionConduitAPIMethod',
'DiffusionQueryConduitAPIMethod' => 'DiffusionConduitAPIMethod', 'DiffusionQueryConduitAPIMethod' => 'DiffusionConduitAPIMethod',
'DiffusionQueryPathsConduitAPIMethod' => 'DiffusionQueryConduitAPIMethod', 'DiffusionQueryPathsConduitAPIMethod' => 'DiffusionQueryConduitAPIMethod',
'DiffusionQuickSearchEngineExtension' => 'PhabricatorQuickSearchEngineExtension',
'DiffusionRawDiffQuery' => 'DiffusionFileFutureQuery', 'DiffusionRawDiffQuery' => 'DiffusionFileFutureQuery',
'DiffusionRawDiffQueryConduitAPIMethod' => 'DiffusionQueryConduitAPIMethod', 'DiffusionRawDiffQueryConduitAPIMethod' => 'DiffusionQueryConduitAPIMethod',
'DiffusionReadmeView' => 'DiffusionView', 'DiffusionReadmeView' => 'DiffusionView',
@@ -6460,6 +6490,7 @@ phutil_register_library_map(array(
'HeraldApplicationActionGroup' => 'HeraldActionGroup', 'HeraldApplicationActionGroup' => 'HeraldActionGroup',
'HeraldApplyTranscript' => 'Phobject', 'HeraldApplyTranscript' => 'Phobject',
'HeraldBasicFieldGroup' => 'HeraldFieldGroup', 'HeraldBasicFieldGroup' => 'HeraldFieldGroup',
'HeraldBuildableState' => 'HeraldState',
'HeraldCommitAdapter' => array( 'HeraldCommitAdapter' => array(
'HeraldAdapter', 'HeraldAdapter',
'HarbormasterBuildableAdapterInterface', 'HarbormasterBuildableAdapterInterface',
@@ -6489,6 +6520,7 @@ phutil_register_library_map(array(
'HeraldGroup' => 'Phobject', 'HeraldGroup' => 'Phobject',
'HeraldInvalidActionException' => 'Exception', 'HeraldInvalidActionException' => 'Exception',
'HeraldInvalidConditionException' => 'Exception', 'HeraldInvalidConditionException' => 'Exception',
'HeraldMailableState' => 'HeraldState',
'HeraldManageGlobalRulesCapability' => 'PhabricatorPolicyCapability', 'HeraldManageGlobalRulesCapability' => 'PhabricatorPolicyCapability',
'HeraldManiphestTaskAdapter' => 'HeraldAdapter', 'HeraldManiphestTaskAdapter' => 'HeraldAdapter',
'HeraldNewController' => 'HeraldController', 'HeraldNewController' => 'HeraldController',
@@ -6533,6 +6565,8 @@ phutil_register_library_map(array(
'HeraldSchemaSpec' => 'PhabricatorConfigSchemaSpec', 'HeraldSchemaSpec' => 'PhabricatorConfigSchemaSpec',
'HeraldSelectFieldValue' => 'HeraldFieldValue', 'HeraldSelectFieldValue' => 'HeraldFieldValue',
'HeraldSpaceField' => 'HeraldField', 'HeraldSpaceField' => 'HeraldField',
'HeraldState' => 'Phobject',
'HeraldStateReasons' => 'Phobject',
'HeraldSubscribersField' => 'HeraldField', 'HeraldSubscribersField' => 'HeraldField',
'HeraldSupportActionGroup' => 'HeraldActionGroup', 'HeraldSupportActionGroup' => 'HeraldActionGroup',
'HeraldSupportFieldGroup' => 'HeraldFieldGroup', 'HeraldSupportFieldGroup' => 'HeraldFieldGroup',
@@ -7855,6 +7889,8 @@ phutil_register_library_map(array(
'PhabricatorCustomFieldEditField' => 'PhabricatorEditField', 'PhabricatorCustomFieldEditField' => 'PhabricatorEditField',
'PhabricatorCustomFieldEditType' => 'PhabricatorEditType', 'PhabricatorCustomFieldEditType' => 'PhabricatorEditType',
'PhabricatorCustomFieldFulltextEngineExtension' => 'PhabricatorFulltextEngineExtension', 'PhabricatorCustomFieldFulltextEngineExtension' => 'PhabricatorFulltextEngineExtension',
'PhabricatorCustomFieldHeraldAction' => 'HeraldAction',
'PhabricatorCustomFieldHeraldActionGroup' => 'HeraldActionGroup',
'PhabricatorCustomFieldHeraldField' => 'HeraldField', 'PhabricatorCustomFieldHeraldField' => 'HeraldField',
'PhabricatorCustomFieldHeraldFieldGroup' => 'HeraldFieldGroup', 'PhabricatorCustomFieldHeraldFieldGroup' => 'HeraldFieldGroup',
'PhabricatorCustomFieldImplementationIncompleteException' => 'Exception', 'PhabricatorCustomFieldImplementationIncompleteException' => 'Exception',
@@ -8595,6 +8631,7 @@ phutil_register_library_map(array(
'PhabricatorMetronomicTriggerClock' => 'PhabricatorTriggerClock', 'PhabricatorMetronomicTriggerClock' => 'PhabricatorTriggerClock',
'PhabricatorModularTransaction' => 'PhabricatorApplicationTransaction', 'PhabricatorModularTransaction' => 'PhabricatorApplicationTransaction',
'PhabricatorModularTransactionType' => 'Phobject', 'PhabricatorModularTransactionType' => 'Phobject',
'PhabricatorMonogramQuickSearchEngineExtension' => 'PhabricatorQuickSearchEngineExtension',
'PhabricatorMonospacedFontSetting' => 'PhabricatorStringSetting', 'PhabricatorMonospacedFontSetting' => 'PhabricatorStringSetting',
'PhabricatorMonospacedTextareasSetting' => 'PhabricatorSelectSetting', 'PhabricatorMonospacedTextareasSetting' => 'PhabricatorSelectSetting',
'PhabricatorMotivatorProfileMenuItem' => 'PhabricatorProfileMenuItem', 'PhabricatorMotivatorProfileMenuItem' => 'PhabricatorProfileMenuItem',
@@ -8987,6 +9024,7 @@ phutil_register_library_map(array(
'PhabricatorPeopleProfileTasksController' => 'PhabricatorPeopleProfileController', 'PhabricatorPeopleProfileTasksController' => 'PhabricatorPeopleProfileController',
'PhabricatorPeopleProfileViewController' => 'PhabricatorPeopleProfileController', 'PhabricatorPeopleProfileViewController' => 'PhabricatorPeopleProfileController',
'PhabricatorPeopleQuery' => 'PhabricatorCursorPagedPolicyAwareQuery', 'PhabricatorPeopleQuery' => 'PhabricatorCursorPagedPolicyAwareQuery',
'PhabricatorPeopleQuickSearchEngineExtension' => 'PhabricatorQuickSearchEngineExtension',
'PhabricatorPeopleRenameController' => 'PhabricatorPeopleController', 'PhabricatorPeopleRenameController' => 'PhabricatorPeopleController',
'PhabricatorPeopleRevisionsProfileMenuItem' => 'PhabricatorProfileMenuItem', 'PhabricatorPeopleRevisionsProfileMenuItem' => 'PhabricatorProfileMenuItem',
'PhabricatorPeopleSearchEngine' => 'PhabricatorApplicationSearchEngine', 'PhabricatorPeopleSearchEngine' => 'PhabricatorApplicationSearchEngine',
@@ -9302,6 +9340,9 @@ phutil_register_library_map(array(
'Phobject', 'Phobject',
'Iterator', 'Iterator',
), ),
'PhabricatorQuickSearchApplicationEngineExtension' => 'PhabricatorQuickSearchEngineExtension',
'PhabricatorQuickSearchEngine' => 'Phobject',
'PhabricatorQuickSearchEngineExtension' => 'Phobject',
'PhabricatorRateLimitRequestExceptionHandler' => 'PhabricatorRequestExceptionHandler', 'PhabricatorRateLimitRequestExceptionHandler' => 'PhabricatorRequestExceptionHandler',
'PhabricatorRecaptchaConfigOptions' => 'PhabricatorApplicationConfigOptions', 'PhabricatorRecaptchaConfigOptions' => 'PhabricatorApplicationConfigOptions',
'PhabricatorRedirectController' => 'PhabricatorController', 'PhabricatorRedirectController' => 'PhabricatorController',
@@ -10547,6 +10588,7 @@ phutil_register_library_map(array(
'PhabricatorDestructibleInterface', 'PhabricatorDestructibleInterface',
'PhabricatorSpacesInterface', 'PhabricatorSpacesInterface',
'PhabricatorFulltextInterface', 'PhabricatorFulltextInterface',
'PhabricatorFerretInterface',
), ),
'PonderQuestionAnswerTransaction' => 'PonderQuestionTransactionType', 'PonderQuestionAnswerTransaction' => 'PonderQuestionTransactionType',
'PonderQuestionAnswerWikiTransaction' => 'PonderQuestionTransactionType', 'PonderQuestionAnswerWikiTransaction' => 'PonderQuestionTransactionType',
@@ -10556,6 +10598,7 @@ phutil_register_library_map(array(
'PonderQuestionEditController' => 'PonderController', 'PonderQuestionEditController' => 'PonderController',
'PonderQuestionEditEngine' => 'PhabricatorEditEngine', 'PonderQuestionEditEngine' => 'PhabricatorEditEngine',
'PonderQuestionEditor' => 'PonderEditor', 'PonderQuestionEditor' => 'PonderEditor',
'PonderQuestionFerretEngine' => 'PhabricatorFerretEngine',
'PonderQuestionFulltextEngine' => 'PhabricatorFulltextEngine', 'PonderQuestionFulltextEngine' => 'PhabricatorFulltextEngine',
'PonderQuestionHistoryController' => 'PonderController', 'PonderQuestionHistoryController' => 'PonderController',
'PonderQuestionListController' => 'PonderController', 'PonderQuestionListController' => 'PonderController',
@@ -10587,6 +10630,7 @@ phutil_register_library_map(array(
'ProjectDefaultViewCapability' => 'PhabricatorPolicyCapability', 'ProjectDefaultViewCapability' => 'PhabricatorPolicyCapability',
'ProjectEditConduitAPIMethod' => 'PhabricatorEditEngineAPIMethod', 'ProjectEditConduitAPIMethod' => 'PhabricatorEditEngineAPIMethod',
'ProjectQueryConduitAPIMethod' => 'ProjectConduitAPIMethod', 'ProjectQueryConduitAPIMethod' => 'ProjectConduitAPIMethod',
'ProjectQuickSearchEngineExtension' => 'PhabricatorQuickSearchEngineExtension',
'ProjectRemarkupRule' => 'PhabricatorObjectRemarkupRule', 'ProjectRemarkupRule' => 'PhabricatorObjectRemarkupRule',
'ProjectRemarkupRuleTestCase' => 'PhabricatorTestCase', 'ProjectRemarkupRuleTestCase' => 'PhabricatorTestCase',
'ProjectReplyHandler' => 'PhabricatorApplicationTransactionReplyHandler', 'ProjectReplyHandler' => 'PhabricatorApplicationTransactionReplyHandler',

View File

@@ -9,9 +9,27 @@ final class PhabricatorAuthNeedsMultiFactorController
return false; return false;
} }
public function shouldRequireEnabledUser() {
// Users who haven't been approved yet are allowed to enroll in MFA. We'll
// kick disabled users out later.
return false;
}
public function shouldRequireEmailVerification() {
// Users who haven't verified their email addresses yet can still enroll
// in MFA.
return false;
}
public function handleRequest(AphrontRequest $request) { public function handleRequest(AphrontRequest $request) {
$viewer = $this->getViewer(); $viewer = $this->getViewer();
if ($viewer->getIsDisabled()) {
// We allowed unapproved and disabled users to hit this controller, but
// want to kick out disabled users now.
return new Aphront400Response();
}
$panel = id(new PhabricatorMultiFactorSettingsPanel()) $panel = id(new PhabricatorMultiFactorSettingsPanel())
->setUser($viewer) ->setUser($viewer)
->setViewer($viewer) ->setViewer($viewer)

View File

@@ -9,6 +9,10 @@ final class PhabricatorAuthMainMenuBarExtension
return true; return true;
} }
public function shouldRequireFullSession() {
return false;
}
public function getExtensionOrder() { public function getExtensionOrder() {
return 900; return 900;
} }

View File

@@ -137,10 +137,6 @@ abstract class PhabricatorController extends AphrontController {
} }
if ($this->shouldRequireEnabledUser()) { if ($this->shouldRequireEnabledUser()) {
if ($user->isLoggedIn() && !$user->getIsApproved()) {
$controller = new PhabricatorAuthNeedsApprovalController();
return $this->delegateToController($controller);
}
if ($user->getIsDisabled()) { if ($user->getIsDisabled()) {
$controller = new PhabricatorDisabledUserController(); $controller = new PhabricatorDisabledUserController();
return $this->delegateToController($controller); return $this->delegateToController($controller);
@@ -160,6 +156,15 @@ abstract class PhabricatorController extends AphrontController {
} }
} }
// Require users sign Legalpad documents before we check if they have
// MFA. If we don't do this, they can get stuck in a state where they
// can't add MFA until they sign, and can't sign until they add MFA.
// See T13024 and PHI223.
$result = $this->requireLegalpadSignatures();
if ($result !== null) {
return $result;
}
// Check if the user needs to configure MFA. // Check if the user needs to configure MFA.
$need_mfa = $this->shouldRequireMultiFactorEnrollment(); $need_mfa = $this->shouldRequireMultiFactorEnrollment();
$have_mfa = $user->getIsEnrolledInMultiFactor(); $have_mfa = $user->getIsEnrolledInMultiFactor();
@@ -224,46 +229,15 @@ abstract class PhabricatorController extends AphrontController {
->withPHIDs(array($application->getPHID())) ->withPHIDs(array($application->getPHID()))
->executeOne(); ->executeOne();
} }
}
// If users need approval, require they wait here. We do this near the
if (!$this->shouldAllowLegallyNonCompliantUsers()) { // end so they can take other actions (like verifying email, signing
$legalpad_class = 'PhabricatorLegalpadApplication'; // documents, and enrolling in MFA) while waiting for an admin to take a
$legalpad = id(new PhabricatorApplicationQuery()) // look at things. See T13024 for more discussion.
->setViewer($user) if ($this->shouldRequireEnabledUser()) {
->withClasses(array($legalpad_class)) if ($user->isLoggedIn() && !$user->getIsApproved()) {
->withInstalled(true) $controller = new PhabricatorAuthNeedsApprovalController();
->execute();
$legalpad = head($legalpad);
$doc_query = id(new LegalpadDocumentQuery())
->setViewer($user)
->withSignatureRequired(1)
->needViewerSignatures(true);
if ($user->hasSession() &&
!$user->getSession()->getIsPartial() &&
!$user->getSession()->getSignedLegalpadDocuments() &&
$user->isLoggedIn() &&
$legalpad) {
$sign_docs = $doc_query->execute();
$must_sign_docs = array();
foreach ($sign_docs as $sign_doc) {
if (!$sign_doc->getUserSignature($user->getPHID())) {
$must_sign_docs[] = $sign_doc;
}
}
if ($must_sign_docs) {
$controller = new LegalpadDocumentSignController();
$this->getRequest()->setURIMap(array(
'id' => head($must_sign_docs)->getID(),
));
$this->setCurrentApplication($legalpad);
return $this->delegateToController($controller); return $this->delegateToController($controller);
} else {
$engine = id(new PhabricatorAuthSessionEngine())
->signLegalpadDocuments($user, $sign_docs);
} }
} }
} }
@@ -558,6 +532,81 @@ abstract class PhabricatorController extends AphrontController {
return $this->buildApplicationCrumbs(); return $this->buildApplicationCrumbs();
} }
private function requireLegalpadSignatures() {
if (!$this->shouldRequireLogin()) {
return null;
}
if ($this->shouldAllowLegallyNonCompliantUsers()) {
return null;
}
$viewer = $this->getViewer();
if (!$viewer->hasSession()) {
return null;
}
$session = $viewer->getSession();
if ($session->getIsPartial()) {
// If the user hasn't made it through MFA yet, require they survive
// MFA first.
return null;
}
if ($session->getSignedLegalpadDocuments()) {
return null;
}
if (!$viewer->isLoggedIn()) {
return null;
}
$must_sign_docs = array();
$sign_docs = array();
$legalpad_class = 'PhabricatorLegalpadApplication';
$legalpad_installed = PhabricatorApplication::isClassInstalledForViewer(
$legalpad_class,
$viewer);
if ($legalpad_installed) {
$sign_docs = id(new LegalpadDocumentQuery())
->setViewer($viewer)
->withSignatureRequired(1)
->needViewerSignatures(true)
->setOrder('oldest')
->execute();
foreach ($sign_docs as $sign_doc) {
if (!$sign_doc->getUserSignature($viewer->getPHID())) {
$must_sign_docs[] = $sign_doc;
}
}
}
if (!$must_sign_docs) {
// If nothing needs to be signed (either because there are no documents
// which require a signature, or because the user has already signed
// all of them) mark the session as good and continue.
$engine = id(new PhabricatorAuthSessionEngine())
->signLegalpadDocuments($viewer, $sign_docs);
return null;
}
$request = $this->getRequest();
$request->setURIMap(
array(
'id' => head($must_sign_docs)->getID(),
));
$application = PhabricatorApplication::getByClass($legalpad_class);
$this->setCurrentApplication($application);
$controller = new LegalpadDocumentSignController();
return $this->delegateToController($controller);
}
/* -( Deprecated )--------------------------------------------------------- */ /* -( Deprecated )--------------------------------------------------------- */

View File

@@ -159,10 +159,10 @@ final class PhabricatorAccessControlTestCase extends PhabricatorTestCase {
$u_unverified, $u_unverified,
$u_admin, $u_admin,
$u_public, $u_public,
$u_notapproved,
), ),
array( array(
$u_disabled, $u_disabled,
$u_notapproved,
)); ));
@@ -224,7 +224,7 @@ final class PhabricatorAccessControlTestCase extends PhabricatorTestCase {
)); ));
$this->checkAccess( $this->checkAccess(
pht('Application Controller'), pht('Application Controller, No Login Required'),
id(clone $app_controller)->setConfig('login', false), id(clone $app_controller)->setConfig('login', false),
$request, $request,
array( array(
@@ -232,10 +232,10 @@ final class PhabricatorAccessControlTestCase extends PhabricatorTestCase {
$u_unverified, $u_unverified,
$u_admin, $u_admin,
$u_public, $u_public,
$u_notapproved,
), ),
array( array(
$u_disabled, $u_disabled,
$u_notapproved,
)); ));
} }

View File

@@ -18,6 +18,9 @@ final class PhabricatorDaemonLogEvent extends PhabricatorDaemonDAO {
'logID' => array( 'logID' => array(
'columns' => array('logID', 'epoch'), 'columns' => array('logID', 'epoch'),
), ),
'key_epoch' => array(
'columns' => array('epoch'),
),
), ),
) + parent::getConfiguration(); ) + parent::getConfiguration();
} }

View File

@@ -0,0 +1,18 @@
<?php
final class DifferentialDiffSearchConduitAPIMethod
extends PhabricatorSearchEngineAPIMethod {
public function getAPIMethodName() {
return 'differential.diff.search';
}
public function newSearchEngine() {
return new DifferentialDiffSearchEngine();
}
public function getMethodSummary() {
return pht('Read information about diffs.');
}
}

View File

@@ -36,6 +36,7 @@ final class DifferentialParseCommitMessageConduitAPIMethod
$field_map = $parser->parseFields($corpus); $field_map = $parser->parseFields($corpus);
$errors = $parser->getErrors(); $errors = $parser->getErrors();
$xactions = $parser->getTransactions();
$revision_id_value = idx( $revision_id_value = idx(
$field_map, $field_map,
@@ -49,6 +50,7 @@ final class DifferentialParseCommitMessageConduitAPIMethod
'value' => $revision_id_value, 'value' => $revision_id_value,
'validDomain' => $revision_id_valid_domain, 'validDomain' => $revision_id_valid_domain,
), ),
'transactions' => $xactions,
); );
} }

View File

@@ -11,6 +11,16 @@ final class DifferentialQueryDiffsConduitAPIMethod
return pht('Query differential diffs which match certain criteria.'); return pht('Query differential diffs which match certain criteria.');
} }
public function getMethodStatus() {
return self::METHOD_STATUS_FROZEN;
}
public function getMethodStatusDescription() {
return pht(
'This method is frozen and will eventually be deprecated. New code '.
'should use "differential.diff.search" instead.');
}
protected function defineParamTypes() { protected function defineParamTypes() {
return array( return array(
'ids' => 'optional list<uint>', 'ids' => 'optional list<uint>',

View File

@@ -86,6 +86,7 @@ EOHELP
array( array(
'/\.py$/', '/\.py$/',
'/\.l?hs$/', '/\.l?hs$/',
'/\.ya?ml$/',
)) ))
->setDescription( ->setDescription(
pht( pht(

View File

@@ -32,14 +32,7 @@ final class DifferentialLegacyQuery
} }
private static function getMap() { private static function getMap() {
$all = array( $all = array_keys(DifferentialRevisionStatus::getAll());
DifferentialRevisionStatus::NEEDS_REVIEW,
DifferentialRevisionStatus::NEEDS_REVISION,
DifferentialRevisionStatus::CHANGES_PLANNED,
DifferentialRevisionStatus::ACCEPTED,
DifferentialRevisionStatus::PUBLISHED,
DifferentialRevisionStatus::ABANDONED,
);
$open = array(); $open = array();
$closed = array(); $closed = array();
@@ -61,6 +54,9 @@ final class DifferentialLegacyQuery
), ),
self::STATUS_NEEDS_REVIEW => array( self::STATUS_NEEDS_REVIEW => array(
DifferentialRevisionStatus::NEEDS_REVIEW, DifferentialRevisionStatus::NEEDS_REVIEW,
// For legacy callers, "Draft" is treated as "Needs Review".
DifferentialRevisionStatus::DRAFT,
), ),
self::STATUS_NEEDS_REVISION => array( self::STATUS_NEEDS_REVISION => array(
DifferentialRevisionStatus::NEEDS_REVISION, DifferentialRevisionStatus::NEEDS_REVISION,

View File

@@ -0,0 +1,96 @@
<?php
final class DifferentialDraftField
extends DifferentialCoreCustomField {
public function getFieldKey() {
return 'differential:draft';
}
public function getFieldName() {
return pht('Draft');
}
public function getFieldDescription() {
return pht('Show a warning about draft revisions.');
}
protected function readValueFromRevision(
DifferentialRevision $revision) {
return null;
}
public function shouldAppearInPropertyView() {
return true;
}
public function renderPropertyViewValue(array $handles) {
return null;
}
public function getWarningsForRevisionHeader(array $handles) {
$viewer = $this->getViewer();
$revision = $this->getObject();
if (!$revision->isDraft()) {
return array();
}
// If the author has held this revision as a draft explicitly, don't
// show any misleading messages about it autosubmitting later.
if ($revision->getHoldAsDraft()) {
return array();
}
$warnings = array();
$blocking_map = array(
HarbormasterBuildStatus::STATUS_FAILED,
HarbormasterBuildStatus::STATUS_ABORTED,
HarbormasterBuildStatus::STATUS_ERROR,
HarbormasterBuildStatus::STATUS_PAUSED,
HarbormasterBuildStatus::STATUS_DEADLOCKED,
);
$blocking_map = array_fuse($blocking_map);
$builds = $revision->loadActiveBuilds($viewer);
$waiting = array();
$blocking = array();
foreach ($builds as $build) {
if (isset($blocking_map[$build->getBuildStatus()])) {
$blocking[] = $build;
} else {
$waiting[] = $build;
}
}
$blocking_list = $viewer->renderHandleList(mpull($blocking, 'getPHID'))
->setAsInline(true);
$waiting_list = $viewer->renderHandleList(mpull($waiting, 'getPHID'))
->setAsInline(true);
if ($blocking) {
$warnings[] = pht(
'This draft revision will not be submitted for review because %s '.
'build(s) failed: %s.',
phutil_count($blocking),
$blocking_list);
$warnings[] = pht(
'Fix build failures and update the revision.');
} else if ($waiting) {
$warnings[] = pht(
'This draft revision will be sent for review once %s '.
'build(s) pass: %s.',
phutil_count($waiting),
$waiting_list);
} else {
$warnings[] = pht(
'This is a draft revision that has not yet been submitted for '.
'review.');
}
return $warnings;
}
}

View File

@@ -13,6 +13,20 @@ final class DifferentialRevisionDependedOnByRevisionEdgeType
return true; return true;
} }
public function getConduitKey() {
return 'revision.child';
}
public function getConduitName() {
return pht('Revision Has Child');
}
public function getConduitDescription() {
return pht(
'The source revision makes changes required by the destination '.
'revision.');
}
public function getTransactionAddString( public function getTransactionAddString(
$actor, $actor,
$add_count, $add_count,

View File

@@ -17,6 +17,19 @@ final class DifferentialRevisionDependsOnRevisionEdgeType
return true; return true;
} }
public function getConduitKey() {
return 'revision.parent';
}
public function getConduitName() {
return pht('Revision Has Parent');
}
public function getConduitDescription() {
return pht(
'The source revision depends on changes in the destination revision.');
}
public function getTransactionAddString( public function getTransactionAddString(
$actor, $actor,
$add_count, $add_count,

View File

@@ -235,6 +235,22 @@ final class DifferentialRevisionEditEngine
$fields[] = $action->newEditField($object, $viewer); $fields[] = $action->newEditField($object, $viewer);
} }
$fields[] = id(new PhabricatorBoolEditField())
->setKey('draft')
->setLabel(pht('Hold as Draft'))
->setIsConduitOnly(true)
->setOptions(
pht('Autosubmit Once Builds Finish'),
pht('Hold as Draft'))
->setTransactionType(
DifferentialRevisionHoldDraftTransaction::TRANSACTIONTYPE)
->setDescription(pht('Hold revision as as draft.'))
->setConduitDescription(
pht(
'Change autosubmission from draft state after builds finish.'))
->setConduitTypeDescription(pht('New "Hold as Draft" setting.'))
->setValue($object->getHoldAsDraft());
return $fields; return $fields;
} }

View File

@@ -9,6 +9,8 @@ final class DifferentialTransactionEditor
private $didExpandInlineState = false; private $didExpandInlineState = false;
private $hasReviewTransaction = false; private $hasReviewTransaction = false;
private $affectedPaths; private $affectedPaths;
private $firstBroadcast = false;
private $wasDraft = false;
public function getEditorApplicationClass() { public function getEditorApplicationClass() {
return 'PhabricatorDifferentialApplication'; return 'PhabricatorDifferentialApplication';
@@ -27,7 +29,7 @@ final class DifferentialTransactionEditor
} }
public function isFirstBroadcast() { public function isFirstBroadcast() {
return $this->getIsNewObject(); return $this->firstBroadcast;
} }
public function getDiffUpdateTransaction(array $xactions) { public function getDiffUpdateTransaction(array $xactions) {
@@ -137,8 +139,7 @@ final class DifferentialTransactionEditor
$object->setRepositoryPHID($diff->getRepositoryPHID()); $object->setRepositoryPHID($diff->getRepositoryPHID());
} }
$object->attachActiveDiff($diff); $object->attachActiveDiff($diff);
$object->setActiveDiffPHID($diff->getPHID());
// TODO: Update the `diffPHID` once we add that.
return; return;
} }
@@ -166,6 +167,8 @@ final class DifferentialTransactionEditor
} }
} }
$this->wasDraft = $object->isDraft();
return parent::expandTransactions($object, $xactions); return parent::expandTransactions($object, $xactions);
} }
@@ -630,6 +633,10 @@ final class DifferentialTransactionEditor
$phids = array(); $phids = array();
$phids[] = $object->getAuthorPHID(); $phids[] = $object->getAuthorPHID();
foreach ($object->getReviewers() as $reviewer) { foreach ($object->getReviewers() as $reviewer) {
if ($reviewer->isResigned()) {
continue;
}
$phids[] = $reviewer->getReviewerPHID(); $phids[] = $reviewer->getReviewerPHID();
} }
return $phids; return $phids;
@@ -1003,28 +1010,9 @@ final class DifferentialTransactionEditor
protected function shouldApplyHeraldRules( protected function shouldApplyHeraldRules(
PhabricatorLiskDAO $object, PhabricatorLiskDAO $object,
array $xactions) { array $xactions) {
if ($this->getIsNewObject()) {
return true; return true;
} }
foreach ($xactions as $xaction) {
switch ($xaction->getTransactionType()) {
case DifferentialTransaction::TYPE_UPDATE:
if (!$this->getIsCloseByCommit()) {
return true;
}
break;
case DifferentialRevisionCommandeerTransaction::TRANSACTIONTYPE:
// When users commandeer revisions, we may need to trigger
// signatures or author-based rules.
return true;
}
}
return parent::shouldApplyHeraldRules($object, $xactions);
}
protected function didApplyHeraldRules( protected function didApplyHeraldRules(
PhabricatorLiskDAO $object, PhabricatorLiskDAO $object,
HeraldAdapter $adapter, HeraldAdapter $adapter,
@@ -1211,6 +1199,49 @@ final class DifferentialTransactionEditor
$revision, $revision,
$revision->getActiveDiff()); $revision->getActiveDiff());
// If the object is still a draft, prevent "Send me an email" and other
// similar rules from acting yet.
if (!$object->shouldBroadcast()) {
$adapter->setForbiddenAction(
HeraldMailableState::STATECONST,
DifferentialHeraldStateReasons::REASON_DRAFT);
}
// If this edit didn't actually change the diff (for example, a user
// edited the title or changed subscribers), prevent "Run build plan"
// and other similar rules from acting yet, since the build results will
// not (or, at least, should not) change unless the actual source changes.
// We also don't run Differential builds if the update was caused by
// discovering a commit, as the expectation is that Diffusion builds take
// over once things land.
$has_update = false;
$has_commit = false;
$type_update = DifferentialTransaction::TYPE_UPDATE;
foreach ($xactions as $xaction) {
if ($xaction->getTransactionType() != $type_update) {
continue;
}
if ($xaction->getMetadataValue('isCommitUpdate')) {
$has_commit = true;
} else {
$has_update = true;
}
break;
}
if ($has_commit) {
$adapter->setForbiddenAction(
HeraldBuildableState::STATECONST,
DifferentialHeraldStateReasons::REASON_LANDED);
} else if (!$has_update) {
$adapter->setForbiddenAction(
HeraldBuildableState::STATECONST,
DifferentialHeraldStateReasons::REASON_UNCHANGED);
}
return $adapter; return $adapter;
} }
@@ -1425,11 +1456,13 @@ final class DifferentialTransactionEditor
protected function getCustomWorkerState() { protected function getCustomWorkerState() {
return array( return array(
'changedPriorToCommitURI' => $this->changedPriorToCommitURI, 'changedPriorToCommitURI' => $this->changedPriorToCommitURI,
'firstBroadcast' => $this->firstBroadcast,
); );
} }
protected function loadCustomWorkerState(array $state) { protected function loadCustomWorkerState(array $state) {
$this->changedPriorToCommitURI = idx($state, 'changedPriorToCommitURI'); $this->changedPriorToCommitURI = idx($state, 'changedPriorToCommitURI');
$this->firstBroadcast = idx($state, 'firstBroadcast');
return $this; return $this;
} }
@@ -1532,19 +1565,31 @@ final class DifferentialTransactionEditor
protected function didApplyTransactions($object, array $xactions) { protected function didApplyTransactions($object, array $xactions) {
// If a draft revision has no outstanding builds and we're automatically // If a draft revision has no outstanding builds and we're automatically
// making drafts public after builds finish, make the revision public. // making drafts public after builds finish, make the revision public.
$auto_undraft = true; $auto_undraft = !$object->getHoldAsDraft();
if ($object->isDraft() && $auto_undraft) { if ($object->isDraft() && $auto_undraft) {
$active_builds = $this->hasActiveBuilds($object); $active_builds = $this->hasActiveBuilds($object);
if (!$active_builds) { if (!$active_builds) {
// When Harbormaster moves a revision out of the draft state, we
// attribute the action to the revision author since this is more
// natural and more useful.
$author_phid = $object->getAuthorPHID();
// Additionally, we change the acting PHID for the transaction set
// to the author if it isn't already a user so that mail comes from
// the natural author.
$acting_phid = $this->getActingAsPHID();
$user_type = PhabricatorPeopleUserPHIDType::TYPECONST;
if (phid_get_type($acting_phid) != $user_type) {
$this->setActingAsPHID($author_phid);
}
$xaction = $object->getApplicationTransactionTemplate() $xaction = $object->getApplicationTransactionTemplate()
->setAuthorPHID($author_phid)
->setTransactionType( ->setTransactionType(
DifferentialRevisionRequestReviewTransaction::TRANSACTIONTYPE) DifferentialRevisionRequestReviewTransaction::TRANSACTIONTYPE)
->setOldValue(false)
->setNewValue(true); ->setNewValue(true);
$xaction = $this->populateTransaction($object, $xaction);
// If we're creating this revision and immediately moving it out of // If we're creating this revision and immediately moving it out of
// the draft state, mark this as a create transaction so it gets // the draft state, mark this as a create transaction so it gets
// hidden in the timeline and mail, since it isn't interesting: it // hidden in the timeline and mail, since it isn't interesting: it
@@ -1553,15 +1598,36 @@ final class DifferentialTransactionEditor
$xaction->setIsCreateTransaction(true); $xaction->setIsCreateTransaction(true);
} }
$object->openTransaction(); // Queue this transaction and apply it separately after the current
// batch of transactions finishes so that Herald can fire on the new
// revision state. See T13027 for discussion.
$this->queueTransaction($xaction);
}
}
// If the revision is new or was a draft, and is no longer a draft, we
// might be sending the first email about it.
// This might mean it was created directly into a non-draft state, or
// it just automatically undrafted after builds finished, or a user
// explicitly promoted it out of the draft state with an action like
// "Request Review".
// If we haven't sent any email about it yet, mark this email as the first
// email so the mail gets enriched with "SUMMARY" and "TEST PLAN".
$is_new = $this->getIsNewObject();
$was_draft = $this->wasDraft;
if (!$object->isDraft() && ($was_draft || $is_new)) {
if (!$object->getHasBroadcast()) {
// Mark this as the first broadcast we're sending about the revision
// so mail can generate specially.
$this->firstBroadcast = true;
$object $object
->setStatus(DifferentialRevisionStatus::NEEDS_REVIEW) ->setHasBroadcast(true)
->save(); ->save();
$xaction->save();
$object->saveTransaction();
$xactions[] = $xaction;
} }
} }
@@ -1570,58 +1636,12 @@ final class DifferentialTransactionEditor
private function hasActiveBuilds($object) { private function hasActiveBuilds($object) {
$viewer = $this->requireActor(); $viewer = $this->requireActor();
$diff = $object->getActiveDiff();
$buildables = id(new HarbormasterBuildableQuery()) $builds = $object->loadActiveBuilds($viewer);
->setViewer($viewer)
->withContainerPHIDs(array($object->getPHID()))
->withBuildablePHIDs(array($diff->getPHID()))
->withManualBuildables(false)
->execute();
if (!$buildables) {
return false;
}
$builds = id(new HarbormasterBuildQuery())
->setViewer($viewer)
->withBuildablePHIDs(mpull($buildables, 'getPHID'))
->withBuildStatuses(
array(
HarbormasterBuildStatus::STATUS_INACTIVE,
HarbormasterBuildStatus::STATUS_PENDING,
HarbormasterBuildStatus::STATUS_BUILDING,
HarbormasterBuildStatus::STATUS_FAILED,
HarbormasterBuildStatus::STATUS_ABORTED,
HarbormasterBuildStatus::STATUS_ERROR,
HarbormasterBuildStatus::STATUS_PAUSED,
HarbormasterBuildStatus::STATUS_DEADLOCKED,
))
->needBuildTargets(true)
->execute();
if (!$builds) { if (!$builds) {
return false; return false;
} }
$active = array();
foreach ($builds as $key => $build) {
foreach ($build->getBuildTargets() as $target) {
if ($target->isAutotarget()) {
// Ignore autotargets when looking for active of failed builds. If
// local tests fail and you continue anyway, you don't need to
// double-confirm them.
continue;
}
// This build has at least one real target that's doing something.
$active[$key] = $build;
break;
}
}
if (!$active) {
return false;
}
return true; return true;
} }

View File

@@ -268,6 +268,16 @@ final class DifferentialDiffExtractionEngine extends Phobject {
$xactions = array(); $xactions = array();
// If the revision isn't closed or "Accepted", write a warning into the
// transaction log. This makes it more clear when users bend the rules.
if (!$revision->isClosed() && !$revision->isAccepted()) {
$wrong_type = DifferentialRevisionWrongStateTransaction::TRANSACTIONTYPE;
$xactions[] = id(new DifferentialTransaction())
->setTransactionType($wrong_type)
->setNewValue($revision->getModernRevisionStatus());
}
$xactions[] = id(new DifferentialTransaction()) $xactions[] = id(new DifferentialTransaction())
->setTransactionType(DifferentialTransaction::TYPE_UPDATE) ->setTransactionType(DifferentialTransaction::TYPE_UPDATE)
->setIgnoreOnNoEffect(true) ->setIgnoreOnNoEffect(true)

View File

@@ -0,0 +1,26 @@
<?php
final class DifferentialHeraldStateReasons
extends HeraldStateReasons {
const REASON_DRAFT = 'differential.draft';
const REASON_UNCHANGED = 'differential.unchanged';
const REASON_LANDED = 'differential.landed';
public function explainReason($reason) {
$reasons = array(
self::REASON_DRAFT => pht(
'This revision is still an unsubmitted draft, so mail will not '.
'be sent yet.'),
self::REASON_UNCHANGED => pht(
'The update which triggered Herald did not update the diff for '.
'this revision, so builds will not run.'),
self::REASON_LANDED => pht(
'The update which triggered Herald was an automatic update in '.
'response to discovering a commit, so builds will not run.'),
);
return idx($reasons, $reason);
}
}

View File

@@ -28,6 +28,7 @@ final class DifferentialCommitMessageParser extends Phobject {
private $errors; private $errors;
private $commitMessageFields; private $commitMessageFields;
private $raiseMissingFieldErrors = true; private $raiseMissingFieldErrors = true;
private $xactions;
public static function newStandardParser(PhabricatorUser $viewer) { public static function newStandardParser(PhabricatorUser $viewer) {
$key_title = DifferentialTitleCommitMessageField::FIELDKEY; $key_title = DifferentialTitleCommitMessageField::FIELDKEY;
@@ -134,6 +135,7 @@ final class DifferentialCommitMessageParser extends Phobject {
*/ */
public function parseCorpus($corpus) { public function parseCorpus($corpus) {
$this->errors = array(); $this->errors = array();
$this->xactions = array();
$label_map = $this->getLabelMap(); $label_map = $this->getLabelMap();
$key_title = $this->titleKey; $key_title = $this->titleKey;
@@ -284,12 +286,25 @@ final class DifferentialCommitMessageParser extends Phobject {
try { try {
$result = $field->parseFieldValue($text_value); $result = $field->parseFieldValue($text_value);
$result_map[$field_key] = $result; $result_map[$field_key] = $result;
try {
$xactions = $field->getFieldTransactions($result);
foreach ($xactions as $xaction) {
$this->xactions[] = $xaction;
}
} catch (Exception $ex) {
$this->errors[] = pht(
'Error extracting field transactions from "%s": %s',
$field->getFieldName(),
$ex->getMessage());
}
} catch (DifferentialFieldParseException $ex) { } catch (DifferentialFieldParseException $ex) {
$this->errors[] = pht( $this->errors[] = pht(
'Error parsing field "%s": %s', 'Error parsing field "%s": %s',
$field->getFieldName(), $field->getFieldName(),
$ex->getMessage()); $ex->getMessage());
} }
} }
if ($this->getRaiseMissingFieldErrors()) { if ($this->getRaiseMissingFieldErrors()) {
@@ -317,6 +332,14 @@ final class DifferentialCommitMessageParser extends Phobject {
} }
/**
* @task parse
*/
public function getTransactions() {
return $this->xactions;
}
/* -( Support Methods )---------------------------------------------------- */ /* -( Support Methods )---------------------------------------------------- */

View File

@@ -6,6 +6,7 @@ final class DifferentialDiffQuery
private $ids; private $ids;
private $phids; private $phids;
private $revisionIDs; private $revisionIDs;
private $revisionPHIDs;
private $commitPHIDs; private $commitPHIDs;
private $hasRevision; private $hasRevision;
@@ -27,6 +28,11 @@ final class DifferentialDiffQuery
return $this; return $this;
} }
public function withRevisionPHIDs(array $revision_phids) {
$this->revisionPHIDs = $revision_phids;
return $this;
}
public function withCommitPHIDs(array $phids) { public function withCommitPHIDs(array $phids) {
$this->commitPHIDs = $phids; $this->commitPHIDs = $phids;
return $this; return $this;
@@ -160,6 +166,25 @@ final class DifferentialDiffQuery
} }
} }
if ($this->revisionPHIDs !== null) {
$viewer = $this->getViewer();
$revisions = id(new DifferentialRevisionQuery())
->setViewer($viewer)
->setParentQuery($this)
->withPHIDs($this->revisionPHIDs)
->execute();
$revision_ids = mpull($revisions, 'getID');
if (!$revision_ids) {
throw new PhabricatorEmptyQueryException();
}
$where[] = qsprintf(
$conn,
'revisionID IN (%Ls)',
$revision_ids);
}
return $where; return $where;
} }

View File

@@ -0,0 +1,79 @@
<?php
final class DifferentialDiffSearchEngine
extends PhabricatorApplicationSearchEngine {
public function getResultTypeDescription() {
return pht('Differential Diffs');
}
public function getApplicationClassName() {
return 'PhabricatorDifferentialApplication';
}
public function newQuery() {
return new DifferentialDiffQuery();
}
protected function buildQueryFromParameters(array $map) {
$query = $this->newQuery();
if ($map['revisionPHIDs']) {
$query->withRevisionPHIDs($map['revisionPHIDs']);
}
return $query;
}
protected function buildCustomSearchFields() {
return array(
id(new PhabricatorPHIDsSearchField())
->setLabel(pht('Revisions'))
->setKey('revisionPHIDs')
->setAliases(array('revision', 'revisions', 'revisionPHID'))
->setDescription(
pht('Find diffs attached to a particular revision.')),
);
}
protected function getURI($path) {
return '/differential/diff/'.$path;
}
protected function getBuiltinQueryNames() {
$names = array();
$names['all'] = pht('All Diffs');
return $names;
}
public function buildSavedQueryFromBuiltin($query_key) {
$query = $this->newSavedQuery();
$query->setQueryKey($query_key);
$viewer = $this->requireViewer();
switch ($query_key) {
case 'all':
return $query;
}
return parent::buildSavedQueryFromBuiltin($query_key);
}
protected function renderResultList(
array $revisions,
PhabricatorSavedQuery $query,
array $handles) {
assert_instances_of($revisions, 'DifferentialDiff');
$viewer = $this->requireViewer();
// NOTE: This is only exposed to Conduit, so we don't currently render
// results.
return id(new PhabricatorApplicationSearchResultView());
}
}

View File

@@ -38,8 +38,6 @@ final class DifferentialRevisionQuery
private $needDrafts; private $needDrafts;
private $needFlags; private $needFlags;
private $buildingGlobalOrder;
/* -( Query Configuration )------------------------------------------------ */ /* -( Query Configuration )------------------------------------------------ */
@@ -484,12 +482,11 @@ final class DifferentialRevisionQuery
} }
if (count($selects) > 1) { if (count($selects) > 1) {
$this->buildingGlobalOrder = true;
$query = qsprintf( $query = qsprintf(
$conn_r, $conn_r,
'%Q %Q %Q', '%Q %Q %Q',
implode(' UNION DISTINCT ', $selects), implode(' UNION DISTINCT ', $selects),
$this->buildOrderClause($conn_r), $this->buildOrderClause($conn_r, true),
$this->buildLimitClause($conn_r)); $this->buildLimitClause($conn_r));
} else { } else {
$query = head($selects); $query = head($selects);
@@ -513,7 +510,6 @@ final class DifferentialRevisionQuery
$group_by = $this->buildGroupClause($conn_r); $group_by = $this->buildGroupClause($conn_r);
$having = $this->buildHavingClause($conn_r); $having = $this->buildHavingClause($conn_r);
$this->buildingGlobalOrder = false;
$order_by = $this->buildOrderClause($conn_r); $order_by = $this->buildOrderClause($conn_r);
$limit = $this->buildLimitClause($conn_r); $limit = $this->buildLimitClause($conn_r);
@@ -758,17 +754,9 @@ final class DifferentialRevisionQuery
} }
public function getOrderableColumns() { public function getOrderableColumns() {
$primary = ($this->buildingGlobalOrder ? null : 'r');
return array( return array(
'id' => array(
'table' => $primary,
'column' => 'id',
'type' => 'int',
'unique' => true,
),
'updated' => array( 'updated' => array(
'table' => $primary, 'table' => $this->getPrimaryTableAlias(),
'column' => 'dateModified', 'column' => 'dateModified',
'type' => 'int', 'type' => 'int',
), ),

View File

@@ -123,6 +123,14 @@ final class DifferentialRevisionRequiredActionResultBucket
$reviewing = array( $reviewing = array(
DifferentialReviewerStatus::STATUS_ADDED, DifferentialReviewerStatus::STATUS_ADDED,
DifferentialReviewerStatus::STATUS_COMMENTED, DifferentialReviewerStatus::STATUS_COMMENTED,
// If an author has used "Request Review" to put an accepted revision
// back into the "Needs Review" state, include "Accepted" reviewers
// whose reviews have been voided in the "Should Review" bucket.
// If we don't do this, they end up in "Waiting on Other Reviewers",
// even if there are no other reviewers.
DifferentialReviewerStatus::STATUS_ACCEPTED,
); );
$reviewing = array_fuse($reviewing); $reviewing = array_fuse($reviewing);
@@ -130,7 +138,7 @@ final class DifferentialRevisionRequiredActionResultBucket
$results = array(); $results = array();
foreach ($objects as $key => $object) { foreach ($objects as $key => $object) {
if (!$this->hasReviewersWithStatus($object, $phids, $reviewing)) { if (!$this->hasReviewersWithStatus($object, $phids, $reviewing, true)) {
continue; continue;
} }

View File

@@ -53,7 +53,8 @@ abstract class DifferentialRevisionResultBucket
protected function hasReviewersWithStatus( protected function hasReviewersWithStatus(
DifferentialRevision $revision, DifferentialRevision $revision,
array $phids, array $phids,
array $statuses) { array $statuses,
$include_voided = null) {
foreach ($revision->getReviewers() as $reviewer) { foreach ($revision->getReviewers() as $reviewer) {
$reviewer_phid = $reviewer->getReviewerPHID(); $reviewer_phid = $reviewer->getReviewerPHID();
@@ -66,6 +67,15 @@ abstract class DifferentialRevisionResultBucket
continue; continue;
} }
if ($include_voided !== null) {
if ($status == DifferentialReviewerStatus::STATUS_ACCEPTED) {
$is_voided = (bool)$reviewer->getVoidedPHID();
if ($is_voided !== $include_voided) {
continue;
}
}
}
return true; return true;
} }

View File

@@ -41,6 +41,20 @@ final class DifferentialChangesetOneUpRenderer
$column_width = 4; $column_width = 4;
$aural_minus = javelin_tag(
'span',
array(
'aural' => true,
),
'- ');
$aural_plus = javelin_tag(
'span',
array(
'aural' => true,
),
'+ ');
$out = array(); $out = array();
foreach ($primitives as $k => $p) { foreach ($primitives as $k => $p) {
$type = $p['type']; $type = $p['type'];
@@ -55,8 +69,10 @@ final class DifferentialChangesetOneUpRenderer
if ($is_old) { if ($is_old) {
if ($p['htype']) { if ($p['htype']) {
$class = 'left old'; $class = 'left old';
$aural = $aural_minus;
} else { } else {
$class = 'left'; $class = 'left';
$aural = null;
} }
if ($type == 'old-file') { if ($type == 'old-file') {
@@ -79,14 +95,20 @@ final class DifferentialChangesetOneUpRenderer
), ),
$line); $line);
$render = $p['render'];
if ($aural !== null) {
$render = array($aural, $render);
}
$cells[] = phutil_tag('th', array('class' => $class)); $cells[] = phutil_tag('th', array('class' => $class));
$cells[] = $no_copy; $cells[] = $no_copy;
$cells[] = phutil_tag('td', array('class' => $class), $p['render']); $cells[] = phutil_tag('td', array('class' => $class), $render);
$cells[] = $no_coverage; $cells[] = $no_coverage;
} else { } else {
if ($p['htype']) { if ($p['htype']) {
$class = 'right new'; $class = 'right new';
$cells[] = phutil_tag('th', array('class' => $class)); $cells[] = phutil_tag('th', array('class' => $class));
$aural = $aural_plus;
} else { } else {
$class = 'right'; $class = 'right';
if ($left_prefix) { if ($left_prefix) {
@@ -98,6 +120,7 @@ final class DifferentialChangesetOneUpRenderer
$oline = $p['oline']; $oline = $p['oline'];
$cells[] = phutil_tag('th', array('id' => $left_id), $oline); $cells[] = phutil_tag('th', array('id' => $left_id), $oline);
$aural = null;
} }
if ($type == 'new-file') { if ($type == 'new-file') {
@@ -120,8 +143,13 @@ final class DifferentialChangesetOneUpRenderer
), ),
$line); $line);
$render = $p['render'];
if ($aural !== null) {
$render = array($aural, $render);
}
$cells[] = $no_copy; $cells[] = $no_copy;
$cells[] = phutil_tag('td', array('class' => $class), $p['render']); $cells[] = phutil_tag('td', array('class' => $class), $render);
$cells[] = $no_coverage; $cells[] = $no_coverage;
} }

View File

@@ -9,7 +9,8 @@ final class DifferentialDiff
HarbormasterCircleCIBuildableInterface, HarbormasterCircleCIBuildableInterface,
HarbormasterBuildkiteBuildableInterface, HarbormasterBuildkiteBuildableInterface,
PhabricatorApplicationTransactionInterface, PhabricatorApplicationTransactionInterface,
PhabricatorDestructibleInterface { PhabricatorDestructibleInterface,
PhabricatorConduitResultInterface {
protected $revisionID; protected $revisionID;
protected $authorPHID; protected $authorPHID;
@@ -740,4 +741,82 @@ final class DifferentialDiff
$this->saveTransaction(); $this->saveTransaction();
} }
/* -( PhabricatorConduitResultInterface )---------------------------------- */
public function getFieldSpecificationsForConduit() {
return array(
id(new PhabricatorConduitSearchFieldSpecification())
->setKey('revisionPHID')
->setType('phid')
->setDescription(pht('Associated revision PHID.')),
id(new PhabricatorConduitSearchFieldSpecification())
->setKey('authorPHID')
->setType('phid')
->setDescription(pht('Revision author PHID.')),
id(new PhabricatorConduitSearchFieldSpecification())
->setKey('repositoryPHID')
->setType('phid')
->setDescription(pht('Associated repository PHID.')),
id(new PhabricatorConduitSearchFieldSpecification())
->setKey('refs')
->setType('map<string, wild>')
->setDescription(pht('List of related VCS references.')),
);
}
public function getFieldValuesForConduit() {
$refs = array();
$branch = $this->getBranch();
if (strlen($branch)) {
$refs[] = array(
'type' => 'branch',
'name' => $branch,
);
}
$onto = $this->loadTargetBranch();
if (strlen($onto)) {
$refs[] = array(
'type' => 'onto',
'name' => $onto,
);
}
$base = $this->getSourceControlBaseRevision();
if (strlen($base)) {
$refs[] = array(
'type' => 'base',
'identifier' => $base,
);
}
$bookmark = $this->getBookmark();
if (strlen($bookmark)) {
$refs[] = array(
'type' => 'bookmark',
'name' => $bookmark,
);
}
$revision_phid = null;
if ($this->getRevisionID()) {
$revision_phid = $this->getRevision()->getPHID();
}
return array(
'revisionPHID' => $revision_phid,
'authorPHID' => $this->getAuthorPHID(),
'repositoryPHID' => $this->getRepositoryPHID(),
'refs' => $refs,
);
}
public function getConduitSearchAttachments() {
return array();
}
} }

View File

@@ -35,6 +35,8 @@ final class DifferentialRevision extends DifferentialDAO
protected $mailKey; protected $mailKey;
protected $branchName; protected $branchName;
protected $repositoryPHID; protected $repositoryPHID;
protected $activeDiffPHID;
protected $viewPolicy = PhabricatorPolicies::POLICY_USER; protected $viewPolicy = PhabricatorPolicies::POLICY_USER;
protected $editPolicy = PhabricatorPolicies::POLICY_USER; protected $editPolicy = PhabricatorPolicies::POLICY_USER;
protected $properties = array(); protected $properties = array();
@@ -57,6 +59,8 @@ final class DifferentialRevision extends DifferentialDAO
const RELATION_SUBSCRIBED = 'subd'; const RELATION_SUBSCRIBED = 'subd';
const PROPERTY_CLOSED_FROM_ACCEPTED = 'wasAcceptedBeforeClose'; const PROPERTY_CLOSED_FROM_ACCEPTED = 'wasAcceptedBeforeClose';
const PROPERTY_DRAFT_HOLD = 'draft.hold';
const PROPERTY_HAS_BROADCAST = 'draft.broadcast';
public static function initializeNewRevision(PhabricatorUser $actor) { public static function initializeNewRevision(PhabricatorUser $actor) {
$app = id(new PhabricatorApplicationQuery()) $app = id(new PhabricatorApplicationQuery())
@@ -67,13 +71,19 @@ final class DifferentialRevision extends DifferentialDAO
$view_policy = $app->getPolicy( $view_policy = $app->getPolicy(
DifferentialDefaultViewCapability::CAPABILITY); DifferentialDefaultViewCapability::CAPABILITY);
if (PhabricatorEnv::getEnvConfig('phabricator.show-prototypes')) {
$initial_state = DifferentialRevisionStatus::DRAFT;
} else {
$initial_state = DifferentialRevisionStatus::NEEDS_REVIEW;
}
return id(new DifferentialRevision()) return id(new DifferentialRevision())
->setViewPolicy($view_policy) ->setViewPolicy($view_policy)
->setAuthorPHID($actor->getPHID()) ->setAuthorPHID($actor->getPHID())
->attachRepository(null) ->attachRepository(null)
->attachActiveDiff(null) ->attachActiveDiff(null)
->attachReviewers(array()) ->attachReviewers(array())
->setModernRevisionStatus(DifferentialRevisionStatus::NEEDS_REVIEW); ->setModernRevisionStatus($initial_state);
} }
protected function getConfiguration() { protected function getConfiguration() {
@@ -702,6 +712,54 @@ final class DifferentialRevision extends DifferentialDAO
return false; return false;
} }
public function getHoldAsDraft() {
return $this->getProperty(self::PROPERTY_DRAFT_HOLD, false);
}
public function setHoldAsDraft($hold) {
return $this->setProperty(self::PROPERTY_DRAFT_HOLD, $hold);
}
public function getHasBroadcast() {
return $this->getProperty(self::PROPERTY_HAS_BROADCAST, false);
}
public function setHasBroadcast($has_broadcast) {
return $this->setProperty(self::PROPERTY_HAS_BROADCAST, $has_broadcast);
}
public function loadActiveBuilds(PhabricatorUser $viewer) {
$diff = $this->getActiveDiff();
$buildables = id(new HarbormasterBuildableQuery())
->setViewer($viewer)
->withContainerPHIDs(array($this->getPHID()))
->withBuildablePHIDs(array($diff->getPHID()))
->withManualBuildables(false)
->execute();
if (!$buildables) {
return array();
}
return id(new HarbormasterBuildQuery())
->setViewer($viewer)
->withBuildablePHIDs(mpull($buildables, 'getPHID'))
->withAutobuilds(false)
->withBuildStatuses(
array(
HarbormasterBuildStatus::STATUS_INACTIVE,
HarbormasterBuildStatus::STATUS_PENDING,
HarbormasterBuildStatus::STATUS_BUILDING,
HarbormasterBuildStatus::STATUS_FAILED,
HarbormasterBuildStatus::STATUS_ABORTED,
HarbormasterBuildStatus::STATUS_ERROR,
HarbormasterBuildStatus::STATUS_PAUSED,
HarbormasterBuildStatus::STATUS_DEADLOCKED,
))
->execute();
}
/* -( HarbormasterBuildableInterface )------------------------------------- */ /* -( HarbormasterBuildableInterface )------------------------------------- */
@@ -938,6 +996,18 @@ final class DifferentialRevision extends DifferentialDAO
->setKey('status') ->setKey('status')
->setType('map<string, wild>') ->setType('map<string, wild>')
->setDescription(pht('Information about revision status.')), ->setDescription(pht('Information about revision status.')),
id(new PhabricatorConduitSearchFieldSpecification())
->setKey('repositoryPHID')
->setType('phid?')
->setDescription(pht('Revision repository PHID.')),
id(new PhabricatorConduitSearchFieldSpecification())
->setKey('diffPHID')
->setType('phid')
->setDescription(pht('Active diff PHID.')),
id(new PhabricatorConduitSearchFieldSpecification())
->setKey('summary')
->setType('string')
->setDescription(pht('Revision summary.')),
); );
} }
@@ -954,6 +1024,9 @@ final class DifferentialRevision extends DifferentialDAO
'title' => $this->getTitle(), 'title' => $this->getTitle(),
'authorPHID' => $this->getAuthorPHID(), 'authorPHID' => $this->getAuthorPHID(),
'status' => $status_info, 'status' => $status_info,
'repositoryPHID' => $this->getRepositoryPHID(),
'diffPHID' => $this->getActiveDiffPHID(),
'summary' => $this->getSummary(),
); );
} }

View File

@@ -301,6 +301,9 @@ final class DifferentialChangesetListView extends AphrontView {
'Hide or show all inline comments.' => 'Hide or show all inline comments.' =>
pht('Hide or show all inline comments.'), pht('Hide or show all inline comments.'),
'Finish editing inline comments before changing display modes.' =>
pht('Finish editing inline comments before changing display modes.'),
), ),
)); ));

View File

@@ -47,6 +47,7 @@ final class DifferentialReviewersView extends AphrontView {
$action_phid = $reviewer->getLastActionDiffPHID(); $action_phid = $reviewer->getLastActionDiffPHID();
$is_current_action = $this->isCurrent($action_phid); $is_current_action = $this->isCurrent($action_phid);
$is_voided = (bool)$reviewer->getVoidedPHID();
$comment_phid = $reviewer->getLastCommentDiffPHID(); $comment_phid = $reviewer->getLastCommentDiffPHID();
$is_current_comment = $this->isCurrent($comment_phid); $is_current_comment = $this->isCurrent($comment_phid);
@@ -86,7 +87,7 @@ final class DifferentialReviewersView extends AphrontView {
break; break;
case DifferentialReviewerStatus::STATUS_ACCEPTED: case DifferentialReviewerStatus::STATUS_ACCEPTED:
if ($is_current_action) { if ($is_current_action && !$is_voided) {
$icon = PHUIStatusItemView::ICON_ACCEPT; $icon = PHUIStatusItemView::ICON_ACCEPT;
$color = 'green'; $color = 'green';
if ($authority_name !== null) { if ($authority_name !== null) {
@@ -97,7 +98,12 @@ final class DifferentialReviewersView extends AphrontView {
} else { } else {
$icon = 'fa-check-circle-o'; $icon = 'fa-check-circle-o';
$color = 'bluegrey'; $color = 'bluegrey';
if ($authority_name !== null) {
if (!$is_current_action && $is_voided) {
// The reviewer accepted the revision, but later the author
// used "Request Review" to request an updated review.
$label = pht('Accepted Earlier');
} else if ($authority_name !== null) {
$label = pht('Accepted Prior Diff (by %s)', $authority_name); $label = pht('Accepted Prior Diff (by %s)', $authority_name);
} else { } else {
$label = pht('Accepted Prior Diff'); $label = pht('Accepted Prior Diff');

View File

@@ -162,6 +162,11 @@ final class DifferentialRevisionAcceptTransaction
'closed. Only open revisions can be accepted.')); 'closed. Only open revisions can be accepted.'));
} }
if ($object->isDraft()) {
throw new Exception(
pht('You can not accept a draft revision.'));
}
$config_key = 'differential.allow-self-accept'; $config_key = 'differential.allow-self-accept';
if (!PhabricatorEnv::getEnvConfig($config_key)) { if (!PhabricatorEnv::getEnvConfig($config_key)) {
if ($this->isViewerRevisionAuthor($object, $viewer)) { if ($this->isViewerRevisionAuthor($object, $viewer)) {

View File

@@ -0,0 +1,56 @@
<?php
final class DifferentialRevisionHoldDraftTransaction
extends DifferentialRevisionTransactionType {
const TRANSACTIONTYPE = 'draft';
const EDITKEY = 'draft';
public function generateOldValue($object) {
return (bool)$object->getHoldAsDraft();
}
public function generateNewValue($object, $value) {
return (bool)$value;
}
public function applyInternalEffects($object, $value) {
$object->setHoldAsDraft($value);
// If draft isn't the default state but we're creating a new revision
// and holding it as a draft, put it in draft mode. See PHI206.
// TODO: This can probably be removed once Draft is the universal default.
if ($this->isNewObject()) {
if ($object->isNeedsReview()) {
$object->setModernRevisionStatus(DifferentialRevisionStatus::DRAFT);
}
}
}
public function getTitle() {
if ($this->getNewValue()) {
return pht(
'%s held this revision as a draft.',
$this->renderAuthor());
} else {
return pht(
'%s set this revision to automatically submit once builds complete.',
$this->renderAuthor());
}
}
public function getTitleForFeed() {
if ($this->getNewValue()) {
return pht(
'%s held %s as a draft.',
$this->renderAuthor(),
$this->renderObject());
} else {
return pht(
'%s set %s to automatically submit once builds complete.',
$this->renderAuthor(),
$this->renderObject());
}
}
}

View File

@@ -57,6 +57,9 @@ final class DifferentialRevisionRequestReviewTransaction
'revisions.')); 'revisions.'));
} }
// When revisions automatically promote out of "Draft" after builds finish,
// the viewer may be acting as the Harbormaster application.
if (!$viewer->isOmnipotent()) {
if (!$this->isViewerRevisionAuthor($object, $viewer)) { if (!$this->isViewerRevisionAuthor($object, $viewer)) {
throw new Exception( throw new Exception(
pht( pht(
@@ -64,6 +67,7 @@ final class DifferentialRevisionRequestReviewTransaction
'the author of the revision.')); 'the author of the revision.'));
} }
} }
}
public function getTitle() { public function getTitle() {
return pht( return pht(

View File

@@ -64,11 +64,6 @@ final class DifferentialRevisionResignTransaction
'been closed. You can only resign from open revisions.')); 'been closed. You can only resign from open revisions.'));
} }
if ($object->isDraft()) {
throw new Exception(
pht('You can not resign from a draft revision.'));
}
$resigned = DifferentialReviewerStatus::STATUS_RESIGNED; $resigned = DifferentialReviewerStatus::STATUS_RESIGNED;
if ($this->getViewerReviewerStatus($object, $viewer) == $resigned) { if ($this->getViewerReviewerStatus($object, $viewer) == $resigned) {
throw new Exception( throw new Exception(

View File

@@ -74,10 +74,10 @@ final class DifferentialRevisionStatusTransaction
return 'status'; return 'status';
} }
public function getFieldValuesForConduit($object, $data) { public function getFieldValuesForConduit($xaction, $data) {
return array( return array(
'old' => $object->getOldValue(), 'old' => $xaction->getOldValue(),
'new' => $object->getNewValue(), 'new' => $xaction->getNewValue(),
); );
} }

View File

@@ -0,0 +1,41 @@
<?php
final class DifferentialRevisionWrongStateTransaction
extends DifferentialRevisionTransactionType {
const TRANSACTIONTYPE = 'differential.revision.wrong';
public function generateOldValue($object) {
return null;
}
public function generateNewValue($object, $value) {
return $value;
}
public function getIcon() {
return 'fa-exclamation';
}
public function getColor() {
return 'pink';
}
public function getActionStrength() {
return 4;
}
public function getTitle() {
$new_value = $this->getNewValue();
$status = DifferentialRevisionStatus::newForStatus($new_value);
return pht(
'This revision was not accepted when it landed; it landed in state %s.',
$this->renderValue($status->getDisplayName()));
}
public function getTitleForFeed() {
return null;
}
}

View File

@@ -47,7 +47,7 @@ final class DiffusionExistsQueryConduitAPIMethod
$commit = $request->getValue('commit'); $commit = $request->getValue('commit');
list($err, $stdout) = $repository->execLocalCommand( list($err, $stdout) = $repository->execLocalCommand(
'id --rev %s', 'id --rev %s',
$commit); hgsprintf('%s', $commit));
return !$err; return !$err;
} }

View File

@@ -123,21 +123,23 @@ final class DiffusionHistoryQueryConduitAPIMethod
// branches). // branches).
if (strlen($path)) { if (strlen($path)) {
$path_arg = csprintf('-- %s', $path); $path_arg = csprintf('%s', $path);
$branch_arg = ''; $revset_arg = hgsprintf(
'reverse(ancestors(%s))',
$commit_hash);
} else { } else {
$path_arg = ''; $path_arg = '';
// NOTE: --branch used to be called --only-branch; use -b for $revset_arg = hgsprintf(
// compatibility. 'reverse(ancestors(%s)) and branch(%s)',
$branch_arg = csprintf('-b %s', $drequest->getBranch()); $drequest->getBranch(),
$commit_hash);
} }
list($stdout) = $repository->execxLocalCommand( list($stdout) = $repository->execxLocalCommand(
'log --debug --template %s --limit %d %C --rev %s %C', 'log --debug --template %s --limit %d --rev %s -- %C',
'{node};{parents}\\n', '{node};{parents}\\n',
($offset + $limit), // No '--skip' in Mercurial. ($offset + $limit), // No '--skip' in Mercurial.
$branch_arg, $revset_arg,
hgsprintf('reverse(ancestors(%s))', $commit_hash),
$path_arg); $path_arg);
$stdout = DiffusionMercurialCommandEngine::filterMercurialDebugOutput( $stdout = DiffusionMercurialCommandEngine::filterMercurialDebugOutput(

View File

@@ -77,7 +77,7 @@ final class DiffusionMergedCommitsQueryConduitAPIMethod
list($parents) = $repository->execxLocalCommand( list($parents) = $repository->execxLocalCommand(
'parents --template=%s --rev %s', 'parents --template=%s --rev %s',
'{node}\\n', '{node}\\n',
$commit); hgsprintf('%s', $commit));
$parents = explode("\n", trim($parents)); $parents = explode("\n", trim($parents));
if (count($parents) < 2) { if (count($parents) < 2) {

View File

@@ -97,7 +97,7 @@ final class DiffusionSearchQueryConduitAPIMethod
$results = array(); $results = array();
$future = $repository->getLocalCommandFuture( $future = $repository->getLocalCommandFuture(
'grep --rev %s --print0 --line-number %s %s', 'grep --rev %s --print0 --line-number -- %s %s',
hgsprintf('ancestors(%s)', $drequest->getStableCommit()), hgsprintf('ancestors(%s)', $drequest->getStableCommit()),
$grep, $grep,
$path); $path);

View File

@@ -969,6 +969,24 @@ final class DiffusionBrowseController extends DiffusionController {
$handles = $viewer->loadHandles($phids); $handles = $viewer->loadHandles($phids);
$author_phids = array();
$author_map = array();
foreach ($blame_commits as $commit) {
$commit_identifier = $commit->getCommitIdentifier();
$author_phid = '';
if (isset($revision_map[$commit_identifier])) {
$revision_id = $revision_map[$commit_identifier];
$revision = $revisions[$revision_id];
$author_phid = $revision->getAuthorPHID();
} else {
$author_phid = $commit->getAuthorPHID();
}
$author_map[$commit_identifier] = $author_phid;
$author_phids[$author_phid] = $author_phid;
}
$colors = array(); $colors = array();
if ($blame_commits) { if ($blame_commits) {
$epochs = array(); $epochs = array();
@@ -1113,6 +1131,7 @@ final class DiffusionBrowseController extends DiffusionController {
// blame outputs. // blame outputs.
$commit_links = $this->renderCommitLinks($blame_commits, $handles); $commit_links = $this->renderCommitLinks($blame_commits, $handles);
$revision_links = $this->renderRevisionLinks($revisions, $handles); $revision_links = $this->renderRevisionLinks($revisions, $handles);
$author_links = $this->renderAuthorLinks($author_map, $handles);
if ($this->coverage) { if ($this->coverage) {
require_celerity_resource('differential-changeset-view-css'); require_celerity_resource('differential-changeset-view-css');
@@ -1127,6 +1146,10 @@ final class DiffusionBrowseController extends DiffusionController {
)); ));
} }
$skip_text = pht('Skip Past This Commit');
$skip_icon = id(new PHUIIconView())
->setIcon('fa-caret-square-o-left');
foreach ($display as $line_index => $line) { foreach ($display as $line_index => $line) {
$row = array(); $row = array();
@@ -1141,15 +1164,15 @@ final class DiffusionBrowseController extends DiffusionController {
$revision_link = null; $revision_link = null;
$commit_link = null; $commit_link = null;
$author_link = null;
$before_link = null; $before_link = null;
$commit_date = null;
$style = 'border-right: 3px solid '.$line['color'].';'; $style = 'background: '.$line['color'].';';
if ($identifier && !$line['duplicate']) { if ($identifier && !$line['duplicate']) {
if (isset($commit_links[$identifier])) { if (isset($commit_links[$identifier])) {
$commit_link = $commit_links[$identifier]['link']; $commit_link = $commit_links[$identifier];
$commit_date = $commit_links[$identifier]['date']; $author_link = $author_links[$author_map[$identifier]];
} }
if (isset($revision_map[$identifier])) { if (isset($revision_map[$identifier])) {
@@ -1160,10 +1183,6 @@ final class DiffusionBrowseController extends DiffusionController {
} }
$skip_href = $line_href.'?before='.$identifier.'&view=blame'; $skip_href = $line_href.'?before='.$identifier.'&view=blame';
$skip_text = pht('Skip Past This Commit');
$icon = id(new PHUIIconView())
->setIcon('fa-caret-square-o-left');
$before_link = javelin_tag( $before_link = javelin_tag(
'a', 'a',
array( array(
@@ -1175,7 +1194,7 @@ final class DiffusionBrowseController extends DiffusionController {
'size' => 300, 'size' => 300,
), ),
), ),
$icon); $skip_icon);
} }
if ($show_blame) { if ($show_blame) {
@@ -1186,34 +1205,27 @@ final class DiffusionBrowseController extends DiffusionController {
), ),
$before_link); $before_link);
$row[] = phutil_tag( $object_links = array();
'th', $object_links[] = $author_link;
array( $object_links[] = $commit_link;
'class' => 'diffusion-rev-link', if ($revision_link) {
), $object_links[] = phutil_tag('span', array(), '/');
$commit_link); $object_links[] = $revision_link;
if ($revision_map) {
$row[] = phutil_tag(
'th',
array(
'class' => 'diffusion-blame-revision',
),
$revision_link);
} }
$row[] = phutil_tag( $row[] = phutil_tag(
'th', 'th',
array( array(
'class' => 'diffusion-blame-date', 'class' => 'diffusion-rev-link',
), ),
$commit_date); $object_links);
} }
$line_link = phutil_tag( $line_link = phutil_tag(
'a', 'a',
array( array(
'href' => $line_href, 'href' => $line_href,
'style' => $style,
), ),
$line_number); $line_number);
@@ -1536,6 +1548,33 @@ final class DiffusionBrowseController extends DiffusionController {
return head($parents); return head($parents);
} }
private function renderRevisionTooltip(
DifferentialRevision $revision,
$handles) {
$viewer = $this->getRequest()->getUser();
$date = phabricator_date($revision->getDateModified(), $viewer);
$id = $revision->getID();
$title = $revision->getTitle();
$header = "D{$id} {$title}";
$author = $handles[$revision->getAuthorPHID()]->getName();
return "{$header}\n{$date} \xC2\xB7 {$author}";
}
private function renderCommitTooltip(
PhabricatorRepositoryCommit $commit,
$author) {
$viewer = $this->getRequest()->getUser();
$date = phabricator_date($commit->getEpoch(), $viewer);
$summary = trim($commit->getSummary());
return "{$summary}\n{$date} \xC2\xB7 {$author}";
}
protected function markupText($text) { protected function markupText($text) {
$engine = PhabricatorMarkupEngine::newDiffusionMarkupEngine(); $engine = PhabricatorMarkupEngine::newDiffusionMarkupEngine();
$engine->setConfig('viewer', $this->getRequest()->getUser()); $engine->setConfig('viewer', $this->getRequest()->getUser());
@@ -1743,6 +1782,9 @@ final class DiffusionBrowseController extends DiffusionController {
->setViewer($viewer) ->setViewer($viewer)
->withRepository($repository) ->withRepository($repository)
->withIdentifiers($identifiers) ->withIdentifiers($identifiers)
// TODO: We only fetch this to improve author display behavior, but
// shouldn't really need to?
->needCommitData(true)
->execute(); ->execute();
$commits = mpull($commits, null, 'getCommitIdentifier'); $commits = mpull($commits, null, 'getCommitIdentifier');
} else { } else {
@@ -1752,29 +1794,59 @@ final class DiffusionBrowseController extends DiffusionController {
return array($identifiers, $commits); return array($identifiers, $commits);
} }
private function renderAuthorLinks(array $authors, $handles) {
$links = array();
foreach ($authors as $phid) {
if (!strlen($phid)) {
// This means we couldn't identify an author for the commit or the
// revision. We just render a blank for alignment.
$style = null;
$href = null;
} else {
$src = $handles[$phid]->getImageURI();
$style = 'background-image: url('.$src.');';
$href = $handles[$phid]->getURI();
}
$links[$phid] = javelin_tag(
$href ? 'a' : 'span',
array(
'class' => 'diffusion-author-link',
'style' => $style,
'href' => $href,
'sigil' => 'has-tooltip',
'meta' => array(
'tip' => $handles[$phid]->getName(),
'align' => 'E',
),
));
}
return $links;
}
private function renderCommitLinks(array $commits, $handles) { private function renderCommitLinks(array $commits, $handles) {
$links = array(); $links = array();
$viewer = $this->getViewer();
foreach ($commits as $identifier => $commit) { foreach ($commits as $identifier => $commit) {
$date = phabricator_date($commit->getEpoch(), $viewer); $tooltip = $this->renderCommitTooltip(
$summary = trim($commit->getSummary()); $commit,
$commit->renderAuthorShortName($handles));
$commit_link = phutil_tag( $commit_link = javelin_tag(
'a', 'a',
array( array(
'href' => $commit->getURI(), 'href' => $commit->getURI(),
'sigil' => 'has-tooltip',
'meta' => array(
'tip' => $tooltip,
'align' => 'E',
'size' => 600,
), ),
$summary);
$commit_date = phutil_tag(
'a',
array(
'href' => $commit->getURI(),
), ),
$date); $commit->getLocalName());
$links[$identifier]['link'] = $commit_link; $links[$identifier] = $commit_link;
$links[$identifier]['date'] = $commit_date;
} }
return $links; return $links;
@@ -1785,10 +1857,19 @@ final class DiffusionBrowseController extends DiffusionController {
foreach ($revisions as $revision) { foreach ($revisions as $revision) {
$revision_id = $revision->getID(); $revision_id = $revision->getID();
$revision_link = phutil_tag(
$tooltip = $this->renderRevisionTooltip($revision, $handles);
$revision_link = javelin_tag(
'a', 'a',
array( array(
'href' => '/'.$revision->getMonogram(), 'href' => '/'.$revision->getMonogram(),
'sigil' => 'has-tooltip',
'meta' => array(
'tip' => $tooltip,
'align' => 'E',
'size' => 600,
),
), ),
$revision->getMonogram()); $revision->getMonogram());

View File

@@ -39,20 +39,23 @@ final class DiffusionCommitController extends DiffusionController {
return $this->buildRawDiffResponse($drequest); return $this->buildRawDiffResponse($drequest);
} }
$commit = id(new DiffusionCommitQuery()) $commits = id(new DiffusionCommitQuery())
->setViewer($viewer) ->setViewer($viewer)
->withRepository($repository) ->withRepository($repository)
->withIdentifiers(array($commit_identifier)) ->withIdentifiers(array($commit_identifier))
->needCommitData(true) ->needCommitData(true)
->needAuditRequests(true) ->needAuditRequests(true)
->executeOne(); ->setLimit(100)
->execute();
$multiple_results = count($commits) > 1;
$crumbs = $this->buildCrumbs(array( $crumbs = $this->buildCrumbs(array(
'commit' => true, 'commit' => !$multiple_results,
)); ));
$crumbs->setBorder(true); $crumbs->setBorder(true);
if (!$commit) { if (!$commits) {
if (!$this->getCommitExists()) { if (!$this->getCommitExists()) {
return new Aphront404Response(); return new Aphront404Response();
} }
@@ -70,7 +73,40 @@ final class DiffusionCommitController extends DiffusionController {
->setTitle($title) ->setTitle($title)
->setCrumbs($crumbs) ->setCrumbs($crumbs)
->appendChild($error); ->appendChild($error);
} else if ($multiple_results) {
$warning_message =
pht(
'The identifier %s is ambiguous and matches more than one commit.',
phutil_tag(
'strong',
array(),
$commit_identifier));
$error = id(new PHUIInfoView())
->setTitle(pht('Ambiguous Commit'))
->setSeverity(PHUIInfoView::SEVERITY_WARNING)
->appendChild($warning_message);
$list = id(new DiffusionCommitListView())
->setViewer($viewer)
->setCommits($commits)
->setNoDataString(pht('No recent commits.'));
$crumbs->addTextCrumb(pht('Ambiguous Commit'));
$matched_commits = id(new PHUITwoColumnView())
->setFooter(array(
$error,
$list,
));
return $this->newPage()
->setTitle(pht('Ambiguous Commit'))
->setCrumbs($crumbs)
->appendChild($matched_commits);
} else {
$commit = head($commits);
} }
$audit_requests = $commit->getAudits(); $audit_requests = $commit->getAudits();
@@ -438,7 +474,8 @@ final class DiffusionCommitController extends DiffusionController {
$repository = $drequest->getRepository(); $repository = $drequest->getRepository();
$view = id(new PHUIPropertyListView()) $view = id(new PHUIPropertyListView())
->setUser($this->getRequest()->getUser()); ->setUser($this->getRequest()->getUser())
->setObject($commit);
$edge_query = id(new PhabricatorEdgeQuery()) $edge_query = id(new PhabricatorEdgeQuery())
->withSourcePHIDs(array($commit_phid)) ->withSourcePHIDs(array($commit_phid))

View File

@@ -46,8 +46,20 @@ final class DiffusionRepositoryController extends DiffusionController {
->withRepositoryPHIDs(array($repository->getPHID())) ->withRepositoryPHIDs(array($repository->getPHID()))
->withRefTypes(array(PhabricatorRepositoryRefCursor::TYPE_BRANCH)) ->withRefTypes(array(PhabricatorRepositoryRefCursor::TYPE_BRANCH))
->withRefNames(array($drequest->getBranch())) ->withRefNames(array($drequest->getBranch()))
->needPositions(true)
->execute(); ->execute();
if ($ref_cursors) {
// It's possible that this branch previously existed, but has been
// deleted. Make sure we have valid cursor positions, not just cursors.
$any_positions = false;
foreach ($ref_cursors as $ref_cursor) {
if ($ref_cursor->getPositions()) {
$any_positions = true;
break;
}
}
if ($any_positions) {
// This is a valid branch, so we necessarily have some content. // This is a valid branch, so we necessarily have some content.
$page_has_content = true; $page_has_content = true;
} else { } else {

View File

@@ -347,11 +347,11 @@ final class DiffusionURIEditor
continue; continue;
} }
$io_type = $uri->getIoType(); $io_type = $uri->getEffectiveIOType();
if ($io_type == PhabricatorRepositoryURI::IO_READWRITE) { if ($io_type == PhabricatorRepositoryURI::IO_READWRITE) {
if ($no_readwrite) { if ($no_readwrite) {
$readwite_conflict = $uri; $readwrite_conflict = $uri;
break; break;
} }
} }

View File

@@ -0,0 +1,12 @@
<?php
final class DiffusionQuickSearchEngineExtension
extends PhabricatorQuickSearchEngineExtension {
public function newQuickSearchDatasources() {
return array(
new DiffusionRepositoryDatasource(),
new DiffusionSymbolDatasource(),
);
}
}

View File

@@ -0,0 +1,3 @@
<?php
final class DiffusionMercurialFlagInjectionException extends Exception {}

View File

@@ -11,6 +11,27 @@ final class DiffusionMercurialCommandEngine
protected function newFormattedCommand($pattern, array $argv) { protected function newFormattedCommand($pattern, array $argv) {
$args = array(); $args = array();
// Crudely blacklist commands which look like they may contain command
// injection via "--config" or "--debugger". See T13012. To do this, we
// print the whole command, parse it using shell rules, then examine each
// argument to see if it looks like "--config" or "--debugger".
$test_command = call_user_func_array(
'csprintf',
array_merge(array($pattern), $argv));
$test_args = id(new PhutilShellLexer())
->splitArguments($test_command);
foreach ($test_args as $test_arg) {
if (preg_match('/^--(config|debugger)/i', $test_arg)) {
throw new DiffusionMercurialFlagInjectionException(
pht(
'Mercurial command appears to contain unsafe injected "--config" '.
'or "--debugger": %s',
$test_command));
}
}
// NOTE: Here, and in Git and Subversion, we override the SSH command even // NOTE: Here, and in Git and Subversion, we override the SSH command even
// if the repository does not use an SSH remote, since our SSH wrapper // if the repository does not use an SSH remote, since our SSH wrapper
// defuses an attack against older versions of Mercurial, Git and // defuses an attack against older versions of Mercurial, Git and

View File

@@ -123,6 +123,62 @@ final class DiffusionCommandEngineTestCase extends PhabricatorTestCase {
'argv' => 'xyz', 'argv' => 'xyz',
'protocol' => 'https', 'protocol' => 'https',
)); ));
// Test that filtering defenses for "--config" and "--debugger" flag
// injections in Mercurial are functional. See T13012.
$caught = null;
try {
$this->assertCommandEngineFormat(
'',
array(),
array(
'vcs' => $type_hg,
'argv' => '--debugger',
));
} catch (DiffusionMercurialFlagInjectionException $ex) {
$caught = $ex;
}
$this->assertTrue(
($caught instanceof DiffusionMercurialFlagInjectionException),
pht('Expected "--debugger" injection in Mercurial to throw.'));
$caught = null;
try {
$this->assertCommandEngineFormat(
'',
array(),
array(
'vcs' => $type_hg,
'argv' => '--config=x',
));
} catch (DiffusionMercurialFlagInjectionException $ex) {
$caught = $ex;
}
$this->assertTrue(
($caught instanceof DiffusionMercurialFlagInjectionException),
pht('Expected "--config" injection in Mercurial to throw.'));
$caught = null;
try {
$this->assertCommandEngineFormat(
'',
array(),
array(
'vcs' => $type_hg,
'argv' => (string)csprintf('%s', '--config=x'),
));
} catch (DiffusionMercurialFlagInjectionException $ex) {
$caught = $ex;
}
$this->assertTrue(
($caught instanceof DiffusionMercurialFlagInjectionException),
pht('Expected quoted "--config" injection in Mercurial to throw.'));
} }
private function assertCommandEngineFormat( private function assertCommandEngineFormat(

View File

@@ -177,7 +177,63 @@ final class DiffusionCommitQuery
} }
protected function loadPage() { protected function loadPage() {
return $this->loadStandardPage($this->newResultObject()); $table = $this->newResultObject();
$conn = $table->establishConnection('r');
$subqueries = array();
if ($this->responsiblePHIDs) {
$base_authors = $this->authorPHIDs;
$base_auditors = $this->auditorPHIDs;
$responsible_phids = $this->responsiblePHIDs;
if ($base_authors) {
$all_authors = array_merge($base_authors, $responsible_phids);
} else {
$all_authors = $responsible_phids;
}
if ($base_auditors) {
$all_auditors = array_merge($base_auditors, $responsible_phids);
} else {
$all_auditors = $responsible_phids;
}
$this->authorPHIDs = $all_authors;
$this->auditorPHIDs = $base_auditors;
$subqueries[] = $this->buildStandardPageQuery(
$conn,
$table->getTableName());
$this->authorPHIDs = $base_authors;
$this->auditorPHIDs = $all_auditors;
$subqueries[] = $this->buildStandardPageQuery(
$conn,
$table->getTableName());
} else {
$subqueries[] = $this->buildStandardPageQuery(
$conn,
$table->getTableName());
}
if (count($subqueries) > 1) {
foreach ($subqueries as $key => $subquery) {
$subqueries[$key] = '('.$subquery.')';
}
$query = qsprintf(
$conn,
'%Q %Q %Q',
implode(' UNION DISTINCT ', $subqueries),
$this->buildOrderClause($conn, true),
$this->buildLimitClause($conn));
} else {
$query = head($subqueries);
}
$rows = queryfx_all($conn, '%Q', $query);
$rows = $this->didLoadRawRows($rows);
return $table->loadAllFromArray($rows);
} }
protected function willFilterPage(array $commits) { protected function willFilterPage(array $commits) {
@@ -487,18 +543,10 @@ final class DiffusionCommitQuery
$this->auditorPHIDs); $this->auditorPHIDs);
} }
if ($this->responsiblePHIDs !== null) {
$where[] = qsprintf(
$conn,
'(audit.auditorPHID IN (%Ls) OR commit.authorPHID IN (%Ls))',
$this->responsiblePHIDs,
$this->responsiblePHIDs);
}
if ($this->statuses !== null) { if ($this->statuses !== null) {
$where[] = qsprintf( $where[] = qsprintf(
$conn, $conn,
'commit.auditStatus IN (%Ls)', 'commit.auditStatus IN (%Ld)',
$this->statuses); $this->statuses);
} }
@@ -541,10 +589,6 @@ final class DiffusionCommitQuery
return ($this->auditIDs || $this->auditorPHIDs); return ($this->auditIDs || $this->auditorPHIDs);
} }
private function shouldJoinAudit() {
return (bool)$this->responsiblePHIDs;
}
private function shouldJoinOwners() { private function shouldJoinOwners() {
return (bool)$this->packagePHIDs; return (bool)$this->packagePHIDs;
} }
@@ -560,13 +604,6 @@ final class DiffusionCommitQuery
$audit_request->getTableName()); $audit_request->getTableName());
} }
if ($this->shouldJoinAudit()) {
$join[] = qsprintf(
$conn,
'LEFT JOIN %T audit ON commit.phid = audit.commitPHID',
$audit_request->getTableName());
}
if ($this->shouldJoinOwners()) { if ($this->shouldJoinOwners()) {
$join[] = qsprintf( $join[] = qsprintf(
$conn, $conn,
@@ -584,10 +621,6 @@ final class DiffusionCommitQuery
return true; return true;
} }
if ($this->shouldJoinAudit()) {
return true;
}
if ($this->shouldJoinOwners()) { if ($this->shouldJoinOwners()) {
return true; return true;
} }

View File

@@ -256,6 +256,66 @@ final class DiffusionLowLevelResolveRefsQuery
return $results; return $results;
} }
// If some of the refs look like hashes, try to bulk resolve them. This
// workflow happens via RefEngine and bulk resolution is dramatically
// faster than individual resolution. See PHI158.
$hashlike = array();
foreach ($unresolved as $key => $ref) {
if (preg_match('/^[a-f0-9]{40}\z/', $ref)) {
$hashlike[$key] = $ref;
}
}
if (count($hashlike) > 1) {
$hashlike_map = array();
$hashlike_groups = array_chunk($hashlike, 64, true);
foreach ($hashlike_groups as $hashlike_group) {
$hashlike_arg = array();
foreach ($hashlike_group as $hashlike_ref) {
$hashlike_arg[] = hgsprintf('%s', $hashlike_ref);
}
$hashlike_arg = '('.implode(' or ', $hashlike_arg).')';
list($err, $refs) = $repository->execLocalCommand(
'log --template=%s --rev %s',
'{node}\n',
$hashlike_arg);
if ($err) {
// NOTE: If any ref fails to resolve, Mercurial will exit with an
// error. We just give up on the whole group and resolve it
// individually below. In theory, we could split it into subgroups
// but the pathway where this bulk resolution matters rarely tries
// to resolve missing refs (see PHI158).
continue;
}
$refs = phutil_split_lines($refs, false);
foreach ($refs as $ref) {
$hashlike_map[$ref] = true;
}
}
foreach ($unresolved as $key => $ref) {
if (!isset($hashlike_map[$ref])) {
continue;
}
$results[$ref][] = array(
'type' => 'commit',
'identifier' => $ref,
);
unset($unresolved[$key]);
}
}
if (!$unresolved) {
return $results;
}
// If we still have unresolved refs (which might be things like "tip"), // If we still have unresolved refs (which might be things like "tip"),
// try to resolve them individually. // try to resolve them individually.

View File

@@ -16,14 +16,14 @@ final class DiffusionMercurialRawDiffQuery extends DiffusionRawDiffQuery {
// If `$commit` has no parents (usually because it's the first commit // If `$commit` has no parents (usually because it's the first commit
// in the repository), we want to diff against `null`. This revset will // in the repository), we want to diff against `null`. This revset will
// do that for us automatically. // do that for us automatically.
$against = '('.$commit.'^ or null)'; $against = hgsprintf('(%s^ or null)', $commit);
} }
$future = $repository->getLocalCommandFuture( $future = $repository->getLocalCommandFuture(
'diff -U %d --git --rev %s --rev %s -- %s', 'diff -U %d --git --rev %s --rev %s -- %s',
$this->getLinesOfContext(), $this->getLinesOfContext(),
$against, $against,
$commit, hgsprintf('%s', $commit),
$path); $path);
return $future; return $future;

View File

@@ -96,9 +96,10 @@ final class DiffusionPatternSearchView extends DiffusionView {
$path_title = Filesystem::readablePath($this->path, $drequest->getPath()); $path_title = Filesystem::readablePath($this->path, $drequest->getPath());
$href = $drequest->generateURI(array( $href = $drequest->generateURI(
array(
'action' => 'browse', 'action' => 'browse',
'path' => $path_title, 'path' => $this->path,
)); ));
$title = phutil_tag('a', array('href' => $href), $path_title); $title = phutil_tag('a', array('href' => $href), $path_title);

View File

@@ -83,6 +83,9 @@ final class DrydockLease extends DrydockDAO
'key_resource' => array( 'key_resource' => array(
'columns' => array('resourcePHID', 'status'), 'columns' => array('resourcePHID', 'status'),
), ),
'key_status' => array(
'columns' => array('status'),
),
), ),
) + parent::getConfiguration(); ) + parent::getConfiguration();
} }

View File

@@ -58,6 +58,14 @@ final class PhabricatorFilesComposeAvatarBuiltinFile
} }
private function composeImage($color, $image, $border) { private function composeImage($color, $image, $border) {
// If we don't have the GD extension installed, just return a static
// default profile image rather than trying to compose a dynamic one.
if (!function_exists('imagecreatefromstring')) {
$root = dirname(phutil_get_library_root('phabricator'));
$default_path = $root.'/resources/builtin/profile.png';
return Filesystem::readFile($default_path);
}
$color_const = hexdec(trim($color, '#')); $color_const = hexdec(trim($color, '#'));
$true_border = self::rgba2gd($border); $true_border = self::rgba2gd($border);
$image_map = self::getImageMap(); $image_map = self::getImageMap();

View File

@@ -14,6 +14,10 @@ final class PhabricatorFileDataController extends PhabricatorFileController {
return false; return false;
} }
public function shouldAllowPartialSessions() {
return true;
}
public function handleRequest(AphrontRequest $request) { public function handleRequest(AphrontRequest $request) {
$viewer = $request->getViewer(); $viewer = $request->getViewer();
$this->phid = $request->getURIData('phid'); $this->phid = $request->getURIData('phid');

View File

@@ -134,6 +134,9 @@ final class PhabricatorFile extends PhabricatorFileDAO
'columns' => array('builtinKey'), 'columns' => array('builtinKey'),
'unique' => true, 'unique' => true,
), ),
'key_engine' => array(
'columns' => array('storageEngine', 'storageHandle(64)'),
),
), ),
) + parent::getConfiguration(); ) + parent::getConfiguration();
} }

View File

@@ -7,6 +7,12 @@ final class HarbormasterRunBuildPlansHeraldAction
const ACTIONCONST = 'harbormaster.build'; const ACTIONCONST = 'harbormaster.build';
public function getRequiredAdapterStates() {
return array(
HeraldBuildableState::STATECONST,
);
}
public function getActionGroupKey() { public function getActionGroupKey() {
return HeraldSupportActionGroup::ACTIONGROUPKEY; return HeraldSupportActionGroup::ACTIONGROUPKEY;
} }

View File

@@ -10,6 +10,7 @@ final class HarbormasterBuildQuery
private $buildPlanPHIDs; private $buildPlanPHIDs;
private $initiatorPHIDs; private $initiatorPHIDs;
private $needBuildTargets; private $needBuildTargets;
private $autobuilds;
public function withIDs(array $ids) { public function withIDs(array $ids) {
$this->ids = $ids; $this->ids = $ids;
@@ -41,6 +42,11 @@ final class HarbormasterBuildQuery
return $this; return $this;
} }
public function withAutobuilds($with_autobuilds) {
$this->autobuilds = $with_autobuilds;
return $this;
}
public function needBuildTargets($need_targets) { public function needBuildTargets($need_targets) {
$this->needBuildTargets = $need_targets; $this->needBuildTargets = $need_targets;
return $this; return $this;
@@ -141,50 +147,87 @@ final class HarbormasterBuildQuery
if ($this->ids !== null) { if ($this->ids !== null) {
$where[] = qsprintf( $where[] = qsprintf(
$conn, $conn,
'id IN (%Ld)', 'b.id IN (%Ld)',
$this->ids); $this->ids);
} }
if ($this->phids !== null) { if ($this->phids !== null) {
$where[] = qsprintf( $where[] = qsprintf(
$conn, $conn,
'phid in (%Ls)', 'b.phid in (%Ls)',
$this->phids); $this->phids);
} }
if ($this->buildStatuses !== null) { if ($this->buildStatuses !== null) {
$where[] = qsprintf( $where[] = qsprintf(
$conn, $conn,
'buildStatus in (%Ls)', 'b.buildStatus in (%Ls)',
$this->buildStatuses); $this->buildStatuses);
} }
if ($this->buildablePHIDs !== null) { if ($this->buildablePHIDs !== null) {
$where[] = qsprintf( $where[] = qsprintf(
$conn, $conn,
'buildablePHID IN (%Ls)', 'b.buildablePHID IN (%Ls)',
$this->buildablePHIDs); $this->buildablePHIDs);
} }
if ($this->buildPlanPHIDs !== null) { if ($this->buildPlanPHIDs !== null) {
$where[] = qsprintf( $where[] = qsprintf(
$conn, $conn,
'buildPlanPHID IN (%Ls)', 'b.buildPlanPHID IN (%Ls)',
$this->buildPlanPHIDs); $this->buildPlanPHIDs);
} }
if ($this->initiatorPHIDs !== null) { if ($this->initiatorPHIDs !== null) {
$where[] = qsprintf( $where[] = qsprintf(
$conn, $conn,
'initiatorPHID IN (%Ls)', 'b.initiatorPHID IN (%Ls)',
$this->initiatorPHIDs); $this->initiatorPHIDs);
} }
if ($this->autobuilds !== null) {
if ($this->autobuilds) {
$where[] = qsprintf(
$conn,
'p.planAutoKey IS NOT NULL');
} else {
$where[] = qsprintf(
$conn,
'p.planAutoKey IS NULL');
}
}
return $where; return $where;
} }
protected function buildJoinClauseParts(AphrontDatabaseConnection $conn) {
$joins = parent::buildJoinClauseParts($conn);
if ($this->shouldJoinPlanTable()) {
$joins[] = qsprintf(
$conn,
'JOIN %T p ON b.buildPlanPHID = p.phid',
id(new HarbormasterBuildPlan())->getTableName());
}
return $joins;
}
private function shouldJoinPlanTable() {
if ($this->autobuilds !== null) {
return true;
}
return false;
}
public function getQueryApplicationClass() { public function getQueryApplicationClass() {
return 'PhabricatorHarbormasterApplication'; return 'PhabricatorHarbormasterApplication';
} }
protected function getPrimaryTableAlias() {
return 'b';
}
} }

View File

@@ -43,6 +43,9 @@ final class HarbormasterBuildArtifact extends HarbormasterDAO
'key_target' => array( 'key_target' => array(
'columns' => array('buildTargetPHID', 'artifactType'), 'columns' => array('buildTargetPHID', 'artifactType'),
), ),
'key_index' => array(
'columns' => array('artifactIndex'),
),
), ),
) + parent::getConfiguration(); ) + parent::getConfiguration();
} }

View File

@@ -17,6 +17,7 @@ abstract class HeraldAction extends Phobject {
const DO_STANDARD_PERMISSION = 'do.standard.permission'; const DO_STANDARD_PERMISSION = 'do.standard.permission';
const DO_STANDARD_INVALID_ACTION = 'do.standard.invalid-action'; const DO_STANDARD_INVALID_ACTION = 'do.standard.invalid-action';
const DO_STANDARD_WRONG_RULE_TYPE = 'do.standard.wrong-rule-type'; const DO_STANDARD_WRONG_RULE_TYPE = 'do.standard.wrong-rule-type';
const DO_STANDARD_FORBIDDEN = 'do.standard.forbidden';
abstract public function getHeraldActionName(); abstract public function getHeraldActionName();
abstract public function supportsObject($object); abstract public function supportsObject($object);
@@ -25,6 +26,10 @@ abstract class HeraldAction extends Phobject {
abstract public function renderActionDescription($value); abstract public function renderActionDescription($value);
public function getRequiredAdapterStates() {
return array();
}
protected function renderActionEffectDescription($type, $data) { protected function renderActionEffectDescription($type, $data) {
return null; return null;
} }
@@ -336,6 +341,11 @@ abstract class HeraldAction extends Phobject {
'color' => 'red', 'color' => 'red',
'name' => pht('Wrong Rule Type'), 'name' => pht('Wrong Rule Type'),
), ),
self::DO_STANDARD_FORBIDDEN => array(
'icon' => 'fa-ban',
'color' => 'violet',
'name' => pht('Forbidden'),
),
); );
} }
@@ -381,6 +391,8 @@ abstract class HeraldAction extends Phobject {
return pht( return pht(
'This action does not support rules of type "%s".', 'This action does not support rules of type "%s".',
$data); $data);
case self::DO_STANDARD_FORBIDDEN:
return HeraldStateReasons::getExplanation($data);
} }
return null; return null;

View File

@@ -37,6 +37,7 @@ abstract class HeraldAdapter extends Phobject {
private $fieldMap; private $fieldMap;
private $actionMap; private $actionMap;
private $edgeCache = array(); private $edgeCache = array();
private $forbiddenActions = array();
public function getEmailPHIDs() { public function getEmailPHIDs() {
return array_values($this->emailPHIDs); return array_values($this->emailPHIDs);
@@ -1116,4 +1117,38 @@ abstract class HeraldAdapter extends Phobject {
return $this->edgeCache[$type]; return $this->edgeCache[$type];
} }
/* -( Forbidden Actions )-------------------------------------------------- */
final public function getForbiddenActions() {
return array_keys($this->forbiddenActions);
}
final public function setForbiddenAction($action, $reason) {
$this->forbiddenActions[$action] = $reason;
return $this;
}
final public function getRequiredFieldStates($field_key) {
return $this->requireFieldImplementation($field_key)
->getRequiredAdapterStates();
}
final public function getRequiredActionStates($action_key) {
return $this->requireActionImplementation($action_key)
->getRequiredAdapterStates();
}
final public function getForbiddenReason($action) {
if (!isset($this->forbiddenActions[$action])) {
throw new Exception(
pht(
'Action "%s" is not forbidden!',
$action));
}
return $this->forbiddenActions[$action];
}
} }

View File

@@ -273,7 +273,11 @@ final class HeraldTranscriptController extends HeraldController {
->setTarget(phutil_tag('strong', array(), pht('Conditions')))); ->setTarget(phutil_tag('strong', array(), pht('Conditions'))));
foreach ($cond_xscripts as $cond_xscript) { foreach ($cond_xscripts as $cond_xscript) {
if ($cond_xscript->getResult()) { if ($cond_xscript->isForbidden()) {
$icon = 'fa-ban';
$color = 'indigo';
$result = pht('Forbidden');
} else if ($cond_xscript->getResult()) {
$icon = 'fa-check'; $icon = 'fa-check';
$color = 'green'; $color = 'green';
$result = pht('Passed'); $result = pht('Passed');
@@ -284,12 +288,17 @@ final class HeraldTranscriptController extends HeraldController {
} }
if ($cond_xscript->getNote()) { if ($cond_xscript->getNote()) {
$note_text = $cond_xscript->getNote();
if ($cond_xscript->isForbidden()) {
$note_text = HeraldStateReasons::getExplanation($note_text);
}
$note = phutil_tag( $note = phutil_tag(
'div', 'div',
array( array(
'class' => 'herald-condition-note', 'class' => 'herald-condition-note',
), ),
$cond_xscript->getNote()); $note_text);
} else { } else {
$note = null; $note = null;
} }
@@ -310,7 +319,12 @@ final class HeraldTranscriptController extends HeraldController {
$cond_list->addItem($cond_item); $cond_list->addItem($cond_item);
} }
if ($rule_xscript->getResult()) { if ($rule_xscript->isForbidden()) {
$last_icon = 'fa-ban';
$last_color = 'indigo';
$last_result = pht('Forbidden');
$last_note = pht('Object state prevented rule evaluation.');
} else if ($rule_xscript->getResult()) {
$last_icon = 'fa-check-circle'; $last_icon = 'fa-check-circle';
$last_color = 'green'; $last_color = 'green';
$last_result = pht('Passed'); $last_result = pht('Passed');

View File

@@ -12,6 +12,9 @@ final class HeraldEngine extends Phobject {
protected $object; protected $object;
private $dryRun; private $dryRun;
private $forbiddenFields = array();
private $forbiddenActions = array();
public function setDryRun($dry_run) { public function setDryRun($dry_run) {
$this->dryRun = $dry_run; $this->dryRun = $dry_run;
return $this; return $this;
@@ -76,39 +79,42 @@ final class HeraldEngine extends Phobject {
// This is not a dry run, and this rule is only supposed to be // This is not a dry run, and this rule is only supposed to be
// applied a single time, and it's already been applied... // applied a single time, and it's already been applied...
// That means automatic failure. // That means automatic failure.
$xscript = id(new HeraldRuleTranscript()) $this->newRuleTranscript($rule)
->setRuleID($rule->getID())
->setResult(false) ->setResult(false)
->setRuleName($rule->getName())
->setRuleOwner($rule->getAuthorPHID())
->setReason( ->setReason(
pht( pht(
'This rule is only supposed to be repeated a single time, '. 'This rule is only supposed to be repeated a single time, '.
'and it has already been applied.')); 'and it has already been applied.'));
$this->transcript->addRuleTranscript($xscript);
$rule_matches = false;
} else {
if ($this->isForbidden($rule, $object)) {
$this->newRuleTranscript($rule)
->setResult(HeraldRuleTranscript::RESULT_FORBIDDEN)
->setReason(
pht(
'Object state is not compatible with rule.'));
$rule_matches = false; $rule_matches = false;
} else { } else {
$rule_matches = $this->doesRuleMatch($rule, $object); $rule_matches = $this->doesRuleMatch($rule, $object);
} }
}
} catch (HeraldRecursiveConditionsException $ex) { } catch (HeraldRecursiveConditionsException $ex) {
$names = array(); $names = array();
foreach ($this->stack as $rule_id => $ignored) { foreach ($this->stack as $rule_phid => $ignored) {
$names[] = '"'.$rules[$rule_id]->getName().'"'; $names[] = '"'.$rules[$rule_phid]->getName().'"';
} }
$names = implode(', ', $names); $names = implode(', ', $names);
foreach ($this->stack as $rule_id => $ignored) { foreach ($this->stack as $rule_phid => $ignored) {
$xscript = new HeraldRuleTranscript(); $this->newRuleTranscript($rules[$rule_phid])
$xscript->setRuleID($rule_id); ->setResult(false)
$xscript->setResult(false); ->setReason(
$xscript->setReason(
pht( pht(
"Rules %s are recursively dependent upon one another! ". "Rules %s are recursively dependent upon one another! ".
"Don't do this! You have formed an unresolvable cycle in the ". "Don't do this! You have formed an unresolvable cycle in the ".
"dependency graph!", "dependency graph!",
$names)); $names));
$xscript->setRuleName($rules[$rule_id]->getName());
$xscript->setRuleOwner($rules[$rule_id]->getAuthorPHID());
$this->transcript->addRuleTranscript($xscript);
} }
$rule_matches = false; $rule_matches = false;
} }
@@ -309,14 +315,9 @@ final class HeraldEngine extends Phobject {
} }
} }
$rule_transcript = new HeraldRuleTranscript(); $this->newRuleTranscript($rule)
$rule_transcript->setRuleID($rule->getID()); ->setResult($result)
$rule_transcript->setResult($result); ->setReason($reason);
$rule_transcript->setReason($reason);
$rule_transcript->setRuleName($rule->getName());
$rule_transcript->setRuleOwner($rule->getAuthorPHID());
$this->transcript->addRuleTranscript($rule_transcript);
return $result; return $result;
} }
@@ -327,16 +328,7 @@ final class HeraldEngine extends Phobject {
HeraldAdapter $object) { HeraldAdapter $object) {
$object_value = $this->getConditionObjectValue($condition, $object); $object_value = $this->getConditionObjectValue($condition, $object);
$test_value = $condition->getValue(); $transcript = $this->newConditionTranscript($rule, $condition);
$cond = $condition->getFieldCondition();
$transcript = new HeraldConditionTranscript();
$transcript->setRuleID($rule->getID());
$transcript->setConditionID($condition->getID());
$transcript->setFieldName($condition->getFieldName());
$transcript->setCondition($cond);
$transcript->setTestValue($test_value);
try { try {
$result = $object->doesConditionMatch( $result = $object->doesConditionMatch(
@@ -351,8 +343,6 @@ final class HeraldEngine extends Phobject {
$transcript->setResult($result); $transcript->setResult($result);
$this->transcript->addConditionTranscript($transcript);
return $result; return $result;
} }
@@ -446,4 +436,136 @@ final class HeraldEngine extends Phobject {
return false; return false;
} }
private function newRuleTranscript(HeraldRule $rule) {
$xscript = id(new HeraldRuleTranscript())
->setRuleID($rule->getID())
->setRuleName($rule->getName())
->setRuleOwner($rule->getAuthorPHID());
$this->transcript->addRuleTranscript($xscript);
return $xscript;
}
private function newConditionTranscript(
HeraldRule $rule,
HeraldCondition $condition) {
$xscript = id(new HeraldConditionTranscript())
->setRuleID($rule->getID())
->setConditionID($condition->getID())
->setFieldName($condition->getFieldName())
->setCondition($condition->getFieldCondition())
->setTestValue($condition->getValue());
$this->transcript->addConditionTranscript($xscript);
return $xscript;
}
private function newApplyTranscript(
HeraldAdapter $adapter,
HeraldRule $rule,
HeraldActionRecord $action) {
$effect = id(new HeraldEffect())
->setObjectPHID($adapter->getPHID())
->setAction($action->getAction())
->setTarget($action->getTarget())
->setRule($rule);
$xscript = new HeraldApplyTranscript($effect, false);
$this->transcript->addApplyTranscript($xscript);
return $xscript;
}
private function isForbidden(
HeraldRule $rule,
HeraldAdapter $adapter) {
$forbidden = $adapter->getForbiddenActions();
if (!$forbidden) {
return false;
}
$forbidden = array_fuse($forbidden);
$is_forbidden = false;
foreach ($rule->getConditions() as $condition) {
$field_key = $condition->getFieldName();
if (!isset($this->forbiddenFields[$field_key])) {
$reason = null;
try {
$states = $adapter->getRequiredFieldStates($field_key);
} catch (Exception $ex) {
$states = array();
}
foreach ($states as $state) {
if (!isset($forbidden[$state])) {
continue;
}
$reason = $adapter->getForbiddenReason($state);
break;
}
$this->forbiddenFields[$field_key] = $reason;
}
$forbidden_reason = $this->forbiddenFields[$field_key];
if ($forbidden_reason !== null) {
$this->newConditionTranscript($rule, $condition)
->setResult(HeraldConditionTranscript::RESULT_FORBIDDEN)
->setNote($forbidden_reason);
$is_forbidden = true;
}
}
foreach ($rule->getActions() as $action_record) {
$action_key = $action_record->getAction();
if (!isset($this->forbiddenActions[$action_key])) {
$reason = null;
try {
$states = $adapter->getRequiredActionStates($action_key);
} catch (Exception $ex) {
$states = array();
}
foreach ($states as $state) {
if (!isset($forbidden[$state])) {
continue;
}
$reason = $adapter->getForbiddenReason($state);
break;
}
$this->forbiddenActions[$action_key] = $reason;
}
$forbidden_reason = $this->forbiddenActions[$action_key];
if ($forbidden_reason !== null) {
$this->newApplyTranscript($adapter, $rule, $action_record)
->setAppliedReason(
array(
array(
'type' => HeraldAction::DO_STANDARD_FORBIDDEN,
'data' => $forbidden_reason,
),
));
$is_forbidden = true;
}
}
return $is_forbidden;
}
} }

View File

@@ -20,6 +20,10 @@ abstract class HeraldField extends Phobject {
return null; return null;
} }
public function getRequiredAdapterStates() {
return array();
}
protected function getHeraldFieldStandardType() { protected function getHeraldFieldStandardType() {
throw new PhutilMethodNotImplementedException(); throw new PhutilMethodNotImplementedException();
} }

View File

@@ -0,0 +1,7 @@
<?php
final class HeraldBuildableState extends HeraldState {
const STATECONST = 'buildable';
}

View File

@@ -0,0 +1,7 @@
<?php
final class HeraldMailableState extends HeraldState {
const STATECONST = 'mailable';
}

View File

@@ -0,0 +1,3 @@
<?php
abstract class HeraldState extends Phobject {}

View File

@@ -0,0 +1,26 @@
<?php
abstract class HeraldStateReasons extends Phobject {
abstract public function explainReason($reason);
final public static function getAllReasons() {
return id(new PhutilClassMapQuery())
->setAncestorClass(__CLASS__)
->execute();
}
final public static function getExplanation($reason) {
$reasons = self::getAllReasons();
foreach ($reasons as $reason_implementation) {
$explanation = $reason_implementation->explainReason($reason);
if ($explanation !== null) {
return $explanation;
}
}
return pht('Unknown reason ("%s").', $reason);
}
}

View File

@@ -10,6 +10,8 @@ final class HeraldConditionTranscript extends Phobject {
protected $note; protected $note;
protected $result; protected $result;
const RESULT_FORBIDDEN = 'forbidden';
public function setRuleID($rule_id) { public function setRuleID($rule_id) {
$this->ruleID = $rule_id; $this->ruleID = $rule_id;
return $this; return $this;
@@ -72,4 +74,9 @@ final class HeraldConditionTranscript extends Phobject {
public function getResult() { public function getResult() {
return $this->result; return $this->result;
} }
public function isForbidden() {
return ($this->getResult() === self::RESULT_FORBIDDEN);
}
} }

View File

@@ -9,6 +9,12 @@ final class HeraldRuleTranscript extends Phobject {
protected $ruleName; protected $ruleName;
protected $ruleOwner; protected $ruleOwner;
const RESULT_FORBIDDEN = 'forbidden';
public function isForbidden() {
return ($this->getResult() === self::RESULT_FORBIDDEN);
}
public function setResult($result) { public function setResult($result) {
$this->result = $result; $this->result = $result;
return $this; return $this;

View File

@@ -77,23 +77,12 @@ final class LegalpadDocumentQuery
return $this; return $this;
} }
public function newResultObject() {
return new LegalpadDocument();
}
protected function loadPage() { protected function loadPage() {
$table = new LegalpadDocument(); return $this->loadStandardPage($this->newResultObject());
$conn_r = $table->establishConnection('r');
$data = queryfx_all(
$conn_r,
'SELECT d.* FROM %T d %Q %Q %Q %Q %Q',
$table->getTableName(),
$this->buildJoinClause($conn_r),
$this->buildWhereClause($conn_r),
$this->buildGroupClause($conn_r),
$this->buildOrderClause($conn_r),
$this->buildLimitClause($conn_r));
$documents = $table->loadAllFromArray($data);
return $documents;
} }
protected function willFilterPage(array $documents) { protected function willFilterPage(array $documents) {
@@ -134,12 +123,12 @@ final class LegalpadDocumentQuery
return $documents; return $documents;
} }
protected function buildJoinClause(AphrontDatabaseConnection $conn_r) { protected function buildJoinClauseParts(AphrontDatabaseConnection $conn) {
$joins = array(); $joins = parent::buildJoinClauseParts($conn);
if ($this->contributorPHIDs !== null) { if ($this->contributorPHIDs !== null) {
$joins[] = qsprintf( $joins[] = qsprintf(
$conn_r, $conn,
'JOIN edge contributor ON contributor.src = d.phid 'JOIN edge contributor ON contributor.src = d.phid
AND contributor.type = %d', AND contributor.type = %d',
PhabricatorObjectHasContributorEdgeType::EDGECONST); PhabricatorObjectHasContributorEdgeType::EDGECONST);
@@ -147,79 +136,81 @@ final class LegalpadDocumentQuery
if ($this->signerPHIDs !== null) { if ($this->signerPHIDs !== null) {
$joins[] = qsprintf( $joins[] = qsprintf(
$conn_r, $conn,
'JOIN %T signer ON signer.documentPHID = d.phid 'JOIN %T signer ON signer.documentPHID = d.phid
AND signer.signerPHID IN (%Ls)', AND signer.signerPHID IN (%Ls)',
id(new LegalpadDocumentSignature())->getTableName(), id(new LegalpadDocumentSignature())->getTableName(),
$this->signerPHIDs); $this->signerPHIDs);
} }
return implode(' ', $joins); return $joins;
} }
protected function buildGroupClause(AphrontDatabaseConnection $conn_r) { protected function shouldGroupQueryResultRows() {
if ($this->contributorPHIDs || $this->signerPHIDs) { if ($this->contributorPHIDs) {
return 'GROUP BY d.id'; return true;
} else {
return '';
}
} }
protected function buildWhereClause(AphrontDatabaseConnection $conn_r) { if ($this->signerPHIDs) {
$where = array(); return true;
}
return parent::shouldGroupQueryResultRows();
}
protected function buildWhereClauseParts(AphrontDatabaseConnection $conn) {
$where = parent::buildWhereClauseParts($conn);
if ($this->ids !== null) { if ($this->ids !== null) {
$where[] = qsprintf( $where[] = qsprintf(
$conn_r, $conn,
'd.id IN (%Ld)', 'd.id IN (%Ld)',
$this->ids); $this->ids);
} }
if ($this->phids !== null) { if ($this->phids !== null) {
$where[] = qsprintf( $where[] = qsprintf(
$conn_r, $conn,
'd.phid IN (%Ls)', 'd.phid IN (%Ls)',
$this->phids); $this->phids);
} }
if ($this->creatorPHIDs !== null) { if ($this->creatorPHIDs !== null) {
$where[] = qsprintf( $where[] = qsprintf(
$conn_r, $conn,
'd.creatorPHID IN (%Ls)', 'd.creatorPHID IN (%Ls)',
$this->creatorPHIDs); $this->creatorPHIDs);
} }
if ($this->dateCreatedAfter !== null) { if ($this->dateCreatedAfter !== null) {
$where[] = qsprintf( $where[] = qsprintf(
$conn_r, $conn,
'd.dateCreated >= %d', 'd.dateCreated >= %d',
$this->dateCreatedAfter); $this->dateCreatedAfter);
} }
if ($this->dateCreatedBefore !== null) { if ($this->dateCreatedBefore !== null) {
$where[] = qsprintf( $where[] = qsprintf(
$conn_r, $conn,
'd.dateCreated <= %d', 'd.dateCreated <= %d',
$this->dateCreatedBefore); $this->dateCreatedBefore);
} }
if ($this->contributorPHIDs !== null) { if ($this->contributorPHIDs !== null) {
$where[] = qsprintf( $where[] = qsprintf(
$conn_r, $conn,
'contributor.dst IN (%Ls)', 'contributor.dst IN (%Ls)',
$this->contributorPHIDs); $this->contributorPHIDs);
} }
if ($this->signatureRequired !== null) { if ($this->signatureRequired !== null) {
$where[] = qsprintf( $where[] = qsprintf(
$conn_r, $conn,
'd.requireSignature = %d', 'd.requireSignature = %d',
$this->signatureRequired); $this->signatureRequired);
} }
$where[] = $this->buildPagingClause($conn_r); return $where;
return $this->formatWhereClause($where);
} }
private function loadDocumentBodies(array $documents) { private function loadDocumentBodies(array $documents) {
@@ -275,4 +266,8 @@ final class LegalpadDocumentQuery
return 'PhabricatorLegalpadApplication'; return 'PhabricatorLegalpadApplication';
} }
protected function getPrimaryTableAlias() {
return 'd';
}
} }

View File

@@ -11,105 +11,66 @@ final class LegalpadDocumentSearchEngine
return 'PhabricatorLegalpadApplication'; return 'PhabricatorLegalpadApplication';
} }
public function buildSavedQueryFromRequest(AphrontRequest $request) { public function newQuery() {
$saved = new PhabricatorSavedQuery(); return id(new LegalpadDocumentQuery())
$saved->setParameter(
'creatorPHIDs',
$this->readUsersFromRequest($request, 'creators'));
$saved->setParameter(
'contributorPHIDs',
$this->readUsersFromRequest($request, 'contributors'));
$saved->setParameter(
'withViewerSignature',
$request->getBool('withViewerSignature'));
$saved->setParameter('createdStart', $request->getStr('createdStart'));
$saved->setParameter('createdEnd', $request->getStr('createdEnd'));
return $saved;
}
public function buildQueryFromSavedQuery(PhabricatorSavedQuery $saved) {
$query = id(new LegalpadDocumentQuery())
->needViewerSignatures(true); ->needViewerSignatures(true);
$creator_phids = $saved->getParameter('creatorPHIDs', array());
if ($creator_phids) {
$query->withCreatorPHIDs($creator_phids);
} }
$contributor_phids = $saved->getParameter('contributorPHIDs', array()); protected function buildCustomSearchFields() {
if ($contributor_phids) { return array(
$query->withContributorPHIDs($contributor_phids); id(new PhabricatorUsersSearchField())
->setLabel(pht('Signed By'))
->setKey('signerPHIDs')
->setAliases(array('signer', 'signers', 'signerPHID'))
->setDescription(
pht('Search for documents signed by given users.')),
id(new PhabricatorUsersSearchField())
->setLabel(pht('Creators'))
->setKey('creatorPHIDs')
->setAliases(array('creator', 'creators', 'creatorPHID'))
->setDescription(
pht('Search for documents with given creators.')),
id(new PhabricatorUsersSearchField())
->setLabel(pht('Contributors'))
->setKey('contributorPHIDs')
->setAliases(array('contributor', 'contributors', 'contributorPHID'))
->setDescription(
pht('Search for documents with given contributors.')),
id(new PhabricatorSearchDateField())
->setLabel(pht('Created After'))
->setKey('createdStart'),
id(new PhabricatorSearchDateField())
->setLabel(pht('Created Before'))
->setKey('createdEnd'),
);
} }
if ($saved->getParameter('withViewerSignature')) { protected function buildQueryFromParameters(array $map) {
$viewer_phid = $this->requireViewer()->getPHID(); $query = $this->newQuery();
if ($viewer_phid) {
$query->withSignerPHIDs(array($viewer_phid)); if ($map['signerPHIDs']) {
} $query->withSignerPHIDs($map['signerPHIDs']);
} }
$start = $this->parseDateTime($saved->getParameter('createdStart')); if ($map['contributorPHIDs']) {
$end = $this->parseDateTime($saved->getParameter('createdEnd')); $query->withContributorPHIDs($map['creatorPHIDs']);
if ($start) {
$query->withDateCreatedAfter($start);
} }
if ($end) { if ($map['creatorPHIDs']) {
$query->withDateCreatedBefore($end); $query->withCreatorPHIDs($map['creatorPHIDs']);
}
if ($map['createdStart']) {
$query->withDateCreatedAfter($map['createdStart']);
}
if ($map['createdEnd']) {
$query->withDateCreatedAfter($map['createdStart']);
} }
return $query; return $query;
} }
public function buildSearchForm(
AphrontFormView $form,
PhabricatorSavedQuery $saved_query) {
$creator_phids = $saved_query->getParameter('creatorPHIDs', array());
$contributor_phids = $saved_query->getParameter(
'contributorPHIDs', array());
$viewer_signature = $saved_query->getParameter('withViewerSignature');
if (!$this->requireViewer()->getPHID()) {
$viewer_signature = false;
}
$form
->appendChild(
id(new AphrontFormCheckboxControl())
->addCheckbox(
'withViewerSignature',
1,
pht('Show only documents I have signed.'),
$viewer_signature)
->setDisabled(!$this->requireViewer()->getPHID()))
->appendControl(
id(new AphrontFormTokenizerControl())
->setDatasource(new PhabricatorPeopleDatasource())
->setName('creators')
->setLabel(pht('Creators'))
->setValue($creator_phids))
->appendControl(
id(new AphrontFormTokenizerControl())
->setDatasource(new PhabricatorPeopleDatasource())
->setName('contributors')
->setLabel(pht('Contributors'))
->setValue($contributor_phids));
$this->buildDateRange(
$form,
$saved_query,
'createdStart',
pht('Created After'),
'createdEnd',
pht('Created Before'));
}
protected function getURI($path) { protected function getURI($path) {
return '/legalpad/'.$path; return '/legalpad/'.$path;
} }
@@ -130,10 +91,11 @@ final class LegalpadDocumentSearchEngine
$query = $this->newSavedQuery(); $query = $this->newSavedQuery();
$query->setQueryKey($query_key); $query->setQueryKey($query_key);
$viewer = $this->requireViewer();
switch ($query_key) { switch ($query_key) {
case 'signed': case 'signed':
return $query return $query->setParameter('signerPHIDs', array($viewer->getPHID()));
->setParameter('withViewerSignature', true);
case 'all': case 'all':
return $query; return $query;
} }
@@ -141,12 +103,6 @@ final class LegalpadDocumentSearchEngine
return parent::buildSavedQueryFromBuiltin($query_key); return parent::buildSavedQueryFromBuiltin($query_key);
} }
protected function getRequiredHandlePHIDsForResultList(
array $documents,
PhabricatorSavedQuery $query) {
return array();
}
protected function renderResultList( protected function renderResultList(
array $documents, array $documents,
PhabricatorSavedQuery $query, PhabricatorSavedQuery $query,

View File

@@ -21,6 +21,16 @@ final class ManiphestGetTaskTransactionsConduitAPIMethod
return 'nonempty list<dict<string, wild>>'; return 'nonempty list<dict<string, wild>>';
} }
public function getMethodStatus() {
return self::METHOD_STATUS_FROZEN;
}
public function getMethodStatusDescription() {
return pht(
'This method is frozen and will eventually be deprecated. New code '.
'should use "transaction.search" instead.');
}
protected function execute(ConduitAPIRequest $request) { protected function execute(ConduitAPIRequest $request) {
$results = array(); $results = array();
$task_ids = $request->getValue('ids'); $task_ids = $request->getValue('ids');

View File

@@ -57,5 +57,15 @@ final class ManiphestTaskDescriptionTransaction
return $changes; return $changes;
} }
public function getTransactionTypeForConduit($xaction) {
return 'description';
}
public function getFieldValuesForConduit($xaction, $data) {
return array(
'old' => $xaction->getOldValue(),
'new' => $xaction->getNewValue(),
);
}
} }

View File

@@ -154,5 +154,15 @@ final class ManiphestTaskOwnerTransaction
} }
public function getTransactionTypeForConduit($xaction) {
return 'owner';
}
public function getFieldValuesForConduit($xaction, $data) {
return array(
'old' => $xaction->getOldValue(),
'new' => $xaction->getNewValue(),
);
}
} }

View File

@@ -107,4 +107,16 @@ final class ManiphestTaskPointsTransaction
return $value; return $value;
} }
public function getTransactionTypeForConduit($xaction) {
return 'points';
}
public function getFieldValuesForConduit($xaction, $data) {
return array(
'old' => $xaction->getOldValue(),
'new' => $xaction->getNewValue(),
);
}
} }

View File

@@ -6,10 +6,7 @@ final class ManiphestTaskPriorityTransaction
const TRANSACTIONTYPE = 'priority'; const TRANSACTIONTYPE = 'priority';
public function generateOldValue($object) { public function generateOldValue($object) {
if ($this->isNewObject()) { return (string)$object->getPriority();
return null;
}
return $object->getPriority();
} }
public function generateNewValue($object, $value) { public function generateNewValue($object, $value) {
@@ -19,7 +16,7 @@ final class ManiphestTaskPriorityTransaction
// should still be allowed, even if the priority is no longer // should still be allowed, even if the priority is no longer
// valid, so treat this as a no-op. // valid, so treat this as a no-op.
if ($value === ManiphestTaskPriority::UNKNOWN_PRIORITY_KEYWORD) { if ($value === ManiphestTaskPriority::UNKNOWN_PRIORITY_KEYWORD) {
return $object->getPriority(); return (string)$object->getPriority();
} }
return (string)ManiphestTaskPriority::getTaskPriorityFromKeyword($value); return (string)ManiphestTaskPriority::getTaskPriorityFromKeyword($value);

View File

@@ -6,9 +6,6 @@ final class ManiphestTaskStatusTransaction
const TRANSACTIONTYPE = 'status'; const TRANSACTIONTYPE = 'status';
public function generateOldValue($object) { public function generateOldValue($object) {
if ($this->isNewObject()) {
return null;
}
return $object->getStatus(); return $object->getStatus();
} }
@@ -229,4 +226,15 @@ final class ManiphestTaskStatusTransaction
} }
public function getTransactionTypeForConduit($xaction) {
return 'status';
}
public function getFieldValuesForConduit($xaction, $data) {
return array(
'old' => $xaction->getOldValue(),
'new' => $xaction->getNewValue(),
);
}
} }

View File

@@ -72,4 +72,15 @@ final class ManiphestTaskTitleTransaction
return $errors; return $errors;
} }
public function getTransactionTypeForConduit($xaction) {
return 'title';
}
public function getFieldValuesForConduit($xaction, $data) {
return array(
'old' => $xaction->getOldValue(),
'new' => $xaction->getNewValue(),
);
}
} }

View File

@@ -0,0 +1,11 @@
<?php
final class PhabricatorQuickSearchApplicationEngineExtension
extends PhabricatorQuickSearchEngineExtension {
public function newQuickSearchDatasources() {
return array(
new PhabricatorApplicationDatasource(),
);
}
}

View File

@@ -6,6 +6,12 @@ abstract class PhabricatorMetaMTAEmailHeraldAction
const DO_SEND = 'do.send'; const DO_SEND = 'do.send';
const DO_FORCE = 'do.force'; const DO_FORCE = 'do.force';
public function getRequiredAdapterStates() {
return array(
HeraldMailableState::STATECONST,
);
}
public function supportsObject($object) { public function supportsObject($object) {
// NOTE: This implementation lacks generality, but there's no great way to // NOTE: This implementation lacks generality, but there's no great way to
// figure out if something generates email right now. // figure out if something generates email right now.

View File

@@ -15,4 +15,8 @@ final class PhabricatorOwnersPackageTransaction
return 'PhabricatorOwnersPackageTransactionType'; return 'PhabricatorOwnersPackageTransactionType';
} }
public function getApplicationTransactionCommentObject() {
return null;
}
} }

View File

@@ -9,6 +9,10 @@ final class PeopleMainMenuBarExtension
return $viewer->isLoggedIn(); return $viewer->isLoggedIn();
} }
public function shouldRequireFullSession() {
return false;
}
public function getExtensionOrder() { public function getExtensionOrder() {
return 1200; return 1200;
} }
@@ -65,6 +69,7 @@ final class PeopleMainMenuBarExtension
$view = id(new PhabricatorActionListView()) $view = id(new PhabricatorActionListView())
->setViewer($viewer); ->setViewer($viewer);
if ($this->getIsFullSession()) {
$view->addAction( $view->addAction(
id(new PhabricatorActionView()) id(new PhabricatorActionView())
->appendChild($user_view)); ->appendChild($user_view));
@@ -101,6 +106,7 @@ final class PeopleMainMenuBarExtension
id(new PhabricatorActionView()) id(new PhabricatorActionView())
->addSigil('logout-item') ->addSigil('logout-item')
->setType(PhabricatorActionView::TYPE_DIVIDER)); ->setType(PhabricatorActionView::TYPE_DIVIDER));
}
$view->addAction( $view->addAction(
id(new PhabricatorActionView()) id(new PhabricatorActionView())

View File

@@ -0,0 +1,11 @@
<?php
final class PhabricatorPeopleQuickSearchEngineExtension
extends PhabricatorQuickSearchEngineExtension {
public function newQuickSearchDatasources() {
return array(
new PhabricatorPeopleDatasource(),
);
}
}

View File

@@ -15,6 +15,10 @@ final class PhameBlogTransaction
return PhabricatorPhameBlogPHIDType::TYPECONST; return PhabricatorPhameBlogPHIDType::TYPECONST;
} }
public function getApplicationTransactionCommentObject() {
return null;
}
public function getBaseTransactionClass() { public function getBaseTransactionClass() {
return 'PhameBlogTransactionType'; return 'PhameBlogTransactionType';
} }

View File

@@ -0,0 +1,18 @@
<?php
final class PonderQuestionFerretEngine
extends PhabricatorFerretEngine {
public function getApplicationName() {
return 'ponder';
}
public function getScopeName() {
return 'question';
}
public function newSearchEngine() {
return new PonderQuestionSearchEngine();
}
}

View File

@@ -11,7 +11,8 @@ final class PonderQuestion extends PonderDAO
PhabricatorProjectInterface, PhabricatorProjectInterface,
PhabricatorDestructibleInterface, PhabricatorDestructibleInterface,
PhabricatorSpacesInterface, PhabricatorSpacesInterface,
PhabricatorFulltextInterface { PhabricatorFulltextInterface,
PhabricatorFerretInterface {
const MARKUP_FIELD_CONTENT = 'markup:content'; const MARKUP_FIELD_CONTENT = 'markup:content';
@@ -300,4 +301,13 @@ final class PonderQuestion extends PonderDAO
return new PonderQuestionFulltextEngine(); return new PonderQuestionFulltextEngine();
} }
/* -( PhabricatorFerretInterface )----------------------------------------- */
public function newFerretEngine() {
return new PonderQuestionFerretEngine();
}
} }

View File

@@ -146,8 +146,7 @@ final class PhabricatorProjectCoreTestCase extends PhabricatorTestCase {
$user = $this->createUser(); $user = $this->createUser();
$user->save(); $user->save();
$user2 = $this->createUser(); $user->setAllowInlineCacheGeneration(true);
$user2->save();
$proj = $this->createProject($user); $proj = $this->createProject($user);
@@ -1289,12 +1288,19 @@ final class PhabricatorProjectCoreTestCase extends PhabricatorTestCase {
$new_name = $proj->getName().' '.mt_rand(); $new_name = $proj->getName().' '.mt_rand();
$xaction = new PhabricatorProjectTransaction(); $params = array(
$xaction->setTransactionType( 'objectIdentifier' => $proj->getID(),
PhabricatorProjectNameTransaction::TRANSACTIONTYPE); 'transactions' => array(
$xaction->setNewValue($new_name); array(
'type' => 'name',
'value' => $new_name,
),
),
);
$this->applyTransactions($proj, $user, array($xaction)); id(new ConduitCall('project.edit', $params))
->setUser($user)
->execute();
return true; return true;
} }

View File

@@ -120,16 +120,6 @@ final class PhabricatorProjectTransactionEditor
PhabricatorApplicationTransaction $xaction) { PhabricatorApplicationTransaction $xaction) {
switch ($xaction->getTransactionType()) { switch ($xaction->getTransactionType()) {
case PhabricatorProjectNameTransaction::TRANSACTIONTYPE:
case PhabricatorProjectStatusTransaction::TRANSACTIONTYPE:
case PhabricatorProjectImageTransaction::TRANSACTIONTYPE:
case PhabricatorProjectIconTransaction::TRANSACTIONTYPE:
case PhabricatorProjectColorTransaction::TRANSACTIONTYPE:
PhabricatorPolicyFilter::requireCapability(
$this->requireActor(),
$object,
PhabricatorPolicyCapability::CAN_EDIT);
return;
case PhabricatorProjectLockTransaction::TRANSACTIONTYPE: case PhabricatorProjectLockTransaction::TRANSACTIONTYPE:
PhabricatorPolicyFilter::requireCapability( PhabricatorPolicyFilter::requireCapability(
$this->requireActor(), $this->requireActor(),

View File

@@ -67,7 +67,9 @@ final class PhabricatorBoardRenderingEngine extends Phobject {
$project_phids = $object->getProjectPHIDs(); $project_phids = $object->getProjectPHIDs();
$project_handles = array_select_keys($this->handles, $project_phids); $project_handles = array_select_keys($this->handles, $project_phids);
if ($project_handles) { if ($project_handles) {
$card->setProjectHandles($project_handles); $card
->setHideArchivedProjects(true)
->setProjectHandles($project_handles);
} }
$cover_phid = $object->getCoverImageThumbnailPHID(); $cover_phid = $object->getCoverImageThumbnailPHID();

View File

@@ -0,0 +1,11 @@
<?php
final class ProjectQuickSearchEngineExtension
extends PhabricatorQuickSearchEngineExtension {
public function newQuickSearchDatasources() {
return array(
new PhabricatorProjectDatasource(),
);
}
}

View File

@@ -8,8 +8,16 @@ final class PhabricatorProjectUIEventListener
} }
public function handleEvent(PhutilEvent $event) { public function handleEvent(PhutilEvent $event) {
$object = $event->getValue('object');
switch ($event->getType()) { switch ($event->getType()) {
case PhabricatorEventType::TYPE_UI_WILLRENDERPROPERTIES: case PhabricatorEventType::TYPE_UI_WILLRENDERPROPERTIES:
// Hacky solution so that property list view on Diffusion
// commits shows build status, but not Projects, Subscriptions,
// or Tokens.
if ($object instanceof PhabricatorRepositoryCommit) {
return;
}
$this->handlePropertyEvent($event); $this->handlePropertyEvent($event);
break; break;
} }

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