diff --git a/resources/celerity/map.php b/resources/celerity/map.php index 54832d23ae..87b45c9dc6 100644 --- a/resources/celerity/map.php +++ b/resources/celerity/map.php @@ -9,7 +9,7 @@ return array( 'names' => array( 'conpherence.pkg.css' => 'e68cf1fa', 'conpherence.pkg.js' => 'b5b51108', - 'core.pkg.css' => '0e4a68ad', + 'core.pkg.css' => 'fe4effd6', 'core.pkg.js' => '5d80e0db', 'darkconsole.pkg.js' => '1f9a31bc', 'differential.pkg.css' => '45951e9e', @@ -36,13 +36,13 @@ return array( 'rsrc/css/aphront/tokenizer.css' => '15d5ff71', 'rsrc/css/aphront/tooltip.css' => '173b9431', 'rsrc/css/aphront/typeahead-browse.css' => 'f2818435', - 'rsrc/css/aphront/typeahead.css' => '4434bc8a', + 'rsrc/css/aphront/typeahead.css' => 'a4a21016', 'rsrc/css/application/almanac/almanac.css' => 'dbb9b3af', 'rsrc/css/application/auth/auth.css' => '0877ed6e', 'rsrc/css/application/base/main-menu-view.css' => '16053029', 'rsrc/css/application/base/notification-menu.css' => '73fefdfa', 'rsrc/css/application/base/phui-theme.css' => '9f261c6b', - 'rsrc/css/application/base/standard-page-view.css' => 'c581d2ac', + 'rsrc/css/application/base/standard-page-view.css' => '34ee718b', 'rsrc/css/application/chatlog/chatlog.css' => 'd295b020', 'rsrc/css/application/conduit/conduit-api.css' => '7bc725c4', 'rsrc/css/application/config/config-options.css' => 'd55ed093', @@ -75,7 +75,7 @@ return array( 'rsrc/css/application/diffusion/diffusion-readme.css' => '419dd5b6', 'rsrc/css/application/diffusion/diffusion-repository.css' => 'ee6f20ec', 'rsrc/css/application/diffusion/diffusion-source.css' => '750add59', - 'rsrc/css/application/diffusion/diffusion.css' => '67bd971b', + 'rsrc/css/application/diffusion/diffusion.css' => 'ceacf994', 'rsrc/css/application/feed/feed.css' => 'ecd4ec57', 'rsrc/css/application/files/global-drag-and-drop.css' => 'b556a948', 'rsrc/css/application/flag/flag.css' => 'bba8f811', @@ -111,7 +111,7 @@ return array( 'rsrc/css/application/releeph/releeph-request-differential-create-dialog.css' => '8d8b92cd', 'rsrc/css/application/releeph/releeph-request-typeahead.css' => '667a48ae', 'rsrc/css/application/search/application-search-view.css' => '66ee5d46', - 'rsrc/css/application/search/search-results.css' => '8f8e08ed', + 'rsrc/css/application/search/search-results.css' => '505dd8cf', 'rsrc/css/application/slowvote/slowvote.css' => 'a94b7230', 'rsrc/css/application/tokens/tokens.css' => '3d0f239e', 'rsrc/css/application/uiexample/example.css' => '528b19de', @@ -165,7 +165,7 @@ return array( 'rsrc/css/phui/phui-image-mask.css' => 'a8498f9c', 'rsrc/css/phui/phui-info-view.css' => 'e1b4ec37', 'rsrc/css/phui/phui-invisible-character-view.css' => '6993d9f0', - 'rsrc/css/phui/phui-left-right.css' => 'f60c67e7', + 'rsrc/css/phui/phui-left-right.css' => '75227a4d', 'rsrc/css/phui/phui-lightbox.css' => '0a035e40', 'rsrc/css/phui/phui-list.css' => '38f8c9bd', 'rsrc/css/phui/phui-object-box.css' => '9cff003c', @@ -178,12 +178,12 @@ return array( 'rsrc/css/phui/phui-status.css' => 'd5263e49', 'rsrc/css/phui/phui-tag-view.css' => 'b4719c50', 'rsrc/css/phui/phui-timeline-view.css' => 'f21db7ca', - 'rsrc/css/phui/phui-two-column-view.css' => 'ae38a939', + 'rsrc/css/phui/phui-two-column-view.css' => '76dcd3d4', 'rsrc/css/phui/workboards/phui-workboard-color.css' => '783cdff5', 'rsrc/css/phui/workboards/phui-workboard.css' => '3bc85455', 'rsrc/css/phui/workboards/phui-workcard.css' => 'cca5fa92', 'rsrc/css/phui/workboards/phui-workpanel.css' => 'a3a63478', - 'rsrc/css/sprite-login.css' => '587d92d7', + 'rsrc/css/sprite-login.css' => '396f3c3a', 'rsrc/css/sprite-tokens.css' => '9cdfd599', 'rsrc/css/syntax/syntax-default.css' => '9923583c', 'rsrc/externals/d3/d3.min.js' => 'a11a5ff2', @@ -359,8 +359,8 @@ return array( 'rsrc/image/phrequent_active.png' => 'a466a8ed', 'rsrc/image/phrequent_inactive.png' => 'bfc15a69', 'rsrc/image/resize.png' => 'fd476de4', - 'rsrc/image/sprite-login-X2.png' => '4abee916', - 'rsrc/image/sprite-login.png' => '2b9663fd', + 'rsrc/image/sprite-login-X2.png' => '308c92c4', + 'rsrc/image/sprite-login.png' => '9ec54245', 'rsrc/image/sprite-tokens-X2.png' => '804a5232', 'rsrc/image/sprite-tokens.png' => 'b41d03da', 'rsrc/image/texture/card-gradient.png' => '815f26e8', @@ -546,7 +546,7 @@ return array( 'aphront-table-view-css' => 'a3aa6910', 'aphront-tokenizer-control-css' => '15d5ff71', 'aphront-tooltip-css' => '173b9431', - 'aphront-typeahead-control-css' => '4434bc8a', + 'aphront-typeahead-control-css' => 'a4a21016', 'application-search-view-css' => '66ee5d46', 'auth-css' => '0877ed6e', 'bulk-job-css' => 'df9c1d4a', @@ -570,7 +570,7 @@ return array( 'differential-revision-history-css' => '0e8eb855', 'differential-revision-list-css' => 'f3c47d33', 'differential-table-of-contents-css' => 'ae4b7a55', - 'diffusion-css' => '67bd971b', + 'diffusion-css' => 'ceacf994', 'diffusion-icons-css' => '0c15255e', 'diffusion-readme-css' => '419dd5b6', 'diffusion-repository-css' => 'ee6f20ec', @@ -798,11 +798,11 @@ return array( 'phabricator-phtize' => 'd254d646', 'phabricator-prefab' => 'c5af80a2', 'phabricator-remarkup-css' => 'cad18339', - 'phabricator-search-results-css' => '8f8e08ed', + 'phabricator-search-results-css' => '505dd8cf', 'phabricator-shaped-request' => '7cbe244b', 'phabricator-slowvote-css' => 'a94b7230', 'phabricator-source-code-view-css' => 'aea41829', - 'phabricator-standard-page-view' => 'c581d2ac', + 'phabricator-standard-page-view' => '34ee718b', 'phabricator-textareautils' => '320810c8', 'phabricator-title' => '485aaa6c', 'phabricator-tooltip' => '358b8c04', @@ -854,7 +854,7 @@ return array( 'phui-info-view-css' => 'e1b4ec37', 'phui-inline-comment-view-css' => '65ae3bc2', 'phui-invisible-character-view-css' => '6993d9f0', - 'phui-left-right-css' => 'f60c67e7', + 'phui-left-right-css' => '75227a4d', 'phui-lightbox-css' => '0a035e40', 'phui-list-view-css' => '38f8c9bd', 'phui-object-box-css' => '9cff003c', @@ -874,7 +874,7 @@ return array( 'phui-tag-view-css' => 'b4719c50', 'phui-theme-css' => '9f261c6b', 'phui-timeline-view-css' => 'f21db7ca', - 'phui-two-column-view-css' => 'ae38a939', + 'phui-two-column-view-css' => '76dcd3d4', 'phui-workboard-color-css' => '783cdff5', 'phui-workboard-view-css' => '3bc85455', 'phui-workcard-view-css' => 'cca5fa92', @@ -897,7 +897,7 @@ return array( 'releeph-request-differential-create-dialog' => '8d8b92cd', 'releeph-request-typeahead-css' => '667a48ae', 'setup-issue-css' => 'f794cfc3', - 'sprite-login-css' => '587d92d7', + 'sprite-login-css' => '396f3c3a', 'sprite-tokens-css' => '9cdfd599', 'syntax-default-css' => '9923583c', 'syntax-highlighting-css' => 'cae95e89', diff --git a/resources/sprite/login_1x/Dropbox.png b/resources/sprite/login_1x/Dropbox.png deleted file mode 100644 index 1d68eee5ca..0000000000 Binary files a/resources/sprite/login_1x/Dropbox.png and /dev/null differ diff --git a/resources/sprite/login_1x/Linkedin.png b/resources/sprite/login_1x/Linkedin.png deleted file mode 100644 index 2cf76885de..0000000000 Binary files a/resources/sprite/login_1x/Linkedin.png and /dev/null differ diff --git a/resources/sprite/login_1x/Openid.png b/resources/sprite/login_1x/Openid.png deleted file mode 100644 index 7259db47fd..0000000000 Binary files a/resources/sprite/login_1x/Openid.png and /dev/null differ diff --git a/resources/sprite/login_1x/Phabricator.png b/resources/sprite/login_1x/Phabricator.png index 5657d9c3c6..b998007a3c 100644 Binary files a/resources/sprite/login_1x/Phabricator.png and b/resources/sprite/login_1x/Phabricator.png differ diff --git a/resources/sprite/login_1x/Yahoo.png b/resources/sprite/login_1x/Yahoo.png deleted file mode 100644 index 47a01c53d7..0000000000 Binary files a/resources/sprite/login_1x/Yahoo.png and /dev/null differ diff --git a/resources/sprite/login_2x/Dropbox.png b/resources/sprite/login_2x/Dropbox.png deleted file mode 100644 index 953ed4eec4..0000000000 Binary files a/resources/sprite/login_2x/Dropbox.png and /dev/null differ diff --git a/resources/sprite/login_2x/Linkedin.png b/resources/sprite/login_2x/Linkedin.png deleted file mode 100644 index 9999d55c5c..0000000000 Binary files a/resources/sprite/login_2x/Linkedin.png and /dev/null differ diff --git a/resources/sprite/login_2x/Openid.png b/resources/sprite/login_2x/Openid.png deleted file mode 100644 index 360aa6e6ef..0000000000 Binary files a/resources/sprite/login_2x/Openid.png and /dev/null differ diff --git a/resources/sprite/login_2x/Phabricator.png b/resources/sprite/login_2x/Phabricator.png index 89c0372fbc..3792f723fc 100644 Binary files a/resources/sprite/login_2x/Phabricator.png and b/resources/sprite/login_2x/Phabricator.png differ diff --git a/resources/sprite/login_2x/Yahoo.png b/resources/sprite/login_2x/Yahoo.png deleted file mode 100644 index 5aa8c4fe74..0000000000 Binary files a/resources/sprite/login_2x/Yahoo.png and /dev/null differ diff --git a/resources/sprite/manifest/login.json b/resources/sprite/manifest/login.json index f5d15dd890..e9bbb9f2cb 100644 --- a/resources/sprite/manifest/login.json +++ b/resources/sprite/manifest/login.json @@ -21,11 +21,6 @@ "rule": ".login-Disqus", "hash": "77b29d56329a3c30b79d6b6673b0e39b" }, - "login-Dropbox": { - "name": "login-Dropbox", - "rule": ".login-Dropbox", - "hash": "5eaf07ae4598227fbbba3474675d18c7" - }, "login-Facebook": { "name": "login-Facebook", "rule": ".login-Facebook", @@ -61,21 +56,11 @@ "rule": ".login-LDAP", "hash": "e31df2e9faf8ca0925ef93128a82fa7a" }, - "login-Linkedin": { - "name": "login-Linkedin", - "rule": ".login-Linkedin", - "hash": "b7ee1e92c923462531f3a34093e57127" - }, "login-MediaWiki": { "name": "login-MediaWiki", "rule": ".login-MediaWiki", "hash": "f1f0a9382434081a9a84e7584828c2dd" }, - "login-Openid": { - "name": "login-Openid", - "rule": ".login-Openid", - "hash": "886f65ad44435344a1fa1c13e7758155" - }, "login-PayPal": { "name": "login-PayPal", "rule": ".login-PayPal", @@ -84,7 +69,7 @@ "login-Phabricator": { "name": "login-Phabricator", "rule": ".login-Phabricator", - "hash": "d0f830803593bbcc025d7d5a29ee3ecd" + "hash": "54f5ddae4b9d138c438ec00ed42544d2" }, "login-Slack": { "name": "login-Slack", @@ -120,11 +105,6 @@ "name": "login-WordPressCOM", "rule": ".login-WordPressCOM", "hash": "9eae4205dbed0c42a18ee4f8e0fa151b" - }, - "login-Yahoo": { - "name": "login-Yahoo", - "rule": ".login-Yahoo", - "hash": "f37822c769f6b8ebd1dda6b3ac89b83b" } }, "scales": [ diff --git a/resources/sql/autopatches/20170811.differential.01.status.php b/resources/sql/autopatches/20170811.differential.01.status.php new file mode 100644 index 0000000000..9d57d033af --- /dev/null +++ b/resources/sql/autopatches/20170811.differential.01.status.php @@ -0,0 +1,48 @@ +" +// control with hard-coded status groups for status selection to using a +// tokenizer with status functions. + +$table = new PhabricatorSavedQuery(); +$conn = $table->establishConnection('w'); + +$status_map = array( + 'status-open' => array('open()'), + 'status-closed' => array('closed()'), + + 'status-accepted' => array('accepted'), + 'status-needs-review' => array('needs-review'), + 'status-needs-revision' => array('needs-revision'), + 'status-abandoned' => array('abandoned'), +); + +foreach (new LiskMigrationIterator($table) as $query) { + if ($query->getEngineClassName() !== 'DifferentialRevisionSearchEngine') { + // This isn't a revision query. + continue; + } + + $parameters = $query->getParameters(); + $status = idx($parameters, 'status'); + + if (!$status) { + // This query didn't specify a "status" value. + continue; + } + + if (!isset($status_map[$status])) { + // The "status" value is unknown, or does not correspond to a + // modern "status" constraint. + continue; + } + + $parameters['statuses'] = $status_map[$status]; + + queryfx( + $conn, + 'UPDATE %T SET parameters = %s WHERE id = %d', + $table->getTableName(), + phutil_json_encode($parameters), + $query->getID()); +} diff --git a/resources/sql/autopatches/20170811.differential.02.modernstatus.sql b/resources/sql/autopatches/20170811.differential.02.modernstatus.sql new file mode 100644 index 0000000000..a305206411 --- /dev/null +++ b/resources/sql/autopatches/20170811.differential.02.modernstatus.sql @@ -0,0 +1,17 @@ +UPDATE {$NAMESPACE}_differential.differential_revision + SET status = "needs-review" WHERE status = "0"; + +UPDATE {$NAMESPACE}_differential.differential_revision + SET status = "needs-revision" WHERE status = "1"; + +UPDATE {$NAMESPACE}_differential.differential_revision + SET status = "accepted" WHERE status = "2"; + +UPDATE {$NAMESPACE}_differential.differential_revision + SET status = "published" WHERE status = "3"; + +UPDATE {$NAMESPACE}_differential.differential_revision + SET status = "abandoned" WHERE status = "4"; + +UPDATE {$NAMESPACE}_differential.differential_revision + SET status = "changes-planned" WHERE status = "5"; diff --git a/resources/sql/autopatches/20170811.differential.03.modernxaction.php b/resources/sql/autopatches/20170811.differential.03.modernxaction.php new file mode 100644 index 0000000000..e84e3ce95b --- /dev/null +++ b/resources/sql/autopatches/20170811.differential.03.modernxaction.php @@ -0,0 +1,38 @@ + 'needs-review', + '1' => 'needs-revision', + '2' => 'accepted', + '3' => 'published', + '4' => 'abandoned', + '5' => 'changes-planned', +); + +$table = new DifferentialTransaction(); +$conn = $table->establishConnection('w'); + +foreach (new LiskMigrationIterator($table) as $xaction) { + $type = $xaction->getTransactionType(); + + if (($type != 'differential:status') && + ($type != 'differential.revision.status')) { + continue; + } + + $old = $xaction->getOldValue(); + $new = $xaction->getNewValue(); + + $old = idx($map, $old, $old); + $new = idx($map, $new, $new); + + queryfx( + $conn, + 'UPDATE %T SET transactionType = %s, oldValue = %s, newValue = %s + WHERE id = %d', + $table->getTableName(), + 'differential.revision.status', + json_encode($old), + json_encode($new), + $xaction->getID()); +} diff --git a/resources/sql/autopatches/20170820.phame.01.post.views.sql b/resources/sql/autopatches/20170820.phame.01.post.views.sql new file mode 100644 index 0000000000..f5f72294f6 --- /dev/null +++ b/resources/sql/autopatches/20170820.phame.01.post.views.sql @@ -0,0 +1,2 @@ +ALTER TABLE {$NAMESPACE}_phame.phame_post + ADD views INTEGER NOT NULL; diff --git a/resources/sql/autopatches/20170820.phame.02.post.views.sql b/resources/sql/autopatches/20170820.phame.02.post.views.sql new file mode 100644 index 0000000000..00b9b29203 --- /dev/null +++ b/resources/sql/autopatches/20170820.phame.02.post.views.sql @@ -0,0 +1,2 @@ +UPDATE {$NAMESPACE}_phame.phame_post + SET views = 0; diff --git a/src/__phutil_library_map__.php b/src/__phutil_library_map__.php index 2b45c3856a..070f56d5c8 100644 --- a/src/__phutil_library_map__.php +++ b/src/__phutil_library_map__.php @@ -445,7 +445,6 @@ phutil_register_library_map(array( 'DifferentialExactUserFunctionDatasource' => 'applications/differential/typeahead/DifferentialExactUserFunctionDatasource.php', 'DifferentialFieldParseException' => 'applications/differential/exception/DifferentialFieldParseException.php', 'DifferentialFieldValidationException' => 'applications/differential/exception/DifferentialFieldValidationException.php', - 'DifferentialFindConduitAPIMethod' => 'applications/differential/conduit/DifferentialFindConduitAPIMethod.php', 'DifferentialGetAllDiffsConduitAPIMethod' => 'applications/differential/conduit/DifferentialGetAllDiffsConduitAPIMethod.php', 'DifferentialGetCommitMessageConduitAPIMethod' => 'applications/differential/conduit/DifferentialGetCommitMessageConduitAPIMethod.php', 'DifferentialGetCommitPathsConduitAPIMethod' => 'applications/differential/conduit/DifferentialGetCommitPathsConduitAPIMethod.php', @@ -521,6 +520,7 @@ phutil_register_library_map(array( 'DifferentialRevisionAuthorProjectsHeraldField' => 'applications/differential/herald/DifferentialRevisionAuthorProjectsHeraldField.php', 'DifferentialRevisionCloseDetailsController' => 'applications/differential/controller/DifferentialRevisionCloseDetailsController.php', 'DifferentialRevisionCloseTransaction' => 'applications/differential/xaction/DifferentialRevisionCloseTransaction.php', + 'DifferentialRevisionClosedStatusDatasource' => 'applications/differential/typeahead/DifferentialRevisionClosedStatusDatasource.php', 'DifferentialRevisionCommandeerTransaction' => 'applications/differential/xaction/DifferentialRevisionCommandeerTransaction.php', 'DifferentialRevisionContentAddedHeraldField' => 'applications/differential/herald/DifferentialRevisionContentAddedHeraldField.php', 'DifferentialRevisionContentHeraldField' => 'applications/differential/herald/DifferentialRevisionContentHeraldField.php', @@ -548,6 +548,7 @@ phutil_register_library_map(array( 'DifferentialRevisionListController' => 'applications/differential/controller/DifferentialRevisionListController.php', 'DifferentialRevisionListView' => 'applications/differential/view/DifferentialRevisionListView.php', 'DifferentialRevisionMailReceiver' => 'applications/differential/mail/DifferentialRevisionMailReceiver.php', + 'DifferentialRevisionOpenStatusDatasource' => 'applications/differential/typeahead/DifferentialRevisionOpenStatusDatasource.php', 'DifferentialRevisionOperationController' => 'applications/differential/controller/DifferentialRevisionOperationController.php', 'DifferentialRevisionPHIDType' => 'applications/differential/phid/DifferentialRevisionPHIDType.php', 'DifferentialRevisionPackageHeraldField' => 'applications/differential/herald/DifferentialRevisionPackageHeraldField.php', @@ -572,6 +573,9 @@ phutil_register_library_map(array( 'DifferentialRevisionSearchConduitAPIMethod' => 'applications/differential/conduit/DifferentialRevisionSearchConduitAPIMethod.php', 'DifferentialRevisionSearchEngine' => 'applications/differential/query/DifferentialRevisionSearchEngine.php', 'DifferentialRevisionStatus' => 'applications/differential/constants/DifferentialRevisionStatus.php', + 'DifferentialRevisionStatusDatasource' => 'applications/differential/typeahead/DifferentialRevisionStatusDatasource.php', + 'DifferentialRevisionStatusFunctionDatasource' => 'applications/differential/typeahead/DifferentialRevisionStatusFunctionDatasource.php', + 'DifferentialRevisionStatusTransaction' => 'applications/differential/xaction/DifferentialRevisionStatusTransaction.php', 'DifferentialRevisionSummaryHeraldField' => 'applications/differential/herald/DifferentialRevisionSummaryHeraldField.php', 'DifferentialRevisionSummaryTransaction' => 'applications/differential/xaction/DifferentialRevisionSummaryTransaction.php', 'DifferentialRevisionTestPlanTransaction' => 'applications/differential/xaction/DifferentialRevisionTestPlanTransaction.php', @@ -772,6 +776,7 @@ phutil_register_library_map(array( 'DiffusionPathQueryTestCase' => 'applications/diffusion/query/pathid/__tests__/DiffusionPathQueryTestCase.php', 'DiffusionPathTreeController' => 'applications/diffusion/controller/DiffusionPathTreeController.php', 'DiffusionPathValidateController' => 'applications/diffusion/controller/DiffusionPathValidateController.php', + 'DiffusionPatternSearchView' => 'applications/diffusion/view/DiffusionPatternSearchView.php', 'DiffusionPhpExternalSymbolsSource' => 'applications/diffusion/symbol/DiffusionPhpExternalSymbolsSource.php', 'DiffusionPreCommitContentAffectedFilesHeraldField' => 'applications/diffusion/herald/DiffusionPreCommitContentAffectedFilesHeraldField.php', 'DiffusionPreCommitContentAuthorHeraldField' => 'applications/diffusion/herald/DiffusionPreCommitContentAuthorHeraldField.php', @@ -4412,6 +4417,7 @@ phutil_register_library_map(array( 'PhamePostTransactionQuery' => 'applications/phame/query/PhamePostTransactionQuery.php', 'PhamePostTransactionType' => 'applications/phame/xaction/PhamePostTransactionType.php', 'PhamePostViewController' => 'applications/phame/controller/post/PhamePostViewController.php', + 'PhamePostViewsTransaction' => 'applications/phame/xaction/PhamePostViewsTransaction.php', 'PhamePostVisibilityTransaction' => 'applications/phame/xaction/PhamePostVisibilityTransaction.php', 'PhameSchemaSpec' => 'applications/phame/storage/PhameSchemaSpec.php', 'PhameSite' => 'applications/phame/site/PhameSite.php', @@ -5415,7 +5421,6 @@ phutil_register_library_map(array( 'DifferentialExactUserFunctionDatasource' => 'PhabricatorTypeaheadCompositeDatasource', 'DifferentialFieldParseException' => 'Exception', 'DifferentialFieldValidationException' => 'Exception', - 'DifferentialFindConduitAPIMethod' => 'DifferentialConduitAPIMethod', 'DifferentialGetAllDiffsConduitAPIMethod' => 'DifferentialConduitAPIMethod', 'DifferentialGetCommitMessageConduitAPIMethod' => 'DifferentialConduitAPIMethod', 'DifferentialGetCommitPathsConduitAPIMethod' => 'DifferentialConduitAPIMethod', @@ -5514,6 +5519,7 @@ phutil_register_library_map(array( 'DifferentialRevisionAuthorProjectsHeraldField' => 'DifferentialRevisionHeraldField', 'DifferentialRevisionCloseDetailsController' => 'DifferentialController', 'DifferentialRevisionCloseTransaction' => 'DifferentialRevisionActionTransaction', + 'DifferentialRevisionClosedStatusDatasource' => 'PhabricatorTypeaheadDatasource', 'DifferentialRevisionCommandeerTransaction' => 'DifferentialRevisionActionTransaction', 'DifferentialRevisionContentAddedHeraldField' => 'DifferentialRevisionHeraldField', 'DifferentialRevisionContentHeraldField' => 'DifferentialRevisionHeraldField', @@ -5541,6 +5547,7 @@ phutil_register_library_map(array( 'DifferentialRevisionListController' => 'DifferentialController', 'DifferentialRevisionListView' => 'AphrontView', 'DifferentialRevisionMailReceiver' => 'PhabricatorObjectMailReceiver', + 'DifferentialRevisionOpenStatusDatasource' => 'PhabricatorTypeaheadDatasource', 'DifferentialRevisionOperationController' => 'DifferentialController', 'DifferentialRevisionPHIDType' => 'PhabricatorPHIDType', 'DifferentialRevisionPackageHeraldField' => 'DifferentialRevisionHeraldField', @@ -5565,6 +5572,9 @@ phutil_register_library_map(array( 'DifferentialRevisionSearchConduitAPIMethod' => 'PhabricatorSearchEngineAPIMethod', 'DifferentialRevisionSearchEngine' => 'PhabricatorApplicationSearchEngine', 'DifferentialRevisionStatus' => 'Phobject', + 'DifferentialRevisionStatusDatasource' => 'PhabricatorTypeaheadDatasource', + 'DifferentialRevisionStatusFunctionDatasource' => 'PhabricatorTypeaheadCompositeDatasource', + 'DifferentialRevisionStatusTransaction' => 'DifferentialRevisionTransactionType', 'DifferentialRevisionSummaryHeraldField' => 'DifferentialRevisionHeraldField', 'DifferentialRevisionSummaryTransaction' => 'DifferentialRevisionTransactionType', 'DifferentialRevisionTestPlanTransaction' => 'DifferentialRevisionTransactionType', @@ -5768,6 +5778,7 @@ phutil_register_library_map(array( 'DiffusionPathQueryTestCase' => 'PhabricatorTestCase', 'DiffusionPathTreeController' => 'DiffusionController', 'DiffusionPathValidateController' => 'DiffusionController', + 'DiffusionPatternSearchView' => 'DiffusionView', 'DiffusionPhpExternalSymbolsSource' => 'DiffusionExternalSymbolsSource', 'DiffusionPreCommitContentAffectedFilesHeraldField' => 'DiffusionPreCommitContentHeraldField', 'DiffusionPreCommitContentAuthorHeraldField' => 'DiffusionPreCommitContentHeraldField', @@ -10031,6 +10042,7 @@ phutil_register_library_map(array( 'PhamePostTransactionQuery' => 'PhabricatorApplicationTransactionQuery', 'PhamePostTransactionType' => 'PhabricatorModularTransactionType', 'PhamePostViewController' => 'PhameLiveController', + 'PhamePostViewsTransaction' => 'PhamePostTransactionType', 'PhamePostVisibilityTransaction' => 'PhamePostTransactionType', 'PhameSchemaSpec' => 'PhabricatorConfigSchemaSpec', 'PhameSite' => 'PhabricatorSite', diff --git a/src/applications/auth/controller/PhabricatorAuthSSHKeyEditController.php b/src/applications/auth/controller/PhabricatorAuthSSHKeyEditController.php index 1023b0cd75..6dfc957f48 100644 --- a/src/applications/auth/controller/PhabricatorAuthSSHKeyEditController.php +++ b/src/applications/auth/controller/PhabricatorAuthSSHKeyEditController.php @@ -93,7 +93,7 @@ final class PhabricatorAuthSSHKeyEditController try { $editor->applyTransactions($key, $xactions); - return id(new AphrontRedirectResponse())->setURI($key->getURI()); + return id(new AphrontRedirectResponse())->setURI($cancel_uri); } catch (PhabricatorApplicationTransactionValidationException $ex) { $validation_exception = $ex; $e_name = $ex->getShortMessage($type_name); diff --git a/src/applications/cache/spec/PhabricatorDataCacheSpec.php b/src/applications/cache/spec/PhabricatorDataCacheSpec.php index 482af00d00..bf6a495f7f 100644 --- a/src/applications/cache/spec/PhabricatorDataCacheSpec.php +++ b/src/applications/cache/spec/PhabricatorDataCacheSpec.php @@ -50,9 +50,15 @@ final class PhabricatorDataCacheSpec extends PhabricatorCacheSpec { ->setVersion(phpversion('apcu')); if (ini_get('apc.enabled')) { + if (function_exists('apcu_clear_cache')) { + $clear_callback = 'apcu_clear_cache'; + } else { + $clear_callback = 'apc_clear_cache'; + } + $this ->setIsEnabled(true) - ->setClearCacheCallback('apc_clear_cache'); + ->setClearCacheCallback($clear_callback); $this->initAPCCommonSpec(); } else { $this->setIsEnabled(false); diff --git a/src/applications/config/check/PhabricatorElasticsearchSetupCheck.php b/src/applications/config/check/PhabricatorElasticsearchSetupCheck.php index 157db5e141..cd29ecdc78 100644 --- a/src/applications/config/check/PhabricatorElasticsearchSetupCheck.php +++ b/src/applications/config/check/PhabricatorElasticsearchSetupCheck.php @@ -7,6 +7,12 @@ final class PhabricatorElasticsearchSetupCheck extends PhabricatorSetupCheck { } protected function executeChecks() { + // TODO: Avoid fataling if we don't have a master database configured + // but have the MySQL search index configured. See T12965. + if (PhabricatorEnv::isReadOnly()) { + return; + } + $services = PhabricatorSearchService::getAllServices(); foreach ($services as $service) { diff --git a/src/applications/config/check/PhabricatorMySQLSetupCheck.php b/src/applications/config/check/PhabricatorMySQLSetupCheck.php index 8e2f39a504..dad9ba6d7b 100644 --- a/src/applications/config/check/PhabricatorMySQLSetupCheck.php +++ b/src/applications/config/check/PhabricatorMySQLSetupCheck.php @@ -9,7 +9,13 @@ final class PhabricatorMySQLSetupCheck extends PhabricatorSetupCheck { protected function executeChecks() { $refs = PhabricatorDatabaseRef::getActiveDatabaseRefs(); foreach ($refs as $ref) { - $this->executeRefChecks($ref); + try { + $this->executeRefChecks($ref); + } catch (AphrontConnectionQueryException $ex) { + // If we're unable to connect to a host, just skip the checks for it. + // This can happen if we're restarting during a cluster incident. See + // T12966 for discussion. + } } } diff --git a/src/applications/differential/conduit/DifferentialCloseConduitAPIMethod.php b/src/applications/differential/conduit/DifferentialCloseConduitAPIMethod.php index 30f8a5e5b7..12ecda1262 100644 --- a/src/applications/differential/conduit/DifferentialCloseConduitAPIMethod.php +++ b/src/applications/differential/conduit/DifferentialCloseConduitAPIMethod.php @@ -52,8 +52,9 @@ final class DifferentialCloseConduitAPIMethod $xactions = array(); $xactions[] = id(new DifferentialTransaction()) - ->setTransactionType(DifferentialTransaction::TYPE_ACTION) - ->setNewValue(DifferentialAction::ACTION_CLOSE); + ->setTransactionType( + DifferentialRevisionCloseTransaction::TRANSACTIONTYPE) + ->setNewValue(true); $content_source = $request->newContentSource(); diff --git a/src/applications/differential/conduit/DifferentialCreateCommentConduitAPIMethod.php b/src/applications/differential/conduit/DifferentialCreateCommentConduitAPIMethod.php index 943c3e7702..21b055a1e4 100644 --- a/src/applications/differential/conduit/DifferentialCreateCommentConduitAPIMethod.php +++ b/src/applications/differential/conduit/DifferentialCreateCommentConduitAPIMethod.php @@ -63,6 +63,7 @@ final class DifferentialCreateCommentConduitAPIMethod 'resign' => DifferentialRevisionResignTransaction::TRANSACTIONTYPE, 'request_review' => DifferentialRevisionRequestReviewTransaction::TRANSACTIONTYPE, + 'rethink' => DifferentialRevisionPlanChangesTransaction::TRANSACTIONTYPE, ); $action = $request->getValue('action'); @@ -76,9 +77,10 @@ final class DifferentialCreateCommentConduitAPIMethod case 'none': break; default: - $xactions[] = id(new DifferentialTransaction()) - ->setTransactionType(DifferentialTransaction::TYPE_ACTION) - ->setNewValue($action); + throw new Exception( + pht( + 'Unsupported action "%s".', + $action)); break; } } diff --git a/src/applications/differential/conduit/DifferentialFindConduitAPIMethod.php b/src/applications/differential/conduit/DifferentialFindConduitAPIMethod.php deleted file mode 100644 index a199f599e8..0000000000 --- a/src/applications/differential/conduit/DifferentialFindConduitAPIMethod.php +++ /dev/null @@ -1,99 +0,0 @@ - 'required '.$this->formatStringConstants($types), - 'guids' => 'required nonempty list', - ); - } - - protected function defineReturnType() { - return 'nonempty list'; - } - - protected function execute(ConduitAPIRequest $request) { - $type = $request->getValue('query'); - $guids = $request->getValue('guids'); - - $results = array(); - if (!$guids) { - return $results; - } - - $query = id(new DifferentialRevisionQuery()) - ->setViewer($request->getUser()); - - switch ($type) { - case 'open': - $query - ->withStatus(DifferentialLegacyQuery::STATUS_OPEN) - ->withAuthors($guids); - break; - case 'committable': - $query - ->withStatus(DifferentialLegacyQuery::STATUS_ACCEPTED) - ->withAuthors($guids); - break; - case 'revision-ids': - $query - ->withIDs($guids); - break; - case 'owned': - $query->withAuthors($guids); - break; - case 'phids': - $query - ->withPHIDs($guids); - break; - } - - $revisions = $query->execute(); - - foreach ($revisions as $revision) { - $diff = $revision->loadActiveDiff(); - if (!$diff) { - continue; - } - $id = $revision->getID(); - $results[] = array( - 'id' => $id, - 'phid' => $revision->getPHID(), - 'name' => $revision->getTitle(), - 'uri' => PhabricatorEnv::getProductionURI('/D'.$id), - 'dateCreated' => $revision->getDateCreated(), - 'authorPHID' => $revision->getAuthorPHID(), - 'statusName' => $revision->getStatusDisplayName(), - 'sourcePath' => $diff->getSourcePath(), - ); - } - - return $results; - } - -} diff --git a/src/applications/differential/conduit/DifferentialGetRevisionConduitAPIMethod.php b/src/applications/differential/conduit/DifferentialGetRevisionConduitAPIMethod.php index 9109849292..a8dd8c4d56 100644 --- a/src/applications/differential/conduit/DifferentialGetRevisionConduitAPIMethod.php +++ b/src/applications/differential/conduit/DifferentialGetRevisionConduitAPIMethod.php @@ -82,7 +82,7 @@ final class DifferentialGetRevisionConduitAPIMethod 'authorPHID' => $revision->getAuthorPHID(), 'uri' => PhabricatorEnv::getURI('/D'.$revision->getID()), 'title' => $revision->getTitle(), - 'status' => $revision->getStatus(), + 'status' => $revision->getLegacyRevisionStatus(), 'statusName' => $revision->getStatusDisplayName(), 'summary' => $revision->getSummary(), 'testPlan' => $revision->getTestPlan(), diff --git a/src/applications/differential/conduit/DifferentialQueryConduitAPIMethod.php b/src/applications/differential/conduit/DifferentialQueryConduitAPIMethod.php index 329a44c857..0b26db0655 100644 --- a/src/applications/differential/conduit/DifferentialQueryConduitAPIMethod.php +++ b/src/applications/differential/conduit/DifferentialQueryConduitAPIMethod.php @@ -150,7 +150,10 @@ final class DifferentialQueryConduitAPIMethod } if ($status) { - $query->withStatus($status); + $statuses = DifferentialLegacyQuery::getModernValues($status); + if ($statuses) { + $query->withStatuses($statuses); + } } if ($order) { $query->setOrder($order); @@ -215,7 +218,7 @@ final class DifferentialQueryConduitAPIMethod 'dateCreated' => $revision->getDateCreated(), 'dateModified' => $revision->getDateModified(), 'authorPHID' => $revision->getAuthorPHID(), - 'status' => $revision->getStatus(), + 'status' => $revision->getLegacyRevisionStatus(), 'statusName' => $revision->getStatusDisplayName(), 'properties' => $revision->getProperties(), 'branch' => $diff->getBranch(), diff --git a/src/applications/differential/constants/DifferentialLegacyQuery.php b/src/applications/differential/constants/DifferentialLegacyQuery.php index 4a20267f10..26d2c4aee2 100644 --- a/src/applications/differential/constants/DifferentialLegacyQuery.php +++ b/src/applications/differential/constants/DifferentialLegacyQuery.php @@ -15,7 +15,7 @@ final class DifferentialLegacyQuery return array_keys(self::getMap()); } - public static function getQueryValues($status) { + public static function getModernValues($status) { if ($status === self::STATUS_ANY) { return null; } @@ -28,18 +28,7 @@ final class DifferentialLegacyQuery $status)); } - $values = array(); - foreach ($map[$status] as $status_constant) { - $status_object = DifferentialRevisionStatus::newForStatus( - $status_constant); - - $legacy_key = $status_object->getLegacyKey(); - if ($legacy_key !== null) { - $values[] = $legacy_key; - } - } - - return $values; + return $map[$status]; } private static function getMap() { diff --git a/src/applications/differential/constants/DifferentialRevisionStatus.php b/src/applications/differential/constants/DifferentialRevisionStatus.php index 37b51f1b3b..b4b291f9c4 100644 --- a/src/applications/differential/constants/DifferentialRevisionStatus.php +++ b/src/applications/differential/constants/DifferentialRevisionStatus.php @@ -32,6 +32,18 @@ final class DifferentialRevisionStatus extends Phobject { return idx($this->spec, 'color.tag', 'bluegrey'); } + public function getTimelineIcon() { + return idx($this->spec, 'icon.timeline'); + } + + public function getTimelineColor() { + return idx($this->spec, 'color.timeline'); + } + + public function getANSIColor() { + return idx($this->spec, 'color.ansi'); + } + public function getDisplayName() { return idx($this->spec, 'name'); } @@ -52,6 +64,10 @@ final class DifferentialRevisionStatus extends Phobject { return ($this->key === self::NEEDS_REVIEW); } + public function isNeedsRevision() { + return ($this->key === self::NEEDS_REVISION); + } + public function isPublished() { return ($this->key === self::PUBLISHED); } @@ -72,22 +88,11 @@ final class DifferentialRevisionStatus extends Phobject { return $result; } - public static function newForLegacyStatus($legacy_status) { - $result = new self(); + public static function getAll() { + $result = array(); - $map = self::getMap(); - foreach ($map as $key => $spec) { - if (!isset($spec['legacy'])) { - continue; - } - - if ($spec['legacy'] != $legacy_status) { - continue; - } - - $result->key = $key; - $result->spec = $spec; - break; + foreach (self::getMap() as $key => $spec) { + $result[$key] = self::newForStatus($key); } return $result; @@ -102,19 +107,23 @@ final class DifferentialRevisionStatus extends Phobject { 'name' => pht('Needs Review'), 'legacy' => 0, 'icon' => 'fa-code', + 'icon.timeline' => 'fa-undo', 'closed' => false, 'color.icon' => 'grey', 'color.tag' => 'bluegrey', 'color.ansi' => 'magenta', + 'color.timeline' => 'orange', ), self::NEEDS_REVISION => array( 'name' => pht('Needs Revision'), 'legacy' => 1, 'icon' => 'fa-refresh', + 'icon.timeline' => 'fa-times', 'closed' => false, 'color.icon' => 'red', 'color.tag' => 'red', 'color.ansi' => 'red', + 'color.timeline' => 'red', ), self::CHANGES_PLANNED => array( 'name' => pht('Changes Planned'), @@ -129,10 +138,12 @@ final class DifferentialRevisionStatus extends Phobject { 'name' => pht('Accepted'), 'legacy' => 2, 'icon' => 'fa-check', + 'icon.timeline' => 'fa-check', 'closed' => $close_on_accept, 'color.icon' => 'green', 'color.tag' => 'green', 'color.ansi' => 'green', + 'color.timeline' => 'green', ), self::PUBLISHED => array( 'name' => pht('Closed'), diff --git a/src/applications/differential/controller/DifferentialDiffViewController.php b/src/applications/differential/controller/DifferentialDiffViewController.php index 488d2cec82..7340687bc4 100644 --- a/src/applications/differential/controller/DifferentialDiffViewController.php +++ b/src/applications/differential/controller/DifferentialDiffViewController.php @@ -177,7 +177,7 @@ final class DifferentialDiffViewController extends DifferentialController { $revisions = id(new DifferentialRevisionQuery()) ->setViewer($viewer) ->withAuthors(array($viewer->getPHID())) - ->withStatus(DifferentialLegacyQuery::STATUS_OPEN) + ->withIsOpen(true) ->requireCapabilities( array( PhabricatorPolicyCapability::CAN_VIEW, diff --git a/src/applications/differential/controller/DifferentialRevisionViewController.php b/src/applications/differential/controller/DifferentialRevisionViewController.php index 81b2aebda6..06d8cf0a3f 100644 --- a/src/applications/differential/controller/DifferentialRevisionViewController.php +++ b/src/applications/differential/controller/DifferentialRevisionViewController.php @@ -808,7 +808,7 @@ final class DifferentialRevisionViewController extends DifferentialController { $query = id(new DifferentialRevisionQuery()) ->setViewer($this->getRequest()->getUser()) - ->withStatus(DifferentialLegacyQuery::STATUS_OPEN) + ->withIsOpen(true) ->withUpdatedEpochBetween($recent, null) ->setOrder(DifferentialRevisionQuery::ORDER_MODIFIED) ->setLimit(10) diff --git a/src/applications/differential/editor/DifferentialTransactionEditor.php b/src/applications/differential/editor/DifferentialTransactionEditor.php index 9204a1aadb..af246feeb0 100644 --- a/src/applications/differential/editor/DifferentialTransactionEditor.php +++ b/src/applications/differential/editor/DifferentialTransactionEditor.php @@ -69,9 +69,7 @@ final class DifferentialTransactionEditor $types[] = PhabricatorTransactions::TYPE_EDIT_POLICY; $types[] = PhabricatorTransactions::TYPE_INLINESTATE; - $types[] = DifferentialTransaction::TYPE_ACTION; $types[] = DifferentialTransaction::TYPE_INLINE; - $types[] = DifferentialTransaction::TYPE_STATUS; $types[] = DifferentialTransaction::TYPE_UPDATE; return $types; @@ -82,8 +80,6 @@ final class DifferentialTransactionEditor PhabricatorApplicationTransaction $xaction) { switch ($xaction->getTransactionType()) { - case DifferentialTransaction::TYPE_ACTION: - return null; case DifferentialTransaction::TYPE_INLINE: return null; case DifferentialTransaction::TYPE_UPDATE: @@ -102,7 +98,6 @@ final class DifferentialTransactionEditor PhabricatorApplicationTransaction $xaction) { switch ($xaction->getTransactionType()) { - case DifferentialTransaction::TYPE_ACTION: case DifferentialTransaction::TYPE_UPDATE: return $xaction->getNewValue(); case DifferentialTransaction::TYPE_INLINE: @@ -121,28 +116,6 @@ final class DifferentialTransactionEditor switch ($xaction->getTransactionType()) { case DifferentialTransaction::TYPE_INLINE: return $xaction->hasComment(); - case DifferentialTransaction::TYPE_ACTION: - $status_closed = ArcanistDifferentialRevisionStatus::CLOSED; - $status_abandoned = ArcanistDifferentialRevisionStatus::ABANDONED; - $status_review = ArcanistDifferentialRevisionStatus::NEEDS_REVIEW; - $status_revision = ArcanistDifferentialRevisionStatus::NEEDS_REVISION; - $status_plan = ArcanistDifferentialRevisionStatus::CHANGES_PLANNED; - - $action_type = $xaction->getNewValue(); - switch ($action_type) { - case DifferentialAction::ACTION_CLOSE: - return ($object->getStatus() != $status_closed); - case DifferentialAction::ACTION_ABANDON: - return ($object->getStatus() != $status_abandoned); - case DifferentialAction::ACTION_RECLAIM: - return ($object->getStatus() == $status_abandoned); - case DifferentialAction::ACTION_REOPEN: - return ($object->getStatus() == $status_closed); - case DifferentialAction::ACTION_RETHINK: - return ($object->getStatus() != $status_plan); - case DifferentialAction::ACTION_CLAIM: - return ($actor_phid != $object->getAuthorPHID()); - } } return parent::transactionHasEffect($object, $xaction); @@ -152,23 +125,16 @@ final class DifferentialTransactionEditor PhabricatorLiskDAO $object, PhabricatorApplicationTransaction $xaction) { - $status_review = ArcanistDifferentialRevisionStatus::NEEDS_REVIEW; - $status_revision = ArcanistDifferentialRevisionStatus::NEEDS_REVISION; - $status_plan = ArcanistDifferentialRevisionStatus::CHANGES_PLANNED; - $status_abandoned = ArcanistDifferentialRevisionStatus::ABANDONED; - $status_accepted = ArcanistDifferentialRevisionStatus::ACCEPTED; - switch ($xaction->getTransactionType()) { case DifferentialTransaction::TYPE_INLINE: return; case DifferentialTransaction::TYPE_UPDATE: if (!$this->getIsCloseByCommit()) { - switch ($object->getStatus()) { - case $status_revision: - case $status_plan: - case $status_abandoned: - $object->setStatus($status_review); - break; + if ($object->isNeedsRevision() || + $object->isChangePlanned() || + $object->isAbandoned()) { + $object->setModernRevisionStatus( + DifferentialRevisionStatus::NEEDS_REVIEW); } } @@ -184,38 +150,6 @@ final class DifferentialTransactionEditor // TODO: Update the `diffPHID` once we add that. return; - case DifferentialTransaction::TYPE_ACTION: - switch ($xaction->getNewValue()) { - case DifferentialAction::ACTION_ABANDON: - $object->setStatus(ArcanistDifferentialRevisionStatus::ABANDONED); - return; - case DifferentialAction::ACTION_RETHINK: - $object->setStatus($status_plan); - return; - case DifferentialAction::ACTION_RECLAIM: - $object->setStatus($status_review); - return; - case DifferentialAction::ACTION_REOPEN: - $object->setStatus($status_review); - return; - case DifferentialAction::ACTION_CLOSE: - $old_status = $object->getStatus(); - $object->setStatus(ArcanistDifferentialRevisionStatus::CLOSED); - $was_accepted = ($old_status == $status_accepted); - $object->setProperty( - DifferentialRevision::PROPERTY_CLOSED_FROM_ACCEPTED, - $was_accepted); - return; - case DifferentialAction::ACTION_CLAIM: - $object->setAuthorPHID($this->getActingAsPHID()); - return; - default: - throw new Exception( - pht( - 'Differential action "%s" is not a valid action!', - $xaction->getNewValue())); - } - break; } return parent::applyCustomInternalTransaction($object, $xaction); @@ -255,9 +189,6 @@ final class DifferentialTransactionEditor $actor_phid = $this->getActingAsPHID(); $type_edge = PhabricatorTransactions::TYPE_EDGE; - $status_plan = ArcanistDifferentialRevisionStatus::CHANGES_PLANNED; - - $edge_reviewer = DifferentialRevisionHasReviewerEdgeType::EDGECONST; $edge_ref_task = DifferentialRevisionHasTaskEdgeType::EDGECONST; $is_sticky_accept = PhabricatorEnv::getEnvConfig( @@ -280,7 +211,7 @@ final class DifferentialTransactionEditor case DifferentialRevisionRequestReviewTransaction::TRANSACTIONTYPE: $downgrade_rejects = true; if ((!$is_sticky_accept) || - ($object->getStatus() != $status_plan)) { + (!$object->isChangePlanned())) { // If the old state isn't "changes planned", downgrade the accepts. // This exception allows an accepted revision to go through // "Plan Changes" -> "Request Review" and return to "accepted" if @@ -297,48 +228,6 @@ final class DifferentialTransactionEditor $old_accept = DifferentialReviewerStatus::STATUS_ACCEPTED_OLDER; $old_reject = DifferentialReviewerStatus::STATUS_REJECTED_OLDER; - if ($downgrade_rejects || $downgrade_accepts) { - // When a revision is updated, change all "reject" to "rejected older - // revision". This means we won't immediately push the update back into - // "needs review", but outstanding rejects will still block it from - // moving to "accepted". - - // We also do this for "Request Review", even though the diff is not - // updated directly. Essentially, this acts like an update which doesn't - // actually change the diff text. - - $edits = array(); - foreach ($object->getReviewers() as $reviewer) { - if ($downgrade_rejects) { - if ($reviewer->getReviewerStatus() == $new_reject) { - $edits[$reviewer->getReviewerPHID()] = array( - 'data' => array( - 'status' => $old_reject, - ), - ); - } - } - - if ($downgrade_accepts) { - if ($reviewer->getReviewerStatus() == $new_accept) { - $edits[$reviewer->getReviewerPHID()] = array( - 'data' => array( - 'status' => $old_accept, - ), - ); - } - } - } - - if ($edits) { - $results[] = id(new DifferentialTransaction()) - ->setTransactionType($type_edge) - ->setMetadataValue('edge:type', $edge_reviewer) - ->setIgnoreOnNoEffect(true) - ->setNewValue(array('+' => $edits)); - } - } - $downgrade = array(); if ($downgrade_accepts) { $downgrade[] = DifferentialReviewerStatus::STATUS_ACCEPTED; @@ -397,56 +286,9 @@ final class DifferentialTransactionEditor } break; - case PhabricatorTransactions::TYPE_COMMENT: - // When a user leaves a comment, upgrade their reviewer status from - // "added" to "commented" if they're also a reviewer. We may further - // upgrade this based on other actions in the transaction group. - - if ($this->hasReviewTransaction) { - // If we're also applying a review transaction, skip this. - break; - } - - $status_added = DifferentialReviewerStatus::STATUS_ADDED; - $status_commented = DifferentialReviewerStatus::STATUS_COMMENTED; - - $data = array( - 'status' => $status_commented, - ); - - $edits = array(); - foreach ($object->getReviewers() as $reviewer) { - if ($reviewer->getReviewerPHID() == $actor_phid) { - if ($reviewer->getReviewerStatus() == $status_added) { - $edits[$actor_phid] = array( - 'data' => $data, - ); - } - } - } - - if ($edits) { - $results[] = id(new DifferentialTransaction()) - ->setTransactionType($type_edge) - ->setMetadataValue('edge:type', $edge_reviewer) - ->setIgnoreOnNoEffect(true) - ->setNewValue(array('+' => $edits)); - } - break; - case DifferentialRevisionCommandeerTransaction::TRANSACTIONTYPE: $is_commandeer = true; break; - - case DifferentialTransaction::TYPE_ACTION: - $action_type = $xaction->getNewValue(); - - switch ($action_type) { - case DifferentialAction::ACTION_CLAIM: - $is_commandeer = true; - break; - } - break; } if ($is_commandeer) { @@ -456,7 +298,6 @@ final class DifferentialTransactionEditor if (!$this->didExpandInlineState) { switch ($xaction->getTransactionType()) { case PhabricatorTransactions::TYPE_COMMENT: - case DifferentialTransaction::TYPE_ACTION: case DifferentialTransaction::TYPE_UPDATE: case DifferentialTransaction::TYPE_INLINE: $this->didExpandInlineState = true; @@ -502,8 +343,6 @@ final class DifferentialTransactionEditor PhabricatorApplicationTransaction $xaction) { switch ($xaction->getTransactionType()) { - case DifferentialTransaction::TYPE_ACTION: - return; case DifferentialTransaction::TYPE_INLINE: $reply = $xaction->getComment()->getReplyToComment(); if ($reply && !$reply->getHasReplies()) { @@ -575,33 +414,6 @@ final class DifferentialTransactionEditor return parent::applyBuiltinExternalTransaction($object, $xaction); } - protected function mergeEdgeData($type, array $u, array $v) { - $result = parent::mergeEdgeData($type, $u, $v); - - switch ($type) { - case DifferentialRevisionHasReviewerEdgeType::EDGECONST: - // When the same reviewer has their status updated by multiple - // transactions, we want the strongest status to win. An example of - // this is when a user adds a comment and also accepts a revision which - // they are a reviewer on. The comment creates a "commented" status, - // while the accept creates an "accepted" status. Since accept is - // stronger, it should win and persist. - - $u_status = idx($u, 'status'); - $v_status = idx($v, 'status'); - $u_str = DifferentialReviewerStatus::getStatusStrength($u_status); - $v_str = DifferentialReviewerStatus::getStatusStrength($v_status); - if ($u_str > $v_str) { - $result['status'] = $u_status; - } else { - $result['status'] = $v_status; - } - break; - } - - return $result; - } - protected function applyFinalEffects( PhabricatorLiskDAO $object, array $xactions) { @@ -641,101 +453,112 @@ final class DifferentialTransactionEditor } } - $status_accepted = ArcanistDifferentialRevisionStatus::ACCEPTED; - $status_revision = ArcanistDifferentialRevisionStatus::NEEDS_REVISION; - $status_review = ArcanistDifferentialRevisionStatus::NEEDS_REVIEW; - - $is_sticky_accept = PhabricatorEnv::getEnvConfig( - 'differential.sticky-accept'); - - $old_status = $object->getStatus(); - $active_diff = $object->getActiveDiff(); - switch ($old_status) { - case $status_accepted: - case $status_revision: - case $status_review: - // Try to move a revision to "accepted". We look for: - // - // - at least one accepting reviewer who is a user; and - // - no rejects; and - // - no rejects of older diffs; and - // - no blocking reviewers. - - $has_accepting_user = false; - $has_rejecting_reviewer = false; - $has_rejecting_older_reviewer = false; - $has_blocking_reviewer = false; - foreach ($object->getReviewers() as $reviewer) { - $reviewer_status = $reviewer->getReviewerStatus(); - switch ($reviewer_status) { - case DifferentialReviewerStatus::STATUS_REJECTED: - $active_phid = $active_diff->getPHID(); - if ($reviewer->isRejected($active_phid)) { - $has_rejecting_reviewer = true; - } else { - $has_rejecting_older_reviewer = true; - } - break; - case DifferentialReviewerStatus::STATUS_REJECTED_OLDER: - $has_rejecting_older_reviewer = true; - break; - case DifferentialReviewerStatus::STATUS_BLOCKING: - $has_blocking_reviewer = true; - break; - case DifferentialReviewerStatus::STATUS_ACCEPTED: - if ($reviewer->isUser()) { - $active_phid = $active_diff->getPHID(); - if ($reviewer->isAccepted($active_phid)) { - $has_accepting_user = true; - } - } - break; - } - } - - $new_status = null; - if ($has_accepting_user && - !$has_rejecting_reviewer && - !$has_rejecting_older_reviewer && - !$has_blocking_reviewer) { - $new_status = $status_accepted; - } else if ($has_rejecting_reviewer) { - // This isn't accepted, and there's at least one rejecting reviewer, - // so the revision needs changes. This usually happens after a - // "reject". - $new_status = $status_revision; - } else if ($old_status == $status_accepted) { - // This revision was accepted, but it no longer satisfies the - // conditions for acceptance. This usually happens after an accepting - // reviewer resigns or is removed. - $new_status = $status_review; - } - - if ($new_status !== null && ($new_status != $old_status)) { - $xaction = id(new DifferentialTransaction()) - ->setTransactionType(DifferentialTransaction::TYPE_STATUS) - ->setOldValue($old_status) - ->setNewValue($new_status); - - $xaction = $this->populateTransaction($object, $xaction)->save(); - - $xactions[] = $xaction; - - $object->setStatus($new_status)->save(); - } - break; - default: - // Revisions can't transition out of other statuses (like closed or - // abandoned) as a side effect of reviewer status changes. - break; - } - - + $xactions = $this->updateReviewStatus($object, $xactions); $this->markReviewerComments($object, $xactions); return $xactions; } + private function updateReviewStatus( + DifferentialRevision $revision, + array $xactions) { + + $was_accepted = $revision->isAccepted(); + $was_revision = $revision->isNeedsRevision(); + $was_review = $revision->isNeedsReview(); + if (!$was_accepted && !$was_revision && !$was_review) { + // Revisions can't transition out of other statuses (like closed or + // abandoned) as a side effect of reviewer status changes. + return $xactions; + } + + // Try to move a revision to "accepted". We look for: + // + // - at least one accepting reviewer who is a user; and + // - no rejects; and + // - no rejects of older diffs; and + // - no blocking reviewers. + + $has_accepting_user = false; + $has_rejecting_reviewer = false; + $has_rejecting_older_reviewer = false; + $has_blocking_reviewer = false; + + $active_diff = $revision->getActiveDiff(); + foreach ($revision->getReviewers() as $reviewer) { + $reviewer_status = $reviewer->getReviewerStatus(); + switch ($reviewer_status) { + case DifferentialReviewerStatus::STATUS_REJECTED: + $active_phid = $active_diff->getPHID(); + if ($reviewer->isRejected($active_phid)) { + $has_rejecting_reviewer = true; + } else { + $has_rejecting_older_reviewer = true; + } + break; + case DifferentialReviewerStatus::STATUS_REJECTED_OLDER: + $has_rejecting_older_reviewer = true; + break; + case DifferentialReviewerStatus::STATUS_BLOCKING: + $has_blocking_reviewer = true; + break; + case DifferentialReviewerStatus::STATUS_ACCEPTED: + if ($reviewer->isUser()) { + $active_phid = $active_diff->getPHID(); + if ($reviewer->isAccepted($active_phid)) { + $has_accepting_user = true; + } + } + break; + } + } + + $new_status = null; + if ($has_accepting_user && + !$has_rejecting_reviewer && + !$has_rejecting_older_reviewer && + !$has_blocking_reviewer) { + $new_status = DifferentialRevisionStatus::ACCEPTED; + } else if ($has_rejecting_reviewer) { + // This isn't accepted, and there's at least one rejecting reviewer, + // so the revision needs changes. This usually happens after a + // "reject". + $new_status = DifferentialRevisionStatus::NEEDS_REVISION; + } else if ($was_accepted) { + // This revision was accepted, but it no longer satisfies the + // conditions for acceptance. This usually happens after an accepting + // reviewer resigns or is removed. + $new_status = DifferentialRevisionStatus::NEEDS_REVIEW; + } + + if ($new_status === null) { + return $xactions; + } + + $old_status = $revision->getModernRevisionStatus(); + if ($new_status == $old_status) { + return $xactions; + } + + $xaction = id(new DifferentialTransaction()) + ->setTransactionType( + DifferentialRevisionStatusTransaction::TRANSACTIONTYPE) + ->setOldValue($old_status) + ->setNewValue($new_status); + + $xaction = $this->populateTransaction($revision, $xaction) + ->save(); + $xactions[] = $xaction; + + // Save the status adjustment we made earlier. + $revision + ->setModernRevisionStatus($new_status) + ->save(); + + return $xactions; + } + + protected function validateTransaction( PhabricatorLiskDAO $object, $type, @@ -748,40 +571,6 @@ final class DifferentialTransactionEditor foreach ($xactions as $xaction) { switch ($type) { - case PhabricatorTransactions::TYPE_EDGE: - switch ($xaction->getMetadataValue('edge:type')) { - case DifferentialRevisionHasReviewerEdgeType::EDGECONST: - - // Prevent the author from becoming a reviewer. - - // NOTE: This is pretty gross, but this restriction is unusual. - // If we end up with too much more of this, we should try to clean - // this up -- maybe by moving validation to after transactions - // are adjusted (so we can just examine the final value) or adding - // a second phase there? - - $author_phid = $object->getAuthorPHID(); - $new = $xaction->getNewValue(); - - $add = idx($new, '+', array()); - $eq = idx($new, '=', array()); - $phids = array_keys($add + $eq); - - foreach ($phids as $phid) { - if (($phid == $author_phid) && - !$allow_self_accept && - !$xaction->getIsCommandeerSideEffect()) { - $errors[] = - new PhabricatorApplicationTransactionValidationError( - $type, - pht('Invalid'), - pht('The author of a revision can not be a reviewer.'), - $xaction); - } - } - break; - } - break; case DifferentialTransaction::TYPE_UPDATE: $diff = $this->loadDiff($xaction->getNewValue()); if (!$diff) { @@ -801,172 +590,12 @@ final class DifferentialTransactionEditor $xaction); } break; - case DifferentialTransaction::TYPE_ACTION: - $error = $this->validateDifferentialAction( - $object, - $type, - $xaction, - $xaction->getNewValue()); - if ($error) { - $errors[] = new PhabricatorApplicationTransactionValidationError( - $type, - pht('Invalid'), - $error, - $xaction); - } - break; } } return $errors; } - private function validateDifferentialAction( - DifferentialRevision $revision, - $type, - DifferentialTransaction $xaction, - $action) { - - $author_phid = $revision->getAuthorPHID(); - $actor_phid = $this->getActingAsPHID(); - $actor_is_author = ($author_phid == $actor_phid); - - $config_abandon_key = 'differential.always-allow-abandon'; - $always_allow_abandon = PhabricatorEnv::getEnvConfig($config_abandon_key); - - $config_close_key = 'differential.always-allow-close'; - $always_allow_close = PhabricatorEnv::getEnvConfig($config_close_key); - - $config_reopen_key = 'differential.allow-reopen'; - $allow_reopen = PhabricatorEnv::getEnvConfig($config_reopen_key); - - $config_self_accept_key = 'differential.allow-self-accept'; - $allow_self_accept = PhabricatorEnv::getEnvConfig($config_self_accept_key); - - $revision_status = $revision->getStatus(); - - $status_accepted = ArcanistDifferentialRevisionStatus::ACCEPTED; - $status_abandoned = ArcanistDifferentialRevisionStatus::ABANDONED; - $status_closed = ArcanistDifferentialRevisionStatus::CLOSED; - - switch ($action) { - case DifferentialAction::ACTION_CLAIM: - // You can claim a revision if you're not the owner. If you are, this - // is a no-op rather than invalid. - - if ($revision_status == $status_closed) { - return pht( - 'You can not commandeer this revision because it has already been '. - 'closed.'); - } - break; - - case DifferentialAction::ACTION_ABANDON: - if (!$actor_is_author && !$always_allow_abandon) { - return pht( - 'You can not abandon this revision because you do not own it. '. - 'You can only abandon revisions you own.'); - } - - if ($revision_status == $status_closed) { - return pht( - 'You can not abandon this revision because it has already been '. - 'closed.'); - } - - // NOTE: Abandons of already-abandoned revisions are treated as no-op - // instead of invalid. Other abandons are OK. - - break; - - case DifferentialAction::ACTION_RECLAIM: - if (!$actor_is_author) { - return pht( - 'You can not reclaim this revision because you do not own '. - 'it. You can only reclaim revisions you own.'); - } - - if ($revision_status == $status_closed) { - return pht( - 'You can not reclaim this revision because it has already been '. - 'closed.'); - } - - // NOTE: Reclaims of other non-abandoned revisions are treated as no-op - // instead of invalid. - - break; - - case DifferentialAction::ACTION_REOPEN: - if (!$allow_reopen) { - return pht( - 'The reopen action is not enabled on this Phabricator install. '. - 'Adjust your configuration to enable it.'); - } - - // NOTE: If the revision is not closed, this is caught as a no-op - // instead of an invalid transaction. - - break; - - case DifferentialAction::ACTION_RETHINK: - if (!$actor_is_author) { - return pht( - 'You can not plan changes to this revision because you do not '. - 'own it. To plan changes to a revision, you must be its owner.'); - } - - switch ($revision_status) { - case ArcanistDifferentialRevisionStatus::ACCEPTED: - case ArcanistDifferentialRevisionStatus::NEEDS_REVISION: - case ArcanistDifferentialRevisionStatus::NEEDS_REVIEW: - // These are OK. - break; - case ArcanistDifferentialRevisionStatus::CHANGES_PLANNED: - // Let this through, it's a no-op. - break; - case ArcanistDifferentialRevisionStatus::ABANDONED: - return pht( - 'You can not plan changes to this revision because it has '. - 'been abandoned.'); - case ArcanistDifferentialRevisionStatus::CLOSED: - return pht( - 'You can not plan changes to this revision because it has '. - 'already been closed.'); - default: - throw new Exception( - pht( - 'Encountered unexpected revision status ("%s") when '. - 'validating "%s" action.', - $revision_status, - $action)); - } - break; - - case DifferentialAction::ACTION_CLOSE: - // We force revisions closed when we discover a corresponding commit. - // In this case, revisions are allowed to transition to closed from - // any state. This is an automated action taken by the daemons. - - if (!$this->getIsCloseByCommit()) { - if (!$actor_is_author && !$always_allow_close) { - return pht( - 'You can not close this revision because you do not own it. To '. - 'close a revision, you must be its owner.'); - } - - if ($revision_status != $status_accepted) { - return pht( - 'You can not close this revision because it has not been '. - 'accepted. You can only close accepted revisions.'); - } - } - break; - } - - return null; - } - protected function sortTransactions(array $xactions) { $xactions = parent::sortTransactions($xactions); @@ -1374,14 +1003,6 @@ final class DifferentialTransactionEditor // When users commandeer revisions, we may need to trigger // signatures or author-based rules. return true; - case DifferentialTransaction::TYPE_ACTION: - switch ($xaction->getNewValue()) { - case DifferentialAction::ACTION_CLAIM: - // When users commandeer revisions, we may need to trigger - // signatures or author-based rules. - return true; - } - break; } } diff --git a/src/applications/differential/phid/DifferentialRevisionPHIDType.php b/src/applications/differential/phid/DifferentialRevisionPHIDType.php index d652a8c056..a117690d66 100644 --- a/src/applications/differential/phid/DifferentialRevisionPHIDType.php +++ b/src/applications/differential/phid/DifferentialRevisionPHIDType.php @@ -33,7 +33,6 @@ final class DifferentialRevisionPHIDType extends PhabricatorPHIDType { $revision = $objects[$phid]; $title = $revision->getTitle(); - $status = $revision->getStatus(); $monogram = $revision->getMonogram(); $uri = $revision->getURI(); @@ -46,10 +45,8 @@ final class DifferentialRevisionPHIDType extends PhabricatorPHIDType { $handle->setStatus(PhabricatorObjectHandle::STATUS_CLOSED); } - $status = $revision->getStatus(); - - $icon = $revision->getStatusIcon($status); - $color = $revision->getStatusIconColor($status); + $icon = $revision->getStatusIcon(); + $color = $revision->getStatusIconColor(); $name = $revision->getStatusDisplayName(); $handle diff --git a/src/applications/differential/query/DifferentialRevisionQuery.php b/src/applications/differential/query/DifferentialRevisionQuery.php index f53ec46a9f..8e6e23776a 100644 --- a/src/applications/differential/query/DifferentialRevisionQuery.php +++ b/src/applications/differential/query/DifferentialRevisionQuery.php @@ -10,8 +10,6 @@ final class DifferentialRevisionQuery private $pathIDs = array(); - private $status = 'status-any'; - private $authors = array(); private $draftAuthors = array(); private $ccs = array(); @@ -25,6 +23,8 @@ final class DifferentialRevisionQuery private $repositoryPHIDs; private $updatedEpochMin; private $updatedEpochMax; + private $statuses; + private $isOpen; const ORDER_MODIFIED = 'order-modified'; const ORDER_CREATED = 'order-created'; @@ -133,16 +133,13 @@ final class DifferentialRevisionQuery return $this; } - /** - * Filter results to revisions with a given status. Provide a class constant, - * such as `DifferentialLegacyQuery::STATUS_OPEN`. - * - * @param const Class STATUS constant, like STATUS_OPEN. - * @return this - * @task config - */ - public function withStatus($status_constant) { - $this->status = $status_constant; + public function withStatuses(array $statuses) { + $this->statuses = $statuses; + return $this; + } + + public function withIsOpen($is_open) { + $this->isOpen = $is_open; return $this; } @@ -694,14 +691,24 @@ final class DifferentialRevisionQuery $this->updatedEpochMax); } - // NOTE: Although the status constants are integers in PHP, the column is a - // string column in MySQL, and MySQL won't use keys on string columns if - // you put integers in the query. - $statuses = DifferentialLegacyQuery::getQueryValues($this->status); - if ($statuses !== null) { + if ($this->statuses !== null) { $where[] = qsprintf( $conn_r, - 'r.status IN (%Ls)', + 'r.status in (%Ls)', + $this->statuses); + } + + if ($this->isOpen !== null) { + if ($this->isOpen) { + $statuses = DifferentialLegacyQuery::getModernValues( + DifferentialLegacyQuery::STATUS_OPEN); + } else { + $statuses = DifferentialLegacyQuery::getModernValues( + DifferentialLegacyQuery::STATUS_CLOSED); + } + $where[] = qsprintf( + $conn_r, + 'r.status in (%Ls)', $statuses); } diff --git a/src/applications/differential/query/DifferentialRevisionRequiredActionResultBucket.php b/src/applications/differential/query/DifferentialRevisionRequiredActionResultBucket.php index 3e6daa6317..195a430b1c 100644 --- a/src/applications/differential/query/DifferentialRevisionRequiredActionResultBucket.php +++ b/src/applications/differential/query/DifferentialRevisionRequiredActionResultBucket.php @@ -151,9 +151,8 @@ final class DifferentialRevisionRequiredActionResultBucket private function filterShouldUpdate(array $phids) { $statuses = array( - ArcanistDifferentialRevisionStatus::NEEDS_REVISION, - ArcanistDifferentialRevisionStatus::CHANGES_PLANNED, - ArcanistDifferentialRevisionStatus::IN_PREPARATION, + DifferentialRevisionStatus::NEEDS_REVISION, + DifferentialRevisionStatus::CHANGES_PLANNED, ); $statuses = array_fuse($statuses); @@ -161,7 +160,7 @@ final class DifferentialRevisionRequiredActionResultBucket $results = array(); foreach ($objects as $key => $object) { - if (empty($statuses[$object->getStatus()])) { + if (empty($statuses[$object->getModernRevisionStatus()])) { continue; } @@ -190,10 +189,9 @@ final class DifferentialRevisionRequiredActionResultBucket private function filterWaitingOnAuthors(array $phids) { $statuses = array( - ArcanistDifferentialRevisionStatus::ACCEPTED, - ArcanistDifferentialRevisionStatus::NEEDS_REVISION, - ArcanistDifferentialRevisionStatus::CHANGES_PLANNED, - ArcanistDifferentialRevisionStatus::IN_PREPARATION, + DifferentialRevisionStatus::ACCEPTED, + DifferentialRevisionStatus::NEEDS_REVISION, + DifferentialRevisionStatus::CHANGES_PLANNED, ); $statuses = array_fuse($statuses); @@ -201,7 +199,7 @@ final class DifferentialRevisionRequiredActionResultBucket $results = array(); foreach ($objects as $key => $object) { - if (empty($statuses[$object->getStatus()])) { + if (empty($statuses[$object->getModernRevisionStatus()])) { continue; } diff --git a/src/applications/differential/query/DifferentialRevisionSearchEngine.php b/src/applications/differential/query/DifferentialRevisionSearchEngine.php index 8cb8ffd2fb..e2f4bfc421 100644 --- a/src/applications/differential/query/DifferentialRevisionSearchEngine.php +++ b/src/applications/differential/query/DifferentialRevisionSearchEngine.php @@ -41,8 +41,8 @@ final class DifferentialRevisionSearchEngine $query->withRepositoryPHIDs($map['repositoryPHIDs']); } - if ($map['status']) { - $query->withStatus($map['status']); + if ($map['statuses']) { + $query->withStatuses($map['statuses']); } return $query; @@ -77,10 +77,11 @@ final class DifferentialRevisionSearchEngine ->setDatasource(new DiffusionRepositoryFunctionDatasource()) ->setDescription( pht('Find revisions from specific repositories.')), - id(new PhabricatorSearchSelectField()) - ->setLabel(pht('Status')) - ->setKey('status') - ->setOptions($this->getStatusOptions()) + id(new PhabricatorSearchDatasourceField()) + ->setLabel(pht('Statuses')) + ->setKey('statuses') + ->setAliases(array('status')) + ->setDatasource(new DifferentialRevisionStatusFunctionDatasource()) ->setDescription( pht('Find revisions with particular statuses.')), ); @@ -115,7 +116,7 @@ final class DifferentialRevisionSearchEngine return $query ->setParameter('responsiblePHIDs', array($viewer->getPHID())) - ->setParameter('status', DifferentialLegacyQuery::STATUS_OPEN) + ->setParameter('statuses', array('open()')) ->setParameter('bucket', $bucket_key); case 'authored': return $query @@ -267,7 +268,7 @@ final class DifferentialRevisionSearchEngine $blocking_revisions = id(new DifferentialRevisionQuery()) ->setViewer($viewer) ->withPHIDs($revision_phids) - ->withStatus(DifferentialLegacyQuery::STATUS_OPEN) + ->withIsOpen(true) ->execute(); $blocking_revisions = mpull($blocking_revisions, null, 'getPHID'); diff --git a/src/applications/differential/storage/DifferentialRevision.php b/src/applications/differential/storage/DifferentialRevision.php index a6ce99080a..eaf856fd80 100644 --- a/src/applications/differential/storage/DifferentialRevision.php +++ b/src/applications/differential/storage/DifferentialRevision.php @@ -72,7 +72,7 @@ final class DifferentialRevision extends DifferentialDAO ->attachRepository(null) ->attachActiveDiff(null) ->attachReviewers(array()) - ->setStatus(ArcanistDifferentialRevisionStatus::NEEDS_REVIEW); + ->setModernRevisionStatus(DifferentialRevisionStatus::NEEDS_REVIEW); } protected function getConfiguration() { @@ -612,6 +612,18 @@ final class DifferentialRevision extends DifferentialDAO return $this; } + public function setModernRevisionStatus($status) { + return $this->setStatus($status); + } + + public function getModernRevisionStatus() { + return $this->getStatus(); + } + + public function getLegacyRevisionStatus() { + return $this->getStatusObject()->getLegacyKey(); + } + public function isClosed() { return $this->getStatusObject()->isClosedStatus(); } @@ -628,6 +640,10 @@ final class DifferentialRevision extends DifferentialDAO return $this->getStatusObject()->isNeedsReview(); } + public function isNeedsRevision() { + return $this->getStatusObject()->isNeedsRevision(); + } + public function isChangePlanned() { return $this->getStatusObject()->isChangePlanned(); } @@ -650,7 +666,7 @@ final class DifferentialRevision extends DifferentialDAO public function getStatusObject() { $status = $this->getStatus(); - return DifferentialRevisionStatus::newForLegacyStatus($status); + return DifferentialRevisionStatus::newForStatus($status); } public function getFlag(PhabricatorUser $viewer) { @@ -897,13 +913,26 @@ final class DifferentialRevision extends DifferentialDAO ->setKey('authorPHID') ->setType('phid') ->setDescription(pht('Revision author PHID.')), + id(new PhabricatorConduitSearchFieldSpecification()) + ->setKey('status') + ->setType('map') + ->setDescription(pht('Information about revision status.')), ); } public function getFieldValuesForConduit() { + $status = $this->getStatusObject(); + $status_info = array( + 'value' => $status->getKey(), + 'name' => $status->getDisplayName(), + 'closed' => $status->isClosedStatus(), + 'color.ansi' => $status->getANSIColor(), + ); + return array( 'title' => $this->getTitle(), 'authorPHID' => $this->getAuthorPHID(), + 'status' => $status_info, ); } diff --git a/src/applications/differential/storage/DifferentialTransaction.php b/src/applications/differential/storage/DifferentialTransaction.php index c6c55e0cdd..1cd1f2b062 100644 --- a/src/applications/differential/storage/DifferentialTransaction.php +++ b/src/applications/differential/storage/DifferentialTransaction.php @@ -8,7 +8,6 @@ final class DifferentialTransaction const TYPE_INLINE = 'differential:inline'; const TYPE_UPDATE = 'differential:update'; const TYPE_ACTION = 'differential:action'; - const TYPE_STATUS = 'differential:status'; const MAILTAG_REVIEWERS = 'differential-reviewers'; const MAILTAG_CLOSED = 'differential-committed'; @@ -305,15 +304,6 @@ final class DifferentialTransaction return DifferentialAction::getBasicStoryText($new, $author_handle); } break; - case self::TYPE_STATUS: - switch ($this->getNewValue()) { - case ArcanistDifferentialRevisionStatus::ACCEPTED: - return pht('This revision is now accepted and ready to land.'); - case ArcanistDifferentialRevisionStatus::NEEDS_REVISION: - return pht('This revision now requires changes to proceed.'); - case ArcanistDifferentialRevisionStatus::NEEDS_REVIEW: - return pht('This revision now requires review to proceed.'); - } } return parent::getTitle(); @@ -457,21 +447,6 @@ final class DifferentialTransaction $object_link); } break; - case self::TYPE_STATUS: - switch ($this->getNewValue()) { - case ArcanistDifferentialRevisionStatus::ACCEPTED: - return pht( - '%s is now accepted and ready to land.', - $object_link); - case ArcanistDifferentialRevisionStatus::NEEDS_REVISION: - return pht( - '%s now requires changes to proceed.', - $object_link); - case ArcanistDifferentialRevisionStatus::NEEDS_REVIEW: - return pht( - '%s now requires review to proceed.', - $object_link); - } } return parent::getTitleForFeed(); @@ -483,16 +458,6 @@ final class DifferentialTransaction return 'fa-comment'; case self::TYPE_UPDATE: return 'fa-refresh'; - case self::TYPE_STATUS: - switch ($this->getNewValue()) { - case ArcanistDifferentialRevisionStatus::ACCEPTED: - return 'fa-check'; - case ArcanistDifferentialRevisionStatus::NEEDS_REVISION: - return 'fa-times'; - case ArcanistDifferentialRevisionStatus::NEEDS_REVIEW: - return 'fa-undo'; - } - break; case self::TYPE_ACTION: switch ($this->getNewValue()) { case DifferentialAction::ACTION_CLOSE: @@ -530,14 +495,12 @@ final class DifferentialTransaction // Never group status changes with other types of actions, they're indirect // and don't make sense when combined with direct actions. - $type_status = self::TYPE_STATUS; - - if ($this->getTransactionType() == $type_status) { + if ($this->isStatusTransaction($this)) { return false; } foreach ($group as $xaction) { - if ($xaction->getTransactionType() == $type_status) { + if ($this->isStatusTransaction($xaction)) { return false; } } @@ -545,21 +508,20 @@ final class DifferentialTransaction return parent::shouldDisplayGroupWith($group); } + private function isStatusTransaction($xaction) { + $status_type = DifferentialRevisionStatusTransaction::TRANSACTIONTYPE; + if ($xaction->getTransactionType() == $status_type) { + return true; + } + + return false; + } + public function getColor() { switch ($this->getTransactionType()) { case self::TYPE_UPDATE: return PhabricatorTransactions::COLOR_SKY; - case self::TYPE_STATUS: - switch ($this->getNewValue()) { - case ArcanistDifferentialRevisionStatus::ACCEPTED: - return PhabricatorTransactions::COLOR_GREEN; - case ArcanistDifferentialRevisionStatus::NEEDS_REVISION: - return PhabricatorTransactions::COLOR_RED; - case ArcanistDifferentialRevisionStatus::NEEDS_REVIEW: - return PhabricatorTransactions::COLOR_ORANGE; - } - break; case self::TYPE_ACTION: switch ($this->getNewValue()) { case DifferentialAction::ACTION_CLOSE: diff --git a/src/applications/differential/typeahead/DifferentialRevisionClosedStatusDatasource.php b/src/applications/differential/typeahead/DifferentialRevisionClosedStatusDatasource.php new file mode 100644 index 0000000000..8487111452 --- /dev/null +++ b/src/applications/differential/typeahead/DifferentialRevisionClosedStatusDatasource.php @@ -0,0 +1,74 @@ + array( + 'name' => pht('Any Closed Status'), + 'summary' => pht('Find results with any closed status.'), + 'description' => pht( + 'This function includes results which have any closed status.'), + ), + ); + } + + public function loadResults() { + $results = array( + $this->buildClosedResult(), + ); + return $this->filterResultsAgainstTokens($results); + } + + protected function evaluateFunction($function, array $argv_list) { + $results = array(); + + $map = DifferentialRevisionStatus::getAll(); + foreach ($argv_list as $argv) { + foreach ($map as $status) { + if ($status->isClosedStatus()) { + $results[] = $status->getKey(); + } + } + } + + return $results; + } + + public function renderFunctionTokens($function, array $argv_list) { + $results = array(); + + foreach ($argv_list as $argv) { + $results[] = PhabricatorTypeaheadTokenView::newFromTypeaheadResult( + $this->buildClosedResult()); + } + + return $results; + } + + private function buildClosedResult() { + $name = pht('Any Closed Status'); + return $this->newFunctionResult() + ->setName($name.' closed') + ->setDisplayName($name) + ->setPHID(self::FUNCTION_TOKEN) + ->setUnique(true) + ->addAttribute(pht('Select any closed status.')); + } + +} diff --git a/src/applications/differential/typeahead/DifferentialRevisionOpenStatusDatasource.php b/src/applications/differential/typeahead/DifferentialRevisionOpenStatusDatasource.php new file mode 100644 index 0000000000..0f00e470c3 --- /dev/null +++ b/src/applications/differential/typeahead/DifferentialRevisionOpenStatusDatasource.php @@ -0,0 +1,74 @@ + array( + 'name' => pht('Any Open Status'), + 'summary' => pht('Find results with any open status.'), + 'description' => pht( + 'This function includes results which have any open status.'), + ), + ); + } + + public function loadResults() { + $results = array( + $this->buildOpenResult(), + ); + return $this->filterResultsAgainstTokens($results); + } + + protected function evaluateFunction($function, array $argv_list) { + $results = array(); + + $map = DifferentialRevisionStatus::getAll(); + foreach ($argv_list as $argv) { + foreach ($map as $status) { + if (!$status->isClosedStatus()) { + $results[] = $status->getKey(); + } + } + } + + return $results; + } + + public function renderFunctionTokens($function, array $argv_list) { + $results = array(); + + foreach ($argv_list as $argv) { + $results[] = PhabricatorTypeaheadTokenView::newFromTypeaheadResult( + $this->buildOpenResult()); + } + + return $results; + } + + private function buildOpenResult() { + $name = pht('Any Open Status'); + return $this->newFunctionResult() + ->setName($name.' open') + ->setDisplayName($name) + ->setPHID(self::FUNCTION_TOKEN) + ->setUnique(true) + ->addAttribute(pht('Select any open status.')); + } + +} diff --git a/src/applications/differential/typeahead/DifferentialRevisionStatusDatasource.php b/src/applications/differential/typeahead/DifferentialRevisionStatusDatasource.php new file mode 100644 index 0000000000..5e240d3c29 --- /dev/null +++ b/src/applications/differential/typeahead/DifferentialRevisionStatusDatasource.php @@ -0,0 +1,52 @@ +buildResults(); + return $this->filterResultsAgainstTokens($results); + } + + + protected function renderSpecialTokens(array $values) { + return $this->renderTokensFromResults($this->buildResults(), $values); + } + + private function buildResults() { + $results = array(); + + $statuses = DifferentialRevisionStatus::getAll(); + foreach ($statuses as $status) { + $key = $status->getKey(); + + $result = id(new PhabricatorTypeaheadResult()) + ->setIcon($status->getIcon()) + ->setPHID($key) + ->setName($status->getDisplayName()); + + if ($status->isClosedStatus()) { + $result->addAttribute(pht('Closed Status')); + } else { + $result->addAttribute(pht('Open Status')); + } + + $results[$key] = $result; + } + + return $results; + } + +} diff --git a/src/applications/differential/typeahead/DifferentialRevisionStatusFunctionDatasource.php b/src/applications/differential/typeahead/DifferentialRevisionStatusFunctionDatasource.php new file mode 100644 index 0000000000..2d4ec38363 --- /dev/null +++ b/src/applications/differential/typeahead/DifferentialRevisionStatusFunctionDatasource.php @@ -0,0 +1,22 @@ +setStatus(ArcanistDifferentialRevisionStatus::ABANDONED); + $status_abandoned = DifferentialRevisionStatus::ABANDONED; + $object->setModernRevisionStatus($status_abandoned); } protected function validateAction($object, PhabricatorUser $viewer) { diff --git a/src/applications/differential/xaction/DifferentialRevisionAcceptTransaction.php b/src/applications/differential/xaction/DifferentialRevisionAcceptTransaction.php index 9b6aad3ad1..f01ce4b487 100644 --- a/src/applications/differential/xaction/DifferentialRevisionAcceptTransaction.php +++ b/src/applications/differential/xaction/DifferentialRevisionAcceptTransaction.php @@ -103,7 +103,7 @@ final class DifferentialRevisionAcceptTransaction if ($reviewer->isAccepted($diff_phid)) { // If a reviewer is already in a full "accepted" state, don't // include that reviewer as an option unless we're listing all - // reviwers, including reviewers who have already accepted. + // reviewers, including reviewers who have already accepted. continue; } } diff --git a/src/applications/differential/xaction/DifferentialRevisionCloseTransaction.php b/src/applications/differential/xaction/DifferentialRevisionCloseTransaction.php index 30bfd5044f..8d01f48eff 100644 --- a/src/applications/differential/xaction/DifferentialRevisionCloseTransaction.php +++ b/src/applications/differential/xaction/DifferentialRevisionCloseTransaction.php @@ -35,14 +35,10 @@ final class DifferentialRevisionCloseTransaction } public function applyInternalEffects($object, $value) { - $status_closed = ArcanistDifferentialRevisionStatus::CLOSED; - $status_accepted = ArcanistDifferentialRevisionStatus::ACCEPTED; + $was_accepted = $object->isAccepted(); - $old_status = $object->getStatus(); - - $object->setStatus($status_closed); - - $was_accepted = ($old_status == $status_accepted); + $status_published = DifferentialRevisionStatus::PUBLISHED; + $object->setModernRevisionStatus($status_published); $object->setProperty( DifferentialRevision::PROPERTY_CLOSED_FROM_ACCEPTED, @@ -50,6 +46,12 @@ final class DifferentialRevisionCloseTransaction } protected function validateAction($object, PhabricatorUser $viewer) { + if ($this->getEditor()->getIsCloseByCommit()) { + // If we're closing a revision because we discovered a commit, we don't + // care what state it was in. + return; + } + if ($object->isClosed()) { throw new Exception( pht( @@ -78,9 +80,50 @@ final class DifferentialRevisionCloseTransaction } public function getTitle() { - return pht( - '%s closed this revision.', - $this->renderAuthor()); + if (!$this->getMetadataValue('isCommitClose')) { + return pht( + '%s closed this revision.', + $this->renderAuthor()); + } + + $commit_phid = $this->getMetadataValue('commitPHID'); + $committer_phid = $this->getMetadataValue('committerPHID'); + $author_phid = $this->getMetadataValue('authorPHID'); + + if ($committer_phid) { + $committer_name = $this->renderHandle($committer_phid); + } else { + $committer_name = $this->getMetadataValue('committerName'); + } + + if ($author_phid) { + $author_name = $this->renderHandle($author_phid); + } else { + $author_name = $this->getMetadatavalue('authorName'); + } + + $same_phid = + strlen($committer_phid) && + strlen($author_phid) && + ($committer_phid == $author_phid); + + $same_name = + !strlen($committer_phid) && + !strlen($author_phid) && + ($committer_name == $author_name); + + if ($same_name || $same_phid) { + return pht( + 'Closed by commit %s (authored by %s).', + $this->renderHandle($commit_phid), + $author_name); + } else { + return pht( + 'Closed by commit %s (authored by %s, committed by %s).', + $this->renderHandle($commit_phid), + $author_name, + $committer_name); + } } public function getTitleForFeed() { diff --git a/src/applications/differential/xaction/DifferentialRevisionPlanChangesTransaction.php b/src/applications/differential/xaction/DifferentialRevisionPlanChangesTransaction.php index 58152bdb67..6e584ceb36 100644 --- a/src/applications/differential/xaction/DifferentialRevisionPlanChangesTransaction.php +++ b/src/applications/differential/xaction/DifferentialRevisionPlanChangesTransaction.php @@ -46,13 +46,12 @@ final class DifferentialRevisionPlanChangesTransaction } public function generateOldValue($object) { - $status_planned = ArcanistDifferentialRevisionStatus::CHANGES_PLANNED; - return ($object->getStatus() == $status_planned); + return $object->isChangePlanned(); } public function applyInternalEffects($object, $value) { - $status_planned = ArcanistDifferentialRevisionStatus::CHANGES_PLANNED; - $object->setStatus($status_planned); + $status_planned = DifferentialRevisionStatus::CHANGES_PLANNED; + $object->setModernRevisionStatus($status_planned); } protected function validateAction($object, PhabricatorUser $viewer) { diff --git a/src/applications/differential/xaction/DifferentialRevisionReclaimTransaction.php b/src/applications/differential/xaction/DifferentialRevisionReclaimTransaction.php index 1f2f6a8d6c..4767924445 100644 --- a/src/applications/differential/xaction/DifferentialRevisionReclaimTransaction.php +++ b/src/applications/differential/xaction/DifferentialRevisionReclaimTransaction.php @@ -47,7 +47,8 @@ final class DifferentialRevisionReclaimTransaction } public function applyInternalEffects($object, $value) { - $object->setStatus(ArcanistDifferentialRevisionStatus::NEEDS_REVIEW); + $status_review = DifferentialRevisionStatus::NEEDS_REVIEW; + $object->setModernRevisionStatus($status_review); } protected function validateAction($object, PhabricatorUser $viewer) { diff --git a/src/applications/differential/xaction/DifferentialRevisionReopenTransaction.php b/src/applications/differential/xaction/DifferentialRevisionReopenTransaction.php index 7ba20772c7..1d28433429 100644 --- a/src/applications/differential/xaction/DifferentialRevisionReopenTransaction.php +++ b/src/applications/differential/xaction/DifferentialRevisionReopenTransaction.php @@ -35,7 +35,8 @@ final class DifferentialRevisionReopenTransaction } public function applyInternalEffects($object, $value) { - $object->setStatus(ArcanistDifferentialRevisionStatus::NEEDS_REVIEW); + $status_review = DifferentialRevisionStatus::NEEDS_REVIEW; + $object->setModernRevisionStatus($status_review); } protected function validateAction($object, PhabricatorUser $viewer) { diff --git a/src/applications/differential/xaction/DifferentialRevisionRequestReviewTransaction.php b/src/applications/differential/xaction/DifferentialRevisionRequestReviewTransaction.php index 65b96d6d8e..d102b0c0c2 100644 --- a/src/applications/differential/xaction/DifferentialRevisionRequestReviewTransaction.php +++ b/src/applications/differential/xaction/DifferentialRevisionRequestReviewTransaction.php @@ -27,13 +27,12 @@ final class DifferentialRevisionRequestReviewTransaction } public function generateOldValue($object) { - $status_review = ArcanistDifferentialRevisionStatus::NEEDS_REVIEW; - return ($object->getStatus() == $status_review); + return $object->isNeedsReview(); } public function applyInternalEffects($object, $value) { - $status_review = ArcanistDifferentialRevisionStatus::NEEDS_REVIEW; - $object->setStatus($status_review); + $status_review = DifferentialRevisionStatus::NEEDS_REVIEW; + $object->setModernRevisionStatus($status_review); } protected function validateAction($object, PhabricatorUser $viewer) { diff --git a/src/applications/differential/xaction/DifferentialRevisionReviewTransaction.php b/src/applications/differential/xaction/DifferentialRevisionReviewTransaction.php index c30514b9ab..b112eb638f 100644 --- a/src/applications/differential/xaction/DifferentialRevisionReviewTransaction.php +++ b/src/applications/differential/xaction/DifferentialRevisionReviewTransaction.php @@ -210,34 +210,6 @@ abstract class DifferentialRevisionReviewTransaction $map = array_select_keys($map, $value); } - // Convert reviewer statuses into edge data. - foreach ($map as $reviewer_phid => $reviewer_status) { - $map[$reviewer_phid] = array( - 'data' => array( - 'status' => $reviewer_status, - ), - ); - } - - // This is currently double-writing: to the old (edge) store and the new - // (reviewer) store. Do the old edge write first. - - $src_phid = $revision->getPHID(); - $edge_type = DifferentialRevisionHasReviewerEdgeType::EDGECONST; - - $editor = new PhabricatorEdgeEditor(); - foreach ($map as $dst_phid => $edge_data) { - if ($status == DifferentialReviewerStatus::STATUS_RESIGNED) { - // TODO: For now, we just remove these reviewers. In the future, we will - // store resignations explicitly. - $editor->removeEdge($src_phid, $edge_type, $dst_phid); - } else { - $editor->addEdge($src_phid, $edge_type, $dst_phid, $edge_data); - } - } - - $editor->save(); - // Now, do the new write. if ($map) { @@ -249,6 +221,7 @@ abstract class DifferentialRevisionReviewTransaction } $table = new DifferentialReviewer(); + $src_phid = $revision->getPHID(); $reviewers = $table->loadAllWhere( 'revisionPHID = %s AND reviewerPHID IN (%Ls)', @@ -256,7 +229,7 @@ abstract class DifferentialRevisionReviewTransaction array_keys($map)); $reviewers = mpull($reviewers, null, 'getReviewerPHID'); - foreach ($map as $dst_phid => $edge_data) { + foreach (array_keys($map) as $dst_phid) { $reviewer = idx($reviewers, $dst_phid); if (!$reviewer) { $reviewer = id(new DifferentialReviewer()) diff --git a/src/applications/differential/xaction/DifferentialRevisionReviewersTransaction.php b/src/applications/differential/xaction/DifferentialRevisionReviewersTransaction.php index a7122f9de4..8b1f9807c1 100644 --- a/src/applications/differential/xaction/DifferentialRevisionReviewersTransaction.php +++ b/src/applications/differential/xaction/DifferentialRevisionReviewersTransaction.php @@ -106,43 +106,9 @@ final class DifferentialRevisionReviewersTransaction public function applyExternalEffects($object, $value) { $src_phid = $object->getPHID(); - // This is currently double-writing: to the old (edge) store and the new - // (reviewer) store. Do the old edge write first. - $old = $this->generateOldValue($object); $new = $value; - $edge_type = DifferentialRevisionHasReviewerEdgeType::EDGECONST; - - $editor = new PhabricatorEdgeEditor(); - $rem = array_diff_key($old, $new); - foreach ($rem as $dst_phid => $status) { - $editor->removeEdge($src_phid, $edge_type, $dst_phid); - } - - foreach ($new as $dst_phid => $status) { - $old_status = idx($old, $dst_phid); - if ($old_status === $status) { - continue; - } - - $data = array( - 'data' => array( - 'status' => $status, - - // TODO: This seemes like it's buggy before the Modular Transactions - // changes. Figure out what's going on here? We don't have a very - // clean way to get the active diff ID right now. - 'diffID' => null, - ), - ); - - $editor->addEdge($src_phid, $edge_type, $dst_phid, $data); - } - - $editor->save(); - - // Now, do the new write. $table = new DifferentialReviewer(); $table_name = $table->getTableName(); diff --git a/src/applications/differential/xaction/DifferentialRevisionStatusTransaction.php b/src/applications/differential/xaction/DifferentialRevisionStatusTransaction.php new file mode 100644 index 0000000000..c08eb9d187 --- /dev/null +++ b/src/applications/differential/xaction/DifferentialRevisionStatusTransaction.php @@ -0,0 +1,73 @@ +getModernRevisionStatus(); + } + + public function applyInternalEffects($object, $value) { + $object->setModernRevisionStatus($value); + } + + public function getTitle() { + $status = $this->newStatusObject(); + + if ($status->isAccepted()) { + return pht('This revision is now accepted and ready to land.'); + } + + if ($status->isNeedsRevision()) { + return pht('This revision now requires changes to proceed.'); + } + + if ($status->isNeedsReview()) { + return pht('This revision now requires review to proceed.'); + } + + return null; + } + + public function getTitleForFeed() { + $status = $this->newStatusObject(); + + if ($status->isAccepted()) { + return pht( + '%s is now accepted and ready to land.', + $this->renderObject()); + } + + if ($status->isNeedsRevision()) { + return pht( + '%s now requires changes to proceed.', + $this->renderObject()); + } + + if ($status->isNeedsReview()) { + return pht( + '%s now requires review to proceed.', + $this->renderObject()); + } + + return null; + } + + public function getIcon() { + $status = $this->newStatusObject(); + return $status->getTimelineIcon(); + } + + public function getColor() { + $status = $this->newStatusObject(); + return $status->getTimelineColor(); + } + + private function newStatusObject() { + $new = $this->getNewValue(); + return DifferentialRevisionStatus::newForStatus($new); + } + +} diff --git a/src/applications/diffusion/controller/DiffusionBranchTableController.php b/src/applications/diffusion/controller/DiffusionBranchTableController.php index 04d00f0306..801b45a450 100644 --- a/src/applications/diffusion/controller/DiffusionBranchTableController.php +++ b/src/applications/diffusion/controller/DiffusionBranchTableController.php @@ -71,6 +71,11 @@ final class DiffusionBranchTableController extends DiffusionController { ->setHeader(pht('Branches')) ->setHeaderIcon('fa-code-fork'); + if (!$repository->isSVN()) { + $branch_tag = $this->renderBranchTag($drequest); + $header->addTag($branch_tag); + } + $tabs = $this->buildTabsView('branch'); $view = id(new PHUITwoColumnView()) diff --git a/src/applications/diffusion/controller/DiffusionBrowseController.php b/src/applications/diffusion/controller/DiffusionBrowseController.php index 32381207fa..9ab7f194bf 100644 --- a/src/applications/diffusion/controller/DiffusionBrowseController.php +++ b/src/applications/diffusion/controller/DiffusionBrowseController.php @@ -5,6 +5,7 @@ final class DiffusionBrowseController extends DiffusionController { private $lintCommit; private $lintMessages; private $coverage; + private $corpusButtons = array(); public function shouldAllowPublic() { return true; @@ -22,8 +23,7 @@ final class DiffusionBrowseController extends DiffusionController { // list. $grep = $request->getStr('grep'); - $find = $request->getStr('find'); - if (strlen($grep) || strlen($find)) { + if (strlen($grep)) { return $this->browseSearch(); } @@ -57,14 +57,17 @@ final class DiffusionBrowseController extends DiffusionController { private function browseSearch() { $drequest = $this->getDiffusionRequest(); $header = $this->buildHeaderView($drequest); + $path = nonempty(basename($drequest->getPath()), '/'); - $search_form = $this->renderSearchForm(); $search_results = $this->renderSearchResults(); + $search_form = $this->renderSearchForm($path); - $search_form = id(new PHUIObjectBoxView()) - ->setHeaderText(pht('Search')) - ->setBackground(PHUIObjectBoxView::BLUE_PROPERTY) - ->appendChild($search_form); + $search_form = phutil_tag( + 'div', + array( + 'class' => 'diffusion-mobile-search-form', + ), + $search_form); $crumbs = $this->buildCrumbs( array( @@ -74,8 +77,11 @@ final class DiffusionBrowseController extends DiffusionController { )); $crumbs->setBorder(true); + $tabs = $this->buildTabsView('code'); + $view = id(new PHUITwoColumnView()) ->setHeader($header) + ->setTabs($tabs) ->setFooter( array( $search_form, @@ -235,50 +241,47 @@ final class DiffusionBrowseController extends DiffusionController { require_celerity_resource('diffusion-source-css'); // Render the page. - $curtain = $this->buildCurtain($drequest, $show_blame, $show_editor); - $properties = $this->buildPropertyView($drequest); + $bar = $this->buildButtonBar($drequest, $show_blame, $show_editor); $header = $this->buildHeaderView($drequest); $header->setHeaderIcon('fa-file-code-o'); - $content = array(); - $follow = $request->getStr('follow'); + $follow_notice = null; if ($follow) { - $notice = new PHUIInfoView(); - $notice->setSeverity(PHUIInfoView::SEVERITY_WARNING); - $notice->setTitle(pht('Unable to Continue')); + $follow_notice = id(new PHUIInfoView()) + ->setSeverity(PHUIInfoView::SEVERITY_WARNING) + ->setTitle(pht('Unable to Continue')); switch ($follow) { case 'first': - $notice->appendChild( + $follow_notice->appendChild( pht( 'Unable to continue tracing the history of this file because '. 'this commit is the first commit in the repository.')); break; case 'created': - $notice->appendChild( + $follow_notice->appendChild( pht( 'Unable to continue tracing the history of this file because '. 'this commit created the file.')); break; } - $content[] = $notice; } $renamed = $request->getStr('renamed'); + $renamed_notice = null; if ($renamed) { - $notice = new PHUIInfoView(); - $notice->setSeverity(PHUIInfoView::SEVERITY_NOTICE); - $notice->setTitle(pht('File Renamed')); - $notice->appendChild( - pht( - 'File history passes through a rename from "%s" to "%s".', - $drequest->getPath(), - $renamed)); - $content[] = $notice; + $renamed_notice = id(new PHUIInfoView()) + ->setSeverity(PHUIInfoView::SEVERITY_NOTICE) + ->setTitle(pht('File Renamed')) + ->appendChild( + pht( + 'File history passes through a rename from "%s" to "%s".', + $drequest->getPath(), + $renamed)); } - $content[] = $corpus; - $content[] = $this->buildOpenRevisions(); + $open_revisions = $this->buildOpenRevisions(); + $owners_list = $this->buildOwnersList($drequest); $crumbs = $this->buildCrumbs( array( @@ -289,17 +292,20 @@ final class DiffusionBrowseController extends DiffusionController { $crumbs->setBorder(true); $basename = basename($this->getDiffusionRequest()->getPath()); + $tabs = $this->buildTabsView('code'); + $bar->setRight($this->corpusButtons); $view = id(new PHUITwoColumnView()) ->setHeader($header) - ->setCurtain($curtain) - ->setMainColumn(array( - $content, - )); - - if ($properties) { - $view->addPropertySection(pht('Details'), $properties); - } + ->setTabs($tabs) + ->setFooter(array( + $bar, + $follow_notice, + $renamed_notice, + $corpus, + $open_revisions, + $owners_list, + )); $title = array($basename, $repository->getDisplayName()); @@ -323,14 +329,12 @@ final class DiffusionBrowseController extends DiffusionController { $reason = $results->getReasonForEmptyResultSet(); - $actions = $this->getActions($drequest); + $this->buildActionButtons($drequest, true); $details = $this->buildPropertyView($drequest); $header = $this->buildHeaderView($drequest); $header->setHeaderIcon('fa-folder-open'); - $search_form = $this->renderSearchForm(); - $empty_result = null; $browse_panel = null; $branch_panel = null; @@ -361,7 +365,7 @@ final class DiffusionBrowseController extends DiffusionController { $title = nonempty(basename($drequest->getPath()), '/'); $icon = 'fa-folder-open'; - $browse_header = $this->buildPanelHeaderView($title, $icon, $actions); + $browse_header = $this->buildPanelHeaderView($title, $icon); $browse_panel = id(new PHUIObjectBoxView()) ->setHeader($browse_header) @@ -369,12 +373,6 @@ final class DiffusionBrowseController extends DiffusionController { ->setTable($browse_table) ->setPager($pager); - $browse_panel->setShowHide( - array(pht('Show Search')), - pht('Hide Search'), - $search_form, - '#'); - $path = $drequest->getPath(); $is_branch = (!strlen($path) && $repository->supportsBranchComparison()); if ($is_branch) { @@ -393,15 +391,23 @@ final class DiffusionBrowseController extends DiffusionController { )); $crumbs->setBorder(true); + $tabs = $this->buildTabsView('code'); + $owners_list = $this->buildOwnersList($drequest); + $bar = id(new PHUILeftRightView()) + ->setRight($this->corpusButtons) + ->addClass('diffusion-action-bar'); $view = id(new PHUITwoColumnView()) ->setHeader($header) + ->setTabs($tabs) ->setFooter( array( + $bar, $branch_panel, $empty_result, $browse_panel, $open_revisions, + $owners_list, $readme, )); @@ -449,159 +455,58 @@ final class DiffusionBrowseController extends DiffusionController { 'limit' => $pager->getPageSize() + 1, 'offset' => $pager->getOffset(), )); - } else { // Filename search. - $search_mode = 'find'; - $query_string = $request->getStr('find'); - $results = $this->callConduitWithDiffusionRequest( - 'diffusion.querypaths', - array( - 'pattern' => $query_string, - 'commit' => $drequest->getStableCommit(), - 'path' => $drequest->getPath(), - 'limit' => $pager->getPageSize() + 1, - 'offset' => $pager->getOffset(), - )); } break; } $results = $pager->sliceResults($results); + $table = null; + $header = null; if ($search_mode == 'grep') { $table = $this->renderGrepResults($results, $query_string); - $header = pht( + $title = pht( 'File content matching "%s" under "%s"', $query_string, nonempty($drequest->getPath(), '/')); - } else { - $table = $this->renderFindResults($results); - $header = pht( - 'Paths matching "%s" under "%s"', - $query_string, - nonempty($drequest->getPath(), '/')); + $header = id(new PHUIHeaderView()) + ->setHeader($title) + ->addClass('diffusion-search-result-header'); } - return id(new PHUIObjectBoxView()) - ->setHeaderText($header) - ->setBackground(PHUIObjectBoxView::BLUE_PROPERTY) - ->setTable($table) - ->setPager($pager); + return array($header, $table, $pager); } private function renderGrepResults(array $results, $pattern) { $drequest = $this->getDiffusionRequest(); - require_celerity_resource('phabricator-search-results-css'); - $rows = array(); - foreach ($results as $result) { - list($path, $line, $string) = $result; - - $href = $drequest->generateURI(array( - 'action' => 'browse', - 'path' => $path, - 'line' => $line, - )); - - $matches = null; - $count = @preg_match_all( - '('.$pattern.')u', - $string, - $matches, - PREG_OFFSET_CAPTURE); - - if (!$count) { - $output = ltrim($string); - } else { - $output = array(); - $cursor = 0; - $length = strlen($string); - foreach ($matches[0] as $match) { - $offset = $match[1]; - if ($cursor != $offset) { - $output[] = array( - 'text' => substr($string, $cursor, $offset), - 'highlight' => false, - ); - } - $output[] = array( - 'text' => $match[0], - 'highlight' => true, - ); - $cursor = $offset + strlen($match[0]); - } - if ($cursor != $length) { - $output[] = array( - 'text' => substr($string, $cursor), - 'highlight' => false, - ); - } - - if ($output) { - $output[0]['text'] = ltrim($output[0]['text']); - } - - foreach ($output as $key => $segment) { - if ($segment['highlight']) { - $output[$key] = phutil_tag('strong', array(), $segment['text']); - } else { - $output[$key] = $segment['text']; - } - } - } - - $string = phutil_tag( - 'pre', - array('class' => 'PhabricatorMonospaced phui-source-fragment'), - $output); - - $path = Filesystem::readablePath($path, $drequest->getPath()); - - $rows[] = array( - phutil_tag('a', array('href' => $href), $path), - $line, - $string, - ); + if (!$results) { + return id(new PHUIInfoView()) + ->setSeverity(PHUIInfoView::SEVERITY_NODATA) + ->appendChild( + pht( + 'The pattern you searched for was not found in the content of any '. + 'files.')); } - $table = id(new AphrontTableView($rows)) - ->setClassName('remarkup-code') - ->setHeaders(array(pht('Path'), pht('Line'), pht('String'))) - ->setColumnClasses(array('', 'n', 'wide')) - ->setNoDataString( - pht( - 'The pattern you searched for was not found in the content of any '. - 'files.')); - - return $table; - } - - private function renderFindResults(array $results) { - $drequest = $this->getDiffusionRequest(); - - $rows = array(); - foreach ($results as $result) { - $href = $drequest->generateURI(array( - 'action' => 'browse', - 'path' => $result, - )); - - $readable = Filesystem::readablePath($result, $drequest->getPath()); - - $rows[] = array( - phutil_tag('a', array('href' => $href), $readable), - ); + $grouped = array(); + foreach ($results as $file) { + list($path, $line, $string) = $file; + $grouped[$path][] = array($line, $string); } - $table = id(new AphrontTableView($rows)) - ->setHeaders(array(pht('Path'))) - ->setColumnClasses(array('wide')) - ->setNoDataString( - pht( - 'The pattern you searched for did not match the names of any '. - 'files.')); + $view = array(); + foreach ($grouped as $path => $matches) { + $view[] = id(new DiffusionPatternSearchView()) + ->setPath($path) + ->setMatches($matches) + ->setPattern($pattern) + ->setDiffusionRequest($drequest) + ->render(); + } - return $table; + return $view; } private function loadLintMessages() { @@ -737,14 +642,13 @@ final class DiffusionBrowseController extends DiffusionController { Javelin::initBehavior('load-blame', array('id' => $id)); - $file = $this->renderFileButton(); + $this->corpusButtons[] = $this->renderFileButton(); $title = basename($this->getDiffusionRequest()->getPath()); $icon = 'fa-file-code-o'; $drequest = $this->getDiffusionRequest(); - $actions = $this->getActions($drequest); + $this->buildActionButtons($drequest); - $header = $this->buildPanelHeaderView($title, $icon, $actions); - $header->addActionLink($file); + $header = $this->buildPanelHeaderView($title, $icon); $corpus = id(new PHUIObjectBoxView()) ->setHeader($header) @@ -783,12 +687,11 @@ final class DiffusionBrowseController extends DiffusionController { return $corpus; } - private function buildCurtain( + private function buildButtonBar( DiffusionRequest $drequest, $show_blame, $show_editor) { - $curtain = $this->newCurtainView($drequest); $viewer = $this->getViewer(); $base_uri = $this->getRequest()->getRequestURI(); @@ -796,19 +699,21 @@ final class DiffusionBrowseController extends DiffusionController { $repository = $drequest->getRepository(); $path = $drequest->getPath(); $line = nonempty((int)$drequest->getLine(), 1); + $buttons = array(); $editor_link = $user->loadEditorLink($path, $line, $repository); $template = $user->loadEditorLink($path, '%l', $repository); - $curtain->addAction( - id(new PhabricatorActionView()) - ->setName(pht('Show Last Change')) + $buttons[] = + id(new PHUIButtonView()) + ->setText(pht('Last Change')) + ->setColor(PHUIButtonView::GREY) ->setHref( $drequest->generateURI( array( 'action' => 'change', ))) - ->setIcon('fa-backward')); + ->setIcon('fa-backward'); if ($show_blame) { $blame_text = pht('Disable Blame'); @@ -820,48 +725,76 @@ final class DiffusionBrowseController extends DiffusionController { $blame_value = 1; } - $curtain->addAction( - id(new PhabricatorActionView()) - ->setName($blame_text) - ->setHref($base_uri->alter('blame', $blame_value)) - ->setIcon($blame_icon) - ->setUser($viewer) - ->setRenderAsForm($viewer->isLoggedIn())); + $blame = id(new PHUIButtonView()) + ->setText($blame_text) + ->setIcon($blame_icon) + ->setUser($viewer) + ->setColor(PHUIButtonView::GREY); - $curtain->addAction( - id(new PhabricatorActionView()) - ->setName(pht('Open in Editor')) - ->setHref($editor_link) - ->setIcon('fa-pencil') - ->setID('editor_link') - ->setMetadata(array('link_template' => $template)) - ->setDisabled(!$editor_link)); + if ($viewer->isLoggedIn()) { + $blame = phabricator_form( + $viewer, + array( + 'action' => $base_uri->alter('blame', $blame_value), + 'method' => 'POST', + 'style' => 'display: inline-block;', + ), + $blame); + } else { + $blame->setTag('a'); + $blame->setHref($base_uri->alter('blame', $blame_value)); + } + $buttons[] = $blame; + + if ($editor_link) { + $buttons[] = + id(new PHUIButtonView()) + ->setTag('a') + ->setText(pht('Open File')) + ->setHref($editor_link) + ->setIcon('fa-pencil') + ->setID('editor_link') + ->setMetadata(array('link_template' => $template)) + ->setDisabled(!$editor_link) + ->setColor(PHUIButtonView::GREY); + } $href = null; + $show_lint = true; if ($this->getRequest()->getStr('lint') !== null) { - $lint_text = pht('Hide %d Lint Message(s)', count($this->lintMessages)); + $lint_text = pht('Hide Lint'); $href = $base_uri->alter('lint', null); } else if ($this->lintCommit === null) { - $lint_text = pht('Lint not Available'); + $show_lint = false; } else { - $lint_text = pht( - 'Show %d Lint Message(s)', - count($this->lintMessages)); + $lint_text = pht('Show Lint'); $href = $this->getDiffusionRequest()->generateURI(array( 'action' => 'browse', 'commit' => $this->lintCommit, ))->alter('lint', ''); } - $curtain->addAction( - id(new PhabricatorActionView()) - ->setName($lint_text) - ->setHref($href) - ->setIcon('fa-exclamation-triangle') - ->setDisabled(!$href)); + if ($show_lint) { + $buttons[] = + id(new PHUIButtonView()) + ->setTag('a') + ->setText($lint_text) + ->setHref($href) + ->setIcon('fa-exclamation-triangle') + ->setDisabled(!$href) + ->setColor(PHUIButtonView::GREY); + } + $bar = id(new PHUILeftRightView()) + ->setLeft($buttons) + ->addClass('diffusion-action-bar full-mobile-buttons'); + return $bar; + } + private function buildOwnersList(DiffusionRequest $drequest) { + + $viewer = $this->getViewer(); $repository = $drequest->getRepository(); $owners = 'PhabricatorOwnersApplication'; @@ -881,30 +814,54 @@ final class DiffusionBrowseController extends DiffusionController { $repository->getPHID(), $drequest->getPath()); + $ownership = id(new PHUIObjectItemListView()) + ->setUser($viewer) + ->setNoDataString(pht('No Owners')); + if ($packages) { - $ownership = id(new PHUIStatusListView()) - ->setUser($viewer); - foreach ($packages as $package) { - $icon = 'fa-list-alt'; - $color = 'grey'; + $item = id(new PHUIObjectItemView()) + ->setObject($package) + ->setObjectName($package->getMonogram()) + ->setHeader($package->getName()) + ->setHref($package->getURI()); - $item = id(new PHUIStatusItemView()) - ->setIcon($icon, $color) - ->setTarget($viewer->renderHandle($package->getPHID())); + $owners = $package->getOwners(); + if ($owners) { + $owner_list = $viewer->renderHandleList( + mpull($owners, 'getUserPHID')); + } else { + $owner_list = phutil_tag('em', array(), pht('None')); + } + $item->addAttribute(pht('Owners: %s', $owner_list)); + + $auto = $package->getAutoReview(); + $autoreview_map = PhabricatorOwnersPackage::getAutoreviewOptionsMap(); + $spec = idx($autoreview_map, $auto, array()); + $name = idx($spec, 'name', $auto); + $item->addIcon('fa-code', $name); + + if ($package->getAuditingEnabled()) { + $item->addIcon('fa-check', pht('Auditing Enabled')); + } else { + $item->addIcon('fa-ban', pht('No Auditing')); + } + + if ($package->isArchived()) { + $item->setDisabled(true); + } $ownership->addItem($item); } - } else { - $ownership = phutil_tag('em', array(), pht('None')); } - $curtain->newPanel() - ->setHeaderText(pht('Owners')) - ->appendChild($ownership); + $view = id(new PHUIObjectBoxView()) + ->setHeaderText(pht('Owner Packages')) + ->setBackground(PHUIObjectBoxView::BLUE_PROPERTY) + ->setObjectList($ownership); } - return $curtain; + return $view; } private function renderFileButton($file_uri = null, $label = null) { @@ -912,11 +869,11 @@ final class DiffusionBrowseController extends DiffusionController { $base_uri = $this->getRequest()->getRequestURI(); if ($file_uri) { - $text = pht('Download Raw'); + $text = pht('Download File'); $href = $file_uri; $icon = 'fa-download'; } else { - $text = pht('View Raw'); + $text = pht('Raw File'); $href = $base_uri->alter('view', 'raw'); $icon = 'fa-file-text'; } @@ -929,7 +886,8 @@ final class DiffusionBrowseController extends DiffusionController { ->setTag('a') ->setText($text) ->setHref($href) - ->setIcon($icon); + ->setIcon($icon) + ->setColor(PHUIButtonView::GREY); return $button; } @@ -1358,13 +1316,12 @@ final class DiffusionBrowseController extends DiffusionController { 'src' => $file_uri, ))); - $file = $this->renderFileButton($file_uri); + $this->corpusButtons[] = $this->renderFileButton($file_uri); $title = basename($this->getDiffusionRequest()->getPath()); $icon = 'fa-file-image-o'; $drequest = $this->getDiffusionRequest(); - $actions = $this->getActions($drequest); - $header = $this->buildPanelHeaderView($title, $icon, $actions); - $header->addActionLink($file); + $this->buildActionButtons($drequest); + $header = $this->buildPanelHeaderView($title, $icon); return id(new PHUIObjectBoxView()) ->setHeader($header) @@ -1379,13 +1336,12 @@ final class DiffusionBrowseController extends DiffusionController { ->addPadding(PHUI::PADDING_LARGE) ->appendChild($text); - $file = $this->renderFileButton($file_uri); + $this->corpusButtons[] = $this->renderFileButton($file_uri); $title = basename($this->getDiffusionRequest()->getPath()); $icon = 'fa-file'; $drequest = $this->getDiffusionRequest(); - $actions = $this->getActions($drequest); - $header = $this->buildPanelHeaderView($title, $icon, $actions); - $header->addActionLink($file); + $this->buildActionButtons($drequest); + $header = $this->buildPanelHeaderView($title, $icon); $box = id(new PHUIObjectBoxView()) ->setHeader($header) @@ -1581,43 +1537,6 @@ final class DiffusionBrowseController extends DiffusionController { return "{$summary}\n{$date} \xC2\xB7 {$author}"; } - protected function renderSearchForm() { - $drequest = $this->getDiffusionRequest(); - - $forms = array(); - $form = id(new AphrontFormView()) - ->setUser($this->getViewer()) - ->setMethod('GET'); - - switch ($drequest->getRepository()->getVersionControlSystem()) { - case PhabricatorRepositoryType::REPOSITORY_TYPE_SVN: - $forms[] = id(clone $form) - ->appendChild(pht('Search is not available in Subversion.')); - break; - default: - $forms[] = id(clone $form) - ->appendChild( - id(new AphrontFormTextWithSubmitControl()) - ->setLabel(pht('File Name')) - ->setSubmitLabel(pht('Search File Names')) - ->setName('find') - ->setValue($this->getRequest()->getStr('find'))); - $forms[] = id(clone $form) - ->appendChild( - id(new AphrontFormTextWithSubmitControl()) - ->setLabel(pht('Pattern')) - ->setSubmitLabel(pht('Grep File Content')) - ->setName('grep') - ->setValue($this->getRequest()->getStr('grep'))); - break; - } - - require_celerity_resource('diffusion-icons-css'); - $form_box = phutil_tag_div('diffusion-search-boxen', $forms); - - return $form_box; - } - protected function markupText($text) { $engine = PhabricatorMarkupEngine::newDiffusionMarkupEngine(); $engine->setConfig('viewer', $this->getRequest()->getUser()); @@ -1635,34 +1554,43 @@ final class DiffusionBrowseController extends DiffusionController { protected function buildHeaderView(DiffusionRequest $drequest) { $viewer = $this->getViewer(); + $repository = $drequest->getRepository(); - $tag = $this->renderCommitHashTag($drequest); + $commit_tag = $this->renderCommitHashTag($drequest); + + $path = nonempty(basename($drequest->getPath()), '/'); + $search = $this->renderSearchForm($path); $header = id(new PHUIHeaderView()) ->setUser($viewer) ->setHeader($this->renderPathLinks($drequest, $mode = 'browse')) - ->addTag($tag); + ->addActionItem($search) + ->addTag($commit_tag) + ->addClass('diffusion-browse-header'); + + if (!$repository->isSVN()) { + $branch_tag = $this->renderBranchTag($drequest); + $header->addTag($branch_tag); + } return $header; } - protected function buildPanelHeaderView($title, $icon, array $actions) { + protected function buildPanelHeaderView($title, $icon) { $header = id(new PHUIHeaderView()) ->setHeader($title) ->setHeaderIcon($icon) ->addClass('diffusion-panel-header-view'); - foreach ($actions as $action_link) { - if ($action_link) { - $header->addActionLink($action_link); - } - } return $header; } - protected function getActions(DiffusionRequest $drequest) { + protected function buildActionButtons( + DiffusionRequest $drequest, + $is_directory = false) { + $viewer = $this->getViewer(); $repository = $drequest->getRepository(); $history_uri = $drequest->generateURI(array('action' => 'history')); @@ -1674,7 +1602,7 @@ final class DiffusionBrowseController extends DiffusionController { 'action' => 'browse', )); - if ($repository->supportsBranchComparison()) { + if ($repository->supportsBranchComparison() && $is_directory) { $compare_uri = $drequest->generateURI(array('action' => 'compare')); $compare = id(new PHUIButtonView()) ->setText(pht('Compare')) @@ -1683,6 +1611,7 @@ final class DiffusionBrowseController extends DiffusionController { ->setTag('a') ->setHref($compare_uri) ->setColor(PHUIButtonView::GREY); + $this->corpusButtons[] = $compare; } $head = null; @@ -1692,6 +1621,7 @@ final class DiffusionBrowseController extends DiffusionController { ->setHref($head_uri) ->setIcon('fa-home') ->setColor(PHUIButtonView::GREY); + $this->corpusButtons[] = $head; } $history = id(new PHUIButtonView()) @@ -1700,8 +1630,8 @@ final class DiffusionBrowseController extends DiffusionController { ->setTag('a') ->setIcon('fa-history') ->setColor(PHUIButtonView::GREY); + $this->corpusButtons[] = $history; - return array($history, $compare, $head); } protected function buildPropertyView( @@ -1758,7 +1688,7 @@ final class DiffusionBrowseController extends DiffusionController { $revisions = id(new DifferentialRevisionQuery()) ->setViewer($viewer) ->withPath($repository->getID(), $path_id) - ->withStatus(DifferentialLegacyQuery::STATUS_OPEN) + ->withIsOpen(true) ->withUpdatedEpochBetween($recent, null) ->setOrder(DifferentialRevisionQuery::ORDER_MODIFIED) ->setLimit(10) @@ -1905,8 +1835,8 @@ final class DiffusionBrowseController extends DiffusionController { $title = basename($this->getDiffusionRequest()->getPath()); $icon = 'fa-archive'; $drequest = $this->getDiffusionRequest(); - $actions = $this->getActions($drequest); - $header = $this->buildPanelHeaderView($title, $icon, $actions); + $this->buildActionButtons($drequest); + $header = $this->buildPanelHeaderView($title, $icon); $severity = PHUIInfoView::SEVERITY_NOTICE; @@ -1918,14 +1848,13 @@ final class DiffusionBrowseController extends DiffusionController { try { $file = $this->loadGitLFSFile($ref); $data = $this->renderGitLFSButton(); - $header->addActionLink($data); } catch (Exception $ex) { $severity = PHUIInfoView::SEVERITY_ERROR; $messages[] = pht('The data for this file could not be loaded.'); } - $raw = $this->renderFileButton(null, pht('View Raw LFS Pointer')); - $header->addActionLink($raw); + $this->corpusButtons[] = $this->renderFileButton( + null, pht('View Raw LFS Pointer')); $corpus = id(new PHUIObjectBoxView()) ->setHeader($header) diff --git a/src/applications/diffusion/controller/DiffusionController.php b/src/applications/diffusion/controller/DiffusionController.php index 8de17c03a2..7ae67c2b5d 100644 --- a/src/applications/diffusion/controller/DiffusionController.php +++ b/src/applications/diffusion/controller/DiffusionController.php @@ -148,7 +148,7 @@ abstract class DiffusionController extends PhabricatorController { if (!$spec['commit'] && !$spec['tags'] && !$spec['branches']) { $branch_name = $drequest->getBranch(); - if ($branch_name) { + if (strlen($branch_name)) { $repository_name .= ' ('.$branch_name.')'; } } @@ -343,6 +343,38 @@ abstract class DiffusionController extends PhabricatorController { return $tag; } + protected function renderBranchTag(DiffusionRequest $drequest) { + $branch = $drequest->getBranch(); + $branch = id(new PhutilUTF8StringTruncator()) + ->setMaximumGlyphs(24) + ->truncateString($branch); + + $tag = id(new PHUITagView()) + ->setName($branch) + ->setColor(PHUITagView::COLOR_INDIGO) + ->setBorder(PHUITagView::BORDER_NONE) + ->setType(PHUITagView::TYPE_OUTLINE) + ->addClass('diffusion-header-branch-tag'); + + return $tag; + } + + protected function renderSymbolicCommit(DiffusionRequest $drequest) { + $symbolic_tag = $drequest->getSymbolicCommit(); + $symbolic_tag = id(new PhutilUTF8StringTruncator()) + ->setMaximumGlyphs(24) + ->truncateString($symbolic_tag); + + $tag = id(new PHUITagView()) + ->setName($symbolic_tag) + ->setIcon('fa-tag') + ->setColor(PHUITagView::COLOR_INDIGO) + ->setBorder(PHUITagView::BORDER_NONE) + ->setType(PHUITagView::TYPE_SHADE); + + return $tag; + } + protected function renderDirectoryReadme(DiffusionBrowseResultSet $browse) { $readme_path = $browse->getReadmePath(); if ($readme_path === null) { @@ -410,6 +442,58 @@ abstract class DiffusionController extends PhabricatorController { ->setContent($readme_corpus); } + protected function renderSearchForm($path = '/') { + $drequest = $this->getDiffusionRequest(); + $viewer = $this->getViewer(); + switch ($drequest->getRepository()->getVersionControlSystem()) { + case PhabricatorRepositoryType::REPOSITORY_TYPE_SVN: + return null; + } + + $search_term = $this->getRequest()->getStr('grep'); + require_celerity_resource('diffusion-icons-css'); + require_celerity_resource('diffusion-css'); + + $href = $drequest->generateURI(array( + 'action' => 'browse', + 'path' => $path, + )); + + $bar = javelin_tag( + 'input', + array( + 'type' => 'text', + 'id' => 'diffusion-search-input', + 'name' => 'grep', + 'class' => 'diffusion-search-input', + 'sigil' => 'diffusion-search-input', + 'placeholder' => pht('Pattern Search'), + 'value' => $search_term, + )); + + $form = phabricator_form( + $viewer, + array( + 'method' => 'GET', + 'action' => $href, + 'sigil' => 'diffusion-search-form', + 'class' => 'diffusion-search-form', + 'id' => 'diffusion-search-form', + ), + array( + $bar, + )); + + $form_view = phutil_tag( + 'div', + array( + 'class' => 'diffusion-search-form-view', + ), + $form); + + return $form_view; + } + protected function buildTabsView($key) { $drequest = $this->getDiffusionRequest(); $repository = $drequest->getRepository(); @@ -418,15 +502,15 @@ abstract class DiffusionController extends PhabricatorController { $view->addMenuItem( id(new PHUIListItemView()) - ->setKey('home') - ->setName(pht('Home')) - ->setIcon('fa-home') + ->setKey('code') + ->setName(pht('Code')) + ->setIcon('fa-code') ->setHref($drequest->generateURI( array( 'action' => 'branch', 'path' => '/', ))) - ->setSelected($key == 'home')); + ->setSelected($key == 'code')); if (!$repository->isSVN()) { $view->addMenuItem( diff --git a/src/applications/diffusion/controller/DiffusionGraphController.php b/src/applications/diffusion/controller/DiffusionGraphController.php index 8ec909139a..5062fe9765 100644 --- a/src/applications/diffusion/controller/DiffusionGraphController.php +++ b/src/applications/diffusion/controller/DiffusionGraphController.php @@ -11,6 +11,7 @@ final class DiffusionGraphController extends DiffusionController { if ($response) { return $response; } + require_celerity_resource('diffusion-css'); $viewer = $this->getViewer(); $drequest = $this->getDiffusionRequest(); @@ -83,6 +84,7 @@ final class DiffusionGraphController extends DiffusionController { private function buildHeader(DiffusionRequest $drequest) { $viewer = $this->getViewer(); + $repository = $drequest->getRepository(); $no_path = !strlen($drequest->getPath()); if ($no_path) { @@ -96,6 +98,11 @@ final class DiffusionGraphController extends DiffusionController { ->setHeader($header_text) ->setHeaderIcon('fa-code-fork'); + if (!$repository->isSVN()) { + $branch_tag = $this->renderBranchTag($drequest); + $header->addTag($branch_tag); + } + return $header; } diff --git a/src/applications/diffusion/controller/DiffusionHistoryController.php b/src/applications/diffusion/controller/DiffusionHistoryController.php index fde35133cc..f50e73295f 100644 --- a/src/applications/diffusion/controller/DiffusionHistoryController.php +++ b/src/applications/diffusion/controller/DiffusionHistoryController.php @@ -11,6 +11,7 @@ final class DiffusionHistoryController extends DiffusionController { if ($response) { return $response; } + require_celerity_resource('diffusion-css'); $viewer = $this->getViewer(); $drequest = $this->getDiffusionRequest(); @@ -78,6 +79,7 @@ final class DiffusionHistoryController extends DiffusionController { private function buildHeader(DiffusionRequest $drequest) { $viewer = $this->getViewer(); + $repository = $drequest->getRepository(); $no_path = !strlen($drequest->getPath()); if ($no_path) { @@ -91,6 +93,16 @@ final class DiffusionHistoryController extends DiffusionController { ->setHeader($header_text) ->setHeaderIcon('fa-clock-o'); + if (!$repository->isSVN()) { + $branch_tag = $this->renderBranchTag($drequest); + $header->addTag($branch_tag); + } + + if ($drequest->getSymbolicCommit()) { + $symbolic_tag = $this->renderSymbolicCommit($drequest); + $header->addTag($symbolic_tag); + } + return $header; } diff --git a/src/applications/diffusion/controller/DiffusionLintController.php b/src/applications/diffusion/controller/DiffusionLintController.php index 980dc8d27c..af3ed571ce 100644 --- a/src/applications/diffusion/controller/DiffusionLintController.php +++ b/src/applications/diffusion/controller/DiffusionLintController.php @@ -167,6 +167,7 @@ final class DiffusionLintController extends DiffusionController { 'path' => true, 'view' => 'lint', )); + $crumbs->setBorder(true); if ($drequest) { $title[] = $drequest->getRepository()->getDisplayName(); @@ -178,7 +179,7 @@ final class DiffusionLintController extends DiffusionController { $branch = $drequest->loadBranch(); $header = id(new PHUIHeaderView()) - ->setHeader($this->renderPathLinks($drequest, 'lint')) + ->setHeader(pht('Lint: %s', $this->renderPathLinks($drequest, 'lint'))) ->setUser($viewer) ->setHeaderIcon('fa-code'); $actions = $this->buildActionView($drequest); @@ -465,6 +466,7 @@ final class DiffusionLintController extends DiffusionController { 'path' => true, 'view' => 'lint', )); + $crumbs->setBorder(true); $header = id(new PHUIHeaderView()) ->setHeader(pht('Lint: %s', $drequest->getRepository()->getDisplayName())) diff --git a/src/applications/diffusion/controller/DiffusionRepositoryController.php b/src/applications/diffusion/controller/DiffusionRepositoryController.php index 39877317df..120dd0d344 100644 --- a/src/applications/diffusion/controller/DiffusionRepositoryController.php +++ b/src/applications/diffusion/controller/DiffusionRepositoryController.php @@ -31,8 +31,6 @@ final class DiffusionRepositoryController extends DiffusionController { $description = $this->buildDescriptionView($repository); $locate_file = $this->buildLocateFile(); - $header->setActionList($actions); - // Before we do any work, make sure we're looking at a some content: we're // on a valid branch, and the repository is not empty. $page_has_content = false; @@ -100,7 +98,7 @@ final class DiffusionRepositoryController extends DiffusionController { ->setErrors(array($empty_message)); } - $tabs = $this->buildTabsView('home'); + $tabs = $this->buildTabsView('code'); $clone_uri = $drequest->generateURI( array( @@ -113,6 +111,15 @@ final class DiffusionRepositoryController extends DiffusionController { $clone_text = pht('Clone'); } + $actions_button = id(new PHUIButtonView()) + ->setTag('a') + ->setText(pht('Actions')) + ->setIcon('fa-bars') + ->addClass('mmr') + ->setColor(PHUIButtonView::GREY) + ->setDropdown(true) + ->setDropdownMenu($actions); + $clone_button = id(new PHUIButtonView()) ->setTag('a') ->setText($clone_text) @@ -123,7 +130,7 @@ final class DiffusionRepositoryController extends DiffusionController { $bar = id(new PHUILeftRightView()) ->setLeft($locate_file) - ->setRight(array($this->branchButton, $clone_button)) + ->setRight(array($this->branchButton, $actions_button, $clone_button)) ->addClass('diffusion-action-bar'); $view = id(new PHUITwoColumnView()) @@ -293,6 +300,9 @@ final class DiffusionRepositoryController extends DiffusionController { private function buildHeaderView(PhabricatorRepository $repository) { $viewer = $this->getViewer(); + $drequest = $this->getDiffusionRequest(); + $search = $this->renderSearchForm(); + $header = id(new PHUIHeaderView()) ->setHeader($repository->getName()) ->setUser($viewer) @@ -300,6 +310,7 @@ final class DiffusionRepositoryController extends DiffusionController { ->setProfileHeader(true) ->setImage($repository->getProfileImageURI()) ->setImageEditURL('/diffusion/picture/'.$repository->getID().'/') + ->addActionItem($search) ->addClass('diffusion-profile-header'); if (!$repository->isTracked()) { @@ -315,6 +326,14 @@ final class DiffusionRepositoryController extends DiffusionController { $header->setStatus('fa-check', 'bluegrey', pht('Active')); } + if (!$repository->isSVN()) { + $default = $repository->getDefaultBranch(); + if ($default != $drequest->getBranch()) { + $branch_tag = $this->renderBranchTag($drequest); + $header->addTag($branch_tag); + } + } + return $header; } @@ -546,8 +565,22 @@ final class DiffusionRepositoryController extends DiffusionController { $browse_uri = $drequest->generateURI(array('action' => 'browse')); $pager->setURI($browse_uri, 'offset'); + $repository_name = $repository->getName(); + $branch_name = $drequest->getBranch(); + if (strlen($branch_name)) { + $repository_name .= ' ('.$branch_name.')'; + } + + $header = phutil_tag( + 'a', + array( + 'href' => $browse_uri, + 'class' => 'diffusion-view-browse-header', + ), + $repository_name); + return id(new PHUIObjectBoxView()) - ->setHeaderText($repository->getName()) + ->setHeaderText($header) ->setBackground(PHUIObjectBoxView::BLUE_PROPERTY) ->setTable($browse_table) ->setPager($pager); diff --git a/src/applications/diffusion/controller/DiffusionRepositoryEditController.php b/src/applications/diffusion/controller/DiffusionRepositoryEditController.php index d3801fa206..bf3f389806 100644 --- a/src/applications/diffusion/controller/DiffusionRepositoryEditController.php +++ b/src/applications/diffusion/controller/DiffusionRepositoryEditController.php @@ -37,9 +37,6 @@ final class DiffusionRepositoryEditController $crumbs->setBorder(true); $title = pht('Choose Repository Type'); - $header = id(new PHUIHeaderView()) - ->setHeader(pht('Create Repository')) - ->setHeaderIcon('fa-plus-square'); $layout = id(new AphrontMultiColumnView()) ->setFluidLayout(true); @@ -47,8 +44,10 @@ final class DiffusionRepositoryEditController $create_uri = $request->getRequestURI(); foreach ($vcs_types as $vcs_key => $vcs_type) { + $image = idx($vcs_type, 'image'); + $image = PhabricatorFile::loadBuiltin($viewer, $image); $action = id(new PHUIActionPanelView()) - ->setIcon(idx($vcs_type, 'icon')) + ->setImage($image->getBestURI()) ->setHeader(idx($vcs_type, 'create.header')) ->setHref($create_uri->alter('vcs', $vcs_key)) ->setSubheader(idx($vcs_type, 'create.subheader')); @@ -62,9 +61,12 @@ final class DiffusionRepositoryEditController $observe_href = PhabricatorEnv::getDoclink( 'Diffusion User Guide: Existing Repositories'); + require_celerity_resource('diffusion-css'); + + $image = PhabricatorFile::loadBuiltin($viewer, 'repo/repo.png'); $hints->addColumn( id(new PHUIActionPanelView()) - ->setIcon('fa-book') + ->setImage($image->getBestURI()) ->setHeader(pht('Import or Observe an Existing Repository')) ->setHref($observe_href) ->setSubheader( @@ -72,12 +74,15 @@ final class DiffusionRepositoryEditController 'Review the documentation describing how to import or observe an '. 'existing repository.'))); + $layout = id(new PHUIBoxView()) + ->addClass('diffusion-create-repo') + ->appendChild($layout); + $view = id(new PHUITwoColumnView()) - ->setHeader($header) + ->setFixed(true) ->setFooter( array( $layout, - phutil_tag('br'), $hints, )); diff --git a/src/applications/diffusion/controller/DiffusionTagListController.php b/src/applications/diffusion/controller/DiffusionTagListController.php index f7cd032e1a..15b8dc93ae 100644 --- a/src/applications/diffusion/controller/DiffusionTagListController.php +++ b/src/applications/diffusion/controller/DiffusionTagListController.php @@ -11,6 +11,7 @@ final class DiffusionTagListController extends DiffusionController { if ($response) { return $response; } + require_celerity_resource('diffusion-css'); $viewer = $this->getViewer(); $drequest = $this->getDiffusionRequest(); @@ -50,6 +51,11 @@ final class DiffusionTagListController extends DiffusionController { ->setHeader(pht('Tags')) ->setHeaderIcon('fa-tags'); + if (!$repository->isSVN()) { + $branch_tag = $this->renderBranchTag($drequest); + $header->addTag($branch_tag); + } + if (!$tags) { $content = $this->renderStatusMessage( pht('No Tags'), diff --git a/src/applications/diffusion/herald/DiffusionCommitRevisionAcceptedHeraldField.php b/src/applications/diffusion/herald/DiffusionCommitRevisionAcceptedHeraldField.php index 810b58f2a1..a125b580eb 100644 --- a/src/applications/diffusion/herald/DiffusionCommitRevisionAcceptedHeraldField.php +++ b/src/applications/diffusion/herald/DiffusionCommitRevisionAcceptedHeraldField.php @@ -19,36 +19,15 @@ final class DiffusionCommitRevisionAcceptedHeraldField return null; } - $status = $revision->getStatus(); - - switch ($status) { - case ArcanistDifferentialRevisionStatus::ACCEPTED: - return $revision->getPHID(); - case ArcanistDifferentialRevisionStatus::CLOSED: - if ($revision->hasRevisionProperty( - DifferentialRevision::PROPERTY_CLOSED_FROM_ACCEPTED)) { - - if ($revision->getProperty( - DifferentialRevision::PROPERTY_CLOSED_FROM_ACCEPTED)) { - return $revision->getPHID(); - } else { - return null; - } - } else { - // continue on to old-style precommitRevisionStatus - break; - } - default: - return null; + if ($revision->isAccepted()) { + return $revision->getPHID(); } - $data = $object->getCommitData(); - $status = $data->getCommitDetail('precommitRevisionStatus'); - - switch ($status) { - case ArcanistDifferentialRevisionStatus::ACCEPTED: - case ArcanistDifferentialRevisionStatus::CLOSED: + $was_accepted = DifferentialRevision::PROPERTY_CLOSED_FROM_ACCEPTED; + if ($revision->isPublished()) { + if ($revision->getProperty($was_accepted)) { return $revision->getPHID(); + } } return null; diff --git a/src/applications/diffusion/herald/DiffusionPreCommitContentRevisionAcceptedHeraldField.php b/src/applications/diffusion/herald/DiffusionPreCommitContentRevisionAcceptedHeraldField.php index e19878e627..63dac6baa6 100644 --- a/src/applications/diffusion/herald/DiffusionPreCommitContentRevisionAcceptedHeraldField.php +++ b/src/applications/diffusion/herald/DiffusionPreCommitContentRevisionAcceptedHeraldField.php @@ -15,21 +15,19 @@ final class DiffusionPreCommitContentRevisionAcceptedHeraldField public function getHeraldFieldValue($object) { $revision = $this->getAdapter()->getRevision(); - if (!$revision) { return null; } - switch ($revision->getStatus()) { - case ArcanistDifferentialRevisionStatus::ACCEPTED: - return $revision->getPHID(); - case ArcanistDifferentialRevisionStatus::CLOSED: - if ($revision->getProperty( - DifferentialRevision::PROPERTY_CLOSED_FROM_ACCEPTED)) { + if ($revision->isAccepted()) { + return $revision->getPHID(); + } - return $revision->getPHID(); - } - break; + $was_accepted = DifferentialRevision::PROPERTY_CLOSED_FROM_ACCEPTED; + if ($revision->isPublished()) { + if ($revision->getProperty($was_accepted)) { + return $revision->getPHID(); + } } return null; diff --git a/src/applications/diffusion/management/DiffusionRepositoryBranchesManagementPanel.php b/src/applications/diffusion/management/DiffusionRepositoryBranchesManagementPanel.php index a735b8d580..17ee506b16 100644 --- a/src/applications/diffusion/management/DiffusionRepositoryBranchesManagementPanel.php +++ b/src/applications/diffusion/management/DiffusionRepositoryBranchesManagementPanel.php @@ -110,6 +110,7 @@ final class DiffusionRepositoryBranchesManagementPanel ->execute(); $branches = DiffusionRepositoryRef::loadAllFromDictionaries($branches); $branches = $pager->sliceResults($branches); + $can_close_branches = ($repository->isHg()); $rows = array(); foreach ($branches as $branch) { @@ -117,8 +118,25 @@ final class DiffusionRepositoryBranchesManagementPanel $tracking = $repository->shouldTrackBranch($branch_name); $autoclosing = $repository->shouldAutocloseBranch($branch_name); + $default = $repository->getDefaultBranch(); + $icon = null; + if ($default == $branch->getShortName()) { + $icon = id(new PHUIIconView()) + ->setIcon('fa-code-fork'); + } + + $fields = $branch->getRawFields(); + $closed = idx($fields, 'closed'); + if ($closed) { + $status = pht('Closed'); + } else { + $status = pht('Open'); + } + $rows[] = array( + $icon, $branch_name, + $status, $tracking ? pht('Tracking') : pht('Off'), $autoclosing ? pht('Autoclose On') : pht('Off'), ); @@ -126,16 +144,28 @@ final class DiffusionRepositoryBranchesManagementPanel $branch_table = new AphrontTableView($rows); $branch_table->setHeaders( array( + '', pht('Branch'), + pht('Status'), pht('Track'), pht('Autoclose'), )); $branch_table->setColumnClasses( array( + '', 'pri', 'narrow', + 'narrow', 'wide', )); + $branch_table->setColumnVisibility( + array( + true, + true, + $can_close_branches, + true, + true, + )); $box = id(new PHUIObjectBoxView()) ->setHeaderText(pht('Branch Status')) diff --git a/src/applications/diffusion/panel/DiffusionSetPasswordSettingsPanel.php b/src/applications/diffusion/panel/DiffusionSetPasswordSettingsPanel.php index 4c84d430cf..a41f322a3d 100644 --- a/src/applications/diffusion/panel/DiffusionSetPasswordSettingsPanel.php +++ b/src/applications/diffusion/panel/DiffusionSetPasswordSettingsPanel.php @@ -233,6 +233,7 @@ final class DiffusionSetPasswordSettingsPanel extends PhabricatorSettingsPanel { $object_box = id(new PHUIObjectBoxView()) ->setHeaderText($title) + ->setBackground(PHUIObjectBoxView::BLUE_PROPERTY) ->setForm($form) ->setFormErrors($errors); @@ -258,6 +259,7 @@ final class DiffusionSetPasswordSettingsPanel extends PhabricatorSettingsPanel { $remove_box = id(new PHUIObjectBoxView()) ->setHeaderText(pht('Remove VCS Password')) + ->setBackground(PHUIObjectBoxView::BLUE_PROPERTY) ->setForm($remove_form); $saved = null; diff --git a/src/applications/diffusion/view/DiffusionBranchListView.php b/src/applications/diffusion/view/DiffusionBranchListView.php index 1a9df42e05..a2ba34893e 100644 --- a/src/applications/diffusion/view/DiffusionBranchListView.php +++ b/src/applications/diffusion/view/DiffusionBranchListView.php @@ -74,14 +74,6 @@ final class DiffusionBranchListView extends DiffusionView { } } - $fields = $branch->getRawFields(); - $closed = idx($fields, 'closed'); - if ($closed) { - $status = pht('Closed'); - } else { - $status = pht('Open'); - } - $browse_href = $drequest->generateURI( array( 'action' => 'browse', @@ -129,6 +121,18 @@ final class DiffusionBranchListView extends DiffusionView { } $item->addAttribute(array($datetime)); + if ($can_close_branches) { + $fields = $branch->getRawFields(); + $closed = idx($fields, 'closed'); + if ($closed) { + $status = pht('Branch Closed'); + $item->setDisabled(true); + } else { + $status = pht('Branch Open'); + } + $item->addAttribute($status); + } + $list->addItem($item); } diff --git a/src/applications/diffusion/view/DiffusionPatternSearchView.php b/src/applications/diffusion/view/DiffusionPatternSearchView.php new file mode 100644 index 0000000000..a679b51400 --- /dev/null +++ b/src/applications/diffusion/view/DiffusionPatternSearchView.php @@ -0,0 +1,124 @@ +path = $path; + return $this; + } + + public function setMatches(array $matches) { + $this->matches = $matches; + return $this; + } + + public function setPattern($pattern) { + $this->pattern = $pattern; + return $this; + } + + public function render() { + $drequest = $this->getDiffusionRequest(); + $path = $this->path; + $pattern = $this->pattern; + $rows = array(); + + foreach ($this->matches as $result) { + list($line, $string) = $result; + + $matches = null; + $count = @preg_match_all( + '('.$pattern.')u', + $string, + $matches, + PREG_OFFSET_CAPTURE); + + if (!$count) { + $output = ltrim($string); + } else { + $output = array(); + $cursor = 0; + $length = strlen($string); + foreach ($matches[0] as $match) { + $offset = $match[1]; + if ($cursor != $offset) { + $output[] = array( + 'text' => substr($string, $cursor, $offset), + 'highlight' => false, + ); + } + $output[] = array( + 'text' => $match[0], + 'highlight' => true, + ); + $cursor = $offset + strlen($match[0]); + } + if ($cursor != $length) { + $output[] = array( + 'text' => substr($string, $cursor), + 'highlight' => false, + ); + } + + if ($output) { + $output[0]['text'] = ltrim($output[0]['text']); + } + + foreach ($output as $key => $segment) { + if ($segment['highlight']) { + $output[$key] = phutil_tag('strong', array(), $segment['text']); + } else { + $output[$key] = $segment['text']; + } + } + } + + $string = phutil_tag( + 'pre', + array('class' => 'PhabricatorMonospaced phui-source-fragment'), + $output); + + $href = $drequest->generateURI(array( + 'action' => 'browse', + 'path' => $path, + 'line' => $line, + )); + + $rows[] = array( + phutil_tag('a', array('href' => $href), $line), + $string, + ); + } + + $path_title = Filesystem::readablePath($this->path, $drequest->getPath()); + + $href = $drequest->generateURI(array( + 'action' => 'browse', + 'path' => $path_title, + )); + + $title = phutil_tag('a', array('href' => $href), $path_title); + + + $table = id(new AphrontTableView($rows)) + ->setClassName('remarkup-code') + ->setHeaders(array(pht('Line'), pht('String'))) + ->setColumnClasses(array('n', 'wide')); + + $header = id(new PHUIHeaderView()) + ->setHeader($title); + + $box = id(new PHUIObjectBoxView()) + ->setHeader($header) + ->setBackground(PHUIObjectBoxView::BLUE_PROPERTY) + ->setTable($table); + + return $box->render(); + } + + +} diff --git a/src/applications/phame/controller/post/PhamePostViewController.php b/src/applications/phame/controller/post/PhamePostViewController.php index 63adedb7ae..7dbaddc17a 100644 --- a/src/applications/phame/controller/post/PhamePostViewController.php +++ b/src/applications/phame/controller/post/PhamePostViewController.php @@ -18,6 +18,25 @@ final class PhamePostViewController $is_live = $this->getIsLive(); $is_external = $this->getIsExternal(); + // Register a blog "view" count + // + if (!$post->isDraft() && !$post->isArchived()) { + $unguarded = AphrontWriteGuard::beginScopedUnguardedWrites(); + $xactions = array(); + $xactions[] = id(new PhamePostTransaction()) + ->setTransactionType(PhamePostViewsTransaction::TRANSACTIONTYPE) + ->setNewValue(null); + + $editor = id(new PhamePostEditor()) + ->setActor($viewer) + ->setContentSourceFromRequest($request) + ->setContinueOnMissingFields(true) + ->setContinueOnNoEffect(true); + + $editor->applyTransactions($post, $xactions); + unset($unguarded); + } + $header = id(new PHUIHeaderView()) ->addClass('phame-header-bar') ->setUser($viewer); @@ -151,6 +170,11 @@ final class PhamePostViewController ->setUser($viewer) ->setObject($post); + $views = id(new PhutilNumber($post->getViews())); + $properties->addProperty( + pht('Views'), + pht('%s', $views)); + $is_live = $this->getIsLive(); $is_external = $this->getIsExternal(); $next_view = new PhameNextPostView(); diff --git a/src/applications/phame/editor/PhamePostEditor.php b/src/applications/phame/editor/PhamePostEditor.php index 488d7a4938..845538dc12 100644 --- a/src/applications/phame/editor/PhamePostEditor.php +++ b/src/applications/phame/editor/PhamePostEditor.php @@ -41,6 +41,12 @@ final class PhamePostEditor if ($object->isDraft() || $object->isArchived()) { return false; } + foreach ($xactions as $xaction) { + switch ($xaction->getTransactionType()) { + case PhamePostViewsTransaction::TRANSACTIONTYPE: + return false; + } + } return true; } diff --git a/src/applications/phame/storage/PhamePost.php b/src/applications/phame/storage/PhamePost.php index a9525e0be7..c3ec4d4608 100644 --- a/src/applications/phame/storage/PhamePost.php +++ b/src/applications/phame/storage/PhamePost.php @@ -22,6 +22,7 @@ final class PhamePost extends PhameDAO protected $phameTitle; protected $body; protected $visibility; + protected $views; protected $configData; protected $datePublished; protected $blogPHID; @@ -40,7 +41,8 @@ final class PhamePost extends PhameDAO ->setBlogPHID($blog->getPHID()) ->attachBlog($blog) ->setDatePublished(PhabricatorTime::getNow()) - ->setVisibility(PhameConstants::VISIBILITY_PUBLISHED); + ->setVisibility(PhameConstants::VISIBILITY_PUBLISHED) + ->setViews(0); return $post; } @@ -128,6 +130,7 @@ final class PhamePost extends PhameDAO 'subtitle' => 'text64', 'phameTitle' => 'sort64?', 'visibility' => 'uint32', + 'views' => 'uint32', 'mailKey' => 'bytes20', 'headerImagePHID' => 'phid?', diff --git a/src/applications/phame/xaction/PhamePostViewsTransaction.php b/src/applications/phame/xaction/PhamePostViewsTransaction.php new file mode 100644 index 0000000000..2260a1d752 --- /dev/null +++ b/src/applications/phame/xaction/PhamePostViewsTransaction.php @@ -0,0 +1,18 @@ +getViews(); + } + + public function applyInternalEffects($object, $value) { + $views = $object->getViews(); + $views++; + $object->setViews($views); + } + +} diff --git a/src/applications/repository/constants/PhabricatorRepositoryType.php b/src/applications/repository/constants/PhabricatorRepositoryType.php index 998bff0d2e..593732ddd8 100644 --- a/src/applications/repository/constants/PhabricatorRepositoryType.php +++ b/src/applications/repository/constants/PhabricatorRepositoryType.php @@ -26,18 +26,21 @@ final class PhabricatorRepositoryType extends Phobject { self::REPOSITORY_TYPE_GIT => array( 'name' => pht('Git'), 'icon' => 'fa-git', + 'image' => 'repo/repo-git.png', 'create.header' => pht('Create Git Repository'), 'create.subheader' => pht('Create a new Git repository.'), ), self::REPOSITORY_TYPE_MERCURIAL => array( 'name' => pht('Mercurial'), 'icon' => 'fa-code-fork', + 'image' => 'repo/repo-hg.png', 'create.header' => pht('Create Mercurial Repository'), 'create.subheader' => pht('Create a new Mercurial repository.'), ), self::REPOSITORY_TYPE_SVN => array( 'name' => pht('Subversion'), 'icon' => 'fa-database', + 'image' => 'repo/repo-svn.png', 'create.header' => pht('Create Subversion Repository'), 'create.subheader' => pht('Create a new Subversion repository.'), ), diff --git a/src/applications/repository/worker/commitmessageparser/PhabricatorRepositoryCommitMessageParserWorker.php b/src/applications/repository/worker/commitmessageparser/PhabricatorRepositoryCommitMessageParserWorker.php index ab11a83b38..fe8abd5925 100644 --- a/src/applications/repository/worker/commitmessageparser/PhabricatorRepositoryCommitMessageParserWorker.php +++ b/src/applications/repository/worker/commitmessageparser/PhabricatorRepositoryCommitMessageParserWorker.php @@ -186,11 +186,6 @@ abstract class PhabricatorRepositoryCommitMessageParserWorker $revision = $revision_query->executeOne(); if ($revision) { - if (!$data->getCommitDetail('precommitRevisionStatus')) { - $data->setCommitDetail( - 'precommitRevisionStatus', - $revision->getStatus()); - } $commit_drev = DiffusionCommitHasRevisionEdgeType::EDGECONST; id(new PhabricatorEdgeEditor()) ->addEdge($commit->getPHID(), $commit_drev, $revision->getPHID()) @@ -205,9 +200,11 @@ abstract class PhabricatorRepositoryCommitMessageParserWorker $should_close = !$revision->isPublished() && $should_autoclose; if ($should_close) { - $commit_close_xaction = id(new DifferentialTransaction()) - ->setTransactionType(DifferentialTransaction::TYPE_ACTION) - ->setNewValue(DifferentialAction::ACTION_CLOSE) + $type_close = DifferentialRevisionCloseTransaction::TRANSACTIONTYPE; + + $commit_close_xaction = id(new DifferentialTransaction()) + ->setTransactionType($type_close) + ->setNewValue(true) ->setMetadataValue('isCommitClose', true); $commit_close_xaction->setMetadataValue( diff --git a/src/applications/search/query/PhabricatorNamedQueryQuery.php b/src/applications/search/query/PhabricatorNamedQueryQuery.php index ce54ae9a84..3decff5494 100644 --- a/src/applications/search/query/PhabricatorNamedQueryQuery.php +++ b/src/applications/search/query/PhabricatorNamedQueryQuery.php @@ -28,55 +28,46 @@ final class PhabricatorNamedQueryQuery return $this; } - protected function loadPage() { - $table = new PhabricatorNamedQuery(); - $conn_r = $table->establishConnection('r'); - - $data = queryfx_all( - $conn_r, - 'SELECT * FROM %T %Q %Q %Q', - $table->getTableName(), - $this->buildWhereClause($conn_r), - $this->buildOrderClause($conn_r), - $this->buildLimitClause($conn_r)); - - return $table->loadAllFromArray($data); + public function newResultObject() { + return new PhabricatorNamedQuery(); } - protected function buildWhereClause(AphrontDatabaseConnection $conn_r) { - $where = array(); + protected function loadPage() { + return $this->loadStandardPage($this->newResultObject()); + } - if ($this->ids) { + protected function buildWhereClauseParts(AphrontDatabaseConnection $conn) { + $where = parent::buildWhereClauseParts($conn); + + if ($this->ids !== null) { $where[] = qsprintf( - $conn_r, + $conn, 'id IN (%Ld)', $this->ids); } - if ($this->engineClassNames) { + if ($this->engineClassNames !== null) { $where[] = qsprintf( - $conn_r, + $conn, 'engineClassName IN (%Ls)', $this->engineClassNames); } - if ($this->userPHIDs) { + if ($this->userPHIDs !== null) { $where[] = qsprintf( - $conn_r, + $conn, 'userPHID IN (%Ls)', $this->userPHIDs); } - if ($this->queryKeys) { + if ($this->queryKeys !== null) { $where[] = qsprintf( - $conn_r, + $conn, 'queryKey IN (%Ls)', $this->queryKeys); } - $where[] = $this->buildPagingClause($conn_r); - - return $this->formatWhereClause($where); + return $where; } public function getQueryApplicationClass() { diff --git a/src/applications/settings/controller/PhabricatorSettingsMainController.php b/src/applications/settings/controller/PhabricatorSettingsMainController.php index 4c1bfab6f1..1e4d4a0b70 100644 --- a/src/applications/settings/controller/PhabricatorSettingsMainController.php +++ b/src/applications/settings/controller/PhabricatorSettingsMainController.php @@ -121,18 +121,18 @@ final class PhabricatorSettingsMainController } $header = id(new PHUIHeaderView()) - ->setHeader($header_text) - ->setHeaderIcon('fa-pencil'); + ->setHeader($header_text); $title = $panel->getPanelName(); $view = id(new PHUITwoColumnView()) ->setHeader($header) - ->setFooter($response); + ->setFixed(true) + ->setNavigation($nav) + ->setMainColumn($response); return $this->newPage() ->setTitle($title) - ->setNavigation($nav) ->setCrumbs($crumbs) ->appendChild($view); diff --git a/src/applications/settings/panel/PhabricatorConpherencePreferencesSettingsPanel.php b/src/applications/settings/panel/PhabricatorConpherencePreferencesSettingsPanel.php index 87643a08ad..6ed6325d67 100644 --- a/src/applications/settings/panel/PhabricatorConpherencePreferencesSettingsPanel.php +++ b/src/applications/settings/panel/PhabricatorConpherencePreferencesSettingsPanel.php @@ -6,7 +6,7 @@ final class PhabricatorConpherencePreferencesSettingsPanel const PANELKEY = 'conpherence'; public function getPanelName() { - return pht('Conpherence Preferences'); + return pht('Conpherence'); } public function getPanelGroupKey() { diff --git a/src/applications/transactions/editengine/PhabricatorEditEngine.php b/src/applications/transactions/editengine/PhabricatorEditEngine.php index c9d8a6887f..d09e2450a1 100644 --- a/src/applications/transactions/editengine/PhabricatorEditEngine.php +++ b/src/applications/transactions/editengine/PhabricatorEditEngine.php @@ -1141,10 +1141,8 @@ abstract class PhabricatorEditEngine if ($this->getIsCreate()) { $header_text = $this->getFormHeaderText($object); - $header_icon = 'fa-plus-square'; } else { $header_text = $this->getObjectEditTitleText($object); - $header_icon = 'fa-pencil'; } $show_preview = !$request->isAjax(); @@ -1185,8 +1183,7 @@ abstract class PhabricatorEditEngine $crumbs = $this->buildCrumbs($object, $final = true); $header = id(new PHUIHeaderView()) - ->setHeader($header_text) - ->setHeaderIcon($header_icon); + ->setHeader($header_text); $crumbs->setBorder(true); if ($action_button) { @@ -1216,8 +1213,6 @@ abstract class PhabricatorEditEngine $view->setHeader($header); } - $view->setFooter($content); - $page = $controller->newPage() ->setTitle($header_text) ->setCrumbs($crumbs) @@ -1225,7 +1220,11 @@ abstract class PhabricatorEditEngine $navigation = $this->getNavigation(); if ($navigation) { - $page->setNavigation($navigation); + $view->setFixed(true); + $view->setNavigation($navigation); + $view->setMainColumn($content); + } else { + $view->setFooter($content); } return $page; diff --git a/src/docs/contributor/bug_reports.diviner b/src/docs/contributor/bug_reports.diviner index eec9569dc6..fcf3eac5c5 100644 --- a/src/docs/contributor/bug_reports.diviner +++ b/src/docs/contributor/bug_reports.diviner @@ -140,7 +140,7 @@ Reproducibility The most important part of your report content is instructions on how to reproduce the issue. What did you do? If you do it again, does it still break? -Does it depend on a specific browser? Can you reproduce the issue on a free +Does it depend on a specific browser? Can you reproduce the issue on a test instance on `admin.phabricator.com`? It is nearly impossible for us to resolve many issues if we can not reproduce diff --git a/support/aphlict/server/lib/AphlictClientServer.js b/support/aphlict/server/lib/AphlictClientServer.js index 0d23d4f4f1..989b3f1db1 100644 --- a/support/aphlict/server/lib/AphlictClientServer.js +++ b/support/aphlict/server/lib/AphlictClientServer.js @@ -92,8 +92,14 @@ JX.install('AphlictClientServer', { var server = this._server.listen.apply(this._server, arguments); var wss = new WebSocket.Server({server: server}); - wss.on('connection', function(ws) { - var path = url.parse(ws.upgradeReq.url).pathname; + // This function checks for upgradeReq which is only available in + // ws2 by default, not ws3. See T12755 for more information. + wss.on('connection', function(ws, request) { + if ('upgradeReq' in ws) { + request = ws.upgradeReq; + } + + var path = url.parse(request.url).pathname; var instance = self._parseInstanceFromPath(path); var listener = self.getListenerList(instance).addListener(ws); diff --git a/webroot/rsrc/css/aphront/typeahead.css b/webroot/rsrc/css/aphront/typeahead.css index 92970386bf..05fec1e70c 100644 --- a/webroot/rsrc/css/aphront/typeahead.css +++ b/webroot/rsrc/css/aphront/typeahead.css @@ -28,6 +28,9 @@ div.jx-typeahead-results a.jx-result { color: {$darkgreytext}; display: block; font-size: {$normalfontsize}; + overflow: hidden; + white-space: nowrap; + text-overflow: ellipsis; } div.jx-typeahead-results a.jx-result + a.jx-result { @@ -68,7 +71,8 @@ div.jx-typeahead-results a.diffusion-locate-file { } .diffusion-locate-file strong { - color: {$blue}; + color: {$fire}; + text-decoration: underline; } .diffusion-locate-file .phui-icon-view { diff --git a/webroot/rsrc/css/application/base/standard-page-view.css b/webroot/rsrc/css/application/base/standard-page-view.css index d930a73797..755ed1d59c 100644 --- a/webroot/rsrc/css/application/base/standard-page-view.css +++ b/webroot/rsrc/css/application/base/standard-page-view.css @@ -186,6 +186,7 @@ a.handle-status-closed:hover { .device .phabricator-standard-page-tabs { margin-bottom: 20px; + padding: 0 12px; } .device-phone .phabricator-standard-page-tabs { diff --git a/webroot/rsrc/css/application/diffusion/diffusion.css b/webroot/rsrc/css/application/diffusion/diffusion.css index 4d18e3dcb7..2f7754f9f8 100644 --- a/webroot/rsrc/css/application/diffusion/diffusion.css +++ b/webroot/rsrc/css/application/diffusion/diffusion.css @@ -12,6 +12,10 @@ display: block; } +.device-phone .diffusion-profile-header .phui-header-col1 { + display: none; +} + .diffusion-action-bar { margin-bottom: 16px; } @@ -33,6 +37,22 @@ display: block; } +.device-phone .diffusion-action-bar .button .phui-button-text { + visibility: hidden; + width: 0; + margin-left: 8px; +} + +.device-phone .full-mobile-buttons.diffusion-action-bar .phui-lr-container + .phui-left-view { + display: inline-block; +} + +.device-phone .full-mobile-buttons.diffusion-action-bar .phui-lr-container + .phui-right-view { + display: inline-block; +} + .diffusion-profile-locate .phui-form-view { margin: 0; padding: 0; @@ -60,6 +80,12 @@ padding: 0; } +.diffusion-view-browse-header { + display: block; + padding: 2px 0; + font-size: {$biggestfontsize}; +} + /* - List Styles ------------------------------------------------------------*/ .diffusion-history-list .phui-oi-link { @@ -106,6 +132,12 @@ margin-right: 4px; } +.diffusion-header-branch-tag.phui-tag-view.phui-tag-type-outline + .phui-tag-core { + padding: 3px 12px 2px; + font-size: {$normalfontsize}; +} + /* - Browse Styles ----------------------------------------------------------*/ .diffusion-browse-table .commit-detail { @@ -116,6 +148,58 @@ color: {$darkbluetext}; } +.diffusion-search-result-header.phui-header-shell { + border: none; + padding-bottom: 24px; +} + +/* - Search Input ------------------------------------------------------------*/ + +.diffusion-search-form-view { + width: 240px; +} + +.diffusion-search-form-view .diffusion-search-input { + width: 240px; + border-radius: 20px; + padding-left: 12px; +} + +.device-phone .diffusion-browse-header .diffusion-search-form-view, +.device-phone .diffusion-profile-header .diffusion-search-form-view { + display: none; +} + +.diffusion-mobile-search-form { + display: none; +} + +.device-phone .diffusion-mobile-search-form { + display: block; +} + +.device-phone .diffusion-search-form-view { + width: 100%; + margin-bottom: 20px; +} + +.device-phone .diffusion-search-form-view .diffusion-search-input { + width: 100%; +} + +/* - Create Repository -------------------------------------------------------*/ + +.diffusion-create-repo { + margin-top: 32px; + margin-bottom: 20px; +} + +.device .diffusion-create-repo { + margin-top: 0; + margin-bottom: 0; +} + + /* - Phone Style ------------------------------------------------------------*/ .device-phone.diffusion-history-view .phui-two-column-view diff --git a/webroot/rsrc/css/application/search/search-results.css b/webroot/rsrc/css/application/search/search-results.css index 02d40fa179..86a0cc075a 100644 --- a/webroot/rsrc/css/application/search/search-results.css +++ b/webroot/rsrc/css/application/search/search-results.css @@ -12,9 +12,10 @@ } .phui-source-fragment strong { - background-color: {$lightyellow}; - font-weight: normal; + background-color: {$gentle.highlight}; + font-weight: 600; color: {$blacktext}; + letter-spacing: 0.02em; } .phui-fulltext-tokens { diff --git a/webroot/rsrc/css/phui/phui-left-right.css b/webroot/rsrc/css/phui/phui-left-right.css index 2d260758b4..ac092cd226 100644 --- a/webroot/rsrc/css/phui/phui-left-right.css +++ b/webroot/rsrc/css/phui/phui-left-right.css @@ -21,6 +21,14 @@ text-align: right; } +.phui-left-view .button { + margin-right: 8px; +} + +.phui-right-view .button { + margin-left: 8px; +} + .phui-lr-view-top .phui-left-view, .phui-lr-view-top .phui-right-view { vertical-align: top; diff --git a/webroot/rsrc/css/phui/phui-two-column-view.css b/webroot/rsrc/css/phui/phui-two-column-view.css index 2957528929..dba1519597 100644 --- a/webroot/rsrc/css/phui/phui-two-column-view.css +++ b/webroot/rsrc/css/phui/phui-two-column-view.css @@ -185,6 +185,15 @@ padding: 0 12px; } +.device-phone .phui-two-column-tabs .phui-list-view.phui-list-tabbar { + text-align: center; +} + +.device-phone .phui-two-column-tabs .phui-list-view.phui-list-tabbar > li { + float: none; + display: inline-block; +} + /* Info View */ .phui-two-column-view .phui-info-view { @@ -235,3 +244,54 @@ .phui-document-view { margin: 0 0 20px 0; } + +/*- Fixed Styles with Navigation -------------------------------------------- */ + +.phui-two-column-fixed.phui-two-column-view .phui-two-column-header { + background: transparent; + border: none; + margin-bottom: 0; +} + +.phui-two-column-fixed.phui-two-column-view .phui-side-column + .phui-box-border { + background: transparent; + border: none; + padding: 0; + width: 180px; +} + +.device-desktop + .phui-two-column-fixed.phui-two-column-view.phui-side-column-left + .phui-side-column { + width: 200px; +} + +.device-desktop + .phui-two-column-fixed.phui-two-column-view.phui-side-column-left + .phui-main-column { + width: calc(100% - 200px) +} + +.phui-two-column-fixed.phui-two-column-view .phui-basic-nav + .phabricator-side-menu { + background: transparent; +} + +.phui-two-column-fixed.phui-two-column-view + .phui-basic-nav .phabricator-side-menu .phui-list-item-selected { + border-radius: 3px; + background-color: #f5f9ff; + border: 1px solid {$sky}; + padding-left: 3px; +} + +.phui-two-column-fixed.phui-two-column-view .phui-basic-nav + .phabricator-side-menu .phui-list-item-href { + border-radius: 3px; +} + +.phui-two-column-fixed.phui-two-column-view .phui-header-action-links + .phui-mobile-menu { + display: block; +} diff --git a/webroot/rsrc/css/sprite-login.css b/webroot/rsrc/css/sprite-login.css index 72638120f2..ec4e082031 100644 --- a/webroot/rsrc/css/sprite-login.css +++ b/webroot/rsrc/css/sprite-login.css @@ -14,7 +14,7 @@ only screen and (-webkit-min-device-pixel-ratio: 1.5), only screen and (min-resolution: 1.5dppx) { .sprite-login { background-image: url(/rsrc/image/sprite-login-X2.png); - background-size: 145px 174px; + background-size: 145px 145px; } } @@ -35,28 +35,24 @@ only screen and (min-resolution: 1.5dppx) { background-position: -87px 0px; } -.login-Dropbox { +.login-Facebook { background-position: -116px 0px; } -.login-Facebook { +.login-Generic { background-position: 0px -29px; } -.login-Generic { +.login-Github { background-position: -29px -29px; } -.login-Github { +.login-Google { background-position: -58px -29px; } -.login-Google { - background-position: -87px -29px; -} - .login-HTTP { - background-position: -116px -29px; + background-position: -87px -29px; } .login-Jira { @@ -67,54 +63,42 @@ only screen and (min-resolution: 1.5dppx) { background-position: -29px -58px; } -.login-Linkedin { +.login-MediaWiki { background-position: -58px -58px; } -.login-MediaWiki { +.login-PayPal { background-position: -87px -58px; } -.login-Openid { - background-position: -116px -58px; -} - -.login-PayPal { +.login-Phabricator { background-position: 0px -87px; } -.login-Phabricator { - background-position: -58px -87px; -} - .login-Slack { - background-position: -87px -87px; + background-position: -29px -87px; } .login-Stripe { - background-position: -116px -87px; + background-position: -58px -87px; } .login-TestPayment { - background-position: 0px -116px; + background-position: -87px -87px; } .login-TwitchTV { - background-position: -29px -116px; + background-position: 0px -116px; } .login-Twitter { - background-position: -58px -116px; + background-position: -29px -116px; } .login-WePay { - background-position: -87px -116px; + background-position: -58px -116px; } .login-WordPressCOM { - background-position: -116px -116px; -} - -.login-Yahoo { - background-position: 0px -145px; + background-position: -87px -116px; } diff --git a/webroot/rsrc/image/sprite-login-X2.png b/webroot/rsrc/image/sprite-login-X2.png index 78a044ee72..2d3c118745 100644 Binary files a/webroot/rsrc/image/sprite-login-X2.png and b/webroot/rsrc/image/sprite-login-X2.png differ diff --git a/webroot/rsrc/image/sprite-login.png b/webroot/rsrc/image/sprite-login.png index a877ad3754..2f79e7ad8a 100644 Binary files a/webroot/rsrc/image/sprite-login.png and b/webroot/rsrc/image/sprite-login.png differ