Merge branch 'master' into blender-tweaks
This commit is contained in:
@@ -9,11 +9,11 @@ return array(
|
||||
'names' => array(
|
||||
'conpherence.pkg.css' => 'e68cf1fa',
|
||||
'conpherence.pkg.js' => '15191c65',
|
||||
'core.pkg.css' => '16369829',
|
||||
'core.pkg.css' => '8ccb0ba3',
|
||||
'core.pkg.js' => '4c79d74f',
|
||||
'darkconsole.pkg.js' => '1f9a31bc',
|
||||
'differential.pkg.css' => '45951e9e',
|
||||
'differential.pkg.js' => 'b71b8c5d',
|
||||
'differential.pkg.js' => '500a75c5',
|
||||
'diffusion.pkg.css' => 'a2d17c7d',
|
||||
'diffusion.pkg.js' => '6134c5a1',
|
||||
'favicon.ico' => '4d48ee79',
|
||||
@@ -73,7 +73,7 @@ return array(
|
||||
'rsrc/css/application/diffusion/diffusion-icons.css' => '0c15255e',
|
||||
'rsrc/css/application/diffusion/diffusion-readme.css' => '419dd5b6',
|
||||
'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/feed/feed.css' => 'ecd4ec57',
|
||||
'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-status.css' => 'd5263e49',
|
||||
'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/workboards/phui-workboard-color.css' => '783cdff5',
|
||||
'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-tab-panel.js' => 'd4eecc63',
|
||||
'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/behavior-preview-link.js' => '051c7832',
|
||||
'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-request-state-change.js' => 'a0b57eb8',
|
||||
'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-queries.js' => 'e9581f08',
|
||||
'rsrc/js/application/slowvote/behavior-slowvote-embed.js' => '887ad43f',
|
||||
@@ -574,7 +574,7 @@ return array(
|
||||
'diffusion-icons-css' => '0c15255e',
|
||||
'diffusion-readme-css' => '419dd5b6',
|
||||
'diffusion-repository-css' => 'ee6f20ec',
|
||||
'diffusion-source-css' => '69ac9399',
|
||||
'diffusion-source-css' => '5f35a3bd',
|
||||
'diviner-shared-css' => '896f1d43',
|
||||
'font-fontawesome' => 'e838e088',
|
||||
'font-lato' => 'c7ccd872',
|
||||
@@ -692,7 +692,7 @@ return array(
|
||||
'javelin-behavior-reorder-applications' => '76b9fc3e',
|
||||
'javelin-behavior-reorder-columns' => 'e1d25dfb',
|
||||
'javelin-behavior-reorder-profile-menu-items' => 'e2e0a072',
|
||||
'javelin-behavior-repository-crossreference' => 'e5339c43',
|
||||
'javelin-behavior-repository-crossreference' => '7fe9bc12',
|
||||
'javelin-behavior-scrollbar' => '834a1173',
|
||||
'javelin-behavior-search-reorder-queries' => 'e9581f08',
|
||||
'javelin-behavior-select-content' => 'bf5374ef',
|
||||
@@ -777,7 +777,7 @@ return array(
|
||||
'phabricator-darkmessage' => 'c48cccdd',
|
||||
'phabricator-dashboard-css' => 'fe5b1869',
|
||||
'phabricator-diff-changeset' => '99abf4cd',
|
||||
'phabricator-diff-changeset-list' => '8f1cd52c',
|
||||
'phabricator-diff-changeset-list' => '3b77efdd',
|
||||
'phabricator-diff-inline' => 'e83d28f3',
|
||||
'phabricator-drag-and-drop-file-upload' => '58dea2fa',
|
||||
'phabricator-draggable-list' => 'bea6e7f4',
|
||||
@@ -874,7 +874,7 @@ return array(
|
||||
'phui-status-list-view-css' => 'd5263e49',
|
||||
'phui-tag-view-css' => 'b4719c50',
|
||||
'phui-theme-css' => '9f261c6b',
|
||||
'phui-timeline-view-css' => 'f21db7ca',
|
||||
'phui-timeline-view-css' => 'e2ef62b1',
|
||||
'phui-two-column-view-css' => '44ec4951',
|
||||
'phui-workboard-color-css' => '783cdff5',
|
||||
'phui-workboard-view-css' => '3bc85455',
|
||||
@@ -1137,6 +1137,10 @@ return array(
|
||||
'javelin-dom',
|
||||
'javelin-magical-init',
|
||||
),
|
||||
'3b77efdd' => array(
|
||||
'javelin-install',
|
||||
'phuix-button-view',
|
||||
),
|
||||
'3cb0b2fc' => array(
|
||||
'javelin-behavior',
|
||||
'javelin-dom',
|
||||
@@ -1549,6 +1553,12 @@ return array(
|
||||
'7f243deb' => array(
|
||||
'javelin-install',
|
||||
),
|
||||
'7fe9bc12' => array(
|
||||
'javelin-behavior',
|
||||
'javelin-dom',
|
||||
'javelin-stratcom',
|
||||
'javelin-uri',
|
||||
),
|
||||
'834a1173' => array(
|
||||
'javelin-behavior',
|
||||
'javelin-scrollbar',
|
||||
@@ -1607,10 +1617,6 @@ return array(
|
||||
'8e1baf68' => array(
|
||||
'phui-button-css',
|
||||
),
|
||||
'8f1cd52c' => array(
|
||||
'javelin-install',
|
||||
'phuix-button-view',
|
||||
),
|
||||
'8f29b364' => array(
|
||||
'javelin-behavior',
|
||||
'javelin-stratcom',
|
||||
@@ -2076,12 +2082,6 @@ return array(
|
||||
'javelin-behavior',
|
||||
'javelin-dom',
|
||||
),
|
||||
'e5339c43' => array(
|
||||
'javelin-behavior',
|
||||
'javelin-dom',
|
||||
'javelin-stratcom',
|
||||
'javelin-uri',
|
||||
),
|
||||
'e5822781' => array(
|
||||
'javelin-behavior',
|
||||
'javelin-dom',
|
||||
|
||||
@@ -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};
|
||||
@@ -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};
|
||||
@@ -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};
|
||||
@@ -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};
|
||||
@@ -0,0 +1,11 @@
|
||||
<?php
|
||||
|
||||
$table = new PonderQuestion();
|
||||
|
||||
foreach (new LiskMigrationIterator($table) as $question) {
|
||||
PhabricatorSearchWorker::queueDocumentForIndexing(
|
||||
$question->getPHID(),
|
||||
array(
|
||||
'force' => true,
|
||||
));
|
||||
}
|
||||
2
resources/sql/autopatches/20171101.diff.01.active.sql
Normal file
2
resources/sql/autopatches/20171101.diff.01.active.sql
Normal file
@@ -0,0 +1,2 @@
|
||||
ALTER TABLE {$NAMESPACE}_differential.differential_revision
|
||||
ADD activeDiffPHID VARBINARY(64) NOT NULL;
|
||||
24
resources/sql/autopatches/20171101.diff.02.populate.php
Normal file
24
resources/sql/autopatches/20171101.diff.02.populate.php
Normal 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);
|
||||
}
|
||||
}
|
||||
@@ -35,16 +35,23 @@ $args->parse(
|
||||
$parser = new MimeMailParser();
|
||||
$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');
|
||||
$content_type = idx($text_body_headers, 'content-type');
|
||||
if (
|
||||
!phutil_is_utf8($text_body) &&
|
||||
(preg_match('/charset="(.*?)"/', $content_type, $matches) ||
|
||||
preg_match('/charset=(\S+)/', $content_type, $matches))
|
||||
) {
|
||||
$text_body = phutil_utf8_convert($text_body, 'UTF-8', $matches[1]);
|
||||
if (strlen($part_body) && !phutil_is_utf8($part_body)) {
|
||||
$part_headers = $parser->getMessageBodyHeaders($part);
|
||||
if (!is_array($part_headers)) {
|
||||
$part_headers = array();
|
||||
}
|
||||
$content_type = idx($part_headers, 'content-type');
|
||||
if (preg_match('/charset="(.*?)"/', $content_type, $matches) ||
|
||||
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();
|
||||
@@ -57,10 +64,7 @@ if ($args->getArg('process-duplicates')) {
|
||||
|
||||
$received = new PhabricatorMetaMTAReceivedMail();
|
||||
$received->setHeaders($headers);
|
||||
$received->setBodies(array(
|
||||
'text' => $text_body,
|
||||
'html' => $parser->getMessageBody('html'),
|
||||
));
|
||||
$received->setBodies($content);
|
||||
|
||||
$attachments = array();
|
||||
foreach ($parser->getAttachments() as $attachment) {
|
||||
|
||||
@@ -439,11 +439,14 @@ phutil_register_library_map(array(
|
||||
'DifferentialDiffQuery' => 'applications/differential/query/DifferentialDiffQuery.php',
|
||||
'DifferentialDiffRepositoryHeraldField' => 'applications/differential/herald/DifferentialDiffRepositoryHeraldField.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',
|
||||
'DifferentialDiffTransaction' => 'applications/differential/storage/DifferentialDiffTransaction.php',
|
||||
'DifferentialDiffTransactionQuery' => 'applications/differential/query/DifferentialDiffTransactionQuery.php',
|
||||
'DifferentialDiffViewController' => 'applications/differential/controller/DifferentialDiffViewController.php',
|
||||
'DifferentialDoorkeeperRevisionFeedStoryPublisher' => 'applications/differential/doorkeeper/DifferentialDoorkeeperRevisionFeedStoryPublisher.php',
|
||||
'DifferentialDraftField' => 'applications/differential/customfield/DifferentialDraftField.php',
|
||||
'DifferentialExactUserFunctionDatasource' => 'applications/differential/typeahead/DifferentialExactUserFunctionDatasource.php',
|
||||
'DifferentialFieldParseException' => 'applications/differential/exception/DifferentialFieldParseException.php',
|
||||
'DifferentialFieldValidationException' => 'applications/differential/exception/DifferentialFieldValidationException.php',
|
||||
@@ -457,6 +460,7 @@ phutil_register_library_map(array(
|
||||
'DifferentialGetWorkingCopy' => 'applications/differential/DifferentialGetWorkingCopy.php',
|
||||
'DifferentialGitSVNIDCommitMessageField' => 'applications/differential/field/DifferentialGitSVNIDCommitMessageField.php',
|
||||
'DifferentialHarbormasterField' => 'applications/differential/customfield/DifferentialHarbormasterField.php',
|
||||
'DifferentialHeraldStateReasons' => 'applications/differential/herald/DifferentialHeraldStateReasons.php',
|
||||
'DifferentialHiddenComment' => 'applications/differential/storage/DifferentialHiddenComment.php',
|
||||
'DifferentialHostField' => 'applications/differential/customfield/DifferentialHostField.php',
|
||||
'DifferentialHovercardEngineExtension' => 'applications/differential/engineextension/DifferentialHovercardEngineExtension.php',
|
||||
@@ -546,6 +550,7 @@ phutil_register_library_map(array(
|
||||
'DifferentialRevisionHasTaskRelationship' => 'applications/differential/relationships/DifferentialRevisionHasTaskRelationship.php',
|
||||
'DifferentialRevisionHeraldField' => 'applications/differential/herald/DifferentialRevisionHeraldField.php',
|
||||
'DifferentialRevisionHeraldFieldGroup' => 'applications/differential/herald/DifferentialRevisionHeraldFieldGroup.php',
|
||||
'DifferentialRevisionHoldDraftTransaction' => 'applications/differential/xaction/DifferentialRevisionHoldDraftTransaction.php',
|
||||
'DifferentialRevisionIDCommitMessageField' => 'applications/differential/field/DifferentialRevisionIDCommitMessageField.php',
|
||||
'DifferentialRevisionInlineTransaction' => 'applications/differential/xaction/DifferentialRevisionInlineTransaction.php',
|
||||
'DifferentialRevisionInlinesController' => 'applications/differential/controller/DifferentialRevisionInlinesController.php',
|
||||
@@ -589,6 +594,7 @@ phutil_register_library_map(array(
|
||||
'DifferentialRevisionUpdateHistoryView' => 'applications/differential/view/DifferentialRevisionUpdateHistoryView.php',
|
||||
'DifferentialRevisionViewController' => 'applications/differential/controller/DifferentialRevisionViewController.php',
|
||||
'DifferentialRevisionVoidTransaction' => 'applications/differential/xaction/DifferentialRevisionVoidTransaction.php',
|
||||
'DifferentialRevisionWrongStateTransaction' => 'applications/differential/xaction/DifferentialRevisionWrongStateTransaction.php',
|
||||
'DifferentialSchemaSpec' => 'applications/differential/storage/DifferentialSchemaSpec.php',
|
||||
'DifferentialSetDiffPropertyConduitAPIMethod' => 'applications/differential/conduit/DifferentialSetDiffPropertyConduitAPIMethod.php',
|
||||
'DifferentialStoredCustomField' => 'applications/differential/customfield/DifferentialStoredCustomField.php',
|
||||
@@ -763,6 +769,7 @@ phutil_register_library_map(array(
|
||||
'DiffusionMercurialBlameQuery' => 'applications/diffusion/query/blame/DiffusionMercurialBlameQuery.php',
|
||||
'DiffusionMercurialCommandEngine' => 'applications/diffusion/protocol/DiffusionMercurialCommandEngine.php',
|
||||
'DiffusionMercurialFileContentQuery' => 'applications/diffusion/query/filecontent/DiffusionMercurialFileContentQuery.php',
|
||||
'DiffusionMercurialFlagInjectionException' => 'applications/diffusion/exception/DiffusionMercurialFlagInjectionException.php',
|
||||
'DiffusionMercurialRawDiffQuery' => 'applications/diffusion/query/rawdiff/DiffusionMercurialRawDiffQuery.php',
|
||||
'DiffusionMercurialRequest' => 'applications/diffusion/request/DiffusionMercurialRequest.php',
|
||||
'DiffusionMercurialResponse' => 'applications/diffusion/response/DiffusionMercurialResponse.php',
|
||||
@@ -825,6 +832,7 @@ phutil_register_library_map(array(
|
||||
'DiffusionQueryCommitsConduitAPIMethod' => 'applications/diffusion/conduit/DiffusionQueryCommitsConduitAPIMethod.php',
|
||||
'DiffusionQueryConduitAPIMethod' => 'applications/diffusion/conduit/DiffusionQueryConduitAPIMethod.php',
|
||||
'DiffusionQueryPathsConduitAPIMethod' => 'applications/diffusion/conduit/DiffusionQueryPathsConduitAPIMethod.php',
|
||||
'DiffusionQuickSearchEngineExtension' => 'applications/diffusion/engineextension/DiffusionQuickSearchEngineExtension.php',
|
||||
'DiffusionRawDiffQuery' => 'applications/diffusion/query/rawdiff/DiffusionRawDiffQuery.php',
|
||||
'DiffusionRawDiffQueryConduitAPIMethod' => 'applications/diffusion/conduit/DiffusionRawDiffQueryConduitAPIMethod.php',
|
||||
'DiffusionReadmeView' => 'applications/diffusion/view/DiffusionReadmeView.php',
|
||||
@@ -1327,6 +1335,7 @@ phutil_register_library_map(array(
|
||||
'HeraldApplicationActionGroup' => 'applications/herald/action/HeraldApplicationActionGroup.php',
|
||||
'HeraldApplyTranscript' => 'applications/herald/storage/transcript/HeraldApplyTranscript.php',
|
||||
'HeraldBasicFieldGroup' => 'applications/herald/field/HeraldBasicFieldGroup.php',
|
||||
'HeraldBuildableState' => 'applications/herald/state/HeraldBuildableState.php',
|
||||
'HeraldCommitAdapter' => 'applications/diffusion/herald/HeraldCommitAdapter.php',
|
||||
'HeraldCondition' => 'applications/herald/storage/HeraldCondition.php',
|
||||
'HeraldConditionTranscript' => 'applications/herald/storage/transcript/HeraldConditionTranscript.php',
|
||||
@@ -1350,6 +1359,7 @@ phutil_register_library_map(array(
|
||||
'HeraldGroup' => 'applications/herald/group/HeraldGroup.php',
|
||||
'HeraldInvalidActionException' => 'applications/herald/engine/exception/HeraldInvalidActionException.php',
|
||||
'HeraldInvalidConditionException' => 'applications/herald/engine/exception/HeraldInvalidConditionException.php',
|
||||
'HeraldMailableState' => 'applications/herald/state/HeraldMailableState.php',
|
||||
'HeraldManageGlobalRulesCapability' => 'applications/herald/capability/HeraldManageGlobalRulesCapability.php',
|
||||
'HeraldManiphestTaskAdapter' => 'applications/maniphest/herald/HeraldManiphestTaskAdapter.php',
|
||||
'HeraldNewController' => 'applications/herald/controller/HeraldNewController.php',
|
||||
@@ -1387,6 +1397,8 @@ phutil_register_library_map(array(
|
||||
'HeraldSchemaSpec' => 'applications/herald/storage/HeraldSchemaSpec.php',
|
||||
'HeraldSelectFieldValue' => 'applications/herald/value/HeraldSelectFieldValue.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',
|
||||
'HeraldSupportActionGroup' => 'applications/herald/action/HeraldSupportActionGroup.php',
|
||||
'HeraldSupportFieldGroup' => 'applications/herald/field/HeraldSupportFieldGroup.php',
|
||||
@@ -2529,6 +2541,8 @@ phutil_register_library_map(array(
|
||||
'PhabricatorCustomFieldEditField' => 'infrastructure/customfield/editor/PhabricatorCustomFieldEditField.php',
|
||||
'PhabricatorCustomFieldEditType' => 'infrastructure/customfield/editor/PhabricatorCustomFieldEditType.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',
|
||||
'PhabricatorCustomFieldHeraldFieldGroup' => 'infrastructure/customfield/herald/PhabricatorCustomFieldHeraldFieldGroup.php',
|
||||
'PhabricatorCustomFieldImplementationIncompleteException' => 'infrastructure/customfield/exception/PhabricatorCustomFieldImplementationIncompleteException.php',
|
||||
@@ -3194,6 +3208,7 @@ phutil_register_library_map(array(
|
||||
'PhabricatorMetronomicTriggerClock' => 'infrastructure/daemon/workers/clock/PhabricatorMetronomicTriggerClock.php',
|
||||
'PhabricatorModularTransaction' => 'applications/transactions/storage/PhabricatorModularTransaction.php',
|
||||
'PhabricatorModularTransactionType' => 'applications/transactions/storage/PhabricatorModularTransactionType.php',
|
||||
'PhabricatorMonogramQuickSearchEngineExtension' => 'applications/typeahead/engineextension/PhabricatorMonogramQuickSearchEngineExtension.php',
|
||||
'PhabricatorMonospacedFontSetting' => 'applications/settings/setting/PhabricatorMonospacedFontSetting.php',
|
||||
'PhabricatorMonospacedTextareasSetting' => 'applications/settings/setting/PhabricatorMonospacedTextareasSetting.php',
|
||||
'PhabricatorMotivatorProfileMenuItem' => 'applications/search/menuitem/PhabricatorMotivatorProfileMenuItem.php',
|
||||
@@ -3517,6 +3532,7 @@ phutil_register_library_map(array(
|
||||
'PhabricatorPeopleProfileTasksController' => 'applications/people/controller/PhabricatorPeopleProfileTasksController.php',
|
||||
'PhabricatorPeopleProfileViewController' => 'applications/people/controller/PhabricatorPeopleProfileViewController.php',
|
||||
'PhabricatorPeopleQuery' => 'applications/people/query/PhabricatorPeopleQuery.php',
|
||||
'PhabricatorPeopleQuickSearchEngineExtension' => 'applications/people/engineextension/PhabricatorPeopleQuickSearchEngineExtension.php',
|
||||
'PhabricatorPeopleRenameController' => 'applications/people/controller/PhabricatorPeopleRenameController.php',
|
||||
'PhabricatorPeopleRevisionsProfileMenuItem' => 'applications/people/menuitem/PhabricatorPeopleRevisionsProfileMenuItem.php',
|
||||
'PhabricatorPeopleSearchEngine' => 'applications/people/query/PhabricatorPeopleSearchEngine.php',
|
||||
@@ -3777,6 +3793,9 @@ phutil_register_library_map(array(
|
||||
'PhabricatorQueryOrderItem' => 'infrastructure/query/order/PhabricatorQueryOrderItem.php',
|
||||
'PhabricatorQueryOrderTestCase' => 'infrastructure/query/order/__tests__/PhabricatorQueryOrderTestCase.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',
|
||||
'PhabricatorRecaptchaConfigOptions' => 'applications/config/option/PhabricatorRecaptchaConfigOptions.php',
|
||||
'PhabricatorRedirectController' => 'applications/base/controller/PhabricatorRedirectController.php',
|
||||
@@ -4778,6 +4797,7 @@ phutil_register_library_map(array(
|
||||
'PonderQuestionEditController' => 'applications/ponder/controller/PonderQuestionEditController.php',
|
||||
'PonderQuestionEditEngine' => 'applications/ponder/editor/PonderQuestionEditEngine.php',
|
||||
'PonderQuestionEditor' => 'applications/ponder/editor/PonderQuestionEditor.php',
|
||||
'PonderQuestionFerretEngine' => 'applications/ponder/search/PonderQuestionFerretEngine.php',
|
||||
'PonderQuestionFulltextEngine' => 'applications/ponder/search/PonderQuestionFulltextEngine.php',
|
||||
'PonderQuestionHistoryController' => 'applications/ponder/controller/PonderQuestionHistoryController.php',
|
||||
'PonderQuestionListController' => 'applications/ponder/controller/PonderQuestionListController.php',
|
||||
@@ -4809,6 +4829,7 @@ phutil_register_library_map(array(
|
||||
'ProjectDefaultViewCapability' => 'applications/project/capability/ProjectDefaultViewCapability.php',
|
||||
'ProjectEditConduitAPIMethod' => 'applications/project/conduit/ProjectEditConduitAPIMethod.php',
|
||||
'ProjectQueryConduitAPIMethod' => 'applications/project/conduit/ProjectQueryConduitAPIMethod.php',
|
||||
'ProjectQuickSearchEngineExtension' => 'applications/project/engineextension/ProjectQuickSearchEngineExtension.php',
|
||||
'ProjectRemarkupRule' => 'applications/project/remarkup/ProjectRemarkupRule.php',
|
||||
'ProjectRemarkupRuleTestCase' => 'applications/project/remarkup/__tests__/ProjectRemarkupRuleTestCase.php',
|
||||
'ProjectReplyHandler' => 'applications/project/mail/ProjectReplyHandler.php',
|
||||
@@ -5433,6 +5454,7 @@ phutil_register_library_map(array(
|
||||
'HarbormasterBuildkiteBuildableInterface',
|
||||
'PhabricatorApplicationTransactionInterface',
|
||||
'PhabricatorDestructibleInterface',
|
||||
'PhabricatorConduitResultInterface',
|
||||
),
|
||||
'DifferentialDiffAffectedFilesHeraldField' => 'DifferentialDiffHeraldField',
|
||||
'DifferentialDiffAuthorHeraldField' => 'DifferentialDiffHeraldField',
|
||||
@@ -5451,11 +5473,14 @@ phutil_register_library_map(array(
|
||||
'DifferentialDiffQuery' => 'PhabricatorCursorPagedPolicyAwareQuery',
|
||||
'DifferentialDiffRepositoryHeraldField' => 'DifferentialDiffHeraldField',
|
||||
'DifferentialDiffRepositoryProjectsHeraldField' => 'DifferentialDiffHeraldField',
|
||||
'DifferentialDiffSearchConduitAPIMethod' => 'PhabricatorSearchEngineAPIMethod',
|
||||
'DifferentialDiffSearchEngine' => 'PhabricatorApplicationSearchEngine',
|
||||
'DifferentialDiffTestCase' => 'PhutilTestCase',
|
||||
'DifferentialDiffTransaction' => 'PhabricatorApplicationTransaction',
|
||||
'DifferentialDiffTransactionQuery' => 'PhabricatorApplicationTransactionQuery',
|
||||
'DifferentialDiffViewController' => 'DifferentialController',
|
||||
'DifferentialDoorkeeperRevisionFeedStoryPublisher' => 'DoorkeeperFeedStoryPublisher',
|
||||
'DifferentialDraftField' => 'DifferentialCoreCustomField',
|
||||
'DifferentialExactUserFunctionDatasource' => 'PhabricatorTypeaheadCompositeDatasource',
|
||||
'DifferentialFieldParseException' => 'Exception',
|
||||
'DifferentialFieldValidationException' => 'Exception',
|
||||
@@ -5469,6 +5494,7 @@ phutil_register_library_map(array(
|
||||
'DifferentialGetWorkingCopy' => 'Phobject',
|
||||
'DifferentialGitSVNIDCommitMessageField' => 'DifferentialCommitMessageField',
|
||||
'DifferentialHarbormasterField' => 'DifferentialCustomField',
|
||||
'DifferentialHeraldStateReasons' => 'HeraldStateReasons',
|
||||
'DifferentialHiddenComment' => 'DifferentialDAO',
|
||||
'DifferentialHostField' => 'DifferentialCustomField',
|
||||
'DifferentialHovercardEngineExtension' => 'PhabricatorHovercardEngineExtension',
|
||||
@@ -5583,6 +5609,7 @@ phutil_register_library_map(array(
|
||||
'DifferentialRevisionHasTaskRelationship' => 'DifferentialRevisionRelationship',
|
||||
'DifferentialRevisionHeraldField' => 'HeraldField',
|
||||
'DifferentialRevisionHeraldFieldGroup' => 'HeraldFieldGroup',
|
||||
'DifferentialRevisionHoldDraftTransaction' => 'DifferentialRevisionTransactionType',
|
||||
'DifferentialRevisionIDCommitMessageField' => 'DifferentialCommitMessageField',
|
||||
'DifferentialRevisionInlineTransaction' => 'PhabricatorModularTransactionType',
|
||||
'DifferentialRevisionInlinesController' => 'DifferentialController',
|
||||
@@ -5626,6 +5653,7 @@ phutil_register_library_map(array(
|
||||
'DifferentialRevisionUpdateHistoryView' => 'AphrontView',
|
||||
'DifferentialRevisionViewController' => 'DifferentialController',
|
||||
'DifferentialRevisionVoidTransaction' => 'DifferentialRevisionTransactionType',
|
||||
'DifferentialRevisionWrongStateTransaction' => 'DifferentialRevisionTransactionType',
|
||||
'DifferentialSchemaSpec' => 'PhabricatorConfigSchemaSpec',
|
||||
'DifferentialSetDiffPropertyConduitAPIMethod' => 'DifferentialConduitAPIMethod',
|
||||
'DifferentialStoredCustomField' => 'DifferentialCustomField',
|
||||
@@ -5803,6 +5831,7 @@ phutil_register_library_map(array(
|
||||
'DiffusionMercurialBlameQuery' => 'DiffusionBlameQuery',
|
||||
'DiffusionMercurialCommandEngine' => 'DiffusionCommandEngine',
|
||||
'DiffusionMercurialFileContentQuery' => 'DiffusionFileContentQuery',
|
||||
'DiffusionMercurialFlagInjectionException' => 'Exception',
|
||||
'DiffusionMercurialRawDiffQuery' => 'DiffusionRawDiffQuery',
|
||||
'DiffusionMercurialRequest' => 'DiffusionRequest',
|
||||
'DiffusionMercurialResponse' => 'AphrontResponse',
|
||||
@@ -5865,6 +5894,7 @@ phutil_register_library_map(array(
|
||||
'DiffusionQueryCommitsConduitAPIMethod' => 'DiffusionConduitAPIMethod',
|
||||
'DiffusionQueryConduitAPIMethod' => 'DiffusionConduitAPIMethod',
|
||||
'DiffusionQueryPathsConduitAPIMethod' => 'DiffusionQueryConduitAPIMethod',
|
||||
'DiffusionQuickSearchEngineExtension' => 'PhabricatorQuickSearchEngineExtension',
|
||||
'DiffusionRawDiffQuery' => 'DiffusionFileFutureQuery',
|
||||
'DiffusionRawDiffQueryConduitAPIMethod' => 'DiffusionQueryConduitAPIMethod',
|
||||
'DiffusionReadmeView' => 'DiffusionView',
|
||||
@@ -6460,6 +6490,7 @@ phutil_register_library_map(array(
|
||||
'HeraldApplicationActionGroup' => 'HeraldActionGroup',
|
||||
'HeraldApplyTranscript' => 'Phobject',
|
||||
'HeraldBasicFieldGroup' => 'HeraldFieldGroup',
|
||||
'HeraldBuildableState' => 'HeraldState',
|
||||
'HeraldCommitAdapter' => array(
|
||||
'HeraldAdapter',
|
||||
'HarbormasterBuildableAdapterInterface',
|
||||
@@ -6489,6 +6520,7 @@ phutil_register_library_map(array(
|
||||
'HeraldGroup' => 'Phobject',
|
||||
'HeraldInvalidActionException' => 'Exception',
|
||||
'HeraldInvalidConditionException' => 'Exception',
|
||||
'HeraldMailableState' => 'HeraldState',
|
||||
'HeraldManageGlobalRulesCapability' => 'PhabricatorPolicyCapability',
|
||||
'HeraldManiphestTaskAdapter' => 'HeraldAdapter',
|
||||
'HeraldNewController' => 'HeraldController',
|
||||
@@ -6533,6 +6565,8 @@ phutil_register_library_map(array(
|
||||
'HeraldSchemaSpec' => 'PhabricatorConfigSchemaSpec',
|
||||
'HeraldSelectFieldValue' => 'HeraldFieldValue',
|
||||
'HeraldSpaceField' => 'HeraldField',
|
||||
'HeraldState' => 'Phobject',
|
||||
'HeraldStateReasons' => 'Phobject',
|
||||
'HeraldSubscribersField' => 'HeraldField',
|
||||
'HeraldSupportActionGroup' => 'HeraldActionGroup',
|
||||
'HeraldSupportFieldGroup' => 'HeraldFieldGroup',
|
||||
@@ -7855,6 +7889,8 @@ phutil_register_library_map(array(
|
||||
'PhabricatorCustomFieldEditField' => 'PhabricatorEditField',
|
||||
'PhabricatorCustomFieldEditType' => 'PhabricatorEditType',
|
||||
'PhabricatorCustomFieldFulltextEngineExtension' => 'PhabricatorFulltextEngineExtension',
|
||||
'PhabricatorCustomFieldHeraldAction' => 'HeraldAction',
|
||||
'PhabricatorCustomFieldHeraldActionGroup' => 'HeraldActionGroup',
|
||||
'PhabricatorCustomFieldHeraldField' => 'HeraldField',
|
||||
'PhabricatorCustomFieldHeraldFieldGroup' => 'HeraldFieldGroup',
|
||||
'PhabricatorCustomFieldImplementationIncompleteException' => 'Exception',
|
||||
@@ -8595,6 +8631,7 @@ phutil_register_library_map(array(
|
||||
'PhabricatorMetronomicTriggerClock' => 'PhabricatorTriggerClock',
|
||||
'PhabricatorModularTransaction' => 'PhabricatorApplicationTransaction',
|
||||
'PhabricatorModularTransactionType' => 'Phobject',
|
||||
'PhabricatorMonogramQuickSearchEngineExtension' => 'PhabricatorQuickSearchEngineExtension',
|
||||
'PhabricatorMonospacedFontSetting' => 'PhabricatorStringSetting',
|
||||
'PhabricatorMonospacedTextareasSetting' => 'PhabricatorSelectSetting',
|
||||
'PhabricatorMotivatorProfileMenuItem' => 'PhabricatorProfileMenuItem',
|
||||
@@ -8987,6 +9024,7 @@ phutil_register_library_map(array(
|
||||
'PhabricatorPeopleProfileTasksController' => 'PhabricatorPeopleProfileController',
|
||||
'PhabricatorPeopleProfileViewController' => 'PhabricatorPeopleProfileController',
|
||||
'PhabricatorPeopleQuery' => 'PhabricatorCursorPagedPolicyAwareQuery',
|
||||
'PhabricatorPeopleQuickSearchEngineExtension' => 'PhabricatorQuickSearchEngineExtension',
|
||||
'PhabricatorPeopleRenameController' => 'PhabricatorPeopleController',
|
||||
'PhabricatorPeopleRevisionsProfileMenuItem' => 'PhabricatorProfileMenuItem',
|
||||
'PhabricatorPeopleSearchEngine' => 'PhabricatorApplicationSearchEngine',
|
||||
@@ -9302,6 +9340,9 @@ phutil_register_library_map(array(
|
||||
'Phobject',
|
||||
'Iterator',
|
||||
),
|
||||
'PhabricatorQuickSearchApplicationEngineExtension' => 'PhabricatorQuickSearchEngineExtension',
|
||||
'PhabricatorQuickSearchEngine' => 'Phobject',
|
||||
'PhabricatorQuickSearchEngineExtension' => 'Phobject',
|
||||
'PhabricatorRateLimitRequestExceptionHandler' => 'PhabricatorRequestExceptionHandler',
|
||||
'PhabricatorRecaptchaConfigOptions' => 'PhabricatorApplicationConfigOptions',
|
||||
'PhabricatorRedirectController' => 'PhabricatorController',
|
||||
@@ -10547,6 +10588,7 @@ phutil_register_library_map(array(
|
||||
'PhabricatorDestructibleInterface',
|
||||
'PhabricatorSpacesInterface',
|
||||
'PhabricatorFulltextInterface',
|
||||
'PhabricatorFerretInterface',
|
||||
),
|
||||
'PonderQuestionAnswerTransaction' => 'PonderQuestionTransactionType',
|
||||
'PonderQuestionAnswerWikiTransaction' => 'PonderQuestionTransactionType',
|
||||
@@ -10556,6 +10598,7 @@ phutil_register_library_map(array(
|
||||
'PonderQuestionEditController' => 'PonderController',
|
||||
'PonderQuestionEditEngine' => 'PhabricatorEditEngine',
|
||||
'PonderQuestionEditor' => 'PonderEditor',
|
||||
'PonderQuestionFerretEngine' => 'PhabricatorFerretEngine',
|
||||
'PonderQuestionFulltextEngine' => 'PhabricatorFulltextEngine',
|
||||
'PonderQuestionHistoryController' => 'PonderController',
|
||||
'PonderQuestionListController' => 'PonderController',
|
||||
@@ -10587,6 +10630,7 @@ phutil_register_library_map(array(
|
||||
'ProjectDefaultViewCapability' => 'PhabricatorPolicyCapability',
|
||||
'ProjectEditConduitAPIMethod' => 'PhabricatorEditEngineAPIMethod',
|
||||
'ProjectQueryConduitAPIMethod' => 'ProjectConduitAPIMethod',
|
||||
'ProjectQuickSearchEngineExtension' => 'PhabricatorQuickSearchEngineExtension',
|
||||
'ProjectRemarkupRule' => 'PhabricatorObjectRemarkupRule',
|
||||
'ProjectRemarkupRuleTestCase' => 'PhabricatorTestCase',
|
||||
'ProjectReplyHandler' => 'PhabricatorApplicationTransactionReplyHandler',
|
||||
|
||||
@@ -9,9 +9,27 @@ final class PhabricatorAuthNeedsMultiFactorController
|
||||
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) {
|
||||
$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())
|
||||
->setUser($viewer)
|
||||
->setViewer($viewer)
|
||||
|
||||
@@ -9,6 +9,10 @@ final class PhabricatorAuthMainMenuBarExtension
|
||||
return true;
|
||||
}
|
||||
|
||||
public function shouldRequireFullSession() {
|
||||
return false;
|
||||
}
|
||||
|
||||
public function getExtensionOrder() {
|
||||
return 900;
|
||||
}
|
||||
|
||||
@@ -137,10 +137,6 @@ abstract class PhabricatorController extends AphrontController {
|
||||
}
|
||||
|
||||
if ($this->shouldRequireEnabledUser()) {
|
||||
if ($user->isLoggedIn() && !$user->getIsApproved()) {
|
||||
$controller = new PhabricatorAuthNeedsApprovalController();
|
||||
return $this->delegateToController($controller);
|
||||
}
|
||||
if ($user->getIsDisabled()) {
|
||||
$controller = new PhabricatorDisabledUserController();
|
||||
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.
|
||||
$need_mfa = $this->shouldRequireMultiFactorEnrollment();
|
||||
$have_mfa = $user->getIsEnrolledInMultiFactor();
|
||||
@@ -224,46 +229,15 @@ abstract class PhabricatorController extends AphrontController {
|
||||
->withPHIDs(array($application->getPHID()))
|
||||
->executeOne();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if (!$this->shouldAllowLegallyNonCompliantUsers()) {
|
||||
$legalpad_class = 'PhabricatorLegalpadApplication';
|
||||
$legalpad = id(new PhabricatorApplicationQuery())
|
||||
->setViewer($user)
|
||||
->withClasses(array($legalpad_class))
|
||||
->withInstalled(true)
|
||||
->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);
|
||||
// If users need approval, require they wait here. We do this near the
|
||||
// end so they can take other actions (like verifying email, signing
|
||||
// documents, and enrolling in MFA) while waiting for an admin to take a
|
||||
// look at things. See T13024 for more discussion.
|
||||
if ($this->shouldRequireEnabledUser()) {
|
||||
if ($user->isLoggedIn() && !$user->getIsApproved()) {
|
||||
$controller = new PhabricatorAuthNeedsApprovalController();
|
||||
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();
|
||||
}
|
||||
|
||||
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 )--------------------------------------------------------- */
|
||||
|
||||
|
||||
@@ -159,10 +159,10 @@ final class PhabricatorAccessControlTestCase extends PhabricatorTestCase {
|
||||
$u_unverified,
|
||||
$u_admin,
|
||||
$u_public,
|
||||
$u_notapproved,
|
||||
),
|
||||
array(
|
||||
$u_disabled,
|
||||
$u_notapproved,
|
||||
));
|
||||
|
||||
|
||||
@@ -224,7 +224,7 @@ final class PhabricatorAccessControlTestCase extends PhabricatorTestCase {
|
||||
));
|
||||
|
||||
$this->checkAccess(
|
||||
pht('Application Controller'),
|
||||
pht('Application Controller, No Login Required'),
|
||||
id(clone $app_controller)->setConfig('login', false),
|
||||
$request,
|
||||
array(
|
||||
@@ -232,10 +232,10 @@ final class PhabricatorAccessControlTestCase extends PhabricatorTestCase {
|
||||
$u_unverified,
|
||||
$u_admin,
|
||||
$u_public,
|
||||
$u_notapproved,
|
||||
),
|
||||
array(
|
||||
$u_disabled,
|
||||
$u_notapproved,
|
||||
));
|
||||
}
|
||||
|
||||
|
||||
@@ -18,6 +18,9 @@ final class PhabricatorDaemonLogEvent extends PhabricatorDaemonDAO {
|
||||
'logID' => array(
|
||||
'columns' => array('logID', 'epoch'),
|
||||
),
|
||||
'key_epoch' => array(
|
||||
'columns' => array('epoch'),
|
||||
),
|
||||
),
|
||||
) + parent::getConfiguration();
|
||||
}
|
||||
|
||||
@@ -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.');
|
||||
}
|
||||
|
||||
}
|
||||
@@ -36,6 +36,7 @@ final class DifferentialParseCommitMessageConduitAPIMethod
|
||||
$field_map = $parser->parseFields($corpus);
|
||||
|
||||
$errors = $parser->getErrors();
|
||||
$xactions = $parser->getTransactions();
|
||||
|
||||
$revision_id_value = idx(
|
||||
$field_map,
|
||||
@@ -49,6 +50,7 @@ final class DifferentialParseCommitMessageConduitAPIMethod
|
||||
'value' => $revision_id_value,
|
||||
'validDomain' => $revision_id_valid_domain,
|
||||
),
|
||||
'transactions' => $xactions,
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -11,6 +11,16 @@ final class DifferentialQueryDiffsConduitAPIMethod
|
||||
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() {
|
||||
return array(
|
||||
'ids' => 'optional list<uint>',
|
||||
|
||||
@@ -86,6 +86,7 @@ EOHELP
|
||||
array(
|
||||
'/\.py$/',
|
||||
'/\.l?hs$/',
|
||||
'/\.ya?ml$/',
|
||||
))
|
||||
->setDescription(
|
||||
pht(
|
||||
|
||||
@@ -32,14 +32,7 @@ final class DifferentialLegacyQuery
|
||||
}
|
||||
|
||||
private static function getMap() {
|
||||
$all = array(
|
||||
DifferentialRevisionStatus::NEEDS_REVIEW,
|
||||
DifferentialRevisionStatus::NEEDS_REVISION,
|
||||
DifferentialRevisionStatus::CHANGES_PLANNED,
|
||||
DifferentialRevisionStatus::ACCEPTED,
|
||||
DifferentialRevisionStatus::PUBLISHED,
|
||||
DifferentialRevisionStatus::ABANDONED,
|
||||
);
|
||||
$all = array_keys(DifferentialRevisionStatus::getAll());
|
||||
|
||||
$open = array();
|
||||
$closed = array();
|
||||
@@ -61,6 +54,9 @@ final class DifferentialLegacyQuery
|
||||
),
|
||||
self::STATUS_NEEDS_REVIEW => array(
|
||||
DifferentialRevisionStatus::NEEDS_REVIEW,
|
||||
|
||||
// For legacy callers, "Draft" is treated as "Needs Review".
|
||||
DifferentialRevisionStatus::DRAFT,
|
||||
),
|
||||
self::STATUS_NEEDS_REVISION => array(
|
||||
DifferentialRevisionStatus::NEEDS_REVISION,
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -13,6 +13,20 @@ final class DifferentialRevisionDependedOnByRevisionEdgeType
|
||||
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(
|
||||
$actor,
|
||||
$add_count,
|
||||
|
||||
@@ -17,6 +17,19 @@ final class DifferentialRevisionDependsOnRevisionEdgeType
|
||||
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(
|
||||
$actor,
|
||||
$add_count,
|
||||
|
||||
@@ -235,6 +235,22 @@ final class DifferentialRevisionEditEngine
|
||||
$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;
|
||||
}
|
||||
|
||||
|
||||
@@ -9,6 +9,8 @@ final class DifferentialTransactionEditor
|
||||
private $didExpandInlineState = false;
|
||||
private $hasReviewTransaction = false;
|
||||
private $affectedPaths;
|
||||
private $firstBroadcast = false;
|
||||
private $wasDraft = false;
|
||||
|
||||
public function getEditorApplicationClass() {
|
||||
return 'PhabricatorDifferentialApplication';
|
||||
@@ -27,7 +29,7 @@ final class DifferentialTransactionEditor
|
||||
}
|
||||
|
||||
public function isFirstBroadcast() {
|
||||
return $this->getIsNewObject();
|
||||
return $this->firstBroadcast;
|
||||
}
|
||||
|
||||
public function getDiffUpdateTransaction(array $xactions) {
|
||||
@@ -137,8 +139,7 @@ final class DifferentialTransactionEditor
|
||||
$object->setRepositoryPHID($diff->getRepositoryPHID());
|
||||
}
|
||||
$object->attachActiveDiff($diff);
|
||||
|
||||
// TODO: Update the `diffPHID` once we add that.
|
||||
$object->setActiveDiffPHID($diff->getPHID());
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -166,6 +167,8 @@ final class DifferentialTransactionEditor
|
||||
}
|
||||
}
|
||||
|
||||
$this->wasDraft = $object->isDraft();
|
||||
|
||||
return parent::expandTransactions($object, $xactions);
|
||||
}
|
||||
|
||||
@@ -630,6 +633,10 @@ final class DifferentialTransactionEditor
|
||||
$phids = array();
|
||||
$phids[] = $object->getAuthorPHID();
|
||||
foreach ($object->getReviewers() as $reviewer) {
|
||||
if ($reviewer->isResigned()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$phids[] = $reviewer->getReviewerPHID();
|
||||
}
|
||||
return $phids;
|
||||
@@ -1003,28 +1010,9 @@ final class DifferentialTransactionEditor
|
||||
protected function shouldApplyHeraldRules(
|
||||
PhabricatorLiskDAO $object,
|
||||
array $xactions) {
|
||||
|
||||
if ($this->getIsNewObject()) {
|
||||
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(
|
||||
PhabricatorLiskDAO $object,
|
||||
HeraldAdapter $adapter,
|
||||
@@ -1211,6 +1199,49 @@ final class DifferentialTransactionEditor
|
||||
$revision,
|
||||
$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;
|
||||
}
|
||||
|
||||
@@ -1425,11 +1456,13 @@ final class DifferentialTransactionEditor
|
||||
protected function getCustomWorkerState() {
|
||||
return array(
|
||||
'changedPriorToCommitURI' => $this->changedPriorToCommitURI,
|
||||
'firstBroadcast' => $this->firstBroadcast,
|
||||
);
|
||||
}
|
||||
|
||||
protected function loadCustomWorkerState(array $state) {
|
||||
$this->changedPriorToCommitURI = idx($state, 'changedPriorToCommitURI');
|
||||
$this->firstBroadcast = idx($state, 'firstBroadcast');
|
||||
return $this;
|
||||
}
|
||||
|
||||
@@ -1532,19 +1565,31 @@ final class DifferentialTransactionEditor
|
||||
protected function didApplyTransactions($object, array $xactions) {
|
||||
// If a draft revision has no outstanding builds and we're automatically
|
||||
// making drafts public after builds finish, make the revision public.
|
||||
$auto_undraft = true;
|
||||
$auto_undraft = !$object->getHoldAsDraft();
|
||||
|
||||
if ($object->isDraft() && $auto_undraft) {
|
||||
$active_builds = $this->hasActiveBuilds($object);
|
||||
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()
|
||||
->setAuthorPHID($author_phid)
|
||||
->setTransactionType(
|
||||
DifferentialRevisionRequestReviewTransaction::TRANSACTIONTYPE)
|
||||
->setOldValue(false)
|
||||
->setNewValue(true);
|
||||
|
||||
$xaction = $this->populateTransaction($object, $xaction);
|
||||
|
||||
// If we're creating this revision and immediately moving it out of
|
||||
// the draft state, mark this as a create transaction so it gets
|
||||
// hidden in the timeline and mail, since it isn't interesting: it
|
||||
@@ -1553,15 +1598,36 @@ final class DifferentialTransactionEditor
|
||||
$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
|
||||
->setStatus(DifferentialRevisionStatus::NEEDS_REVIEW)
|
||||
->setHasBroadcast(true)
|
||||
->save();
|
||||
|
||||
$xaction->save();
|
||||
$object->saveTransaction();
|
||||
|
||||
$xactions[] = $xaction;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1570,58 +1636,12 @@ final class DifferentialTransactionEditor
|
||||
|
||||
private function hasActiveBuilds($object) {
|
||||
$viewer = $this->requireActor();
|
||||
$diff = $object->getActiveDiff();
|
||||
|
||||
$buildables = id(new HarbormasterBuildableQuery())
|
||||
->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();
|
||||
$builds = $object->loadActiveBuilds($viewer);
|
||||
if (!$builds) {
|
||||
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;
|
||||
}
|
||||
|
||||
|
||||
@@ -268,6 +268,16 @@ final class DifferentialDiffExtractionEngine extends Phobject {
|
||||
|
||||
$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())
|
||||
->setTransactionType(DifferentialTransaction::TYPE_UPDATE)
|
||||
->setIgnoreOnNoEffect(true)
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -28,6 +28,7 @@ final class DifferentialCommitMessageParser extends Phobject {
|
||||
private $errors;
|
||||
private $commitMessageFields;
|
||||
private $raiseMissingFieldErrors = true;
|
||||
private $xactions;
|
||||
|
||||
public static function newStandardParser(PhabricatorUser $viewer) {
|
||||
$key_title = DifferentialTitleCommitMessageField::FIELDKEY;
|
||||
@@ -134,6 +135,7 @@ final class DifferentialCommitMessageParser extends Phobject {
|
||||
*/
|
||||
public function parseCorpus($corpus) {
|
||||
$this->errors = array();
|
||||
$this->xactions = array();
|
||||
|
||||
$label_map = $this->getLabelMap();
|
||||
$key_title = $this->titleKey;
|
||||
@@ -284,12 +286,25 @@ final class DifferentialCommitMessageParser extends Phobject {
|
||||
try {
|
||||
$result = $field->parseFieldValue($text_value);
|
||||
$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) {
|
||||
$this->errors[] = pht(
|
||||
'Error parsing field "%s": %s',
|
||||
$field->getFieldName(),
|
||||
$ex->getMessage());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if ($this->getRaiseMissingFieldErrors()) {
|
||||
@@ -317,6 +332,14 @@ final class DifferentialCommitMessageParser extends Phobject {
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @task parse
|
||||
*/
|
||||
public function getTransactions() {
|
||||
return $this->xactions;
|
||||
}
|
||||
|
||||
|
||||
/* -( Support Methods )---------------------------------------------------- */
|
||||
|
||||
|
||||
|
||||
@@ -6,6 +6,7 @@ final class DifferentialDiffQuery
|
||||
private $ids;
|
||||
private $phids;
|
||||
private $revisionIDs;
|
||||
private $revisionPHIDs;
|
||||
private $commitPHIDs;
|
||||
private $hasRevision;
|
||||
|
||||
@@ -27,6 +28,11 @@ final class DifferentialDiffQuery
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function withRevisionPHIDs(array $revision_phids) {
|
||||
$this->revisionPHIDs = $revision_phids;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function withCommitPHIDs(array $phids) {
|
||||
$this->commitPHIDs = $phids;
|
||||
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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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());
|
||||
}
|
||||
|
||||
}
|
||||
@@ -38,8 +38,6 @@ final class DifferentialRevisionQuery
|
||||
private $needDrafts;
|
||||
private $needFlags;
|
||||
|
||||
private $buildingGlobalOrder;
|
||||
|
||||
|
||||
/* -( Query Configuration )------------------------------------------------ */
|
||||
|
||||
@@ -484,12 +482,11 @@ final class DifferentialRevisionQuery
|
||||
}
|
||||
|
||||
if (count($selects) > 1) {
|
||||
$this->buildingGlobalOrder = true;
|
||||
$query = qsprintf(
|
||||
$conn_r,
|
||||
'%Q %Q %Q',
|
||||
implode(' UNION DISTINCT ', $selects),
|
||||
$this->buildOrderClause($conn_r),
|
||||
$this->buildOrderClause($conn_r, true),
|
||||
$this->buildLimitClause($conn_r));
|
||||
} else {
|
||||
$query = head($selects);
|
||||
@@ -513,7 +510,6 @@ final class DifferentialRevisionQuery
|
||||
$group_by = $this->buildGroupClause($conn_r);
|
||||
$having = $this->buildHavingClause($conn_r);
|
||||
|
||||
$this->buildingGlobalOrder = false;
|
||||
$order_by = $this->buildOrderClause($conn_r);
|
||||
|
||||
$limit = $this->buildLimitClause($conn_r);
|
||||
@@ -758,17 +754,9 @@ final class DifferentialRevisionQuery
|
||||
}
|
||||
|
||||
public function getOrderableColumns() {
|
||||
$primary = ($this->buildingGlobalOrder ? null : 'r');
|
||||
|
||||
return array(
|
||||
'id' => array(
|
||||
'table' => $primary,
|
||||
'column' => 'id',
|
||||
'type' => 'int',
|
||||
'unique' => true,
|
||||
),
|
||||
'updated' => array(
|
||||
'table' => $primary,
|
||||
'table' => $this->getPrimaryTableAlias(),
|
||||
'column' => 'dateModified',
|
||||
'type' => 'int',
|
||||
),
|
||||
|
||||
@@ -123,6 +123,14 @@ final class DifferentialRevisionRequiredActionResultBucket
|
||||
$reviewing = array(
|
||||
DifferentialReviewerStatus::STATUS_ADDED,
|
||||
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);
|
||||
|
||||
@@ -130,7 +138,7 @@ final class DifferentialRevisionRequiredActionResultBucket
|
||||
|
||||
$results = array();
|
||||
foreach ($objects as $key => $object) {
|
||||
if (!$this->hasReviewersWithStatus($object, $phids, $reviewing)) {
|
||||
if (!$this->hasReviewersWithStatus($object, $phids, $reviewing, true)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
|
||||
@@ -53,7 +53,8 @@ abstract class DifferentialRevisionResultBucket
|
||||
protected function hasReviewersWithStatus(
|
||||
DifferentialRevision $revision,
|
||||
array $phids,
|
||||
array $statuses) {
|
||||
array $statuses,
|
||||
$include_voided = null) {
|
||||
|
||||
foreach ($revision->getReviewers() as $reviewer) {
|
||||
$reviewer_phid = $reviewer->getReviewerPHID();
|
||||
@@ -66,6 +67,15 @@ abstract class DifferentialRevisionResultBucket
|
||||
continue;
|
||||
}
|
||||
|
||||
if ($include_voided !== null) {
|
||||
if ($status == DifferentialReviewerStatus::STATUS_ACCEPTED) {
|
||||
$is_voided = (bool)$reviewer->getVoidedPHID();
|
||||
if ($is_voided !== $include_voided) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
@@ -41,6 +41,20 @@ final class DifferentialChangesetOneUpRenderer
|
||||
|
||||
$column_width = 4;
|
||||
|
||||
$aural_minus = javelin_tag(
|
||||
'span',
|
||||
array(
|
||||
'aural' => true,
|
||||
),
|
||||
'- ');
|
||||
|
||||
$aural_plus = javelin_tag(
|
||||
'span',
|
||||
array(
|
||||
'aural' => true,
|
||||
),
|
||||
'+ ');
|
||||
|
||||
$out = array();
|
||||
foreach ($primitives as $k => $p) {
|
||||
$type = $p['type'];
|
||||
@@ -55,8 +69,10 @@ final class DifferentialChangesetOneUpRenderer
|
||||
if ($is_old) {
|
||||
if ($p['htype']) {
|
||||
$class = 'left old';
|
||||
$aural = $aural_minus;
|
||||
} else {
|
||||
$class = 'left';
|
||||
$aural = null;
|
||||
}
|
||||
|
||||
if ($type == 'old-file') {
|
||||
@@ -79,14 +95,20 @@ final class DifferentialChangesetOneUpRenderer
|
||||
),
|
||||
$line);
|
||||
|
||||
$render = $p['render'];
|
||||
if ($aural !== null) {
|
||||
$render = array($aural, $render);
|
||||
}
|
||||
|
||||
$cells[] = phutil_tag('th', array('class' => $class));
|
||||
$cells[] = $no_copy;
|
||||
$cells[] = phutil_tag('td', array('class' => $class), $p['render']);
|
||||
$cells[] = phutil_tag('td', array('class' => $class), $render);
|
||||
$cells[] = $no_coverage;
|
||||
} else {
|
||||
if ($p['htype']) {
|
||||
$class = 'right new';
|
||||
$cells[] = phutil_tag('th', array('class' => $class));
|
||||
$aural = $aural_plus;
|
||||
} else {
|
||||
$class = 'right';
|
||||
if ($left_prefix) {
|
||||
@@ -98,6 +120,7 @@ final class DifferentialChangesetOneUpRenderer
|
||||
$oline = $p['oline'];
|
||||
|
||||
$cells[] = phutil_tag('th', array('id' => $left_id), $oline);
|
||||
$aural = null;
|
||||
}
|
||||
|
||||
if ($type == 'new-file') {
|
||||
@@ -120,8 +143,13 @@ final class DifferentialChangesetOneUpRenderer
|
||||
),
|
||||
$line);
|
||||
|
||||
$render = $p['render'];
|
||||
if ($aural !== null) {
|
||||
$render = array($aural, $render);
|
||||
}
|
||||
|
||||
$cells[] = $no_copy;
|
||||
$cells[] = phutil_tag('td', array('class' => $class), $p['render']);
|
||||
$cells[] = phutil_tag('td', array('class' => $class), $render);
|
||||
$cells[] = $no_coverage;
|
||||
}
|
||||
|
||||
|
||||
@@ -9,7 +9,8 @@ final class DifferentialDiff
|
||||
HarbormasterCircleCIBuildableInterface,
|
||||
HarbormasterBuildkiteBuildableInterface,
|
||||
PhabricatorApplicationTransactionInterface,
|
||||
PhabricatorDestructibleInterface {
|
||||
PhabricatorDestructibleInterface,
|
||||
PhabricatorConduitResultInterface {
|
||||
|
||||
protected $revisionID;
|
||||
protected $authorPHID;
|
||||
@@ -740,4 +741,82 @@ final class DifferentialDiff
|
||||
$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();
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
@@ -35,6 +35,8 @@ final class DifferentialRevision extends DifferentialDAO
|
||||
protected $mailKey;
|
||||
protected $branchName;
|
||||
protected $repositoryPHID;
|
||||
protected $activeDiffPHID;
|
||||
|
||||
protected $viewPolicy = PhabricatorPolicies::POLICY_USER;
|
||||
protected $editPolicy = PhabricatorPolicies::POLICY_USER;
|
||||
protected $properties = array();
|
||||
@@ -57,6 +59,8 @@ final class DifferentialRevision extends DifferentialDAO
|
||||
const RELATION_SUBSCRIBED = 'subd';
|
||||
|
||||
const PROPERTY_CLOSED_FROM_ACCEPTED = 'wasAcceptedBeforeClose';
|
||||
const PROPERTY_DRAFT_HOLD = 'draft.hold';
|
||||
const PROPERTY_HAS_BROADCAST = 'draft.broadcast';
|
||||
|
||||
public static function initializeNewRevision(PhabricatorUser $actor) {
|
||||
$app = id(new PhabricatorApplicationQuery())
|
||||
@@ -67,13 +71,19 @@ final class DifferentialRevision extends DifferentialDAO
|
||||
$view_policy = $app->getPolicy(
|
||||
DifferentialDefaultViewCapability::CAPABILITY);
|
||||
|
||||
if (PhabricatorEnv::getEnvConfig('phabricator.show-prototypes')) {
|
||||
$initial_state = DifferentialRevisionStatus::DRAFT;
|
||||
} else {
|
||||
$initial_state = DifferentialRevisionStatus::NEEDS_REVIEW;
|
||||
}
|
||||
|
||||
return id(new DifferentialRevision())
|
||||
->setViewPolicy($view_policy)
|
||||
->setAuthorPHID($actor->getPHID())
|
||||
->attachRepository(null)
|
||||
->attachActiveDiff(null)
|
||||
->attachReviewers(array())
|
||||
->setModernRevisionStatus(DifferentialRevisionStatus::NEEDS_REVIEW);
|
||||
->setModernRevisionStatus($initial_state);
|
||||
}
|
||||
|
||||
protected function getConfiguration() {
|
||||
@@ -702,6 +712,54 @@ final class DifferentialRevision extends DifferentialDAO
|
||||
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 )------------------------------------- */
|
||||
|
||||
@@ -938,6 +996,18 @@ final class DifferentialRevision extends DifferentialDAO
|
||||
->setKey('status')
|
||||
->setType('map<string, wild>')
|
||||
->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(),
|
||||
'authorPHID' => $this->getAuthorPHID(),
|
||||
'status' => $status_info,
|
||||
'repositoryPHID' => $this->getRepositoryPHID(),
|
||||
'diffPHID' => $this->getActiveDiffPHID(),
|
||||
'summary' => $this->getSummary(),
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -301,6 +301,9 @@ final class DifferentialChangesetListView extends AphrontView {
|
||||
|
||||
'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.'),
|
||||
),
|
||||
));
|
||||
|
||||
|
||||
@@ -47,6 +47,7 @@ final class DifferentialReviewersView extends AphrontView {
|
||||
|
||||
$action_phid = $reviewer->getLastActionDiffPHID();
|
||||
$is_current_action = $this->isCurrent($action_phid);
|
||||
$is_voided = (bool)$reviewer->getVoidedPHID();
|
||||
|
||||
$comment_phid = $reviewer->getLastCommentDiffPHID();
|
||||
$is_current_comment = $this->isCurrent($comment_phid);
|
||||
@@ -86,7 +87,7 @@ final class DifferentialReviewersView extends AphrontView {
|
||||
break;
|
||||
|
||||
case DifferentialReviewerStatus::STATUS_ACCEPTED:
|
||||
if ($is_current_action) {
|
||||
if ($is_current_action && !$is_voided) {
|
||||
$icon = PHUIStatusItemView::ICON_ACCEPT;
|
||||
$color = 'green';
|
||||
if ($authority_name !== null) {
|
||||
@@ -97,7 +98,12 @@ final class DifferentialReviewersView extends AphrontView {
|
||||
} else {
|
||||
$icon = 'fa-check-circle-o';
|
||||
$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);
|
||||
} else {
|
||||
$label = pht('Accepted Prior Diff');
|
||||
|
||||
@@ -162,6 +162,11 @@ final class DifferentialRevisionAcceptTransaction
|
||||
'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';
|
||||
if (!PhabricatorEnv::getEnvConfig($config_key)) {
|
||||
if ($this->isViewerRevisionAuthor($object, $viewer)) {
|
||||
|
||||
@@ -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());
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -57,6 +57,9 @@ final class DifferentialRevisionRequestReviewTransaction
|
||||
'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)) {
|
||||
throw new Exception(
|
||||
pht(
|
||||
@@ -64,6 +67,7 @@ final class DifferentialRevisionRequestReviewTransaction
|
||||
'the author of the revision.'));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public function getTitle() {
|
||||
return pht(
|
||||
|
||||
@@ -64,11 +64,6 @@ final class DifferentialRevisionResignTransaction
|
||||
'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;
|
||||
if ($this->getViewerReviewerStatus($object, $viewer) == $resigned) {
|
||||
throw new Exception(
|
||||
|
||||
@@ -74,10 +74,10 @@ final class DifferentialRevisionStatusTransaction
|
||||
return 'status';
|
||||
}
|
||||
|
||||
public function getFieldValuesForConduit($object, $data) {
|
||||
public function getFieldValuesForConduit($xaction, $data) {
|
||||
return array(
|
||||
'old' => $object->getOldValue(),
|
||||
'new' => $object->getNewValue(),
|
||||
'old' => $xaction->getOldValue(),
|
||||
'new' => $xaction->getNewValue(),
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -47,7 +47,7 @@ final class DiffusionExistsQueryConduitAPIMethod
|
||||
$commit = $request->getValue('commit');
|
||||
list($err, $stdout) = $repository->execLocalCommand(
|
||||
'id --rev %s',
|
||||
$commit);
|
||||
hgsprintf('%s', $commit));
|
||||
return !$err;
|
||||
}
|
||||
|
||||
|
||||
@@ -123,21 +123,23 @@ final class DiffusionHistoryQueryConduitAPIMethod
|
||||
// branches).
|
||||
|
||||
if (strlen($path)) {
|
||||
$path_arg = csprintf('-- %s', $path);
|
||||
$branch_arg = '';
|
||||
$path_arg = csprintf('%s', $path);
|
||||
$revset_arg = hgsprintf(
|
||||
'reverse(ancestors(%s))',
|
||||
$commit_hash);
|
||||
} else {
|
||||
$path_arg = '';
|
||||
// NOTE: --branch used to be called --only-branch; use -b for
|
||||
// compatibility.
|
||||
$branch_arg = csprintf('-b %s', $drequest->getBranch());
|
||||
$revset_arg = hgsprintf(
|
||||
'reverse(ancestors(%s)) and branch(%s)',
|
||||
$drequest->getBranch(),
|
||||
$commit_hash);
|
||||
}
|
||||
|
||||
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',
|
||||
($offset + $limit), // No '--skip' in Mercurial.
|
||||
$branch_arg,
|
||||
hgsprintf('reverse(ancestors(%s))', $commit_hash),
|
||||
$revset_arg,
|
||||
$path_arg);
|
||||
|
||||
$stdout = DiffusionMercurialCommandEngine::filterMercurialDebugOutput(
|
||||
|
||||
@@ -77,7 +77,7 @@ final class DiffusionMergedCommitsQueryConduitAPIMethod
|
||||
list($parents) = $repository->execxLocalCommand(
|
||||
'parents --template=%s --rev %s',
|
||||
'{node}\\n',
|
||||
$commit);
|
||||
hgsprintf('%s', $commit));
|
||||
$parents = explode("\n", trim($parents));
|
||||
|
||||
if (count($parents) < 2) {
|
||||
|
||||
@@ -97,7 +97,7 @@ final class DiffusionSearchQueryConduitAPIMethod
|
||||
|
||||
$results = array();
|
||||
$future = $repository->getLocalCommandFuture(
|
||||
'grep --rev %s --print0 --line-number %s %s',
|
||||
'grep --rev %s --print0 --line-number -- %s %s',
|
||||
hgsprintf('ancestors(%s)', $drequest->getStableCommit()),
|
||||
$grep,
|
||||
$path);
|
||||
|
||||
@@ -969,6 +969,24 @@ final class DiffusionBrowseController extends DiffusionController {
|
||||
|
||||
$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();
|
||||
if ($blame_commits) {
|
||||
$epochs = array();
|
||||
@@ -1113,6 +1131,7 @@ final class DiffusionBrowseController extends DiffusionController {
|
||||
// blame outputs.
|
||||
$commit_links = $this->renderCommitLinks($blame_commits, $handles);
|
||||
$revision_links = $this->renderRevisionLinks($revisions, $handles);
|
||||
$author_links = $this->renderAuthorLinks($author_map, $handles);
|
||||
|
||||
if ($this->coverage) {
|
||||
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) {
|
||||
$row = array();
|
||||
|
||||
@@ -1141,15 +1164,15 @@ final class DiffusionBrowseController extends DiffusionController {
|
||||
|
||||
$revision_link = null;
|
||||
$commit_link = null;
|
||||
$author_link = null;
|
||||
$before_link = null;
|
||||
$commit_date = null;
|
||||
|
||||
$style = 'border-right: 3px solid '.$line['color'].';';
|
||||
$style = 'background: '.$line['color'].';';
|
||||
|
||||
if ($identifier && !$line['duplicate']) {
|
||||
if (isset($commit_links[$identifier])) {
|
||||
$commit_link = $commit_links[$identifier]['link'];
|
||||
$commit_date = $commit_links[$identifier]['date'];
|
||||
$commit_link = $commit_links[$identifier];
|
||||
$author_link = $author_links[$author_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_text = pht('Skip Past This Commit');
|
||||
$icon = id(new PHUIIconView())
|
||||
->setIcon('fa-caret-square-o-left');
|
||||
|
||||
$before_link = javelin_tag(
|
||||
'a',
|
||||
array(
|
||||
@@ -1175,7 +1194,7 @@ final class DiffusionBrowseController extends DiffusionController {
|
||||
'size' => 300,
|
||||
),
|
||||
),
|
||||
$icon);
|
||||
$skip_icon);
|
||||
}
|
||||
|
||||
if ($show_blame) {
|
||||
@@ -1186,34 +1205,27 @@ final class DiffusionBrowseController extends DiffusionController {
|
||||
),
|
||||
$before_link);
|
||||
|
||||
$row[] = phutil_tag(
|
||||
'th',
|
||||
array(
|
||||
'class' => 'diffusion-rev-link',
|
||||
),
|
||||
$commit_link);
|
||||
|
||||
if ($revision_map) {
|
||||
$row[] = phutil_tag(
|
||||
'th',
|
||||
array(
|
||||
'class' => 'diffusion-blame-revision',
|
||||
),
|
||||
$revision_link);
|
||||
$object_links = array();
|
||||
$object_links[] = $author_link;
|
||||
$object_links[] = $commit_link;
|
||||
if ($revision_link) {
|
||||
$object_links[] = phutil_tag('span', array(), '/');
|
||||
$object_links[] = $revision_link;
|
||||
}
|
||||
|
||||
$row[] = phutil_tag(
|
||||
'th',
|
||||
array(
|
||||
'class' => 'diffusion-blame-date',
|
||||
'class' => 'diffusion-rev-link',
|
||||
),
|
||||
$commit_date);
|
||||
$object_links);
|
||||
}
|
||||
|
||||
$line_link = phutil_tag(
|
||||
'a',
|
||||
array(
|
||||
'href' => $line_href,
|
||||
'style' => $style,
|
||||
),
|
||||
$line_number);
|
||||
|
||||
@@ -1536,6 +1548,33 @@ final class DiffusionBrowseController extends DiffusionController {
|
||||
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) {
|
||||
$engine = PhabricatorMarkupEngine::newDiffusionMarkupEngine();
|
||||
$engine->setConfig('viewer', $this->getRequest()->getUser());
|
||||
@@ -1743,6 +1782,9 @@ final class DiffusionBrowseController extends DiffusionController {
|
||||
->setViewer($viewer)
|
||||
->withRepository($repository)
|
||||
->withIdentifiers($identifiers)
|
||||
// TODO: We only fetch this to improve author display behavior, but
|
||||
// shouldn't really need to?
|
||||
->needCommitData(true)
|
||||
->execute();
|
||||
$commits = mpull($commits, null, 'getCommitIdentifier');
|
||||
} else {
|
||||
@@ -1752,29 +1794,59 @@ final class DiffusionBrowseController extends DiffusionController {
|
||||
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) {
|
||||
$links = array();
|
||||
$viewer = $this->getViewer();
|
||||
foreach ($commits as $identifier => $commit) {
|
||||
$date = phabricator_date($commit->getEpoch(), $viewer);
|
||||
$summary = trim($commit->getSummary());
|
||||
$tooltip = $this->renderCommitTooltip(
|
||||
$commit,
|
||||
$commit->renderAuthorShortName($handles));
|
||||
|
||||
$commit_link = phutil_tag(
|
||||
$commit_link = javelin_tag(
|
||||
'a',
|
||||
array(
|
||||
'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]['date'] = $commit_date;
|
||||
$links[$identifier] = $commit_link;
|
||||
}
|
||||
|
||||
return $links;
|
||||
@@ -1785,10 +1857,19 @@ final class DiffusionBrowseController extends DiffusionController {
|
||||
|
||||
foreach ($revisions as $revision) {
|
||||
$revision_id = $revision->getID();
|
||||
$revision_link = phutil_tag(
|
||||
|
||||
$tooltip = $this->renderRevisionTooltip($revision, $handles);
|
||||
|
||||
$revision_link = javelin_tag(
|
||||
'a',
|
||||
array(
|
||||
'href' => '/'.$revision->getMonogram(),
|
||||
'sigil' => 'has-tooltip',
|
||||
'meta' => array(
|
||||
'tip' => $tooltip,
|
||||
'align' => 'E',
|
||||
'size' => 600,
|
||||
),
|
||||
),
|
||||
$revision->getMonogram());
|
||||
|
||||
|
||||
@@ -39,20 +39,23 @@ final class DiffusionCommitController extends DiffusionController {
|
||||
return $this->buildRawDiffResponse($drequest);
|
||||
}
|
||||
|
||||
$commit = id(new DiffusionCommitQuery())
|
||||
$commits = id(new DiffusionCommitQuery())
|
||||
->setViewer($viewer)
|
||||
->withRepository($repository)
|
||||
->withIdentifiers(array($commit_identifier))
|
||||
->needCommitData(true)
|
||||
->needAuditRequests(true)
|
||||
->executeOne();
|
||||
->setLimit(100)
|
||||
->execute();
|
||||
|
||||
$multiple_results = count($commits) > 1;
|
||||
|
||||
$crumbs = $this->buildCrumbs(array(
|
||||
'commit' => true,
|
||||
'commit' => !$multiple_results,
|
||||
));
|
||||
$crumbs->setBorder(true);
|
||||
|
||||
if (!$commit) {
|
||||
if (!$commits) {
|
||||
if (!$this->getCommitExists()) {
|
||||
return new Aphront404Response();
|
||||
}
|
||||
@@ -70,7 +73,40 @@ final class DiffusionCommitController extends DiffusionController {
|
||||
->setTitle($title)
|
||||
->setCrumbs($crumbs)
|
||||
->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();
|
||||
@@ -438,7 +474,8 @@ final class DiffusionCommitController extends DiffusionController {
|
||||
$repository = $drequest->getRepository();
|
||||
|
||||
$view = id(new PHUIPropertyListView())
|
||||
->setUser($this->getRequest()->getUser());
|
||||
->setUser($this->getRequest()->getUser())
|
||||
->setObject($commit);
|
||||
|
||||
$edge_query = id(new PhabricatorEdgeQuery())
|
||||
->withSourcePHIDs(array($commit_phid))
|
||||
|
||||
@@ -46,8 +46,20 @@ final class DiffusionRepositoryController extends DiffusionController {
|
||||
->withRepositoryPHIDs(array($repository->getPHID()))
|
||||
->withRefTypes(array(PhabricatorRepositoryRefCursor::TYPE_BRANCH))
|
||||
->withRefNames(array($drequest->getBranch()))
|
||||
->needPositions(true)
|
||||
->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.
|
||||
$page_has_content = true;
|
||||
} else {
|
||||
|
||||
@@ -347,11 +347,11 @@ final class DiffusionURIEditor
|
||||
continue;
|
||||
}
|
||||
|
||||
$io_type = $uri->getIoType();
|
||||
$io_type = $uri->getEffectiveIOType();
|
||||
|
||||
if ($io_type == PhabricatorRepositoryURI::IO_READWRITE) {
|
||||
if ($no_readwrite) {
|
||||
$readwite_conflict = $uri;
|
||||
$readwrite_conflict = $uri;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,12 @@
|
||||
<?php
|
||||
|
||||
final class DiffusionQuickSearchEngineExtension
|
||||
extends PhabricatorQuickSearchEngineExtension {
|
||||
|
||||
public function newQuickSearchDatasources() {
|
||||
return array(
|
||||
new DiffusionRepositoryDatasource(),
|
||||
new DiffusionSymbolDatasource(),
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
<?php
|
||||
|
||||
final class DiffusionMercurialFlagInjectionException extends Exception {}
|
||||
@@ -11,6 +11,27 @@ final class DiffusionMercurialCommandEngine
|
||||
protected function newFormattedCommand($pattern, array $argv) {
|
||||
$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
|
||||
// if the repository does not use an SSH remote, since our SSH wrapper
|
||||
// defuses an attack against older versions of Mercurial, Git and
|
||||
|
||||
@@ -123,6 +123,62 @@ final class DiffusionCommandEngineTestCase extends PhabricatorTestCase {
|
||||
'argv' => 'xyz',
|
||||
'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(
|
||||
|
||||
@@ -177,7 +177,63 @@ final class DiffusionCommitQuery
|
||||
}
|
||||
|
||||
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) {
|
||||
@@ -487,18 +543,10 @@ final class DiffusionCommitQuery
|
||||
$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) {
|
||||
$where[] = qsprintf(
|
||||
$conn,
|
||||
'commit.auditStatus IN (%Ls)',
|
||||
'commit.auditStatus IN (%Ld)',
|
||||
$this->statuses);
|
||||
}
|
||||
|
||||
@@ -541,10 +589,6 @@ final class DiffusionCommitQuery
|
||||
return ($this->auditIDs || $this->auditorPHIDs);
|
||||
}
|
||||
|
||||
private function shouldJoinAudit() {
|
||||
return (bool)$this->responsiblePHIDs;
|
||||
}
|
||||
|
||||
private function shouldJoinOwners() {
|
||||
return (bool)$this->packagePHIDs;
|
||||
}
|
||||
@@ -560,13 +604,6 @@ final class DiffusionCommitQuery
|
||||
$audit_request->getTableName());
|
||||
}
|
||||
|
||||
if ($this->shouldJoinAudit()) {
|
||||
$join[] = qsprintf(
|
||||
$conn,
|
||||
'LEFT JOIN %T audit ON commit.phid = audit.commitPHID',
|
||||
$audit_request->getTableName());
|
||||
}
|
||||
|
||||
if ($this->shouldJoinOwners()) {
|
||||
$join[] = qsprintf(
|
||||
$conn,
|
||||
@@ -584,10 +621,6 @@ final class DiffusionCommitQuery
|
||||
return true;
|
||||
}
|
||||
|
||||
if ($this->shouldJoinAudit()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if ($this->shouldJoinOwners()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -256,6 +256,66 @@ final class DiffusionLowLevelResolveRefsQuery
|
||||
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"),
|
||||
// try to resolve them individually.
|
||||
|
||||
|
||||
@@ -16,14 +16,14 @@ final class DiffusionMercurialRawDiffQuery extends DiffusionRawDiffQuery {
|
||||
// If `$commit` has no parents (usually because it's the first commit
|
||||
// in the repository), we want to diff against `null`. This revset will
|
||||
// do that for us automatically.
|
||||
$against = '('.$commit.'^ or null)';
|
||||
$against = hgsprintf('(%s^ or null)', $commit);
|
||||
}
|
||||
|
||||
$future = $repository->getLocalCommandFuture(
|
||||
'diff -U %d --git --rev %s --rev %s -- %s',
|
||||
$this->getLinesOfContext(),
|
||||
$against,
|
||||
$commit,
|
||||
hgsprintf('%s', $commit),
|
||||
$path);
|
||||
|
||||
return $future;
|
||||
|
||||
@@ -96,9 +96,10 @@ final class DiffusionPatternSearchView extends DiffusionView {
|
||||
|
||||
$path_title = Filesystem::readablePath($this->path, $drequest->getPath());
|
||||
|
||||
$href = $drequest->generateURI(array(
|
||||
$href = $drequest->generateURI(
|
||||
array(
|
||||
'action' => 'browse',
|
||||
'path' => $path_title,
|
||||
'path' => $this->path,
|
||||
));
|
||||
|
||||
$title = phutil_tag('a', array('href' => $href), $path_title);
|
||||
|
||||
@@ -83,6 +83,9 @@ final class DrydockLease extends DrydockDAO
|
||||
'key_resource' => array(
|
||||
'columns' => array('resourcePHID', 'status'),
|
||||
),
|
||||
'key_status' => array(
|
||||
'columns' => array('status'),
|
||||
),
|
||||
),
|
||||
) + parent::getConfiguration();
|
||||
}
|
||||
|
||||
@@ -58,6 +58,14 @@ final class PhabricatorFilesComposeAvatarBuiltinFile
|
||||
}
|
||||
|
||||
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, '#'));
|
||||
$true_border = self::rgba2gd($border);
|
||||
$image_map = self::getImageMap();
|
||||
|
||||
@@ -14,6 +14,10 @@ final class PhabricatorFileDataController extends PhabricatorFileController {
|
||||
return false;
|
||||
}
|
||||
|
||||
public function shouldAllowPartialSessions() {
|
||||
return true;
|
||||
}
|
||||
|
||||
public function handleRequest(AphrontRequest $request) {
|
||||
$viewer = $request->getViewer();
|
||||
$this->phid = $request->getURIData('phid');
|
||||
|
||||
@@ -134,6 +134,9 @@ final class PhabricatorFile extends PhabricatorFileDAO
|
||||
'columns' => array('builtinKey'),
|
||||
'unique' => true,
|
||||
),
|
||||
'key_engine' => array(
|
||||
'columns' => array('storageEngine', 'storageHandle(64)'),
|
||||
),
|
||||
),
|
||||
) + parent::getConfiguration();
|
||||
}
|
||||
|
||||
@@ -7,6 +7,12 @@ final class HarbormasterRunBuildPlansHeraldAction
|
||||
|
||||
const ACTIONCONST = 'harbormaster.build';
|
||||
|
||||
public function getRequiredAdapterStates() {
|
||||
return array(
|
||||
HeraldBuildableState::STATECONST,
|
||||
);
|
||||
}
|
||||
|
||||
public function getActionGroupKey() {
|
||||
return HeraldSupportActionGroup::ACTIONGROUPKEY;
|
||||
}
|
||||
|
||||
@@ -10,6 +10,7 @@ final class HarbormasterBuildQuery
|
||||
private $buildPlanPHIDs;
|
||||
private $initiatorPHIDs;
|
||||
private $needBuildTargets;
|
||||
private $autobuilds;
|
||||
|
||||
public function withIDs(array $ids) {
|
||||
$this->ids = $ids;
|
||||
@@ -41,6 +42,11 @@ final class HarbormasterBuildQuery
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function withAutobuilds($with_autobuilds) {
|
||||
$this->autobuilds = $with_autobuilds;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function needBuildTargets($need_targets) {
|
||||
$this->needBuildTargets = $need_targets;
|
||||
return $this;
|
||||
@@ -141,50 +147,87 @@ final class HarbormasterBuildQuery
|
||||
if ($this->ids !== null) {
|
||||
$where[] = qsprintf(
|
||||
$conn,
|
||||
'id IN (%Ld)',
|
||||
'b.id IN (%Ld)',
|
||||
$this->ids);
|
||||
}
|
||||
|
||||
if ($this->phids !== null) {
|
||||
$where[] = qsprintf(
|
||||
$conn,
|
||||
'phid in (%Ls)',
|
||||
'b.phid in (%Ls)',
|
||||
$this->phids);
|
||||
}
|
||||
|
||||
if ($this->buildStatuses !== null) {
|
||||
$where[] = qsprintf(
|
||||
$conn,
|
||||
'buildStatus in (%Ls)',
|
||||
'b.buildStatus in (%Ls)',
|
||||
$this->buildStatuses);
|
||||
}
|
||||
|
||||
if ($this->buildablePHIDs !== null) {
|
||||
$where[] = qsprintf(
|
||||
$conn,
|
||||
'buildablePHID IN (%Ls)',
|
||||
'b.buildablePHID IN (%Ls)',
|
||||
$this->buildablePHIDs);
|
||||
}
|
||||
|
||||
if ($this->buildPlanPHIDs !== null) {
|
||||
$where[] = qsprintf(
|
||||
$conn,
|
||||
'buildPlanPHID IN (%Ls)',
|
||||
'b.buildPlanPHID IN (%Ls)',
|
||||
$this->buildPlanPHIDs);
|
||||
}
|
||||
|
||||
if ($this->initiatorPHIDs !== null) {
|
||||
$where[] = qsprintf(
|
||||
$conn,
|
||||
'initiatorPHID IN (%Ls)',
|
||||
'b.initiatorPHID IN (%Ls)',
|
||||
$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;
|
||||
}
|
||||
|
||||
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() {
|
||||
return 'PhabricatorHarbormasterApplication';
|
||||
}
|
||||
|
||||
protected function getPrimaryTableAlias() {
|
||||
return 'b';
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -43,6 +43,9 @@ final class HarbormasterBuildArtifact extends HarbormasterDAO
|
||||
'key_target' => array(
|
||||
'columns' => array('buildTargetPHID', 'artifactType'),
|
||||
),
|
||||
'key_index' => array(
|
||||
'columns' => array('artifactIndex'),
|
||||
),
|
||||
),
|
||||
) + parent::getConfiguration();
|
||||
}
|
||||
|
||||
@@ -17,6 +17,7 @@ abstract class HeraldAction extends Phobject {
|
||||
const DO_STANDARD_PERMISSION = 'do.standard.permission';
|
||||
const DO_STANDARD_INVALID_ACTION = 'do.standard.invalid-action';
|
||||
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 supportsObject($object);
|
||||
@@ -25,6 +26,10 @@ abstract class HeraldAction extends Phobject {
|
||||
|
||||
abstract public function renderActionDescription($value);
|
||||
|
||||
public function getRequiredAdapterStates() {
|
||||
return array();
|
||||
}
|
||||
|
||||
protected function renderActionEffectDescription($type, $data) {
|
||||
return null;
|
||||
}
|
||||
@@ -336,6 +341,11 @@ abstract class HeraldAction extends Phobject {
|
||||
'color' => 'red',
|
||||
'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(
|
||||
'This action does not support rules of type "%s".',
|
||||
$data);
|
||||
case self::DO_STANDARD_FORBIDDEN:
|
||||
return HeraldStateReasons::getExplanation($data);
|
||||
}
|
||||
|
||||
return null;
|
||||
|
||||
@@ -37,6 +37,7 @@ abstract class HeraldAdapter extends Phobject {
|
||||
private $fieldMap;
|
||||
private $actionMap;
|
||||
private $edgeCache = array();
|
||||
private $forbiddenActions = array();
|
||||
|
||||
public function getEmailPHIDs() {
|
||||
return array_values($this->emailPHIDs);
|
||||
@@ -1116,4 +1117,38 @@ abstract class HeraldAdapter extends Phobject {
|
||||
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];
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -273,7 +273,11 @@ final class HeraldTranscriptController extends HeraldController {
|
||||
->setTarget(phutil_tag('strong', array(), pht('Conditions'))));
|
||||
|
||||
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';
|
||||
$color = 'green';
|
||||
$result = pht('Passed');
|
||||
@@ -284,12 +288,17 @@ final class HeraldTranscriptController extends HeraldController {
|
||||
}
|
||||
|
||||
if ($cond_xscript->getNote()) {
|
||||
$note_text = $cond_xscript->getNote();
|
||||
if ($cond_xscript->isForbidden()) {
|
||||
$note_text = HeraldStateReasons::getExplanation($note_text);
|
||||
}
|
||||
|
||||
$note = phutil_tag(
|
||||
'div',
|
||||
array(
|
||||
'class' => 'herald-condition-note',
|
||||
),
|
||||
$cond_xscript->getNote());
|
||||
$note_text);
|
||||
} else {
|
||||
$note = null;
|
||||
}
|
||||
@@ -310,7 +319,12 @@ final class HeraldTranscriptController extends HeraldController {
|
||||
$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_color = 'green';
|
||||
$last_result = pht('Passed');
|
||||
|
||||
@@ -12,6 +12,9 @@ final class HeraldEngine extends Phobject {
|
||||
protected $object;
|
||||
private $dryRun;
|
||||
|
||||
private $forbiddenFields = array();
|
||||
private $forbiddenActions = array();
|
||||
|
||||
public function setDryRun($dry_run) {
|
||||
$this->dryRun = $dry_run;
|
||||
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
|
||||
// applied a single time, and it's already been applied...
|
||||
// That means automatic failure.
|
||||
$xscript = id(new HeraldRuleTranscript())
|
||||
->setRuleID($rule->getID())
|
||||
$this->newRuleTranscript($rule)
|
||||
->setResult(false)
|
||||
->setRuleName($rule->getName())
|
||||
->setRuleOwner($rule->getAuthorPHID())
|
||||
->setReason(
|
||||
pht(
|
||||
'This rule is only supposed to be repeated a single time, '.
|
||||
'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;
|
||||
} else {
|
||||
$rule_matches = $this->doesRuleMatch($rule, $object);
|
||||
}
|
||||
}
|
||||
} catch (HeraldRecursiveConditionsException $ex) {
|
||||
$names = array();
|
||||
foreach ($this->stack as $rule_id => $ignored) {
|
||||
$names[] = '"'.$rules[$rule_id]->getName().'"';
|
||||
foreach ($this->stack as $rule_phid => $ignored) {
|
||||
$names[] = '"'.$rules[$rule_phid]->getName().'"';
|
||||
}
|
||||
$names = implode(', ', $names);
|
||||
foreach ($this->stack as $rule_id => $ignored) {
|
||||
$xscript = new HeraldRuleTranscript();
|
||||
$xscript->setRuleID($rule_id);
|
||||
$xscript->setResult(false);
|
||||
$xscript->setReason(
|
||||
foreach ($this->stack as $rule_phid => $ignored) {
|
||||
$this->newRuleTranscript($rules[$rule_phid])
|
||||
->setResult(false)
|
||||
->setReason(
|
||||
pht(
|
||||
"Rules %s are recursively dependent upon one another! ".
|
||||
"Don't do this! You have formed an unresolvable cycle in the ".
|
||||
"dependency graph!",
|
||||
$names));
|
||||
$xscript->setRuleName($rules[$rule_id]->getName());
|
||||
$xscript->setRuleOwner($rules[$rule_id]->getAuthorPHID());
|
||||
$this->transcript->addRuleTranscript($xscript);
|
||||
}
|
||||
$rule_matches = false;
|
||||
}
|
||||
@@ -309,14 +315,9 @@ final class HeraldEngine extends Phobject {
|
||||
}
|
||||
}
|
||||
|
||||
$rule_transcript = new HeraldRuleTranscript();
|
||||
$rule_transcript->setRuleID($rule->getID());
|
||||
$rule_transcript->setResult($result);
|
||||
$rule_transcript->setReason($reason);
|
||||
$rule_transcript->setRuleName($rule->getName());
|
||||
$rule_transcript->setRuleOwner($rule->getAuthorPHID());
|
||||
|
||||
$this->transcript->addRuleTranscript($rule_transcript);
|
||||
$this->newRuleTranscript($rule)
|
||||
->setResult($result)
|
||||
->setReason($reason);
|
||||
|
||||
return $result;
|
||||
}
|
||||
@@ -327,16 +328,7 @@ final class HeraldEngine extends Phobject {
|
||||
HeraldAdapter $object) {
|
||||
|
||||
$object_value = $this->getConditionObjectValue($condition, $object);
|
||||
$test_value = $condition->getValue();
|
||||
|
||||
$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);
|
||||
$transcript = $this->newConditionTranscript($rule, $condition);
|
||||
|
||||
try {
|
||||
$result = $object->doesConditionMatch(
|
||||
@@ -351,8 +343,6 @@ final class HeraldEngine extends Phobject {
|
||||
|
||||
$transcript->setResult($result);
|
||||
|
||||
$this->transcript->addConditionTranscript($transcript);
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
@@ -446,4 +436,136 @@ final class HeraldEngine extends Phobject {
|
||||
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;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -20,6 +20,10 @@ abstract class HeraldField extends Phobject {
|
||||
return null;
|
||||
}
|
||||
|
||||
public function getRequiredAdapterStates() {
|
||||
return array();
|
||||
}
|
||||
|
||||
protected function getHeraldFieldStandardType() {
|
||||
throw new PhutilMethodNotImplementedException();
|
||||
}
|
||||
|
||||
7
src/applications/herald/state/HeraldBuildableState.php
Normal file
7
src/applications/herald/state/HeraldBuildableState.php
Normal file
@@ -0,0 +1,7 @@
|
||||
<?php
|
||||
|
||||
final class HeraldBuildableState extends HeraldState {
|
||||
|
||||
const STATECONST = 'buildable';
|
||||
|
||||
}
|
||||
7
src/applications/herald/state/HeraldMailableState.php
Normal file
7
src/applications/herald/state/HeraldMailableState.php
Normal file
@@ -0,0 +1,7 @@
|
||||
<?php
|
||||
|
||||
final class HeraldMailableState extends HeraldState {
|
||||
|
||||
const STATECONST = 'mailable';
|
||||
|
||||
}
|
||||
3
src/applications/herald/state/HeraldState.php
Normal file
3
src/applications/herald/state/HeraldState.php
Normal file
@@ -0,0 +1,3 @@
|
||||
<?php
|
||||
|
||||
abstract class HeraldState extends Phobject {}
|
||||
26
src/applications/herald/state/HeraldStateReasons.php
Normal file
26
src/applications/herald/state/HeraldStateReasons.php
Normal 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);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -10,6 +10,8 @@ final class HeraldConditionTranscript extends Phobject {
|
||||
protected $note;
|
||||
protected $result;
|
||||
|
||||
const RESULT_FORBIDDEN = 'forbidden';
|
||||
|
||||
public function setRuleID($rule_id) {
|
||||
$this->ruleID = $rule_id;
|
||||
return $this;
|
||||
@@ -72,4 +74,9 @@ final class HeraldConditionTranscript extends Phobject {
|
||||
public function getResult() {
|
||||
return $this->result;
|
||||
}
|
||||
|
||||
public function isForbidden() {
|
||||
return ($this->getResult() === self::RESULT_FORBIDDEN);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -9,6 +9,12 @@ final class HeraldRuleTranscript extends Phobject {
|
||||
protected $ruleName;
|
||||
protected $ruleOwner;
|
||||
|
||||
const RESULT_FORBIDDEN = 'forbidden';
|
||||
|
||||
public function isForbidden() {
|
||||
return ($this->getResult() === self::RESULT_FORBIDDEN);
|
||||
}
|
||||
|
||||
public function setResult($result) {
|
||||
$this->result = $result;
|
||||
return $this;
|
||||
|
||||
@@ -77,23 +77,12 @@ final class LegalpadDocumentQuery
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function newResultObject() {
|
||||
return new LegalpadDocument();
|
||||
}
|
||||
|
||||
protected function loadPage() {
|
||||
$table = new LegalpadDocument();
|
||||
$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;
|
||||
return $this->loadStandardPage($this->newResultObject());
|
||||
}
|
||||
|
||||
protected function willFilterPage(array $documents) {
|
||||
@@ -134,12 +123,12 @@ final class LegalpadDocumentQuery
|
||||
return $documents;
|
||||
}
|
||||
|
||||
protected function buildJoinClause(AphrontDatabaseConnection $conn_r) {
|
||||
$joins = array();
|
||||
protected function buildJoinClauseParts(AphrontDatabaseConnection $conn) {
|
||||
$joins = parent::buildJoinClauseParts($conn);
|
||||
|
||||
if ($this->contributorPHIDs !== null) {
|
||||
$joins[] = qsprintf(
|
||||
$conn_r,
|
||||
$conn,
|
||||
'JOIN edge contributor ON contributor.src = d.phid
|
||||
AND contributor.type = %d',
|
||||
PhabricatorObjectHasContributorEdgeType::EDGECONST);
|
||||
@@ -147,79 +136,81 @@ final class LegalpadDocumentQuery
|
||||
|
||||
if ($this->signerPHIDs !== null) {
|
||||
$joins[] = qsprintf(
|
||||
$conn_r,
|
||||
$conn,
|
||||
'JOIN %T signer ON signer.documentPHID = d.phid
|
||||
AND signer.signerPHID IN (%Ls)',
|
||||
id(new LegalpadDocumentSignature())->getTableName(),
|
||||
$this->signerPHIDs);
|
||||
}
|
||||
|
||||
return implode(' ', $joins);
|
||||
return $joins;
|
||||
}
|
||||
|
||||
protected function buildGroupClause(AphrontDatabaseConnection $conn_r) {
|
||||
if ($this->contributorPHIDs || $this->signerPHIDs) {
|
||||
return 'GROUP BY d.id';
|
||||
} else {
|
||||
return '';
|
||||
}
|
||||
protected function shouldGroupQueryResultRows() {
|
||||
if ($this->contributorPHIDs) {
|
||||
return true;
|
||||
}
|
||||
|
||||
protected function buildWhereClause(AphrontDatabaseConnection $conn_r) {
|
||||
$where = array();
|
||||
if ($this->signerPHIDs) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return parent::shouldGroupQueryResultRows();
|
||||
}
|
||||
|
||||
protected function buildWhereClauseParts(AphrontDatabaseConnection $conn) {
|
||||
$where = parent::buildWhereClauseParts($conn);
|
||||
|
||||
if ($this->ids !== null) {
|
||||
$where[] = qsprintf(
|
||||
$conn_r,
|
||||
$conn,
|
||||
'd.id IN (%Ld)',
|
||||
$this->ids);
|
||||
}
|
||||
|
||||
if ($this->phids !== null) {
|
||||
$where[] = qsprintf(
|
||||
$conn_r,
|
||||
$conn,
|
||||
'd.phid IN (%Ls)',
|
||||
$this->phids);
|
||||
}
|
||||
|
||||
if ($this->creatorPHIDs !== null) {
|
||||
$where[] = qsprintf(
|
||||
$conn_r,
|
||||
$conn,
|
||||
'd.creatorPHID IN (%Ls)',
|
||||
$this->creatorPHIDs);
|
||||
}
|
||||
|
||||
if ($this->dateCreatedAfter !== null) {
|
||||
$where[] = qsprintf(
|
||||
$conn_r,
|
||||
$conn,
|
||||
'd.dateCreated >= %d',
|
||||
$this->dateCreatedAfter);
|
||||
}
|
||||
|
||||
if ($this->dateCreatedBefore !== null) {
|
||||
$where[] = qsprintf(
|
||||
$conn_r,
|
||||
$conn,
|
||||
'd.dateCreated <= %d',
|
||||
$this->dateCreatedBefore);
|
||||
}
|
||||
|
||||
if ($this->contributorPHIDs !== null) {
|
||||
$where[] = qsprintf(
|
||||
$conn_r,
|
||||
$conn,
|
||||
'contributor.dst IN (%Ls)',
|
||||
$this->contributorPHIDs);
|
||||
}
|
||||
|
||||
if ($this->signatureRequired !== null) {
|
||||
$where[] = qsprintf(
|
||||
$conn_r,
|
||||
$conn,
|
||||
'd.requireSignature = %d',
|
||||
$this->signatureRequired);
|
||||
}
|
||||
|
||||
$where[] = $this->buildPagingClause($conn_r);
|
||||
|
||||
return $this->formatWhereClause($where);
|
||||
return $where;
|
||||
}
|
||||
|
||||
private function loadDocumentBodies(array $documents) {
|
||||
@@ -275,4 +266,8 @@ final class LegalpadDocumentQuery
|
||||
return 'PhabricatorLegalpadApplication';
|
||||
}
|
||||
|
||||
protected function getPrimaryTableAlias() {
|
||||
return 'd';
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -11,105 +11,66 @@ final class LegalpadDocumentSearchEngine
|
||||
return 'PhabricatorLegalpadApplication';
|
||||
}
|
||||
|
||||
public function buildSavedQueryFromRequest(AphrontRequest $request) {
|
||||
$saved = new PhabricatorSavedQuery();
|
||||
$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())
|
||||
public function newQuery() {
|
||||
return id(new LegalpadDocumentQuery())
|
||||
->needViewerSignatures(true);
|
||||
|
||||
$creator_phids = $saved->getParameter('creatorPHIDs', array());
|
||||
if ($creator_phids) {
|
||||
$query->withCreatorPHIDs($creator_phids);
|
||||
}
|
||||
|
||||
$contributor_phids = $saved->getParameter('contributorPHIDs', array());
|
||||
if ($contributor_phids) {
|
||||
$query->withContributorPHIDs($contributor_phids);
|
||||
protected function buildCustomSearchFields() {
|
||||
return array(
|
||||
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')) {
|
||||
$viewer_phid = $this->requireViewer()->getPHID();
|
||||
if ($viewer_phid) {
|
||||
$query->withSignerPHIDs(array($viewer_phid));
|
||||
}
|
||||
protected function buildQueryFromParameters(array $map) {
|
||||
$query = $this->newQuery();
|
||||
|
||||
if ($map['signerPHIDs']) {
|
||||
$query->withSignerPHIDs($map['signerPHIDs']);
|
||||
}
|
||||
|
||||
$start = $this->parseDateTime($saved->getParameter('createdStart'));
|
||||
$end = $this->parseDateTime($saved->getParameter('createdEnd'));
|
||||
|
||||
if ($start) {
|
||||
$query->withDateCreatedAfter($start);
|
||||
if ($map['contributorPHIDs']) {
|
||||
$query->withContributorPHIDs($map['creatorPHIDs']);
|
||||
}
|
||||
|
||||
if ($end) {
|
||||
$query->withDateCreatedBefore($end);
|
||||
if ($map['creatorPHIDs']) {
|
||||
$query->withCreatorPHIDs($map['creatorPHIDs']);
|
||||
}
|
||||
|
||||
if ($map['createdStart']) {
|
||||
$query->withDateCreatedAfter($map['createdStart']);
|
||||
}
|
||||
|
||||
if ($map['createdEnd']) {
|
||||
$query->withDateCreatedAfter($map['createdStart']);
|
||||
}
|
||||
|
||||
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) {
|
||||
return '/legalpad/'.$path;
|
||||
}
|
||||
@@ -130,10 +91,11 @@ final class LegalpadDocumentSearchEngine
|
||||
$query = $this->newSavedQuery();
|
||||
$query->setQueryKey($query_key);
|
||||
|
||||
$viewer = $this->requireViewer();
|
||||
|
||||
switch ($query_key) {
|
||||
case 'signed':
|
||||
return $query
|
||||
->setParameter('withViewerSignature', true);
|
||||
return $query->setParameter('signerPHIDs', array($viewer->getPHID()));
|
||||
case 'all':
|
||||
return $query;
|
||||
}
|
||||
@@ -141,12 +103,6 @@ final class LegalpadDocumentSearchEngine
|
||||
return parent::buildSavedQueryFromBuiltin($query_key);
|
||||
}
|
||||
|
||||
protected function getRequiredHandlePHIDsForResultList(
|
||||
array $documents,
|
||||
PhabricatorSavedQuery $query) {
|
||||
return array();
|
||||
}
|
||||
|
||||
protected function renderResultList(
|
||||
array $documents,
|
||||
PhabricatorSavedQuery $query,
|
||||
|
||||
@@ -21,6 +21,16 @@ final class ManiphestGetTaskTransactionsConduitAPIMethod
|
||||
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) {
|
||||
$results = array();
|
||||
$task_ids = $request->getValue('ids');
|
||||
|
||||
@@ -57,5 +57,15 @@ final class ManiphestTaskDescriptionTransaction
|
||||
return $changes;
|
||||
}
|
||||
|
||||
public function getTransactionTypeForConduit($xaction) {
|
||||
return 'description';
|
||||
}
|
||||
|
||||
public function getFieldValuesForConduit($xaction, $data) {
|
||||
return array(
|
||||
'old' => $xaction->getOldValue(),
|
||||
'new' => $xaction->getNewValue(),
|
||||
);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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(),
|
||||
);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -107,4 +107,16 @@ final class ManiphestTaskPointsTransaction
|
||||
return $value;
|
||||
}
|
||||
|
||||
public function getTransactionTypeForConduit($xaction) {
|
||||
return 'points';
|
||||
}
|
||||
|
||||
public function getFieldValuesForConduit($xaction, $data) {
|
||||
return array(
|
||||
'old' => $xaction->getOldValue(),
|
||||
'new' => $xaction->getNewValue(),
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
@@ -6,10 +6,7 @@ final class ManiphestTaskPriorityTransaction
|
||||
const TRANSACTIONTYPE = 'priority';
|
||||
|
||||
public function generateOldValue($object) {
|
||||
if ($this->isNewObject()) {
|
||||
return null;
|
||||
}
|
||||
return $object->getPriority();
|
||||
return (string)$object->getPriority();
|
||||
}
|
||||
|
||||
public function generateNewValue($object, $value) {
|
||||
@@ -19,7 +16,7 @@ final class ManiphestTaskPriorityTransaction
|
||||
// should still be allowed, even if the priority is no longer
|
||||
// valid, so treat this as a no-op.
|
||||
if ($value === ManiphestTaskPriority::UNKNOWN_PRIORITY_KEYWORD) {
|
||||
return $object->getPriority();
|
||||
return (string)$object->getPriority();
|
||||
}
|
||||
|
||||
return (string)ManiphestTaskPriority::getTaskPriorityFromKeyword($value);
|
||||
|
||||
@@ -6,9 +6,6 @@ final class ManiphestTaskStatusTransaction
|
||||
const TRANSACTIONTYPE = 'status';
|
||||
|
||||
public function generateOldValue($object) {
|
||||
if ($this->isNewObject()) {
|
||||
return null;
|
||||
}
|
||||
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(),
|
||||
);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -72,4 +72,15 @@ final class ManiphestTaskTitleTransaction
|
||||
return $errors;
|
||||
}
|
||||
|
||||
public function getTransactionTypeForConduit($xaction) {
|
||||
return 'title';
|
||||
}
|
||||
|
||||
public function getFieldValuesForConduit($xaction, $data) {
|
||||
return array(
|
||||
'old' => $xaction->getOldValue(),
|
||||
'new' => $xaction->getNewValue(),
|
||||
);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,11 @@
|
||||
<?php
|
||||
|
||||
final class PhabricatorQuickSearchApplicationEngineExtension
|
||||
extends PhabricatorQuickSearchEngineExtension {
|
||||
|
||||
public function newQuickSearchDatasources() {
|
||||
return array(
|
||||
new PhabricatorApplicationDatasource(),
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -6,6 +6,12 @@ abstract class PhabricatorMetaMTAEmailHeraldAction
|
||||
const DO_SEND = 'do.send';
|
||||
const DO_FORCE = 'do.force';
|
||||
|
||||
public function getRequiredAdapterStates() {
|
||||
return array(
|
||||
HeraldMailableState::STATECONST,
|
||||
);
|
||||
}
|
||||
|
||||
public function supportsObject($object) {
|
||||
// NOTE: This implementation lacks generality, but there's no great way to
|
||||
// figure out if something generates email right now.
|
||||
|
||||
@@ -15,4 +15,8 @@ final class PhabricatorOwnersPackageTransaction
|
||||
return 'PhabricatorOwnersPackageTransactionType';
|
||||
}
|
||||
|
||||
public function getApplicationTransactionCommentObject() {
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -9,6 +9,10 @@ final class PeopleMainMenuBarExtension
|
||||
return $viewer->isLoggedIn();
|
||||
}
|
||||
|
||||
public function shouldRequireFullSession() {
|
||||
return false;
|
||||
}
|
||||
|
||||
public function getExtensionOrder() {
|
||||
return 1200;
|
||||
}
|
||||
@@ -65,6 +69,7 @@ final class PeopleMainMenuBarExtension
|
||||
$view = id(new PhabricatorActionListView())
|
||||
->setViewer($viewer);
|
||||
|
||||
if ($this->getIsFullSession()) {
|
||||
$view->addAction(
|
||||
id(new PhabricatorActionView())
|
||||
->appendChild($user_view));
|
||||
@@ -101,6 +106,7 @@ final class PeopleMainMenuBarExtension
|
||||
id(new PhabricatorActionView())
|
||||
->addSigil('logout-item')
|
||||
->setType(PhabricatorActionView::TYPE_DIVIDER));
|
||||
}
|
||||
|
||||
$view->addAction(
|
||||
id(new PhabricatorActionView())
|
||||
|
||||
@@ -0,0 +1,11 @@
|
||||
<?php
|
||||
|
||||
final class PhabricatorPeopleQuickSearchEngineExtension
|
||||
extends PhabricatorQuickSearchEngineExtension {
|
||||
|
||||
public function newQuickSearchDatasources() {
|
||||
return array(
|
||||
new PhabricatorPeopleDatasource(),
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -15,6 +15,10 @@ final class PhameBlogTransaction
|
||||
return PhabricatorPhameBlogPHIDType::TYPECONST;
|
||||
}
|
||||
|
||||
public function getApplicationTransactionCommentObject() {
|
||||
return null;
|
||||
}
|
||||
|
||||
public function getBaseTransactionClass() {
|
||||
return 'PhameBlogTransactionType';
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -11,7 +11,8 @@ final class PonderQuestion extends PonderDAO
|
||||
PhabricatorProjectInterface,
|
||||
PhabricatorDestructibleInterface,
|
||||
PhabricatorSpacesInterface,
|
||||
PhabricatorFulltextInterface {
|
||||
PhabricatorFulltextInterface,
|
||||
PhabricatorFerretInterface {
|
||||
|
||||
const MARKUP_FIELD_CONTENT = 'markup:content';
|
||||
|
||||
@@ -300,4 +301,13 @@ final class PonderQuestion extends PonderDAO
|
||||
return new PonderQuestionFulltextEngine();
|
||||
}
|
||||
|
||||
|
||||
/* -( PhabricatorFerretInterface )----------------------------------------- */
|
||||
|
||||
|
||||
public function newFerretEngine() {
|
||||
return new PonderQuestionFerretEngine();
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
@@ -146,8 +146,7 @@ final class PhabricatorProjectCoreTestCase extends PhabricatorTestCase {
|
||||
$user = $this->createUser();
|
||||
$user->save();
|
||||
|
||||
$user2 = $this->createUser();
|
||||
$user2->save();
|
||||
$user->setAllowInlineCacheGeneration(true);
|
||||
|
||||
$proj = $this->createProject($user);
|
||||
|
||||
@@ -1289,12 +1288,19 @@ final class PhabricatorProjectCoreTestCase extends PhabricatorTestCase {
|
||||
|
||||
$new_name = $proj->getName().' '.mt_rand();
|
||||
|
||||
$xaction = new PhabricatorProjectTransaction();
|
||||
$xaction->setTransactionType(
|
||||
PhabricatorProjectNameTransaction::TRANSACTIONTYPE);
|
||||
$xaction->setNewValue($new_name);
|
||||
$params = array(
|
||||
'objectIdentifier' => $proj->getID(),
|
||||
'transactions' => array(
|
||||
array(
|
||||
'type' => 'name',
|
||||
'value' => $new_name,
|
||||
),
|
||||
),
|
||||
);
|
||||
|
||||
$this->applyTransactions($proj, $user, array($xaction));
|
||||
id(new ConduitCall('project.edit', $params))
|
||||
->setUser($user)
|
||||
->execute();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -120,16 +120,6 @@ final class PhabricatorProjectTransactionEditor
|
||||
PhabricatorApplicationTransaction $xaction) {
|
||||
|
||||
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:
|
||||
PhabricatorPolicyFilter::requireCapability(
|
||||
$this->requireActor(),
|
||||
|
||||
@@ -67,7 +67,9 @@ final class PhabricatorBoardRenderingEngine extends Phobject {
|
||||
$project_phids = $object->getProjectPHIDs();
|
||||
$project_handles = array_select_keys($this->handles, $project_phids);
|
||||
if ($project_handles) {
|
||||
$card->setProjectHandles($project_handles);
|
||||
$card
|
||||
->setHideArchivedProjects(true)
|
||||
->setProjectHandles($project_handles);
|
||||
}
|
||||
|
||||
$cover_phid = $object->getCoverImageThumbnailPHID();
|
||||
|
||||
@@ -0,0 +1,11 @@
|
||||
<?php
|
||||
|
||||
final class ProjectQuickSearchEngineExtension
|
||||
extends PhabricatorQuickSearchEngineExtension {
|
||||
|
||||
public function newQuickSearchDatasources() {
|
||||
return array(
|
||||
new PhabricatorProjectDatasource(),
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -8,8 +8,16 @@ final class PhabricatorProjectUIEventListener
|
||||
}
|
||||
|
||||
public function handleEvent(PhutilEvent $event) {
|
||||
$object = $event->getValue('object');
|
||||
|
||||
switch ($event->getType()) {
|
||||
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);
|
||||
break;
|
||||
}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user