Merge branch 'master' into blender-tweaks
This commit is contained in:
		| @@ -9,11 +9,11 @@ return array( | |||||||
|   'names' => array( |   'names' => array( | ||||||
|     'conpherence.pkg.css' => 'e68cf1fa', |     'conpherence.pkg.css' => 'e68cf1fa', | ||||||
|     'conpherence.pkg.js' => '15191c65', |     'conpherence.pkg.js' => '15191c65', | ||||||
|     'core.pkg.css' => '16369829', |     'core.pkg.css' => '8ccb0ba3', | ||||||
|     'core.pkg.js' => '4c79d74f', |     'core.pkg.js' => '4c79d74f', | ||||||
|     'darkconsole.pkg.js' => '1f9a31bc', |     'darkconsole.pkg.js' => '1f9a31bc', | ||||||
|     'differential.pkg.css' => '45951e9e', |     'differential.pkg.css' => '45951e9e', | ||||||
|     'differential.pkg.js' => 'b71b8c5d', |     'differential.pkg.js' => '500a75c5', | ||||||
|     'diffusion.pkg.css' => 'a2d17c7d', |     'diffusion.pkg.css' => 'a2d17c7d', | ||||||
|     'diffusion.pkg.js' => '6134c5a1', |     'diffusion.pkg.js' => '6134c5a1', | ||||||
|     'favicon.ico' => '4d48ee79', |     'favicon.ico' => '4d48ee79', | ||||||
| @@ -73,7 +73,7 @@ return array( | |||||||
|     'rsrc/css/application/diffusion/diffusion-icons.css' => '0c15255e', |     'rsrc/css/application/diffusion/diffusion-icons.css' => '0c15255e', | ||||||
|     'rsrc/css/application/diffusion/diffusion-readme.css' => '419dd5b6', |     'rsrc/css/application/diffusion/diffusion-readme.css' => '419dd5b6', | ||||||
|     'rsrc/css/application/diffusion/diffusion-repository.css' => 'ee6f20ec', |     'rsrc/css/application/diffusion/diffusion-repository.css' => 'ee6f20ec', | ||||||
|     'rsrc/css/application/diffusion/diffusion-source.css' => '69ac9399', |     'rsrc/css/application/diffusion/diffusion-source.css' => '5f35a3bd', | ||||||
|     'rsrc/css/application/diffusion/diffusion.css' => '45727264', |     'rsrc/css/application/diffusion/diffusion.css' => '45727264', | ||||||
|     'rsrc/css/application/feed/feed.css' => 'ecd4ec57', |     'rsrc/css/application/feed/feed.css' => 'ecd4ec57', | ||||||
|     'rsrc/css/application/files/global-drag-and-drop.css' => 'b556a948', |     'rsrc/css/application/files/global-drag-and-drop.css' => 'b556a948', | ||||||
| @@ -176,7 +176,7 @@ return array( | |||||||
|     'rsrc/css/phui/phui-spacing.css' => '042804d6', |     'rsrc/css/phui/phui-spacing.css' => '042804d6', | ||||||
|     'rsrc/css/phui/phui-status.css' => 'd5263e49', |     'rsrc/css/phui/phui-status.css' => 'd5263e49', | ||||||
|     'rsrc/css/phui/phui-tag-view.css' => 'b4719c50', |     'rsrc/css/phui/phui-tag-view.css' => 'b4719c50', | ||||||
|     'rsrc/css/phui/phui-timeline-view.css' => 'f21db7ca', |     'rsrc/css/phui/phui-timeline-view.css' => 'e2ef62b1', | ||||||
|     'rsrc/css/phui/phui-two-column-view.css' => '44ec4951', |     'rsrc/css/phui/phui-two-column-view.css' => '44ec4951', | ||||||
|     'rsrc/css/phui/workboards/phui-workboard-color.css' => '783cdff5', |     'rsrc/css/phui/workboards/phui-workboard-color.css' => '783cdff5', | ||||||
|     'rsrc/css/phui/workboards/phui-workboard.css' => '3bc85455', |     'rsrc/css/phui/workboards/phui-workboard.css' => '3bc85455', | ||||||
| @@ -398,7 +398,7 @@ return array( | |||||||
|     'rsrc/js/application/dashboard/behavior-dashboard-query-panel-select.js' => '453c5375', |     'rsrc/js/application/dashboard/behavior-dashboard-query-panel-select.js' => '453c5375', | ||||||
|     'rsrc/js/application/dashboard/behavior-dashboard-tab-panel.js' => 'd4eecc63', |     'rsrc/js/application/dashboard/behavior-dashboard-tab-panel.js' => 'd4eecc63', | ||||||
|     'rsrc/js/application/diff/DiffChangeset.js' => '99abf4cd', |     'rsrc/js/application/diff/DiffChangeset.js' => '99abf4cd', | ||||||
|     'rsrc/js/application/diff/DiffChangesetList.js' => '8f1cd52c', |     'rsrc/js/application/diff/DiffChangesetList.js' => '3b77efdd', | ||||||
|     'rsrc/js/application/diff/DiffInline.js' => 'e83d28f3', |     'rsrc/js/application/diff/DiffInline.js' => 'e83d28f3', | ||||||
|     'rsrc/js/application/diff/behavior-preview-link.js' => '051c7832', |     'rsrc/js/application/diff/behavior-preview-link.js' => '051c7832', | ||||||
|     'rsrc/js/application/differential/behavior-comment-preview.js' => '51c5ad07', |     'rsrc/js/application/differential/behavior-comment-preview.js' => '51c5ad07', | ||||||
| @@ -446,7 +446,7 @@ return array( | |||||||
|     'rsrc/js/application/releeph/releeph-preview-branch.js' => 'b2b4fbaf', |     'rsrc/js/application/releeph/releeph-preview-branch.js' => 'b2b4fbaf', | ||||||
|     'rsrc/js/application/releeph/releeph-request-state-change.js' => 'a0b57eb8', |     'rsrc/js/application/releeph/releeph-request-state-change.js' => 'a0b57eb8', | ||||||
|     'rsrc/js/application/releeph/releeph-request-typeahead.js' => 'de2e896f', |     'rsrc/js/application/releeph/releeph-request-typeahead.js' => 'de2e896f', | ||||||
|     'rsrc/js/application/repository/repository-crossreference.js' => 'e5339c43', |     'rsrc/js/application/repository/repository-crossreference.js' => '7fe9bc12', | ||||||
|     'rsrc/js/application/search/behavior-reorder-profile-menu-items.js' => 'e2e0a072', |     'rsrc/js/application/search/behavior-reorder-profile-menu-items.js' => 'e2e0a072', | ||||||
|     'rsrc/js/application/search/behavior-reorder-queries.js' => 'e9581f08', |     'rsrc/js/application/search/behavior-reorder-queries.js' => 'e9581f08', | ||||||
|     'rsrc/js/application/slowvote/behavior-slowvote-embed.js' => '887ad43f', |     'rsrc/js/application/slowvote/behavior-slowvote-embed.js' => '887ad43f', | ||||||
| @@ -574,7 +574,7 @@ return array( | |||||||
|     'diffusion-icons-css' => '0c15255e', |     'diffusion-icons-css' => '0c15255e', | ||||||
|     'diffusion-readme-css' => '419dd5b6', |     'diffusion-readme-css' => '419dd5b6', | ||||||
|     'diffusion-repository-css' => 'ee6f20ec', |     'diffusion-repository-css' => 'ee6f20ec', | ||||||
|     'diffusion-source-css' => '69ac9399', |     'diffusion-source-css' => '5f35a3bd', | ||||||
|     'diviner-shared-css' => '896f1d43', |     'diviner-shared-css' => '896f1d43', | ||||||
|     'font-fontawesome' => 'e838e088', |     'font-fontawesome' => 'e838e088', | ||||||
|     'font-lato' => 'c7ccd872', |     'font-lato' => 'c7ccd872', | ||||||
| @@ -692,7 +692,7 @@ return array( | |||||||
|     'javelin-behavior-reorder-applications' => '76b9fc3e', |     'javelin-behavior-reorder-applications' => '76b9fc3e', | ||||||
|     'javelin-behavior-reorder-columns' => 'e1d25dfb', |     'javelin-behavior-reorder-columns' => 'e1d25dfb', | ||||||
|     'javelin-behavior-reorder-profile-menu-items' => 'e2e0a072', |     'javelin-behavior-reorder-profile-menu-items' => 'e2e0a072', | ||||||
|     'javelin-behavior-repository-crossreference' => 'e5339c43', |     'javelin-behavior-repository-crossreference' => '7fe9bc12', | ||||||
|     'javelin-behavior-scrollbar' => '834a1173', |     'javelin-behavior-scrollbar' => '834a1173', | ||||||
|     'javelin-behavior-search-reorder-queries' => 'e9581f08', |     'javelin-behavior-search-reorder-queries' => 'e9581f08', | ||||||
|     'javelin-behavior-select-content' => 'bf5374ef', |     'javelin-behavior-select-content' => 'bf5374ef', | ||||||
| @@ -777,7 +777,7 @@ return array( | |||||||
|     'phabricator-darkmessage' => 'c48cccdd', |     'phabricator-darkmessage' => 'c48cccdd', | ||||||
|     'phabricator-dashboard-css' => 'fe5b1869', |     'phabricator-dashboard-css' => 'fe5b1869', | ||||||
|     'phabricator-diff-changeset' => '99abf4cd', |     'phabricator-diff-changeset' => '99abf4cd', | ||||||
|     'phabricator-diff-changeset-list' => '8f1cd52c', |     'phabricator-diff-changeset-list' => '3b77efdd', | ||||||
|     'phabricator-diff-inline' => 'e83d28f3', |     'phabricator-diff-inline' => 'e83d28f3', | ||||||
|     'phabricator-drag-and-drop-file-upload' => '58dea2fa', |     'phabricator-drag-and-drop-file-upload' => '58dea2fa', | ||||||
|     'phabricator-draggable-list' => 'bea6e7f4', |     'phabricator-draggable-list' => 'bea6e7f4', | ||||||
| @@ -874,7 +874,7 @@ return array( | |||||||
|     'phui-status-list-view-css' => 'd5263e49', |     'phui-status-list-view-css' => 'd5263e49', | ||||||
|     'phui-tag-view-css' => 'b4719c50', |     'phui-tag-view-css' => 'b4719c50', | ||||||
|     'phui-theme-css' => '9f261c6b', |     'phui-theme-css' => '9f261c6b', | ||||||
|     'phui-timeline-view-css' => 'f21db7ca', |     'phui-timeline-view-css' => 'e2ef62b1', | ||||||
|     'phui-two-column-view-css' => '44ec4951', |     'phui-two-column-view-css' => '44ec4951', | ||||||
|     'phui-workboard-color-css' => '783cdff5', |     'phui-workboard-color-css' => '783cdff5', | ||||||
|     'phui-workboard-view-css' => '3bc85455', |     'phui-workboard-view-css' => '3bc85455', | ||||||
| @@ -1137,6 +1137,10 @@ return array( | |||||||
|       'javelin-dom', |       'javelin-dom', | ||||||
|       'javelin-magical-init', |       'javelin-magical-init', | ||||||
|     ), |     ), | ||||||
|  |     '3b77efdd' => array( | ||||||
|  |       'javelin-install', | ||||||
|  |       'phuix-button-view', | ||||||
|  |     ), | ||||||
|     '3cb0b2fc' => array( |     '3cb0b2fc' => array( | ||||||
|       'javelin-behavior', |       'javelin-behavior', | ||||||
|       'javelin-dom', |       'javelin-dom', | ||||||
| @@ -1549,6 +1553,12 @@ return array( | |||||||
|     '7f243deb' => array( |     '7f243deb' => array( | ||||||
|       'javelin-install', |       'javelin-install', | ||||||
|     ), |     ), | ||||||
|  |     '7fe9bc12' => array( | ||||||
|  |       'javelin-behavior', | ||||||
|  |       'javelin-dom', | ||||||
|  |       'javelin-stratcom', | ||||||
|  |       'javelin-uri', | ||||||
|  |     ), | ||||||
|     '834a1173' => array( |     '834a1173' => array( | ||||||
|       'javelin-behavior', |       'javelin-behavior', | ||||||
|       'javelin-scrollbar', |       'javelin-scrollbar', | ||||||
| @@ -1607,10 +1617,6 @@ return array( | |||||||
|     '8e1baf68' => array( |     '8e1baf68' => array( | ||||||
|       'phui-button-css', |       'phui-button-css', | ||||||
|     ), |     ), | ||||||
|     '8f1cd52c' => array( |  | ||||||
|       'javelin-install', |  | ||||||
|       'phuix-button-view', |  | ||||||
|     ), |  | ||||||
|     '8f29b364' => array( |     '8f29b364' => array( | ||||||
|       'javelin-behavior', |       'javelin-behavior', | ||||||
|       'javelin-stratcom', |       'javelin-stratcom', | ||||||
| @@ -2076,12 +2082,6 @@ return array( | |||||||
|       'javelin-behavior', |       'javelin-behavior', | ||||||
|       'javelin-dom', |       'javelin-dom', | ||||||
|     ), |     ), | ||||||
|     'e5339c43' => array( |  | ||||||
|       'javelin-behavior', |  | ||||||
|       'javelin-dom', |  | ||||||
|       'javelin-stratcom', |  | ||||||
|       'javelin-uri', |  | ||||||
|     ), |  | ||||||
|     'e5822781' => array( |     'e5822781' => array( | ||||||
|       'javelin-behavior', |       'javelin-behavior', | ||||||
|       'javelin-dom', |       'javelin-dom', | ||||||
|   | |||||||
| @@ -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 = new MimeMailParser(); | ||||||
| $parser->setText(file_get_contents('php://stdin')); | $parser->setText(file_get_contents('php://stdin')); | ||||||
|  |  | ||||||
| $text_body = $parser->getMessageBody('text'); | $content = array(); | ||||||
|  | foreach (array('text', 'html') as $part) { | ||||||
|  |   $part_body = $parser->getMessageBody($part); | ||||||
|  |  | ||||||
| $text_body_headers = $parser->getMessageBodyHeaders('text'); |   if (strlen($part_body) && !phutil_is_utf8($part_body)) { | ||||||
| $content_type = idx($text_body_headers, 'content-type'); |     $part_headers = $parser->getMessageBodyHeaders($part); | ||||||
| if ( |     if (!is_array($part_headers)) { | ||||||
|   !phutil_is_utf8($text_body) && |       $part_headers = array(); | ||||||
|   (preg_match('/charset="(.*?)"/', $content_type, $matches) || |     } | ||||||
|    preg_match('/charset=(\S+)/', $content_type, $matches)) |     $content_type = idx($part_headers, 'content-type'); | ||||||
| ) { |     if (preg_match('/charset="(.*?)"/', $content_type, $matches) || | ||||||
|   $text_body = phutil_utf8_convert($text_body, 'UTF-8', $matches[1]); |         preg_match('/charset=(\S+)/', $content_type, $matches)) { | ||||||
|  |       $part_body = phutil_utf8_convert($part_body, 'UTF-8', $matches[1]); | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   $content[$part] = $part_body; | ||||||
| } | } | ||||||
|  |  | ||||||
| $headers = $parser->getHeaders(); | $headers = $parser->getHeaders(); | ||||||
| @@ -57,10 +64,7 @@ if ($args->getArg('process-duplicates')) { | |||||||
|  |  | ||||||
| $received = new PhabricatorMetaMTAReceivedMail(); | $received = new PhabricatorMetaMTAReceivedMail(); | ||||||
| $received->setHeaders($headers); | $received->setHeaders($headers); | ||||||
| $received->setBodies(array( | $received->setBodies($content); | ||||||
|   'text' => $text_body, |  | ||||||
|   'html' => $parser->getMessageBody('html'), |  | ||||||
| )); |  | ||||||
|  |  | ||||||
| $attachments = array(); | $attachments = array(); | ||||||
| foreach ($parser->getAttachments() as $attachment) { | foreach ($parser->getAttachments() as $attachment) { | ||||||
|   | |||||||
| @@ -439,11 +439,14 @@ phutil_register_library_map(array( | |||||||
|     'DifferentialDiffQuery' => 'applications/differential/query/DifferentialDiffQuery.php', |     'DifferentialDiffQuery' => 'applications/differential/query/DifferentialDiffQuery.php', | ||||||
|     'DifferentialDiffRepositoryHeraldField' => 'applications/differential/herald/DifferentialDiffRepositoryHeraldField.php', |     'DifferentialDiffRepositoryHeraldField' => 'applications/differential/herald/DifferentialDiffRepositoryHeraldField.php', | ||||||
|     'DifferentialDiffRepositoryProjectsHeraldField' => 'applications/differential/herald/DifferentialDiffRepositoryProjectsHeraldField.php', |     'DifferentialDiffRepositoryProjectsHeraldField' => 'applications/differential/herald/DifferentialDiffRepositoryProjectsHeraldField.php', | ||||||
|  |     'DifferentialDiffSearchConduitAPIMethod' => 'applications/differential/conduit/DifferentialDiffSearchConduitAPIMethod.php', | ||||||
|  |     'DifferentialDiffSearchEngine' => 'applications/differential/query/DifferentialDiffSearchEngine.php', | ||||||
|     'DifferentialDiffTestCase' => 'applications/differential/storage/__tests__/DifferentialDiffTestCase.php', |     'DifferentialDiffTestCase' => 'applications/differential/storage/__tests__/DifferentialDiffTestCase.php', | ||||||
|     'DifferentialDiffTransaction' => 'applications/differential/storage/DifferentialDiffTransaction.php', |     'DifferentialDiffTransaction' => 'applications/differential/storage/DifferentialDiffTransaction.php', | ||||||
|     'DifferentialDiffTransactionQuery' => 'applications/differential/query/DifferentialDiffTransactionQuery.php', |     'DifferentialDiffTransactionQuery' => 'applications/differential/query/DifferentialDiffTransactionQuery.php', | ||||||
|     'DifferentialDiffViewController' => 'applications/differential/controller/DifferentialDiffViewController.php', |     'DifferentialDiffViewController' => 'applications/differential/controller/DifferentialDiffViewController.php', | ||||||
|     'DifferentialDoorkeeperRevisionFeedStoryPublisher' => 'applications/differential/doorkeeper/DifferentialDoorkeeperRevisionFeedStoryPublisher.php', |     'DifferentialDoorkeeperRevisionFeedStoryPublisher' => 'applications/differential/doorkeeper/DifferentialDoorkeeperRevisionFeedStoryPublisher.php', | ||||||
|  |     'DifferentialDraftField' => 'applications/differential/customfield/DifferentialDraftField.php', | ||||||
|     'DifferentialExactUserFunctionDatasource' => 'applications/differential/typeahead/DifferentialExactUserFunctionDatasource.php', |     'DifferentialExactUserFunctionDatasource' => 'applications/differential/typeahead/DifferentialExactUserFunctionDatasource.php', | ||||||
|     'DifferentialFieldParseException' => 'applications/differential/exception/DifferentialFieldParseException.php', |     'DifferentialFieldParseException' => 'applications/differential/exception/DifferentialFieldParseException.php', | ||||||
|     'DifferentialFieldValidationException' => 'applications/differential/exception/DifferentialFieldValidationException.php', |     'DifferentialFieldValidationException' => 'applications/differential/exception/DifferentialFieldValidationException.php', | ||||||
| @@ -457,6 +460,7 @@ phutil_register_library_map(array( | |||||||
|     'DifferentialGetWorkingCopy' => 'applications/differential/DifferentialGetWorkingCopy.php', |     'DifferentialGetWorkingCopy' => 'applications/differential/DifferentialGetWorkingCopy.php', | ||||||
|     'DifferentialGitSVNIDCommitMessageField' => 'applications/differential/field/DifferentialGitSVNIDCommitMessageField.php', |     'DifferentialGitSVNIDCommitMessageField' => 'applications/differential/field/DifferentialGitSVNIDCommitMessageField.php', | ||||||
|     'DifferentialHarbormasterField' => 'applications/differential/customfield/DifferentialHarbormasterField.php', |     'DifferentialHarbormasterField' => 'applications/differential/customfield/DifferentialHarbormasterField.php', | ||||||
|  |     'DifferentialHeraldStateReasons' => 'applications/differential/herald/DifferentialHeraldStateReasons.php', | ||||||
|     'DifferentialHiddenComment' => 'applications/differential/storage/DifferentialHiddenComment.php', |     'DifferentialHiddenComment' => 'applications/differential/storage/DifferentialHiddenComment.php', | ||||||
|     'DifferentialHostField' => 'applications/differential/customfield/DifferentialHostField.php', |     'DifferentialHostField' => 'applications/differential/customfield/DifferentialHostField.php', | ||||||
|     'DifferentialHovercardEngineExtension' => 'applications/differential/engineextension/DifferentialHovercardEngineExtension.php', |     'DifferentialHovercardEngineExtension' => 'applications/differential/engineextension/DifferentialHovercardEngineExtension.php', | ||||||
| @@ -546,6 +550,7 @@ phutil_register_library_map(array( | |||||||
|     'DifferentialRevisionHasTaskRelationship' => 'applications/differential/relationships/DifferentialRevisionHasTaskRelationship.php', |     'DifferentialRevisionHasTaskRelationship' => 'applications/differential/relationships/DifferentialRevisionHasTaskRelationship.php', | ||||||
|     'DifferentialRevisionHeraldField' => 'applications/differential/herald/DifferentialRevisionHeraldField.php', |     'DifferentialRevisionHeraldField' => 'applications/differential/herald/DifferentialRevisionHeraldField.php', | ||||||
|     'DifferentialRevisionHeraldFieldGroup' => 'applications/differential/herald/DifferentialRevisionHeraldFieldGroup.php', |     'DifferentialRevisionHeraldFieldGroup' => 'applications/differential/herald/DifferentialRevisionHeraldFieldGroup.php', | ||||||
|  |     'DifferentialRevisionHoldDraftTransaction' => 'applications/differential/xaction/DifferentialRevisionHoldDraftTransaction.php', | ||||||
|     'DifferentialRevisionIDCommitMessageField' => 'applications/differential/field/DifferentialRevisionIDCommitMessageField.php', |     'DifferentialRevisionIDCommitMessageField' => 'applications/differential/field/DifferentialRevisionIDCommitMessageField.php', | ||||||
|     'DifferentialRevisionInlineTransaction' => 'applications/differential/xaction/DifferentialRevisionInlineTransaction.php', |     'DifferentialRevisionInlineTransaction' => 'applications/differential/xaction/DifferentialRevisionInlineTransaction.php', | ||||||
|     'DifferentialRevisionInlinesController' => 'applications/differential/controller/DifferentialRevisionInlinesController.php', |     'DifferentialRevisionInlinesController' => 'applications/differential/controller/DifferentialRevisionInlinesController.php', | ||||||
| @@ -589,6 +594,7 @@ phutil_register_library_map(array( | |||||||
|     'DifferentialRevisionUpdateHistoryView' => 'applications/differential/view/DifferentialRevisionUpdateHistoryView.php', |     'DifferentialRevisionUpdateHistoryView' => 'applications/differential/view/DifferentialRevisionUpdateHistoryView.php', | ||||||
|     'DifferentialRevisionViewController' => 'applications/differential/controller/DifferentialRevisionViewController.php', |     'DifferentialRevisionViewController' => 'applications/differential/controller/DifferentialRevisionViewController.php', | ||||||
|     'DifferentialRevisionVoidTransaction' => 'applications/differential/xaction/DifferentialRevisionVoidTransaction.php', |     'DifferentialRevisionVoidTransaction' => 'applications/differential/xaction/DifferentialRevisionVoidTransaction.php', | ||||||
|  |     'DifferentialRevisionWrongStateTransaction' => 'applications/differential/xaction/DifferentialRevisionWrongStateTransaction.php', | ||||||
|     'DifferentialSchemaSpec' => 'applications/differential/storage/DifferentialSchemaSpec.php', |     'DifferentialSchemaSpec' => 'applications/differential/storage/DifferentialSchemaSpec.php', | ||||||
|     'DifferentialSetDiffPropertyConduitAPIMethod' => 'applications/differential/conduit/DifferentialSetDiffPropertyConduitAPIMethod.php', |     'DifferentialSetDiffPropertyConduitAPIMethod' => 'applications/differential/conduit/DifferentialSetDiffPropertyConduitAPIMethod.php', | ||||||
|     'DifferentialStoredCustomField' => 'applications/differential/customfield/DifferentialStoredCustomField.php', |     'DifferentialStoredCustomField' => 'applications/differential/customfield/DifferentialStoredCustomField.php', | ||||||
| @@ -763,6 +769,7 @@ phutil_register_library_map(array( | |||||||
|     'DiffusionMercurialBlameQuery' => 'applications/diffusion/query/blame/DiffusionMercurialBlameQuery.php', |     'DiffusionMercurialBlameQuery' => 'applications/diffusion/query/blame/DiffusionMercurialBlameQuery.php', | ||||||
|     'DiffusionMercurialCommandEngine' => 'applications/diffusion/protocol/DiffusionMercurialCommandEngine.php', |     'DiffusionMercurialCommandEngine' => 'applications/diffusion/protocol/DiffusionMercurialCommandEngine.php', | ||||||
|     'DiffusionMercurialFileContentQuery' => 'applications/diffusion/query/filecontent/DiffusionMercurialFileContentQuery.php', |     'DiffusionMercurialFileContentQuery' => 'applications/diffusion/query/filecontent/DiffusionMercurialFileContentQuery.php', | ||||||
|  |     'DiffusionMercurialFlagInjectionException' => 'applications/diffusion/exception/DiffusionMercurialFlagInjectionException.php', | ||||||
|     'DiffusionMercurialRawDiffQuery' => 'applications/diffusion/query/rawdiff/DiffusionMercurialRawDiffQuery.php', |     'DiffusionMercurialRawDiffQuery' => 'applications/diffusion/query/rawdiff/DiffusionMercurialRawDiffQuery.php', | ||||||
|     'DiffusionMercurialRequest' => 'applications/diffusion/request/DiffusionMercurialRequest.php', |     'DiffusionMercurialRequest' => 'applications/diffusion/request/DiffusionMercurialRequest.php', | ||||||
|     'DiffusionMercurialResponse' => 'applications/diffusion/response/DiffusionMercurialResponse.php', |     'DiffusionMercurialResponse' => 'applications/diffusion/response/DiffusionMercurialResponse.php', | ||||||
| @@ -825,6 +832,7 @@ phutil_register_library_map(array( | |||||||
|     'DiffusionQueryCommitsConduitAPIMethod' => 'applications/diffusion/conduit/DiffusionQueryCommitsConduitAPIMethod.php', |     'DiffusionQueryCommitsConduitAPIMethod' => 'applications/diffusion/conduit/DiffusionQueryCommitsConduitAPIMethod.php', | ||||||
|     'DiffusionQueryConduitAPIMethod' => 'applications/diffusion/conduit/DiffusionQueryConduitAPIMethod.php', |     'DiffusionQueryConduitAPIMethod' => 'applications/diffusion/conduit/DiffusionQueryConduitAPIMethod.php', | ||||||
|     'DiffusionQueryPathsConduitAPIMethod' => 'applications/diffusion/conduit/DiffusionQueryPathsConduitAPIMethod.php', |     'DiffusionQueryPathsConduitAPIMethod' => 'applications/diffusion/conduit/DiffusionQueryPathsConduitAPIMethod.php', | ||||||
|  |     'DiffusionQuickSearchEngineExtension' => 'applications/diffusion/engineextension/DiffusionQuickSearchEngineExtension.php', | ||||||
|     'DiffusionRawDiffQuery' => 'applications/diffusion/query/rawdiff/DiffusionRawDiffQuery.php', |     'DiffusionRawDiffQuery' => 'applications/diffusion/query/rawdiff/DiffusionRawDiffQuery.php', | ||||||
|     'DiffusionRawDiffQueryConduitAPIMethod' => 'applications/diffusion/conduit/DiffusionRawDiffQueryConduitAPIMethod.php', |     'DiffusionRawDiffQueryConduitAPIMethod' => 'applications/diffusion/conduit/DiffusionRawDiffQueryConduitAPIMethod.php', | ||||||
|     'DiffusionReadmeView' => 'applications/diffusion/view/DiffusionReadmeView.php', |     'DiffusionReadmeView' => 'applications/diffusion/view/DiffusionReadmeView.php', | ||||||
| @@ -1327,6 +1335,7 @@ phutil_register_library_map(array( | |||||||
|     'HeraldApplicationActionGroup' => 'applications/herald/action/HeraldApplicationActionGroup.php', |     'HeraldApplicationActionGroup' => 'applications/herald/action/HeraldApplicationActionGroup.php', | ||||||
|     'HeraldApplyTranscript' => 'applications/herald/storage/transcript/HeraldApplyTranscript.php', |     'HeraldApplyTranscript' => 'applications/herald/storage/transcript/HeraldApplyTranscript.php', | ||||||
|     'HeraldBasicFieldGroup' => 'applications/herald/field/HeraldBasicFieldGroup.php', |     'HeraldBasicFieldGroup' => 'applications/herald/field/HeraldBasicFieldGroup.php', | ||||||
|  |     'HeraldBuildableState' => 'applications/herald/state/HeraldBuildableState.php', | ||||||
|     'HeraldCommitAdapter' => 'applications/diffusion/herald/HeraldCommitAdapter.php', |     'HeraldCommitAdapter' => 'applications/diffusion/herald/HeraldCommitAdapter.php', | ||||||
|     'HeraldCondition' => 'applications/herald/storage/HeraldCondition.php', |     'HeraldCondition' => 'applications/herald/storage/HeraldCondition.php', | ||||||
|     'HeraldConditionTranscript' => 'applications/herald/storage/transcript/HeraldConditionTranscript.php', |     'HeraldConditionTranscript' => 'applications/herald/storage/transcript/HeraldConditionTranscript.php', | ||||||
| @@ -1350,6 +1359,7 @@ phutil_register_library_map(array( | |||||||
|     'HeraldGroup' => 'applications/herald/group/HeraldGroup.php', |     'HeraldGroup' => 'applications/herald/group/HeraldGroup.php', | ||||||
|     'HeraldInvalidActionException' => 'applications/herald/engine/exception/HeraldInvalidActionException.php', |     'HeraldInvalidActionException' => 'applications/herald/engine/exception/HeraldInvalidActionException.php', | ||||||
|     'HeraldInvalidConditionException' => 'applications/herald/engine/exception/HeraldInvalidConditionException.php', |     'HeraldInvalidConditionException' => 'applications/herald/engine/exception/HeraldInvalidConditionException.php', | ||||||
|  |     'HeraldMailableState' => 'applications/herald/state/HeraldMailableState.php', | ||||||
|     'HeraldManageGlobalRulesCapability' => 'applications/herald/capability/HeraldManageGlobalRulesCapability.php', |     'HeraldManageGlobalRulesCapability' => 'applications/herald/capability/HeraldManageGlobalRulesCapability.php', | ||||||
|     'HeraldManiphestTaskAdapter' => 'applications/maniphest/herald/HeraldManiphestTaskAdapter.php', |     'HeraldManiphestTaskAdapter' => 'applications/maniphest/herald/HeraldManiphestTaskAdapter.php', | ||||||
|     'HeraldNewController' => 'applications/herald/controller/HeraldNewController.php', |     'HeraldNewController' => 'applications/herald/controller/HeraldNewController.php', | ||||||
| @@ -1387,6 +1397,8 @@ phutil_register_library_map(array( | |||||||
|     'HeraldSchemaSpec' => 'applications/herald/storage/HeraldSchemaSpec.php', |     'HeraldSchemaSpec' => 'applications/herald/storage/HeraldSchemaSpec.php', | ||||||
|     'HeraldSelectFieldValue' => 'applications/herald/value/HeraldSelectFieldValue.php', |     'HeraldSelectFieldValue' => 'applications/herald/value/HeraldSelectFieldValue.php', | ||||||
|     'HeraldSpaceField' => 'applications/spaces/herald/HeraldSpaceField.php', |     'HeraldSpaceField' => 'applications/spaces/herald/HeraldSpaceField.php', | ||||||
|  |     'HeraldState' => 'applications/herald/state/HeraldState.php', | ||||||
|  |     'HeraldStateReasons' => 'applications/herald/state/HeraldStateReasons.php', | ||||||
|     'HeraldSubscribersField' => 'applications/subscriptions/herald/HeraldSubscribersField.php', |     'HeraldSubscribersField' => 'applications/subscriptions/herald/HeraldSubscribersField.php', | ||||||
|     'HeraldSupportActionGroup' => 'applications/herald/action/HeraldSupportActionGroup.php', |     'HeraldSupportActionGroup' => 'applications/herald/action/HeraldSupportActionGroup.php', | ||||||
|     'HeraldSupportFieldGroup' => 'applications/herald/field/HeraldSupportFieldGroup.php', |     'HeraldSupportFieldGroup' => 'applications/herald/field/HeraldSupportFieldGroup.php', | ||||||
| @@ -2529,6 +2541,8 @@ phutil_register_library_map(array( | |||||||
|     'PhabricatorCustomFieldEditField' => 'infrastructure/customfield/editor/PhabricatorCustomFieldEditField.php', |     'PhabricatorCustomFieldEditField' => 'infrastructure/customfield/editor/PhabricatorCustomFieldEditField.php', | ||||||
|     'PhabricatorCustomFieldEditType' => 'infrastructure/customfield/editor/PhabricatorCustomFieldEditType.php', |     'PhabricatorCustomFieldEditType' => 'infrastructure/customfield/editor/PhabricatorCustomFieldEditType.php', | ||||||
|     'PhabricatorCustomFieldFulltextEngineExtension' => 'infrastructure/customfield/engineextension/PhabricatorCustomFieldFulltextEngineExtension.php', |     'PhabricatorCustomFieldFulltextEngineExtension' => 'infrastructure/customfield/engineextension/PhabricatorCustomFieldFulltextEngineExtension.php', | ||||||
|  |     'PhabricatorCustomFieldHeraldAction' => 'infrastructure/customfield/herald/PhabricatorCustomFieldHeraldAction.php', | ||||||
|  |     'PhabricatorCustomFieldHeraldActionGroup' => 'infrastructure/customfield/herald/PhabricatorCustomFieldHeraldActionGroup.php', | ||||||
|     'PhabricatorCustomFieldHeraldField' => 'infrastructure/customfield/herald/PhabricatorCustomFieldHeraldField.php', |     'PhabricatorCustomFieldHeraldField' => 'infrastructure/customfield/herald/PhabricatorCustomFieldHeraldField.php', | ||||||
|     'PhabricatorCustomFieldHeraldFieldGroup' => 'infrastructure/customfield/herald/PhabricatorCustomFieldHeraldFieldGroup.php', |     'PhabricatorCustomFieldHeraldFieldGroup' => 'infrastructure/customfield/herald/PhabricatorCustomFieldHeraldFieldGroup.php', | ||||||
|     'PhabricatorCustomFieldImplementationIncompleteException' => 'infrastructure/customfield/exception/PhabricatorCustomFieldImplementationIncompleteException.php', |     'PhabricatorCustomFieldImplementationIncompleteException' => 'infrastructure/customfield/exception/PhabricatorCustomFieldImplementationIncompleteException.php', | ||||||
| @@ -3194,6 +3208,7 @@ phutil_register_library_map(array( | |||||||
|     'PhabricatorMetronomicTriggerClock' => 'infrastructure/daemon/workers/clock/PhabricatorMetronomicTriggerClock.php', |     'PhabricatorMetronomicTriggerClock' => 'infrastructure/daemon/workers/clock/PhabricatorMetronomicTriggerClock.php', | ||||||
|     'PhabricatorModularTransaction' => 'applications/transactions/storage/PhabricatorModularTransaction.php', |     'PhabricatorModularTransaction' => 'applications/transactions/storage/PhabricatorModularTransaction.php', | ||||||
|     'PhabricatorModularTransactionType' => 'applications/transactions/storage/PhabricatorModularTransactionType.php', |     'PhabricatorModularTransactionType' => 'applications/transactions/storage/PhabricatorModularTransactionType.php', | ||||||
|  |     'PhabricatorMonogramQuickSearchEngineExtension' => 'applications/typeahead/engineextension/PhabricatorMonogramQuickSearchEngineExtension.php', | ||||||
|     'PhabricatorMonospacedFontSetting' => 'applications/settings/setting/PhabricatorMonospacedFontSetting.php', |     'PhabricatorMonospacedFontSetting' => 'applications/settings/setting/PhabricatorMonospacedFontSetting.php', | ||||||
|     'PhabricatorMonospacedTextareasSetting' => 'applications/settings/setting/PhabricatorMonospacedTextareasSetting.php', |     'PhabricatorMonospacedTextareasSetting' => 'applications/settings/setting/PhabricatorMonospacedTextareasSetting.php', | ||||||
|     'PhabricatorMotivatorProfileMenuItem' => 'applications/search/menuitem/PhabricatorMotivatorProfileMenuItem.php', |     'PhabricatorMotivatorProfileMenuItem' => 'applications/search/menuitem/PhabricatorMotivatorProfileMenuItem.php', | ||||||
| @@ -3517,6 +3532,7 @@ phutil_register_library_map(array( | |||||||
|     'PhabricatorPeopleProfileTasksController' => 'applications/people/controller/PhabricatorPeopleProfileTasksController.php', |     'PhabricatorPeopleProfileTasksController' => 'applications/people/controller/PhabricatorPeopleProfileTasksController.php', | ||||||
|     'PhabricatorPeopleProfileViewController' => 'applications/people/controller/PhabricatorPeopleProfileViewController.php', |     'PhabricatorPeopleProfileViewController' => 'applications/people/controller/PhabricatorPeopleProfileViewController.php', | ||||||
|     'PhabricatorPeopleQuery' => 'applications/people/query/PhabricatorPeopleQuery.php', |     'PhabricatorPeopleQuery' => 'applications/people/query/PhabricatorPeopleQuery.php', | ||||||
|  |     'PhabricatorPeopleQuickSearchEngineExtension' => 'applications/people/engineextension/PhabricatorPeopleQuickSearchEngineExtension.php', | ||||||
|     'PhabricatorPeopleRenameController' => 'applications/people/controller/PhabricatorPeopleRenameController.php', |     'PhabricatorPeopleRenameController' => 'applications/people/controller/PhabricatorPeopleRenameController.php', | ||||||
|     'PhabricatorPeopleRevisionsProfileMenuItem' => 'applications/people/menuitem/PhabricatorPeopleRevisionsProfileMenuItem.php', |     'PhabricatorPeopleRevisionsProfileMenuItem' => 'applications/people/menuitem/PhabricatorPeopleRevisionsProfileMenuItem.php', | ||||||
|     'PhabricatorPeopleSearchEngine' => 'applications/people/query/PhabricatorPeopleSearchEngine.php', |     'PhabricatorPeopleSearchEngine' => 'applications/people/query/PhabricatorPeopleSearchEngine.php', | ||||||
| @@ -3777,6 +3793,9 @@ phutil_register_library_map(array( | |||||||
|     'PhabricatorQueryOrderItem' => 'infrastructure/query/order/PhabricatorQueryOrderItem.php', |     'PhabricatorQueryOrderItem' => 'infrastructure/query/order/PhabricatorQueryOrderItem.php', | ||||||
|     'PhabricatorQueryOrderTestCase' => 'infrastructure/query/order/__tests__/PhabricatorQueryOrderTestCase.php', |     'PhabricatorQueryOrderTestCase' => 'infrastructure/query/order/__tests__/PhabricatorQueryOrderTestCase.php', | ||||||
|     'PhabricatorQueryOrderVector' => 'infrastructure/query/order/PhabricatorQueryOrderVector.php', |     'PhabricatorQueryOrderVector' => 'infrastructure/query/order/PhabricatorQueryOrderVector.php', | ||||||
|  |     'PhabricatorQuickSearchApplicationEngineExtension' => 'applications/meta/engineextension/PhabricatorQuickSearchApplicationEngineExtension.php', | ||||||
|  |     'PhabricatorQuickSearchEngine' => 'applications/search/engine/PhabricatorQuickSearchEngine.php', | ||||||
|  |     'PhabricatorQuickSearchEngineExtension' => 'applications/search/engineextension/PhabricatorQuickSearchEngineExtension.php', | ||||||
|     'PhabricatorRateLimitRequestExceptionHandler' => 'aphront/handler/PhabricatorRateLimitRequestExceptionHandler.php', |     'PhabricatorRateLimitRequestExceptionHandler' => 'aphront/handler/PhabricatorRateLimitRequestExceptionHandler.php', | ||||||
|     'PhabricatorRecaptchaConfigOptions' => 'applications/config/option/PhabricatorRecaptchaConfigOptions.php', |     'PhabricatorRecaptchaConfigOptions' => 'applications/config/option/PhabricatorRecaptchaConfigOptions.php', | ||||||
|     'PhabricatorRedirectController' => 'applications/base/controller/PhabricatorRedirectController.php', |     'PhabricatorRedirectController' => 'applications/base/controller/PhabricatorRedirectController.php', | ||||||
| @@ -4778,6 +4797,7 @@ phutil_register_library_map(array( | |||||||
|     'PonderQuestionEditController' => 'applications/ponder/controller/PonderQuestionEditController.php', |     'PonderQuestionEditController' => 'applications/ponder/controller/PonderQuestionEditController.php', | ||||||
|     'PonderQuestionEditEngine' => 'applications/ponder/editor/PonderQuestionEditEngine.php', |     'PonderQuestionEditEngine' => 'applications/ponder/editor/PonderQuestionEditEngine.php', | ||||||
|     'PonderQuestionEditor' => 'applications/ponder/editor/PonderQuestionEditor.php', |     'PonderQuestionEditor' => 'applications/ponder/editor/PonderQuestionEditor.php', | ||||||
|  |     'PonderQuestionFerretEngine' => 'applications/ponder/search/PonderQuestionFerretEngine.php', | ||||||
|     'PonderQuestionFulltextEngine' => 'applications/ponder/search/PonderQuestionFulltextEngine.php', |     'PonderQuestionFulltextEngine' => 'applications/ponder/search/PonderQuestionFulltextEngine.php', | ||||||
|     'PonderQuestionHistoryController' => 'applications/ponder/controller/PonderQuestionHistoryController.php', |     'PonderQuestionHistoryController' => 'applications/ponder/controller/PonderQuestionHistoryController.php', | ||||||
|     'PonderQuestionListController' => 'applications/ponder/controller/PonderQuestionListController.php', |     'PonderQuestionListController' => 'applications/ponder/controller/PonderQuestionListController.php', | ||||||
| @@ -4809,6 +4829,7 @@ phutil_register_library_map(array( | |||||||
|     'ProjectDefaultViewCapability' => 'applications/project/capability/ProjectDefaultViewCapability.php', |     'ProjectDefaultViewCapability' => 'applications/project/capability/ProjectDefaultViewCapability.php', | ||||||
|     'ProjectEditConduitAPIMethod' => 'applications/project/conduit/ProjectEditConduitAPIMethod.php', |     'ProjectEditConduitAPIMethod' => 'applications/project/conduit/ProjectEditConduitAPIMethod.php', | ||||||
|     'ProjectQueryConduitAPIMethod' => 'applications/project/conduit/ProjectQueryConduitAPIMethod.php', |     'ProjectQueryConduitAPIMethod' => 'applications/project/conduit/ProjectQueryConduitAPIMethod.php', | ||||||
|  |     'ProjectQuickSearchEngineExtension' => 'applications/project/engineextension/ProjectQuickSearchEngineExtension.php', | ||||||
|     'ProjectRemarkupRule' => 'applications/project/remarkup/ProjectRemarkupRule.php', |     'ProjectRemarkupRule' => 'applications/project/remarkup/ProjectRemarkupRule.php', | ||||||
|     'ProjectRemarkupRuleTestCase' => 'applications/project/remarkup/__tests__/ProjectRemarkupRuleTestCase.php', |     'ProjectRemarkupRuleTestCase' => 'applications/project/remarkup/__tests__/ProjectRemarkupRuleTestCase.php', | ||||||
|     'ProjectReplyHandler' => 'applications/project/mail/ProjectReplyHandler.php', |     'ProjectReplyHandler' => 'applications/project/mail/ProjectReplyHandler.php', | ||||||
| @@ -5433,6 +5454,7 @@ phutil_register_library_map(array( | |||||||
|       'HarbormasterBuildkiteBuildableInterface', |       'HarbormasterBuildkiteBuildableInterface', | ||||||
|       'PhabricatorApplicationTransactionInterface', |       'PhabricatorApplicationTransactionInterface', | ||||||
|       'PhabricatorDestructibleInterface', |       'PhabricatorDestructibleInterface', | ||||||
|  |       'PhabricatorConduitResultInterface', | ||||||
|     ), |     ), | ||||||
|     'DifferentialDiffAffectedFilesHeraldField' => 'DifferentialDiffHeraldField', |     'DifferentialDiffAffectedFilesHeraldField' => 'DifferentialDiffHeraldField', | ||||||
|     'DifferentialDiffAuthorHeraldField' => 'DifferentialDiffHeraldField', |     'DifferentialDiffAuthorHeraldField' => 'DifferentialDiffHeraldField', | ||||||
| @@ -5451,11 +5473,14 @@ phutil_register_library_map(array( | |||||||
|     'DifferentialDiffQuery' => 'PhabricatorCursorPagedPolicyAwareQuery', |     'DifferentialDiffQuery' => 'PhabricatorCursorPagedPolicyAwareQuery', | ||||||
|     'DifferentialDiffRepositoryHeraldField' => 'DifferentialDiffHeraldField', |     'DifferentialDiffRepositoryHeraldField' => 'DifferentialDiffHeraldField', | ||||||
|     'DifferentialDiffRepositoryProjectsHeraldField' => 'DifferentialDiffHeraldField', |     'DifferentialDiffRepositoryProjectsHeraldField' => 'DifferentialDiffHeraldField', | ||||||
|  |     'DifferentialDiffSearchConduitAPIMethod' => 'PhabricatorSearchEngineAPIMethod', | ||||||
|  |     'DifferentialDiffSearchEngine' => 'PhabricatorApplicationSearchEngine', | ||||||
|     'DifferentialDiffTestCase' => 'PhutilTestCase', |     'DifferentialDiffTestCase' => 'PhutilTestCase', | ||||||
|     'DifferentialDiffTransaction' => 'PhabricatorApplicationTransaction', |     'DifferentialDiffTransaction' => 'PhabricatorApplicationTransaction', | ||||||
|     'DifferentialDiffTransactionQuery' => 'PhabricatorApplicationTransactionQuery', |     'DifferentialDiffTransactionQuery' => 'PhabricatorApplicationTransactionQuery', | ||||||
|     'DifferentialDiffViewController' => 'DifferentialController', |     'DifferentialDiffViewController' => 'DifferentialController', | ||||||
|     'DifferentialDoorkeeperRevisionFeedStoryPublisher' => 'DoorkeeperFeedStoryPublisher', |     'DifferentialDoorkeeperRevisionFeedStoryPublisher' => 'DoorkeeperFeedStoryPublisher', | ||||||
|  |     'DifferentialDraftField' => 'DifferentialCoreCustomField', | ||||||
|     'DifferentialExactUserFunctionDatasource' => 'PhabricatorTypeaheadCompositeDatasource', |     'DifferentialExactUserFunctionDatasource' => 'PhabricatorTypeaheadCompositeDatasource', | ||||||
|     'DifferentialFieldParseException' => 'Exception', |     'DifferentialFieldParseException' => 'Exception', | ||||||
|     'DifferentialFieldValidationException' => 'Exception', |     'DifferentialFieldValidationException' => 'Exception', | ||||||
| @@ -5469,6 +5494,7 @@ phutil_register_library_map(array( | |||||||
|     'DifferentialGetWorkingCopy' => 'Phobject', |     'DifferentialGetWorkingCopy' => 'Phobject', | ||||||
|     'DifferentialGitSVNIDCommitMessageField' => 'DifferentialCommitMessageField', |     'DifferentialGitSVNIDCommitMessageField' => 'DifferentialCommitMessageField', | ||||||
|     'DifferentialHarbormasterField' => 'DifferentialCustomField', |     'DifferentialHarbormasterField' => 'DifferentialCustomField', | ||||||
|  |     'DifferentialHeraldStateReasons' => 'HeraldStateReasons', | ||||||
|     'DifferentialHiddenComment' => 'DifferentialDAO', |     'DifferentialHiddenComment' => 'DifferentialDAO', | ||||||
|     'DifferentialHostField' => 'DifferentialCustomField', |     'DifferentialHostField' => 'DifferentialCustomField', | ||||||
|     'DifferentialHovercardEngineExtension' => 'PhabricatorHovercardEngineExtension', |     'DifferentialHovercardEngineExtension' => 'PhabricatorHovercardEngineExtension', | ||||||
| @@ -5583,6 +5609,7 @@ phutil_register_library_map(array( | |||||||
|     'DifferentialRevisionHasTaskRelationship' => 'DifferentialRevisionRelationship', |     'DifferentialRevisionHasTaskRelationship' => 'DifferentialRevisionRelationship', | ||||||
|     'DifferentialRevisionHeraldField' => 'HeraldField', |     'DifferentialRevisionHeraldField' => 'HeraldField', | ||||||
|     'DifferentialRevisionHeraldFieldGroup' => 'HeraldFieldGroup', |     'DifferentialRevisionHeraldFieldGroup' => 'HeraldFieldGroup', | ||||||
|  |     'DifferentialRevisionHoldDraftTransaction' => 'DifferentialRevisionTransactionType', | ||||||
|     'DifferentialRevisionIDCommitMessageField' => 'DifferentialCommitMessageField', |     'DifferentialRevisionIDCommitMessageField' => 'DifferentialCommitMessageField', | ||||||
|     'DifferentialRevisionInlineTransaction' => 'PhabricatorModularTransactionType', |     'DifferentialRevisionInlineTransaction' => 'PhabricatorModularTransactionType', | ||||||
|     'DifferentialRevisionInlinesController' => 'DifferentialController', |     'DifferentialRevisionInlinesController' => 'DifferentialController', | ||||||
| @@ -5626,6 +5653,7 @@ phutil_register_library_map(array( | |||||||
|     'DifferentialRevisionUpdateHistoryView' => 'AphrontView', |     'DifferentialRevisionUpdateHistoryView' => 'AphrontView', | ||||||
|     'DifferentialRevisionViewController' => 'DifferentialController', |     'DifferentialRevisionViewController' => 'DifferentialController', | ||||||
|     'DifferentialRevisionVoidTransaction' => 'DifferentialRevisionTransactionType', |     'DifferentialRevisionVoidTransaction' => 'DifferentialRevisionTransactionType', | ||||||
|  |     'DifferentialRevisionWrongStateTransaction' => 'DifferentialRevisionTransactionType', | ||||||
|     'DifferentialSchemaSpec' => 'PhabricatorConfigSchemaSpec', |     'DifferentialSchemaSpec' => 'PhabricatorConfigSchemaSpec', | ||||||
|     'DifferentialSetDiffPropertyConduitAPIMethod' => 'DifferentialConduitAPIMethod', |     'DifferentialSetDiffPropertyConduitAPIMethod' => 'DifferentialConduitAPIMethod', | ||||||
|     'DifferentialStoredCustomField' => 'DifferentialCustomField', |     'DifferentialStoredCustomField' => 'DifferentialCustomField', | ||||||
| @@ -5803,6 +5831,7 @@ phutil_register_library_map(array( | |||||||
|     'DiffusionMercurialBlameQuery' => 'DiffusionBlameQuery', |     'DiffusionMercurialBlameQuery' => 'DiffusionBlameQuery', | ||||||
|     'DiffusionMercurialCommandEngine' => 'DiffusionCommandEngine', |     'DiffusionMercurialCommandEngine' => 'DiffusionCommandEngine', | ||||||
|     'DiffusionMercurialFileContentQuery' => 'DiffusionFileContentQuery', |     'DiffusionMercurialFileContentQuery' => 'DiffusionFileContentQuery', | ||||||
|  |     'DiffusionMercurialFlagInjectionException' => 'Exception', | ||||||
|     'DiffusionMercurialRawDiffQuery' => 'DiffusionRawDiffQuery', |     'DiffusionMercurialRawDiffQuery' => 'DiffusionRawDiffQuery', | ||||||
|     'DiffusionMercurialRequest' => 'DiffusionRequest', |     'DiffusionMercurialRequest' => 'DiffusionRequest', | ||||||
|     'DiffusionMercurialResponse' => 'AphrontResponse', |     'DiffusionMercurialResponse' => 'AphrontResponse', | ||||||
| @@ -5865,6 +5894,7 @@ phutil_register_library_map(array( | |||||||
|     'DiffusionQueryCommitsConduitAPIMethod' => 'DiffusionConduitAPIMethod', |     'DiffusionQueryCommitsConduitAPIMethod' => 'DiffusionConduitAPIMethod', | ||||||
|     'DiffusionQueryConduitAPIMethod' => 'DiffusionConduitAPIMethod', |     'DiffusionQueryConduitAPIMethod' => 'DiffusionConduitAPIMethod', | ||||||
|     'DiffusionQueryPathsConduitAPIMethod' => 'DiffusionQueryConduitAPIMethod', |     'DiffusionQueryPathsConduitAPIMethod' => 'DiffusionQueryConduitAPIMethod', | ||||||
|  |     'DiffusionQuickSearchEngineExtension' => 'PhabricatorQuickSearchEngineExtension', | ||||||
|     'DiffusionRawDiffQuery' => 'DiffusionFileFutureQuery', |     'DiffusionRawDiffQuery' => 'DiffusionFileFutureQuery', | ||||||
|     'DiffusionRawDiffQueryConduitAPIMethod' => 'DiffusionQueryConduitAPIMethod', |     'DiffusionRawDiffQueryConduitAPIMethod' => 'DiffusionQueryConduitAPIMethod', | ||||||
|     'DiffusionReadmeView' => 'DiffusionView', |     'DiffusionReadmeView' => 'DiffusionView', | ||||||
| @@ -6460,6 +6490,7 @@ phutil_register_library_map(array( | |||||||
|     'HeraldApplicationActionGroup' => 'HeraldActionGroup', |     'HeraldApplicationActionGroup' => 'HeraldActionGroup', | ||||||
|     'HeraldApplyTranscript' => 'Phobject', |     'HeraldApplyTranscript' => 'Phobject', | ||||||
|     'HeraldBasicFieldGroup' => 'HeraldFieldGroup', |     'HeraldBasicFieldGroup' => 'HeraldFieldGroup', | ||||||
|  |     'HeraldBuildableState' => 'HeraldState', | ||||||
|     'HeraldCommitAdapter' => array( |     'HeraldCommitAdapter' => array( | ||||||
|       'HeraldAdapter', |       'HeraldAdapter', | ||||||
|       'HarbormasterBuildableAdapterInterface', |       'HarbormasterBuildableAdapterInterface', | ||||||
| @@ -6489,6 +6520,7 @@ phutil_register_library_map(array( | |||||||
|     'HeraldGroup' => 'Phobject', |     'HeraldGroup' => 'Phobject', | ||||||
|     'HeraldInvalidActionException' => 'Exception', |     'HeraldInvalidActionException' => 'Exception', | ||||||
|     'HeraldInvalidConditionException' => 'Exception', |     'HeraldInvalidConditionException' => 'Exception', | ||||||
|  |     'HeraldMailableState' => 'HeraldState', | ||||||
|     'HeraldManageGlobalRulesCapability' => 'PhabricatorPolicyCapability', |     'HeraldManageGlobalRulesCapability' => 'PhabricatorPolicyCapability', | ||||||
|     'HeraldManiphestTaskAdapter' => 'HeraldAdapter', |     'HeraldManiphestTaskAdapter' => 'HeraldAdapter', | ||||||
|     'HeraldNewController' => 'HeraldController', |     'HeraldNewController' => 'HeraldController', | ||||||
| @@ -6533,6 +6565,8 @@ phutil_register_library_map(array( | |||||||
|     'HeraldSchemaSpec' => 'PhabricatorConfigSchemaSpec', |     'HeraldSchemaSpec' => 'PhabricatorConfigSchemaSpec', | ||||||
|     'HeraldSelectFieldValue' => 'HeraldFieldValue', |     'HeraldSelectFieldValue' => 'HeraldFieldValue', | ||||||
|     'HeraldSpaceField' => 'HeraldField', |     'HeraldSpaceField' => 'HeraldField', | ||||||
|  |     'HeraldState' => 'Phobject', | ||||||
|  |     'HeraldStateReasons' => 'Phobject', | ||||||
|     'HeraldSubscribersField' => 'HeraldField', |     'HeraldSubscribersField' => 'HeraldField', | ||||||
|     'HeraldSupportActionGroup' => 'HeraldActionGroup', |     'HeraldSupportActionGroup' => 'HeraldActionGroup', | ||||||
|     'HeraldSupportFieldGroup' => 'HeraldFieldGroup', |     'HeraldSupportFieldGroup' => 'HeraldFieldGroup', | ||||||
| @@ -7855,6 +7889,8 @@ phutil_register_library_map(array( | |||||||
|     'PhabricatorCustomFieldEditField' => 'PhabricatorEditField', |     'PhabricatorCustomFieldEditField' => 'PhabricatorEditField', | ||||||
|     'PhabricatorCustomFieldEditType' => 'PhabricatorEditType', |     'PhabricatorCustomFieldEditType' => 'PhabricatorEditType', | ||||||
|     'PhabricatorCustomFieldFulltextEngineExtension' => 'PhabricatorFulltextEngineExtension', |     'PhabricatorCustomFieldFulltextEngineExtension' => 'PhabricatorFulltextEngineExtension', | ||||||
|  |     'PhabricatorCustomFieldHeraldAction' => 'HeraldAction', | ||||||
|  |     'PhabricatorCustomFieldHeraldActionGroup' => 'HeraldActionGroup', | ||||||
|     'PhabricatorCustomFieldHeraldField' => 'HeraldField', |     'PhabricatorCustomFieldHeraldField' => 'HeraldField', | ||||||
|     'PhabricatorCustomFieldHeraldFieldGroup' => 'HeraldFieldGroup', |     'PhabricatorCustomFieldHeraldFieldGroup' => 'HeraldFieldGroup', | ||||||
|     'PhabricatorCustomFieldImplementationIncompleteException' => 'Exception', |     'PhabricatorCustomFieldImplementationIncompleteException' => 'Exception', | ||||||
| @@ -8595,6 +8631,7 @@ phutil_register_library_map(array( | |||||||
|     'PhabricatorMetronomicTriggerClock' => 'PhabricatorTriggerClock', |     'PhabricatorMetronomicTriggerClock' => 'PhabricatorTriggerClock', | ||||||
|     'PhabricatorModularTransaction' => 'PhabricatorApplicationTransaction', |     'PhabricatorModularTransaction' => 'PhabricatorApplicationTransaction', | ||||||
|     'PhabricatorModularTransactionType' => 'Phobject', |     'PhabricatorModularTransactionType' => 'Phobject', | ||||||
|  |     'PhabricatorMonogramQuickSearchEngineExtension' => 'PhabricatorQuickSearchEngineExtension', | ||||||
|     'PhabricatorMonospacedFontSetting' => 'PhabricatorStringSetting', |     'PhabricatorMonospacedFontSetting' => 'PhabricatorStringSetting', | ||||||
|     'PhabricatorMonospacedTextareasSetting' => 'PhabricatorSelectSetting', |     'PhabricatorMonospacedTextareasSetting' => 'PhabricatorSelectSetting', | ||||||
|     'PhabricatorMotivatorProfileMenuItem' => 'PhabricatorProfileMenuItem', |     'PhabricatorMotivatorProfileMenuItem' => 'PhabricatorProfileMenuItem', | ||||||
| @@ -8987,6 +9024,7 @@ phutil_register_library_map(array( | |||||||
|     'PhabricatorPeopleProfileTasksController' => 'PhabricatorPeopleProfileController', |     'PhabricatorPeopleProfileTasksController' => 'PhabricatorPeopleProfileController', | ||||||
|     'PhabricatorPeopleProfileViewController' => 'PhabricatorPeopleProfileController', |     'PhabricatorPeopleProfileViewController' => 'PhabricatorPeopleProfileController', | ||||||
|     'PhabricatorPeopleQuery' => 'PhabricatorCursorPagedPolicyAwareQuery', |     'PhabricatorPeopleQuery' => 'PhabricatorCursorPagedPolicyAwareQuery', | ||||||
|  |     'PhabricatorPeopleQuickSearchEngineExtension' => 'PhabricatorQuickSearchEngineExtension', | ||||||
|     'PhabricatorPeopleRenameController' => 'PhabricatorPeopleController', |     'PhabricatorPeopleRenameController' => 'PhabricatorPeopleController', | ||||||
|     'PhabricatorPeopleRevisionsProfileMenuItem' => 'PhabricatorProfileMenuItem', |     'PhabricatorPeopleRevisionsProfileMenuItem' => 'PhabricatorProfileMenuItem', | ||||||
|     'PhabricatorPeopleSearchEngine' => 'PhabricatorApplicationSearchEngine', |     'PhabricatorPeopleSearchEngine' => 'PhabricatorApplicationSearchEngine', | ||||||
| @@ -9302,6 +9340,9 @@ phutil_register_library_map(array( | |||||||
|       'Phobject', |       'Phobject', | ||||||
|       'Iterator', |       'Iterator', | ||||||
|     ), |     ), | ||||||
|  |     'PhabricatorQuickSearchApplicationEngineExtension' => 'PhabricatorQuickSearchEngineExtension', | ||||||
|  |     'PhabricatorQuickSearchEngine' => 'Phobject', | ||||||
|  |     'PhabricatorQuickSearchEngineExtension' => 'Phobject', | ||||||
|     'PhabricatorRateLimitRequestExceptionHandler' => 'PhabricatorRequestExceptionHandler', |     'PhabricatorRateLimitRequestExceptionHandler' => 'PhabricatorRequestExceptionHandler', | ||||||
|     'PhabricatorRecaptchaConfigOptions' => 'PhabricatorApplicationConfigOptions', |     'PhabricatorRecaptchaConfigOptions' => 'PhabricatorApplicationConfigOptions', | ||||||
|     'PhabricatorRedirectController' => 'PhabricatorController', |     'PhabricatorRedirectController' => 'PhabricatorController', | ||||||
| @@ -10547,6 +10588,7 @@ phutil_register_library_map(array( | |||||||
|       'PhabricatorDestructibleInterface', |       'PhabricatorDestructibleInterface', | ||||||
|       'PhabricatorSpacesInterface', |       'PhabricatorSpacesInterface', | ||||||
|       'PhabricatorFulltextInterface', |       'PhabricatorFulltextInterface', | ||||||
|  |       'PhabricatorFerretInterface', | ||||||
|     ), |     ), | ||||||
|     'PonderQuestionAnswerTransaction' => 'PonderQuestionTransactionType', |     'PonderQuestionAnswerTransaction' => 'PonderQuestionTransactionType', | ||||||
|     'PonderQuestionAnswerWikiTransaction' => 'PonderQuestionTransactionType', |     'PonderQuestionAnswerWikiTransaction' => 'PonderQuestionTransactionType', | ||||||
| @@ -10556,6 +10598,7 @@ phutil_register_library_map(array( | |||||||
|     'PonderQuestionEditController' => 'PonderController', |     'PonderQuestionEditController' => 'PonderController', | ||||||
|     'PonderQuestionEditEngine' => 'PhabricatorEditEngine', |     'PonderQuestionEditEngine' => 'PhabricatorEditEngine', | ||||||
|     'PonderQuestionEditor' => 'PonderEditor', |     'PonderQuestionEditor' => 'PonderEditor', | ||||||
|  |     'PonderQuestionFerretEngine' => 'PhabricatorFerretEngine', | ||||||
|     'PonderQuestionFulltextEngine' => 'PhabricatorFulltextEngine', |     'PonderQuestionFulltextEngine' => 'PhabricatorFulltextEngine', | ||||||
|     'PonderQuestionHistoryController' => 'PonderController', |     'PonderQuestionHistoryController' => 'PonderController', | ||||||
|     'PonderQuestionListController' => 'PonderController', |     'PonderQuestionListController' => 'PonderController', | ||||||
| @@ -10587,6 +10630,7 @@ phutil_register_library_map(array( | |||||||
|     'ProjectDefaultViewCapability' => 'PhabricatorPolicyCapability', |     'ProjectDefaultViewCapability' => 'PhabricatorPolicyCapability', | ||||||
|     'ProjectEditConduitAPIMethod' => 'PhabricatorEditEngineAPIMethod', |     'ProjectEditConduitAPIMethod' => 'PhabricatorEditEngineAPIMethod', | ||||||
|     'ProjectQueryConduitAPIMethod' => 'ProjectConduitAPIMethod', |     'ProjectQueryConduitAPIMethod' => 'ProjectConduitAPIMethod', | ||||||
|  |     'ProjectQuickSearchEngineExtension' => 'PhabricatorQuickSearchEngineExtension', | ||||||
|     'ProjectRemarkupRule' => 'PhabricatorObjectRemarkupRule', |     'ProjectRemarkupRule' => 'PhabricatorObjectRemarkupRule', | ||||||
|     'ProjectRemarkupRuleTestCase' => 'PhabricatorTestCase', |     'ProjectRemarkupRuleTestCase' => 'PhabricatorTestCase', | ||||||
|     'ProjectReplyHandler' => 'PhabricatorApplicationTransactionReplyHandler', |     'ProjectReplyHandler' => 'PhabricatorApplicationTransactionReplyHandler', | ||||||
|   | |||||||
| @@ -9,9 +9,27 @@ final class PhabricatorAuthNeedsMultiFactorController | |||||||
|     return false; |     return false; | ||||||
|   } |   } | ||||||
|  |  | ||||||
|  |   public function shouldRequireEnabledUser() { | ||||||
|  |     // Users who haven't been approved yet are allowed to enroll in MFA. We'll | ||||||
|  |     // kick disabled users out later. | ||||||
|  |     return false; | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   public function shouldRequireEmailVerification() { | ||||||
|  |     // Users who haven't verified their email addresses yet can still enroll | ||||||
|  |     // in MFA. | ||||||
|  |     return false; | ||||||
|  |   } | ||||||
|  |  | ||||||
|   public function handleRequest(AphrontRequest $request) { |   public function handleRequest(AphrontRequest $request) { | ||||||
|     $viewer = $this->getViewer(); |     $viewer = $this->getViewer(); | ||||||
|  |  | ||||||
|  |     if ($viewer->getIsDisabled()) { | ||||||
|  |       // We allowed unapproved and disabled users to hit this controller, but | ||||||
|  |       // want to kick out disabled users now. | ||||||
|  |       return new Aphront400Response(); | ||||||
|  |     } | ||||||
|  |  | ||||||
|     $panel = id(new PhabricatorMultiFactorSettingsPanel()) |     $panel = id(new PhabricatorMultiFactorSettingsPanel()) | ||||||
|       ->setUser($viewer) |       ->setUser($viewer) | ||||||
|       ->setViewer($viewer) |       ->setViewer($viewer) | ||||||
|   | |||||||
| @@ -9,6 +9,10 @@ final class PhabricatorAuthMainMenuBarExtension | |||||||
|     return true; |     return true; | ||||||
|   } |   } | ||||||
|  |  | ||||||
|  |   public function shouldRequireFullSession() { | ||||||
|  |     return false; | ||||||
|  |   } | ||||||
|  |  | ||||||
|   public function getExtensionOrder() { |   public function getExtensionOrder() { | ||||||
|     return 900; |     return 900; | ||||||
|   } |   } | ||||||
|   | |||||||
| @@ -137,10 +137,6 @@ abstract class PhabricatorController extends AphrontController { | |||||||
|     } |     } | ||||||
|  |  | ||||||
|     if ($this->shouldRequireEnabledUser()) { |     if ($this->shouldRequireEnabledUser()) { | ||||||
|       if ($user->isLoggedIn() && !$user->getIsApproved()) { |  | ||||||
|         $controller = new PhabricatorAuthNeedsApprovalController(); |  | ||||||
|         return $this->delegateToController($controller); |  | ||||||
|       } |  | ||||||
|       if ($user->getIsDisabled()) { |       if ($user->getIsDisabled()) { | ||||||
|         $controller = new PhabricatorDisabledUserController(); |         $controller = new PhabricatorDisabledUserController(); | ||||||
|         return $this->delegateToController($controller); |         return $this->delegateToController($controller); | ||||||
| @@ -160,6 +156,15 @@ abstract class PhabricatorController extends AphrontController { | |||||||
|       } |       } | ||||||
|     } |     } | ||||||
|  |  | ||||||
|  |     // Require users sign Legalpad documents before we check if they have | ||||||
|  |     // MFA. If we don't do this, they can get stuck in a state where they | ||||||
|  |     // can't add MFA until they sign, and can't sign until they add MFA. | ||||||
|  |     // See T13024 and PHI223. | ||||||
|  |     $result = $this->requireLegalpadSignatures(); | ||||||
|  |     if ($result !== null) { | ||||||
|  |       return $result; | ||||||
|  |     } | ||||||
|  |  | ||||||
|     // Check if the user needs to configure MFA. |     // Check if the user needs to configure MFA. | ||||||
|     $need_mfa = $this->shouldRequireMultiFactorEnrollment(); |     $need_mfa = $this->shouldRequireMultiFactorEnrollment(); | ||||||
|     $have_mfa = $user->getIsEnrolledInMultiFactor(); |     $have_mfa = $user->getIsEnrolledInMultiFactor(); | ||||||
| @@ -224,46 +229,15 @@ abstract class PhabricatorController extends AphrontController { | |||||||
|           ->withPHIDs(array($application->getPHID())) |           ->withPHIDs(array($application->getPHID())) | ||||||
|           ->executeOne(); |           ->executeOne(); | ||||||
|       } |       } | ||||||
|     } |  | ||||||
|  |  | ||||||
|  |       // If users need approval, require they wait here. We do this near the | ||||||
|     if (!$this->shouldAllowLegallyNonCompliantUsers()) { |       // end so they can take other actions (like verifying email, signing | ||||||
|       $legalpad_class = 'PhabricatorLegalpadApplication'; |       // documents, and enrolling in MFA) while waiting for an admin to take a | ||||||
|       $legalpad = id(new PhabricatorApplicationQuery()) |       // look at things. See T13024 for more discussion. | ||||||
|         ->setViewer($user) |       if ($this->shouldRequireEnabledUser()) { | ||||||
|         ->withClasses(array($legalpad_class)) |         if ($user->isLoggedIn() && !$user->getIsApproved()) { | ||||||
|         ->withInstalled(true) |           $controller = new PhabricatorAuthNeedsApprovalController(); | ||||||
|         ->execute(); |  | ||||||
|       $legalpad = head($legalpad); |  | ||||||
|  |  | ||||||
|       $doc_query = id(new LegalpadDocumentQuery()) |  | ||||||
|         ->setViewer($user) |  | ||||||
|         ->withSignatureRequired(1) |  | ||||||
|         ->needViewerSignatures(true); |  | ||||||
|  |  | ||||||
|       if ($user->hasSession() && |  | ||||||
|           !$user->getSession()->getIsPartial() && |  | ||||||
|           !$user->getSession()->getSignedLegalpadDocuments() && |  | ||||||
|           $user->isLoggedIn() && |  | ||||||
|           $legalpad) { |  | ||||||
|  |  | ||||||
|         $sign_docs = $doc_query->execute(); |  | ||||||
|         $must_sign_docs = array(); |  | ||||||
|         foreach ($sign_docs as $sign_doc) { |  | ||||||
|           if (!$sign_doc->getUserSignature($user->getPHID())) { |  | ||||||
|             $must_sign_docs[] = $sign_doc; |  | ||||||
|           } |  | ||||||
|         } |  | ||||||
|         if ($must_sign_docs) { |  | ||||||
|           $controller = new LegalpadDocumentSignController(); |  | ||||||
|           $this->getRequest()->setURIMap(array( |  | ||||||
|             'id' => head($must_sign_docs)->getID(), |  | ||||||
|           )); |  | ||||||
|           $this->setCurrentApplication($legalpad); |  | ||||||
|           return $this->delegateToController($controller); |           return $this->delegateToController($controller); | ||||||
|         } else { |  | ||||||
|           $engine = id(new PhabricatorAuthSessionEngine()) |  | ||||||
|             ->signLegalpadDocuments($user, $sign_docs); |  | ||||||
|         } |         } | ||||||
|       } |       } | ||||||
|     } |     } | ||||||
| @@ -558,6 +532,81 @@ abstract class PhabricatorController extends AphrontController { | |||||||
|     return $this->buildApplicationCrumbs(); |     return $this->buildApplicationCrumbs(); | ||||||
|   } |   } | ||||||
|  |  | ||||||
|  |   private function requireLegalpadSignatures() { | ||||||
|  |     if (!$this->shouldRequireLogin()) { | ||||||
|  |       return null; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     if ($this->shouldAllowLegallyNonCompliantUsers()) { | ||||||
|  |       return null; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     $viewer = $this->getViewer(); | ||||||
|  |  | ||||||
|  |     if (!$viewer->hasSession()) { | ||||||
|  |       return null; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     $session = $viewer->getSession(); | ||||||
|  |     if ($session->getIsPartial()) { | ||||||
|  |       // If the user hasn't made it through MFA yet, require they survive | ||||||
|  |       // MFA first. | ||||||
|  |       return null; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     if ($session->getSignedLegalpadDocuments()) { | ||||||
|  |       return null; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     if (!$viewer->isLoggedIn()) { | ||||||
|  |       return null; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     $must_sign_docs = array(); | ||||||
|  |     $sign_docs = array(); | ||||||
|  |  | ||||||
|  |     $legalpad_class = 'PhabricatorLegalpadApplication'; | ||||||
|  |     $legalpad_installed = PhabricatorApplication::isClassInstalledForViewer( | ||||||
|  |       $legalpad_class, | ||||||
|  |       $viewer); | ||||||
|  |     if ($legalpad_installed) { | ||||||
|  |       $sign_docs = id(new LegalpadDocumentQuery()) | ||||||
|  |         ->setViewer($viewer) | ||||||
|  |         ->withSignatureRequired(1) | ||||||
|  |         ->needViewerSignatures(true) | ||||||
|  |         ->setOrder('oldest') | ||||||
|  |         ->execute(); | ||||||
|  |  | ||||||
|  |       foreach ($sign_docs as $sign_doc) { | ||||||
|  |         if (!$sign_doc->getUserSignature($viewer->getPHID())) { | ||||||
|  |           $must_sign_docs[] = $sign_doc; | ||||||
|  |         } | ||||||
|  |       } | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     if (!$must_sign_docs) { | ||||||
|  |       // If nothing needs to be signed (either because there are no documents | ||||||
|  |       // which require a signature, or because the user has already signed | ||||||
|  |       // all of them) mark the session as good and continue. | ||||||
|  |       $engine = id(new PhabricatorAuthSessionEngine()) | ||||||
|  |         ->signLegalpadDocuments($viewer, $sign_docs); | ||||||
|  |  | ||||||
|  |       return null; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     $request = $this->getRequest(); | ||||||
|  |     $request->setURIMap( | ||||||
|  |       array( | ||||||
|  |         'id' => head($must_sign_docs)->getID(), | ||||||
|  |       )); | ||||||
|  |  | ||||||
|  |     $application = PhabricatorApplication::getByClass($legalpad_class); | ||||||
|  |     $this->setCurrentApplication($application); | ||||||
|  |  | ||||||
|  |     $controller = new LegalpadDocumentSignController(); | ||||||
|  |     return $this->delegateToController($controller); | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |  | ||||||
| /* -(  Deprecated  )--------------------------------------------------------- */ | /* -(  Deprecated  )--------------------------------------------------------- */ | ||||||
|  |  | ||||||
|   | |||||||
| @@ -159,10 +159,10 @@ final class PhabricatorAccessControlTestCase extends PhabricatorTestCase { | |||||||
|         $u_unverified, |         $u_unverified, | ||||||
|         $u_admin, |         $u_admin, | ||||||
|         $u_public, |         $u_public, | ||||||
|  |         $u_notapproved, | ||||||
|       ), |       ), | ||||||
|       array( |       array( | ||||||
|         $u_disabled, |         $u_disabled, | ||||||
|         $u_notapproved, |  | ||||||
|       )); |       )); | ||||||
|  |  | ||||||
|  |  | ||||||
| @@ -224,7 +224,7 @@ final class PhabricatorAccessControlTestCase extends PhabricatorTestCase { | |||||||
|       )); |       )); | ||||||
|  |  | ||||||
|     $this->checkAccess( |     $this->checkAccess( | ||||||
|       pht('Application Controller'), |       pht('Application Controller, No Login Required'), | ||||||
|       id(clone $app_controller)->setConfig('login', false), |       id(clone $app_controller)->setConfig('login', false), | ||||||
|       $request, |       $request, | ||||||
|       array( |       array( | ||||||
| @@ -232,10 +232,10 @@ final class PhabricatorAccessControlTestCase extends PhabricatorTestCase { | |||||||
|         $u_unverified, |         $u_unverified, | ||||||
|         $u_admin, |         $u_admin, | ||||||
|         $u_public, |         $u_public, | ||||||
|  |         $u_notapproved, | ||||||
|       ), |       ), | ||||||
|       array( |       array( | ||||||
|         $u_disabled, |         $u_disabled, | ||||||
|         $u_notapproved, |  | ||||||
|       )); |       )); | ||||||
|   } |   } | ||||||
|  |  | ||||||
|   | |||||||
| @@ -18,6 +18,9 @@ final class PhabricatorDaemonLogEvent extends PhabricatorDaemonDAO { | |||||||
|         'logID' => array( |         'logID' => array( | ||||||
|           'columns' => array('logID', 'epoch'), |           'columns' => array('logID', 'epoch'), | ||||||
|         ), |         ), | ||||||
|  |         'key_epoch' => array( | ||||||
|  |           'columns' => array('epoch'), | ||||||
|  |         ), | ||||||
|       ), |       ), | ||||||
|     ) + parent::getConfiguration(); |     ) + parent::getConfiguration(); | ||||||
|   } |   } | ||||||
|   | |||||||
| @@ -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); |     $field_map = $parser->parseFields($corpus); | ||||||
|  |  | ||||||
|     $errors = $parser->getErrors(); |     $errors = $parser->getErrors(); | ||||||
|  |     $xactions = $parser->getTransactions(); | ||||||
|  |  | ||||||
|     $revision_id_value = idx( |     $revision_id_value = idx( | ||||||
|       $field_map, |       $field_map, | ||||||
| @@ -49,6 +50,7 @@ final class DifferentialParseCommitMessageConduitAPIMethod | |||||||
|         'value' => $revision_id_value, |         'value' => $revision_id_value, | ||||||
|         'validDomain' => $revision_id_valid_domain, |         'validDomain' => $revision_id_valid_domain, | ||||||
|       ), |       ), | ||||||
|  |       'transactions' => $xactions, | ||||||
|     ); |     ); | ||||||
|   } |   } | ||||||
|  |  | ||||||
|   | |||||||
| @@ -11,6 +11,16 @@ final class DifferentialQueryDiffsConduitAPIMethod | |||||||
|     return pht('Query differential diffs which match certain criteria.'); |     return pht('Query differential diffs which match certain criteria.'); | ||||||
|   } |   } | ||||||
|  |  | ||||||
|  |   public function getMethodStatus() { | ||||||
|  |     return self::METHOD_STATUS_FROZEN; | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   public function getMethodStatusDescription() { | ||||||
|  |     return pht( | ||||||
|  |       'This method is frozen and will eventually be deprecated. New code '. | ||||||
|  |       'should use "differential.diff.search" instead.'); | ||||||
|  |   } | ||||||
|  |  | ||||||
|   protected function defineParamTypes() { |   protected function defineParamTypes() { | ||||||
|     return array( |     return array( | ||||||
|       'ids' => 'optional list<uint>', |       'ids' => 'optional list<uint>', | ||||||
|   | |||||||
| @@ -86,6 +86,7 @@ EOHELP | |||||||
|         array( |         array( | ||||||
|           '/\.py$/', |           '/\.py$/', | ||||||
|           '/\.l?hs$/', |           '/\.l?hs$/', | ||||||
|  |           '/\.ya?ml$/', | ||||||
|         )) |         )) | ||||||
|         ->setDescription( |         ->setDescription( | ||||||
|           pht( |           pht( | ||||||
|   | |||||||
| @@ -32,14 +32,7 @@ final class DifferentialLegacyQuery | |||||||
|   } |   } | ||||||
|  |  | ||||||
|   private static function getMap() { |   private static function getMap() { | ||||||
|     $all = array( |     $all = array_keys(DifferentialRevisionStatus::getAll()); | ||||||
|       DifferentialRevisionStatus::NEEDS_REVIEW, |  | ||||||
|       DifferentialRevisionStatus::NEEDS_REVISION, |  | ||||||
|       DifferentialRevisionStatus::CHANGES_PLANNED, |  | ||||||
|       DifferentialRevisionStatus::ACCEPTED, |  | ||||||
|       DifferentialRevisionStatus::PUBLISHED, |  | ||||||
|       DifferentialRevisionStatus::ABANDONED, |  | ||||||
|     ); |  | ||||||
|  |  | ||||||
|     $open = array(); |     $open = array(); | ||||||
|     $closed = array(); |     $closed = array(); | ||||||
| @@ -61,6 +54,9 @@ final class DifferentialLegacyQuery | |||||||
|       ), |       ), | ||||||
|       self::STATUS_NEEDS_REVIEW => array( |       self::STATUS_NEEDS_REVIEW => array( | ||||||
|         DifferentialRevisionStatus::NEEDS_REVIEW, |         DifferentialRevisionStatus::NEEDS_REVIEW, | ||||||
|  |  | ||||||
|  |         // For legacy callers, "Draft" is treated as "Needs Review". | ||||||
|  |         DifferentialRevisionStatus::DRAFT, | ||||||
|       ), |       ), | ||||||
|       self::STATUS_NEEDS_REVISION => array( |       self::STATUS_NEEDS_REVISION => array( | ||||||
|         DifferentialRevisionStatus::NEEDS_REVISION, |         DifferentialRevisionStatus::NEEDS_REVISION, | ||||||
|   | |||||||
| @@ -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; |     return true; | ||||||
|   } |   } | ||||||
|  |  | ||||||
|  |   public function getConduitKey() { | ||||||
|  |     return 'revision.child'; | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   public function getConduitName() { | ||||||
|  |     return pht('Revision Has Child'); | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   public function getConduitDescription() { | ||||||
|  |     return pht( | ||||||
|  |       'The source revision makes changes required by the destination '. | ||||||
|  |       'revision.'); | ||||||
|  |   } | ||||||
|  |  | ||||||
|   public function getTransactionAddString( |   public function getTransactionAddString( | ||||||
|     $actor, |     $actor, | ||||||
|     $add_count, |     $add_count, | ||||||
|   | |||||||
| @@ -17,6 +17,19 @@ final class DifferentialRevisionDependsOnRevisionEdgeType | |||||||
|     return true; |     return true; | ||||||
|   } |   } | ||||||
|  |  | ||||||
|  |   public function getConduitKey() { | ||||||
|  |     return 'revision.parent'; | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   public function getConduitName() { | ||||||
|  |     return pht('Revision Has Parent'); | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   public function getConduitDescription() { | ||||||
|  |     return pht( | ||||||
|  |       'The source revision depends on changes in the destination revision.'); | ||||||
|  |   } | ||||||
|  |  | ||||||
|   public function getTransactionAddString( |   public function getTransactionAddString( | ||||||
|     $actor, |     $actor, | ||||||
|     $add_count, |     $add_count, | ||||||
|   | |||||||
| @@ -235,6 +235,22 @@ final class DifferentialRevisionEditEngine | |||||||
|       $fields[] = $action->newEditField($object, $viewer); |       $fields[] = $action->newEditField($object, $viewer); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|  |     $fields[] = id(new PhabricatorBoolEditField()) | ||||||
|  |       ->setKey('draft') | ||||||
|  |       ->setLabel(pht('Hold as Draft')) | ||||||
|  |       ->setIsConduitOnly(true) | ||||||
|  |       ->setOptions( | ||||||
|  |         pht('Autosubmit Once Builds Finish'), | ||||||
|  |         pht('Hold as Draft')) | ||||||
|  |       ->setTransactionType( | ||||||
|  |         DifferentialRevisionHoldDraftTransaction::TRANSACTIONTYPE) | ||||||
|  |       ->setDescription(pht('Hold revision as as draft.')) | ||||||
|  |       ->setConduitDescription( | ||||||
|  |         pht( | ||||||
|  |           'Change autosubmission from draft state after builds finish.')) | ||||||
|  |       ->setConduitTypeDescription(pht('New "Hold as Draft" setting.')) | ||||||
|  |       ->setValue($object->getHoldAsDraft()); | ||||||
|  |  | ||||||
|     return $fields; |     return $fields; | ||||||
|   } |   } | ||||||
|  |  | ||||||
|   | |||||||
| @@ -9,6 +9,8 @@ final class DifferentialTransactionEditor | |||||||
|   private $didExpandInlineState = false; |   private $didExpandInlineState = false; | ||||||
|   private $hasReviewTransaction = false; |   private $hasReviewTransaction = false; | ||||||
|   private $affectedPaths; |   private $affectedPaths; | ||||||
|  |   private $firstBroadcast = false; | ||||||
|  |   private $wasDraft = false; | ||||||
|  |  | ||||||
|   public function getEditorApplicationClass() { |   public function getEditorApplicationClass() { | ||||||
|     return 'PhabricatorDifferentialApplication'; |     return 'PhabricatorDifferentialApplication'; | ||||||
| @@ -27,7 +29,7 @@ final class DifferentialTransactionEditor | |||||||
|   } |   } | ||||||
|  |  | ||||||
|   public function isFirstBroadcast() { |   public function isFirstBroadcast() { | ||||||
|     return $this->getIsNewObject(); |     return $this->firstBroadcast; | ||||||
|   } |   } | ||||||
|  |  | ||||||
|   public function getDiffUpdateTransaction(array $xactions) { |   public function getDiffUpdateTransaction(array $xactions) { | ||||||
| @@ -137,8 +139,7 @@ final class DifferentialTransactionEditor | |||||||
|           $object->setRepositoryPHID($diff->getRepositoryPHID()); |           $object->setRepositoryPHID($diff->getRepositoryPHID()); | ||||||
|         } |         } | ||||||
|         $object->attachActiveDiff($diff); |         $object->attachActiveDiff($diff); | ||||||
|  |         $object->setActiveDiffPHID($diff->getPHID()); | ||||||
|         // TODO: Update the `diffPHID` once we add that. |  | ||||||
|         return; |         return; | ||||||
|     } |     } | ||||||
|  |  | ||||||
| @@ -166,6 +167,8 @@ final class DifferentialTransactionEditor | |||||||
|       } |       } | ||||||
|     } |     } | ||||||
|  |  | ||||||
|  |     $this->wasDraft = $object->isDraft(); | ||||||
|  |  | ||||||
|     return parent::expandTransactions($object, $xactions); |     return parent::expandTransactions($object, $xactions); | ||||||
|   } |   } | ||||||
|  |  | ||||||
| @@ -630,6 +633,10 @@ final class DifferentialTransactionEditor | |||||||
|     $phids = array(); |     $phids = array(); | ||||||
|     $phids[] = $object->getAuthorPHID(); |     $phids[] = $object->getAuthorPHID(); | ||||||
|     foreach ($object->getReviewers() as $reviewer) { |     foreach ($object->getReviewers() as $reviewer) { | ||||||
|  |       if ($reviewer->isResigned()) { | ||||||
|  |         continue; | ||||||
|  |       } | ||||||
|  |  | ||||||
|       $phids[] = $reviewer->getReviewerPHID(); |       $phids[] = $reviewer->getReviewerPHID(); | ||||||
|     } |     } | ||||||
|     return $phids; |     return $phids; | ||||||
| @@ -1003,28 +1010,9 @@ final class DifferentialTransactionEditor | |||||||
|   protected function shouldApplyHeraldRules( |   protected function shouldApplyHeraldRules( | ||||||
|     PhabricatorLiskDAO $object, |     PhabricatorLiskDAO $object, | ||||||
|     array $xactions) { |     array $xactions) { | ||||||
|  |  | ||||||
|     if ($this->getIsNewObject()) { |  | ||||||
|     return true; |     return true; | ||||||
|   } |   } | ||||||
|  |  | ||||||
|     foreach ($xactions as $xaction) { |  | ||||||
|       switch ($xaction->getTransactionType()) { |  | ||||||
|         case DifferentialTransaction::TYPE_UPDATE: |  | ||||||
|           if (!$this->getIsCloseByCommit()) { |  | ||||||
|             return true; |  | ||||||
|           } |  | ||||||
|           break; |  | ||||||
|         case DifferentialRevisionCommandeerTransaction::TRANSACTIONTYPE: |  | ||||||
|           // When users commandeer revisions, we may need to trigger |  | ||||||
|           // signatures or author-based rules. |  | ||||||
|           return true; |  | ||||||
|       } |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     return parent::shouldApplyHeraldRules($object, $xactions); |  | ||||||
|   } |  | ||||||
|  |  | ||||||
|   protected function didApplyHeraldRules( |   protected function didApplyHeraldRules( | ||||||
|     PhabricatorLiskDAO $object, |     PhabricatorLiskDAO $object, | ||||||
|     HeraldAdapter $adapter, |     HeraldAdapter $adapter, | ||||||
| @@ -1211,6 +1199,49 @@ final class DifferentialTransactionEditor | |||||||
|       $revision, |       $revision, | ||||||
|       $revision->getActiveDiff()); |       $revision->getActiveDiff()); | ||||||
|  |  | ||||||
|  |     // If the object is still a draft, prevent "Send me an email" and other | ||||||
|  |     // similar rules from acting yet. | ||||||
|  |     if (!$object->shouldBroadcast()) { | ||||||
|  |       $adapter->setForbiddenAction( | ||||||
|  |         HeraldMailableState::STATECONST, | ||||||
|  |         DifferentialHeraldStateReasons::REASON_DRAFT); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     // If this edit didn't actually change the diff (for example, a user | ||||||
|  |     // edited the title or changed subscribers), prevent "Run build plan" | ||||||
|  |     // and other similar rules from acting yet, since the build results will | ||||||
|  |     // not (or, at least, should not) change unless the actual source changes. | ||||||
|  |     // We also don't run Differential builds if the update was caused by | ||||||
|  |     // discovering a commit, as the expectation is that Diffusion builds take | ||||||
|  |     // over once things land. | ||||||
|  |     $has_update = false; | ||||||
|  |     $has_commit = false; | ||||||
|  |  | ||||||
|  |     $type_update = DifferentialTransaction::TYPE_UPDATE; | ||||||
|  |     foreach ($xactions as $xaction) { | ||||||
|  |       if ($xaction->getTransactionType() != $type_update) { | ||||||
|  |         continue; | ||||||
|  |       } | ||||||
|  |  | ||||||
|  |       if ($xaction->getMetadataValue('isCommitUpdate')) { | ||||||
|  |         $has_commit = true; | ||||||
|  |       } else { | ||||||
|  |         $has_update = true; | ||||||
|  |       } | ||||||
|  |  | ||||||
|  |       break; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     if ($has_commit) { | ||||||
|  |       $adapter->setForbiddenAction( | ||||||
|  |         HeraldBuildableState::STATECONST, | ||||||
|  |         DifferentialHeraldStateReasons::REASON_LANDED); | ||||||
|  |     } else if (!$has_update) { | ||||||
|  |       $adapter->setForbiddenAction( | ||||||
|  |         HeraldBuildableState::STATECONST, | ||||||
|  |         DifferentialHeraldStateReasons::REASON_UNCHANGED); | ||||||
|  |     } | ||||||
|  |  | ||||||
|     return $adapter; |     return $adapter; | ||||||
|   } |   } | ||||||
|  |  | ||||||
| @@ -1425,11 +1456,13 @@ final class DifferentialTransactionEditor | |||||||
|   protected function getCustomWorkerState() { |   protected function getCustomWorkerState() { | ||||||
|     return array( |     return array( | ||||||
|       'changedPriorToCommitURI' => $this->changedPriorToCommitURI, |       'changedPriorToCommitURI' => $this->changedPriorToCommitURI, | ||||||
|  |       'firstBroadcast' => $this->firstBroadcast, | ||||||
|     ); |     ); | ||||||
|   } |   } | ||||||
|  |  | ||||||
|   protected function loadCustomWorkerState(array $state) { |   protected function loadCustomWorkerState(array $state) { | ||||||
|     $this->changedPriorToCommitURI = idx($state, 'changedPriorToCommitURI'); |     $this->changedPriorToCommitURI = idx($state, 'changedPriorToCommitURI'); | ||||||
|  |     $this->firstBroadcast = idx($state, 'firstBroadcast'); | ||||||
|     return $this; |     return $this; | ||||||
|   } |   } | ||||||
|  |  | ||||||
| @@ -1532,19 +1565,31 @@ final class DifferentialTransactionEditor | |||||||
|   protected function didApplyTransactions($object, array $xactions) { |   protected function didApplyTransactions($object, array $xactions) { | ||||||
|     // If a draft revision has no outstanding builds and we're automatically |     // If a draft revision has no outstanding builds and we're automatically | ||||||
|     // making drafts public after builds finish, make the revision public. |     // making drafts public after builds finish, make the revision public. | ||||||
|     $auto_undraft = true; |     $auto_undraft = !$object->getHoldAsDraft(); | ||||||
|  |  | ||||||
|     if ($object->isDraft() && $auto_undraft) { |     if ($object->isDraft() && $auto_undraft) { | ||||||
|       $active_builds = $this->hasActiveBuilds($object); |       $active_builds = $this->hasActiveBuilds($object); | ||||||
|       if (!$active_builds) { |       if (!$active_builds) { | ||||||
|  |         // When Harbormaster moves a revision out of the draft state, we | ||||||
|  |         // attribute the action to the revision author since this is more | ||||||
|  |         // natural and more useful. | ||||||
|  |         $author_phid = $object->getAuthorPHID(); | ||||||
|  |  | ||||||
|  |         // Additionally, we change the acting PHID for the transaction set | ||||||
|  |         // to the author if it isn't already a user so that mail comes from | ||||||
|  |         // the natural author. | ||||||
|  |         $acting_phid = $this->getActingAsPHID(); | ||||||
|  |         $user_type = PhabricatorPeopleUserPHIDType::TYPECONST; | ||||||
|  |         if (phid_get_type($acting_phid) != $user_type) { | ||||||
|  |           $this->setActingAsPHID($author_phid); | ||||||
|  |         } | ||||||
|  |  | ||||||
|         $xaction = $object->getApplicationTransactionTemplate() |         $xaction = $object->getApplicationTransactionTemplate() | ||||||
|  |           ->setAuthorPHID($author_phid) | ||||||
|           ->setTransactionType( |           ->setTransactionType( | ||||||
|             DifferentialRevisionRequestReviewTransaction::TRANSACTIONTYPE) |             DifferentialRevisionRequestReviewTransaction::TRANSACTIONTYPE) | ||||||
|           ->setOldValue(false) |  | ||||||
|           ->setNewValue(true); |           ->setNewValue(true); | ||||||
|  |  | ||||||
|         $xaction = $this->populateTransaction($object, $xaction); |  | ||||||
|  |  | ||||||
|         // If we're creating this revision and immediately moving it out of |         // If we're creating this revision and immediately moving it out of | ||||||
|         // the draft state, mark this as a create transaction so it gets |         // the draft state, mark this as a create transaction so it gets | ||||||
|         // hidden in the timeline and mail, since it isn't interesting: it |         // hidden in the timeline and mail, since it isn't interesting: it | ||||||
| @@ -1553,15 +1598,36 @@ final class DifferentialTransactionEditor | |||||||
|           $xaction->setIsCreateTransaction(true); |           $xaction->setIsCreateTransaction(true); | ||||||
|         } |         } | ||||||
|  |  | ||||||
|         $object->openTransaction(); |         // Queue this transaction and apply it separately after the current | ||||||
|  |         // batch of transactions finishes so that Herald can fire on the new | ||||||
|  |         // revision state. See T13027 for discussion. | ||||||
|  |         $this->queueTransaction($xaction); | ||||||
|  |       } | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     // If the revision is new or was a draft, and is no longer a draft, we | ||||||
|  |     // might be sending the first email about it. | ||||||
|  |  | ||||||
|  |     // This might mean it was created directly into a non-draft state, or | ||||||
|  |     // it just automatically undrafted after builds finished, or a user | ||||||
|  |     // explicitly promoted it out of the draft state with an action like | ||||||
|  |     // "Request Review". | ||||||
|  |  | ||||||
|  |     // If we haven't sent any email about it yet, mark this email as the first | ||||||
|  |     // email so the mail gets enriched with "SUMMARY" and "TEST PLAN". | ||||||
|  |  | ||||||
|  |     $is_new = $this->getIsNewObject(); | ||||||
|  |     $was_draft = $this->wasDraft; | ||||||
|  |  | ||||||
|  |     if (!$object->isDraft() && ($was_draft || $is_new)) { | ||||||
|  |       if (!$object->getHasBroadcast()) { | ||||||
|  |         // Mark this as the first broadcast we're sending about the revision | ||||||
|  |         // so mail can generate specially. | ||||||
|  |         $this->firstBroadcast = true; | ||||||
|  |  | ||||||
|         $object |         $object | ||||||
|             ->setStatus(DifferentialRevisionStatus::NEEDS_REVIEW) |           ->setHasBroadcast(true) | ||||||
|           ->save(); |           ->save(); | ||||||
|  |  | ||||||
|           $xaction->save(); |  | ||||||
|         $object->saveTransaction(); |  | ||||||
|  |  | ||||||
|         $xactions[] = $xaction; |  | ||||||
|       } |       } | ||||||
|     } |     } | ||||||
|  |  | ||||||
| @@ -1570,58 +1636,12 @@ final class DifferentialTransactionEditor | |||||||
|  |  | ||||||
|   private function hasActiveBuilds($object) { |   private function hasActiveBuilds($object) { | ||||||
|     $viewer = $this->requireActor(); |     $viewer = $this->requireActor(); | ||||||
|     $diff = $object->getActiveDiff(); |  | ||||||
|  |  | ||||||
|     $buildables = id(new HarbormasterBuildableQuery()) |     $builds = $object->loadActiveBuilds($viewer); | ||||||
|       ->setViewer($viewer) |  | ||||||
|       ->withContainerPHIDs(array($object->getPHID())) |  | ||||||
|       ->withBuildablePHIDs(array($diff->getPHID())) |  | ||||||
|       ->withManualBuildables(false) |  | ||||||
|       ->execute(); |  | ||||||
|     if (!$buildables) { |  | ||||||
|       return false; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     $builds = id(new HarbormasterBuildQuery()) |  | ||||||
|       ->setViewer($viewer) |  | ||||||
|       ->withBuildablePHIDs(mpull($buildables, 'getPHID')) |  | ||||||
|       ->withBuildStatuses( |  | ||||||
|         array( |  | ||||||
|           HarbormasterBuildStatus::STATUS_INACTIVE, |  | ||||||
|           HarbormasterBuildStatus::STATUS_PENDING, |  | ||||||
|           HarbormasterBuildStatus::STATUS_BUILDING, |  | ||||||
|           HarbormasterBuildStatus::STATUS_FAILED, |  | ||||||
|           HarbormasterBuildStatus::STATUS_ABORTED, |  | ||||||
|           HarbormasterBuildStatus::STATUS_ERROR, |  | ||||||
|           HarbormasterBuildStatus::STATUS_PAUSED, |  | ||||||
|           HarbormasterBuildStatus::STATUS_DEADLOCKED, |  | ||||||
|         )) |  | ||||||
|       ->needBuildTargets(true) |  | ||||||
|       ->execute(); |  | ||||||
|     if (!$builds) { |     if (!$builds) { | ||||||
|       return false; |       return false; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     $active = array(); |  | ||||||
|     foreach ($builds as $key => $build) { |  | ||||||
|       foreach ($build->getBuildTargets() as $target) { |  | ||||||
|         if ($target->isAutotarget()) { |  | ||||||
|           // Ignore autotargets when looking for active of failed builds. If |  | ||||||
|           // local tests fail and you continue anyway, you don't need to |  | ||||||
|           // double-confirm them. |  | ||||||
|           continue; |  | ||||||
|         } |  | ||||||
|  |  | ||||||
|         // This build has at least one real target that's doing something. |  | ||||||
|         $active[$key] = $build; |  | ||||||
|         break; |  | ||||||
|       } |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     if (!$active) { |  | ||||||
|       return false; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     return true; |     return true; | ||||||
|   } |   } | ||||||
|  |  | ||||||
|   | |||||||
| @@ -268,6 +268,16 @@ final class DifferentialDiffExtractionEngine extends Phobject { | |||||||
|  |  | ||||||
|     $xactions = array(); |     $xactions = array(); | ||||||
|  |  | ||||||
|  |     // If the revision isn't closed or "Accepted", write a warning into the | ||||||
|  |     // transaction log. This makes it more clear when users bend the rules. | ||||||
|  |     if (!$revision->isClosed() && !$revision->isAccepted()) { | ||||||
|  |       $wrong_type = DifferentialRevisionWrongStateTransaction::TRANSACTIONTYPE; | ||||||
|  |  | ||||||
|  |       $xactions[] = id(new DifferentialTransaction()) | ||||||
|  |         ->setTransactionType($wrong_type) | ||||||
|  |         ->setNewValue($revision->getModernRevisionStatus()); | ||||||
|  |     } | ||||||
|  |  | ||||||
|     $xactions[] = id(new DifferentialTransaction()) |     $xactions[] = id(new DifferentialTransaction()) | ||||||
|       ->setTransactionType(DifferentialTransaction::TYPE_UPDATE) |       ->setTransactionType(DifferentialTransaction::TYPE_UPDATE) | ||||||
|       ->setIgnoreOnNoEffect(true) |       ->setIgnoreOnNoEffect(true) | ||||||
|   | |||||||
| @@ -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 $errors; | ||||||
|   private $commitMessageFields; |   private $commitMessageFields; | ||||||
|   private $raiseMissingFieldErrors = true; |   private $raiseMissingFieldErrors = true; | ||||||
|  |   private $xactions; | ||||||
|  |  | ||||||
|   public static function newStandardParser(PhabricatorUser $viewer) { |   public static function newStandardParser(PhabricatorUser $viewer) { | ||||||
|     $key_title = DifferentialTitleCommitMessageField::FIELDKEY; |     $key_title = DifferentialTitleCommitMessageField::FIELDKEY; | ||||||
| @@ -134,6 +135,7 @@ final class DifferentialCommitMessageParser extends Phobject { | |||||||
|    */ |    */ | ||||||
|   public function parseCorpus($corpus) { |   public function parseCorpus($corpus) { | ||||||
|     $this->errors = array(); |     $this->errors = array(); | ||||||
|  |     $this->xactions = array(); | ||||||
|  |  | ||||||
|     $label_map = $this->getLabelMap(); |     $label_map = $this->getLabelMap(); | ||||||
|     $key_title = $this->titleKey; |     $key_title = $this->titleKey; | ||||||
| @@ -284,12 +286,25 @@ final class DifferentialCommitMessageParser extends Phobject { | |||||||
|       try { |       try { | ||||||
|         $result = $field->parseFieldValue($text_value); |         $result = $field->parseFieldValue($text_value); | ||||||
|         $result_map[$field_key] = $result; |         $result_map[$field_key] = $result; | ||||||
|  |  | ||||||
|  |         try { | ||||||
|  |           $xactions = $field->getFieldTransactions($result); | ||||||
|  |           foreach ($xactions as $xaction) { | ||||||
|  |             $this->xactions[] = $xaction; | ||||||
|  |           } | ||||||
|  |         } catch (Exception $ex) { | ||||||
|  |           $this->errors[] = pht( | ||||||
|  |             'Error extracting field transactions from "%s": %s', | ||||||
|  |             $field->getFieldName(), | ||||||
|  |             $ex->getMessage()); | ||||||
|  |         } | ||||||
|       } catch (DifferentialFieldParseException $ex) { |       } catch (DifferentialFieldParseException $ex) { | ||||||
|         $this->errors[] = pht( |         $this->errors[] = pht( | ||||||
|           'Error parsing field "%s": %s', |           'Error parsing field "%s": %s', | ||||||
|           $field->getFieldName(), |           $field->getFieldName(), | ||||||
|           $ex->getMessage()); |           $ex->getMessage()); | ||||||
|       } |       } | ||||||
|  |  | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     if ($this->getRaiseMissingFieldErrors()) { |     if ($this->getRaiseMissingFieldErrors()) { | ||||||
| @@ -317,6 +332,14 @@ final class DifferentialCommitMessageParser extends Phobject { | |||||||
|   } |   } | ||||||
|  |  | ||||||
|  |  | ||||||
|  |   /** | ||||||
|  |    * @task parse | ||||||
|  |    */ | ||||||
|  |   public function getTransactions() { | ||||||
|  |     return $this->xactions; | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |  | ||||||
| /* -(  Support Methods  )---------------------------------------------------- */ | /* -(  Support Methods  )---------------------------------------------------- */ | ||||||
|  |  | ||||||
|  |  | ||||||
|   | |||||||
| @@ -6,6 +6,7 @@ final class DifferentialDiffQuery | |||||||
|   private $ids; |   private $ids; | ||||||
|   private $phids; |   private $phids; | ||||||
|   private $revisionIDs; |   private $revisionIDs; | ||||||
|  |   private $revisionPHIDs; | ||||||
|   private $commitPHIDs; |   private $commitPHIDs; | ||||||
|   private $hasRevision; |   private $hasRevision; | ||||||
|  |  | ||||||
| @@ -27,6 +28,11 @@ final class DifferentialDiffQuery | |||||||
|     return $this; |     return $this; | ||||||
|   } |   } | ||||||
|  |  | ||||||
|  |   public function withRevisionPHIDs(array $revision_phids) { | ||||||
|  |     $this->revisionPHIDs = $revision_phids; | ||||||
|  |     return $this; | ||||||
|  |   } | ||||||
|  |  | ||||||
|   public function withCommitPHIDs(array $phids) { |   public function withCommitPHIDs(array $phids) { | ||||||
|     $this->commitPHIDs = $phids; |     $this->commitPHIDs = $phids; | ||||||
|     return $this; |     return $this; | ||||||
| @@ -160,6 +166,25 @@ final class DifferentialDiffQuery | |||||||
|       } |       } | ||||||
|     } |     } | ||||||
|  |  | ||||||
|  |     if ($this->revisionPHIDs !== null) { | ||||||
|  |       $viewer = $this->getViewer(); | ||||||
|  |  | ||||||
|  |       $revisions = id(new DifferentialRevisionQuery()) | ||||||
|  |         ->setViewer($viewer) | ||||||
|  |         ->setParentQuery($this) | ||||||
|  |         ->withPHIDs($this->revisionPHIDs) | ||||||
|  |         ->execute(); | ||||||
|  |       $revision_ids = mpull($revisions, 'getID'); | ||||||
|  |       if (!$revision_ids) { | ||||||
|  |         throw new PhabricatorEmptyQueryException(); | ||||||
|  |       } | ||||||
|  |  | ||||||
|  |       $where[] = qsprintf( | ||||||
|  |         $conn, | ||||||
|  |         'revisionID IN (%Ls)', | ||||||
|  |         $revision_ids); | ||||||
|  |     } | ||||||
|  |  | ||||||
|     return $where; |     return $where; | ||||||
|   } |   } | ||||||
|  |  | ||||||
|   | |||||||
| @@ -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 $needDrafts; | ||||||
|   private $needFlags; |   private $needFlags; | ||||||
|  |  | ||||||
|   private $buildingGlobalOrder; |  | ||||||
|  |  | ||||||
|  |  | ||||||
| /* -(  Query Configuration  )------------------------------------------------ */ | /* -(  Query Configuration  )------------------------------------------------ */ | ||||||
|  |  | ||||||
| @@ -484,12 +482,11 @@ final class DifferentialRevisionQuery | |||||||
|     } |     } | ||||||
|  |  | ||||||
|     if (count($selects) > 1) { |     if (count($selects) > 1) { | ||||||
|       $this->buildingGlobalOrder = true; |  | ||||||
|       $query = qsprintf( |       $query = qsprintf( | ||||||
|         $conn_r, |         $conn_r, | ||||||
|         '%Q %Q %Q', |         '%Q %Q %Q', | ||||||
|         implode(' UNION DISTINCT ', $selects), |         implode(' UNION DISTINCT ', $selects), | ||||||
|         $this->buildOrderClause($conn_r), |         $this->buildOrderClause($conn_r, true), | ||||||
|         $this->buildLimitClause($conn_r)); |         $this->buildLimitClause($conn_r)); | ||||||
|     } else { |     } else { | ||||||
|       $query = head($selects); |       $query = head($selects); | ||||||
| @@ -513,7 +510,6 @@ final class DifferentialRevisionQuery | |||||||
|     $group_by = $this->buildGroupClause($conn_r); |     $group_by = $this->buildGroupClause($conn_r); | ||||||
|     $having = $this->buildHavingClause($conn_r); |     $having = $this->buildHavingClause($conn_r); | ||||||
|  |  | ||||||
|     $this->buildingGlobalOrder = false; |  | ||||||
|     $order_by = $this->buildOrderClause($conn_r); |     $order_by = $this->buildOrderClause($conn_r); | ||||||
|  |  | ||||||
|     $limit = $this->buildLimitClause($conn_r); |     $limit = $this->buildLimitClause($conn_r); | ||||||
| @@ -758,17 +754,9 @@ final class DifferentialRevisionQuery | |||||||
|   } |   } | ||||||
|  |  | ||||||
|   public function getOrderableColumns() { |   public function getOrderableColumns() { | ||||||
|     $primary = ($this->buildingGlobalOrder ? null : 'r'); |  | ||||||
|  |  | ||||||
|     return array( |     return array( | ||||||
|       'id' => array( |  | ||||||
|         'table' => $primary, |  | ||||||
|         'column' => 'id', |  | ||||||
|         'type' => 'int', |  | ||||||
|         'unique' => true, |  | ||||||
|       ), |  | ||||||
|       'updated' => array( |       'updated' => array( | ||||||
|         'table' => $primary, |         'table' => $this->getPrimaryTableAlias(), | ||||||
|         'column' => 'dateModified', |         'column' => 'dateModified', | ||||||
|         'type' => 'int', |         'type' => 'int', | ||||||
|       ), |       ), | ||||||
|   | |||||||
| @@ -123,6 +123,14 @@ final class DifferentialRevisionRequiredActionResultBucket | |||||||
|     $reviewing = array( |     $reviewing = array( | ||||||
|       DifferentialReviewerStatus::STATUS_ADDED, |       DifferentialReviewerStatus::STATUS_ADDED, | ||||||
|       DifferentialReviewerStatus::STATUS_COMMENTED, |       DifferentialReviewerStatus::STATUS_COMMENTED, | ||||||
|  |  | ||||||
|  |       // If an author has used "Request Review" to put an accepted revision | ||||||
|  |       // back into the "Needs Review" state, include "Accepted" reviewers | ||||||
|  |       // whose reviews have been voided in the "Should Review" bucket. | ||||||
|  |  | ||||||
|  |       // If we don't do this, they end up in "Waiting on Other Reviewers", | ||||||
|  |       // even if there are no other reviewers. | ||||||
|  |       DifferentialReviewerStatus::STATUS_ACCEPTED, | ||||||
|     ); |     ); | ||||||
|     $reviewing = array_fuse($reviewing); |     $reviewing = array_fuse($reviewing); | ||||||
|  |  | ||||||
| @@ -130,7 +138,7 @@ final class DifferentialRevisionRequiredActionResultBucket | |||||||
|  |  | ||||||
|     $results = array(); |     $results = array(); | ||||||
|     foreach ($objects as $key => $object) { |     foreach ($objects as $key => $object) { | ||||||
|       if (!$this->hasReviewersWithStatus($object, $phids, $reviewing)) { |       if (!$this->hasReviewersWithStatus($object, $phids, $reviewing, true)) { | ||||||
|         continue; |         continue; | ||||||
|       } |       } | ||||||
|  |  | ||||||
|   | |||||||
| @@ -53,7 +53,8 @@ abstract class DifferentialRevisionResultBucket | |||||||
|   protected function hasReviewersWithStatus( |   protected function hasReviewersWithStatus( | ||||||
|     DifferentialRevision $revision, |     DifferentialRevision $revision, | ||||||
|     array $phids, |     array $phids, | ||||||
|     array $statuses) { |     array $statuses, | ||||||
|  |     $include_voided = null) { | ||||||
|  |  | ||||||
|     foreach ($revision->getReviewers() as $reviewer) { |     foreach ($revision->getReviewers() as $reviewer) { | ||||||
|       $reviewer_phid = $reviewer->getReviewerPHID(); |       $reviewer_phid = $reviewer->getReviewerPHID(); | ||||||
| @@ -66,6 +67,15 @@ abstract class DifferentialRevisionResultBucket | |||||||
|         continue; |         continue; | ||||||
|       } |       } | ||||||
|  |  | ||||||
|  |       if ($include_voided !== null) { | ||||||
|  |         if ($status == DifferentialReviewerStatus::STATUS_ACCEPTED) { | ||||||
|  |           $is_voided = (bool)$reviewer->getVoidedPHID(); | ||||||
|  |           if ($is_voided !== $include_voided) { | ||||||
|  |             continue; | ||||||
|  |           } | ||||||
|  |         } | ||||||
|  |       } | ||||||
|  |  | ||||||
|       return true; |       return true; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|   | |||||||
| @@ -41,6 +41,20 @@ final class DifferentialChangesetOneUpRenderer | |||||||
|  |  | ||||||
|     $column_width = 4; |     $column_width = 4; | ||||||
|  |  | ||||||
|  |     $aural_minus = javelin_tag( | ||||||
|  |       'span', | ||||||
|  |       array( | ||||||
|  |         'aural' => true, | ||||||
|  |       ), | ||||||
|  |       '- '); | ||||||
|  |  | ||||||
|  |     $aural_plus = javelin_tag( | ||||||
|  |       'span', | ||||||
|  |       array( | ||||||
|  |         'aural' => true, | ||||||
|  |       ), | ||||||
|  |       '+ '); | ||||||
|  |  | ||||||
|     $out = array(); |     $out = array(); | ||||||
|     foreach ($primitives as $k => $p) { |     foreach ($primitives as $k => $p) { | ||||||
|       $type = $p['type']; |       $type = $p['type']; | ||||||
| @@ -55,8 +69,10 @@ final class DifferentialChangesetOneUpRenderer | |||||||
|           if ($is_old) { |           if ($is_old) { | ||||||
|             if ($p['htype']) { |             if ($p['htype']) { | ||||||
|               $class = 'left old'; |               $class = 'left old'; | ||||||
|  |               $aural = $aural_minus; | ||||||
|             } else { |             } else { | ||||||
|               $class = 'left'; |               $class = 'left'; | ||||||
|  |               $aural = null; | ||||||
|             } |             } | ||||||
|  |  | ||||||
|             if ($type == 'old-file') { |             if ($type == 'old-file') { | ||||||
| @@ -79,14 +95,20 @@ final class DifferentialChangesetOneUpRenderer | |||||||
|               ), |               ), | ||||||
|               $line); |               $line); | ||||||
|  |  | ||||||
|  |             $render = $p['render']; | ||||||
|  |             if ($aural !== null) { | ||||||
|  |               $render = array($aural, $render); | ||||||
|  |             } | ||||||
|  |  | ||||||
|             $cells[] = phutil_tag('th', array('class' => $class)); |             $cells[] = phutil_tag('th', array('class' => $class)); | ||||||
|             $cells[] = $no_copy; |             $cells[] = $no_copy; | ||||||
|             $cells[] = phutil_tag('td', array('class' => $class), $p['render']); |             $cells[] = phutil_tag('td', array('class' => $class), $render); | ||||||
|             $cells[] = $no_coverage; |             $cells[] = $no_coverage; | ||||||
|           } else { |           } else { | ||||||
|             if ($p['htype']) { |             if ($p['htype']) { | ||||||
|               $class = 'right new'; |               $class = 'right new'; | ||||||
|               $cells[] = phutil_tag('th', array('class' => $class)); |               $cells[] = phutil_tag('th', array('class' => $class)); | ||||||
|  |               $aural = $aural_plus; | ||||||
|             } else { |             } else { | ||||||
|               $class = 'right'; |               $class = 'right'; | ||||||
|               if ($left_prefix) { |               if ($left_prefix) { | ||||||
| @@ -98,6 +120,7 @@ final class DifferentialChangesetOneUpRenderer | |||||||
|               $oline = $p['oline']; |               $oline = $p['oline']; | ||||||
|  |  | ||||||
|               $cells[] = phutil_tag('th', array('id' => $left_id), $oline); |               $cells[] = phutil_tag('th', array('id' => $left_id), $oline); | ||||||
|  |               $aural = null; | ||||||
|             } |             } | ||||||
|  |  | ||||||
|             if ($type == 'new-file') { |             if ($type == 'new-file') { | ||||||
| @@ -120,8 +143,13 @@ final class DifferentialChangesetOneUpRenderer | |||||||
|               ), |               ), | ||||||
|               $line); |               $line); | ||||||
|  |  | ||||||
|  |             $render = $p['render']; | ||||||
|  |             if ($aural !== null) { | ||||||
|  |               $render = array($aural, $render); | ||||||
|  |             } | ||||||
|  |  | ||||||
|             $cells[] = $no_copy; |             $cells[] = $no_copy; | ||||||
|             $cells[] = phutil_tag('td', array('class' => $class), $p['render']); |             $cells[] = phutil_tag('td', array('class' => $class), $render); | ||||||
|             $cells[] = $no_coverage; |             $cells[] = $no_coverage; | ||||||
|           } |           } | ||||||
|  |  | ||||||
|   | |||||||
| @@ -9,7 +9,8 @@ final class DifferentialDiff | |||||||
|     HarbormasterCircleCIBuildableInterface, |     HarbormasterCircleCIBuildableInterface, | ||||||
|     HarbormasterBuildkiteBuildableInterface, |     HarbormasterBuildkiteBuildableInterface, | ||||||
|     PhabricatorApplicationTransactionInterface, |     PhabricatorApplicationTransactionInterface, | ||||||
|     PhabricatorDestructibleInterface { |     PhabricatorDestructibleInterface, | ||||||
|  |     PhabricatorConduitResultInterface { | ||||||
|  |  | ||||||
|   protected $revisionID; |   protected $revisionID; | ||||||
|   protected $authorPHID; |   protected $authorPHID; | ||||||
| @@ -740,4 +741,82 @@ final class DifferentialDiff | |||||||
|     $this->saveTransaction(); |     $this->saveTransaction(); | ||||||
|   } |   } | ||||||
|  |  | ||||||
|  |  | ||||||
|  | /* -(  PhabricatorConduitResultInterface  )---------------------------------- */ | ||||||
|  |  | ||||||
|  |  | ||||||
|  |   public function getFieldSpecificationsForConduit() { | ||||||
|  |     return array( | ||||||
|  |       id(new PhabricatorConduitSearchFieldSpecification()) | ||||||
|  |         ->setKey('revisionPHID') | ||||||
|  |         ->setType('phid') | ||||||
|  |         ->setDescription(pht('Associated revision PHID.')), | ||||||
|  |       id(new PhabricatorConduitSearchFieldSpecification()) | ||||||
|  |         ->setKey('authorPHID') | ||||||
|  |         ->setType('phid') | ||||||
|  |         ->setDescription(pht('Revision author PHID.')), | ||||||
|  |       id(new PhabricatorConduitSearchFieldSpecification()) | ||||||
|  |         ->setKey('repositoryPHID') | ||||||
|  |         ->setType('phid') | ||||||
|  |         ->setDescription(pht('Associated repository PHID.')), | ||||||
|  |       id(new PhabricatorConduitSearchFieldSpecification()) | ||||||
|  |         ->setKey('refs') | ||||||
|  |         ->setType('map<string, wild>') | ||||||
|  |         ->setDescription(pht('List of related VCS references.')), | ||||||
|  |     ); | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   public function getFieldValuesForConduit() { | ||||||
|  |     $refs = array(); | ||||||
|  |  | ||||||
|  |     $branch = $this->getBranch(); | ||||||
|  |     if (strlen($branch)) { | ||||||
|  |       $refs[] = array( | ||||||
|  |         'type' => 'branch', | ||||||
|  |         'name' => $branch, | ||||||
|  |       ); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     $onto = $this->loadTargetBranch(); | ||||||
|  |     if (strlen($onto)) { | ||||||
|  |       $refs[] = array( | ||||||
|  |         'type' => 'onto', | ||||||
|  |         'name' => $onto, | ||||||
|  |       ); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     $base = $this->getSourceControlBaseRevision(); | ||||||
|  |     if (strlen($base)) { | ||||||
|  |       $refs[] = array( | ||||||
|  |         'type' => 'base', | ||||||
|  |         'identifier' => $base, | ||||||
|  |       ); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     $bookmark = $this->getBookmark(); | ||||||
|  |     if (strlen($bookmark)) { | ||||||
|  |       $refs[] = array( | ||||||
|  |         'type' => 'bookmark', | ||||||
|  |         'name' => $bookmark, | ||||||
|  |       ); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     $revision_phid = null; | ||||||
|  |     if ($this->getRevisionID()) { | ||||||
|  |       $revision_phid = $this->getRevision()->getPHID(); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     return array( | ||||||
|  |       'revisionPHID' => $revision_phid, | ||||||
|  |       'authorPHID' => $this->getAuthorPHID(), | ||||||
|  |       'repositoryPHID' => $this->getRepositoryPHID(), | ||||||
|  |       'refs' => $refs, | ||||||
|  |     ); | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   public function getConduitSearchAttachments() { | ||||||
|  |     return array(); | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |  | ||||||
| } | } | ||||||
|   | |||||||
| @@ -35,6 +35,8 @@ final class DifferentialRevision extends DifferentialDAO | |||||||
|   protected $mailKey; |   protected $mailKey; | ||||||
|   protected $branchName; |   protected $branchName; | ||||||
|   protected $repositoryPHID; |   protected $repositoryPHID; | ||||||
|  |   protected $activeDiffPHID; | ||||||
|  |  | ||||||
|   protected $viewPolicy = PhabricatorPolicies::POLICY_USER; |   protected $viewPolicy = PhabricatorPolicies::POLICY_USER; | ||||||
|   protected $editPolicy = PhabricatorPolicies::POLICY_USER; |   protected $editPolicy = PhabricatorPolicies::POLICY_USER; | ||||||
|   protected $properties = array(); |   protected $properties = array(); | ||||||
| @@ -57,6 +59,8 @@ final class DifferentialRevision extends DifferentialDAO | |||||||
|   const RELATION_SUBSCRIBED   = 'subd'; |   const RELATION_SUBSCRIBED   = 'subd'; | ||||||
|  |  | ||||||
|   const PROPERTY_CLOSED_FROM_ACCEPTED = 'wasAcceptedBeforeClose'; |   const PROPERTY_CLOSED_FROM_ACCEPTED = 'wasAcceptedBeforeClose'; | ||||||
|  |   const PROPERTY_DRAFT_HOLD = 'draft.hold'; | ||||||
|  |   const PROPERTY_HAS_BROADCAST = 'draft.broadcast'; | ||||||
|  |  | ||||||
|   public static function initializeNewRevision(PhabricatorUser $actor) { |   public static function initializeNewRevision(PhabricatorUser $actor) { | ||||||
|     $app = id(new PhabricatorApplicationQuery()) |     $app = id(new PhabricatorApplicationQuery()) | ||||||
| @@ -67,13 +71,19 @@ final class DifferentialRevision extends DifferentialDAO | |||||||
|     $view_policy = $app->getPolicy( |     $view_policy = $app->getPolicy( | ||||||
|       DifferentialDefaultViewCapability::CAPABILITY); |       DifferentialDefaultViewCapability::CAPABILITY); | ||||||
|  |  | ||||||
|  |     if (PhabricatorEnv::getEnvConfig('phabricator.show-prototypes')) { | ||||||
|  |       $initial_state = DifferentialRevisionStatus::DRAFT; | ||||||
|  |     } else { | ||||||
|  |       $initial_state = DifferentialRevisionStatus::NEEDS_REVIEW; | ||||||
|  |     } | ||||||
|  |  | ||||||
|     return id(new DifferentialRevision()) |     return id(new DifferentialRevision()) | ||||||
|       ->setViewPolicy($view_policy) |       ->setViewPolicy($view_policy) | ||||||
|       ->setAuthorPHID($actor->getPHID()) |       ->setAuthorPHID($actor->getPHID()) | ||||||
|       ->attachRepository(null) |       ->attachRepository(null) | ||||||
|       ->attachActiveDiff(null) |       ->attachActiveDiff(null) | ||||||
|       ->attachReviewers(array()) |       ->attachReviewers(array()) | ||||||
|       ->setModernRevisionStatus(DifferentialRevisionStatus::NEEDS_REVIEW); |       ->setModernRevisionStatus($initial_state); | ||||||
|   } |   } | ||||||
|  |  | ||||||
|   protected function getConfiguration() { |   protected function getConfiguration() { | ||||||
| @@ -702,6 +712,54 @@ final class DifferentialRevision extends DifferentialDAO | |||||||
|     return false; |     return false; | ||||||
|   } |   } | ||||||
|  |  | ||||||
|  |   public function getHoldAsDraft() { | ||||||
|  |     return $this->getProperty(self::PROPERTY_DRAFT_HOLD, false); | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   public function setHoldAsDraft($hold) { | ||||||
|  |     return $this->setProperty(self::PROPERTY_DRAFT_HOLD, $hold); | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   public function getHasBroadcast() { | ||||||
|  |     return $this->getProperty(self::PROPERTY_HAS_BROADCAST, false); | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   public function setHasBroadcast($has_broadcast) { | ||||||
|  |     return $this->setProperty(self::PROPERTY_HAS_BROADCAST, $has_broadcast); | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |  | ||||||
|  |   public function loadActiveBuilds(PhabricatorUser $viewer) { | ||||||
|  |     $diff = $this->getActiveDiff(); | ||||||
|  |  | ||||||
|  |     $buildables = id(new HarbormasterBuildableQuery()) | ||||||
|  |       ->setViewer($viewer) | ||||||
|  |       ->withContainerPHIDs(array($this->getPHID())) | ||||||
|  |       ->withBuildablePHIDs(array($diff->getPHID())) | ||||||
|  |       ->withManualBuildables(false) | ||||||
|  |       ->execute(); | ||||||
|  |     if (!$buildables) { | ||||||
|  |       return array(); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     return id(new HarbormasterBuildQuery()) | ||||||
|  |       ->setViewer($viewer) | ||||||
|  |       ->withBuildablePHIDs(mpull($buildables, 'getPHID')) | ||||||
|  |       ->withAutobuilds(false) | ||||||
|  |       ->withBuildStatuses( | ||||||
|  |         array( | ||||||
|  |           HarbormasterBuildStatus::STATUS_INACTIVE, | ||||||
|  |           HarbormasterBuildStatus::STATUS_PENDING, | ||||||
|  |           HarbormasterBuildStatus::STATUS_BUILDING, | ||||||
|  |           HarbormasterBuildStatus::STATUS_FAILED, | ||||||
|  |           HarbormasterBuildStatus::STATUS_ABORTED, | ||||||
|  |           HarbormasterBuildStatus::STATUS_ERROR, | ||||||
|  |           HarbormasterBuildStatus::STATUS_PAUSED, | ||||||
|  |           HarbormasterBuildStatus::STATUS_DEADLOCKED, | ||||||
|  |         )) | ||||||
|  |       ->execute(); | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |  | ||||||
| /* -(  HarbormasterBuildableInterface  )------------------------------------- */ | /* -(  HarbormasterBuildableInterface  )------------------------------------- */ | ||||||
|  |  | ||||||
| @@ -938,6 +996,18 @@ final class DifferentialRevision extends DifferentialDAO | |||||||
|         ->setKey('status') |         ->setKey('status') | ||||||
|         ->setType('map<string, wild>') |         ->setType('map<string, wild>') | ||||||
|         ->setDescription(pht('Information about revision status.')), |         ->setDescription(pht('Information about revision status.')), | ||||||
|  |       id(new PhabricatorConduitSearchFieldSpecification()) | ||||||
|  |         ->setKey('repositoryPHID') | ||||||
|  |         ->setType('phid?') | ||||||
|  |         ->setDescription(pht('Revision repository PHID.')), | ||||||
|  |       id(new PhabricatorConduitSearchFieldSpecification()) | ||||||
|  |         ->setKey('diffPHID') | ||||||
|  |         ->setType('phid') | ||||||
|  |         ->setDescription(pht('Active diff PHID.')), | ||||||
|  |       id(new PhabricatorConduitSearchFieldSpecification()) | ||||||
|  |         ->setKey('summary') | ||||||
|  |         ->setType('string') | ||||||
|  |         ->setDescription(pht('Revision summary.')), | ||||||
|     ); |     ); | ||||||
|   } |   } | ||||||
|  |  | ||||||
| @@ -954,6 +1024,9 @@ final class DifferentialRevision extends DifferentialDAO | |||||||
|       'title' => $this->getTitle(), |       'title' => $this->getTitle(), | ||||||
|       'authorPHID' => $this->getAuthorPHID(), |       'authorPHID' => $this->getAuthorPHID(), | ||||||
|       'status' => $status_info, |       'status' => $status_info, | ||||||
|  |       'repositoryPHID' => $this->getRepositoryPHID(), | ||||||
|  |       'diffPHID' => $this->getActiveDiffPHID(), | ||||||
|  |       'summary' => $this->getSummary(), | ||||||
|     ); |     ); | ||||||
|   } |   } | ||||||
|  |  | ||||||
|   | |||||||
| @@ -301,6 +301,9 @@ final class DifferentialChangesetListView extends AphrontView { | |||||||
|  |  | ||||||
|         'Hide or show all inline comments.' => |         'Hide or show all inline comments.' => | ||||||
|           pht('Hide or show all inline comments.'), |           pht('Hide or show all inline comments.'), | ||||||
|  |  | ||||||
|  |         'Finish editing inline comments before changing display modes.' => | ||||||
|  |           pht('Finish editing inline comments before changing display modes.'), | ||||||
|       ), |       ), | ||||||
|     )); |     )); | ||||||
|  |  | ||||||
|   | |||||||
| @@ -47,6 +47,7 @@ final class DifferentialReviewersView extends AphrontView { | |||||||
|  |  | ||||||
|       $action_phid = $reviewer->getLastActionDiffPHID(); |       $action_phid = $reviewer->getLastActionDiffPHID(); | ||||||
|       $is_current_action = $this->isCurrent($action_phid); |       $is_current_action = $this->isCurrent($action_phid); | ||||||
|  |       $is_voided = (bool)$reviewer->getVoidedPHID(); | ||||||
|  |  | ||||||
|       $comment_phid = $reviewer->getLastCommentDiffPHID(); |       $comment_phid = $reviewer->getLastCommentDiffPHID(); | ||||||
|       $is_current_comment = $this->isCurrent($comment_phid); |       $is_current_comment = $this->isCurrent($comment_phid); | ||||||
| @@ -86,7 +87,7 @@ final class DifferentialReviewersView extends AphrontView { | |||||||
|           break; |           break; | ||||||
|  |  | ||||||
|         case DifferentialReviewerStatus::STATUS_ACCEPTED: |         case DifferentialReviewerStatus::STATUS_ACCEPTED: | ||||||
|           if ($is_current_action) { |           if ($is_current_action && !$is_voided) { | ||||||
|             $icon = PHUIStatusItemView::ICON_ACCEPT; |             $icon = PHUIStatusItemView::ICON_ACCEPT; | ||||||
|             $color = 'green'; |             $color = 'green'; | ||||||
|             if ($authority_name !== null) { |             if ($authority_name !== null) { | ||||||
| @@ -97,7 +98,12 @@ final class DifferentialReviewersView extends AphrontView { | |||||||
|           } else { |           } else { | ||||||
|             $icon = 'fa-check-circle-o'; |             $icon = 'fa-check-circle-o'; | ||||||
|             $color = 'bluegrey'; |             $color = 'bluegrey'; | ||||||
|             if ($authority_name !== null) { |  | ||||||
|  |             if (!$is_current_action && $is_voided) { | ||||||
|  |               // The reviewer accepted the revision, but later the author | ||||||
|  |               // used "Request Review" to request an updated review. | ||||||
|  |               $label = pht('Accepted Earlier'); | ||||||
|  |             } else if ($authority_name !== null) { | ||||||
|               $label = pht('Accepted Prior Diff (by %s)', $authority_name); |               $label = pht('Accepted Prior Diff (by %s)', $authority_name); | ||||||
|             } else { |             } else { | ||||||
|               $label = pht('Accepted Prior Diff'); |               $label = pht('Accepted Prior Diff'); | ||||||
|   | |||||||
| @@ -162,6 +162,11 @@ final class DifferentialRevisionAcceptTransaction | |||||||
|           'closed. Only open revisions can be accepted.')); |           'closed. Only open revisions can be accepted.')); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|  |     if ($object->isDraft()) { | ||||||
|  |       throw new Exception( | ||||||
|  |         pht('You can not accept a draft revision.')); | ||||||
|  |     } | ||||||
|  |  | ||||||
|     $config_key = 'differential.allow-self-accept'; |     $config_key = 'differential.allow-self-accept'; | ||||||
|     if (!PhabricatorEnv::getEnvConfig($config_key)) { |     if (!PhabricatorEnv::getEnvConfig($config_key)) { | ||||||
|       if ($this->isViewerRevisionAuthor($object, $viewer)) { |       if ($this->isViewerRevisionAuthor($object, $viewer)) { | ||||||
|   | |||||||
| @@ -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.')); |           'revisions.')); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|  |     // When revisions automatically promote out of "Draft" after builds finish, | ||||||
|  |     // the viewer may be acting as the Harbormaster application. | ||||||
|  |     if (!$viewer->isOmnipotent()) { | ||||||
|       if (!$this->isViewerRevisionAuthor($object, $viewer)) { |       if (!$this->isViewerRevisionAuthor($object, $viewer)) { | ||||||
|         throw new Exception( |         throw new Exception( | ||||||
|           pht( |           pht( | ||||||
| @@ -64,6 +67,7 @@ final class DifferentialRevisionRequestReviewTransaction | |||||||
|             'the author of the revision.')); |             'the author of the revision.')); | ||||||
|       } |       } | ||||||
|     } |     } | ||||||
|  |   } | ||||||
|  |  | ||||||
|   public function getTitle() { |   public function getTitle() { | ||||||
|     return pht( |     return pht( | ||||||
|   | |||||||
| @@ -64,11 +64,6 @@ final class DifferentialRevisionResignTransaction | |||||||
|           'been closed. You can only resign from open revisions.')); |           'been closed. You can only resign from open revisions.')); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     if ($object->isDraft()) { |  | ||||||
|       throw new Exception( |  | ||||||
|         pht('You can not resign from a draft revision.')); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     $resigned = DifferentialReviewerStatus::STATUS_RESIGNED; |     $resigned = DifferentialReviewerStatus::STATUS_RESIGNED; | ||||||
|     if ($this->getViewerReviewerStatus($object, $viewer) == $resigned) { |     if ($this->getViewerReviewerStatus($object, $viewer) == $resigned) { | ||||||
|       throw new Exception( |       throw new Exception( | ||||||
|   | |||||||
| @@ -74,10 +74,10 @@ final class DifferentialRevisionStatusTransaction | |||||||
|     return 'status'; |     return 'status'; | ||||||
|   } |   } | ||||||
|  |  | ||||||
|   public function getFieldValuesForConduit($object, $data) { |   public function getFieldValuesForConduit($xaction, $data) { | ||||||
|     return array( |     return array( | ||||||
|       'old' => $object->getOldValue(), |       'old' => $xaction->getOldValue(), | ||||||
|       'new' => $object->getNewValue(), |       'new' => $xaction->getNewValue(), | ||||||
|     ); |     ); | ||||||
|   } |   } | ||||||
|  |  | ||||||
|   | |||||||
| @@ -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'); |     $commit = $request->getValue('commit'); | ||||||
|     list($err, $stdout) = $repository->execLocalCommand( |     list($err, $stdout) = $repository->execLocalCommand( | ||||||
|       'id --rev %s', |       'id --rev %s', | ||||||
|       $commit); |       hgsprintf('%s', $commit)); | ||||||
|     return !$err; |     return !$err; | ||||||
|   } |   } | ||||||
|  |  | ||||||
|   | |||||||
| @@ -123,21 +123,23 @@ final class DiffusionHistoryQueryConduitAPIMethod | |||||||
|     // branches). |     // branches). | ||||||
|  |  | ||||||
|     if (strlen($path)) { |     if (strlen($path)) { | ||||||
|       $path_arg = csprintf('-- %s', $path); |       $path_arg = csprintf('%s', $path); | ||||||
|       $branch_arg = ''; |       $revset_arg = hgsprintf( | ||||||
|  |         'reverse(ancestors(%s))', | ||||||
|  |         $commit_hash); | ||||||
|     } else { |     } else { | ||||||
|       $path_arg = ''; |       $path_arg = ''; | ||||||
|       // NOTE: --branch used to be called --only-branch; use -b for |       $revset_arg = hgsprintf( | ||||||
|       // compatibility. |         'reverse(ancestors(%s)) and branch(%s)', | ||||||
|       $branch_arg = csprintf('-b %s', $drequest->getBranch()); |         $drequest->getBranch(), | ||||||
|  |         $commit_hash); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     list($stdout) = $repository->execxLocalCommand( |     list($stdout) = $repository->execxLocalCommand( | ||||||
|       'log --debug --template %s --limit %d %C --rev %s %C', |       'log --debug --template %s --limit %d --rev %s -- %C', | ||||||
|       '{node};{parents}\\n', |       '{node};{parents}\\n', | ||||||
|       ($offset + $limit), // No '--skip' in Mercurial. |       ($offset + $limit), // No '--skip' in Mercurial. | ||||||
|       $branch_arg, |       $revset_arg, | ||||||
|       hgsprintf('reverse(ancestors(%s))', $commit_hash), |  | ||||||
|       $path_arg); |       $path_arg); | ||||||
|  |  | ||||||
|     $stdout = DiffusionMercurialCommandEngine::filterMercurialDebugOutput( |     $stdout = DiffusionMercurialCommandEngine::filterMercurialDebugOutput( | ||||||
|   | |||||||
| @@ -77,7 +77,7 @@ final class DiffusionMergedCommitsQueryConduitAPIMethod | |||||||
|     list($parents) = $repository->execxLocalCommand( |     list($parents) = $repository->execxLocalCommand( | ||||||
|       'parents --template=%s --rev %s', |       'parents --template=%s --rev %s', | ||||||
|       '{node}\\n', |       '{node}\\n', | ||||||
|       $commit); |       hgsprintf('%s', $commit)); | ||||||
|     $parents = explode("\n", trim($parents)); |     $parents = explode("\n", trim($parents)); | ||||||
|  |  | ||||||
|     if (count($parents) < 2) { |     if (count($parents) < 2) { | ||||||
|   | |||||||
| @@ -97,7 +97,7 @@ final class DiffusionSearchQueryConduitAPIMethod | |||||||
|  |  | ||||||
|     $results = array(); |     $results = array(); | ||||||
|     $future = $repository->getLocalCommandFuture( |     $future = $repository->getLocalCommandFuture( | ||||||
|       'grep --rev %s --print0 --line-number %s %s', |       'grep --rev %s --print0 --line-number -- %s %s', | ||||||
|       hgsprintf('ancestors(%s)', $drequest->getStableCommit()), |       hgsprintf('ancestors(%s)', $drequest->getStableCommit()), | ||||||
|       $grep, |       $grep, | ||||||
|       $path); |       $path); | ||||||
|   | |||||||
| @@ -969,6 +969,24 @@ final class DiffusionBrowseController extends DiffusionController { | |||||||
|  |  | ||||||
|     $handles = $viewer->loadHandles($phids); |     $handles = $viewer->loadHandles($phids); | ||||||
|  |  | ||||||
|  |     $author_phids = array(); | ||||||
|  |     $author_map = array(); | ||||||
|  |     foreach ($blame_commits as $commit) { | ||||||
|  |       $commit_identifier = $commit->getCommitIdentifier(); | ||||||
|  |  | ||||||
|  |       $author_phid = ''; | ||||||
|  |       if (isset($revision_map[$commit_identifier])) { | ||||||
|  |         $revision_id = $revision_map[$commit_identifier]; | ||||||
|  |         $revision = $revisions[$revision_id]; | ||||||
|  |         $author_phid = $revision->getAuthorPHID(); | ||||||
|  |       } else { | ||||||
|  |         $author_phid = $commit->getAuthorPHID(); | ||||||
|  |       } | ||||||
|  |  | ||||||
|  |       $author_map[$commit_identifier] = $author_phid; | ||||||
|  |       $author_phids[$author_phid] = $author_phid; | ||||||
|  |     } | ||||||
|  |  | ||||||
|     $colors = array(); |     $colors = array(); | ||||||
|     if ($blame_commits) { |     if ($blame_commits) { | ||||||
|       $epochs = array(); |       $epochs = array(); | ||||||
| @@ -1113,6 +1131,7 @@ final class DiffusionBrowseController extends DiffusionController { | |||||||
|     // blame outputs. |     // blame outputs. | ||||||
|     $commit_links = $this->renderCommitLinks($blame_commits, $handles); |     $commit_links = $this->renderCommitLinks($blame_commits, $handles); | ||||||
|     $revision_links = $this->renderRevisionLinks($revisions, $handles); |     $revision_links = $this->renderRevisionLinks($revisions, $handles); | ||||||
|  |     $author_links = $this->renderAuthorLinks($author_map, $handles); | ||||||
|  |  | ||||||
|     if ($this->coverage) { |     if ($this->coverage) { | ||||||
|       require_celerity_resource('differential-changeset-view-css'); |       require_celerity_resource('differential-changeset-view-css'); | ||||||
| @@ -1127,6 +1146,10 @@ final class DiffusionBrowseController extends DiffusionController { | |||||||
|         )); |         )); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|  |     $skip_text = pht('Skip Past This Commit'); | ||||||
|  |     $skip_icon = id(new PHUIIconView()) | ||||||
|  |       ->setIcon('fa-caret-square-o-left'); | ||||||
|  |  | ||||||
|     foreach ($display as $line_index => $line) { |     foreach ($display as $line_index => $line) { | ||||||
|       $row = array(); |       $row = array(); | ||||||
|  |  | ||||||
| @@ -1141,15 +1164,15 @@ final class DiffusionBrowseController extends DiffusionController { | |||||||
|  |  | ||||||
|       $revision_link = null; |       $revision_link = null; | ||||||
|       $commit_link = null; |       $commit_link = null; | ||||||
|  |       $author_link = null; | ||||||
|       $before_link = null; |       $before_link = null; | ||||||
|       $commit_date = null; |  | ||||||
|  |  | ||||||
|       $style = 'border-right: 3px solid '.$line['color'].';'; |       $style = 'background: '.$line['color'].';'; | ||||||
|  |  | ||||||
|       if ($identifier && !$line['duplicate']) { |       if ($identifier && !$line['duplicate']) { | ||||||
|         if (isset($commit_links[$identifier])) { |         if (isset($commit_links[$identifier])) { | ||||||
|           $commit_link = $commit_links[$identifier]['link']; |           $commit_link = $commit_links[$identifier]; | ||||||
|           $commit_date = $commit_links[$identifier]['date']; |           $author_link = $author_links[$author_map[$identifier]]; | ||||||
|         } |         } | ||||||
|  |  | ||||||
|         if (isset($revision_map[$identifier])) { |         if (isset($revision_map[$identifier])) { | ||||||
| @@ -1160,10 +1183,6 @@ final class DiffusionBrowseController extends DiffusionController { | |||||||
|         } |         } | ||||||
|  |  | ||||||
|         $skip_href = $line_href.'?before='.$identifier.'&view=blame'; |         $skip_href = $line_href.'?before='.$identifier.'&view=blame'; | ||||||
|         $skip_text = pht('Skip Past This Commit'); |  | ||||||
|         $icon = id(new PHUIIconView()) |  | ||||||
|           ->setIcon('fa-caret-square-o-left'); |  | ||||||
|  |  | ||||||
|         $before_link = javelin_tag( |         $before_link = javelin_tag( | ||||||
|           'a', |           'a', | ||||||
|           array( |           array( | ||||||
| @@ -1175,7 +1194,7 @@ final class DiffusionBrowseController extends DiffusionController { | |||||||
|               'size'    => 300, |               'size'    => 300, | ||||||
|             ), |             ), | ||||||
|           ), |           ), | ||||||
|           $icon); |           $skip_icon); | ||||||
|       } |       } | ||||||
|  |  | ||||||
|       if ($show_blame) { |       if ($show_blame) { | ||||||
| @@ -1186,34 +1205,27 @@ final class DiffusionBrowseController extends DiffusionController { | |||||||
|           ), |           ), | ||||||
|           $before_link); |           $before_link); | ||||||
|  |  | ||||||
|         $row[] = phutil_tag( |         $object_links = array(); | ||||||
|           'th', |         $object_links[] = $author_link; | ||||||
|           array( |         $object_links[] = $commit_link; | ||||||
|             'class' => 'diffusion-rev-link', |         if ($revision_link) { | ||||||
|           ), |           $object_links[] = phutil_tag('span', array(), '/'); | ||||||
|           $commit_link); |           $object_links[] = $revision_link; | ||||||
|  |  | ||||||
|         if ($revision_map) { |  | ||||||
|           $row[] = phutil_tag( |  | ||||||
|             'th', |  | ||||||
|             array( |  | ||||||
|               'class' => 'diffusion-blame-revision', |  | ||||||
|             ), |  | ||||||
|             $revision_link); |  | ||||||
|         } |         } | ||||||
|  |  | ||||||
|         $row[] = phutil_tag( |         $row[] = phutil_tag( | ||||||
|           'th', |           'th', | ||||||
|           array( |           array( | ||||||
|             'class' => 'diffusion-blame-date', |             'class' => 'diffusion-rev-link', | ||||||
|           ), |           ), | ||||||
|           $commit_date); |           $object_links); | ||||||
|       } |       } | ||||||
|  |  | ||||||
|       $line_link = phutil_tag( |       $line_link = phutil_tag( | ||||||
|         'a', |         'a', | ||||||
|         array( |         array( | ||||||
|           'href' => $line_href, |           'href' => $line_href, | ||||||
|  |           'style' => $style, | ||||||
|         ), |         ), | ||||||
|         $line_number); |         $line_number); | ||||||
|  |  | ||||||
| @@ -1536,6 +1548,33 @@ final class DiffusionBrowseController extends DiffusionController { | |||||||
|     return head($parents); |     return head($parents); | ||||||
|   } |   } | ||||||
|  |  | ||||||
|  |   private function renderRevisionTooltip( | ||||||
|  |     DifferentialRevision $revision, | ||||||
|  |     $handles) { | ||||||
|  |     $viewer = $this->getRequest()->getUser(); | ||||||
|  |  | ||||||
|  |     $date = phabricator_date($revision->getDateModified(), $viewer); | ||||||
|  |     $id = $revision->getID(); | ||||||
|  |     $title = $revision->getTitle(); | ||||||
|  |     $header = "D{$id} {$title}"; | ||||||
|  |  | ||||||
|  |     $author = $handles[$revision->getAuthorPHID()]->getName(); | ||||||
|  |  | ||||||
|  |     return "{$header}\n{$date} \xC2\xB7 {$author}"; | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   private function renderCommitTooltip( | ||||||
|  |     PhabricatorRepositoryCommit $commit, | ||||||
|  |     $author) { | ||||||
|  |  | ||||||
|  |     $viewer = $this->getRequest()->getUser(); | ||||||
|  |  | ||||||
|  |     $date = phabricator_date($commit->getEpoch(), $viewer); | ||||||
|  |     $summary = trim($commit->getSummary()); | ||||||
|  |  | ||||||
|  |     return "{$summary}\n{$date} \xC2\xB7 {$author}"; | ||||||
|  |   } | ||||||
|  |  | ||||||
|   protected function markupText($text) { |   protected function markupText($text) { | ||||||
|     $engine = PhabricatorMarkupEngine::newDiffusionMarkupEngine(); |     $engine = PhabricatorMarkupEngine::newDiffusionMarkupEngine(); | ||||||
|     $engine->setConfig('viewer', $this->getRequest()->getUser()); |     $engine->setConfig('viewer', $this->getRequest()->getUser()); | ||||||
| @@ -1743,6 +1782,9 @@ final class DiffusionBrowseController extends DiffusionController { | |||||||
|         ->setViewer($viewer) |         ->setViewer($viewer) | ||||||
|         ->withRepository($repository) |         ->withRepository($repository) | ||||||
|         ->withIdentifiers($identifiers) |         ->withIdentifiers($identifiers) | ||||||
|  |         // TODO: We only fetch this to improve author display behavior, but | ||||||
|  |         // shouldn't really need to? | ||||||
|  |         ->needCommitData(true) | ||||||
|         ->execute(); |         ->execute(); | ||||||
|       $commits = mpull($commits, null, 'getCommitIdentifier'); |       $commits = mpull($commits, null, 'getCommitIdentifier'); | ||||||
|     } else { |     } else { | ||||||
| @@ -1752,29 +1794,59 @@ final class DiffusionBrowseController extends DiffusionController { | |||||||
|     return array($identifiers, $commits); |     return array($identifiers, $commits); | ||||||
|   } |   } | ||||||
|  |  | ||||||
|  |   private function renderAuthorLinks(array $authors, $handles) { | ||||||
|  |     $links = array(); | ||||||
|  |  | ||||||
|  |     foreach ($authors as $phid) { | ||||||
|  |       if (!strlen($phid)) { | ||||||
|  |         // This means we couldn't identify an author for the commit or the | ||||||
|  |         // revision. We just render a blank for alignment. | ||||||
|  |         $style = null; | ||||||
|  |         $href = null; | ||||||
|  |       } else { | ||||||
|  |         $src = $handles[$phid]->getImageURI(); | ||||||
|  |         $style = 'background-image: url('.$src.');'; | ||||||
|  |         $href = $handles[$phid]->getURI(); | ||||||
|  |       } | ||||||
|  |  | ||||||
|  |       $links[$phid] = javelin_tag( | ||||||
|  |         $href ? 'a' : 'span', | ||||||
|  |         array( | ||||||
|  |           'class' => 'diffusion-author-link', | ||||||
|  |           'style' => $style, | ||||||
|  |           'href' => $href, | ||||||
|  |           'sigil' => 'has-tooltip', | ||||||
|  |           'meta' => array( | ||||||
|  |             'tip' => $handles[$phid]->getName(), | ||||||
|  |             'align' => 'E', | ||||||
|  |           ), | ||||||
|  |         )); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     return $links; | ||||||
|  |   } | ||||||
|  |  | ||||||
|   private function renderCommitLinks(array $commits, $handles) { |   private function renderCommitLinks(array $commits, $handles) { | ||||||
|     $links = array(); |     $links = array(); | ||||||
|     $viewer = $this->getViewer(); |  | ||||||
|     foreach ($commits as $identifier => $commit) { |     foreach ($commits as $identifier => $commit) { | ||||||
|       $date = phabricator_date($commit->getEpoch(), $viewer); |       $tooltip = $this->renderCommitTooltip( | ||||||
|       $summary = trim($commit->getSummary()); |         $commit, | ||||||
|  |         $commit->renderAuthorShortName($handles)); | ||||||
|  |  | ||||||
|       $commit_link = phutil_tag( |       $commit_link = javelin_tag( | ||||||
|         'a', |         'a', | ||||||
|         array( |         array( | ||||||
|           'href' => $commit->getURI(), |           'href' => $commit->getURI(), | ||||||
|  |           'sigil' => 'has-tooltip', | ||||||
|  |           'meta'  => array( | ||||||
|  |             'tip'   => $tooltip, | ||||||
|  |             'align' => 'E', | ||||||
|  |             'size'  => 600, | ||||||
|           ), |           ), | ||||||
|         $summary); |  | ||||||
|  |  | ||||||
|       $commit_date = phutil_tag( |  | ||||||
|         'a', |  | ||||||
|         array( |  | ||||||
|           'href' => $commit->getURI(), |  | ||||||
|         ), |         ), | ||||||
|         $date); |         $commit->getLocalName()); | ||||||
|  |  | ||||||
|       $links[$identifier]['link'] = $commit_link; |       $links[$identifier] = $commit_link; | ||||||
|       $links[$identifier]['date'] = $commit_date; |  | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     return $links; |     return $links; | ||||||
| @@ -1785,10 +1857,19 @@ final class DiffusionBrowseController extends DiffusionController { | |||||||
|  |  | ||||||
|     foreach ($revisions as $revision) { |     foreach ($revisions as $revision) { | ||||||
|       $revision_id = $revision->getID(); |       $revision_id = $revision->getID(); | ||||||
|       $revision_link = phutil_tag( |  | ||||||
|  |       $tooltip = $this->renderRevisionTooltip($revision, $handles); | ||||||
|  |  | ||||||
|  |       $revision_link = javelin_tag( | ||||||
|         'a', |         'a', | ||||||
|         array( |         array( | ||||||
|           'href' => '/'.$revision->getMonogram(), |           'href' => '/'.$revision->getMonogram(), | ||||||
|  |           'sigil' => 'has-tooltip', | ||||||
|  |           'meta'  => array( | ||||||
|  |             'tip'   => $tooltip, | ||||||
|  |             'align' => 'E', | ||||||
|  |             'size'  => 600, | ||||||
|  |           ), | ||||||
|         ), |         ), | ||||||
|         $revision->getMonogram()); |         $revision->getMonogram()); | ||||||
|  |  | ||||||
|   | |||||||
| @@ -39,20 +39,23 @@ final class DiffusionCommitController extends DiffusionController { | |||||||
|       return $this->buildRawDiffResponse($drequest); |       return $this->buildRawDiffResponse($drequest); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     $commit = id(new DiffusionCommitQuery()) |     $commits = id(new DiffusionCommitQuery()) | ||||||
|       ->setViewer($viewer) |       ->setViewer($viewer) | ||||||
|       ->withRepository($repository) |       ->withRepository($repository) | ||||||
|       ->withIdentifiers(array($commit_identifier)) |       ->withIdentifiers(array($commit_identifier)) | ||||||
|       ->needCommitData(true) |       ->needCommitData(true) | ||||||
|       ->needAuditRequests(true) |       ->needAuditRequests(true) | ||||||
|       ->executeOne(); |       ->setLimit(100) | ||||||
|  |       ->execute(); | ||||||
|  |  | ||||||
|  |     $multiple_results = count($commits) > 1; | ||||||
|  |  | ||||||
|     $crumbs = $this->buildCrumbs(array( |     $crumbs = $this->buildCrumbs(array( | ||||||
|       'commit' => true, |       'commit' => !$multiple_results, | ||||||
|     )); |     )); | ||||||
|     $crumbs->setBorder(true); |     $crumbs->setBorder(true); | ||||||
|  |  | ||||||
|     if (!$commit) { |     if (!$commits) { | ||||||
|       if (!$this->getCommitExists()) { |       if (!$this->getCommitExists()) { | ||||||
|         return new Aphront404Response(); |         return new Aphront404Response(); | ||||||
|       } |       } | ||||||
| @@ -70,7 +73,40 @@ final class DiffusionCommitController extends DiffusionController { | |||||||
|         ->setTitle($title) |         ->setTitle($title) | ||||||
|         ->setCrumbs($crumbs) |         ->setCrumbs($crumbs) | ||||||
|         ->appendChild($error); |         ->appendChild($error); | ||||||
|  |     } else if ($multiple_results) { | ||||||
|  |  | ||||||
|  |       $warning_message = | ||||||
|  |         pht( | ||||||
|  |           'The identifier %s is ambiguous and matches more than one commit.', | ||||||
|  |           phutil_tag( | ||||||
|  |             'strong', | ||||||
|  |             array(), | ||||||
|  |             $commit_identifier)); | ||||||
|  |  | ||||||
|  |       $error = id(new PHUIInfoView()) | ||||||
|  |         ->setTitle(pht('Ambiguous Commit')) | ||||||
|  |         ->setSeverity(PHUIInfoView::SEVERITY_WARNING) | ||||||
|  |         ->appendChild($warning_message); | ||||||
|  |  | ||||||
|  |       $list = id(new DiffusionCommitListView()) | ||||||
|  |         ->setViewer($viewer) | ||||||
|  |         ->setCommits($commits) | ||||||
|  |         ->setNoDataString(pht('No recent commits.')); | ||||||
|  |  | ||||||
|  |       $crumbs->addTextCrumb(pht('Ambiguous Commit')); | ||||||
|  |  | ||||||
|  |       $matched_commits = id(new PHUITwoColumnView()) | ||||||
|  |         ->setFooter(array( | ||||||
|  |           $error, | ||||||
|  |           $list, | ||||||
|  |         )); | ||||||
|  |  | ||||||
|  |       return $this->newPage() | ||||||
|  |         ->setTitle(pht('Ambiguous Commit')) | ||||||
|  |         ->setCrumbs($crumbs) | ||||||
|  |         ->appendChild($matched_commits); | ||||||
|  |     } else { | ||||||
|  |       $commit = head($commits); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     $audit_requests = $commit->getAudits(); |     $audit_requests = $commit->getAudits(); | ||||||
| @@ -438,7 +474,8 @@ final class DiffusionCommitController extends DiffusionController { | |||||||
|     $repository = $drequest->getRepository(); |     $repository = $drequest->getRepository(); | ||||||
|  |  | ||||||
|     $view = id(new PHUIPropertyListView()) |     $view = id(new PHUIPropertyListView()) | ||||||
|       ->setUser($this->getRequest()->getUser()); |       ->setUser($this->getRequest()->getUser()) | ||||||
|  |       ->setObject($commit); | ||||||
|  |  | ||||||
|     $edge_query = id(new PhabricatorEdgeQuery()) |     $edge_query = id(new PhabricatorEdgeQuery()) | ||||||
|       ->withSourcePHIDs(array($commit_phid)) |       ->withSourcePHIDs(array($commit_phid)) | ||||||
|   | |||||||
| @@ -46,8 +46,20 @@ final class DiffusionRepositoryController extends DiffusionController { | |||||||
|         ->withRepositoryPHIDs(array($repository->getPHID())) |         ->withRepositoryPHIDs(array($repository->getPHID())) | ||||||
|         ->withRefTypes(array(PhabricatorRepositoryRefCursor::TYPE_BRANCH)) |         ->withRefTypes(array(PhabricatorRepositoryRefCursor::TYPE_BRANCH)) | ||||||
|         ->withRefNames(array($drequest->getBranch())) |         ->withRefNames(array($drequest->getBranch())) | ||||||
|  |         ->needPositions(true) | ||||||
|         ->execute(); |         ->execute(); | ||||||
|       if ($ref_cursors) { |  | ||||||
|  |       // It's possible that this branch previously existed, but has been | ||||||
|  |       // deleted. Make sure we have valid cursor positions, not just cursors. | ||||||
|  |       $any_positions = false; | ||||||
|  |       foreach ($ref_cursors as $ref_cursor) { | ||||||
|  |         if ($ref_cursor->getPositions()) { | ||||||
|  |           $any_positions = true; | ||||||
|  |           break; | ||||||
|  |         } | ||||||
|  |       } | ||||||
|  |  | ||||||
|  |       if ($any_positions) { | ||||||
|         // This is a valid branch, so we necessarily have some content. |         // This is a valid branch, so we necessarily have some content. | ||||||
|         $page_has_content = true; |         $page_has_content = true; | ||||||
|       } else { |       } else { | ||||||
|   | |||||||
| @@ -347,11 +347,11 @@ final class DiffusionURIEditor | |||||||
|                 continue; |                 continue; | ||||||
|               } |               } | ||||||
|  |  | ||||||
|               $io_type = $uri->getIoType(); |               $io_type = $uri->getEffectiveIOType(); | ||||||
|  |  | ||||||
|               if ($io_type == PhabricatorRepositoryURI::IO_READWRITE) { |               if ($io_type == PhabricatorRepositoryURI::IO_READWRITE) { | ||||||
|                 if ($no_readwrite) { |                 if ($no_readwrite) { | ||||||
|                   $readwite_conflict = $uri; |                   $readwrite_conflict = $uri; | ||||||
|                   break; |                   break; | ||||||
|                 } |                 } | ||||||
|               } |               } | ||||||
|   | |||||||
| @@ -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) { |   protected function newFormattedCommand($pattern, array $argv) { | ||||||
|     $args = array(); |     $args = array(); | ||||||
|  |  | ||||||
|  |     // Crudely blacklist commands which look like they may contain command | ||||||
|  |     // injection via "--config" or "--debugger". See T13012. To do this, we | ||||||
|  |     // print the whole command, parse it using shell rules, then examine each | ||||||
|  |     // argument to see if it looks like "--config" or "--debugger". | ||||||
|  |  | ||||||
|  |     $test_command = call_user_func_array( | ||||||
|  |       'csprintf', | ||||||
|  |       array_merge(array($pattern), $argv)); | ||||||
|  |     $test_args = id(new PhutilShellLexer()) | ||||||
|  |       ->splitArguments($test_command); | ||||||
|  |  | ||||||
|  |     foreach ($test_args as $test_arg) { | ||||||
|  |       if (preg_match('/^--(config|debugger)/i', $test_arg)) { | ||||||
|  |         throw new DiffusionMercurialFlagInjectionException( | ||||||
|  |           pht( | ||||||
|  |             'Mercurial command appears to contain unsafe injected "--config" '. | ||||||
|  |             'or "--debugger": %s', | ||||||
|  |             $test_command)); | ||||||
|  |       } | ||||||
|  |     } | ||||||
|  |  | ||||||
|     // NOTE: Here, and in Git and Subversion, we override the SSH command even |     // NOTE: Here, and in Git and Subversion, we override the SSH command even | ||||||
|     // if the repository does not use an SSH remote, since our SSH wrapper |     // if the repository does not use an SSH remote, since our SSH wrapper | ||||||
|     // defuses an attack against older versions of Mercurial, Git and |     // defuses an attack against older versions of Mercurial, Git and | ||||||
|   | |||||||
| @@ -123,6 +123,62 @@ final class DiffusionCommandEngineTestCase extends PhabricatorTestCase { | |||||||
|         'argv' => 'xyz', |         'argv' => 'xyz', | ||||||
|         'protocol' => 'https', |         'protocol' => 'https', | ||||||
|       )); |       )); | ||||||
|  |  | ||||||
|  |     // Test that filtering defenses for "--config" and "--debugger" flag | ||||||
|  |     // injections in Mercurial are functional. See T13012. | ||||||
|  |  | ||||||
|  |     $caught = null; | ||||||
|  |     try { | ||||||
|  |       $this->assertCommandEngineFormat( | ||||||
|  |         '', | ||||||
|  |         array(), | ||||||
|  |         array( | ||||||
|  |           'vcs' => $type_hg, | ||||||
|  |           'argv' => '--debugger', | ||||||
|  |         )); | ||||||
|  |     } catch (DiffusionMercurialFlagInjectionException $ex) { | ||||||
|  |       $caught = $ex; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     $this->assertTrue( | ||||||
|  |       ($caught instanceof DiffusionMercurialFlagInjectionException), | ||||||
|  |       pht('Expected "--debugger" injection in Mercurial to throw.')); | ||||||
|  |  | ||||||
|  |  | ||||||
|  |     $caught = null; | ||||||
|  |     try { | ||||||
|  |       $this->assertCommandEngineFormat( | ||||||
|  |         '', | ||||||
|  |         array(), | ||||||
|  |         array( | ||||||
|  |           'vcs' => $type_hg, | ||||||
|  |           'argv' => '--config=x', | ||||||
|  |         )); | ||||||
|  |     } catch (DiffusionMercurialFlagInjectionException $ex) { | ||||||
|  |       $caught = $ex; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     $this->assertTrue( | ||||||
|  |       ($caught instanceof DiffusionMercurialFlagInjectionException), | ||||||
|  |       pht('Expected "--config" injection in Mercurial to throw.')); | ||||||
|  |  | ||||||
|  |     $caught = null; | ||||||
|  |     try { | ||||||
|  |       $this->assertCommandEngineFormat( | ||||||
|  |         '', | ||||||
|  |         array(), | ||||||
|  |         array( | ||||||
|  |           'vcs' => $type_hg, | ||||||
|  |           'argv' => (string)csprintf('%s', '--config=x'), | ||||||
|  |         )); | ||||||
|  |     } catch (DiffusionMercurialFlagInjectionException $ex) { | ||||||
|  |       $caught = $ex; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     $this->assertTrue( | ||||||
|  |       ($caught instanceof DiffusionMercurialFlagInjectionException), | ||||||
|  |       pht('Expected quoted "--config" injection in Mercurial to throw.')); | ||||||
|  |  | ||||||
|   } |   } | ||||||
|  |  | ||||||
|   private function assertCommandEngineFormat( |   private function assertCommandEngineFormat( | ||||||
|   | |||||||
| @@ -177,7 +177,63 @@ final class DiffusionCommitQuery | |||||||
|   } |   } | ||||||
|  |  | ||||||
|   protected function loadPage() { |   protected function loadPage() { | ||||||
|     return $this->loadStandardPage($this->newResultObject()); |     $table = $this->newResultObject(); | ||||||
|  |     $conn = $table->establishConnection('r'); | ||||||
|  |  | ||||||
|  |     $subqueries = array(); | ||||||
|  |     if ($this->responsiblePHIDs) { | ||||||
|  |       $base_authors = $this->authorPHIDs; | ||||||
|  |       $base_auditors = $this->auditorPHIDs; | ||||||
|  |  | ||||||
|  |       $responsible_phids = $this->responsiblePHIDs; | ||||||
|  |       if ($base_authors) { | ||||||
|  |         $all_authors = array_merge($base_authors, $responsible_phids); | ||||||
|  |       } else { | ||||||
|  |         $all_authors = $responsible_phids; | ||||||
|  |       } | ||||||
|  |  | ||||||
|  |       if ($base_auditors) { | ||||||
|  |         $all_auditors = array_merge($base_auditors, $responsible_phids); | ||||||
|  |       } else { | ||||||
|  |         $all_auditors = $responsible_phids; | ||||||
|  |       } | ||||||
|  |  | ||||||
|  |       $this->authorPHIDs = $all_authors; | ||||||
|  |       $this->auditorPHIDs = $base_auditors; | ||||||
|  |       $subqueries[] = $this->buildStandardPageQuery( | ||||||
|  |         $conn, | ||||||
|  |         $table->getTableName()); | ||||||
|  |  | ||||||
|  |       $this->authorPHIDs = $base_authors; | ||||||
|  |       $this->auditorPHIDs = $all_auditors; | ||||||
|  |       $subqueries[] = $this->buildStandardPageQuery( | ||||||
|  |         $conn, | ||||||
|  |         $table->getTableName()); | ||||||
|  |     } else { | ||||||
|  |       $subqueries[] = $this->buildStandardPageQuery( | ||||||
|  |         $conn, | ||||||
|  |         $table->getTableName()); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     if (count($subqueries) > 1) { | ||||||
|  |       foreach ($subqueries as $key => $subquery) { | ||||||
|  |         $subqueries[$key] = '('.$subquery.')'; | ||||||
|  |       } | ||||||
|  |  | ||||||
|  |       $query = qsprintf( | ||||||
|  |         $conn, | ||||||
|  |         '%Q %Q %Q', | ||||||
|  |         implode(' UNION DISTINCT ', $subqueries), | ||||||
|  |         $this->buildOrderClause($conn, true), | ||||||
|  |         $this->buildLimitClause($conn)); | ||||||
|  |     } else { | ||||||
|  |       $query = head($subqueries); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     $rows = queryfx_all($conn, '%Q', $query); | ||||||
|  |     $rows = $this->didLoadRawRows($rows); | ||||||
|  |  | ||||||
|  |     return $table->loadAllFromArray($rows); | ||||||
|   } |   } | ||||||
|  |  | ||||||
|   protected function willFilterPage(array $commits) { |   protected function willFilterPage(array $commits) { | ||||||
| @@ -487,18 +543,10 @@ final class DiffusionCommitQuery | |||||||
|         $this->auditorPHIDs); |         $this->auditorPHIDs); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     if ($this->responsiblePHIDs !== null) { |  | ||||||
|       $where[] = qsprintf( |  | ||||||
|         $conn, |  | ||||||
|         '(audit.auditorPHID IN (%Ls) OR commit.authorPHID IN (%Ls))', |  | ||||||
|         $this->responsiblePHIDs, |  | ||||||
|         $this->responsiblePHIDs); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     if ($this->statuses !== null) { |     if ($this->statuses !== null) { | ||||||
|       $where[] = qsprintf( |       $where[] = qsprintf( | ||||||
|         $conn, |         $conn, | ||||||
|         'commit.auditStatus IN (%Ls)', |         'commit.auditStatus IN (%Ld)', | ||||||
|         $this->statuses); |         $this->statuses); | ||||||
|     } |     } | ||||||
|  |  | ||||||
| @@ -541,10 +589,6 @@ final class DiffusionCommitQuery | |||||||
|     return ($this->auditIDs || $this->auditorPHIDs); |     return ($this->auditIDs || $this->auditorPHIDs); | ||||||
|   } |   } | ||||||
|  |  | ||||||
|   private function shouldJoinAudit() { |  | ||||||
|     return (bool)$this->responsiblePHIDs; |  | ||||||
|   } |  | ||||||
|  |  | ||||||
|   private function shouldJoinOwners() { |   private function shouldJoinOwners() { | ||||||
|     return (bool)$this->packagePHIDs; |     return (bool)$this->packagePHIDs; | ||||||
|   } |   } | ||||||
| @@ -560,13 +604,6 @@ final class DiffusionCommitQuery | |||||||
|         $audit_request->getTableName()); |         $audit_request->getTableName()); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     if ($this->shouldJoinAudit()) { |  | ||||||
|       $join[] = qsprintf( |  | ||||||
|         $conn, |  | ||||||
|         'LEFT JOIN %T audit ON commit.phid = audit.commitPHID', |  | ||||||
|         $audit_request->getTableName()); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     if ($this->shouldJoinOwners()) { |     if ($this->shouldJoinOwners()) { | ||||||
|       $join[] = qsprintf( |       $join[] = qsprintf( | ||||||
|         $conn, |         $conn, | ||||||
| @@ -584,10 +621,6 @@ final class DiffusionCommitQuery | |||||||
|       return true; |       return true; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     if ($this->shouldJoinAudit()) { |  | ||||||
|       return true; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     if ($this->shouldJoinOwners()) { |     if ($this->shouldJoinOwners()) { | ||||||
|       return true; |       return true; | ||||||
|     } |     } | ||||||
|   | |||||||
| @@ -256,6 +256,66 @@ final class DiffusionLowLevelResolveRefsQuery | |||||||
|       return $results; |       return $results; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|  |     // If some of the refs look like hashes, try to bulk resolve them. This | ||||||
|  |     // workflow happens via RefEngine and bulk resolution is dramatically | ||||||
|  |     // faster than individual resolution. See PHI158. | ||||||
|  |  | ||||||
|  |     $hashlike = array(); | ||||||
|  |     foreach ($unresolved as $key => $ref) { | ||||||
|  |       if (preg_match('/^[a-f0-9]{40}\z/', $ref)) { | ||||||
|  |         $hashlike[$key] = $ref; | ||||||
|  |       } | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     if (count($hashlike) > 1) { | ||||||
|  |       $hashlike_map = array(); | ||||||
|  |  | ||||||
|  |       $hashlike_groups = array_chunk($hashlike, 64, true); | ||||||
|  |       foreach ($hashlike_groups as $hashlike_group) { | ||||||
|  |         $hashlike_arg = array(); | ||||||
|  |         foreach ($hashlike_group as $hashlike_ref) { | ||||||
|  |           $hashlike_arg[] = hgsprintf('%s', $hashlike_ref); | ||||||
|  |         } | ||||||
|  |         $hashlike_arg = '('.implode(' or ', $hashlike_arg).')'; | ||||||
|  |  | ||||||
|  |         list($err, $refs) = $repository->execLocalCommand( | ||||||
|  |           'log --template=%s --rev %s', | ||||||
|  |           '{node}\n', | ||||||
|  |           $hashlike_arg); | ||||||
|  |         if ($err) { | ||||||
|  |           // NOTE: If any ref fails to resolve, Mercurial will exit with an | ||||||
|  |           // error. We just give up on the whole group and resolve it | ||||||
|  |           // individually below. In theory, we could split it into subgroups | ||||||
|  |           // but the pathway where this bulk resolution matters rarely tries | ||||||
|  |           // to resolve missing refs (see PHI158). | ||||||
|  |           continue; | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         $refs = phutil_split_lines($refs, false); | ||||||
|  |  | ||||||
|  |         foreach ($refs as $ref) { | ||||||
|  |           $hashlike_map[$ref] = true; | ||||||
|  |         } | ||||||
|  |       } | ||||||
|  |  | ||||||
|  |       foreach ($unresolved as $key => $ref) { | ||||||
|  |         if (!isset($hashlike_map[$ref])) { | ||||||
|  |           continue; | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         $results[$ref][] = array( | ||||||
|  |           'type' => 'commit', | ||||||
|  |           'identifier' => $ref, | ||||||
|  |         ); | ||||||
|  |  | ||||||
|  |         unset($unresolved[$key]); | ||||||
|  |       } | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     if (!$unresolved) { | ||||||
|  |       return $results; | ||||||
|  |     } | ||||||
|  |  | ||||||
|     // If we still have unresolved refs (which might be things like "tip"), |     // If we still have unresolved refs (which might be things like "tip"), | ||||||
|     // try to resolve them individually. |     // try to resolve them individually. | ||||||
|  |  | ||||||
|   | |||||||
| @@ -16,14 +16,14 @@ final class DiffusionMercurialRawDiffQuery extends DiffusionRawDiffQuery { | |||||||
|       // If `$commit` has no parents (usually because it's the first commit |       // If `$commit` has no parents (usually because it's the first commit | ||||||
|       // in the repository), we want to diff against `null`. This revset will |       // in the repository), we want to diff against `null`. This revset will | ||||||
|       // do that for us automatically. |       // do that for us automatically. | ||||||
|       $against = '('.$commit.'^ or null)'; |       $against = hgsprintf('(%s^ or null)', $commit); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     $future = $repository->getLocalCommandFuture( |     $future = $repository->getLocalCommandFuture( | ||||||
|       'diff -U %d --git --rev %s --rev %s -- %s', |       'diff -U %d --git --rev %s --rev %s -- %s', | ||||||
|       $this->getLinesOfContext(), |       $this->getLinesOfContext(), | ||||||
|       $against, |       $against, | ||||||
|       $commit, |       hgsprintf('%s', $commit), | ||||||
|       $path); |       $path); | ||||||
|  |  | ||||||
|     return $future; |     return $future; | ||||||
|   | |||||||
| @@ -96,9 +96,10 @@ final class DiffusionPatternSearchView extends DiffusionView { | |||||||
|  |  | ||||||
|     $path_title = Filesystem::readablePath($this->path, $drequest->getPath()); |     $path_title = Filesystem::readablePath($this->path, $drequest->getPath()); | ||||||
|  |  | ||||||
|     $href = $drequest->generateURI(array( |     $href = $drequest->generateURI( | ||||||
|  |       array( | ||||||
|         'action' => 'browse', |         'action' => 'browse', | ||||||
|       'path' => $path_title, |         'path' => $this->path, | ||||||
|       )); |       )); | ||||||
|  |  | ||||||
|     $title = phutil_tag('a', array('href' => $href), $path_title); |     $title = phutil_tag('a', array('href' => $href), $path_title); | ||||||
|   | |||||||
| @@ -83,6 +83,9 @@ final class DrydockLease extends DrydockDAO | |||||||
|         'key_resource' => array( |         'key_resource' => array( | ||||||
|           'columns' => array('resourcePHID', 'status'), |           'columns' => array('resourcePHID', 'status'), | ||||||
|         ), |         ), | ||||||
|  |         'key_status' => array( | ||||||
|  |           'columns' => array('status'), | ||||||
|  |         ), | ||||||
|       ), |       ), | ||||||
|     ) + parent::getConfiguration(); |     ) + parent::getConfiguration(); | ||||||
|   } |   } | ||||||
|   | |||||||
| @@ -58,6 +58,14 @@ final class PhabricatorFilesComposeAvatarBuiltinFile | |||||||
|   } |   } | ||||||
|  |  | ||||||
|   private function composeImage($color, $image, $border) { |   private function composeImage($color, $image, $border) { | ||||||
|  |     // If we don't have the GD extension installed, just return a static | ||||||
|  |     // default profile image rather than trying to compose a dynamic one. | ||||||
|  |     if (!function_exists('imagecreatefromstring')) { | ||||||
|  |       $root = dirname(phutil_get_library_root('phabricator')); | ||||||
|  |       $default_path = $root.'/resources/builtin/profile.png'; | ||||||
|  |       return Filesystem::readFile($default_path); | ||||||
|  |     } | ||||||
|  |  | ||||||
|     $color_const = hexdec(trim($color, '#')); |     $color_const = hexdec(trim($color, '#')); | ||||||
|     $true_border = self::rgba2gd($border); |     $true_border = self::rgba2gd($border); | ||||||
|     $image_map = self::getImageMap(); |     $image_map = self::getImageMap(); | ||||||
|   | |||||||
| @@ -14,6 +14,10 @@ final class PhabricatorFileDataController extends PhabricatorFileController { | |||||||
|     return false; |     return false; | ||||||
|   } |   } | ||||||
|  |  | ||||||
|  |   public function shouldAllowPartialSessions() { | ||||||
|  |     return true; | ||||||
|  |   } | ||||||
|  |  | ||||||
|   public function handleRequest(AphrontRequest $request) { |   public function handleRequest(AphrontRequest $request) { | ||||||
|     $viewer = $request->getViewer(); |     $viewer = $request->getViewer(); | ||||||
|     $this->phid = $request->getURIData('phid'); |     $this->phid = $request->getURIData('phid'); | ||||||
|   | |||||||
| @@ -134,6 +134,9 @@ final class PhabricatorFile extends PhabricatorFileDAO | |||||||
|           'columns' => array('builtinKey'), |           'columns' => array('builtinKey'), | ||||||
|           'unique' => true, |           'unique' => true, | ||||||
|         ), |         ), | ||||||
|  |         'key_engine' => array( | ||||||
|  |           'columns' => array('storageEngine', 'storageHandle(64)'), | ||||||
|  |         ), | ||||||
|       ), |       ), | ||||||
|     ) + parent::getConfiguration(); |     ) + parent::getConfiguration(); | ||||||
|   } |   } | ||||||
|   | |||||||
| @@ -7,6 +7,12 @@ final class HarbormasterRunBuildPlansHeraldAction | |||||||
|  |  | ||||||
|   const ACTIONCONST = 'harbormaster.build'; |   const ACTIONCONST = 'harbormaster.build'; | ||||||
|  |  | ||||||
|  |   public function getRequiredAdapterStates() { | ||||||
|  |     return array( | ||||||
|  |       HeraldBuildableState::STATECONST, | ||||||
|  |     ); | ||||||
|  |   } | ||||||
|  |  | ||||||
|   public function getActionGroupKey() { |   public function getActionGroupKey() { | ||||||
|     return HeraldSupportActionGroup::ACTIONGROUPKEY; |     return HeraldSupportActionGroup::ACTIONGROUPKEY; | ||||||
|   } |   } | ||||||
|   | |||||||
| @@ -10,6 +10,7 @@ final class HarbormasterBuildQuery | |||||||
|   private $buildPlanPHIDs; |   private $buildPlanPHIDs; | ||||||
|   private $initiatorPHIDs; |   private $initiatorPHIDs; | ||||||
|   private $needBuildTargets; |   private $needBuildTargets; | ||||||
|  |   private $autobuilds; | ||||||
|  |  | ||||||
|   public function withIDs(array $ids) { |   public function withIDs(array $ids) { | ||||||
|     $this->ids = $ids; |     $this->ids = $ids; | ||||||
| @@ -41,6 +42,11 @@ final class HarbormasterBuildQuery | |||||||
|     return $this; |     return $this; | ||||||
|   } |   } | ||||||
|  |  | ||||||
|  |   public function withAutobuilds($with_autobuilds) { | ||||||
|  |     $this->autobuilds = $with_autobuilds; | ||||||
|  |     return $this; | ||||||
|  |   } | ||||||
|  |  | ||||||
|   public function needBuildTargets($need_targets) { |   public function needBuildTargets($need_targets) { | ||||||
|     $this->needBuildTargets = $need_targets; |     $this->needBuildTargets = $need_targets; | ||||||
|     return $this; |     return $this; | ||||||
| @@ -141,50 +147,87 @@ final class HarbormasterBuildQuery | |||||||
|     if ($this->ids !== null) { |     if ($this->ids !== null) { | ||||||
|       $where[] = qsprintf( |       $where[] = qsprintf( | ||||||
|         $conn, |         $conn, | ||||||
|         'id IN (%Ld)', |         'b.id IN (%Ld)', | ||||||
|         $this->ids); |         $this->ids); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     if ($this->phids !== null) { |     if ($this->phids !== null) { | ||||||
|       $where[] = qsprintf( |       $where[] = qsprintf( | ||||||
|         $conn, |         $conn, | ||||||
|         'phid in (%Ls)', |         'b.phid in (%Ls)', | ||||||
|         $this->phids); |         $this->phids); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     if ($this->buildStatuses !== null) { |     if ($this->buildStatuses !== null) { | ||||||
|       $where[] = qsprintf( |       $where[] = qsprintf( | ||||||
|         $conn, |         $conn, | ||||||
|         'buildStatus in (%Ls)', |         'b.buildStatus in (%Ls)', | ||||||
|         $this->buildStatuses); |         $this->buildStatuses); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     if ($this->buildablePHIDs !== null) { |     if ($this->buildablePHIDs !== null) { | ||||||
|       $where[] = qsprintf( |       $where[] = qsprintf( | ||||||
|         $conn, |         $conn, | ||||||
|         'buildablePHID IN (%Ls)', |         'b.buildablePHID IN (%Ls)', | ||||||
|         $this->buildablePHIDs); |         $this->buildablePHIDs); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     if ($this->buildPlanPHIDs !== null) { |     if ($this->buildPlanPHIDs !== null) { | ||||||
|       $where[] = qsprintf( |       $where[] = qsprintf( | ||||||
|         $conn, |         $conn, | ||||||
|         'buildPlanPHID IN (%Ls)', |         'b.buildPlanPHID IN (%Ls)', | ||||||
|         $this->buildPlanPHIDs); |         $this->buildPlanPHIDs); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     if ($this->initiatorPHIDs !== null) { |     if ($this->initiatorPHIDs !== null) { | ||||||
|       $where[] = qsprintf( |       $where[] = qsprintf( | ||||||
|         $conn, |         $conn, | ||||||
|         'initiatorPHID IN (%Ls)', |         'b.initiatorPHID IN (%Ls)', | ||||||
|         $this->initiatorPHIDs); |         $this->initiatorPHIDs); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|  |     if ($this->autobuilds !== null) { | ||||||
|  |       if ($this->autobuilds) { | ||||||
|  |         $where[] = qsprintf( | ||||||
|  |           $conn, | ||||||
|  |           'p.planAutoKey IS NOT NULL'); | ||||||
|  |       } else { | ||||||
|  |         $where[] = qsprintf( | ||||||
|  |           $conn, | ||||||
|  |           'p.planAutoKey IS NULL'); | ||||||
|  |       } | ||||||
|  |     } | ||||||
|  |  | ||||||
|     return $where; |     return $where; | ||||||
|   } |   } | ||||||
|  |  | ||||||
|  |   protected function buildJoinClauseParts(AphrontDatabaseConnection $conn) { | ||||||
|  |     $joins = parent::buildJoinClauseParts($conn); | ||||||
|  |  | ||||||
|  |     if ($this->shouldJoinPlanTable()) { | ||||||
|  |       $joins[] = qsprintf( | ||||||
|  |         $conn, | ||||||
|  |         'JOIN %T p ON b.buildPlanPHID = p.phid', | ||||||
|  |         id(new HarbormasterBuildPlan())->getTableName()); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     return $joins; | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   private function shouldJoinPlanTable() { | ||||||
|  |     if ($this->autobuilds !== null) { | ||||||
|  |       return true; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     return false; | ||||||
|  |   } | ||||||
|  |  | ||||||
|   public function getQueryApplicationClass() { |   public function getQueryApplicationClass() { | ||||||
|     return 'PhabricatorHarbormasterApplication'; |     return 'PhabricatorHarbormasterApplication'; | ||||||
|   } |   } | ||||||
|  |  | ||||||
|  |   protected function getPrimaryTableAlias() { | ||||||
|  |     return 'b'; | ||||||
|  |   } | ||||||
|  |  | ||||||
| } | } | ||||||
|   | |||||||
| @@ -43,6 +43,9 @@ final class HarbormasterBuildArtifact extends HarbormasterDAO | |||||||
|         'key_target' => array( |         'key_target' => array( | ||||||
|           'columns' => array('buildTargetPHID', 'artifactType'), |           'columns' => array('buildTargetPHID', 'artifactType'), | ||||||
|         ), |         ), | ||||||
|  |         'key_index' => array( | ||||||
|  |           'columns' => array('artifactIndex'), | ||||||
|  |         ), | ||||||
|       ), |       ), | ||||||
|     ) + parent::getConfiguration(); |     ) + parent::getConfiguration(); | ||||||
|   } |   } | ||||||
|   | |||||||
| @@ -17,6 +17,7 @@ abstract class HeraldAction extends Phobject { | |||||||
|   const DO_STANDARD_PERMISSION = 'do.standard.permission'; |   const DO_STANDARD_PERMISSION = 'do.standard.permission'; | ||||||
|   const DO_STANDARD_INVALID_ACTION = 'do.standard.invalid-action'; |   const DO_STANDARD_INVALID_ACTION = 'do.standard.invalid-action'; | ||||||
|   const DO_STANDARD_WRONG_RULE_TYPE = 'do.standard.wrong-rule-type'; |   const DO_STANDARD_WRONG_RULE_TYPE = 'do.standard.wrong-rule-type'; | ||||||
|  |   const DO_STANDARD_FORBIDDEN = 'do.standard.forbidden'; | ||||||
|  |  | ||||||
|   abstract public function getHeraldActionName(); |   abstract public function getHeraldActionName(); | ||||||
|   abstract public function supportsObject($object); |   abstract public function supportsObject($object); | ||||||
| @@ -25,6 +26,10 @@ abstract class HeraldAction extends Phobject { | |||||||
|  |  | ||||||
|   abstract public function renderActionDescription($value); |   abstract public function renderActionDescription($value); | ||||||
|  |  | ||||||
|  |   public function getRequiredAdapterStates() { | ||||||
|  |     return array(); | ||||||
|  |   } | ||||||
|  |  | ||||||
|   protected function renderActionEffectDescription($type, $data) { |   protected function renderActionEffectDescription($type, $data) { | ||||||
|     return null; |     return null; | ||||||
|   } |   } | ||||||
| @@ -336,6 +341,11 @@ abstract class HeraldAction extends Phobject { | |||||||
|         'color' => 'red', |         'color' => 'red', | ||||||
|         'name' => pht('Wrong Rule Type'), |         'name' => pht('Wrong Rule Type'), | ||||||
|       ), |       ), | ||||||
|  |       self::DO_STANDARD_FORBIDDEN => array( | ||||||
|  |         'icon' => 'fa-ban', | ||||||
|  |         'color' => 'violet', | ||||||
|  |         'name' => pht('Forbidden'), | ||||||
|  |       ), | ||||||
|     ); |     ); | ||||||
|   } |   } | ||||||
|  |  | ||||||
| @@ -381,6 +391,8 @@ abstract class HeraldAction extends Phobject { | |||||||
|         return pht( |         return pht( | ||||||
|           'This action does not support rules of type "%s".', |           'This action does not support rules of type "%s".', | ||||||
|           $data); |           $data); | ||||||
|  |       case self::DO_STANDARD_FORBIDDEN: | ||||||
|  |         return HeraldStateReasons::getExplanation($data); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     return null; |     return null; | ||||||
|   | |||||||
| @@ -37,6 +37,7 @@ abstract class HeraldAdapter extends Phobject { | |||||||
|   private $fieldMap; |   private $fieldMap; | ||||||
|   private $actionMap; |   private $actionMap; | ||||||
|   private $edgeCache = array(); |   private $edgeCache = array(); | ||||||
|  |   private $forbiddenActions = array(); | ||||||
|  |  | ||||||
|   public function getEmailPHIDs() { |   public function getEmailPHIDs() { | ||||||
|     return array_values($this->emailPHIDs); |     return array_values($this->emailPHIDs); | ||||||
| @@ -1116,4 +1117,38 @@ abstract class HeraldAdapter extends Phobject { | |||||||
|     return $this->edgeCache[$type]; |     return $this->edgeCache[$type]; | ||||||
|   } |   } | ||||||
|  |  | ||||||
|  |  | ||||||
|  | /* -(  Forbidden Actions  )-------------------------------------------------- */ | ||||||
|  |  | ||||||
|  |  | ||||||
|  |   final public function getForbiddenActions() { | ||||||
|  |     return array_keys($this->forbiddenActions); | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   final public function setForbiddenAction($action, $reason) { | ||||||
|  |     $this->forbiddenActions[$action] = $reason; | ||||||
|  |     return $this; | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   final public function getRequiredFieldStates($field_key) { | ||||||
|  |     return $this->requireFieldImplementation($field_key) | ||||||
|  |       ->getRequiredAdapterStates(); | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   final public function getRequiredActionStates($action_key) { | ||||||
|  |     return $this->requireActionImplementation($action_key) | ||||||
|  |       ->getRequiredAdapterStates(); | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   final public function getForbiddenReason($action) { | ||||||
|  |     if (!isset($this->forbiddenActions[$action])) { | ||||||
|  |       throw new Exception( | ||||||
|  |         pht( | ||||||
|  |           'Action "%s" is not forbidden!', | ||||||
|  |           $action)); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     return $this->forbiddenActions[$action]; | ||||||
|  |   } | ||||||
|  |  | ||||||
| } | } | ||||||
|   | |||||||
| @@ -273,7 +273,11 @@ final class HeraldTranscriptController extends HeraldController { | |||||||
|           ->setTarget(phutil_tag('strong', array(), pht('Conditions')))); |           ->setTarget(phutil_tag('strong', array(), pht('Conditions')))); | ||||||
|  |  | ||||||
|       foreach ($cond_xscripts as $cond_xscript) { |       foreach ($cond_xscripts as $cond_xscript) { | ||||||
|         if ($cond_xscript->getResult()) { |         if ($cond_xscript->isForbidden()) { | ||||||
|  |           $icon = 'fa-ban'; | ||||||
|  |           $color = 'indigo'; | ||||||
|  |           $result = pht('Forbidden'); | ||||||
|  |         } else if ($cond_xscript->getResult()) { | ||||||
|           $icon = 'fa-check'; |           $icon = 'fa-check'; | ||||||
|           $color = 'green'; |           $color = 'green'; | ||||||
|           $result = pht('Passed'); |           $result = pht('Passed'); | ||||||
| @@ -284,12 +288,17 @@ final class HeraldTranscriptController extends HeraldController { | |||||||
|         } |         } | ||||||
|  |  | ||||||
|         if ($cond_xscript->getNote()) { |         if ($cond_xscript->getNote()) { | ||||||
|  |           $note_text = $cond_xscript->getNote(); | ||||||
|  |           if ($cond_xscript->isForbidden()) { | ||||||
|  |             $note_text = HeraldStateReasons::getExplanation($note_text); | ||||||
|  |           } | ||||||
|  |  | ||||||
|           $note = phutil_tag( |           $note = phutil_tag( | ||||||
|             'div', |             'div', | ||||||
|             array( |             array( | ||||||
|               'class' => 'herald-condition-note', |               'class' => 'herald-condition-note', | ||||||
|             ), |             ), | ||||||
|             $cond_xscript->getNote()); |             $note_text); | ||||||
|         } else { |         } else { | ||||||
|           $note = null; |           $note = null; | ||||||
|         } |         } | ||||||
| @@ -310,7 +319,12 @@ final class HeraldTranscriptController extends HeraldController { | |||||||
|         $cond_list->addItem($cond_item); |         $cond_list->addItem($cond_item); | ||||||
|       } |       } | ||||||
|  |  | ||||||
|       if ($rule_xscript->getResult()) { |       if ($rule_xscript->isForbidden()) { | ||||||
|  |         $last_icon = 'fa-ban'; | ||||||
|  |         $last_color = 'indigo'; | ||||||
|  |         $last_result = pht('Forbidden'); | ||||||
|  |         $last_note = pht('Object state prevented rule evaluation.'); | ||||||
|  |       } else if ($rule_xscript->getResult()) { | ||||||
|         $last_icon = 'fa-check-circle'; |         $last_icon = 'fa-check-circle'; | ||||||
|         $last_color = 'green'; |         $last_color = 'green'; | ||||||
|         $last_result = pht('Passed'); |         $last_result = pht('Passed'); | ||||||
|   | |||||||
| @@ -12,6 +12,9 @@ final class HeraldEngine extends Phobject { | |||||||
|   protected $object; |   protected $object; | ||||||
|   private $dryRun; |   private $dryRun; | ||||||
|  |  | ||||||
|  |   private $forbiddenFields = array(); | ||||||
|  |   private $forbiddenActions = array(); | ||||||
|  |  | ||||||
|   public function setDryRun($dry_run) { |   public function setDryRun($dry_run) { | ||||||
|     $this->dryRun = $dry_run; |     $this->dryRun = $dry_run; | ||||||
|     return $this; |     return $this; | ||||||
| @@ -76,39 +79,42 @@ final class HeraldEngine extends Phobject { | |||||||
|           // This is not a dry run, and this rule is only supposed to be |           // This is not a dry run, and this rule is only supposed to be | ||||||
|           // applied a single time, and it's already been applied... |           // applied a single time, and it's already been applied... | ||||||
|           // That means automatic failure. |           // That means automatic failure. | ||||||
|           $xscript = id(new HeraldRuleTranscript()) |           $this->newRuleTranscript($rule) | ||||||
|             ->setRuleID($rule->getID()) |  | ||||||
|             ->setResult(false) |             ->setResult(false) | ||||||
|             ->setRuleName($rule->getName()) |  | ||||||
|             ->setRuleOwner($rule->getAuthorPHID()) |  | ||||||
|             ->setReason( |             ->setReason( | ||||||
|               pht( |               pht( | ||||||
|                 'This rule is only supposed to be repeated a single time, '. |                 'This rule is only supposed to be repeated a single time, '. | ||||||
|                 'and it has already been applied.')); |                 'and it has already been applied.')); | ||||||
|           $this->transcript->addRuleTranscript($xscript); |  | ||||||
|  |           $rule_matches = false; | ||||||
|  |         } else { | ||||||
|  |           if ($this->isForbidden($rule, $object)) { | ||||||
|  |             $this->newRuleTranscript($rule) | ||||||
|  |               ->setResult(HeraldRuleTranscript::RESULT_FORBIDDEN) | ||||||
|  |               ->setReason( | ||||||
|  |                 pht( | ||||||
|  |                   'Object state is not compatible with rule.')); | ||||||
|  |  | ||||||
|             $rule_matches = false; |             $rule_matches = false; | ||||||
|           } else { |           } else { | ||||||
|             $rule_matches = $this->doesRuleMatch($rule, $object); |             $rule_matches = $this->doesRuleMatch($rule, $object); | ||||||
|           } |           } | ||||||
|  |         } | ||||||
|       } catch (HeraldRecursiveConditionsException $ex) { |       } catch (HeraldRecursiveConditionsException $ex) { | ||||||
|         $names = array(); |         $names = array(); | ||||||
|         foreach ($this->stack as $rule_id => $ignored) { |         foreach ($this->stack as $rule_phid => $ignored) { | ||||||
|           $names[] = '"'.$rules[$rule_id]->getName().'"'; |           $names[] = '"'.$rules[$rule_phid]->getName().'"'; | ||||||
|         } |         } | ||||||
|         $names = implode(', ', $names); |         $names = implode(', ', $names); | ||||||
|         foreach ($this->stack as $rule_id => $ignored) { |         foreach ($this->stack as $rule_phid => $ignored) { | ||||||
|           $xscript = new HeraldRuleTranscript(); |           $this->newRuleTranscript($rules[$rule_phid]) | ||||||
|           $xscript->setRuleID($rule_id); |             ->setResult(false) | ||||||
|           $xscript->setResult(false); |             ->setReason( | ||||||
|           $xscript->setReason( |  | ||||||
|               pht( |               pht( | ||||||
|                 "Rules %s are recursively dependent upon one another! ". |                 "Rules %s are recursively dependent upon one another! ". | ||||||
|                 "Don't do this! You have formed an unresolvable cycle in the ". |                 "Don't do this! You have formed an unresolvable cycle in the ". | ||||||
|                 "dependency graph!", |                 "dependency graph!", | ||||||
|                 $names)); |                 $names)); | ||||||
|           $xscript->setRuleName($rules[$rule_id]->getName()); |  | ||||||
|           $xscript->setRuleOwner($rules[$rule_id]->getAuthorPHID()); |  | ||||||
|           $this->transcript->addRuleTranscript($xscript); |  | ||||||
|         } |         } | ||||||
|         $rule_matches = false; |         $rule_matches = false; | ||||||
|       } |       } | ||||||
| @@ -309,14 +315,9 @@ final class HeraldEngine extends Phobject { | |||||||
|       } |       } | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     $rule_transcript = new HeraldRuleTranscript(); |     $this->newRuleTranscript($rule) | ||||||
|     $rule_transcript->setRuleID($rule->getID()); |       ->setResult($result) | ||||||
|     $rule_transcript->setResult($result); |       ->setReason($reason); | ||||||
|     $rule_transcript->setReason($reason); |  | ||||||
|     $rule_transcript->setRuleName($rule->getName()); |  | ||||||
|     $rule_transcript->setRuleOwner($rule->getAuthorPHID()); |  | ||||||
|  |  | ||||||
|     $this->transcript->addRuleTranscript($rule_transcript); |  | ||||||
|  |  | ||||||
|     return $result; |     return $result; | ||||||
|   } |   } | ||||||
| @@ -327,16 +328,7 @@ final class HeraldEngine extends Phobject { | |||||||
|     HeraldAdapter $object) { |     HeraldAdapter $object) { | ||||||
|  |  | ||||||
|     $object_value = $this->getConditionObjectValue($condition, $object); |     $object_value = $this->getConditionObjectValue($condition, $object); | ||||||
|     $test_value   = $condition->getValue(); |     $transcript = $this->newConditionTranscript($rule, $condition); | ||||||
|  |  | ||||||
|     $cond = $condition->getFieldCondition(); |  | ||||||
|  |  | ||||||
|     $transcript = new HeraldConditionTranscript(); |  | ||||||
|     $transcript->setRuleID($rule->getID()); |  | ||||||
|     $transcript->setConditionID($condition->getID()); |  | ||||||
|     $transcript->setFieldName($condition->getFieldName()); |  | ||||||
|     $transcript->setCondition($cond); |  | ||||||
|     $transcript->setTestValue($test_value); |  | ||||||
|  |  | ||||||
|     try { |     try { | ||||||
|       $result = $object->doesConditionMatch( |       $result = $object->doesConditionMatch( | ||||||
| @@ -351,8 +343,6 @@ final class HeraldEngine extends Phobject { | |||||||
|  |  | ||||||
|     $transcript->setResult($result); |     $transcript->setResult($result); | ||||||
|  |  | ||||||
|     $this->transcript->addConditionTranscript($transcript); |  | ||||||
|  |  | ||||||
|     return $result; |     return $result; | ||||||
|   } |   } | ||||||
|  |  | ||||||
| @@ -446,4 +436,136 @@ final class HeraldEngine extends Phobject { | |||||||
|     return false; |     return false; | ||||||
|   } |   } | ||||||
|  |  | ||||||
|  |   private function newRuleTranscript(HeraldRule $rule) { | ||||||
|  |     $xscript = id(new HeraldRuleTranscript()) | ||||||
|  |       ->setRuleID($rule->getID()) | ||||||
|  |       ->setRuleName($rule->getName()) | ||||||
|  |       ->setRuleOwner($rule->getAuthorPHID()); | ||||||
|  |  | ||||||
|  |     $this->transcript->addRuleTranscript($xscript); | ||||||
|  |  | ||||||
|  |     return $xscript; | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   private function newConditionTranscript( | ||||||
|  |     HeraldRule $rule, | ||||||
|  |     HeraldCondition $condition) { | ||||||
|  |  | ||||||
|  |     $xscript = id(new HeraldConditionTranscript()) | ||||||
|  |       ->setRuleID($rule->getID()) | ||||||
|  |       ->setConditionID($condition->getID()) | ||||||
|  |       ->setFieldName($condition->getFieldName()) | ||||||
|  |       ->setCondition($condition->getFieldCondition()) | ||||||
|  |       ->setTestValue($condition->getValue()); | ||||||
|  |  | ||||||
|  |     $this->transcript->addConditionTranscript($xscript); | ||||||
|  |  | ||||||
|  |     return $xscript; | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   private function newApplyTranscript( | ||||||
|  |     HeraldAdapter $adapter, | ||||||
|  |     HeraldRule $rule, | ||||||
|  |     HeraldActionRecord $action) { | ||||||
|  |  | ||||||
|  |     $effect = id(new HeraldEffect()) | ||||||
|  |       ->setObjectPHID($adapter->getPHID()) | ||||||
|  |       ->setAction($action->getAction()) | ||||||
|  |       ->setTarget($action->getTarget()) | ||||||
|  |       ->setRule($rule); | ||||||
|  |  | ||||||
|  |     $xscript = new HeraldApplyTranscript($effect, false); | ||||||
|  |  | ||||||
|  |     $this->transcript->addApplyTranscript($xscript); | ||||||
|  |  | ||||||
|  |     return $xscript; | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   private function isForbidden( | ||||||
|  |     HeraldRule $rule, | ||||||
|  |     HeraldAdapter $adapter) { | ||||||
|  |  | ||||||
|  |     $forbidden = $adapter->getForbiddenActions(); | ||||||
|  |     if (!$forbidden) { | ||||||
|  |       return false; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     $forbidden = array_fuse($forbidden); | ||||||
|  |  | ||||||
|  |     $is_forbidden = false; | ||||||
|  |  | ||||||
|  |     foreach ($rule->getConditions() as $condition) { | ||||||
|  |       $field_key = $condition->getFieldName(); | ||||||
|  |  | ||||||
|  |       if (!isset($this->forbiddenFields[$field_key])) { | ||||||
|  |         $reason = null; | ||||||
|  |  | ||||||
|  |         try { | ||||||
|  |           $states = $adapter->getRequiredFieldStates($field_key); | ||||||
|  |         } catch (Exception $ex) { | ||||||
|  |           $states = array(); | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         foreach ($states as $state) { | ||||||
|  |           if (!isset($forbidden[$state])) { | ||||||
|  |             continue; | ||||||
|  |           } | ||||||
|  |           $reason = $adapter->getForbiddenReason($state); | ||||||
|  |           break; | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         $this->forbiddenFields[$field_key] = $reason; | ||||||
|  |       } | ||||||
|  |  | ||||||
|  |       $forbidden_reason = $this->forbiddenFields[$field_key]; | ||||||
|  |       if ($forbidden_reason !== null) { | ||||||
|  |         $this->newConditionTranscript($rule, $condition) | ||||||
|  |           ->setResult(HeraldConditionTranscript::RESULT_FORBIDDEN) | ||||||
|  |           ->setNote($forbidden_reason); | ||||||
|  |  | ||||||
|  |         $is_forbidden = true; | ||||||
|  |       } | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     foreach ($rule->getActions() as $action_record) { | ||||||
|  |       $action_key = $action_record->getAction(); | ||||||
|  |  | ||||||
|  |       if (!isset($this->forbiddenActions[$action_key])) { | ||||||
|  |         $reason = null; | ||||||
|  |  | ||||||
|  |         try { | ||||||
|  |           $states = $adapter->getRequiredActionStates($action_key); | ||||||
|  |         } catch (Exception $ex) { | ||||||
|  |           $states = array(); | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         foreach ($states as $state) { | ||||||
|  |           if (!isset($forbidden[$state])) { | ||||||
|  |             continue; | ||||||
|  |           } | ||||||
|  |           $reason = $adapter->getForbiddenReason($state); | ||||||
|  |           break; | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         $this->forbiddenActions[$action_key] = $reason; | ||||||
|  |       } | ||||||
|  |  | ||||||
|  |       $forbidden_reason = $this->forbiddenActions[$action_key]; | ||||||
|  |       if ($forbidden_reason !== null) { | ||||||
|  |         $this->newApplyTranscript($adapter, $rule, $action_record) | ||||||
|  |           ->setAppliedReason( | ||||||
|  |             array( | ||||||
|  |               array( | ||||||
|  |                 'type' => HeraldAction::DO_STANDARD_FORBIDDEN, | ||||||
|  |                 'data' => $forbidden_reason, | ||||||
|  |               ), | ||||||
|  |             )); | ||||||
|  |  | ||||||
|  |         $is_forbidden = true; | ||||||
|  |       } | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     return $is_forbidden; | ||||||
|  |   } | ||||||
|  |  | ||||||
| } | } | ||||||
|   | |||||||
| @@ -20,6 +20,10 @@ abstract class HeraldField extends Phobject { | |||||||
|     return null; |     return null; | ||||||
|   } |   } | ||||||
|  |  | ||||||
|  |   public function getRequiredAdapterStates() { | ||||||
|  |     return array(); | ||||||
|  |   } | ||||||
|  |  | ||||||
|   protected function getHeraldFieldStandardType() { |   protected function getHeraldFieldStandardType() { | ||||||
|     throw new PhutilMethodNotImplementedException(); |     throw new PhutilMethodNotImplementedException(); | ||||||
|   } |   } | ||||||
|   | |||||||
							
								
								
									
										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 $note; | ||||||
|   protected $result; |   protected $result; | ||||||
|  |  | ||||||
|  |   const RESULT_FORBIDDEN = 'forbidden'; | ||||||
|  |  | ||||||
|   public function setRuleID($rule_id) { |   public function setRuleID($rule_id) { | ||||||
|     $this->ruleID = $rule_id; |     $this->ruleID = $rule_id; | ||||||
|     return $this; |     return $this; | ||||||
| @@ -72,4 +74,9 @@ final class HeraldConditionTranscript extends Phobject { | |||||||
|   public function getResult() { |   public function getResult() { | ||||||
|     return $this->result; |     return $this->result; | ||||||
|   } |   } | ||||||
|  |  | ||||||
|  |   public function isForbidden() { | ||||||
|  |     return ($this->getResult() === self::RESULT_FORBIDDEN); | ||||||
|  |   } | ||||||
|  |  | ||||||
| } | } | ||||||
|   | |||||||
| @@ -9,6 +9,12 @@ final class HeraldRuleTranscript extends Phobject { | |||||||
|   protected $ruleName; |   protected $ruleName; | ||||||
|   protected $ruleOwner; |   protected $ruleOwner; | ||||||
|  |  | ||||||
|  |   const RESULT_FORBIDDEN = 'forbidden'; | ||||||
|  |  | ||||||
|  |   public function isForbidden() { | ||||||
|  |     return ($this->getResult() === self::RESULT_FORBIDDEN); | ||||||
|  |   } | ||||||
|  |  | ||||||
|   public function setResult($result) { |   public function setResult($result) { | ||||||
|     $this->result = $result; |     $this->result = $result; | ||||||
|     return $this; |     return $this; | ||||||
|   | |||||||
| @@ -77,23 +77,12 @@ final class LegalpadDocumentQuery | |||||||
|     return $this; |     return $this; | ||||||
|   } |   } | ||||||
|  |  | ||||||
|  |   public function newResultObject() { | ||||||
|  |     return new LegalpadDocument(); | ||||||
|  |   } | ||||||
|  |  | ||||||
|   protected function loadPage() { |   protected function loadPage() { | ||||||
|     $table = new LegalpadDocument(); |     return $this->loadStandardPage($this->newResultObject()); | ||||||
|     $conn_r = $table->establishConnection('r'); |  | ||||||
|  |  | ||||||
|     $data = queryfx_all( |  | ||||||
|       $conn_r, |  | ||||||
|       'SELECT d.* FROM %T d %Q %Q %Q %Q %Q', |  | ||||||
|       $table->getTableName(), |  | ||||||
|       $this->buildJoinClause($conn_r), |  | ||||||
|       $this->buildWhereClause($conn_r), |  | ||||||
|       $this->buildGroupClause($conn_r), |  | ||||||
|       $this->buildOrderClause($conn_r), |  | ||||||
|       $this->buildLimitClause($conn_r)); |  | ||||||
|  |  | ||||||
|     $documents = $table->loadAllFromArray($data); |  | ||||||
|  |  | ||||||
|     return $documents; |  | ||||||
|   } |   } | ||||||
|  |  | ||||||
|   protected function willFilterPage(array $documents) { |   protected function willFilterPage(array $documents) { | ||||||
| @@ -134,12 +123,12 @@ final class LegalpadDocumentQuery | |||||||
|     return $documents; |     return $documents; | ||||||
|   } |   } | ||||||
|  |  | ||||||
|   protected function buildJoinClause(AphrontDatabaseConnection $conn_r) { |   protected function buildJoinClauseParts(AphrontDatabaseConnection $conn) { | ||||||
|     $joins = array(); |     $joins = parent::buildJoinClauseParts($conn); | ||||||
|  |  | ||||||
|     if ($this->contributorPHIDs !== null) { |     if ($this->contributorPHIDs !== null) { | ||||||
|       $joins[] = qsprintf( |       $joins[] = qsprintf( | ||||||
|         $conn_r, |         $conn, | ||||||
|         'JOIN edge contributor ON contributor.src = d.phid |         'JOIN edge contributor ON contributor.src = d.phid | ||||||
|           AND contributor.type = %d', |           AND contributor.type = %d', | ||||||
|         PhabricatorObjectHasContributorEdgeType::EDGECONST); |         PhabricatorObjectHasContributorEdgeType::EDGECONST); | ||||||
| @@ -147,79 +136,81 @@ final class LegalpadDocumentQuery | |||||||
|  |  | ||||||
|     if ($this->signerPHIDs !== null) { |     if ($this->signerPHIDs !== null) { | ||||||
|       $joins[] = qsprintf( |       $joins[] = qsprintf( | ||||||
|         $conn_r, |         $conn, | ||||||
|         'JOIN %T signer ON signer.documentPHID = d.phid |         'JOIN %T signer ON signer.documentPHID = d.phid | ||||||
|           AND signer.signerPHID IN (%Ls)', |           AND signer.signerPHID IN (%Ls)', | ||||||
|         id(new LegalpadDocumentSignature())->getTableName(), |         id(new LegalpadDocumentSignature())->getTableName(), | ||||||
|         $this->signerPHIDs); |         $this->signerPHIDs); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     return implode(' ', $joins); |     return $joins; | ||||||
|   } |   } | ||||||
|  |  | ||||||
|   protected function buildGroupClause(AphrontDatabaseConnection $conn_r) { |   protected function shouldGroupQueryResultRows() { | ||||||
|     if ($this->contributorPHIDs || $this->signerPHIDs) { |     if ($this->contributorPHIDs) { | ||||||
|       return 'GROUP BY d.id'; |       return true; | ||||||
|     } else { |  | ||||||
|       return ''; |  | ||||||
|     } |  | ||||||
|     } |     } | ||||||
|  |  | ||||||
|   protected function buildWhereClause(AphrontDatabaseConnection $conn_r) { |     if ($this->signerPHIDs) { | ||||||
|     $where = array(); |       return true; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     return parent::shouldGroupQueryResultRows(); | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   protected function buildWhereClauseParts(AphrontDatabaseConnection $conn) { | ||||||
|  |     $where = parent::buildWhereClauseParts($conn); | ||||||
|  |  | ||||||
|     if ($this->ids !== null) { |     if ($this->ids !== null) { | ||||||
|       $where[] = qsprintf( |       $where[] = qsprintf( | ||||||
|         $conn_r, |         $conn, | ||||||
|         'd.id IN (%Ld)', |         'd.id IN (%Ld)', | ||||||
|         $this->ids); |         $this->ids); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     if ($this->phids !== null) { |     if ($this->phids !== null) { | ||||||
|       $where[] = qsprintf( |       $where[] = qsprintf( | ||||||
|         $conn_r, |         $conn, | ||||||
|         'd.phid IN (%Ls)', |         'd.phid IN (%Ls)', | ||||||
|         $this->phids); |         $this->phids); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     if ($this->creatorPHIDs !== null) { |     if ($this->creatorPHIDs !== null) { | ||||||
|       $where[] = qsprintf( |       $where[] = qsprintf( | ||||||
|         $conn_r, |         $conn, | ||||||
|         'd.creatorPHID IN (%Ls)', |         'd.creatorPHID IN (%Ls)', | ||||||
|         $this->creatorPHIDs); |         $this->creatorPHIDs); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     if ($this->dateCreatedAfter !== null) { |     if ($this->dateCreatedAfter !== null) { | ||||||
|       $where[] = qsprintf( |       $where[] = qsprintf( | ||||||
|         $conn_r, |         $conn, | ||||||
|         'd.dateCreated >= %d', |         'd.dateCreated >= %d', | ||||||
|         $this->dateCreatedAfter); |         $this->dateCreatedAfter); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     if ($this->dateCreatedBefore !== null) { |     if ($this->dateCreatedBefore !== null) { | ||||||
|       $where[] = qsprintf( |       $where[] = qsprintf( | ||||||
|         $conn_r, |         $conn, | ||||||
|         'd.dateCreated <= %d', |         'd.dateCreated <= %d', | ||||||
|         $this->dateCreatedBefore); |         $this->dateCreatedBefore); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     if ($this->contributorPHIDs !== null) { |     if ($this->contributorPHIDs !== null) { | ||||||
|       $where[] = qsprintf( |       $where[] = qsprintf( | ||||||
|         $conn_r, |         $conn, | ||||||
|         'contributor.dst IN (%Ls)', |         'contributor.dst IN (%Ls)', | ||||||
|         $this->contributorPHIDs); |         $this->contributorPHIDs); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     if ($this->signatureRequired !== null) { |     if ($this->signatureRequired !== null) { | ||||||
|       $where[] = qsprintf( |       $where[] = qsprintf( | ||||||
|         $conn_r, |         $conn, | ||||||
|         'd.requireSignature = %d', |         'd.requireSignature = %d', | ||||||
|         $this->signatureRequired); |         $this->signatureRequired); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     $where[] = $this->buildPagingClause($conn_r); |     return $where; | ||||||
|  |  | ||||||
|     return $this->formatWhereClause($where); |  | ||||||
|   } |   } | ||||||
|  |  | ||||||
|   private function loadDocumentBodies(array $documents) { |   private function loadDocumentBodies(array $documents) { | ||||||
| @@ -275,4 +266,8 @@ final class LegalpadDocumentQuery | |||||||
|     return 'PhabricatorLegalpadApplication'; |     return 'PhabricatorLegalpadApplication'; | ||||||
|   } |   } | ||||||
|  |  | ||||||
|  |   protected function getPrimaryTableAlias() { | ||||||
|  |     return 'd'; | ||||||
|  |   } | ||||||
|  |  | ||||||
| } | } | ||||||
|   | |||||||
| @@ -11,105 +11,66 @@ final class LegalpadDocumentSearchEngine | |||||||
|     return 'PhabricatorLegalpadApplication'; |     return 'PhabricatorLegalpadApplication'; | ||||||
|   } |   } | ||||||
|  |  | ||||||
|   public function buildSavedQueryFromRequest(AphrontRequest $request) { |   public function newQuery() { | ||||||
|     $saved = new PhabricatorSavedQuery(); |     return id(new LegalpadDocumentQuery()) | ||||||
|     $saved->setParameter( |  | ||||||
|       'creatorPHIDs', |  | ||||||
|       $this->readUsersFromRequest($request, 'creators')); |  | ||||||
|  |  | ||||||
|     $saved->setParameter( |  | ||||||
|       'contributorPHIDs', |  | ||||||
|       $this->readUsersFromRequest($request, 'contributors')); |  | ||||||
|  |  | ||||||
|     $saved->setParameter( |  | ||||||
|       'withViewerSignature', |  | ||||||
|       $request->getBool('withViewerSignature')); |  | ||||||
|  |  | ||||||
|     $saved->setParameter('createdStart', $request->getStr('createdStart')); |  | ||||||
|     $saved->setParameter('createdEnd', $request->getStr('createdEnd')); |  | ||||||
|  |  | ||||||
|     return $saved; |  | ||||||
|   } |  | ||||||
|  |  | ||||||
|   public function buildQueryFromSavedQuery(PhabricatorSavedQuery $saved) { |  | ||||||
|     $query = id(new LegalpadDocumentQuery()) |  | ||||||
|       ->needViewerSignatures(true); |       ->needViewerSignatures(true); | ||||||
|  |  | ||||||
|     $creator_phids = $saved->getParameter('creatorPHIDs', array()); |  | ||||||
|     if ($creator_phids) { |  | ||||||
|       $query->withCreatorPHIDs($creator_phids); |  | ||||||
|   } |   } | ||||||
|  |  | ||||||
|     $contributor_phids = $saved->getParameter('contributorPHIDs', array()); |   protected function buildCustomSearchFields() { | ||||||
|     if ($contributor_phids) { |     return array( | ||||||
|       $query->withContributorPHIDs($contributor_phids); |       id(new PhabricatorUsersSearchField()) | ||||||
|  |         ->setLabel(pht('Signed By')) | ||||||
|  |         ->setKey('signerPHIDs') | ||||||
|  |         ->setAliases(array('signer', 'signers', 'signerPHID')) | ||||||
|  |         ->setDescription( | ||||||
|  |           pht('Search for documents signed by given users.')), | ||||||
|  |       id(new PhabricatorUsersSearchField()) | ||||||
|  |         ->setLabel(pht('Creators')) | ||||||
|  |         ->setKey('creatorPHIDs') | ||||||
|  |         ->setAliases(array('creator', 'creators', 'creatorPHID')) | ||||||
|  |         ->setDescription( | ||||||
|  |           pht('Search for documents with given creators.')), | ||||||
|  |       id(new PhabricatorUsersSearchField()) | ||||||
|  |         ->setLabel(pht('Contributors')) | ||||||
|  |         ->setKey('contributorPHIDs') | ||||||
|  |         ->setAliases(array('contributor', 'contributors', 'contributorPHID')) | ||||||
|  |         ->setDescription( | ||||||
|  |           pht('Search for documents with given contributors.')), | ||||||
|  |       id(new PhabricatorSearchDateField()) | ||||||
|  |         ->setLabel(pht('Created After')) | ||||||
|  |         ->setKey('createdStart'), | ||||||
|  |       id(new PhabricatorSearchDateField()) | ||||||
|  |         ->setLabel(pht('Created Before')) | ||||||
|  |         ->setKey('createdEnd'), | ||||||
|  |     ); | ||||||
|   } |   } | ||||||
|  |  | ||||||
|     if ($saved->getParameter('withViewerSignature')) { |   protected function buildQueryFromParameters(array $map) { | ||||||
|       $viewer_phid = $this->requireViewer()->getPHID(); |     $query = $this->newQuery(); | ||||||
|       if ($viewer_phid) { |  | ||||||
|         $query->withSignerPHIDs(array($viewer_phid)); |     if ($map['signerPHIDs']) { | ||||||
|       } |       $query->withSignerPHIDs($map['signerPHIDs']); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     $start = $this->parseDateTime($saved->getParameter('createdStart')); |     if ($map['contributorPHIDs']) { | ||||||
|     $end = $this->parseDateTime($saved->getParameter('createdEnd')); |       $query->withContributorPHIDs($map['creatorPHIDs']); | ||||||
|  |  | ||||||
|     if ($start) { |  | ||||||
|       $query->withDateCreatedAfter($start); |  | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     if ($end) { |     if ($map['creatorPHIDs']) { | ||||||
|       $query->withDateCreatedBefore($end); |       $query->withCreatorPHIDs($map['creatorPHIDs']); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     if ($map['createdStart']) { | ||||||
|  |       $query->withDateCreatedAfter($map['createdStart']); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     if ($map['createdEnd']) { | ||||||
|  |       $query->withDateCreatedAfter($map['createdStart']); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     return $query; |     return $query; | ||||||
|   } |   } | ||||||
|  |  | ||||||
|   public function buildSearchForm( |  | ||||||
|     AphrontFormView $form, |  | ||||||
|     PhabricatorSavedQuery $saved_query) { |  | ||||||
|  |  | ||||||
|     $creator_phids = $saved_query->getParameter('creatorPHIDs', array()); |  | ||||||
|     $contributor_phids = $saved_query->getParameter( |  | ||||||
|       'contributorPHIDs', array()); |  | ||||||
|  |  | ||||||
|     $viewer_signature = $saved_query->getParameter('withViewerSignature'); |  | ||||||
|     if (!$this->requireViewer()->getPHID()) { |  | ||||||
|       $viewer_signature = false; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     $form |  | ||||||
|       ->appendChild( |  | ||||||
|         id(new AphrontFormCheckboxControl()) |  | ||||||
|           ->addCheckbox( |  | ||||||
|             'withViewerSignature', |  | ||||||
|             1, |  | ||||||
|             pht('Show only documents I have signed.'), |  | ||||||
|             $viewer_signature) |  | ||||||
|           ->setDisabled(!$this->requireViewer()->getPHID())) |  | ||||||
|       ->appendControl( |  | ||||||
|         id(new AphrontFormTokenizerControl()) |  | ||||||
|           ->setDatasource(new PhabricatorPeopleDatasource()) |  | ||||||
|           ->setName('creators') |  | ||||||
|           ->setLabel(pht('Creators')) |  | ||||||
|           ->setValue($creator_phids)) |  | ||||||
|       ->appendControl( |  | ||||||
|         id(new AphrontFormTokenizerControl()) |  | ||||||
|           ->setDatasource(new PhabricatorPeopleDatasource()) |  | ||||||
|           ->setName('contributors') |  | ||||||
|           ->setLabel(pht('Contributors')) |  | ||||||
|           ->setValue($contributor_phids)); |  | ||||||
|  |  | ||||||
|     $this->buildDateRange( |  | ||||||
|       $form, |  | ||||||
|       $saved_query, |  | ||||||
|       'createdStart', |  | ||||||
|       pht('Created After'), |  | ||||||
|       'createdEnd', |  | ||||||
|       pht('Created Before')); |  | ||||||
|   } |  | ||||||
|  |  | ||||||
|   protected function getURI($path) { |   protected function getURI($path) { | ||||||
|     return '/legalpad/'.$path; |     return '/legalpad/'.$path; | ||||||
|   } |   } | ||||||
| @@ -130,10 +91,11 @@ final class LegalpadDocumentSearchEngine | |||||||
|     $query = $this->newSavedQuery(); |     $query = $this->newSavedQuery(); | ||||||
|     $query->setQueryKey($query_key); |     $query->setQueryKey($query_key); | ||||||
|  |  | ||||||
|  |     $viewer = $this->requireViewer(); | ||||||
|  |  | ||||||
|     switch ($query_key) { |     switch ($query_key) { | ||||||
|       case 'signed': |       case 'signed': | ||||||
|         return $query |         return $query->setParameter('signerPHIDs', array($viewer->getPHID())); | ||||||
|           ->setParameter('withViewerSignature', true); |  | ||||||
|       case 'all': |       case 'all': | ||||||
|         return $query; |         return $query; | ||||||
|     } |     } | ||||||
| @@ -141,12 +103,6 @@ final class LegalpadDocumentSearchEngine | |||||||
|     return parent::buildSavedQueryFromBuiltin($query_key); |     return parent::buildSavedQueryFromBuiltin($query_key); | ||||||
|   } |   } | ||||||
|  |  | ||||||
|   protected function getRequiredHandlePHIDsForResultList( |  | ||||||
|     array $documents, |  | ||||||
|     PhabricatorSavedQuery $query) { |  | ||||||
|     return array(); |  | ||||||
|   } |  | ||||||
|  |  | ||||||
|   protected function renderResultList( |   protected function renderResultList( | ||||||
|     array $documents, |     array $documents, | ||||||
|     PhabricatorSavedQuery $query, |     PhabricatorSavedQuery $query, | ||||||
|   | |||||||
| @@ -21,6 +21,16 @@ final class ManiphestGetTaskTransactionsConduitAPIMethod | |||||||
|     return 'nonempty list<dict<string, wild>>'; |     return 'nonempty list<dict<string, wild>>'; | ||||||
|   } |   } | ||||||
|  |  | ||||||
|  |   public function getMethodStatus() { | ||||||
|  |     return self::METHOD_STATUS_FROZEN; | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   public function getMethodStatusDescription() { | ||||||
|  |     return pht( | ||||||
|  |       'This method is frozen and will eventually be deprecated. New code '. | ||||||
|  |       'should use "transaction.search" instead.'); | ||||||
|  |   } | ||||||
|  |  | ||||||
|   protected function execute(ConduitAPIRequest $request) { |   protected function execute(ConduitAPIRequest $request) { | ||||||
|     $results = array(); |     $results = array(); | ||||||
|     $task_ids = $request->getValue('ids'); |     $task_ids = $request->getValue('ids'); | ||||||
|   | |||||||
| @@ -57,5 +57,15 @@ final class ManiphestTaskDescriptionTransaction | |||||||
|     return $changes; |     return $changes; | ||||||
|   } |   } | ||||||
|  |  | ||||||
|  |   public function getTransactionTypeForConduit($xaction) { | ||||||
|  |     return 'description'; | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   public function getFieldValuesForConduit($xaction, $data) { | ||||||
|  |     return array( | ||||||
|  |       'old' => $xaction->getOldValue(), | ||||||
|  |       'new' => $xaction->getNewValue(), | ||||||
|  |     ); | ||||||
|  |   } | ||||||
|  |  | ||||||
| } | } | ||||||
|   | |||||||
| @@ -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; |     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'; |   const TRANSACTIONTYPE = 'priority'; | ||||||
|  |  | ||||||
|   public function generateOldValue($object) { |   public function generateOldValue($object) { | ||||||
|     if ($this->isNewObject()) { |     return (string)$object->getPriority(); | ||||||
|       return null; |  | ||||||
|     } |  | ||||||
|     return $object->getPriority(); |  | ||||||
|   } |   } | ||||||
|  |  | ||||||
|   public function generateNewValue($object, $value) { |   public function generateNewValue($object, $value) { | ||||||
| @@ -19,7 +16,7 @@ final class ManiphestTaskPriorityTransaction | |||||||
|     // should still be allowed, even if the priority is no longer |     // should still be allowed, even if the priority is no longer | ||||||
|     // valid, so treat this as a no-op. |     // valid, so treat this as a no-op. | ||||||
|     if ($value === ManiphestTaskPriority::UNKNOWN_PRIORITY_KEYWORD) { |     if ($value === ManiphestTaskPriority::UNKNOWN_PRIORITY_KEYWORD) { | ||||||
|       return $object->getPriority(); |       return (string)$object->getPriority(); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     return (string)ManiphestTaskPriority::getTaskPriorityFromKeyword($value); |     return (string)ManiphestTaskPriority::getTaskPriorityFromKeyword($value); | ||||||
|   | |||||||
| @@ -6,9 +6,6 @@ final class ManiphestTaskStatusTransaction | |||||||
|   const TRANSACTIONTYPE = 'status'; |   const TRANSACTIONTYPE = 'status'; | ||||||
|  |  | ||||||
|   public function generateOldValue($object) { |   public function generateOldValue($object) { | ||||||
|     if ($this->isNewObject()) { |  | ||||||
|       return null; |  | ||||||
|     } |  | ||||||
|     return $object->getStatus(); |     return $object->getStatus(); | ||||||
|   } |   } | ||||||
|  |  | ||||||
| @@ -229,4 +226,15 @@ final class ManiphestTaskStatusTransaction | |||||||
|  |  | ||||||
|   } |   } | ||||||
|  |  | ||||||
|  |   public function getTransactionTypeForConduit($xaction) { | ||||||
|  |     return 'status'; | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   public function getFieldValuesForConduit($xaction, $data) { | ||||||
|  |     return array( | ||||||
|  |       'old' => $xaction->getOldValue(), | ||||||
|  |       'new' => $xaction->getNewValue(), | ||||||
|  |     ); | ||||||
|  |   } | ||||||
|  |  | ||||||
| } | } | ||||||
|   | |||||||
| @@ -72,4 +72,15 @@ final class ManiphestTaskTitleTransaction | |||||||
|     return $errors; |     return $errors; | ||||||
|   } |   } | ||||||
|  |  | ||||||
|  |   public function getTransactionTypeForConduit($xaction) { | ||||||
|  |     return 'title'; | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   public function getFieldValuesForConduit($xaction, $data) { | ||||||
|  |     return array( | ||||||
|  |       'old' => $xaction->getOldValue(), | ||||||
|  |       'new' => $xaction->getNewValue(), | ||||||
|  |     ); | ||||||
|  |   } | ||||||
|  |  | ||||||
| } | } | ||||||
|   | |||||||
| @@ -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_SEND = 'do.send'; | ||||||
|   const DO_FORCE = 'do.force'; |   const DO_FORCE = 'do.force'; | ||||||
|  |  | ||||||
|  |   public function getRequiredAdapterStates() { | ||||||
|  |     return array( | ||||||
|  |       HeraldMailableState::STATECONST, | ||||||
|  |     ); | ||||||
|  |   } | ||||||
|  |  | ||||||
|   public function supportsObject($object) { |   public function supportsObject($object) { | ||||||
|     // NOTE: This implementation lacks generality, but there's no great way to |     // NOTE: This implementation lacks generality, but there's no great way to | ||||||
|     // figure out if something generates email right now. |     // figure out if something generates email right now. | ||||||
|   | |||||||
| @@ -15,4 +15,8 @@ final class PhabricatorOwnersPackageTransaction | |||||||
|     return 'PhabricatorOwnersPackageTransactionType'; |     return 'PhabricatorOwnersPackageTransactionType'; | ||||||
|   } |   } | ||||||
|  |  | ||||||
|  |   public function getApplicationTransactionCommentObject() { | ||||||
|  |     return null; | ||||||
|  |   } | ||||||
|  |  | ||||||
| } | } | ||||||
|   | |||||||
| @@ -9,6 +9,10 @@ final class PeopleMainMenuBarExtension | |||||||
|     return $viewer->isLoggedIn(); |     return $viewer->isLoggedIn(); | ||||||
|   } |   } | ||||||
|  |  | ||||||
|  |   public function shouldRequireFullSession() { | ||||||
|  |     return false; | ||||||
|  |   } | ||||||
|  |  | ||||||
|   public function getExtensionOrder() { |   public function getExtensionOrder() { | ||||||
|     return 1200; |     return 1200; | ||||||
|   } |   } | ||||||
| @@ -65,6 +69,7 @@ final class PeopleMainMenuBarExtension | |||||||
|     $view = id(new PhabricatorActionListView()) |     $view = id(new PhabricatorActionListView()) | ||||||
|       ->setViewer($viewer); |       ->setViewer($viewer); | ||||||
|  |  | ||||||
|  |     if ($this->getIsFullSession()) { | ||||||
|       $view->addAction( |       $view->addAction( | ||||||
|         id(new PhabricatorActionView()) |         id(new PhabricatorActionView()) | ||||||
|           ->appendChild($user_view)); |           ->appendChild($user_view)); | ||||||
| @@ -101,6 +106,7 @@ final class PeopleMainMenuBarExtension | |||||||
|         id(new PhabricatorActionView()) |         id(new PhabricatorActionView()) | ||||||
|           ->addSigil('logout-item') |           ->addSigil('logout-item') | ||||||
|           ->setType(PhabricatorActionView::TYPE_DIVIDER)); |           ->setType(PhabricatorActionView::TYPE_DIVIDER)); | ||||||
|  |     } | ||||||
|  |  | ||||||
|     $view->addAction( |     $view->addAction( | ||||||
|       id(new PhabricatorActionView()) |       id(new PhabricatorActionView()) | ||||||
|   | |||||||
| @@ -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; |     return PhabricatorPhameBlogPHIDType::TYPECONST; | ||||||
|   } |   } | ||||||
|  |  | ||||||
|  |   public function getApplicationTransactionCommentObject() { | ||||||
|  |     return null; | ||||||
|  |   } | ||||||
|  |  | ||||||
|   public function getBaseTransactionClass() { |   public function getBaseTransactionClass() { | ||||||
|     return 'PhameBlogTransactionType'; |     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, |     PhabricatorProjectInterface, | ||||||
|     PhabricatorDestructibleInterface, |     PhabricatorDestructibleInterface, | ||||||
|     PhabricatorSpacesInterface, |     PhabricatorSpacesInterface, | ||||||
|     PhabricatorFulltextInterface { |     PhabricatorFulltextInterface, | ||||||
|  |     PhabricatorFerretInterface { | ||||||
|  |  | ||||||
|   const MARKUP_FIELD_CONTENT = 'markup:content'; |   const MARKUP_FIELD_CONTENT = 'markup:content'; | ||||||
|  |  | ||||||
| @@ -300,4 +301,13 @@ final class PonderQuestion extends PonderDAO | |||||||
|     return new PonderQuestionFulltextEngine(); |     return new PonderQuestionFulltextEngine(); | ||||||
|   } |   } | ||||||
|  |  | ||||||
|  |  | ||||||
|  | /* -(  PhabricatorFerretInterface  )----------------------------------------- */ | ||||||
|  |  | ||||||
|  |  | ||||||
|  |   public function newFerretEngine() { | ||||||
|  |     return new PonderQuestionFerretEngine(); | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |  | ||||||
| } | } | ||||||
|   | |||||||
| @@ -146,8 +146,7 @@ final class PhabricatorProjectCoreTestCase extends PhabricatorTestCase { | |||||||
|     $user = $this->createUser(); |     $user = $this->createUser(); | ||||||
|     $user->save(); |     $user->save(); | ||||||
|  |  | ||||||
|     $user2 = $this->createUser(); |     $user->setAllowInlineCacheGeneration(true); | ||||||
|     $user2->save(); |  | ||||||
|  |  | ||||||
|     $proj = $this->createProject($user); |     $proj = $this->createProject($user); | ||||||
|  |  | ||||||
| @@ -1289,12 +1288,19 @@ final class PhabricatorProjectCoreTestCase extends PhabricatorTestCase { | |||||||
|  |  | ||||||
|     $new_name = $proj->getName().' '.mt_rand(); |     $new_name = $proj->getName().' '.mt_rand(); | ||||||
|  |  | ||||||
|     $xaction = new PhabricatorProjectTransaction(); |     $params = array( | ||||||
|     $xaction->setTransactionType( |       'objectIdentifier' => $proj->getID(), | ||||||
|       PhabricatorProjectNameTransaction::TRANSACTIONTYPE); |       'transactions' => array( | ||||||
|     $xaction->setNewValue($new_name); |         array( | ||||||
|  |           'type' => 'name', | ||||||
|  |           'value' => $new_name, | ||||||
|  |         ), | ||||||
|  |       ), | ||||||
|  |     ); | ||||||
|  |  | ||||||
|     $this->applyTransactions($proj, $user, array($xaction)); |     id(new ConduitCall('project.edit', $params)) | ||||||
|  |       ->setUser($user) | ||||||
|  |       ->execute(); | ||||||
|  |  | ||||||
|     return true; |     return true; | ||||||
|   } |   } | ||||||
|   | |||||||
| @@ -120,16 +120,6 @@ final class PhabricatorProjectTransactionEditor | |||||||
|     PhabricatorApplicationTransaction $xaction) { |     PhabricatorApplicationTransaction $xaction) { | ||||||
|  |  | ||||||
|     switch ($xaction->getTransactionType()) { |     switch ($xaction->getTransactionType()) { | ||||||
|       case PhabricatorProjectNameTransaction::TRANSACTIONTYPE: |  | ||||||
|       case PhabricatorProjectStatusTransaction::TRANSACTIONTYPE: |  | ||||||
|       case PhabricatorProjectImageTransaction::TRANSACTIONTYPE: |  | ||||||
|       case PhabricatorProjectIconTransaction::TRANSACTIONTYPE: |  | ||||||
|       case PhabricatorProjectColorTransaction::TRANSACTIONTYPE: |  | ||||||
|         PhabricatorPolicyFilter::requireCapability( |  | ||||||
|           $this->requireActor(), |  | ||||||
|           $object, |  | ||||||
|           PhabricatorPolicyCapability::CAN_EDIT); |  | ||||||
|         return; |  | ||||||
|       case PhabricatorProjectLockTransaction::TRANSACTIONTYPE: |       case PhabricatorProjectLockTransaction::TRANSACTIONTYPE: | ||||||
|         PhabricatorPolicyFilter::requireCapability( |         PhabricatorPolicyFilter::requireCapability( | ||||||
|           $this->requireActor(), |           $this->requireActor(), | ||||||
|   | |||||||
| @@ -67,7 +67,9 @@ final class PhabricatorBoardRenderingEngine extends Phobject { | |||||||
|     $project_phids = $object->getProjectPHIDs(); |     $project_phids = $object->getProjectPHIDs(); | ||||||
|     $project_handles = array_select_keys($this->handles, $project_phids); |     $project_handles = array_select_keys($this->handles, $project_phids); | ||||||
|     if ($project_handles) { |     if ($project_handles) { | ||||||
|       $card->setProjectHandles($project_handles); |       $card | ||||||
|  |         ->setHideArchivedProjects(true) | ||||||
|  |         ->setProjectHandles($project_handles); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     $cover_phid = $object->getCoverImageThumbnailPHID(); |     $cover_phid = $object->getCoverImageThumbnailPHID(); | ||||||
|   | |||||||
| @@ -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) { |   public function handleEvent(PhutilEvent $event) { | ||||||
|  |     $object = $event->getValue('object'); | ||||||
|  |  | ||||||
|     switch ($event->getType()) { |     switch ($event->getType()) { | ||||||
|       case PhabricatorEventType::TYPE_UI_WILLRENDERPROPERTIES: |       case PhabricatorEventType::TYPE_UI_WILLRENDERPROPERTIES: | ||||||
|  |         // Hacky solution so that property list view on Diffusion | ||||||
|  |         // commits shows build status, but not Projects, Subscriptions, | ||||||
|  |         // or Tokens. | ||||||
|  |         if ($object instanceof PhabricatorRepositoryCommit) { | ||||||
|  |           return; | ||||||
|  |         } | ||||||
|         $this->handlePropertyEvent($event); |         $this->handlePropertyEvent($event); | ||||||
|         break; |         break; | ||||||
|     } |     } | ||||||
|   | |||||||
Some files were not shown because too many files have changed in this diff Show More
		Reference in New Issue
	
	Block a user