diff --git a/resources/celerity/map.php b/resources/celerity/map.php index 62b6b48482..2eeba419ee 100644 --- a/resources/celerity/map.php +++ b/resources/celerity/map.php @@ -12,8 +12,8 @@ return array( 'core.pkg.css' => '9c725fa0', 'core.pkg.js' => 'a2ead3fe', 'darkconsole.pkg.js' => 'e7393ebb', - 'differential.pkg.css' => 'f69afb45', - 'differential.pkg.js' => '40b18f35', + 'differential.pkg.css' => '9535a7e6', + 'differential.pkg.js' => 'ddfeb49b', 'diffusion.pkg.css' => '91c5d3a6', 'diffusion.pkg.js' => '84c8f8fd', 'favicon.ico' => '30672e08', @@ -59,7 +59,7 @@ return array( 'rsrc/css/application/dashboard/dashboard.css' => 'bc6f2127', 'rsrc/css/application/diff/inline-comment-summary.css' => '51efda3a', 'rsrc/css/application/differential/add-comment.css' => 'c47f8c40', - 'rsrc/css/application/differential/changeset-view.css' => '11395d9c', + 'rsrc/css/application/differential/changeset-view.css' => 'e1621fd5', 'rsrc/css/application/differential/core.css' => '5b7b8ff4', 'rsrc/css/application/differential/phui-inline-comment.css' => '5953c28e', 'rsrc/css/application/differential/revision-comment.css' => '14b8565a', @@ -399,13 +399,12 @@ return array( 'rsrc/js/application/diff/behavior-preview-link.js' => '051c7832', 'rsrc/js/application/differential/ChangesetViewManager.js' => 'a2828756', 'rsrc/js/application/differential/DifferentialInlineCommentEditor.js' => '2e3f9738', - 'rsrc/js/application/differential/behavior-add-reviewers-and-ccs.js' => 'e10f8e18', 'rsrc/js/application/differential/behavior-comment-jump.js' => '4fdb476d', 'rsrc/js/application/differential/behavior-comment-preview.js' => 'b064af76', 'rsrc/js/application/differential/behavior-diff-radios.js' => 'e1ff79b1', 'rsrc/js/application/differential/behavior-dropdown-menus.js' => '9a6b9324', 'rsrc/js/application/differential/behavior-edit-inline-comments.js' => '4fbbc3e9', - 'rsrc/js/application/differential/behavior-keyboard-nav.js' => '2c426492', + 'rsrc/js/application/differential/behavior-keyboard-nav.js' => '92904457', 'rsrc/js/application/differential/behavior-populate.js' => '8694b1df', 'rsrc/js/application/differential/behavior-toggle-files.js' => 'ca3f91eb', 'rsrc/js/application/differential/behavior-user-select.js' => 'a8d8459d', @@ -574,7 +573,7 @@ return array( 'conpherence-thread-manager' => 'c8b5ee6f', 'conpherence-transaction-css' => '85129c68', 'd3' => 'a11a5ff2', - 'differential-changeset-view-css' => '11395d9c', + 'differential-changeset-view-css' => 'e1621fd5', 'differential-core-view-css' => '5b7b8ff4', 'differential-inline-comment-editor' => '2e3f9738', 'differential-revision-add-comment-css' => 'c47f8c40', @@ -627,13 +626,12 @@ return array( 'javelin-behavior-detect-timezone' => '4c193c96', 'javelin-behavior-device' => 'bb1dd507', 'javelin-behavior-diff-preview-link' => '051c7832', - 'javelin-behavior-differential-add-reviewers-and-ccs' => 'e10f8e18', 'javelin-behavior-differential-comment-jump' => '4fdb476d', 'javelin-behavior-differential-diff-radios' => 'e1ff79b1', 'javelin-behavior-differential-dropdown-menus' => '9a6b9324', 'javelin-behavior-differential-edit-inline-comments' => '4fbbc3e9', 'javelin-behavior-differential-feedback-preview' => 'b064af76', - 'javelin-behavior-differential-keyboard-navigation' => '2c426492', + 'javelin-behavior-differential-keyboard-navigation' => '92904457', 'javelin-behavior-differential-populate' => '8694b1df', 'javelin-behavior-differential-toggle-files' => 'ca3f91eb', 'javelin-behavior-differential-user-select' => 'a8d8459d', @@ -1032,9 +1030,6 @@ return array( 'javelin-dom', 'javelin-typeahead-normalizer', ), - '11395d9c' => array( - 'phui-inline-comment-view-css', - ), '12884df9' => array( 'javelin-behavior', 'javelin-stratcom', @@ -1147,12 +1142,6 @@ return array( 'javelin-install', 'javelin-util', ), - '2c426492' => array( - 'javelin-behavior', - 'javelin-dom', - 'javelin-stratcom', - 'phabricator-keyboard-shortcut', - ), '2caa8fb8' => array( 'javelin-install', 'javelin-event', @@ -1654,6 +1643,12 @@ return array( 'javelin-dom', 'javelin-request', ), + 92904457 => array( + 'javelin-behavior', + 'javelin-dom', + 'javelin-stratcom', + 'phabricator-keyboard-shortcut', + ), '92b9ec77' => array( 'javelin-behavior', 'javelin-stratcom', @@ -2089,10 +2084,8 @@ return array( 'javelin-request', 'javelin-util', ), - 'e10f8e18' => array( - 'javelin-behavior', - 'javelin-dom', - 'phabricator-prefab', + 'e1621fd5' => array( + 'phui-inline-comment-view-css', ), 'e1d25dfb' => array( 'javelin-behavior', @@ -2463,7 +2456,6 @@ return array( 'javelin-behavior-differential-populate', 'javelin-behavior-differential-diff-radios', 'javelin-behavior-differential-comment-jump', - 'javelin-behavior-differential-add-reviewers-and-ccs', 'javelin-behavior-differential-keyboard-navigation', 'javelin-behavior-aphront-drag-and-drop-textarea', 'javelin-behavior-phabricator-object-selector', diff --git a/resources/celerity/packages.php b/resources/celerity/packages.php index 95cf5303ef..caa4512e34 100644 --- a/resources/celerity/packages.php +++ b/resources/celerity/packages.php @@ -195,7 +195,6 @@ return array( 'javelin-behavior-differential-populate', 'javelin-behavior-differential-diff-radios', 'javelin-behavior-differential-comment-jump', - 'javelin-behavior-differential-add-reviewers-and-ccs', 'javelin-behavior-differential-keyboard-navigation', 'javelin-behavior-aphront-drag-and-drop-textarea', 'javelin-behavior-phabricator-object-selector', diff --git a/resources/sql/autopatches/20161213.diff.01.hunks.php b/resources/sql/autopatches/20161213.diff.01.hunks.php index c6394defce..574adb3b4f 100644 --- a/resources/sql/autopatches/20161213.diff.01.hunks.php +++ b/resources/sql/autopatches/20161213.diff.01.hunks.php @@ -28,7 +28,8 @@ foreach (new LiskRawMigrationIterator($conn, $src_table) as $row) { DifferentialModernHunk::DATATYPE_TEXT, 'utf8', DifferentialModernHunk::DATAFORMAT_RAW, - $row['changes'], + // In rare cases, this could be NULL. See T12090. + (string)$row['changes'], $row['dateCreated'], $row['dateModified']); } diff --git a/resources/sql/autopatches/20170106.menu.01.customphd.sql b/resources/sql/autopatches/20170106.menu.01.customphd.sql new file mode 100644 index 0000000000..2fb642ca8b --- /dev/null +++ b/resources/sql/autopatches/20170106.menu.01.customphd.sql @@ -0,0 +1,2 @@ +ALTER TABLE {$NAMESPACE}_search.search_profilepanelconfiguration + ADD customPHID VARBINARY(64); diff --git a/resources/sql/autopatches/20170109.diff.01.commit.sql b/resources/sql/autopatches/20170109.diff.01.commit.sql new file mode 100644 index 0000000000..2a28900272 --- /dev/null +++ b/resources/sql/autopatches/20170109.diff.01.commit.sql @@ -0,0 +1,2 @@ +ALTER TABLE {$NAMESPACE}_differential.differential_diff + ADD commitPHID VARBINARY(64); diff --git a/scripts/daemon/launch_daemon.php b/scripts/daemon/launch_daemon.php index 7c75ab671e..44e70f710d 100755 --- a/scripts/daemon/launch_daemon.php +++ b/scripts/daemon/launch_daemon.php @@ -5,7 +5,11 @@ // script, except it loads the Phabricator environment and adds some Phabricator // specific flags. -declare(ticks = 1); +if (function_exists('pcntl_async_signals')) { + pcntl_async_signals(true); +} else { + declare(ticks = 1); +} $root = dirname(dirname(dirname(__FILE__))); require_once $root.'/scripts/__init_script__.php'; diff --git a/scripts/ssh/ssh-connect.php b/scripts/ssh/ssh-connect.php index a9bc921e69..9757f3cbbe 100755 --- a/scripts/ssh/ssh-connect.php +++ b/scripts/ssh/ssh-connect.php @@ -7,7 +7,11 @@ // In some cases, Subversion sends us SIGTERM. If we don't catch the signal and // react to it, we won't run object destructors by default and thus won't clean // up temporary files. Declare ticks so we can install a signal handler. -declare(ticks=1); +if (function_exists('pcntl_async_signals')) { + pcntl_async_signals(true); +} else { + declare(ticks = 1); +} $root = dirname(dirname(dirname(__FILE__))); require_once $root.'/scripts/__init_script__.php'; diff --git a/src/__phutil_library_map__.php b/src/__phutil_library_map__.php index 43ab0407e6..617365f333 100644 --- a/src/__phutil_library_map__.php +++ b/src/__phutil_library_map__.php @@ -519,6 +519,7 @@ phutil_register_library_map(array( 'DifferentialRevisionControlSystem' => 'applications/differential/constants/DifferentialRevisionControlSystem.php', 'DifferentialRevisionDependedOnByRevisionEdgeType' => 'applications/differential/edge/DifferentialRevisionDependedOnByRevisionEdgeType.php', 'DifferentialRevisionDependsOnRevisionEdgeType' => 'applications/differential/edge/DifferentialRevisionDependsOnRevisionEdgeType.php', + 'DifferentialRevisionDraftEngine' => 'applications/differential/engine/DifferentialRevisionDraftEngine.php', 'DifferentialRevisionEditConduitAPIMethod' => 'applications/differential/conduit/DifferentialRevisionEditConduitAPIMethod.php', 'DifferentialRevisionEditController' => 'applications/differential/controller/DifferentialRevisionEditController.php', 'DifferentialRevisionEditEngine' => 'applications/differential/editor/DifferentialRevisionEditEngine.php', @@ -612,18 +613,25 @@ phutil_register_library_map(array( 'DiffusionCloneURIView' => 'applications/diffusion/view/DiffusionCloneURIView.php', 'DiffusionCommandEngine' => 'applications/diffusion/protocol/DiffusionCommandEngine.php', 'DiffusionCommandEngineTestCase' => 'applications/diffusion/protocol/__tests__/DiffusionCommandEngineTestCase.php', + 'DiffusionCommitAcceptTransaction' => 'applications/diffusion/xaction/DiffusionCommitAcceptTransaction.php', + 'DiffusionCommitActionTransaction' => 'applications/diffusion/xaction/DiffusionCommitActionTransaction.php', 'DiffusionCommitAffectedFilesHeraldField' => 'applications/diffusion/herald/DiffusionCommitAffectedFilesHeraldField.php', + 'DiffusionCommitAuditTransaction' => 'applications/diffusion/xaction/DiffusionCommitAuditTransaction.php', + 'DiffusionCommitAuditorsTransaction' => 'applications/diffusion/xaction/DiffusionCommitAuditorsTransaction.php', 'DiffusionCommitAuthorHeraldField' => 'applications/diffusion/herald/DiffusionCommitAuthorHeraldField.php', 'DiffusionCommitAutocloseHeraldField' => 'applications/diffusion/herald/DiffusionCommitAutocloseHeraldField.php', 'DiffusionCommitBranchesController' => 'applications/diffusion/controller/DiffusionCommitBranchesController.php', 'DiffusionCommitBranchesHeraldField' => 'applications/diffusion/herald/DiffusionCommitBranchesHeraldField.php', 'DiffusionCommitCommitterHeraldField' => 'applications/diffusion/herald/DiffusionCommitCommitterHeraldField.php', + 'DiffusionCommitConcernTransaction' => 'applications/diffusion/xaction/DiffusionCommitConcernTransaction.php', 'DiffusionCommitController' => 'applications/diffusion/controller/DiffusionCommitController.php', 'DiffusionCommitDiffContentAddedHeraldField' => 'applications/diffusion/herald/DiffusionCommitDiffContentAddedHeraldField.php', 'DiffusionCommitDiffContentHeraldField' => 'applications/diffusion/herald/DiffusionCommitDiffContentHeraldField.php', 'DiffusionCommitDiffContentRemovedHeraldField' => 'applications/diffusion/herald/DiffusionCommitDiffContentRemovedHeraldField.php', 'DiffusionCommitDiffEnormousHeraldField' => 'applications/diffusion/herald/DiffusionCommitDiffEnormousHeraldField.php', + 'DiffusionCommitEditConduitAPIMethod' => 'applications/diffusion/conduit/DiffusionCommitEditConduitAPIMethod.php', 'DiffusionCommitEditController' => 'applications/diffusion/controller/DiffusionCommitEditController.php', + 'DiffusionCommitEditEngine' => 'applications/diffusion/editor/DiffusionCommitEditEngine.php', 'DiffusionCommitFulltextEngine' => 'applications/repository/search/DiffusionCommitFulltextEngine.php', 'DiffusionCommitHasRevisionEdgeType' => 'applications/diffusion/edge/DiffusionCommitHasRevisionEdgeType.php', 'DiffusionCommitHasRevisionRelationship' => 'applications/diffusion/relationships/DiffusionCommitHasRevisionRelationship.php', @@ -635,6 +643,7 @@ phutil_register_library_map(array( 'DiffusionCommitHintQuery' => 'applications/diffusion/query/DiffusionCommitHintQuery.php', 'DiffusionCommitHookEngine' => 'applications/diffusion/engine/DiffusionCommitHookEngine.php', 'DiffusionCommitHookRejectException' => 'applications/diffusion/exception/DiffusionCommitHookRejectException.php', + 'DiffusionCommitListController' => 'applications/diffusion/controller/DiffusionCommitListController.php', 'DiffusionCommitMergeHeraldField' => 'applications/diffusion/herald/DiffusionCommitMergeHeraldField.php', 'DiffusionCommitMessageHeraldField' => 'applications/diffusion/herald/DiffusionCommitMessageHeraldField.php', 'DiffusionCommitPackageAuditHeraldField' => 'applications/diffusion/herald/DiffusionCommitPackageAuditHeraldField.php', @@ -649,6 +658,9 @@ phutil_register_library_map(array( 'DiffusionCommitRemarkupRuleTestCase' => 'applications/diffusion/remarkup/__tests__/DiffusionCommitRemarkupRuleTestCase.php', 'DiffusionCommitRepositoryHeraldField' => 'applications/diffusion/herald/DiffusionCommitRepositoryHeraldField.php', 'DiffusionCommitRepositoryProjectsHeraldField' => 'applications/diffusion/herald/DiffusionCommitRepositoryProjectsHeraldField.php', + 'DiffusionCommitRequiredActionResultBucket' => 'applications/diffusion/query/DiffusionCommitRequiredActionResultBucket.php', + 'DiffusionCommitResignTransaction' => 'applications/diffusion/xaction/DiffusionCommitResignTransaction.php', + 'DiffusionCommitResultBucket' => 'applications/diffusion/query/DiffusionCommitResultBucket.php', 'DiffusionCommitRevertedByCommitEdgeType' => 'applications/diffusion/edge/DiffusionCommitRevertedByCommitEdgeType.php', 'DiffusionCommitRevertsCommitEdgeType' => 'applications/diffusion/edge/DiffusionCommitRevertsCommitEdgeType.php', 'DiffusionCommitReviewerHeraldField' => 'applications/diffusion/herald/DiffusionCommitReviewerHeraldField.php', @@ -656,7 +668,9 @@ phutil_register_library_map(array( 'DiffusionCommitRevisionHeraldField' => 'applications/diffusion/herald/DiffusionCommitRevisionHeraldField.php', 'DiffusionCommitRevisionReviewersHeraldField' => 'applications/diffusion/herald/DiffusionCommitRevisionReviewersHeraldField.php', 'DiffusionCommitRevisionSubscribersHeraldField' => 'applications/diffusion/herald/DiffusionCommitRevisionSubscribersHeraldField.php', + 'DiffusionCommitSearchConduitAPIMethod' => 'applications/diffusion/conduit/DiffusionCommitSearchConduitAPIMethod.php', 'DiffusionCommitTagsController' => 'applications/diffusion/controller/DiffusionCommitTagsController.php', + 'DiffusionCommitTransactionType' => 'applications/diffusion/xaction/DiffusionCommitTransactionType.php', 'DiffusionCompareController' => 'applications/diffusion/controller/DiffusionCompareController.php', 'DiffusionConduitAPIMethod' => 'applications/diffusion/conduit/DiffusionConduitAPIMethod.php', 'DiffusionController' => 'applications/diffusion/controller/DiffusionController.php', @@ -1858,19 +1872,16 @@ phutil_register_library_map(array( 'PhabricatorAsanaSubtaskHasObjectEdgeType' => 'applications/doorkeeper/edge/PhabricatorAsanaSubtaskHasObjectEdgeType.php', 'PhabricatorAsanaTaskHasObjectEdgeType' => 'applications/doorkeeper/edge/PhabricatorAsanaTaskHasObjectEdgeType.php', 'PhabricatorAuditActionConstants' => 'applications/audit/constants/PhabricatorAuditActionConstants.php', - 'PhabricatorAuditAddCommentController' => 'applications/audit/controller/PhabricatorAuditAddCommentController.php', 'PhabricatorAuditApplication' => 'applications/audit/application/PhabricatorAuditApplication.php', 'PhabricatorAuditCommentEditor' => 'applications/audit/editor/PhabricatorAuditCommentEditor.php', 'PhabricatorAuditCommitStatusConstants' => 'applications/audit/constants/PhabricatorAuditCommitStatusConstants.php', 'PhabricatorAuditController' => 'applications/audit/controller/PhabricatorAuditController.php', 'PhabricatorAuditEditor' => 'applications/audit/editor/PhabricatorAuditEditor.php', 'PhabricatorAuditInlineComment' => 'applications/audit/storage/PhabricatorAuditInlineComment.php', - 'PhabricatorAuditListController' => 'applications/audit/controller/PhabricatorAuditListController.php', 'PhabricatorAuditListView' => 'applications/audit/view/PhabricatorAuditListView.php', 'PhabricatorAuditMailReceiver' => 'applications/audit/mail/PhabricatorAuditMailReceiver.php', 'PhabricatorAuditManagementDeleteWorkflow' => 'applications/audit/management/PhabricatorAuditManagementDeleteWorkflow.php', 'PhabricatorAuditManagementWorkflow' => 'applications/audit/management/PhabricatorAuditManagementWorkflow.php', - 'PhabricatorAuditPreviewController' => 'applications/audit/controller/PhabricatorAuditPreviewController.php', 'PhabricatorAuditReplyHandler' => 'applications/audit/mail/PhabricatorAuditReplyHandler.php', 'PhabricatorAuditStatusConstants' => 'applications/audit/constants/PhabricatorAuditStatusConstants.php', 'PhabricatorAuditTransaction' => 'applications/audit/storage/PhabricatorAuditTransaction.php', @@ -2025,6 +2036,7 @@ phutil_register_library_map(array( 'PhabricatorBcryptPasswordHasher' => 'infrastructure/util/password/PhabricatorBcryptPasswordHasher.php', 'PhabricatorBinariesSetupCheck' => 'applications/config/check/PhabricatorBinariesSetupCheck.php', 'PhabricatorBitbucketAuthProvider' => 'applications/auth/provider/PhabricatorBitbucketAuthProvider.php', + 'PhabricatorBoardColumnsSearchEngineAttachment' => 'applications/project/engineextension/PhabricatorBoardColumnsSearchEngineAttachment.php', 'PhabricatorBoardLayoutEngine' => 'applications/project/engine/PhabricatorBoardLayoutEngine.php', 'PhabricatorBoardRenderingEngine' => 'applications/project/engine/PhabricatorBoardRenderingEngine.php', 'PhabricatorBoardResponseEngine' => 'applications/project/engine/PhabricatorBoardResponseEngine.php', @@ -2044,6 +2056,7 @@ phutil_register_library_map(array( 'PhabricatorBotUser' => 'infrastructure/daemon/bot/target/PhabricatorBotUser.php', 'PhabricatorBotWhatsNewHandler' => 'infrastructure/daemon/bot/handler/PhabricatorBotWhatsNewHandler.php', 'PhabricatorBritishEnglishTranslation' => 'infrastructure/internationalization/translation/PhabricatorBritishEnglishTranslation.php', + 'PhabricatorBuiltinDraftEngine' => 'applications/transactions/draft/PhabricatorBuiltinDraftEngine.php', 'PhabricatorBuiltinPatchList' => 'infrastructure/storage/patch/PhabricatorBuiltinPatchList.php', 'PhabricatorBulkContentSource' => 'infrastructure/daemon/contentsource/PhabricatorBulkContentSource.php', 'PhabricatorBusyUIExample' => 'applications/uiexample/examples/PhabricatorBusyUIExample.php', @@ -2539,6 +2552,8 @@ phutil_register_library_map(array( 'PhabricatorDoorkeeperApplication' => 'applications/doorkeeper/application/PhabricatorDoorkeeperApplication.php', 'PhabricatorDraft' => 'applications/draft/storage/PhabricatorDraft.php', 'PhabricatorDraftDAO' => 'applications/draft/storage/PhabricatorDraftDAO.php', + 'PhabricatorDraftEngine' => 'applications/transactions/draft/PhabricatorDraftEngine.php', + 'PhabricatorDraftInterface' => 'applications/transactions/draft/PhabricatorDraftInterface.php', 'PhabricatorDrydockApplication' => 'applications/drydock/application/PhabricatorDrydockApplication.php', 'PhabricatorEdgeConfig' => 'infrastructure/edges/constants/PhabricatorEdgeConfig.php', 'PhabricatorEdgeConstants' => 'infrastructure/edges/constants/PhabricatorEdgeConstants.php', @@ -2653,6 +2668,13 @@ phutil_register_library_map(array( 'PhabricatorFactSimpleSpec' => 'applications/fact/spec/PhabricatorFactSimpleSpec.php', 'PhabricatorFactSpec' => 'applications/fact/spec/PhabricatorFactSpec.php', 'PhabricatorFactUpdateIterator' => 'applications/fact/extract/PhabricatorFactUpdateIterator.php', + 'PhabricatorFavoritesApplication' => 'applications/favorites/application/PhabricatorFavoritesApplication.php', + 'PhabricatorFavoritesConstants' => 'applications/favorites/constants/PhabricatorFavoritesConstants.php', + 'PhabricatorFavoritesController' => 'applications/favorites/controller/PhabricatorFavoritesController.php', + 'PhabricatorFavoritesMainController' => 'applications/favorites/controller/PhabricatorFavoritesMainController.php', + 'PhabricatorFavoritesManageProfileMenuItem' => 'applications/favorites/menuitem/PhabricatorFavoritesManageProfileMenuItem.php', + 'PhabricatorFavoritesMenuItemController' => 'applications/favorites/controller/PhabricatorFavoritesMenuItemController.php', + 'PhabricatorFavoritesProfileMenuEngine' => 'applications/favorites/engine/PhabricatorFavoritesProfileMenuEngine.php', 'PhabricatorFaxContentSource' => 'infrastructure/contentsource/PhabricatorFaxContentSource.php', 'PhabricatorFeedApplication' => 'applications/feed/application/PhabricatorFeedApplication.php', 'PhabricatorFeedBuilder' => 'applications/feed/builder/PhabricatorFeedBuilder.php', @@ -2810,9 +2832,14 @@ phutil_register_library_map(array( 'PhabricatorHeraldContentSource' => 'applications/herald/contentsource/PhabricatorHeraldContentSource.php', 'PhabricatorHighSecurityRequestExceptionHandler' => 'aphront/handler/PhabricatorHighSecurityRequestExceptionHandler.php', 'PhabricatorHomeApplication' => 'applications/home/application/PhabricatorHomeApplication.php', + 'PhabricatorHomeConstants' => 'applications/home/constants/PhabricatorHomeConstants.php', 'PhabricatorHomeController' => 'applications/home/controller/PhabricatorHomeController.php', 'PhabricatorHomeMainController' => 'applications/home/controller/PhabricatorHomeMainController.php', + 'PhabricatorHomeManageProfileMenuItem' => 'applications/home/menuitem/PhabricatorHomeManageProfileMenuItem.php', + 'PhabricatorHomeMenuController' => 'applications/home/controller/PhabricatorHomeMenuController.php', + 'PhabricatorHomeMenuItemController' => 'applications/home/controller/PhabricatorHomeMenuItemController.php', 'PhabricatorHomePreferencesSettingsPanel' => 'applications/settings/panel/PhabricatorHomePreferencesSettingsPanel.php', + 'PhabricatorHomeProfileMenuEngine' => 'applications/home/engine/PhabricatorHomeProfileMenuEngine.php', 'PhabricatorHomeQuickCreateController' => 'applications/home/controller/PhabricatorHomeQuickCreateController.php', 'PhabricatorHovercardEngineExtension' => 'applications/search/engineextension/PhabricatorHovercardEngineExtension.php', 'PhabricatorHovercardEngineExtensionModule' => 'applications/search/engineextension/PhabricatorHovercardEngineExtensionModule.php', @@ -3072,6 +3099,7 @@ phutil_register_library_map(array( 'PhabricatorObjectHasAsanaSubtaskEdgeType' => 'applications/doorkeeper/edge/PhabricatorObjectHasAsanaSubtaskEdgeType.php', 'PhabricatorObjectHasAsanaTaskEdgeType' => 'applications/doorkeeper/edge/PhabricatorObjectHasAsanaTaskEdgeType.php', 'PhabricatorObjectHasContributorEdgeType' => 'applications/transactions/edges/PhabricatorObjectHasContributorEdgeType.php', + 'PhabricatorObjectHasDraftEdgeType' => 'applications/transactions/edges/PhabricatorObjectHasDraftEdgeType.php', 'PhabricatorObjectHasFileEdgeType' => 'applications/transactions/edges/PhabricatorObjectHasFileEdgeType.php', 'PhabricatorObjectHasJiraIssueEdgeType' => 'applications/doorkeeper/edge/PhabricatorObjectHasJiraIssueEdgeType.php', 'PhabricatorObjectHasSubscriberEdgeType' => 'applications/transactions/edges/PhabricatorObjectHasSubscriberEdgeType.php', @@ -3426,6 +3454,7 @@ phutil_register_library_map(array( 'PhabricatorProjectColumnPosition' => 'applications/project/storage/PhabricatorProjectColumnPosition.php', 'PhabricatorProjectColumnPositionQuery' => 'applications/project/query/PhabricatorProjectColumnPositionQuery.php', 'PhabricatorProjectColumnQuery' => 'applications/project/query/PhabricatorProjectColumnQuery.php', + 'PhabricatorProjectColumnSearchEngine' => 'applications/project/query/PhabricatorProjectColumnSearchEngine.php', 'PhabricatorProjectColumnTransaction' => 'applications/project/storage/PhabricatorProjectColumnTransaction.php', 'PhabricatorProjectColumnTransactionEditor' => 'applications/project/editor/PhabricatorProjectColumnTransactionEditor.php', 'PhabricatorProjectColumnTransactionQuery' => 'applications/project/query/PhabricatorProjectColumnTransactionQuery.php', @@ -3514,6 +3543,7 @@ phutil_register_library_map(array( 'PhabricatorProjectWatcherListView' => 'applications/project/view/PhabricatorProjectWatcherListView.php', 'PhabricatorProjectWorkboardBackgroundColor' => 'applications/project/constants/PhabricatorProjectWorkboardBackgroundColor.php', 'PhabricatorProjectWorkboardProfileMenuItem' => 'applications/project/menuitem/PhabricatorProjectWorkboardProfileMenuItem.php', + 'PhabricatorProjectsAncestorsSearchEngineAttachment' => 'applications/project/engineextension/PhabricatorProjectsAncestorsSearchEngineAttachment.php', 'PhabricatorProjectsCurtainExtension' => 'applications/project/engineextension/PhabricatorProjectsCurtainExtension.php', 'PhabricatorProjectsEditEngineExtension' => 'applications/project/engineextension/PhabricatorProjectsEditEngineExtension.php', 'PhabricatorProjectsEditField' => 'applications/transactions/editfield/PhabricatorProjectsEditField.php', @@ -4452,6 +4482,7 @@ phutil_register_library_map(array( 'ProjectAddProjectsEmailCommand' => 'applications/project/command/ProjectAddProjectsEmailCommand.php', 'ProjectBoardTaskCard' => 'applications/project/view/ProjectBoardTaskCard.php', 'ProjectCanLockProjectsCapability' => 'applications/project/capability/ProjectCanLockProjectsCapability.php', + 'ProjectColumnSearchConduitAPIMethod' => 'applications/project/conduit/ProjectColumnSearchConduitAPIMethod.php', 'ProjectConduitAPIMethod' => 'applications/project/conduit/ProjectConduitAPIMethod.php', 'ProjectCreateConduitAPIMethod' => 'applications/project/conduit/ProjectCreateConduitAPIMethod.php', 'ProjectCreateProjectsCapability' => 'applications/project/capability/ProjectCreateProjectsCapability.php', @@ -5184,6 +5215,7 @@ phutil_register_library_map(array( 'PhabricatorProjectInterface', 'PhabricatorFulltextInterface', 'PhabricatorConduitResultInterface', + 'PhabricatorDraftInterface', ), 'DifferentialRevisionAbandonTransaction' => 'DifferentialRevisionActionTransaction', 'DifferentialRevisionAcceptTransaction' => 'DifferentialRevisionReviewTransaction', @@ -5200,6 +5232,7 @@ phutil_register_library_map(array( 'DifferentialRevisionControlSystem' => 'Phobject', 'DifferentialRevisionDependedOnByRevisionEdgeType' => 'PhabricatorEdgeType', 'DifferentialRevisionDependsOnRevisionEdgeType' => 'PhabricatorEdgeType', + 'DifferentialRevisionDraftEngine' => 'PhabricatorDraftEngine', 'DifferentialRevisionEditConduitAPIMethod' => 'PhabricatorEditEngineAPIMethod', 'DifferentialRevisionEditController' => 'DifferentialController', 'DifferentialRevisionEditEngine' => 'PhabricatorEditEngine', @@ -5293,18 +5326,25 @@ phutil_register_library_map(array( 'DiffusionCloneURIView' => 'AphrontView', 'DiffusionCommandEngine' => 'Phobject', 'DiffusionCommandEngineTestCase' => 'PhabricatorTestCase', + 'DiffusionCommitAcceptTransaction' => 'DiffusionCommitAuditTransaction', + 'DiffusionCommitActionTransaction' => 'DiffusionCommitTransactionType', 'DiffusionCommitAffectedFilesHeraldField' => 'DiffusionCommitHeraldField', + 'DiffusionCommitAuditTransaction' => 'DiffusionCommitActionTransaction', + 'DiffusionCommitAuditorsTransaction' => 'DiffusionCommitTransactionType', 'DiffusionCommitAuthorHeraldField' => 'DiffusionCommitHeraldField', 'DiffusionCommitAutocloseHeraldField' => 'DiffusionCommitHeraldField', 'DiffusionCommitBranchesController' => 'DiffusionController', 'DiffusionCommitBranchesHeraldField' => 'DiffusionCommitHeraldField', 'DiffusionCommitCommitterHeraldField' => 'DiffusionCommitHeraldField', + 'DiffusionCommitConcernTransaction' => 'DiffusionCommitAuditTransaction', 'DiffusionCommitController' => 'DiffusionController', 'DiffusionCommitDiffContentAddedHeraldField' => 'DiffusionCommitHeraldField', 'DiffusionCommitDiffContentHeraldField' => 'DiffusionCommitHeraldField', 'DiffusionCommitDiffContentRemovedHeraldField' => 'DiffusionCommitHeraldField', 'DiffusionCommitDiffEnormousHeraldField' => 'DiffusionCommitHeraldField', + 'DiffusionCommitEditConduitAPIMethod' => 'PhabricatorEditEngineAPIMethod', 'DiffusionCommitEditController' => 'DiffusionController', + 'DiffusionCommitEditEngine' => 'PhabricatorEditEngine', 'DiffusionCommitFulltextEngine' => 'PhabricatorFulltextEngine', 'DiffusionCommitHasRevisionEdgeType' => 'PhabricatorEdgeType', 'DiffusionCommitHasRevisionRelationship' => 'DiffusionCommitRelationship', @@ -5316,6 +5356,7 @@ phutil_register_library_map(array( 'DiffusionCommitHintQuery' => 'PhabricatorCursorPagedPolicyAwareQuery', 'DiffusionCommitHookEngine' => 'Phobject', 'DiffusionCommitHookRejectException' => 'Exception', + 'DiffusionCommitListController' => 'DiffusionController', 'DiffusionCommitMergeHeraldField' => 'DiffusionCommitHeraldField', 'DiffusionCommitMessageHeraldField' => 'DiffusionCommitHeraldField', 'DiffusionCommitPackageAuditHeraldField' => 'DiffusionCommitHeraldField', @@ -5330,6 +5371,9 @@ phutil_register_library_map(array( 'DiffusionCommitRemarkupRuleTestCase' => 'PhabricatorTestCase', 'DiffusionCommitRepositoryHeraldField' => 'DiffusionCommitHeraldField', 'DiffusionCommitRepositoryProjectsHeraldField' => 'DiffusionCommitHeraldField', + 'DiffusionCommitRequiredActionResultBucket' => 'DiffusionCommitResultBucket', + 'DiffusionCommitResignTransaction' => 'DiffusionCommitAuditTransaction', + 'DiffusionCommitResultBucket' => 'PhabricatorSearchResultBucket', 'DiffusionCommitRevertedByCommitEdgeType' => 'PhabricatorEdgeType', 'DiffusionCommitRevertsCommitEdgeType' => 'PhabricatorEdgeType', 'DiffusionCommitReviewerHeraldField' => 'DiffusionCommitHeraldField', @@ -5337,7 +5381,9 @@ phutil_register_library_map(array( 'DiffusionCommitRevisionHeraldField' => 'DiffusionCommitHeraldField', 'DiffusionCommitRevisionReviewersHeraldField' => 'DiffusionCommitHeraldField', 'DiffusionCommitRevisionSubscribersHeraldField' => 'DiffusionCommitHeraldField', + 'DiffusionCommitSearchConduitAPIMethod' => 'PhabricatorSearchEngineAPIMethod', 'DiffusionCommitTagsController' => 'DiffusionController', + 'DiffusionCommitTransactionType' => 'PhabricatorModularTransactionType', 'DiffusionCompareController' => 'DiffusionController', 'DiffusionConduitAPIMethod' => 'ConduitAPIMethod', 'DiffusionController' => 'PhabricatorController', @@ -6723,7 +6769,6 @@ phutil_register_library_map(array( 'PhabricatorAsanaSubtaskHasObjectEdgeType' => 'PhabricatorEdgeType', 'PhabricatorAsanaTaskHasObjectEdgeType' => 'PhabricatorEdgeType', 'PhabricatorAuditActionConstants' => 'Phobject', - 'PhabricatorAuditAddCommentController' => 'PhabricatorAuditController', 'PhabricatorAuditApplication' => 'PhabricatorApplication', 'PhabricatorAuditCommentEditor' => 'PhabricatorEditor', 'PhabricatorAuditCommitStatusConstants' => 'Phobject', @@ -6733,15 +6778,13 @@ phutil_register_library_map(array( 'Phobject', 'PhabricatorInlineCommentInterface', ), - 'PhabricatorAuditListController' => 'PhabricatorAuditController', 'PhabricatorAuditListView' => 'AphrontView', 'PhabricatorAuditMailReceiver' => 'PhabricatorObjectMailReceiver', 'PhabricatorAuditManagementDeleteWorkflow' => 'PhabricatorAuditManagementWorkflow', 'PhabricatorAuditManagementWorkflow' => 'PhabricatorManagementWorkflow', - 'PhabricatorAuditPreviewController' => 'PhabricatorAuditController', 'PhabricatorAuditReplyHandler' => 'PhabricatorApplicationTransactionReplyHandler', 'PhabricatorAuditStatusConstants' => 'Phobject', - 'PhabricatorAuditTransaction' => 'PhabricatorApplicationTransaction', + 'PhabricatorAuditTransaction' => 'PhabricatorModularTransaction', 'PhabricatorAuditTransactionComment' => 'PhabricatorApplicationTransactionComment', 'PhabricatorAuditTransactionQuery' => 'PhabricatorApplicationTransactionQuery', 'PhabricatorAuditTransactionView' => 'PhabricatorApplicationTransactionView', @@ -6925,6 +6968,7 @@ phutil_register_library_map(array( 'PhabricatorBcryptPasswordHasher' => 'PhabricatorPasswordHasher', 'PhabricatorBinariesSetupCheck' => 'PhabricatorSetupCheck', 'PhabricatorBitbucketAuthProvider' => 'PhabricatorOAuth1AuthProvider', + 'PhabricatorBoardColumnsSearchEngineAttachment' => 'PhabricatorSearchEngineAttachment', 'PhabricatorBoardLayoutEngine' => 'Phobject', 'PhabricatorBoardRenderingEngine' => 'Phobject', 'PhabricatorBoardResponseEngine' => 'Phobject', @@ -6944,6 +6988,7 @@ phutil_register_library_map(array( 'PhabricatorBotUser' => 'PhabricatorBotTarget', 'PhabricatorBotWhatsNewHandler' => 'PhabricatorBotHandler', 'PhabricatorBritishEnglishTranslation' => 'PhutilTranslation', + 'PhabricatorBuiltinDraftEngine' => 'PhabricatorDraftEngine', 'PhabricatorBuiltinPatchList' => 'PhabricatorSQLPatchList', 'PhabricatorBulkContentSource' => 'PhabricatorContentSource', 'PhabricatorBusyUIExample' => 'PhabricatorUIExample', @@ -7522,6 +7567,7 @@ phutil_register_library_map(array( 'PhabricatorDoorkeeperApplication' => 'PhabricatorApplication', 'PhabricatorDraft' => 'PhabricatorDraftDAO', 'PhabricatorDraftDAO' => 'PhabricatorLiskDAO', + 'PhabricatorDraftEngine' => 'Phobject', 'PhabricatorDrydockApplication' => 'PhabricatorApplication', 'PhabricatorEdgeConfig' => 'PhabricatorEdgeConstants', 'PhabricatorEdgeConstants' => 'Phobject', @@ -7645,6 +7691,13 @@ phutil_register_library_map(array( 'PhabricatorFactSimpleSpec' => 'PhabricatorFactSpec', 'PhabricatorFactSpec' => 'Phobject', 'PhabricatorFactUpdateIterator' => 'PhutilBufferedIterator', + 'PhabricatorFavoritesApplication' => 'PhabricatorApplication', + 'PhabricatorFavoritesConstants' => 'PhabricatorFavoritesController', + 'PhabricatorFavoritesController' => 'PhabricatorController', + 'PhabricatorFavoritesMainController' => 'PhabricatorFavoritesController', + 'PhabricatorFavoritesManageProfileMenuItem' => 'PhabricatorProfileMenuItem', + 'PhabricatorFavoritesMenuItemController' => 'PhabricatorFavoritesController', + 'PhabricatorFavoritesProfileMenuEngine' => 'PhabricatorProfileMenuEngine', 'PhabricatorFaxContentSource' => 'PhabricatorContentSource', 'PhabricatorFeedApplication' => 'PhabricatorApplication', 'PhabricatorFeedBuilder' => 'Phobject', @@ -7838,9 +7891,14 @@ phutil_register_library_map(array( 'PhabricatorHeraldContentSource' => 'PhabricatorContentSource', 'PhabricatorHighSecurityRequestExceptionHandler' => 'PhabricatorRequestExceptionHandler', 'PhabricatorHomeApplication' => 'PhabricatorApplication', + 'PhabricatorHomeConstants' => 'PhabricatorHomeController', 'PhabricatorHomeController' => 'PhabricatorController', 'PhabricatorHomeMainController' => 'PhabricatorHomeController', + 'PhabricatorHomeManageProfileMenuItem' => 'PhabricatorProfileMenuItem', + 'PhabricatorHomeMenuController' => 'PhabricatorHomeController', + 'PhabricatorHomeMenuItemController' => 'PhabricatorHomeController', 'PhabricatorHomePreferencesSettingsPanel' => 'PhabricatorSettingsPanel', + 'PhabricatorHomeProfileMenuEngine' => 'PhabricatorProfileMenuEngine', 'PhabricatorHomeQuickCreateController' => 'PhabricatorHomeController', 'PhabricatorHovercardEngineExtension' => 'Phobject', 'PhabricatorHovercardEngineExtensionModule' => 'PhabricatorConfigModule', @@ -8123,6 +8181,7 @@ phutil_register_library_map(array( 'PhabricatorObjectHasAsanaSubtaskEdgeType' => 'PhabricatorEdgeType', 'PhabricatorObjectHasAsanaTaskEdgeType' => 'PhabricatorEdgeType', 'PhabricatorObjectHasContributorEdgeType' => 'PhabricatorEdgeType', + 'PhabricatorObjectHasDraftEdgeType' => 'PhabricatorEdgeType', 'PhabricatorObjectHasFileEdgeType' => 'PhabricatorEdgeType', 'PhabricatorObjectHasJiraIssueEdgeType' => 'PhabricatorEdgeType', 'PhabricatorObjectHasSubscriberEdgeType' => 'PhabricatorEdgeType', @@ -8562,6 +8621,7 @@ phutil_register_library_map(array( 'PhabricatorPolicyInterface', 'PhabricatorDestructibleInterface', 'PhabricatorExtendedPolicyInterface', + 'PhabricatorConduitResultInterface', ), 'PhabricatorProjectColumnDetailController' => 'PhabricatorProjectBoardController', 'PhabricatorProjectColumnEditController' => 'PhabricatorProjectBoardController', @@ -8573,6 +8633,7 @@ phutil_register_library_map(array( ), 'PhabricatorProjectColumnPositionQuery' => 'PhabricatorCursorPagedPolicyAwareQuery', 'PhabricatorProjectColumnQuery' => 'PhabricatorCursorPagedPolicyAwareQuery', + 'PhabricatorProjectColumnSearchEngine' => 'PhabricatorApplicationSearchEngine', 'PhabricatorProjectColumnTransaction' => 'PhabricatorApplicationTransaction', 'PhabricatorProjectColumnTransactionEditor' => 'PhabricatorApplicationTransactionEditor', 'PhabricatorProjectColumnTransactionQuery' => 'PhabricatorApplicationTransactionQuery', @@ -8666,6 +8727,7 @@ phutil_register_library_map(array( 'PhabricatorProjectWatcherListView' => 'PhabricatorProjectUserListView', 'PhabricatorProjectWorkboardBackgroundColor' => 'Phobject', 'PhabricatorProjectWorkboardProfileMenuItem' => 'PhabricatorProfileMenuItem', + 'PhabricatorProjectsAncestorsSearchEngineAttachment' => 'PhabricatorSearchEngineAttachment', 'PhabricatorProjectsCurtainExtension' => 'PHUICurtainExtension', 'PhabricatorProjectsEditEngineExtension' => 'PhabricatorEditEngineExtension', 'PhabricatorProjectsEditField' => 'PhabricatorTokenizerEditField', @@ -8733,6 +8795,7 @@ phutil_register_library_map(array( 'PhabricatorCustomFieldInterface', 'PhabricatorApplicationTransactionInterface', 'PhabricatorFulltextInterface', + 'PhabricatorConduitResultInterface', ), 'PhabricatorRepositoryCommitChangeParserWorker' => 'PhabricatorRepositoryCommitParserWorker', 'PhabricatorRepositoryCommitData' => 'PhabricatorRepositoryDAO', @@ -9845,6 +9908,7 @@ phutil_register_library_map(array( 'ProjectAddProjectsEmailCommand' => 'MetaMTAEmailTransactionCommand', 'ProjectBoardTaskCard' => 'Phobject', 'ProjectCanLockProjectsCapability' => 'PhabricatorPolicyCapability', + 'ProjectColumnSearchConduitAPIMethod' => 'PhabricatorSearchEngineAPIMethod', 'ProjectConduitAPIMethod' => 'ConduitAPIMethod', 'ProjectCreateConduitAPIMethod' => 'ProjectConduitAPIMethod', 'ProjectCreateProjectsCapability' => 'PhabricatorPolicyCapability', diff --git a/src/applications/audit/application/PhabricatorAuditApplication.php b/src/applications/audit/application/PhabricatorAuditApplication.php index 932f2ca651..7d8ad94aa7 100644 --- a/src/applications/audit/application/PhabricatorAuditApplication.php +++ b/src/applications/audit/application/PhabricatorAuditApplication.php @@ -3,7 +3,7 @@ final class PhabricatorAuditApplication extends PhabricatorApplication { public function getBaseURI() { - return '/audit/'; + return '/diffusion/commit/'; } public function getIcon() { @@ -18,84 +18,20 @@ final class PhabricatorAuditApplication extends PhabricatorApplication { return pht('Browse and Audit Commits'); } + public function canUninstall() { + // Audit was once a separate application, but has largely merged with + // Diffusion. + return false; + } + public function isPinnedByDefault(PhabricatorUser $viewer) { - return true; - } - - public function getHelpDocumentationArticles(PhabricatorUser $viewer) { - return array( - array( - 'name' => pht('Audit User Guide'), - 'href' => PhabricatorEnv::getDoclink('Audit User Guide'), - ), - ); - } - - public function getRoutes() { - return array( - '/audit/' => array( - '(?:query/(?P[^/]+)/)?' => 'PhabricatorAuditListController', - 'addcomment/' => 'PhabricatorAuditAddCommentController', - 'preview/(?P[1-9]\d*)/' => 'PhabricatorAuditPreviewController', - ), - ); + return parent::isClassInstalledForViewer( + 'PhabricatorDiffusionApplication', + $viewer); } public function getApplicationOrder() { return 0.130; } - public function loadStatus(PhabricatorUser $user) { - $status = array(); - $limit = self::MAX_STATUS_ITEMS; - - $phids = PhabricatorAuditCommentEditor::loadAuditPHIDsForUser($user); - - $query = id(new DiffusionCommitQuery()) - ->setViewer($user) - ->withAuthorPHIDs(array($user->getPHID())) - ->withAuditStatus(DiffusionCommitQuery::AUDIT_STATUS_CONCERN) - ->setLimit($limit); - $commits = $query->execute(); - - $count = count($commits); - if ($count >= $limit) { - $count_str = pht('%s+ Problem Commits', new PhutilNumber($limit - 1)); - } else { - $count_str = pht('%s Problem Commit(s)', new PhutilNumber($count)); - } - - $type = PhabricatorApplicationStatusView::TYPE_NEEDS_ATTENTION; - $status[] = id(new PhabricatorApplicationStatusView()) - ->setType($type) - ->setText($count_str) - ->setCount($count); - - $query = id(new DiffusionCommitQuery()) - ->setViewer($user) - ->withNeedsAuditByPHIDs($phids) - ->withAuditStatus(DiffusionCommitQuery::AUDIT_STATUS_OPEN) - ->setLimit($limit); - $commits = $query->execute(); - - $count = count($commits); - if ($count >= $limit) { - $count_str = pht( - '%s+ Commits Awaiting Audit', - new PhutilNumber($limit - 1)); - } else { - $count_str = pht( - '%s Commit(s) Awaiting Audit', - new PhutilNumber($count)); - } - - $type = PhabricatorApplicationStatusView::TYPE_WARNING; - $status[] = id(new PhabricatorApplicationStatusView()) - ->setType($type) - ->setText($count_str) - ->setCount($count); - - return $status; - } - } diff --git a/src/applications/audit/conduit/AuditConduitAPIMethod.php b/src/applications/audit/conduit/AuditConduitAPIMethod.php index 3a11b27698..a5a7957b1f 100644 --- a/src/applications/audit/conduit/AuditConduitAPIMethod.php +++ b/src/applications/audit/conduit/AuditConduitAPIMethod.php @@ -3,7 +3,8 @@ abstract class AuditConduitAPIMethod extends ConduitAPIMethod { final public function getApplication() { - return PhabricatorApplication::getByClass('PhabricatorAuditApplication'); + return PhabricatorApplication::getByClass( + 'PhabricatorDiffusionApplication'); } } diff --git a/src/applications/audit/conduit/AuditQueryConduitAPIMethod.php b/src/applications/audit/conduit/AuditQueryConduitAPIMethod.php index 97dbec3d68..0f2fe473f5 100644 --- a/src/applications/audit/conduit/AuditQueryConduitAPIMethod.php +++ b/src/applications/audit/conduit/AuditQueryConduitAPIMethod.php @@ -2,6 +2,12 @@ final class AuditQueryConduitAPIMethod extends AuditConduitAPIMethod { + const AUDIT_LEGACYSTATUS_ANY = 'audit-status-any'; + const AUDIT_LEGACYSTATUS_OPEN = 'audit-status-open'; + const AUDIT_LEGACYSTATUS_CONCERN = 'audit-status-concern'; + const AUDIT_LEGACYSTATUS_ACCEPTED = 'audit-status-accepted'; + const AUDIT_LEGACYSTATUS_PARTIAL = 'audit-status-partial'; + public function getAPIMethodName() { return 'audit.query'; } @@ -10,13 +16,23 @@ final class AuditQueryConduitAPIMethod extends AuditConduitAPIMethod { return pht('Query audit requests.'); } + public function getMethodStatus() { + return self::METHOD_STATUS_FROZEN; + } + + public function getMethodStatusDescription() { + return pht( + 'This method is frozen and will eventually be deprecated. New code '. + 'should use "diffusion.commit.search" instead.'); + } + protected function defineParamTypes() { $statuses = array( - DiffusionCommitQuery::AUDIT_STATUS_ANY, - DiffusionCommitQuery::AUDIT_STATUS_OPEN, - DiffusionCommitQuery::AUDIT_STATUS_CONCERN, - DiffusionCommitQuery::AUDIT_STATUS_ACCEPTED, - DiffusionCommitQuery::AUDIT_STATUS_PARTIAL, + self::AUDIT_LEGACYSTATUS_ANY, + self::AUDIT_LEGACYSTATUS_OPEN, + self::AUDIT_LEGACYSTATUS_CONCERN, + self::AUDIT_LEGACYSTATUS_ACCEPTED, + self::AUDIT_LEGACYSTATUS_PARTIAL, ); $status_const = $this->formatStringConstants($statuses); @@ -50,10 +66,26 @@ final class AuditQueryConduitAPIMethod extends AuditConduitAPIMethod { $query->withPHIDs($commit_phids); } - $status = $request->getValue( - 'status', - DiffusionCommitQuery::AUDIT_STATUS_ANY); - $query->withAuditStatus($status); + $status_map = array( + self::AUDIT_LEGACYSTATUS_OPEN => array( + PhabricatorAuditCommitStatusConstants::NEEDS_AUDIT, + PhabricatorAuditCommitStatusConstants::CONCERN_RAISED, + ), + self::AUDIT_LEGACYSTATUS_CONCERN => array( + PhabricatorAuditCommitStatusConstants::CONCERN_RAISED, + ), + self::AUDIT_LEGACYSTATUS_ACCEPTED => array( + PhabricatorAuditCommitStatusConstants::CONCERN_ACCEPTED, + ), + self::AUDIT_LEGACYSTATUS_PARTIAL => array( + PhabricatorAuditCommitStatusConstants::PARTIALLY_AUDITED, + ), + ); + + $status = $request->getValue('status'); + if (isset($status_map[$status])) { + $query->withStatuses($status_map[$status]); + } // NOTE: These affect the number of commits identified, which is sort of // reasonable but means the method may return an arbitrary number of diff --git a/src/applications/audit/constants/PhabricatorAuditCommitStatusConstants.php b/src/applications/audit/constants/PhabricatorAuditCommitStatusConstants.php index ca959d4aef..bced1b2dc6 100644 --- a/src/applications/audit/constants/PhabricatorAuditCommitStatusConstants.php +++ b/src/applications/audit/constants/PhabricatorAuditCommitStatusConstants.php @@ -10,7 +10,7 @@ final class PhabricatorAuditCommitStatusConstants extends Phobject { public static function getStatusNameMap() { $map = array( - self::NONE => pht('None'), + self::NONE => pht('No Audits'), self::NEEDS_AUDIT => pht('Audit Required'), self::CONCERN_RAISED => pht('Concern Raised'), self::PARTIALLY_AUDITED => pht('Partially Audited'), @@ -28,6 +28,7 @@ final class PhabricatorAuditCommitStatusConstants extends Phobject { return array( self::CONCERN_RAISED, self::NEEDS_AUDIT, + self::PARTIALLY_AUDITED, ); } @@ -45,6 +46,9 @@ final class PhabricatorAuditCommitStatusConstants extends Phobject { case self::FULLY_AUDITED: $color = 'green'; break; + case self::NONE: + $color = 'bluegrey'; + break; default: $color = null; break; @@ -55,13 +59,18 @@ final class PhabricatorAuditCommitStatusConstants extends Phobject { public static function getStatusIcon($code) { switch ($code) { case self::CONCERN_RAISED: - $icon = 'fa-exclamation-circle'; + $icon = 'fa-times-circle'; break; case self::NEEDS_AUDIT: - case self::PARTIALLY_AUDITED: $icon = 'fa-exclamation-circle'; break; + case self::PARTIALLY_AUDITED: + $icon = 'fa-check-circle-o'; + break; case self::FULLY_AUDITED: + $icon = 'fa-check-circle'; + break; + case self::NONE: $icon = 'fa-check'; break; default: diff --git a/src/applications/audit/constants/PhabricatorAuditStatusConstants.php b/src/applications/audit/constants/PhabricatorAuditStatusConstants.php index 5920b7f357..9685e152e9 100644 --- a/src/applications/audit/constants/PhabricatorAuditStatusConstants.php +++ b/src/applications/audit/constants/PhabricatorAuditStatusConstants.php @@ -28,6 +28,13 @@ final class PhabricatorAuditStatusConstants extends Phobject { return $map; } + public static function getActionRequiredStatusConstants() { + return array( + self::AUDIT_REQUIRED, + self::AUDIT_REQUESTED, + ); + } + public static function getStatusName($code) { return idx(self::getStatusNameMap(), $code, pht('Unknown')); } diff --git a/src/applications/audit/controller/PhabricatorAuditAddCommentController.php b/src/applications/audit/controller/PhabricatorAuditAddCommentController.php deleted file mode 100644 index 5e50b2ae9d..0000000000 --- a/src/applications/audit/controller/PhabricatorAuditAddCommentController.php +++ /dev/null @@ -1,90 +0,0 @@ -getViewer(); - - if (!$request->isFormPost()) { - return new Aphront403Response(); - } - - $commit_phid = $request->getStr('commit'); - $commit = id(new DiffusionCommitQuery()) - ->setViewer($viewer) - ->withPHIDs(array($commit_phid)) - ->needAuditRequests(true) - ->executeOne(); - if (!$commit) { - return new Aphront404Response(); - } - - $xactions = array(); - - // make sure we only add auditors or ccs if the action matches - $action = $request->getStr('action'); - switch ($action) { - case PhabricatorAuditActionConstants::ADD_AUDITORS: - $auditors = $request->getArr('auditors'); - $xactions[] = id(new PhabricatorAuditTransaction()) - ->setTransactionType(PhabricatorAuditActionConstants::ADD_AUDITORS) - ->setNewValue(array_fuse($auditors)); - break; - case PhabricatorAuditActionConstants::ADD_CCS: - $xactions[] = id(new PhabricatorAuditTransaction()) - ->setTransactionType(PhabricatorTransactions::TYPE_SUBSCRIBERS) - ->setNewValue( - array( - '+' => $request->getArr('ccs'), - )); - break; - case PhabricatorAuditActionConstants::COMMENT: - // We'll deal with this below. - break; - default: - $xactions[] = id(new PhabricatorAuditTransaction()) - ->setTransactionType(PhabricatorAuditActionConstants::ACTION) - ->setNewValue($action); - break; - } - - $content = $request->getStr('content'); - if (strlen($content)) { - $xactions[] = id(new PhabricatorAuditTransaction()) - ->setTransactionType(PhabricatorTransactions::TYPE_COMMENT) - ->attachComment( - id(new PhabricatorAuditTransactionComment()) - ->setCommitPHID($commit->getPHID()) - ->setContent($content)); - } - - $inlines = PhabricatorAuditInlineComment::loadDraftComments( - $viewer, - $commit->getPHID()); - foreach ($inlines as $inline) { - $xactions[] = id(new PhabricatorAuditTransaction()) - ->setTransactionType(PhabricatorAuditActionConstants::INLINE) - ->attachComment($inline->getTransactionComment()); - } - - id(new PhabricatorAuditEditor()) - ->setActor($viewer) - ->setContentSourceFromRequest($request) - ->setContinueOnMissingFields(true) - ->applyTransactions($commit, $xactions); - - $draft = id(new PhabricatorDraft())->loadOneWhere( - 'authorPHID = %s AND draftKey = %s', - $viewer->getPHID(), - 'diffusion-audit-'.$commit->getID()); - if ($draft) { - $draft->delete(); - } - - $uri = $commit->getURI(); - - return id(new AphrontRedirectResponse())->setURI($uri); - } - -} diff --git a/src/applications/audit/controller/PhabricatorAuditListController.php b/src/applications/audit/controller/PhabricatorAuditListController.php deleted file mode 100644 index 4d2d7bf37e..0000000000 --- a/src/applications/audit/controller/PhabricatorAuditListController.php +++ /dev/null @@ -1,19 +0,0 @@ -setQueryKey($request->getURIData('queryKey')) - ->setSearchEngine(new PhabricatorCommitSearchEngine()) - ->setNavigation($this->buildSideNavView()); - - return $this->delegateToController($controller); - } - -} diff --git a/src/applications/audit/controller/PhabricatorAuditPreviewController.php b/src/applications/audit/controller/PhabricatorAuditPreviewController.php deleted file mode 100644 index 2b0212370c..0000000000 --- a/src/applications/audit/controller/PhabricatorAuditPreviewController.php +++ /dev/null @@ -1,82 +0,0 @@ -getViewer(); - $id = $request->getURIData('id'); - - $commit = id(new PhabricatorRepositoryCommit())->load($id); - if (!$commit) { - return new Aphront404Response(); - } - - $xactions = array(); - - $action = $request->getStr('action'); - if ($action != PhabricatorAuditActionConstants::COMMENT) { - $action_xaction = id(new PhabricatorAuditTransaction()) - ->setAuthorPHID($viewer->getPHID()) - ->setObjectPHID($commit->getPHID()) - ->setTransactionType(PhabricatorAuditActionConstants::ACTION) - ->setNewValue($action); - - $auditors = $request->getStrList('auditors'); - if ($action == PhabricatorAuditActionConstants::ADD_AUDITORS && - $auditors) { - $action_xaction->setTransactionType($action); - $action_xaction->setNewValue(array_fuse($auditors)); - } - - $ccs = $request->getStrList('ccs'); - if ($action == PhabricatorAuditActionConstants::ADD_CCS && $ccs) { - $action_xaction->setTransactionType( - PhabricatorTransactions::TYPE_SUBSCRIBERS); - - // NOTE: This doesn't get processed before use, so just provide fake - // values. - $action_xaction->setOldValue(array()); - $action_xaction->setNewValue($ccs); - } - - $xactions[] = $action_xaction; - } - - $content = $request->getStr('content'); - if (strlen($content)) { - $xactions[] = id(new PhabricatorAuditTransaction()) - ->setAuthorPHID($viewer->getPHID()) - ->setObjectPHID($commit->getPHID()) - ->setTransactionType(PhabricatorTransactions::TYPE_COMMENT) - ->attachComment( - id(new PhabricatorAuditTransactionComment()) - ->setContent($content)); - } - - $phids = array(); - foreach ($xactions as $xaction) { - $phids[] = $xaction->getRequiredHandlePHIDs(); - } - $phids = array_mergev($phids); - $handles = $this->loadViewerHandles($phids); - foreach ($xactions as $xaction) { - $xaction->setHandles($handles); - } - - $view = id(new PhabricatorAuditTransactionView()) - ->setIsPreview(true) - ->setUser($viewer) - ->setObjectPHID($commit->getPHID()) - ->setTransactions($xactions); - - id(new PhabricatorDraft()) - ->setAuthorPHID($viewer->getPHID()) - ->setDraftKey('diffusion-audit-'.$id) - ->setDraft($content) - ->replaceOrDelete(); - - return id(new AphrontAjaxResponse())->setContent(hsprintf('%s', $view)); - } - -} diff --git a/src/applications/audit/editor/PhabricatorAuditEditor.php b/src/applications/audit/editor/PhabricatorAuditEditor.php index a6ef866c93..4b2d9bec29 100644 --- a/src/applications/audit/editor/PhabricatorAuditEditor.php +++ b/src/applications/audit/editor/PhabricatorAuditEditor.php @@ -10,7 +10,7 @@ final class PhabricatorAuditEditor private $rawPatch; private $auditorPHIDs = array(); - private $didExpandInlineState; + private $didExpandInlineState = false; public function addAuditReason($phid, $reason) { if (!isset($this->auditReasonMap[$phid])) { @@ -42,7 +42,7 @@ final class PhabricatorAuditEditor } public function getEditorApplicationClass() { - return 'PhabricatorAuditApplication'; + return 'PhabricatorDiffusionApplication'; } public function getEditorObjectsDescription() { @@ -67,6 +67,21 @@ final class PhabricatorAuditEditor return $types; } + protected function expandTransactions( + PhabricatorLiskDAO $object, + array $xactions) { + + foreach ($xactions as $xaction) { + switch ($xaction->getTransactionType()) { + case PhabricatorTransactions::TYPE_INLINESTATE: + $this->didExpandInlineState = true; + break; + } + } + + return parent::expandTransactions($object, $xactions); + } + protected function transactionHasEffect( PhabricatorLiskDAO $object, PhabricatorApplicationTransaction $xaction) { diff --git a/src/applications/audit/mail/PhabricatorAuditMailReceiver.php b/src/applications/audit/mail/PhabricatorAuditMailReceiver.php index 36e68c76a9..9dc70e9d8a 100644 --- a/src/applications/audit/mail/PhabricatorAuditMailReceiver.php +++ b/src/applications/audit/mail/PhabricatorAuditMailReceiver.php @@ -4,7 +4,7 @@ final class PhabricatorAuditMailReceiver extends PhabricatorObjectMailReceiver { public function isEnabled() { return PhabricatorApplication::isClassInstalled( - 'PhabricatorAuditApplication'); + 'PhabricatorDiffusionApplication'); } protected function getObjectPattern() { diff --git a/src/applications/audit/management/PhabricatorAuditManagementDeleteWorkflow.php b/src/applications/audit/management/PhabricatorAuditManagementDeleteWorkflow.php index 95fbdb430f..23583422fb 100644 --- a/src/applications/audit/management/PhabricatorAuditManagementDeleteWorkflow.php +++ b/src/applications/audit/management/PhabricatorAuditManagementDeleteWorkflow.php @@ -67,9 +67,6 @@ final class PhabricatorAuditManagementDeleteWorkflow $ids = $this->parseList($args->getArg('ids')); $status = $args->getArg('status'); - if (!$status) { - $status = DiffusionCommitQuery::AUDIT_STATUS_OPEN; - } $min_date = $this->loadDate($args->getArg('min-commit-date')); $max_date = $this->loadDate($args->getArg('max-commit-date')); @@ -85,7 +82,7 @@ final class PhabricatorAuditManagementDeleteWorkflow ->needAuditRequests(true); if ($status) { - $query->withAuditStatus($status); + $query->withStatuses(array($status)); } $id_map = array(); diff --git a/src/applications/audit/query/PhabricatorCommitSearchEngine.php b/src/applications/audit/query/PhabricatorCommitSearchEngine.php index b0c5ec1d2c..fa4fa3c963 100644 --- a/src/applications/audit/query/PhabricatorCommitSearchEngine.php +++ b/src/applications/audit/query/PhabricatorCommitSearchEngine.php @@ -17,23 +17,27 @@ final class PhabricatorCommitSearchEngine ->needCommitData(true); } + protected function newResultBuckets() { + return DiffusionCommitResultBucket::getAllResultBuckets(); + } + protected function buildQueryFromParameters(array $map) { $query = $this->newQuery(); - if ($map['needsAuditByPHIDs']) { - $query->withNeedsAuditByPHIDs($map['needsAuditByPHIDs']); + if ($map['responsiblePHIDs']) { + $query->withResponsiblePHIDs($map['responsiblePHIDs']); } if ($map['auditorPHIDs']) { $query->withAuditorPHIDs($map['auditorPHIDs']); } - if ($map['commitAuthorPHIDs']) { - $query->withAuthorPHIDs($map['commitAuthorPHIDs']); + if ($map['authorPHIDs']) { + $query->withAuthorPHIDs($map['authorPHIDs']); } - if ($map['auditStatus']) { - $query->withAuditStatus($map['auditStatus']); + if ($map['statuses']) { + $query->withStatuses($map['statuses']); } if ($map['repositoryPHIDs']) { @@ -46,48 +50,47 @@ final class PhabricatorCommitSearchEngine protected function buildCustomSearchFields() { return array( id(new PhabricatorSearchDatasourceField()) - ->setLabel(pht('Needs Audit By')) - ->setKey('needsAuditByPHIDs') - ->setAliases(array('needs', 'need')) - ->setDatasource(new DiffusionAuditorFunctionDatasource()), + ->setLabel(pht('Responsible Users')) + ->setKey('responsiblePHIDs') + ->setConduitKey('responsible') + ->setAliases(array('responsible', 'responsibles', 'responsiblePHID')) + ->setDatasource(new DifferentialResponsibleDatasource()), + id(new PhabricatorUsersSearchField()) + ->setLabel(pht('Authors')) + ->setKey('authorPHIDs') + ->setConduitKey('authors') + ->setAliases(array('author', 'authors', 'authorPHID')), id(new PhabricatorSearchDatasourceField()) ->setLabel(pht('Auditors')) ->setKey('auditorPHIDs') - ->setAliases(array('auditor', 'auditors')) + ->setConduitKey('auditors') + ->setAliases(array('auditor', 'auditors', 'auditorPHID')) ->setDatasource(new DiffusionAuditorFunctionDatasource()), - id(new PhabricatorUsersSearchField()) - ->setLabel(pht('Authors')) - ->setKey('commitAuthorPHIDs') - ->setAliases(array('author', 'authors')), - id(new PhabricatorSearchSelectField()) + id(new PhabricatorSearchCheckboxesField()) ->setLabel(pht('Audit Status')) - ->setKey('auditStatus') + ->setKey('statuses') ->setAliases(array('status')) - ->setOptions($this->getAuditStatusOptions()), + ->setOptions(PhabricatorAuditCommitStatusConstants::getStatusNameMap()), id(new PhabricatorSearchDatasourceField()) ->setLabel(pht('Repositories')) ->setKey('repositoryPHIDs') - ->setAliases(array('repository', 'repositories')) + ->setConduitKey('repositories') + ->setAliases(array('repository', 'repositories', 'repositoryPHID')) ->setDatasource(new DiffusionRepositoryDatasource()), ); } protected function getURI($path) { - return '/audit/'.$path; + return '/diffusion/commit/'.$path; } protected function getBuiltinQueryNames() { $names = array(); if ($this->requireViewer()->isLoggedIn()) { - $names['need'] = pht('Needs Audit'); - $names['problem'] = pht('Problem Commits'); - } - - $names['open'] = pht('Open Audits'); - - if ($this->requireViewer()->isLoggedIn()) { - $names['authored'] = pht('Authored Commits'); + $names['active'] = pht('Active Audits'); + $names['authored'] = pht('Authored'); + $names['audited'] = pht('Audited'); } $names['all'] = pht('All Commits'); @@ -101,76 +104,71 @@ final class PhabricatorCommitSearchEngine $viewer = $this->requireViewer(); $viewer_phid = $viewer->getPHID(); - $status_open = DiffusionCommitQuery::AUDIT_STATUS_OPEN; - switch ($query_key) { case 'all': return $query; - case 'open': - $query->setParameter('auditStatus', $status_open); - return $query; - case 'need': - $needs_tokens = array( - $viewer_phid, - 'projects('.$viewer_phid.')', - 'packages('.$viewer_phid.')', - ); + case 'active': + $bucket_key = DiffusionCommitRequiredActionResultBucket::BUCKETKEY; - $query->setParameter('needsAuditByPHIDs', $needs_tokens); - $query->setParameter('auditStatus', $status_open); + $open = PhabricatorAuditCommitStatusConstants::getOpenStatusConstants(); + + $query + ->setParameter('responsiblePHIDs', array($viewer_phid)) + ->setParameter('statuses', $open) + ->setParameter('bucket', $bucket_key); return $query; case 'authored': - $query->setParameter('commitAuthorPHIDs', array($viewer->getPHID())); + $query + ->setParameter('authorPHIDs', array($viewer_phid)); return $query; - case 'problem': - $query->setParameter('commitAuthorPHIDs', array($viewer->getPHID())); - $query->setParameter( - 'auditStatus', - DiffusionCommitQuery::AUDIT_STATUS_CONCERN); + case 'audited': + $query + ->setParameter('auditorPHIDs', array($viewer_phid)); return $query; } return parent::buildSavedQueryFromBuiltin($query_key); } - private function getAuditStatusOptions() { - return array( - DiffusionCommitQuery::AUDIT_STATUS_ANY => pht('Any'), - DiffusionCommitQuery::AUDIT_STATUS_OPEN => pht('Open'), - DiffusionCommitQuery::AUDIT_STATUS_CONCERN => pht('Concern Raised'), - DiffusionCommitQuery::AUDIT_STATUS_ACCEPTED => pht('Accepted'), - DiffusionCommitQuery::AUDIT_STATUS_PARTIAL => pht('Partially Audited'), - ); - } - protected function renderResultList( array $commits, PhabricatorSavedQuery $query, array $handles) { - assert_instances_of($commits, 'PhabricatorRepositoryCommit'); - $viewer = $this->requireViewer(); - $nodata = pht('No matching audits.'); - $view = id(new PhabricatorAuditListView()) - ->setUser($viewer) - ->setCommits($commits) - ->setAuthorityPHIDs( - PhabricatorAuditCommentEditor::loadAuditPHIDsForUser($viewer)) - ->setNoDataString($nodata); - $phids = $view->getRequiredHandlePHIDs(); - if ($phids) { - $handles = id(new PhabricatorHandleQuery()) - ->setViewer($viewer) - ->withPHIDs($phids) - ->execute(); + $bucket = $this->getResultBucket($query); + + $template = id(new PhabricatorAuditListView()) + ->setViewer($viewer); + + $views = array(); + if ($bucket) { + $bucket->setViewer($viewer); + + try { + $groups = $bucket->newResultGroups($query, $commits); + + foreach ($groups as $group) { + $views[] = id(clone $template) + ->setHeader($group->getName()) + ->setNoDataString($group->getNoDataString()) + ->setCommits($group->getObjects()); + } + } catch (Exception $ex) { + $this->addError($ex->getMessage()); + } } else { - $handles = array(); + $views[] = id(clone $template) + ->setCommits($commits) + ->setNoDataString(pht('No matching commits.')); } - $view->setHandles($handles); - $list = $view->buildList(); + if (count($views) == 1) { + $list = head($views)->buildList(); + } else { + $list = $views; + } $result = new PhabricatorApplicationSearchResultView(); $result->setContent($list); diff --git a/src/applications/audit/storage/PhabricatorAuditInlineComment.php b/src/applications/audit/storage/PhabricatorAuditInlineComment.php index ecb627efec..b330368d1b 100644 --- a/src/applications/audit/storage/PhabricatorAuditInlineComment.php +++ b/src/applications/audit/storage/PhabricatorAuditInlineComment.php @@ -68,7 +68,8 @@ final class PhabricatorAuditInlineComment public static function loadDraftComments( PhabricatorUser $viewer, - $commit_phid) { + $commit_phid, + $raw = false) { $inlines = id(new DiffusionDiffInlineCommentQuery()) ->setViewer($viewer) @@ -80,6 +81,10 @@ final class PhabricatorAuditInlineComment ->needReplyToComments(true) ->execute(); + if ($raw) { + return $inlines; + } + return self::buildProxies($inlines); } diff --git a/src/applications/audit/storage/PhabricatorAuditTransaction.php b/src/applications/audit/storage/PhabricatorAuditTransaction.php index e1f7a9d08a..ff3751b912 100644 --- a/src/applications/audit/storage/PhabricatorAuditTransaction.php +++ b/src/applications/audit/storage/PhabricatorAuditTransaction.php @@ -1,7 +1,7 @@ getTransactionType()) { + case DiffusionCommitAcceptTransaction::TRANSACTIONTYPE: + $tags[] = self::MAILTAG_ACTION_ACCEPT; + break; + case DiffusionCommitConcernTransaction::TRANSACTIONTYPE: + $tags[] = self::MAILTAG_ACTION_CONCERN; + break; + case DiffusionCommitResignTransaction::TRANSACTIONTYPE: + $tags[] = self::MAILTAG_ACTION_RESIGN; + break; + case DiffusionCommitAuditorsTransaction::TRANSACTIONTYPE: + $tags[] = self::MAILTAG_ADD_AUDITORS; + break; case PhabricatorAuditActionConstants::ACTION: switch ($this->getNewValue()) { case PhabricatorAuditActionConstants::CONCERN: diff --git a/src/applications/audit/view/PhabricatorAuditListView.php b/src/applications/audit/view/PhabricatorAuditListView.php index 593172006c..f348acc759 100644 --- a/src/applications/audit/view/PhabricatorAuditListView.php +++ b/src/applications/audit/view/PhabricatorAuditListView.php @@ -3,23 +3,10 @@ final class PhabricatorAuditListView extends AphrontView { private $commits; - private $handles; - private $authorityPHIDs = array(); + private $header; private $noDataString; - private $highlightedAudits; - public function setHandles(array $handles) { - assert_instances_of($handles, 'PhabricatorObjectHandle'); - $this->handles = $handles; - return $this; - } - - public function setAuthorityPHIDs(array $phids) { - $this->authorityPHIDs = $phids; - return $this; - } - public function setNoDataString($no_data_string) { $this->noDataString = $no_data_string; return $this; @@ -29,6 +16,15 @@ final class PhabricatorAuditListView extends AphrontView { return $this->noDataString; } + public function setHeader($header) { + $this->header = $header; + return $this; + } + + public function getHeader() { + return $this->header; + } + /** * These commits should have both commit data and audit requests attached. */ @@ -42,28 +38,6 @@ final class PhabricatorAuditListView extends AphrontView { return $this->commits; } - public function getRequiredHandlePHIDs() { - $phids = array(); - $commits = $this->getCommits(); - foreach ($commits as $commit) { - $phids[$commit->getPHID()] = true; - $phids[$commit->getAuthorPHID()] = true; - $audits = $commit->getAudits(); - foreach ($audits as $audit) { - $phids[$audit->getAuditorPHID()] = true; - } - } - return array_keys($phids); - } - - private function getHandle($phid) { - $handle = idx($this->handles, $phid); - if (!$handle) { - throw new Exception(pht("No handle for '%s'!", $phid)); - } - return $handle; - } - private function getCommitDescription($phid) { if ($this->commits === null) { return pht('(Unknown Commit)'); @@ -96,62 +70,43 @@ final class PhabricatorAuditListView extends AphrontView { } public function buildList() { - $user = $this->getUser(); - if (!$user) { - throw new Exception( - pht( - 'You must %s before %s!', - 'setUser()', - __FUNCTION__.'()')); - } + $viewer = $this->getViewer(); $rowc = array(); + $handles = $viewer->loadHandles(mpull($this->commits, 'getPHID')); + $list = new PHUIObjectItemListView(); foreach ($this->commits as $commit) { $commit_phid = $commit->getPHID(); - $commit_handle = $this->getHandle($commit_phid); + $commit_handle = $handles[$commit_phid]; $committed = null; $commit_name = $commit_handle->getName(); $commit_link = $commit_handle->getURI(); $commit_desc = $this->getCommitDescription($commit_phid); - $committed = phabricator_datetime($commit->getEpoch(), $user); + $committed = phabricator_datetime($commit->getEpoch(), $viewer); $audits = mpull($commit->getAudits(), null, 'getAuditorPHID'); $auditors = array(); $reasons = array(); foreach ($audits as $audit) { $auditor_phid = $audit->getAuditorPHID(); - $auditors[$auditor_phid] = - $this->getHandle($auditor_phid)->renderLink(); + $auditors[$auditor_phid] = $viewer->renderHandle($auditor_phid); } $auditors = phutil_implode_html(', ', $auditors); - $authority_audits = array_select_keys($audits, $this->authorityPHIDs); - if ($authority_audits) { - $audit = reset($authority_audits); - } else { - $audit = reset($audits); - } - if ($audit) { - $reasons = $audit->getAuditReasons(); - $reasons = phutil_implode_html(', ', $reasons); - $status_code = $audit->getAuditStatus(); - $status_text = - PhabricatorAuditStatusConstants::getStatusName($status_code); - $status_color = - PhabricatorAuditStatusConstants::getStatusColor($status_code); - $status_icon = - PhabricatorAuditStatusConstants::getStatusIcon($status_code); - } else { - $reasons = null; - $status_text = null; - $status_color = null; - $status_icon = null; - } + $status = $commit->getAuditStatus(); + + $status_text = + PhabricatorAuditCommitStatusConstants::getStatusName($status); + $status_color = + PhabricatorAuditCommitStatusConstants::getStatusColor($status); + $status_icon = + PhabricatorAuditCommitStatusConstants::getStatusIcon($status); + $author_phid = $commit->getAuthorPHID(); if ($author_phid) { - $author_name = $this->getHandle($author_phid)->renderLink(); + $author_name = $viewer->renderHandle($author_phid); } else { $author_name = $commit->getCommitData()->getAuthorName(); } @@ -169,8 +124,7 @@ final class PhabricatorAuditListView extends AphrontView { } if ($status_color) { - $item->setStatusIcon( - $status_icon.' '.$status_color, $status_text); + $item->setStatusIcon($status_icon.' '.$status_color, $status_text); } $list->addItem($item); @@ -180,6 +134,10 @@ final class PhabricatorAuditListView extends AphrontView { $list->setNoDataString($this->noDataString); } + if ($this->header) { + $list->setHeader($this->header); + } + return $list; } diff --git a/src/applications/audit/view/PhabricatorAuditTransactionView.php b/src/applications/audit/view/PhabricatorAuditTransactionView.php index 305ea19c56..897440d9d7 100644 --- a/src/applications/audit/view/PhabricatorAuditTransactionView.php +++ b/src/applications/audit/view/PhabricatorAuditTransactionView.php @@ -3,7 +3,7 @@ final class PhabricatorAuditTransactionView extends PhabricatorApplicationTransactionView { - private $pathMap; + private $pathMap = array(); public function setPathMap(array $path_map) { $this->pathMap = $path_map; @@ -55,12 +55,17 @@ final class PhabricatorAuditTransactionView $type_inline = PhabricatorAuditActionConstants::INLINE; $group = $xaction->getTransactionGroup(); + if ($xaction->getTransactionType() == $type_inline) { array_unshift($group, $xaction); } else { $out[] = parent::renderTransactionContent($xaction); } + if ($this->getIsPreview()) { + return $out; + } + if (!$group) { return $out; } @@ -76,48 +81,58 @@ final class PhabricatorAuditTransactionView } } - if ($inlines) { - - // TODO: This should do something similar to sortAndGroupInlines() to get - // a stable ordering. - - $inlines_by_path = array(); - foreach ($inlines as $key => $inline) { - $comment = $inline->getComment(); - if (!$comment) { - // TODO: Migrate these away? They probably do not exist on normal - // non-development installs. - unset($inlines[$key]); - continue; - } - $path_id = $comment->getPathID(); - $inlines_by_path[$path_id][] = $inline; + $structs = array(); + foreach ($inlines as $key => $inline) { + $comment = $inline->getComment(); + if (!$comment) { + // TODO: Migrate these away? They probably do not exist on normal + // non-development installs. + unset($inlines[$key]); + continue; } - $inline_view = new PhabricatorInlineSummaryView(); - foreach ($inlines_by_path as $path_id => $group) { - $path = idx($this->pathMap, $path_id); - if ($path === null) { - continue; - } - - $items = array(); - foreach ($group as $inline) { - $comment = $inline->getComment(); - $item = array( - 'id' => $comment->getID(), - 'line' => $comment->getLineNumber(), - 'length' => $comment->getLineLength(), - 'content' => parent::renderTransactionContent($inline), - ); - $items[] = $item; - } - $inline_view->addCommentGroup($path, $items); + $path_id = $comment->getPathID(); + $path = idx($this->pathMap, $path_id); + if ($path === null) { + continue; } - $out[] = $inline_view; + $structs[] = array( + 'inline' => $inline, + 'path' => $path, + 'sort' => (string)id(new PhutilSortVector()) + ->addString($path) + ->addInt($comment->getLineNumber()) + ->addInt($comment->getLineLength()) + ->addInt($inline->getID()), + ); } + if (!$structs) { + return $out; + } + + $structs = isort($structs, 'sort'); + $structs = igroup($structs, 'path'); + + $inline_view = new PhabricatorInlineSummaryView(); + foreach ($structs as $path => $group) { + $inlines = ipull($group, 'inline'); + $items = array(); + foreach ($inlines as $inline) { + $comment = $inline->getComment(); + $items[] = array( + 'id' => $comment->getID(), + 'line' => $comment->getLineNumber(), + 'length' => $comment->getLineLength(), + 'content' => parent::renderTransactionContent($inline), + ); + } + $inline_view->addCommentGroup($path, $items); + } + + $out[] = $inline_view; + return $out; } diff --git a/src/applications/cache/spec/PhabricatorDataCacheSpec.php b/src/applications/cache/spec/PhabricatorDataCacheSpec.php index eae5d75790..482af00d00 100644 --- a/src/applications/cache/spec/PhabricatorDataCacheSpec.php +++ b/src/applications/cache/spec/PhabricatorDataCacheSpec.php @@ -79,28 +79,40 @@ final class PhabricatorDataCacheSpec extends PhabricatorCacheSpec { } private function initAPCCommonSpec() { - $mem = apc_sma_info(); - $this->setTotalMemory($mem['num_seg'] * $mem['seg_size']); - - $info = apc_cache_info('user'); - $this->setUsedMemory($info['mem_size']); - $this->setEntryCount(count($info['cache_list'])); - - $cache = $info['cache_list']; $state = array(); - foreach ($cache as $item) { - $info = idx($item, 'info', ''); - $key = self::getKeyPattern($info); - if (empty($state[$key])) { - $state[$key] = array( - 'max' => 0, - 'total' => 0, - 'count' => 0, - ); + + if (function_exists('apcu_sma_info')) { + $mem = apcu_sma_info(); + $info = apcu_cache_info(); + } else if (function_exists('apc_sma_info')) { + $mem = apc_sma_info(); + $info = apc_cache_info('user'); + } else { + $mem = null; + } + + if ($mem) { + $this->setTotalMemory($mem['num_seg'] * $mem['seg_size']); + + $this->setUsedMemory($info['mem_size']); + $this->setEntryCount(count($info['cache_list'])); + + $cache = $info['cache_list']; + $state = array(); + foreach ($cache as $item) { + $info = idx($item, 'info', ''); + $key = self::getKeyPattern($info); + if (empty($state[$key])) { + $state[$key] = array( + 'max' => 0, + 'total' => 0, + 'count' => 0, + ); + } + $state[$key]['max'] = max($state[$key]['max'], $item['mem_size']); + $state[$key]['total'] += $item['mem_size']; + $state[$key]['count']++; } - $state[$key]['max'] = max($state[$key]['max'], $item['mem_size']); - $state[$key]['total'] += $item['mem_size']; - $state[$key]['count']++; } $this->setCacheSummary($state); diff --git a/src/applications/celerity/postprocessor/CelerityDefaultPostprocessor.php b/src/applications/celerity/postprocessor/CelerityDefaultPostprocessor.php index 7b2fed7686..be5bfc14c2 100644 --- a/src/applications/celerity/postprocessor/CelerityDefaultPostprocessor.php +++ b/src/applications/celerity/postprocessor/CelerityDefaultPostprocessor.php @@ -192,6 +192,14 @@ final class CelerityDefaultPostprocessor 'sh-disabledtext' => '#a6a6a6', 'sh-disabledbackground' => '#f3f3f3', + // Diffs + 'new-background' => '#eaffea', + 'new-bright' => '#a6f3a6', + 'old-background' => '#ffecec', + 'old-bright' => '#f8cbcb', + 'move-background' => '#fdf5d4', + 'copy-background' => '#f1c40f', + // Background color for "most" themes. 'page.background' => '#f8f8fb', 'page.sidenav' => '#f0f0f2', diff --git a/src/applications/conduit/controller/PhabricatorConduitConsoleController.php b/src/applications/conduit/controller/PhabricatorConduitConsoleController.php index b767ead4f0..b696645e55 100644 --- a/src/applications/conduit/controller/PhabricatorConduitConsoleController.php +++ b/src/applications/conduit/controller/PhabricatorConduitConsoleController.php @@ -128,6 +128,13 @@ final class PhabricatorConduitConsoleController $stability_label = pht('Deprecated Method'); $stability_info = nonempty($reason, pht('This method is deprecated.')); break; + case ConduitAPIMethod::METHOD_STATUS_FROZEN: + $stability_icon = 'fa-archive grey'; + $stability_label = pht('Frozen Method'); + $stability_info = nonempty( + $reason, + pht('This method is frozen and will eventually be deprecated.')); + break; default: $stability_label = null; break; diff --git a/src/applications/conduit/method/ConduitAPIMethod.php b/src/applications/conduit/method/ConduitAPIMethod.php index 3a2cc6e30d..05831a782d 100644 --- a/src/applications/conduit/method/ConduitAPIMethod.php +++ b/src/applications/conduit/method/ConduitAPIMethod.php @@ -11,9 +11,10 @@ abstract class ConduitAPIMethod private $viewer; - const METHOD_STATUS_STABLE = 'stable'; - const METHOD_STATUS_UNSTABLE = 'unstable'; - const METHOD_STATUS_DEPRECATED = 'deprecated'; + const METHOD_STATUS_STABLE = 'stable'; + const METHOD_STATUS_UNSTABLE = 'unstable'; + const METHOD_STATUS_DEPRECATED = 'deprecated'; + const METHOD_STATUS_FROZEN = 'frozen'; const SCOPE_NEVER = 'scope.never'; const SCOPE_ALWAYS = 'scope.always'; diff --git a/src/applications/conduit/query/PhabricatorConduitMethodQuery.php b/src/applications/conduit/query/PhabricatorConduitMethodQuery.php index 0409e4ceb3..eb61d8c480 100644 --- a/src/applications/conduit/query/PhabricatorConduitMethodQuery.php +++ b/src/applications/conduit/query/PhabricatorConduitMethodQuery.php @@ -66,7 +66,8 @@ final class PhabricatorConduitMethodQuery } $status = array( - ConduitAPIMethod::METHOD_STATUS_STABLE => $this->isStable, + ConduitAPIMethod::METHOD_STATUS_STABLE => $this->isStable, + ConduitAPIMethod::METHOD_STATUS_FROZEN => $this->isStable, ConduitAPIMethod::METHOD_STATUS_DEPRECATED => $this->isDeprecated, ConduitAPIMethod::METHOD_STATUS_UNSTABLE => $this->isUnstable, ); diff --git a/src/applications/conduit/query/PhabricatorConduitSearchEngine.php b/src/applications/conduit/query/PhabricatorConduitSearchEngine.php index 3c01005722..787c2154d5 100644 --- a/src/applications/conduit/query/PhabricatorConduitSearchEngine.php +++ b/src/applications/conduit/query/PhabricatorConduitSearchEngine.php @@ -166,6 +166,10 @@ final class PhabricatorConduitSearchEngine $item->addIcon('fa-warning', pht('Deprecated')); $item->setStatusIcon('fa-warning red'); break; + case ConduitAPIMethod::METHOD_STATUS_FROZEN: + $item->addIcon('fa-archive', pht('Frozen')); + $item->setStatusIcon('fa-archive grey'); + break; } $list->addItem($item); diff --git a/src/applications/config/check/PhabricatorPHPConfigSetupCheck.php b/src/applications/config/check/PhabricatorPHPConfigSetupCheck.php index f286b46f9c..1dd3add94d 100644 --- a/src/applications/config/check/PhabricatorPHPConfigSetupCheck.php +++ b/src/applications/config/check/PhabricatorPHPConfigSetupCheck.php @@ -40,7 +40,13 @@ final class PhabricatorPHPConfigSetupCheck extends PhabricatorSetupCheck { ->setMessage($message); } - $raw_post_data = (int)ini_get('always_populate_raw_post_data'); + if (version_compare(phpversion(), '7', '>=')) { + // This option was removed in PHP7. + $raw_post_data = -1; + } else { + $raw_post_data = (int)ini_get('always_populate_raw_post_data'); + } + if ($raw_post_data != -1) { $summary = pht( 'PHP setting "%s" should be set to "-1" to avoid deprecation '. diff --git a/src/applications/config/check/PhabricatorPHPPreflightSetupCheck.php b/src/applications/config/check/PhabricatorPHPPreflightSetupCheck.php index 82a13438d8..7c9653f4ff 100644 --- a/src/applications/config/check/PhabricatorPHPPreflightSetupCheck.php +++ b/src/applications/config/check/PhabricatorPHPPreflightSetupCheck.php @@ -11,15 +11,16 @@ final class PhabricatorPHPPreflightSetupCheck extends PhabricatorSetupCheck { } protected function executeChecks() { - if (version_compare(phpversion(), 7, '>=')) { + if (version_compare(phpversion(), 7, '>=') && + version_compare(phpversion(), 7.1, '<')) { $message = pht( - 'This version of Phabricator does not support PHP 7. You '. - 'are running PHP %s.', + 'This version of Phabricator does not support PHP 7.0. You '. + 'are running PHP %s. Upgrade to PHP 7.1 or newer.', phpversion()); $this->newIssue('php.version7') ->setIsFatal(true) - ->setName(pht('PHP 7 Not Supported')) + ->setName(pht('PHP 7.0 Not Supported')) ->setMessage($message) ->addLink( 'https://phurl.io/u/php7', diff --git a/src/applications/differential/conduit/DifferentialCloseConduitAPIMethod.php b/src/applications/differential/conduit/DifferentialCloseConduitAPIMethod.php index e613647935..d71876961e 100644 --- a/src/applications/differential/conduit/DifferentialCloseConduitAPIMethod.php +++ b/src/applications/differential/conduit/DifferentialCloseConduitAPIMethod.php @@ -11,6 +11,16 @@ final class DifferentialCloseConduitAPIMethod return pht('Close a Differential revision.'); } + public function getMethodStatus() { + return self::METHOD_STATUS_FROZEN; + } + + public function getMethodStatusDescription() { + return pht( + 'This method is frozen and will eventually be deprecated. New code '. + 'should use "differential.revision.edit" instead.'); + } + protected function defineParamTypes() { return array( 'revisionID' => 'required int', diff --git a/src/applications/differential/conduit/DifferentialCreateCommentConduitAPIMethod.php b/src/applications/differential/conduit/DifferentialCreateCommentConduitAPIMethod.php index 8f4b154876..459298c54f 100644 --- a/src/applications/differential/conduit/DifferentialCreateCommentConduitAPIMethod.php +++ b/src/applications/differential/conduit/DifferentialCreateCommentConduitAPIMethod.php @@ -11,6 +11,16 @@ final class DifferentialCreateCommentConduitAPIMethod return pht('Add a comment to a Differential revision.'); } + public function getMethodStatus() { + return self::METHOD_STATUS_FROZEN; + } + + public function getMethodStatusDescription() { + return pht( + 'This method is frozen and will eventually be deprecated. New code '. + 'should use "differential.revision.edit" instead.'); + } + protected function defineParamTypes() { return array( 'revision_id' => 'required revisionid', diff --git a/src/applications/differential/conduit/DifferentialCreateRevisionConduitAPIMethod.php b/src/applications/differential/conduit/DifferentialCreateRevisionConduitAPIMethod.php index 371d57cd9c..532d63680b 100644 --- a/src/applications/differential/conduit/DifferentialCreateRevisionConduitAPIMethod.php +++ b/src/applications/differential/conduit/DifferentialCreateRevisionConduitAPIMethod.php @@ -11,6 +11,16 @@ final class DifferentialCreateRevisionConduitAPIMethod return pht('Create a new Differential revision.'); } + public function getMethodStatus() { + return self::METHOD_STATUS_FROZEN; + } + + public function getMethodStatusDescription() { + return pht( + 'This method is frozen and will eventually be deprecated. New code '. + 'should use "differential.revision.edit" instead.'); + } + protected function defineParamTypes() { return array( // TODO: Arcanist passes this; prevent fatals after D4191 until Conduit diff --git a/src/applications/differential/conduit/DifferentialQueryConduitAPIMethod.php b/src/applications/differential/conduit/DifferentialQueryConduitAPIMethod.php index 1d4bcc2a8d..720361367a 100644 --- a/src/applications/differential/conduit/DifferentialQueryConduitAPIMethod.php +++ b/src/applications/differential/conduit/DifferentialQueryConduitAPIMethod.php @@ -11,6 +11,16 @@ final class DifferentialQueryConduitAPIMethod return pht('Query Differential revisions which match certain criteria.'); } + public function getMethodStatus() { + return self::METHOD_STATUS_FROZEN; + } + + public function getMethodStatusDescription() { + return pht( + 'This method is frozen and will eventually be deprecated. New code '. + 'should use "differential.revision.search" instead.'); + } + protected function defineParamTypes() { $hash_types = ArcanistDifferentialRevisionHash::getTypes(); $hash_const = $this->formatStringConstants($hash_types); diff --git a/src/applications/differential/conduit/DifferentialQueryDiffsConduitAPIMethod.php b/src/applications/differential/conduit/DifferentialQueryDiffsConduitAPIMethod.php index 836181d1df..7ff8600f81 100644 --- a/src/applications/differential/conduit/DifferentialQueryDiffsConduitAPIMethod.php +++ b/src/applications/differential/conduit/DifferentialQueryDiffsConduitAPIMethod.php @@ -26,16 +26,27 @@ final class DifferentialQueryDiffsConduitAPIMethod $ids = $request->getValue('ids', array()); $revision_ids = $request->getValue('revisionIDs', array()); - $diffs = array(); - if ($ids || $revision_ids) { - $diffs = id(new DifferentialDiffQuery()) - ->setViewer($request->getUser()) - ->withIDs($ids) - ->withRevisionIDs($revision_ids) - ->needChangesets(true) - ->execute(); + if (!$ids && !$revision_ids) { + // This method just returns nothing if you pass no constraints because + // pagination hadn't been invented yet in 2008 when this method was + // written. + return array(); } + $query = id(new DifferentialDiffQuery()) + ->setViewer($request->getUser()) + ->needChangesets(true); + + if ($ids) { + $query->withIDs($ids); + } + + if ($revision_ids) { + $query->withRevisionIDs($revision_ids); + } + + $diffs = $query->execute(); + return mpull($diffs, 'getDiffDict', 'getID'); } diff --git a/src/applications/differential/conduit/DifferentialUpdateRevisionConduitAPIMethod.php b/src/applications/differential/conduit/DifferentialUpdateRevisionConduitAPIMethod.php index 232dbf79b4..d45dc9749e 100644 --- a/src/applications/differential/conduit/DifferentialUpdateRevisionConduitAPIMethod.php +++ b/src/applications/differential/conduit/DifferentialUpdateRevisionConduitAPIMethod.php @@ -11,6 +11,16 @@ final class DifferentialUpdateRevisionConduitAPIMethod return pht('Update a Differential revision.'); } + public function getMethodStatus() { + return self::METHOD_STATUS_FROZEN; + } + + public function getMethodStatusDescription() { + return pht( + 'This method is frozen and will eventually be deprecated. New code '. + 'should use "differential.revision.edit" instead.'); + } + protected function defineParamTypes() { return array( 'id' => 'required revisionid', diff --git a/src/applications/differential/controller/DifferentialInlineCommentEditController.php b/src/applications/differential/controller/DifferentialInlineCommentEditController.php index 42b96920eb..926158cb41 100644 --- a/src/applications/differential/controller/DifferentialInlineCommentEditController.php +++ b/src/applications/differential/controller/DifferentialInlineCommentEditController.php @@ -152,36 +152,23 @@ final class DifferentialInlineCommentEditController protected function deleteComment(PhabricatorInlineCommentInterface $inline) { $inline->openTransaction(); - $inline->setIsDeleted(1)->save(); - DifferentialDraft::deleteHasDraft( - $inline->getAuthorPHID(), - $inline->getRevisionPHID(), - $inline->getPHID()); - + $this->syncDraft(); $inline->saveTransaction(); } protected function undeleteComment( PhabricatorInlineCommentInterface $inline) { $inline->openTransaction(); - $inline->setIsDeleted(0)->save(); - DifferentialDraft::markHasDraft( - $inline->getAuthorPHID(), - $inline->getRevisionPHID(), - $inline->getPHID()); - + $this->syncDraft(); $inline->saveTransaction(); } protected function saveComment(PhabricatorInlineCommentInterface $inline) { $inline->openTransaction(); $inline->save(); - DifferentialDraft::markHasDraft( - $inline->getAuthorPHID(), - $inline->getRevisionPHID(), - $inline->getPHID()); + $this->syncDraft(); $inline->saveTransaction(); } @@ -224,4 +211,14 @@ final class DifferentialInlineCommentEditController $ids); } + private function syncDraft() { + $viewer = $this->getViewer(); + $revision = $this->loadRevision(); + + $revision->newDraftEngine() + ->setObject($revision) + ->setViewer($viewer) + ->synchronize(); + } + } diff --git a/src/applications/differential/controller/DifferentialRevisionViewController.php b/src/applications/differential/controller/DifferentialRevisionViewController.php index 6bf9aaa44f..f126345fb7 100644 --- a/src/applications/differential/controller/DifferentialRevisionViewController.php +++ b/src/applications/differential/controller/DifferentialRevisionViewController.php @@ -463,6 +463,7 @@ final class DifferentialRevisionViewController extends DifferentialController { } Javelin::initBehavior('differential-user-select'); + Javelin::initBehavior('differential-keyboard-navigation'); $view = id(new PHUITwoColumnView()) ->setHeader($header) diff --git a/src/applications/differential/editor/DifferentialRevisionEditEngine.php b/src/applications/differential/editor/DifferentialRevisionEditEngine.php index d33be9035b..5c2fef275d 100644 --- a/src/applications/differential/editor/DifferentialRevisionEditEngine.php +++ b/src/applications/differential/editor/DifferentialRevisionEditEngine.php @@ -29,7 +29,7 @@ final class DifferentialRevisionEditEngine return 'PhabricatorDifferentialApplication'; } - protected function supportsEditEngineConfiguration() { + public function isEngineConfigurable() { return false; } diff --git a/src/applications/differential/editor/DifferentialTransactionEditor.php b/src/applications/differential/editor/DifferentialTransactionEditor.php index eedcd6b7bb..85a44669c8 100644 --- a/src/applications/differential/editor/DifferentialTransactionEditor.php +++ b/src/applications/differential/editor/DifferentialTransactionEditor.php @@ -18,6 +18,14 @@ final class DifferentialTransactionEditor return pht('Differential Revisions'); } + public function getCreateObjectTitle($author, $object) { + return pht('%s created this revision.', $author); + } + + public function getCreateObjectTitleForFeed($author, $object) { + return pht('%s created %s.', $author, $object); + } + public function getDiffUpdateTransaction(array $xactions) { $type_update = DifferentialTransaction::TYPE_UPDATE; diff --git a/src/applications/differential/engine/DifferentialDiffExtractionEngine.php b/src/applications/differential/engine/DifferentialDiffExtractionEngine.php index 05e19bbe71..952c76c63f 100644 --- a/src/applications/differential/engine/DifferentialDiffExtractionEngine.php +++ b/src/applications/differential/engine/DifferentialDiffExtractionEngine.php @@ -26,6 +26,19 @@ final class DifferentialDiffExtractionEngine extends Phobject { public function newDiffFromCommit(PhabricatorRepositoryCommit $commit) { $viewer = $this->getViewer(); + // If we already have an unattached diff for this commit, just reuse it. + // This stops us from repeatedly generating diffs if something goes wrong + // later in the process. See T10968 for context. + $existing_diffs = id(new DifferentialDiffQuery()) + ->setViewer($viewer) + ->withCommitPHIDs(array($commit->getPHID())) + ->withHasRevision(false) + ->needChangesets(true) + ->execute(); + if ($existing_diffs) { + return head($existing_diffs); + } + $repository = $commit->getRepository(); $identifier = $commit->getCommitIdentifier(); $monogram = $commit->getMonogram(); @@ -73,6 +86,7 @@ final class DifferentialDiffExtractionEngine extends Phobject { $diff = DifferentialDiff::newFromRawChanges($viewer, $changes) ->setRepositoryPHID($repository->getPHID()) + ->setCommitPHID($commit->getPHID()) ->setCreationMethod('commit') ->setSourceControlSystem($repository->getVersionControlSystem()) ->setLintStatus(DifferentialLintStatus::LINT_AUTO_SKIP) diff --git a/src/applications/differential/engine/DifferentialRevisionDraftEngine.php b/src/applications/differential/engine/DifferentialRevisionDraftEngine.php new file mode 100644 index 0000000000..94669b0182 --- /dev/null +++ b/src/applications/differential/engine/DifferentialRevisionDraftEngine.php @@ -0,0 +1,17 @@ +getViewer(); + $revision = $this->getObject(); + + $inlines = DifferentialTransactionQuery::loadUnsubmittedInlineComments( + $viewer, + $revision); + + return (bool)$inlines; + } + +} diff --git a/src/applications/differential/phid/DifferentialRevisionPHIDType.php b/src/applications/differential/phid/DifferentialRevisionPHIDType.php index b22c8b05cb..5a6cf701ae 100644 --- a/src/applications/differential/phid/DifferentialRevisionPHIDType.php +++ b/src/applications/differential/phid/DifferentialRevisionPHIDType.php @@ -33,16 +33,30 @@ final class DifferentialRevisionPHIDType extends PhabricatorPHIDType { $revision = $objects[$phid]; $title = $revision->getTitle(); - $id = $revision->getID(); $status = $revision->getStatus(); + $monogram = $revision->getMonogram(); + $uri = $revision->getURI(); - $handle->setName("D{$id}"); - $handle->setURI("/D{$id}"); - $handle->setFullName("D{$id}: {$title}"); + $handle + ->setName($monogram) + ->setURI($uri) + ->setFullName("{$monogram}: {$title}"); if ($revision->isClosed()) { $handle->setStatus(PhabricatorObjectHandle::STATUS_CLOSED); } + + $status = $revision->getStatus(); + + $icon = DifferentialRevisionStatus::getRevisionStatusIcon($status); + $color = DifferentialRevisionStatus::getRevisionStatusColor($status); + $name = ArcanistDifferentialRevisionStatus::getNameForRevisionStatus( + $status); + + $handle + ->setStateIcon($icon) + ->setStateColor($color) + ->setStateName($name); } } diff --git a/src/applications/differential/query/DifferentialDiffQuery.php b/src/applications/differential/query/DifferentialDiffQuery.php index 23c016446c..f779e40c26 100644 --- a/src/applications/differential/query/DifferentialDiffQuery.php +++ b/src/applications/differential/query/DifferentialDiffQuery.php @@ -6,6 +6,8 @@ final class DifferentialDiffQuery private $ids; private $phids; private $revisionIDs; + private $commitPHIDs; + private $hasRevision; private $needChangesets = false; private $needProperties; @@ -25,6 +27,16 @@ final class DifferentialDiffQuery return $this; } + public function withCommitPHIDs(array $phids) { + $this->commitPHIDs = $phids; + return $this; + } + + public function withHasRevision($has_revision) { + $this->hasRevision = $has_revision; + return $this; + } + public function needChangesets($bool) { $this->needChangesets = $bool; return $this; @@ -108,27 +120,46 @@ final class DifferentialDiffQuery protected function buildWhereClauseParts(AphrontDatabaseConnection $conn) { $where = parent::buildWhereClauseParts($conn); - if ($this->ids) { + if ($this->ids !== null) { $where[] = qsprintf( $conn, 'id IN (%Ld)', $this->ids); } - if ($this->phids) { + if ($this->phids !== null) { $where[] = qsprintf( $conn, 'phid IN (%Ls)', $this->phids); } - if ($this->revisionIDs) { + if ($this->revisionIDs !== null) { $where[] = qsprintf( $conn, 'revisionID IN (%Ld)', $this->revisionIDs); } + if ($this->commitPHIDs !== null) { + $where[] = qsprintf( + $conn, + 'commitPHID IN (%Ls)', + $this->commitPHIDs); + } + + if ($this->hasRevision !== null) { + if ($this->hasRevision) { + $where[] = qsprintf( + $conn, + 'revisionID IS NOT NULL'); + } else { + $where[] = qsprintf( + $conn, + 'revisionID IS NULL'); + } + } + return $where; } diff --git a/src/applications/differential/query/DifferentialRevisionQuery.php b/src/applications/differential/query/DifferentialRevisionQuery.php index 6dd690d179..a4e446ad0d 100644 --- a/src/applications/differential/query/DifferentialRevisionQuery.php +++ b/src/applications/differential/query/DifferentialRevisionQuery.php @@ -473,15 +473,33 @@ final class DifferentialRevisionQuery } if ($this->needDrafts) { - $drafts = id(new DifferentialDraft())->loadAllWhere( - 'authorPHID = %s AND objectPHID IN (%Ls)', - $viewer->getPHID(), - mpull($revisions, 'getPHID')); - $drafts = mgroup($drafts, 'getObjectPHID'); - foreach ($revisions as $revision) { - $revision->attachDrafts( - $viewer, - idx($drafts, $revision->getPHID(), array())); + $viewer_phid = $viewer->getPHID(); + $draft_type = PhabricatorObjectHasDraftEdgeType::EDGECONST; + + if (!$viewer_phid) { + // Viewers without a valid PHID can never have drafts. + foreach ($revisions as $revision) { + $revision->attachHasDraft($viewer, false); + } + } else { + $edge_query = id(new PhabricatorEdgeQuery()) + ->withSourcePHIDs(mpull($revisions, 'getPHID')) + ->withEdgeTypes( + array( + $draft_type, + )) + ->withDestinationPHIDs(array($viewer_phid)); + + $edge_query->execute(); + + foreach ($revisions as $revision) { + $has_draft = (bool)$edge_query->getDestinationPHIDs( + array( + $revision->getPHID(), + )); + + $revision->attachHasDraft($viewer, $has_draft); + } } } @@ -621,12 +639,13 @@ final class DifferentialRevisionQuery } if ($this->draftAuthors) { - $differential_draft = new DifferentialDraft(); $joins[] = qsprintf( $conn_r, - 'JOIN %T has_draft ON has_draft.objectPHID = r.phid '. - 'AND has_draft.authorPHID IN (%Ls)', - $differential_draft->getTableName(), + 'JOIN %T has_draft ON has_draft.srcPHID = r.phid + AND has_draft.type = %s + AND has_draft.dstPHID IN (%Ls)', + PhabricatorEdgeConfig::TABLE_NAME_EDGE, + PhabricatorObjectHasDraftEdgeType::EDGECONST, $this->draftAuthors); } diff --git a/src/applications/differential/storage/DifferentialDiff.php b/src/applications/differential/storage/DifferentialDiff.php index 891af35982..d8c2ea9786 100644 --- a/src/applications/differential/storage/DifferentialDiff.php +++ b/src/applications/differential/storage/DifferentialDiff.php @@ -13,6 +13,7 @@ final class DifferentialDiff protected $revisionID; protected $authorPHID; protected $repositoryPHID; + protected $commitPHID; protected $sourceMachine; protected $sourcePath; @@ -62,6 +63,7 @@ final class DifferentialDiff 'branch' => 'text255?', 'bookmark' => 'text255?', 'repositoryUUID' => 'text64?', + 'commitPHID' => 'phid?', // T6203/NULLABILITY // These should be non-null; all diffs should have a creation method @@ -73,6 +75,9 @@ final class DifferentialDiff 'revisionID' => array( 'columns' => array('revisionID'), ), + 'key_commit' => array( + 'columns' => array('commitPHID'), + ), ), ) + parent::getConfiguration(); } diff --git a/src/applications/differential/storage/DifferentialDraft.php b/src/applications/differential/storage/DifferentialDraft.php index 371698937c..7dbe2f68bd 100644 --- a/src/applications/differential/storage/DifferentialDraft.php +++ b/src/applications/differential/storage/DifferentialDraft.php @@ -20,46 +20,4 @@ final class DifferentialDraft extends DifferentialDAO { ) + parent::getConfiguration(); } - public static function markHasDraft( - $author_phid, - $object_phid, - $draft_key) { - try { - id(new DifferentialDraft()) - ->setObjectPHID($object_phid) - ->setAuthorPHID($author_phid) - ->setDraftKey($draft_key) - ->save(); - } catch (AphrontDuplicateKeyQueryException $ex) { - // no worries - } - } - - public static function deleteHasDraft( - $author_phid, - $object_phid, - $draft_key) { - $draft = id(new DifferentialDraft())->loadOneWhere( - 'objectPHID = %s AND authorPHID = %s AND draftKey = %s', - $object_phid, - $author_phid, - $draft_key); - if ($draft) { - $draft->delete(); - } - } - - public static function deleteAllDrafts( - $author_phid, - $object_phid) { - - $drafts = id(new DifferentialDraft())->loadAllWhere( - 'objectPHID = %s AND authorPHID = %s', - $object_phid, - $author_phid); - foreach ($drafts as $draft) { - $draft->delete(); - } - } - } diff --git a/src/applications/differential/storage/DifferentialRevision.php b/src/applications/differential/storage/DifferentialRevision.php index 1e90b12dc4..df43043d6d 100644 --- a/src/applications/differential/storage/DifferentialRevision.php +++ b/src/applications/differential/storage/DifferentialRevision.php @@ -15,7 +15,8 @@ final class DifferentialRevision extends DifferentialDAO PhabricatorDestructibleInterface, PhabricatorProjectInterface, PhabricatorFulltextInterface, - PhabricatorConduitResultInterface { + PhabricatorConduitResultInterface, + PhabricatorDraftInterface { protected $title = ''; protected $originalTitle; @@ -488,12 +489,12 @@ final class DifferentialRevision extends DifferentialDAO return $this; } - public function getDrafts(PhabricatorUser $viewer) { - return $this->assertAttachedKey($this->drafts, $viewer->getPHID()); + public function getHasDraft(PhabricatorUser $viewer) { + return $this->assertAttachedKey($this->drafts, $viewer->getCacheFragment()); } - public function attachDrafts(PhabricatorUser $viewer, array $drafts) { - $this->drafts[$viewer->getPHID()] = $drafts; + public function attachHasDraft(PhabricatorUser $viewer, $has_draft) { + $this->drafts[$viewer->getCacheFragment()] = $has_draft; return $this; } @@ -735,4 +736,12 @@ final class DifferentialRevision extends DifferentialDAO return array(); } + +/* -( PhabricatorDraftInterface )------------------------------------------ */ + + + public function newDraftEngine() { + return new DifferentialRevisionDraftEngine(); + } + } diff --git a/src/applications/differential/view/DifferentialRevisionListView.php b/src/applications/differential/view/DifferentialRevisionListView.php index 7ab0f2d183..811e74fd65 100644 --- a/src/applications/differential/view/DifferentialRevisionListView.php +++ b/src/applications/differential/view/DifferentialRevisionListView.php @@ -92,7 +92,7 @@ final class DifferentialRevisionListView extends AphrontView { ''); } - if ($revision->getDrafts($viewer)) { + if ($revision->getHasDraft($viewer)) { $icons['draft'] = true; } diff --git a/src/applications/differential/xaction/DifferentialRevisionAbandonTransaction.php b/src/applications/differential/xaction/DifferentialRevisionAbandonTransaction.php index 627e1c9d37..2f221bd8c6 100644 --- a/src/applications/differential/xaction/DifferentialRevisionAbandonTransaction.php +++ b/src/applications/differential/xaction/DifferentialRevisionAbandonTransaction.php @@ -26,6 +26,10 @@ final class DifferentialRevisionAbandonTransaction return 500; } + public function getActionName() { + return pht('Abandoned'); + } + public function getCommandKeyword() { return 'abandon'; } diff --git a/src/applications/differential/xaction/DifferentialRevisionAcceptTransaction.php b/src/applications/differential/xaction/DifferentialRevisionAcceptTransaction.php index dcb19c4037..df4e3b4d33 100644 --- a/src/applications/differential/xaction/DifferentialRevisionAcceptTransaction.php +++ b/src/applications/differential/xaction/DifferentialRevisionAcceptTransaction.php @@ -26,6 +26,10 @@ final class DifferentialRevisionAcceptTransaction return 500; } + public function getActionName() { + return pht('Accepted'); + } + public function getCommandKeyword() { $accept_key = 'differential.enable-email-accept'; $allow_email_accept = PhabricatorEnv::getEnvConfig($accept_key); diff --git a/src/applications/differential/xaction/DifferentialRevisionActionTransaction.php b/src/applications/differential/xaction/DifferentialRevisionActionTransaction.php index dd80706b44..5507d1309d 100644 --- a/src/applications/differential/xaction/DifferentialRevisionActionTransaction.php +++ b/src/applications/differential/xaction/DifferentialRevisionActionTransaction.php @@ -35,6 +35,10 @@ abstract class DifferentialRevisionActionTransaction return 1000; } + public function getActionStrength() { + return 3; + } + public function getRevisionActionOrderVector() { return id(new PhutilSortVector()) ->addInt($this->getRevisionActionOrder()); diff --git a/src/applications/differential/xaction/DifferentialRevisionCloseTransaction.php b/src/applications/differential/xaction/DifferentialRevisionCloseTransaction.php index c2ba6c1bb8..30bfd5044f 100644 --- a/src/applications/differential/xaction/DifferentialRevisionCloseTransaction.php +++ b/src/applications/differential/xaction/DifferentialRevisionCloseTransaction.php @@ -26,6 +26,10 @@ final class DifferentialRevisionCloseTransaction return 300; } + public function getActionName() { + return pht('Closed'); + } + public function generateOldValue($object) { return $object->isClosed(); } diff --git a/src/applications/differential/xaction/DifferentialRevisionCommandeerTransaction.php b/src/applications/differential/xaction/DifferentialRevisionCommandeerTransaction.php index 57ecf5a039..4e4cd9600f 100644 --- a/src/applications/differential/xaction/DifferentialRevisionCommandeerTransaction.php +++ b/src/applications/differential/xaction/DifferentialRevisionCommandeerTransaction.php @@ -26,6 +26,10 @@ final class DifferentialRevisionCommandeerTransaction return 700; } + public function getActionName() { + return pht('Commandeered'); + } + public function getCommandKeyword() { return 'commandeer'; } diff --git a/src/applications/differential/xaction/DifferentialRevisionPlanChangesTransaction.php b/src/applications/differential/xaction/DifferentialRevisionPlanChangesTransaction.php index 35c40273f6..e7cdaa2455 100644 --- a/src/applications/differential/xaction/DifferentialRevisionPlanChangesTransaction.php +++ b/src/applications/differential/xaction/DifferentialRevisionPlanChangesTransaction.php @@ -27,6 +27,10 @@ final class DifferentialRevisionPlanChangesTransaction return 200; } + public function getActionName() { + return pht('Planned Changes'); + } + public function getCommandKeyword() { return 'planchanges'; } diff --git a/src/applications/differential/xaction/DifferentialRevisionReclaimTransaction.php b/src/applications/differential/xaction/DifferentialRevisionReclaimTransaction.php index 96a1f4415c..1f2f6a8d6c 100644 --- a/src/applications/differential/xaction/DifferentialRevisionReclaimTransaction.php +++ b/src/applications/differential/xaction/DifferentialRevisionReclaimTransaction.php @@ -26,6 +26,10 @@ final class DifferentialRevisionReclaimTransaction return 600; } + public function getActionName() { + return pht('Reclaimed'); + } + public function getCommandKeyword() { return 'reclaim'; } diff --git a/src/applications/differential/xaction/DifferentialRevisionRejectTransaction.php b/src/applications/differential/xaction/DifferentialRevisionRejectTransaction.php index 808412ff78..96812de06b 100644 --- a/src/applications/differential/xaction/DifferentialRevisionRejectTransaction.php +++ b/src/applications/differential/xaction/DifferentialRevisionRejectTransaction.php @@ -26,6 +26,10 @@ final class DifferentialRevisionRejectTransaction return 600; } + public function getActionName() { + return pht('Requested Changes'); + } + public function getCommandKeyword() { return 'request'; } diff --git a/src/applications/differential/xaction/DifferentialRevisionReopenTransaction.php b/src/applications/differential/xaction/DifferentialRevisionReopenTransaction.php index 5024ab99fa..84015b9286 100644 --- a/src/applications/differential/xaction/DifferentialRevisionReopenTransaction.php +++ b/src/applications/differential/xaction/DifferentialRevisionReopenTransaction.php @@ -26,6 +26,10 @@ final class DifferentialRevisionReopenTransaction return 400; } + public function getActionName() { + return pht('Reopened'); + } + public function generateOldValue($object) { return !$object->isClosed(); } diff --git a/src/applications/differential/xaction/DifferentialRevisionRequestReviewTransaction.php b/src/applications/differential/xaction/DifferentialRevisionRequestReviewTransaction.php index cd1c14c66e..32fcb3271e 100644 --- a/src/applications/differential/xaction/DifferentialRevisionRequestReviewTransaction.php +++ b/src/applications/differential/xaction/DifferentialRevisionRequestReviewTransaction.php @@ -22,6 +22,10 @@ final class DifferentialRevisionRequestReviewTransaction return 200; } + public function getActionName() { + return pht('Requested Review'); + } + public function generateOldValue($object) { $status_review = ArcanistDifferentialRevisionStatus::NEEDS_REVIEW; return ($object->getStatus() == $status_review); diff --git a/src/applications/differential/xaction/DifferentialRevisionResignTransaction.php b/src/applications/differential/xaction/DifferentialRevisionResignTransaction.php index 0d3045247d..5b75d7753f 100644 --- a/src/applications/differential/xaction/DifferentialRevisionResignTransaction.php +++ b/src/applications/differential/xaction/DifferentialRevisionResignTransaction.php @@ -30,6 +30,10 @@ final class DifferentialRevisionResignTransaction return 'resign'; } + public function getActionName() { + return pht('Resigned'); + } + public function getCommandAliases() { return array(); } diff --git a/src/applications/differential/xaction/DifferentialRevisionReviewersTransaction.php b/src/applications/differential/xaction/DifferentialRevisionReviewersTransaction.php index 6ecf738162..1e1b35c1c3 100644 --- a/src/applications/differential/xaction/DifferentialRevisionReviewersTransaction.php +++ b/src/applications/differential/xaction/DifferentialRevisionReviewersTransaction.php @@ -24,7 +24,13 @@ final class DifferentialRevisionReviewersTransaction // First, remove any reviewers we're getting rid of. $rem = idx($value, '-', array()); $rem = $datasource->evaluateTokens($rem); - foreach ($rem as $phid) { + foreach ($rem as $spec) { + if (!is_array($spec)) { + $phid = $spec; + } else { + $phid = $spec['phid']; + } + unset($reviewers[$phid]); } diff --git a/src/applications/diffusion/application/PhabricatorDiffusionApplication.php b/src/applications/diffusion/application/PhabricatorDiffusionApplication.php index bcbb5cdbfa..8fef05bd88 100644 --- a/src/applications/diffusion/application/PhabricatorDiffusionApplication.php +++ b/src/applications/diffusion/application/PhabricatorDiffusionApplication.php @@ -28,6 +28,10 @@ final class PhabricatorDiffusionApplication extends PhabricatorApplication { 'name' => pht('Diffusion User Guide'), 'href' => PhabricatorEnv::getDoclink('Diffusion User Guide'), ), + array( + 'name' => pht('Audit User Guide'), + 'href' => PhabricatorEnv::getDoclink('Audit User Guide'), + ), ); } @@ -63,8 +67,6 @@ final class PhabricatorDiffusionApplication extends PhabricatorApplication { => 'DiffusionCommitBranchesController', 'commit/(?P[a-z0-9]+)/tags/' => 'DiffusionCommitTagsController', - 'commit/(?P[a-z0-9]+)/edit/' - => 'DiffusionCommitEditController', 'compare/' => 'DiffusionCompareController', 'manage/(?:(?P[^/]+)/)?' => 'DiffusionRepositoryManagePanelsController', @@ -131,6 +133,13 @@ final class PhabricatorDiffusionApplication extends PhabricatorApplication { 'symbol/(?P[^/]+)/' => 'DiffusionSymbolController', 'external/' => 'DiffusionExternalController', 'lint/' => 'DiffusionLintController', + + 'commit/' => array( + $this->getQueryRoutePattern() => + 'DiffusionCommitListController', + $this->getEditRoutePattern('edit/') => + 'DiffusionCommitEditController', + ), ), ); } diff --git a/src/applications/diffusion/conduit/DiffusionCommitEditConduitAPIMethod.php b/src/applications/diffusion/conduit/DiffusionCommitEditConduitAPIMethod.php new file mode 100644 index 0000000000..442784a366 --- /dev/null +++ b/src/applications/diffusion/conduit/DiffusionCommitEditConduitAPIMethod.php @@ -0,0 +1,20 @@ +'; } diff --git a/src/applications/diffusion/controller/DiffusionCommitController.php b/src/applications/diffusion/controller/DiffusionCommitController.php index 3ec28d31a5..97cb219b51 100644 --- a/src/applications/diffusion/controller/DiffusionCommitController.php +++ b/src/applications/diffusion/controller/DiffusionCommitController.php @@ -369,7 +369,9 @@ final class DiffusionCommitController extends DiffusionController { } - $add_comment = $this->renderAddCommentPanel($commit, $audit_requests); + $add_comment = $this->renderAddCommentPanel( + $commit, + $timeline); $filetree_on = $viewer->compareUserSetting( PhabricatorShowFiletreeSetting::SETTINGKEY, @@ -717,150 +719,24 @@ final class DiffusionCommitController extends DiffusionController { private function renderAddCommentPanel( PhabricatorRepositoryCommit $commit, - array $audit_requests) { - assert_instances_of($audit_requests, 'PhabricatorRepositoryAuditRequest'); + $timeline) { $request = $this->getRequest(); $viewer = $request->getUser(); - if (!$viewer->isLoggedIn()) { - return id(new PhabricatorApplicationTransactionCommentView()) - ->setUser($viewer) - ->setRequestURI($request->getRequestURI()); - } - - $is_serious = PhabricatorEnv::getEnvConfig('phabricator.serious-business'); - - $pane_id = celerity_generate_unique_node_id(); - Javelin::initBehavior( - 'differential-keyboard-navigation', - array( - 'haunt' => $pane_id, - )); - - $draft = id(new PhabricatorDraft())->loadOneWhere( - 'authorPHID = %s AND draftKey = %s', - $viewer->getPHID(), - 'diffusion-audit-'.$commit->getID()); - if ($draft) { - $draft = $draft->getDraft(); - } else { - $draft = null; - } - - $actions = $this->getAuditActions($commit, $audit_requests); - - $mailable_source = new PhabricatorMetaMTAMailableDatasource(); - $auditor_source = new DiffusionAuditorDatasource(); - - $form = id(new AphrontFormView()) - ->setUser($viewer) - ->setAction('/audit/addcomment/') - ->addHiddenInput('commit', $commit->getPHID()) - ->appendChild( - id(new AphrontFormSelectControl()) - ->setLabel(pht('Action')) - ->setName('action') - ->setID('audit-action') - ->setOptions($actions)) - ->appendControl( - id(new AphrontFormTokenizerControl()) - ->setLabel(pht('Add Auditors')) - ->setName('auditors') - ->setControlID('add-auditors') - ->setControlStyle('display: none') - ->setID('add-auditors-tokenizer') - ->setDisableBehavior(true) - ->setDatasource($auditor_source)) - ->appendControl( - id(new AphrontFormTokenizerControl()) - ->setLabel(pht('Add CCs')) - ->setName('ccs') - ->setControlID('add-ccs') - ->setControlStyle('display: none') - ->setID('add-ccs-tokenizer') - ->setDisableBehavior(true) - ->setDatasource($mailable_source)) - ->appendChild( - id(new PhabricatorRemarkupControl()) - ->setLabel(pht('Comments')) - ->setName('content') - ->setValue($draft) - ->setID('audit-content') - ->setUser($viewer)) - ->appendChild( - id(new AphrontFormSubmitControl()) - ->setValue(pht('Submit'))); - - $header = new PHUIHeaderView(); - $header->setHeader( - $is_serious ? pht('Audit Commit') : pht('Creative Accounting')); - - Javelin::initBehavior( - 'differential-add-reviewers-and-ccs', - array( - 'dynamic' => array( - 'add-auditors-tokenizer' => array( - 'actions' => array('add_auditors' => 1), - 'src' => $auditor_source->getDatasourceURI(), - 'row' => 'add-auditors', - 'placeholder' => $auditor_source->getPlaceholderText(), - ), - 'add-ccs-tokenizer' => array( - 'actions' => array('add_ccs' => 1), - 'src' => $mailable_source->getDatasourceURI(), - 'row' => 'add-ccs', - 'placeholder' => $mailable_source->getPlaceholderText(), - ), - ), - 'select' => 'audit-action', - )); - - Javelin::initBehavior('differential-feedback-preview', array( - 'uri' => '/audit/preview/'.$commit->getID().'/', - 'preview' => 'audit-preview', - 'content' => 'audit-content', - 'action' => 'audit-action', - 'previewTokenizers' => array( - 'auditors' => 'add-auditors-tokenizer', - 'ccs' => 'add-ccs-tokenizer', - ), - 'inline' => 'inline-comment-preview', - 'inlineuri' => '/diffusion/inline/preview/'.$commit->getPHID().'/', - )); - - $loading = phutil_tag_div( - 'aphront-panel-preview-loading-text', - pht('Loading preview...')); - - $preview_panel = phutil_tag_div( - 'aphront-panel-preview aphront-panel-flush', - array( - phutil_tag('div', array('id' => 'audit-preview'), $loading), - phutil_tag('div', array('id' => 'inline-comment-preview')), - )); + Javelin::initBehavior('differential-keyboard-navigation'); // TODO: This is pretty awkward, unify the CSS between Diffusion and // Differential better. require_celerity_resource('differential-core-view-css'); - $anchor = id(new PhabricatorAnchorView()) - ->setAnchorName('comment') - ->setNavigationMarker(true) - ->render(); + $comment_view = id(new DiffusionCommitEditEngine()) + ->setViewer($viewer) + ->buildEditEngineCommentView($commit); - $comment_box = id(new PHUIObjectBoxView()) - ->setHeader($header) - ->appendChild($form); + $comment_view->setTransactionTimeline($timeline); - return phutil_tag( - 'div', - array( - 'id' => $pane_id, - ), - phutil_tag_div( - 'differential-add-comment-panel', - array($anchor, $comment_box, $preview_panel))); + return $comment_view; } /** @@ -997,12 +873,12 @@ final class DiffusionCommitController extends DiffusionController { $commit, PhabricatorPolicyCapability::CAN_EDIT); - $identifier = $commit->getCommitIdentifier(); - $uri = $repository->getPathURI("commit/{$identifier}/edit/"); + $id = $commit->getID(); + $edit_uri = $this->getApplicationURI("/commit/edit/{$id}/"); $action = id(new PhabricatorActionView()) ->setName(pht('Edit Commit')) - ->setHref($uri) + ->setHref($edit_uri) ->setIcon('fa-pencil') ->setDisabled(!$can_edit) ->setWorkflow(!$can_edit); diff --git a/src/applications/diffusion/controller/DiffusionCommitEditController.php b/src/applications/diffusion/controller/DiffusionCommitEditController.php index 895eda4c6d..cc1d1e1c59 100644 --- a/src/applications/diffusion/controller/DiffusionCommitEditController.php +++ b/src/applications/diffusion/controller/DiffusionCommitEditController.php @@ -1,120 +1,12 @@ loadDiffusionContext(); - if ($response) { - return $response; - } - - $viewer = $this->getViewer(); - $drequest = $this->getDiffusionRequest(); - $repository = $drequest->getRepository(); - $commit = $drequest->loadCommit(); - - if (!$commit) { - return new Aphront404Response(); - } - - $data = $commit->loadCommitData(); - $page_title = pht('Edit Diffusion Commit'); - - $commit_phid = $commit->getPHID(); - $edge_type = PhabricatorProjectObjectHasProjectEdgeType::EDGECONST; - $current_proj_phids = PhabricatorEdgeQuery::loadDestinationPHIDs( - $commit_phid, - $edge_type); - - if ($request->isFormPost()) { - $xactions = array(); - $proj_phids = $request->getArr('projects'); - $xactions[] = id(new PhabricatorAuditTransaction()) - ->setTransactionType(PhabricatorTransactions::TYPE_EDGE) - ->setMetadataValue('edge:type', $edge_type) - ->setNewValue(array('=' => array_fuse($proj_phids))); - - $editor = id(new PhabricatorAuditEditor()) - ->setActor($viewer) - ->setContinueOnNoEffect(true) - ->setContentSourceFromRequest($request); - - $editor->applyTransactions($commit, $xactions); - - return id(new AphrontRedirectResponse()) - ->setURI($commit->getURI()); - } - - $tokenizer_id = celerity_generate_unique_node_id(); - $form = id(new AphrontFormView()) - ->setUser($viewer) - ->setAction($request->getRequestURI()->getPath()) - ->appendControl( - id(new AphrontFormTokenizerControl()) - ->setLabel(pht('Projects')) - ->setName('projects') - ->setValue($current_proj_phids) - ->setID($tokenizer_id) - ->setDatasource(new PhabricatorProjectDatasource())); - - $reason = $data->getCommitDetail('autocloseReason', false); - $reason = PhabricatorRepository::BECAUSE_AUTOCLOSE_FORCED; - if ($reason !== false) { - switch ($reason) { - case PhabricatorRepository::BECAUSE_REPOSITORY_IMPORTING: - $desc = pht('No, Repository Importing'); - break; - case PhabricatorRepository::BECAUSE_AUTOCLOSE_DISABLED: - $desc = pht('No, Autoclose Disabled'); - break; - case PhabricatorRepository::BECAUSE_NOT_ON_AUTOCLOSE_BRANCH: - $desc = pht('No, Not On Autoclose Branch'); - break; - case PhabricatorRepository::BECAUSE_AUTOCLOSE_FORCED: - $desc = pht('Yes, Forced Via bin/repository CLI Tool.'); - break; - case null: - $desc = pht('Yes'); - break; - default: - $desc = pht('Unknown'); - break; - } - - $doc_href = PhabricatorEnv::getDoclink('Diffusion User Guide: Autoclose'); - $doc_link = phutil_tag( - 'a', - array( - 'href' => $doc_href, - 'target' => '_blank', - ), - pht('Learn More')); - - $form->appendChild( - id(new AphrontFormMarkupControl()) - ->setLabel(pht('Autoclose?')) - ->setValue(array($desc, " \xC2\xB7 ", $doc_link))); - } - - $form->appendControl( - id(new AphrontFormSubmitControl()) - ->setValue(pht('Save')) - ->addCancelButton($commit->getURI())); - - $crumbs = $this->buildCrumbs( - array( - 'commit' => true, - )); - $crumbs->addTextCrumb(pht('Edit')); - - $form_box = id(new PHUIObjectBoxView()) - ->setHeaderText($page_title) - ->setForm($form); - - return $this->newPage() - ->setTitle($page_title) - ->setCrumbs($crumbs) - ->appendChild($form_box); + return id(new DiffusionCommitEditEngine()) + ->setController($this) + ->buildResponse(); } } diff --git a/src/applications/diffusion/controller/DiffusionCommitListController.php b/src/applications/diffusion/controller/DiffusionCommitListController.php new file mode 100644 index 0000000000..b8c008c4d6 --- /dev/null +++ b/src/applications/diffusion/controller/DiffusionCommitListController.php @@ -0,0 +1,26 @@ +setController($this) + ->buildResponse(); + } + + protected function buildApplicationCrumbs() { + $crumbs = parent::buildApplicationCrumbs(); + + $crumbs->addTextCrumb( + pht('Commits'), + $this->getApplicationURI('commit/')); + + return $crumbs; + } + +} diff --git a/src/applications/diffusion/controller/DiffusionRepositoryListController.php b/src/applications/diffusion/controller/DiffusionRepositoryListController.php index eebe0bf91f..d886209c89 100644 --- a/src/applications/diffusion/controller/DiffusionRepositoryListController.php +++ b/src/applications/diffusion/controller/DiffusionRepositoryListController.php @@ -7,8 +7,19 @@ final class DiffusionRepositoryListController extends DiffusionController { } public function handleRequest(AphrontRequest $request) { + $items = array(); + + $items[] = id(new PHUIListItemView()) + ->setType(PHUIListItemView::TYPE_LABEL) + ->setName(pht('Commits')); + + $items[] = id(new PHUIListItemView()) + ->setName('Browse Commits') + ->setHref($this->getApplicationURI('commit/')); + return id(new PhabricatorRepositorySearchEngine()) ->setController($this) + ->setNavigationItems($items) ->buildResponse(); } diff --git a/src/applications/diffusion/editor/DiffusionCommitEditEngine.php b/src/applications/diffusion/editor/DiffusionCommitEditEngine.php new file mode 100644 index 0000000000..28614579a0 --- /dev/null +++ b/src/applications/diffusion/editor/DiffusionCommitEditEngine.php @@ -0,0 +1,233 @@ +attachRepository($repository) + ->attachCommitData($data) + ->attachAudits(array()); + } + + protected function newObjectQuery() { + return id(new DiffusionCommitQuery()) + ->needCommitData(true) + ->needAuditRequests(true); + } + + protected function getEditorURI() { + return $this->getApplication()->getApplicationURI('commit/edit/'); + } + + protected function newCommentActionGroups() { + return array( + id(new PhabricatorEditEngineCommentActionGroup()) + ->setKey(self::ACTIONGROUP_AUDIT) + ->setLabel(pht('Audit Actions')), + id(new PhabricatorEditEngineCommentActionGroup()) + ->setKey(self::ACTIONGROUP_COMMIT) + ->setLabel(pht('Commit Actions')), + ); + } + + protected function getObjectCreateTitleText($object) { + return pht('Create Commit'); + } + + protected function getObjectCreateShortText() { + return pht('Create Commit'); + } + + protected function getObjectEditTitleText($object) { + return pht('Edit Commit: %s', $object->getDisplayName()); + } + + protected function getObjectEditShortText($object) { + return $object->getDisplayName(); + } + + protected function getObjectName() { + return pht('Commit'); + } + + protected function getObjectViewURI($object) { + return $object->getURI(); + } + + protected function getCreateNewObjectPolicy() { + return PhabricatorPolicies::POLICY_NOONE; + } + + protected function buildCustomEditFields($object) { + $viewer = $this->getViewer(); + $data = $object->getCommitData(); + + $fields = array(); + + $fields[] = id(new PhabricatorDatasourceEditField()) + ->setKey('auditors') + ->setLabel(pht('Auditors')) + ->setDatasource(new DiffusionAuditorDatasource()) + ->setUseEdgeTransactions(true) + ->setTransactionType( + DiffusionCommitAuditorsTransaction::TRANSACTIONTYPE) + ->setCommentActionLabel(pht('Change Auditors')) + ->setDescription(pht('Auditors for this commit.')) + ->setConduitDescription(pht('Change the auditors for this commit.')) + ->setConduitTypeDescription(pht('New auditors.')) + ->setValue($object->getAuditorPHIDsForEdit()); + + $reason = $data->getCommitDetail('autocloseReason', false); + $reason = PhabricatorRepository::BECAUSE_AUTOCLOSE_FORCED; + if ($reason !== false) { + switch ($reason) { + case PhabricatorRepository::BECAUSE_REPOSITORY_IMPORTING: + $desc = pht('No, Repository Importing'); + break; + case PhabricatorRepository::BECAUSE_AUTOCLOSE_DISABLED: + $desc = pht('No, Autoclose Disabled'); + break; + case PhabricatorRepository::BECAUSE_NOT_ON_AUTOCLOSE_BRANCH: + $desc = pht('No, Not On Autoclose Branch'); + break; + case PhabricatorRepository::BECAUSE_AUTOCLOSE_FORCED: + $desc = pht('Yes, Forced Via bin/repository CLI Tool.'); + break; + case null: + $desc = pht('Yes'); + break; + default: + $desc = pht('Unknown'); + break; + } + + $doc_href = PhabricatorEnv::getDoclink('Diffusion User Guide: Autoclose'); + $doc_link = phutil_tag( + 'a', + array( + 'href' => $doc_href, + 'target' => '_blank', + ), + pht('Learn More')); + + $fields[] = id(new PhabricatorStaticEditField()) + ->setLabel(pht('Autoclose?')) + ->setValue(array($desc, " \xC2\xB7 ", $doc_link)); + } + + $actions = DiffusionCommitActionTransaction::loadAllActions(); + $actions = msortv($actions, 'getCommitActionOrderVector'); + + foreach ($actions as $key => $action) { + $fields[] = $action->newEditField($object, $viewer); + } + + return $fields; + } + + protected function newAutomaticCommentTransactions($object) { + $viewer = $this->getViewer(); + $xactions = array(); + + $inlines = PhabricatorAuditInlineComment::loadDraftComments( + $viewer, + $object->getPHID(), + $raw = true); + $inlines = msort($inlines, 'getID'); + + foreach ($inlines as $inline) { + $xactions[] = $object->getApplicationTransactionTemplate() + ->setTransactionType(PhabricatorAuditActionConstants::INLINE) + ->attachComment($inline); + } + + $viewer_phid = $viewer->getPHID(); + $viewer_is_author = ($object->getAuthorPHID() == $viewer_phid); + if ($viewer_is_author) { + $state_map = PhabricatorTransactions::getInlineStateMap(); + + $inlines = id(new DiffusionDiffInlineCommentQuery()) + ->setViewer($viewer) + ->withCommitPHIDs(array($object->getPHID())) + ->withFixedStates(array_keys($state_map)) + ->execute(); + if ($inlines) { + $old_value = mpull($inlines, 'getFixedState', 'getPHID'); + $new_value = array(); + foreach ($old_value as $key => $state) { + $new_value[$key] = $state_map[$state]; + } + + $xactions[] = $object->getApplicationTransactionTemplate() + ->setTransactionType(PhabricatorTransactions::TYPE_INLINESTATE) + ->setIgnoreOnNoEffect(true) + ->setOldValue($old_value) + ->setNewValue($new_value); + } + } + + return $xactions; + } + + protected function newCommentPreviewContent($object, array $xactions) { + $viewer = $this->getViewer(); + $type_inline = PhabricatorAuditActionConstants::INLINE; + + $inlines = array(); + foreach ($xactions as $xaction) { + if ($xaction->getTransactionType() === $type_inline) { + $inlines[] = $xaction->getComment(); + } + } + + $content = array(); + + if ($inlines) { + $inline_preview = id(new PHUIDiffInlineCommentPreviewListView()) + ->setViewer($viewer) + ->setInlineComments($inlines); + + $content[] = phutil_tag( + 'div', + array( + 'id' => 'inline-comment-preview', + ), + $inline_preview); + } + + return $content; + } +} diff --git a/src/applications/diffusion/herald/HeraldCommitAdapter.php b/src/applications/diffusion/herald/HeraldCommitAdapter.php index 7b4d26b77c..9d63b9db3d 100644 --- a/src/applications/diffusion/herald/HeraldCommitAdapter.php +++ b/src/applications/diffusion/herald/HeraldCommitAdapter.php @@ -104,12 +104,26 @@ final class HeraldCommitAdapter public function getTriggerObjectPHIDs() { $project_type = PhabricatorProjectObjectHasProjectEdgeType::EDGECONST; - return array_merge( - array( - $this->getRepository()->getPHID(), - $this->getPHID(), - ), - $this->loadEdgePHIDs($project_type)); + $repository_phid = $this->getRepository()->getPHID(); + $commit_phid = $this->getObject()->getPHID(); + + $phids = array(); + $phids[] = $commit_phid; + $phids[] = $repository_phid; + + // NOTE: This is projects for the repository, not for the commit. When + // Herald evalutes, commits normally can not have any project tags yet. + $repository_project_phids = PhabricatorEdgeQuery::loadDestinationPHIDs( + $repository_phid, + $project_type); + foreach ($repository_project_phids as $phid) { + $phids[] = $phid; + } + + $phids = array_unique($phids); + $phids = array_values($phids); + + return $phids; } public function explainValidTriggerObjects() { diff --git a/src/applications/diffusion/protocol/DiffusionRepositoryClusterEngine.php b/src/applications/diffusion/protocol/DiffusionRepositoryClusterEngine.php index 8937c1f205..4bb9ca6255 100644 --- a/src/applications/diffusion/protocol/DiffusionRepositoryClusterEngine.php +++ b/src/applications/diffusion/protocol/DiffusionRepositoryClusterEngine.php @@ -251,9 +251,8 @@ final class DiffusionRepositoryClusterEngine extends Phobject { pht( 'Repository "%s" exists on more than one device, but no device '. 'has any repository version information. Phabricator can not '. - 'guess which copy of the existing data is authoritative. Remove '. - 'all but one device from service to mark the remaining device '. - 'as the authority.', + 'guess which copy of the existing data is authoritative. Promote '. + 'a device or see "Ambigous Leaders" in the documentation.', $repository->getDisplayName())); } diff --git a/src/applications/diffusion/query/DiffusionCommitQuery.php b/src/applications/diffusion/query/DiffusionCommitQuery.php index 4aeca4c38e..a24f8038e0 100644 --- a/src/applications/diffusion/query/DiffusionCommitQuery.php +++ b/src/applications/diffusion/query/DiffusionCommitQuery.php @@ -11,22 +11,16 @@ final class DiffusionCommitQuery private $repositoryIDs; private $repositoryPHIDs; private $identifierMap; + private $responsiblePHIDs; + private $statuses; private $needAuditRequests; private $auditIDs; private $auditorPHIDs; - private $needsAuditByPHIDs; - private $auditStatus; private $epochMin; private $epochMax; private $importing; - const AUDIT_STATUS_ANY = 'audit-status-any'; - const AUDIT_STATUS_OPEN = 'audit-status-open'; - const AUDIT_STATUS_CONCERN = 'audit-status-concern'; - const AUDIT_STATUS_ACCEPTED = 'audit-status-accepted'; - const AUDIT_STATUS_PARTIAL = 'audit-status-partial'; - private $needCommitData; public function withIDs(array $ids) { @@ -119,14 +113,22 @@ final class DiffusionCommitQuery return $this; } - public function withNeedsAuditByPHIDs(array $needs_phids) { - $this->needsAuditByPHIDs = $needs_phids; + public function withResponsiblePHIDs(array $responsible_phids) { + $this->responsiblePHIDs = $responsible_phids; + return $this; + } + + public function withStatuses(array $statuses) { + $this->statuses = $statuses; return $this; } public function withAuditStatus($status) { - $this->auditStatus = $status; - return $this; + // TODO: Replace callers with `withStatuses()`. + return $this->withStatuses( + array( + $status, + )); } public function withEpochRange($min, $max) { @@ -251,10 +253,7 @@ final class DiffusionCommitQuery } } - // TODO: This should just be `needAuditRequests`, not `shouldJoinAudits()`, - // but leave that for a future diff. - - if ($this->needAuditRequests || $this->shouldJoinAudits()) { + if ($this->needAuditRequests) { $requests = id(new PhabricatorRepositoryAuditRequest())->loadAllWhere( 'commitPHID IN (%Ls)', mpull($commits, 'getPHID')); @@ -459,67 +458,30 @@ final class DiffusionCommitQuery if ($this->auditIDs !== null) { $where[] = qsprintf( $conn, - 'audit.id IN (%Ld)', + 'auditor.id IN (%Ld)', $this->auditIDs); } if ($this->auditorPHIDs !== null) { $where[] = qsprintf( $conn, - 'audit.auditorPHID IN (%Ls)', + 'auditor.auditorPHID IN (%Ls)', $this->auditorPHIDs); } - if ($this->needsAuditByPHIDs !== null) { + if ($this->responsiblePHIDs !== null) { $where[] = qsprintf( $conn, - 'needs.auditorPHID IN (%Ls)', - $this->needsAuditByPHIDs); + '(audit.auditorPHID IN (%Ls) OR commit.authorPHID IN (%Ls))', + $this->responsiblePHIDs, + $this->responsiblePHIDs); } - $status = $this->auditStatus; - if ($status !== null) { - switch ($status) { - case self::AUDIT_STATUS_PARTIAL: - $where[] = qsprintf( - $conn, - 'commit.auditStatus = %d', - PhabricatorAuditCommitStatusConstants::PARTIALLY_AUDITED); - break; - case self::AUDIT_STATUS_ACCEPTED: - $where[] = qsprintf( - $conn, - 'commit.auditStatus = %d', - PhabricatorAuditCommitStatusConstants::FULLY_AUDITED); - break; - case self::AUDIT_STATUS_CONCERN: - $where[] = qsprintf( - $conn, - 'status.auditStatus = %s', - PhabricatorAuditStatusConstants::CONCERNED); - break; - case self::AUDIT_STATUS_OPEN: - $where[] = qsprintf( - $conn, - 'status.auditStatus in (%Ls)', - PhabricatorAuditStatusConstants::getOpenStatusConstants()); - break; - case self::AUDIT_STATUS_ANY: - break; - default: - $valid = array( - self::AUDIT_STATUS_ANY, - self::AUDIT_STATUS_OPEN, - self::AUDIT_STATUS_CONCERN, - self::AUDIT_STATUS_ACCEPTED, - self::AUDIT_STATUS_PARTIAL, - ); - throw new Exception( - pht( - "Unknown audit status '%s'! Valid statuses are: %s.", - $status, - implode(', ', $valid))); - } + if ($this->statuses !== null) { + $where[] = qsprintf( + $conn, + 'commit.auditStatus IN (%Ls)', + $this->statuses); } return $where; @@ -535,61 +497,41 @@ final class DiffusionCommitQuery } } - private function shouldJoinStatus() { - return $this->auditStatus; + private function shouldJoinAuditor() { + return ($this->auditIDs || $this->auditorPHIDs); } - private function shouldJoinAudits() { - return $this->auditIDs || $this->auditorPHIDs; - } - - private function shouldJoinNeeds() { - return $this->needsAuditByPHIDs; + private function shouldJoinAudit() { + return (bool)$this->responsiblePHIDs; } protected function buildJoinClauseParts(AphrontDatabaseConnection $conn) { $join = parent::buildJoinClauseParts($conn); $audit_request = new PhabricatorRepositoryAuditRequest(); - if ($this->shouldJoinStatus()) { + if ($this->shouldJoinAuditor()) { $join[] = qsprintf( $conn, - 'LEFT JOIN %T status ON commit.phid = status.commitPHID', + 'JOIN %T auditor ON commit.phid = auditor.commitPHID', $audit_request->getTableName()); } - if ($this->shouldJoinAudits()) { + if ($this->shouldJoinAudit()) { $join[] = qsprintf( $conn, - 'JOIN %T audit ON commit.phid = audit.commitPHID', + 'LEFT JOIN %T audit ON commit.phid = audit.commitPHID', $audit_request->getTableName()); } - if ($this->shouldJoinNeeds()) { - $join[] = qsprintf( - $conn, - 'JOIN %T needs ON commit.phid = needs.commitPHID - AND needs.auditStatus IN (%Ls)', - $audit_request->getTableName(), - array( - PhabricatorAuditStatusConstants::AUDIT_REQUESTED, - PhabricatorAuditStatusConstants::AUDIT_REQUIRED, - )); - } - return $join; } protected function shouldGroupQueryResultRows() { - if ($this->shouldJoinStatus()) { + if ($this->shouldJoinAuditor()) { return true; } - if ($this->shouldJoinAudits()) { - return true; - } - - if ($this->shouldJoinNeeds()) { + if ($this->shouldJoinAudit()) { return true; } diff --git a/src/applications/diffusion/query/DiffusionCommitRequiredActionResultBucket.php b/src/applications/diffusion/query/DiffusionCommitRequiredActionResultBucket.php new file mode 100644 index 0000000000..ad80cddf39 --- /dev/null +++ b/src/applications/diffusion/query/DiffusionCommitRequiredActionResultBucket.php @@ -0,0 +1,151 @@ +objects = $objects; + + $phids = $query->getEvaluatedParameter('responsiblePHIDs', array()); + if (!$phids) { + throw new Exception( + pht( + 'You can not bucket results by required action without '. + 'specifying "Responsible Users".')); + } + $phids = array_fuse($phids); + + $groups = array(); + + $groups[] = $this->newGroup() + ->setName(pht('Needs Attention')) + ->setNoDataString(pht('None of your commits have active concerns.')) + ->setObjects($this->filterConcernRaised($phids)); + + $groups[] = $this->newGroup() + ->setName(pht('Ready to Audit')) + ->setNoDataString(pht('No commits are waiting for you to audit them.')) + ->setObjects($this->filterShouldAudit($phids)); + + $groups[] = $this->newGroup() + ->setName(pht('Waiting on Authors')) + ->setNoDataString(pht('None of your audits are waiting on authors.')) + ->setObjects($this->filterWaitingOnAuthors($phids)); + + $groups[] = $this->newGroup() + ->setName(pht('Waiting on Auditors')) + ->setNoDataString(pht('None of your commits are waiting on audit.')) + ->setObjects($this->filterWaitingOnAuditors($phids)); + + // Because you can apply these buckets to queries which include revisions + // that have been closed, add an "Other" bucket if we still have stuff + // that didn't get filtered into any of the previous buckets. + if ($this->objects) { + $groups[] = $this->newGroup() + ->setName(pht('Other Commits')) + ->setObjects($this->objects); + } + + return $groups; + } + + private function filterConcernRaised(array $phids) { + $results = array(); + $objects = $this->objects; + + $status_concern = PhabricatorAuditCommitStatusConstants::CONCERN_RAISED; + + foreach ($objects as $key => $object) { + if (empty($phids[$object->getAuthorPHID()])) { + continue; + } + + if ($object->getAuditStatus() != $status_concern) { + continue; + } + + $results[$key] = $object; + unset($this->objects[$key]); + } + + return $results; + } + + private function filterShouldAudit(array $phids) { + $results = array(); + $objects = $this->objects; + + $should_audit = array( + PhabricatorAuditStatusConstants::AUDIT_REQUIRED, + PhabricatorAuditStatusConstants::AUDIT_REQUESTED, + ); + $should_audit = array_fuse($should_audit); + + foreach ($objects as $key => $object) { + if (!$this->hasAuditorsWithStatus($object, $phids, $should_audit)) { + continue; + } + + $results[$key] = $object; + unset($this->objects[$key]); + } + + return $results; + } + + private function filterWaitingOnAuthors(array $phids) { + $results = array(); + $objects = $this->objects; + + $status_concern = PhabricatorAuditCommitStatusConstants::CONCERN_RAISED; + + foreach ($objects as $key => $object) { + if ($object->getAuditStatus() != $status_concern) { + continue; + } + + if (isset($phids[$object->getAuthorPHID()])) { + continue; + } + + $results[$key] = $object; + unset($this->objects[$key]); + } + + return $results; + } + + private function filterWaitingOnAuditors(array $phids) { + $results = array(); + $objects = $this->objects; + + $status_waiting = array( + PhabricatorAuditCommitStatusConstants::NEEDS_AUDIT, + PhabricatorAuditCommitStatusConstants::PARTIALLY_AUDITED, + ); + $status_waiting = array_fuse($status_waiting); + + foreach ($objects as $key => $object) { + if (empty($status_waiting[$object->getAuditStatus()])) { + continue; + } + + $results[$key] = $object; + unset($this->objects[$key]); + } + + return $results; + } + +} diff --git a/src/applications/diffusion/query/DiffusionCommitResultBucket.php b/src/applications/diffusion/query/DiffusionCommitResultBucket.php new file mode 100644 index 0000000000..28d80aa56c --- /dev/null +++ b/src/applications/diffusion/query/DiffusionCommitResultBucket.php @@ -0,0 +1,33 @@ +setAncestorClass(__CLASS__) + ->setUniqueMethod('getResultBucketKey') + ->execute(); + } + + protected function hasAuditorsWithStatus( + PhabricatorRepositoryCommit $commit, + array $phids, + array $statuses) { + + foreach ($commit->getAudits() as $audit) { + if (!isset($phids[$audit->getAuditorPHID()])) { + continue; + } + + if (!isset($statuses[$audit->getAuditStatus()])) { + continue; + } + + return true; + } + + return false; + } + +} diff --git a/src/applications/diffusion/view/DiffusionCloneURIView.php b/src/applications/diffusion/view/DiffusionCloneURIView.php index fc861ed183..c9dae57563 100644 --- a/src/applications/diffusion/view/DiffusionCloneURIView.php +++ b/src/applications/diffusion/view/DiffusionCloneURIView.php @@ -35,6 +35,8 @@ final class DiffusionCloneURIView extends AphrontView { } public function render() { + $viewer = $this->getViewer(); + require_celerity_resource('diffusion-icons-css'); Javelin::initBehavior('select-content'); @@ -87,12 +89,18 @@ final class DiffusionCloneURIView extends AphrontView { case PhabricatorRepositoryURI::IO_READWRITE: switch ($uri->getBuiltinProtocol()) { case PhabricatorRepositoryURI::BUILTIN_PROTOCOL_SSH: - $auth_uri = '/settings/panel/ssh/'; + $auth_uri = id(new PhabricatorSSHKeysSettingsPanel()) + ->setViewer($viewer) + ->setUser($viewer) + ->getPanelURI(); $auth_tip = pht('Manage SSH Keys'); $auth_disabled = false; break; default: - $auth_uri = '/settings/panel/vcspassword'; + $auth_uri = id(new DiffusionSetPasswordSettingsPanel()) + ->setViewer($viewer) + ->setUser($viewer) + ->getPanelURI(); $auth_tip = pht('Manage Password'); $auth_disabled = false; break; diff --git a/src/applications/diffusion/xaction/DiffusionCommitAcceptTransaction.php b/src/applications/diffusion/xaction/DiffusionCommitAcceptTransaction.php new file mode 100644 index 0000000000..76c2de609e --- /dev/null +++ b/src/applications/diffusion/xaction/DiffusionCommitAcceptTransaction.php @@ -0,0 +1,78 @@ +getActor(); + return $this->isViewerAcceptingAuditor($object, $actor); + } + + public function applyExternalEffects($object, $value) { + $status = PhabricatorAuditStatusConstants::ACCEPTED; + $actor = $this->getActor(); + $this->applyAuditorEffect($object, $actor, $value, $status); + } + + protected function validateAction($object, PhabricatorUser $viewer) { + $config_key = 'audit.can-author-close-audit'; + if (!PhabricatorEnv::getEnvConfig($config_key)) { + if ($this->isViewerCommitAuthor($object, $viewer)) { + throw new Exception( + pht( + 'You can not accept this commit because you are the commit '. + 'author. You can only accept commits you did not author. You can '. + 'change this behavior by adjusting the "%s" setting in Config.', + $config_key)); + } + } + + if ($this->isViewerAcceptingAuditor($object, $viewer)) { + throw new Exception( + pht( + 'You can not accept this commit because you have already '. + 'accepted it.')); + } + } + + public function getTitle() { + return pht( + '%s accepted this commit.', + $this->renderAuthor()); + } + + public function getTitleForFeed() { + return pht( + '%s accepted %s.', + $this->renderAuthor(), + $this->renderObject()); + } + +} diff --git a/src/applications/diffusion/xaction/DiffusionCommitActionTransaction.php b/src/applications/diffusion/xaction/DiffusionCommitActionTransaction.php new file mode 100644 index 0000000000..4756f914e3 --- /dev/null +++ b/src/applications/diffusion/xaction/DiffusionCommitActionTransaction.php @@ -0,0 +1,118 @@ +getPhobjectClassConstant('ACTIONKEY', 32); + } + + public function isActionAvailable($object, PhabricatorUser $viewer) { + try { + $this->validateAction($object, $viewer); + return true; + } catch (Exception $ex) { + return false; + } + } + + abstract protected function validateAction($object, PhabricatorUser $viewer); + abstract protected function getCommitActionLabel(); + + public function getCommandKeyword() { + return null; + } + + public function getCommandAliases() { + return array(); + } + + public function getCommandSummary() { + return null; + } + + protected function getCommitActionOrder() { + return 1000; + } + + public function getCommitActionOrderVector() { + return id(new PhutilSortVector()) + ->addInt($this->getCommitActionOrder()); + } + + protected function getCommitActionGroupKey() { + return DiffusionCommitEditEngine::ACTIONGROUP_COMMIT; + } + + protected function getCommitActionDescription() { + return null; + } + + public static function loadAllActions() { + return id(new PhutilClassMapQuery()) + ->setAncestorClass(__CLASS__) + ->setUniqueMethod('getCommitActionKey') + ->execute(); + } + + protected function isViewerCommitAuthor( + PhabricatorRepositoryCommit $commit, + PhabricatorUser $viewer) { + + if (!$viewer->getPHID()) { + return false; + } + + return ($viewer->getPHID() === $commit->getAuthorPHID()); + } + + public function newEditField( + PhabricatorRepositoryCommit $commit, + PhabricatorUser $viewer) { + + $field = id(new PhabricatorApplyEditField()) + ->setKey($this->getCommitActionKey()) + ->setTransactionType($this->getTransactionTypeConstant()) + ->setValue(true); + + if ($this->isActionAvailable($commit, $viewer)) { + $label = $this->getCommitActionLabel(); + if ($label !== null) { + $field->setCommentActionLabel($label); + + $description = $this->getCommitActionDescription(); + $field->setActionDescription($description); + + $group_key = $this->getCommitActionGroupKey(); + $field->setCommentActionGroupKey($group_key); + + $field->setActionConflictKey('commit.action'); + } + } + + return $field; + } + + public function validateTransactions($object, array $xactions) { + $errors = array(); + $actor = $this->getActor(); + + $action_exception = null; + try { + $this->validateAction($object, $actor); + } catch (Exception $ex) { + $action_exception = $ex; + } + + foreach ($xactions as $xaction) { + if ($action_exception) { + $errors[] = $this->newInvalidError( + $action_exception->getMessage(), + $xaction); + } + } + + return $errors; + } + +} diff --git a/src/applications/diffusion/xaction/DiffusionCommitAuditTransaction.php b/src/applications/diffusion/xaction/DiffusionCommitAuditTransaction.php new file mode 100644 index 0000000000..86656d85a6 --- /dev/null +++ b/src/applications/diffusion/xaction/DiffusionCommitAuditTransaction.php @@ -0,0 +1,118 @@ +getViewerAuditStatus($commit, $viewer) !== null); + } + + protected function isViewerAnyActiveAuditor( + PhabricatorRepositoryCommit $commit, + PhabricatorUser $viewer) { + + // This omits various inactive states like "Resigned" and "Not Required". + + return $this->isViewerAuditStatusAmong( + $commit, + $viewer, + array( + PhabricatorAuditStatusConstants::AUDIT_REQUIRED, + PhabricatorAuditStatusConstants::CONCERNED, + PhabricatorAuditStatusConstants::ACCEPTED, + PhabricatorAuditStatusConstants::AUDIT_REQUESTED, + )); + } + + protected function isViewerAcceptingAuditor( + PhabricatorRepositoryCommit $commit, + PhabricatorUser $viewer) { + return $this->isViewerAuditStatusAmong( + $commit, + $viewer, + array( + PhabricatorAuditStatusConstants::ACCEPTED, + )); + } + + protected function isViewerRejectingAuditor( + PhabricatorRepositoryCommit $commit, + PhabricatorUser $viewer) { + return $this->isViewerAuditStatusAmong( + $commit, + $viewer, + array( + PhabricatorAuditStatusConstants::CONCERNED, + )); + } + + protected function getViewerAuditStatus( + PhabricatorRepositoryCommit $commit, + PhabricatorUser $viewer) { + + if (!$viewer->getPHID()) { + return null; + } + + foreach ($commit->getAudits() as $audit) { + if ($audit->getAuditorPHID() != $viewer->getPHID()) { + continue; + } + + return $audit->getAuditStatus(); + } + + return null; + } + + protected function isViewerAuditStatusAmong( + PhabricatorRepositoryCommit $commit, + PhabricatorUser $viewer, + array $status_list) { + + $status = $this->getViewerAuditStatus($commit, $viewer); + if ($status === null) { + return false; + } + + $status_map = array_fuse($status_list); + return isset($status_map[$status]); + } + + protected function applyAuditorEffect( + PhabricatorRepositoryCommit $commit, + PhabricatorUser $viewer, + $value, + $status) { + + $audits = $commit->getAudits(); + $audits = mpull($audits, null, 'getAuditorPHID'); + + $map = array(); + + $with_authority = ($status != PhabricatorAuditStatusConstants::RESIGNED); + if ($with_authority) { + $has_authority = PhabricatorAuditCommentEditor::loadAuditPHIDsForUser( + $viewer); + $has_authority = array_fuse($has_authority); + foreach ($audits as $audit) { + $auditor_phid = $audit->getAuditorPHID(); + if (isset($has_authority[$auditor_phid])) { + $map[$auditor_phid] = $status; + } + } + } + + // In all cases, you affect yourself. + $map[$viewer->getPHID()] = $status; + + $this->updateAudits($commit, $map); + } + +} diff --git a/src/applications/diffusion/xaction/DiffusionCommitAuditorsTransaction.php b/src/applications/diffusion/xaction/DiffusionCommitAuditorsTransaction.php new file mode 100644 index 0000000000..a4b1d817b4 --- /dev/null +++ b/src/applications/diffusion/xaction/DiffusionCommitAuditorsTransaction.php @@ -0,0 +1,216 @@ +getAudits(); + return mpull($auditors, 'getAuditStatus', 'getAuditorPHID'); + } + + public function generateNewValue($object, $value) { + $actor = $this->getActor(); + + $auditors = $this->generateOldValue($object); + $old_auditors = $auditors; + + $request_status = PhabricatorAuditStatusConstants::AUDIT_REQUESTED; + + $rem = idx($value, '-', array()); + foreach ($rem as $phid) { + unset($auditors[$phid]); + } + + $add = idx($value, '+', array()); + $add_map = array(); + foreach ($add as $phid) { + $add_map[$phid] = $request_status; + } + + $set = idx($value, '=', null); + if ($set !== null) { + foreach ($set as $phid) { + $add_map[$phid] = $request_status; + } + + $auditors = array(); + } + + foreach ($add_map as $phid => $new_status) { + $old_status = idx($old_auditors, $phid); + + if ($old_status) { + $auditors[$phid] = $old_status; + continue; + } + + $auditors[$phid] = $new_status; + } + + return $auditors; + } + + public function getTransactionHasEffect($object, $old, $new) { + ksort($old); + ksort($new); + return ($old !== $new); + } + + public function applyExternalEffects($object, $value) { + $src_phid = $object->getPHID(); + + $old = $this->generateOldValue($object); + $new = $value; + + $auditors = $object->getAudits(); + $auditors = mpull($auditors, null, 'getAuditorPHID'); + + $rem = array_diff_key($old, $new); + foreach ($rem as $phid => $status) { + $auditor = idx($auditors, $phid); + if ($auditor) { + $auditor->delete(); + } + } + + $this->updateAudits($object, $new); + } + + public function getTitle() { + $old = $this->getOldValue(); + $new = $this->getNewValue(); + + $rem = array_diff_key($old, $new); + $add = array_diff_key($new, $old); + $rem_phids = array_keys($rem); + $add_phids = array_keys($add); + $total_count = count($rem) + count($add); + + if ($rem && $add) { + return pht( + '%s edited %s auditor(s), removed %s: %s; added %s: %s.', + $this->renderAuthor(), + new PhutilNumber($total_count), + phutil_count($rem_phids), + $this->renderHandleList($rem_phids), + phutil_count($add_phids), + $this->renderHandleList($add_phids)); + } else if ($add) { + return pht( + '%s added %s auditor(s): %s.', + $this->renderAuthor(), + phutil_count($add_phids), + $this->renderHandleList($add_phids)); + } else { + return pht( + '%s removed %s auditor(s): %s.', + $this->renderAuthor(), + phutil_count($rem_phids), + $this->renderHandleList($rem_phids)); + } + } + + public function getTitleForFeed() { + $old = $this->getOldValue(); + $new = $this->getNewValue(); + + $rem = array_diff_key($old, $new); + $add = array_diff_key($new, $old); + $rem_phids = array_keys($rem); + $add_phids = array_keys($add); + $total_count = count($rem) + count($add); + + if ($rem && $add) { + return pht( + '%s edited %s auditor(s) for %s, removed %s: %s; added %s: %s.', + $this->renderAuthor(), + new PhutilNumber($total_count), + $this->renderObject(), + phutil_count($rem_phids), + $this->renderHandleList($rem_phids), + phutil_count($add_phids), + $this->renderHandleList($add_phids)); + } else if ($add) { + return pht( + '%s added %s auditor(s) for %s: %s.', + $this->renderAuthor(), + phutil_count($add_phids), + $this->renderObject(), + $this->renderHandleList($add_phids)); + } else { + return pht( + '%s removed %s auditor(s) for %s: %s.', + $this->renderAuthor(), + phutil_count($rem_phids), + $this->renderObject(), + $this->renderHandleList($rem_phids)); + } + } + + public function validateTransactions($object, array $xactions) { + $actor = $this->getActor(); + $errors = array(); + + if (!$xactions) { + return $errors; + } + + $author_phid = $object->getAuthorPHID(); + $can_author_close_key = 'audit.can-author-close-audit'; + $can_author_close = PhabricatorEnv::getEnvConfig($can_author_close_key); + + $old = $this->generateOldValue($object); + foreach ($xactions as $xaction) { + $new = $this->generateNewValue($object, $xaction->getNewValue()); + + $add = array_diff_key($new, $old); + if (!$add) { + continue; + } + + $objects = id(new PhabricatorObjectQuery()) + ->setViewer($actor) + ->withPHIDs(array_keys($add)) + ->execute(); + $objects = mpull($objects, null, 'getPHID'); + + foreach ($add as $phid => $status) { + if (!isset($objects[$phid])) { + $errors[] = $this->newInvalidError( + pht( + 'Auditor "%s" is not a valid object.', + $phid), + $xaction); + continue; + } + + switch (phid_get_type($phid)) { + case PhabricatorPeopleUserPHIDType::TYPECONST: + case PhabricatorOwnersPackagePHIDType::TYPECONST: + case PhabricatorProjectProjectPHIDType::TYPECONST: + break; + default: + $errors[] = $this->newInvalidError( + pht( + 'Auditor "%s" must be a user, a package, or a project.', + $phid), + $xaction); + continue 2; + } + + $is_self = ($phid === $author_phid); + if ($is_self && !$can_author_close) { + $errors[] = $this->newInvalidError( + pht('The author of a commit can not be an auditor.'), + $xaction); + continue; + } + } + } + + return $errors; + } + +} diff --git a/src/applications/diffusion/xaction/DiffusionCommitConcernTransaction.php b/src/applications/diffusion/xaction/DiffusionCommitConcernTransaction.php new file mode 100644 index 0000000000..6bbc27818f --- /dev/null +++ b/src/applications/diffusion/xaction/DiffusionCommitConcernTransaction.php @@ -0,0 +1,74 @@ +getActor(); + return $this->isViewerRejectingAuditor($object, $actor); + } + + public function applyExternalEffects($object, $value) { + $status = PhabricatorAuditStatusConstants::CONCERNED; + $actor = $this->getActor(); + $this->applyAuditorEffect($object, $actor, $value, $status); + } + + protected function validateAction($object, PhabricatorUser $viewer) { + if ($this->isViewerCommitAuthor($object, $viewer)) { + throw new Exception( + pht( + 'You can not raise a concern with this commit because you are '. + 'the commit author. You can only raise concerns with commits '. + 'you did not author.')); + } + + if ($this->isViewerRejectingAuditor($object, $viewer)) { + throw new Exception( + pht( + 'You can not raise a concern with this commit because you have '. + 'already raised a concern with it.')); + } + } + + public function getTitle() { + return pht( + '%s raised a concern with this commit.', + $this->renderAuthor()); + } + + public function getTitleForFeed() { + return pht( + '%s raised a concern with %s.', + $this->renderAuthor(), + $this->renderObject()); + } + +} diff --git a/src/applications/diffusion/xaction/DiffusionCommitResignTransaction.php b/src/applications/diffusion/xaction/DiffusionCommitResignTransaction.php new file mode 100644 index 0000000000..103d0fabfe --- /dev/null +++ b/src/applications/diffusion/xaction/DiffusionCommitResignTransaction.php @@ -0,0 +1,66 @@ +getActor(); + return !$this->isViewerAnyActiveAuditor($object, $actor); + } + + public function applyExternalEffects($object, $value) { + $status = PhabricatorAuditStatusConstants::RESIGNED; + $actor = $this->getActor(); + $this->applyAuditorEffect($object, $actor, $value, $status); + } + + protected function validateAction($object, PhabricatorUser $viewer) { + if (!$this->isViewerAnyActiveAuditor($object, $viewer)) { + throw new Exception( + pht( + 'You can not resign from this commit because you are not an '. + 'active auditor.')); + } + } + + public function getTitle() { + return pht( + '%s resigned from this commit.', + $this->renderAuthor()); + } + + public function getTitleForFeed() { + return pht( + '%s resigned from %s.', + $this->renderAuthor(), + $this->renderObject()); + } + +} diff --git a/src/applications/diffusion/xaction/DiffusionCommitTransactionType.php b/src/applications/diffusion/xaction/DiffusionCommitTransactionType.php new file mode 100644 index 0000000000..34d476d3d1 --- /dev/null +++ b/src/applications/diffusion/xaction/DiffusionCommitTransactionType.php @@ -0,0 +1,37 @@ +getAudits(); + $audits = mpull($audits, null, 'getAuditorPHID'); + + foreach ($new as $phid => $status) { + $audit = idx($audits, $phid); + if (!$audit) { + $audit = id(new PhabricatorRepositoryAuditRequest()) + ->setAuditorPHID($phid) + ->setCommitPHID($commit->getPHID()); + + $audits[$phid] = $audit; + } else { + if ($audit->getAuditStatus() === $status) { + continue; + } + } + + $audit + ->setAuditStatus($status) + ->save(); + } + + $commit->attachAudits($audits); + + return $audits; + } + +} diff --git a/src/applications/favorites/application/PhabricatorFavoritesApplication.php b/src/applications/favorites/application/PhabricatorFavoritesApplication.php new file mode 100644 index 0000000000..3c5211fec4 --- /dev/null +++ b/src/applications/favorites/application/PhabricatorFavoritesApplication.php @@ -0,0 +1,39 @@ + array( + '' => 'PhabricatorFavoritesMainController', + '(?Pglobal|personal)/item/' => $this->getProfileMenuRouting( + 'PhabricatorFavoritesMenuItemController'), + ), + ); + } + + public function isLaunchable() { + return false; + } + + public function getApplicationOrder() { + return 9; + } + +} diff --git a/src/applications/favorites/constants/PhabricatorFavoritesConstants.php b/src/applications/favorites/constants/PhabricatorFavoritesConstants.php new file mode 100644 index 0000000000..c11a435eef --- /dev/null +++ b/src/applications/favorites/constants/PhabricatorFavoritesConstants.php @@ -0,0 +1,11 @@ +getViewer(); + + $menu = id(new PHUIObjectItemListView()) + ->setUser($viewer); + + $menu->addItem( + id(new PHUIObjectItemView()) + ->setHeader(pht('Personal Menu Items')) + ->setHref($this->getApplicationURI('personal/item/configure/')) + ->setImageURI($viewer->getProfileImageURI()) + ->addAttribute(pht('Edit favorites for your personal account.'))); + + $icon = id(new PHUIIconView()) + ->setIcon('fa-globe') + ->setBackground('bg-blue'); + + $menu->addItem( + id(new PHUIObjectItemView()) + ->setHeader(pht('Global Menu Items')) + ->setHref($this->getApplicationURI('global/item/configure/')) + ->setImageIcon($icon) + ->addAttribute(pht('Edit global default favorites for all users.'))); + + $crumbs = $this->buildApplicationCrumbs(); + $crumbs->addTextCrumb(pht('Manage')); + $crumbs->setBorder(true); + + $box = id(new PHUIObjectBoxView()) + ->setObjectList($menu); + + $header = id(new PHUIHeaderView()) + ->setHeader(pht('Manage Favorites')) + ->setHeaderIcon('fa-star-o'); + + $view = id(new PHUITwoColumnView()) + ->setHeader($header) + ->setFooter(array( + $box, + )); + + return $this->newPage() + ->setTitle(pht('Manage')) + ->setCrumbs($crumbs) + ->appendChild($view); + + } + +} diff --git a/src/applications/favorites/controller/PhabricatorFavoritesMenuItemController.php b/src/applications/favorites/controller/PhabricatorFavoritesMenuItemController.php new file mode 100644 index 0000000000..a398cf6ee8 --- /dev/null +++ b/src/applications/favorites/controller/PhabricatorFavoritesMenuItemController.php @@ -0,0 +1,29 @@ +getViewer(); + $type = $request->getURIData('type'); + $custom_phid = null; + if ($type == 'personal') { + $custom_phid = $viewer->getPHID(); + } + + $application = 'PhabricatorFavoritesApplication'; + $favorites = id(new PhabricatorApplicationQuery()) + ->setViewer($viewer) + ->withClasses(array($application)) + ->withInstalled(true) + ->executeOne(); + + $engine = id(new PhabricatorFavoritesProfileMenuEngine()) + ->setProfileObject($favorites) + ->setCustomPHID($custom_phid) + ->setController($this); + + return $engine->buildResponse(); + } + +} diff --git a/src/applications/favorites/engine/PhabricatorFavoritesProfileMenuEngine.php b/src/applications/favorites/engine/PhabricatorFavoritesProfileMenuEngine.php new file mode 100644 index 0000000000..a12922a6e8 --- /dev/null +++ b/src/applications/favorites/engine/PhabricatorFavoritesProfileMenuEngine.php @@ -0,0 +1,70 @@ +getProfileObject(); + $custom = $this->getCustomPHID(); + + if ($custom) { + return "/favorites/personal/item/{$path}"; + } else { + return "/favorites/global/item/{$path}"; + } + } + + protected function getBuiltinProfileItems($object) { + $items = array(); + $custom_phid = $this->getCustomPHID(); + + // Built-in Global Defaults + if (!$custom_phid) { + $create_task = array( + 'name' => null, + 'formKey' => + id(new ManiphestEditEngine())->getProfileMenuItemDefault(), + ); + + $create_project = array( + 'name' => null, + 'formKey' => + id(new PhabricatorProjectEditEngine())->getProfileMenuItemDefault(), + ); + + $create_repository = array( + 'name' => null, + 'formKey' => + id(new DiffusionRepositoryEditEngine())->getProfileMenuItemDefault(), + ); + + $items[] = $this->newItem() + ->setBuiltinKey(PhabricatorFavoritesConstants::ITEM_TASK) + ->setMenuItemKey(PhabricatorEditEngineProfileMenuItem::MENUITEMKEY) + ->setMenuItemProperties($create_task); + + $items[] = $this->newItem() + ->setBuiltinKey(PhabricatorFavoritesConstants::ITEM_PROJECT) + ->setMenuItemKey(PhabricatorEditEngineProfileMenuItem::MENUITEMKEY) + ->setMenuItemProperties($create_project); + + $items[] = $this->newItem() + ->setBuiltinKey(PhabricatorFavoritesConstants::ITEM_REPOSITORY) + ->setMenuItemKey(PhabricatorEditEngineProfileMenuItem::MENUITEMKEY) + ->setMenuItemProperties($create_repository); + } + + // Single Manage Item, switches URI based on admin/user + $items[] = $this->newItem() + ->setBuiltinKey(PhabricatorFavoritesConstants::ITEM_MANAGE) + ->setMenuItemKey( + PhabricatorFavoritesManageProfileMenuItem::MENUITEMKEY); + + return $items; + } + +} diff --git a/src/applications/favorites/menuitem/PhabricatorFavoritesManageProfileMenuItem.php b/src/applications/favorites/menuitem/PhabricatorFavoritesManageProfileMenuItem.php new file mode 100644 index 0000000000..36847061bf --- /dev/null +++ b/src/applications/favorites/menuitem/PhabricatorFavoritesManageProfileMenuItem.php @@ -0,0 +1,72 @@ +getMenuItemProperty('name'); + + if (strlen($name)) { + return $name; + } + + return $this->getDefaultName(); + } + + public function buildEditEngineFields( + PhabricatorProfileMenuItemConfiguration $config) { + return array( + id(new PhabricatorTextEditField()) + ->setKey('name') + ->setLabel(pht('Name')) + ->setPlaceholder($this->getDefaultName()) + ->setValue($config->getMenuItemProperty('name')), + ); + } + + protected function newNavigationMenuItems( + PhabricatorProfileMenuItemConfiguration $config) { + $viewer = $this->getViewer(); + + if ($viewer->isLoggedIn()) { + $admin = $viewer->getIsAdmin(); + $name = $this->getDisplayName($config); + $icon = 'fa-pencil'; + $href = '/favorites/personal/item/configure/'; + if ($admin) { + $href = '/favorites/'; + } + + $item = $this->newItem() + ->setHref($href) + ->setName($name) + ->setIcon($icon); + } + + return array( + $item, + ); + } + +} diff --git a/src/applications/home/application/PhabricatorHomeApplication.php b/src/applications/home/application/PhabricatorHomeApplication.php index 9b31ee537f..8d1b97a797 100644 --- a/src/applications/home/application/PhabricatorHomeApplication.php +++ b/src/applications/home/application/PhabricatorHomeApplication.php @@ -27,6 +27,11 @@ final class PhabricatorHomeApplication extends PhabricatorApplication { '/(?Phome)/' => 'PhabricatorHomeMainController', '/home/' => array( 'create/' => 'PhabricatorHomeQuickCreateController', + 'menu/' => array( + '' => 'PhabricatorHomeMenuController', + '(?Pglobal|personal)/item/' => $this->getProfileMenuRouting( + 'PhabricatorHomeMenuItemController'), + ), ), ); } diff --git a/src/applications/home/constants/PhabricatorHomeConstants.php b/src/applications/home/constants/PhabricatorHomeConstants.php new file mode 100644 index 0000000000..d1613acf58 --- /dev/null +++ b/src/applications/home/constants/PhabricatorHomeConstants.php @@ -0,0 +1,9 @@ +getViewer(); + + $menu = id(new PHUIObjectItemListView()) + ->setUser($viewer); + + $menu->addItem( + id(new PHUIObjectItemView()) + ->setHeader(pht('Personal Menu Items')) + ->setHref($this->getApplicationURI('menu/personal/item/configure/')) + ->setImageURI($viewer->getProfileImageURI()) + ->addAttribute(pht('Edit the menu for your personal account.'))); + + $icon = id(new PHUIIconView()) + ->setIcon('fa-globe') + ->setBackground('bg-blue'); + + $menu->addItem( + id(new PHUIObjectItemView()) + ->setHeader(pht('Global Menu Items')) + ->setHref($this->getApplicationURI('menu/global/item/configure/')) + ->setImageIcon($icon) + ->addAttribute(pht('Edit the global default menu for all users.'))); + + $crumbs = $this->buildApplicationCrumbs(); + $crumbs->addTextCrumb(pht('Manage')); + $crumbs->setBorder(true); + + $box = id(new PHUIObjectBoxView()) + ->setObjectList($menu); + + $header = id(new PHUIHeaderView()) + ->setHeader(pht('Manage Home Menu')) + ->setHeaderIcon('fa-home'); + + $view = id(new PHUITwoColumnView()) + ->setHeader($header) + ->setFooter(array( + $box, + )); + + return $this->newPage() + ->setTitle(pht('Manage Home Menu')) + ->setCrumbs($crumbs) + ->appendChild($view); + + } + +} diff --git a/src/applications/home/controller/PhabricatorHomeMenuItemController.php b/src/applications/home/controller/PhabricatorHomeMenuItemController.php new file mode 100644 index 0000000000..51842dd881 --- /dev/null +++ b/src/applications/home/controller/PhabricatorHomeMenuItemController.php @@ -0,0 +1,29 @@ +getViewer(); + $type = $request->getURIData('type'); + $custom_phid = null; + if ($type == 'personal') { + $custom_phid = $viewer->getPHID(); + } + + $application = 'PhabricatorHomeApplication'; + $home_app = id(new PhabricatorApplicationQuery()) + ->setViewer($viewer) + ->withClasses(array($application)) + ->withInstalled(true) + ->executeOne(); + + $engine = id(new PhabricatorHomeProfileMenuEngine()) + ->setProfileObject($home_app) + ->setCustomPHID($custom_phid) + ->setController($this); + + return $engine->buildResponse(); + } + +} diff --git a/src/applications/home/engine/PhabricatorHomeProfileMenuEngine.php b/src/applications/home/engine/PhabricatorHomeProfileMenuEngine.php new file mode 100644 index 0000000000..ebcc92b189 --- /dev/null +++ b/src/applications/home/engine/PhabricatorHomeProfileMenuEngine.php @@ -0,0 +1,58 @@ +getProfileObject(); + $custom = $this->getCustomPHID(); + + if ($custom) { + return "/home/menu/personal/item/{$path}"; + } else { + return "/home/menu/global/item/{$path}"; + } + } + + protected function getBuiltinProfileItems($object) { + $viewer = $this->getViewer(); + $items = array(); + $custom_phid = $this->getCustomPHID(); + + $applications = id(new PhabricatorApplicationQuery()) + ->setViewer($viewer) + ->withInstalled(true) + ->withUnlisted(false) + ->withLaunchable(true) + ->execute(); + + foreach ($applications as $application) { + if (!$application->isPinnedByDefault($viewer)) { + continue; + } + + $properties = array( + 'name' => $application->getName(), + 'application' => $application->getPHID(), + ); + + $items[] = $this->newItem() + ->setBuiltinKey($application->getPHID()) + ->setMenuItemKey(PhabricatorApplicationProfileMenuItem::MENUITEMKEY) + ->setMenuItemProperties($properties); + } + + // Single Manage Item, switches URI based on admin/user + $items[] = $this->newItem() + ->setBuiltinKey(PhabricatorHomeConstants::ITEM_MANAGE) + ->setMenuItemKey( + PhabricatorHomeManageProfileMenuItem::MENUITEMKEY); + + return $items; + } + +} diff --git a/src/applications/home/menuitem/PhabricatorHomeManageProfileMenuItem.php b/src/applications/home/menuitem/PhabricatorHomeManageProfileMenuItem.php new file mode 100644 index 0000000000..514cd8e026 --- /dev/null +++ b/src/applications/home/menuitem/PhabricatorHomeManageProfileMenuItem.php @@ -0,0 +1,72 @@ +getMenuItemProperty('name'); + + if (strlen($name)) { + return $name; + } + + return $this->getDefaultName(); + } + + public function buildEditEngineFields( + PhabricatorProfileMenuItemConfiguration $config) { + return array( + id(new PhabricatorTextEditField()) + ->setKey('name') + ->setLabel(pht('Name')) + ->setPlaceholder($this->getDefaultName()) + ->setValue($config->getMenuItemProperty('name')), + ); + } + + protected function newNavigationMenuItems( + PhabricatorProfileMenuItemConfiguration $config) { + $viewer = $this->getViewer(); + + if ($viewer->isLoggedIn()) { + $admin = $viewer->getIsAdmin(); + $name = $this->getDisplayName($config); + $icon = 'fa-pencil'; + $href = '/home/menu/personal/item/configure/'; + if ($admin) { + $href = '/home/menu/'; + } + + $item = $this->newItem() + ->setHref($href) + ->setName($name) + ->setIcon($icon); + } + + return array( + $item, + ); + } + +} diff --git a/src/applications/maniphest/conduit/ManiphestCreateTaskConduitAPIMethod.php b/src/applications/maniphest/conduit/ManiphestCreateTaskConduitAPIMethod.php index 3a404774cf..fac96372fe 100644 --- a/src/applications/maniphest/conduit/ManiphestCreateTaskConduitAPIMethod.php +++ b/src/applications/maniphest/conduit/ManiphestCreateTaskConduitAPIMethod.php @@ -11,6 +11,16 @@ final class ManiphestCreateTaskConduitAPIMethod return pht('Create a new Maniphest task.'); } + public function getMethodStatus() { + return self::METHOD_STATUS_FROZEN; + } + + public function getMethodStatusDescription() { + return pht( + 'This method is frozen and will eventually be deprecated. New code '. + 'should use "maniphest.edit" instead.'); + } + protected function defineParamTypes() { return $this->getTaskFields($is_new = true); } diff --git a/src/applications/maniphest/conduit/ManiphestInfoConduitAPIMethod.php b/src/applications/maniphest/conduit/ManiphestInfoConduitAPIMethod.php index 367feff41a..8bd439253f 100644 --- a/src/applications/maniphest/conduit/ManiphestInfoConduitAPIMethod.php +++ b/src/applications/maniphest/conduit/ManiphestInfoConduitAPIMethod.php @@ -10,6 +10,16 @@ final class ManiphestInfoConduitAPIMethod extends ManiphestConduitAPIMethod { return pht('Retrieve information about a Maniphest task, given its ID.'); } + public function getMethodStatus() { + return self::METHOD_STATUS_FROZEN; + } + + public function getMethodStatusDescription() { + return pht( + 'This method is frozen and will eventually be deprecated. New code '. + 'should use "maniphest.search" instead.'); + } + protected function defineParamTypes() { return array( 'task_id' => 'required id', diff --git a/src/applications/maniphest/conduit/ManiphestQueryConduitAPIMethod.php b/src/applications/maniphest/conduit/ManiphestQueryConduitAPIMethod.php index ea8aeb95ed..45373d2587 100644 --- a/src/applications/maniphest/conduit/ManiphestQueryConduitAPIMethod.php +++ b/src/applications/maniphest/conduit/ManiphestQueryConduitAPIMethod.php @@ -10,6 +10,16 @@ final class ManiphestQueryConduitAPIMethod extends ManiphestConduitAPIMethod { return pht('Execute complex searches for Maniphest tasks.'); } + public function getMethodStatus() { + return self::METHOD_STATUS_FROZEN; + } + + public function getMethodStatusDescription() { + return pht( + 'This method is frozen and will eventually be deprecated. New code '. + 'should use "maniphest.search" instead.'); + } + protected function defineParamTypes() { $statuses = array( ManiphestTaskQuery::STATUS_ANY, diff --git a/src/applications/maniphest/conduit/ManiphestUpdateConduitAPIMethod.php b/src/applications/maniphest/conduit/ManiphestUpdateConduitAPIMethod.php index d4adee9570..d8d02becd7 100644 --- a/src/applications/maniphest/conduit/ManiphestUpdateConduitAPIMethod.php +++ b/src/applications/maniphest/conduit/ManiphestUpdateConduitAPIMethod.php @@ -10,6 +10,16 @@ final class ManiphestUpdateConduitAPIMethod extends ManiphestConduitAPIMethod { return pht('Update an existing Maniphest task.'); } + public function getMethodStatus() { + return self::METHOD_STATUS_FROZEN; + } + + public function getMethodStatusDescription() { + return pht( + 'This method is frozen and will eventually be deprecated. New code '. + 'should use "maniphest.edit" instead.'); + } + protected function defineErrorTypes() { return array( 'ERR-BAD-TASK' => pht('No such Maniphest task exists.'), diff --git a/src/applications/maniphest/controller/ManiphestTaskDetailController.php b/src/applications/maniphest/controller/ManiphestTaskDetailController.php index f1157bcb9f..ca0fe5466e 100644 --- a/src/applications/maniphest/controller/ManiphestTaskDetailController.php +++ b/src/applications/maniphest/controller/ManiphestTaskDetailController.php @@ -397,7 +397,8 @@ final class ManiphestTaskDetailController extends ManiphestController { foreach ($commit_phids as $phid) { $revisions_commits[$phid] = $handles->renderHandle($phid) - ->setShowHovercard(true); + ->setShowHovercard(true) + ->setShowStateIcon(true); $revision_phid = key($drev_edges[$phid][$commit_drev]); $revision_handle = $handles->getHandleIfExists($revision_phid); if ($revision_handle) { @@ -412,12 +413,16 @@ final class ManiphestTaskDetailController extends ManiphestController { } foreach ($edge_types as $edge_type => $edge_name) { - if ($edges[$edge_type]) { - $edge_handles = $viewer->loadHandles(array_keys($edges[$edge_type])); - $view->addProperty( - $edge_name, - $edge_handles->renderList()); + if (!$edges[$edge_type]) { + continue; } + + $edge_handles = $viewer->loadHandles(array_keys($edges[$edge_type])); + + $edge_list = $edge_handles->renderList() + ->setShowStateIcons(true); + + $view->addProperty($edge_name, $edge_list); } if ($revisions_commits) { diff --git a/src/applications/maniphest/storage/ManiphestTask.php b/src/applications/maniphest/storage/ManiphestTask.php index 4fb9fc2861..76722f6719 100644 --- a/src/applications/maniphest/storage/ManiphestTask.php +++ b/src/applications/maniphest/storage/ManiphestTask.php @@ -505,7 +505,10 @@ final class ManiphestTask extends ManiphestDAO } public function getConduitSearchAttachments() { - return array(); + return array( + id(new PhabricatorBoardColumnsSearchEngineAttachment()) + ->setAttachmentKey('columns'), + ); } diff --git a/src/applications/owners/controller/PhabricatorOwnersDetailController.php b/src/applications/owners/controller/PhabricatorOwnersDetailController.php index c91de51cc6..b641290f56 100644 --- a/src/applications/owners/controller/PhabricatorOwnersDetailController.php +++ b/src/applications/owners/controller/PhabricatorOwnersDetailController.php @@ -65,19 +65,23 @@ final class PhabricatorOwnersDetailController $commit_views = array(); - $commit_uri = id(new PhutilURI('/audit/')) + $commit_uri = id(new PhutilURI('/diffusion/commit/')) ->setQueryParams( array( 'auditorPHIDs' => $package->getPHID(), )); - $status_concern = DiffusionCommitQuery::AUDIT_STATUS_CONCERN; + $status_concern = PhabricatorAuditCommitStatusConstants::CONCERN_RAISED; $attention_commits = id(new DiffusionCommitQuery()) ->setViewer($request->getUser()) ->withAuditorPHIDs(array($package->getPHID())) - ->withAuditStatus($status_concern) + ->withStatuses( + array( + $status_concern, + )) ->needCommitData(true) + ->needAuditRequests(true) ->setLimit(10) ->execute(); $view = id(new PhabricatorAuditListView()) @@ -100,7 +104,8 @@ final class PhabricatorOwnersDetailController ->setViewer($request->getUser()) ->withAuditorPHIDs(array($package->getPHID())) ->needCommitData(true) - ->setLimit(100) + ->needAuditRequests(true) + ->setLimit(25) ->execute(); $view = id(new PhabricatorAuditListView()) @@ -119,13 +124,6 @@ final class PhabricatorOwnersDetailController ->setText(pht('View All')), ); - $phids = array(); - foreach ($commit_views as $commit_view) { - $phids[] = $commit_view['view']->getRequiredHandlePHIDs(); - } - $phids = array_mergev($phids); - $handles = $this->loadViewerHandles($phids); - $commit_panels = array(); foreach ($commit_views as $commit_view) { $commit_panel = id(new PHUIObjectBoxView()) @@ -136,7 +134,6 @@ final class PhabricatorOwnersDetailController if (isset($commit_view['button'])) { $commit_header->addActionLink($commit_view['button']); } - $commit_view['view']->setHandles($handles); $commit_panel->setHeader($commit_header); $commit_panel->appendChild($commit_view['view']); diff --git a/src/applications/paste/conduit/PasteCreateConduitAPIMethod.php b/src/applications/paste/conduit/PasteCreateConduitAPIMethod.php index b6ccd151ba..3badd04da7 100644 --- a/src/applications/paste/conduit/PasteCreateConduitAPIMethod.php +++ b/src/applications/paste/conduit/PasteCreateConduitAPIMethod.php @@ -10,6 +10,16 @@ final class PasteCreateConduitAPIMethod extends PasteConduitAPIMethod { return pht('Create a new paste.'); } + public function getMethodStatus() { + return self::METHOD_STATUS_FROZEN; + } + + public function getMethodStatusDescription() { + return pht( + 'This method is frozen and will eventually be deprecated. New code '. + 'should use "paste.edit" instead.'); + } + protected function defineParamTypes() { return array( 'content' => 'required string', diff --git a/src/applications/paste/conduit/PasteQueryConduitAPIMethod.php b/src/applications/paste/conduit/PasteQueryConduitAPIMethod.php index 6f3f87acf2..f20c18c06a 100644 --- a/src/applications/paste/conduit/PasteQueryConduitAPIMethod.php +++ b/src/applications/paste/conduit/PasteQueryConduitAPIMethod.php @@ -10,6 +10,16 @@ final class PasteQueryConduitAPIMethod extends PasteConduitAPIMethod { return pht('Query Pastes.'); } + public function getMethodStatus() { + return self::METHOD_STATUS_FROZEN; + } + + public function getMethodStatusDescription() { + return pht( + 'This method is frozen and will eventually be deprecated. New code '. + 'should use "paste.search" instead.'); + } + protected function defineParamTypes() { return array( 'ids' => 'optional list', diff --git a/src/applications/people/conduit/UserQueryConduitAPIMethod.php b/src/applications/people/conduit/UserQueryConduitAPIMethod.php index c42414a6be..df0ec65841 100644 --- a/src/applications/people/conduit/UserQueryConduitAPIMethod.php +++ b/src/applications/people/conduit/UserQueryConduitAPIMethod.php @@ -10,6 +10,16 @@ final class UserQueryConduitAPIMethod extends UserConduitAPIMethod { return pht('Query users.'); } + public function getMethodStatus() { + return self::METHOD_STATUS_FROZEN; + } + + public function getMethodStatusDescription() { + return pht( + 'This method is frozen and will eventually be deprecated. New code '. + 'should use "user.search" instead.'); + } + protected function defineParamTypes() { return array( 'usernames' => 'optional list', diff --git a/src/applications/people/engine/PhabricatorPeopleProfileMenuEngine.php b/src/applications/people/engine/PhabricatorPeopleProfileMenuEngine.php index aab3896721..9965147a12 100644 --- a/src/applications/people/engine/PhabricatorPeopleProfileMenuEngine.php +++ b/src/applications/people/engine/PhabricatorPeopleProfileMenuEngine.php @@ -63,7 +63,7 @@ final class PhabricatorPeopleProfileMenuEngine $viewer); if ($have_diffusion) { $uri = urisprintf( - '/audit/?authors=%s#R', + '/diffusion/commit/?authors=%s#R', $object->getPHID()); $items[] = $this->newItem() diff --git a/src/applications/phid/PhabricatorObjectHandle.php b/src/applications/phid/PhabricatorObjectHandle.php index 943464ed67..e6230c4ae6 100644 --- a/src/applications/phid/PhabricatorObjectHandle.php +++ b/src/applications/phid/PhabricatorObjectHandle.php @@ -31,6 +31,10 @@ final class PhabricatorObjectHandle private $tokenIcon; private $commandLineObjectName; + private $stateIcon; + private $stateColor; + private $stateName; + public function setIcon($icon) { $this->icon = $icon; return $this; @@ -284,6 +288,54 @@ final class PhabricatorObjectHandle return $this->complete; } + public function setStateIcon($state_icon) { + $this->stateIcon = $state_icon; + return $this; + } + + public function getStateIcon() { + return $this->stateIcon; + } + + public function setStateColor($state_color) { + $this->stateColor = $state_color; + return $this; + } + + public function getStateColor() { + return $this->stateColor; + } + + public function setStateName($state_name) { + $this->stateName = $state_name; + return $this; + } + + public function getStateName() { + return $this->stateName; + } + + public function renderStateIcon() { + $icon = $this->getStateIcon(); + if ($icon === null) { + $icon = 'fa-question-circle-o'; + } + + $color = $this->getStateColor(); + + $name = $this->getStateName(); + if ($name === null) { + $name = pht('Unknown'); + } + + return id(new PHUIIconView()) + ->setIcon($icon, $color) + ->addSigil('has-tooltip') + ->setMetadata( + array( + 'tip' => $name, + )); + } public function renderLink($name = null) { return $this->renderLinkWithAttributes($name, array()); diff --git a/src/applications/phid/view/PHUIHandleListView.php b/src/applications/phid/view/PHUIHandleListView.php index f7f02a391b..d15b3d15c6 100644 --- a/src/applications/phid/view/PHUIHandleListView.php +++ b/src/applications/phid/view/PHUIHandleListView.php @@ -13,6 +13,7 @@ final class PHUIHandleListView private $handleList; private $asInline; private $asText; + private $showStateIcons; public function setHandleList(PhabricatorHandleList $list) { $this->handleList = $list; @@ -37,20 +38,40 @@ final class PHUIHandleListView return $this->asText; } + public function setShowStateIcons($show_state_icons) { + $this->showStateIcons = $show_state_icons; + return $this; + } + + public function getShowStateIcons() { + return $this->showStateIcons; + } + protected function getTagName() { - // TODO: It would be nice to render this with a proper
    , at least in - // block mode, but don't stir the waters up too much for now. - return 'span'; + if ($this->getAsText()) { + return null; + } else { + // TODO: It would be nice to render this with a proper
      , at least + // in block mode, but don't stir the waters up too much for now. + return 'span'; + } } protected function getTagContent() { $list = $this->handleList; + + $show_state_icons = $this->getShowStateIcons(); + $items = array(); foreach ($list as $handle) { $view = $list->renderHandle($handle->getPHID()) ->setShowHovercard(true) ->setAsText($this->getAsText()); + if ($show_state_icons) { + $view->setShowStateIcon(true); + } + $items[] = $view; } diff --git a/src/applications/phid/view/PHUIHandleView.php b/src/applications/phid/view/PHUIHandleView.php index 6d968bd7ee..96ae7de025 100644 --- a/src/applications/phid/view/PHUIHandleView.php +++ b/src/applications/phid/view/PHUIHandleView.php @@ -17,6 +17,7 @@ final class PHUIHandleView private $asText; private $useShortName; private $showHovercard; + private $showStateIcon; public function setHandleList(PhabricatorHandleList $list) { $this->handleList = $list; @@ -48,6 +49,15 @@ final class PHUIHandleView return $this; } + public function setShowStateIcon($show_state_icon) { + $this->showStateIcon = $show_state_icon; + return $this; + } + + public function getShowStateIcon() { + return $this->showStateIcon; + } + public function render() { $handle = $this->handleList[$this->handlePHID]; @@ -77,6 +87,11 @@ final class PHUIHandleView $link = $handle->renderLink($name); } + if ($this->showStateIcon) { + $icon = $handle->renderStateIcon(); + $link = array($icon, ' ', $link); + } + return $link; } diff --git a/src/applications/policy/filter/PhabricatorPolicyFilter.php b/src/applications/policy/filter/PhabricatorPolicyFilter.php index e0c223cd2c..44931085fc 100644 --- a/src/applications/policy/filter/PhabricatorPolicyFilter.php +++ b/src/applications/policy/filter/PhabricatorPolicyFilter.php @@ -917,15 +917,27 @@ final class PhabricatorPolicyFilter extends Phobject { } private function getApplicationForPHID($phid) { - $phid_type = phid_get_type($phid); + static $class_map = array(); - $type_objects = PhabricatorPHIDType::getTypes(array($phid_type)); - $type_object = idx($type_objects, $phid_type); - if (!$type_object) { + $phid_type = phid_get_type($phid); + if (!isset($class_map[$phid_type])) { + $type_objects = PhabricatorPHIDType::getTypes(array($phid_type)); + $type_object = idx($type_objects, $phid_type); + if (!$type_object) { + $class = false; + } else { + $class = $type_object->getPHIDTypeApplicationClass(); + } + + $class_map[$phid_type] = $class; + } + + $class = $class_map[$phid_type]; + if ($class === false) { return null; } - return $type_object->getPHIDTypeApplicationClass(); + return $class; } } diff --git a/src/applications/project/conduit/ProjectColumnSearchConduitAPIMethod.php b/src/applications/project/conduit/ProjectColumnSearchConduitAPIMethod.php new file mode 100644 index 0000000000..7cf21922b4 --- /dev/null +++ b/src/applications/project/conduit/ProjectColumnSearchConduitAPIMethod.php @@ -0,0 +1,18 @@ + 'required string', diff --git a/src/applications/project/conduit/ProjectQueryConduitAPIMethod.php b/src/applications/project/conduit/ProjectQueryConduitAPIMethod.php index 3116ecb7f9..0a02088413 100644 --- a/src/applications/project/conduit/ProjectQueryConduitAPIMethod.php +++ b/src/applications/project/conduit/ProjectQueryConduitAPIMethod.php @@ -10,6 +10,16 @@ final class ProjectQueryConduitAPIMethod extends ProjectConduitAPIMethod { return pht('Execute searches for Projects.'); } + public function getMethodStatus() { + return self::METHOD_STATUS_FROZEN; + } + + public function getMethodStatusDescription() { + return pht( + 'This method is frozen and will eventually be deprecated. New code '. + 'should use "project.search" instead.'); + } + protected function defineParamTypes() { $statuses = array( diff --git a/src/applications/project/engineextension/PhabricatorBoardColumnsSearchEngineAttachment.php b/src/applications/project/engineextension/PhabricatorBoardColumnsSearchEngineAttachment.php new file mode 100644 index 0000000000..0a54c23b4a --- /dev/null +++ b/src/applications/project/engineextension/PhabricatorBoardColumnsSearchEngineAttachment.php @@ -0,0 +1,80 @@ +getViewer(); + + $objects = mpull($objects, null, 'getPHID'); + $object_phids = array_keys($objects); + + $edge_query = id(new PhabricatorEdgeQuery()) + ->withSourcePHIDs($object_phids) + ->withEdgeTypes( + array( + PhabricatorProjectObjectHasProjectEdgeType::EDGECONST, + )); + $edge_query->execute(); + + $project_phids = $edge_query->getDestinationPHIDs(); + + $engine = id(new PhabricatorBoardLayoutEngine()) + ->setViewer($viewer) + ->setBoardPHIDs($project_phids) + ->setObjectPHIDs($object_phids) + ->executeLayout(); + + $results = array(); + foreach ($objects as $phid => $object) { + $board_phids = $edge_query->getDestinationPHIDs(array($phid)); + + $boards = array(); + foreach ($board_phids as $board_phid) { + $columns = array(); + foreach ($engine->getObjectColumns($board_phid, $phid) as $column) { + if ($column->getProxyPHID()) { + // When an object is in a proxy column, don't return it on this + // attachment. This information can be reconstructed from other + // queries, is something of an implementation detail, and seems + // unlikely to be interesting to API consumers. + continue 2; + } + + $columns[] = $column->getRefForConduit(); + } + + // If a project has no workboard, the object won't appear on any + // columns. Just omit it from the result set. + if (!$columns) { + continue; + } + + $boards[$board_phid] = array( + 'columns' => $columns, + ); + } + + $results[$phid] = $boards; + } + + return $results; + } + + public function getAttachmentForObject($object, $data, $spec) { + $boards = idx($data, $object->getPHID(), array()); + + return array( + 'boards' => $boards, + ); + } + +} diff --git a/src/applications/project/engineextension/PhabricatorProjectsAncestorsSearchEngineAttachment.php b/src/applications/project/engineextension/PhabricatorProjectsAncestorsSearchEngineAttachment.php new file mode 100644 index 0000000000..3113068664 --- /dev/null +++ b/src/applications/project/engineextension/PhabricatorProjectsAncestorsSearchEngineAttachment.php @@ -0,0 +1,30 @@ +getAncestorProjects(); + + // Order ancestors by depth, ascending. + $ancestors = array_reverse($ancestors); + + $results = array(); + foreach ($ancestors as $ancestor) { + $results[] = $ancestor->getRefForConduit(); + } + + return array( + 'ancestors' => $results, + ); + } + +} diff --git a/src/applications/project/query/PhabricatorProjectColumnSearchEngine.php b/src/applications/project/query/PhabricatorProjectColumnSearchEngine.php new file mode 100644 index 0000000000..0658ec75b0 --- /dev/null +++ b/src/applications/project/query/PhabricatorProjectColumnSearchEngine.php @@ -0,0 +1,75 @@ +setLabel(pht('Projects')) + ->setKey('projectPHIDs') + ->setConduitKey('projects') + ->setAliases(array('project', 'projects', 'projectPHID')), + ); + } + + + protected function buildQueryFromParameters(array $map) { + $query = $this->newQuery(); + + if ($map['projectPHIDs']) { + $query->withProjectPHIDs($map['projectPHIDs']); + } + + return $query; + } + + protected function getURI($path) { + // NOTE: There's no way to query columns in the web UI, at least for + // the moment. + return null; + } + + protected function getBuiltinQueryNames() { + $names = array(); + + $names['all'] = pht('All'); + + return $names; + } + + public function buildSavedQueryFromBuiltin($query_key) { + $query = $this->newSavedQuery(); + $query->setQueryKey($query_key); + + switch ($query_key) { + case 'all': + return $query; + } + + return parent::buildSavedQueryFromBuiltin($query_key); + } + + protected function renderResultList( + array $projects, + PhabricatorSavedQuery $query, + array $handles) { + assert_instances_of($projects, 'PhabricatorProjectColumn'); + $viewer = $this->requireViewer(); + + return null; + } + +} diff --git a/src/applications/project/query/PhabricatorProjectSearchEngine.php b/src/applications/project/query/PhabricatorProjectSearchEngine.php index cfb1868a61..5c153be5a4 100644 --- a/src/applications/project/query/PhabricatorProjectSearchEngine.php +++ b/src/applications/project/query/PhabricatorProjectSearchEngine.php @@ -13,8 +13,7 @@ final class PhabricatorProjectSearchEngine public function newQuery() { return id(new PhabricatorProjectQuery()) - ->needImages(true) - ->withIsMilestone(false); + ->needImages(true); } protected function buildCustomSearchFields() { @@ -25,15 +24,28 @@ final class PhabricatorProjectSearchEngine id(new PhabricatorUsersSearchField()) ->setLabel(pht('Members')) ->setKey('memberPHIDs') + ->setConduitKey('members') ->setAliases(array('member', 'members')), id(new PhabricatorUsersSearchField()) ->setLabel(pht('Watchers')) ->setKey('watcherPHIDs') + ->setConduitKey('watchers') ->setAliases(array('watcher', 'watchers')), id(new PhabricatorSearchSelectField()) ->setLabel(pht('Status')) ->setKey('status') ->setOptions($this->getStatusOptions()), + id(new PhabricatorSearchThreeStateField()) + ->setLabel(pht('Milestones')) + ->setKey('isMilestone') + ->setOptions( + pht('(Show All)'), + pht('Show Only Milestones'), + pht('Hide Milestones')) + ->setDescription( + pht( + 'Pass true to find only milestones, or false to omit '. + 'milestones.')), id(new PhabricatorSearchCheckboxesField()) ->setLabel(pht('Icons')) ->setKey('icons') @@ -42,6 +54,19 @@ final class PhabricatorProjectSearchEngine ->setLabel(pht('Colors')) ->setKey('colors') ->setOptions($this->getColorOptions()), + id(new PhabricatorPHIDsSearchField()) + ->setLabel(pht('Parent Projects')) + ->setKey('parentPHIDs') + ->setConduitKey('parents') + ->setAliases(array('parent', 'parents', 'parentPHID')) + ->setDescription(pht('Find direct subprojects of specified parents.')), + id(new PhabricatorPHIDsSearchField()) + ->setLabel(pht('Ancestor Projects')) + ->setKey('ancestorPHIDs') + ->setConduitKey('ancestors') + ->setAliases(array('ancestor', 'ancestors', 'ancestorPHID')) + ->setDescription( + pht('Find all subprojects beneath specified ancestors.')), ); } @@ -77,6 +102,18 @@ final class PhabricatorProjectSearchEngine $query->withColors($map['colors']); } + if ($map['isMilestone'] !== null) { + $query->withIsMilestone($map['isMilestone']); + } + + if ($map['parentPHIDs']) { + $query->withParentProjectPHIDs($map['parentPHIDs']); + } + + if ($map['ancestorPHIDs']) { + $query->withAncestorProjectPHIDs($map['ancestorPHIDs']); + } + return $query; } @@ -103,6 +140,9 @@ final class PhabricatorProjectSearchEngine $viewer_phid = $this->requireViewer()->getPHID(); + // By default, do not show milestones in the list view. + $query->setParameter('isMilestone', false); + switch ($query_key) { case 'all': return $query; diff --git a/src/applications/project/storage/PhabricatorProject.php b/src/applications/project/storage/PhabricatorProject.php index 47e251f3af..a46ebd3352 100644 --- a/src/applications/project/storage/PhabricatorProject.php +++ b/src/applications/project/storage/PhabricatorProject.php @@ -741,6 +741,24 @@ final class PhabricatorProject extends PhabricatorProjectDAO ->setKey('slug') ->setType('string') ->setDescription(pht('Primary slug/hashtag.')), + id(new PhabricatorConduitSearchFieldSpecification()) + ->setKey('milestone') + ->setType('int?') + ->setDescription(pht('For milestones, milestone sequence number.')), + id(new PhabricatorConduitSearchFieldSpecification()) + ->setKey('parent') + ->setType('map?') + ->setDescription( + pht( + 'For subprojects and milestones, a brief description of the '. + 'parent project.')), + id(new PhabricatorConduitSearchFieldSpecification()) + ->setKey('depth') + ->setType('int') + ->setDescription( + pht( + 'For subprojects and milestones, depth of this project in the '. + 'tree. Root projects have depth 0.')), id(new PhabricatorConduitSearchFieldSpecification()) ->setKey('icon') ->setType('map') @@ -756,9 +774,25 @@ final class PhabricatorProject extends PhabricatorProjectDAO $color_key = $this->getColor(); $color_name = PhabricatorProjectIconSet::getColorName($color_key); + if ($this->isMilestone()) { + $milestone = (int)$this->getMilestoneNumber(); + } else { + $milestone = null; + } + + $parent = $this->getParentProject(); + if ($parent) { + $parent_ref = $parent->getRefForConduit(); + } else { + $parent_ref = null; + } + return array( 'name' => $this->getName(), 'slug' => $this->getPrimarySlug(), + 'milestone' => $milestone, + 'depth' => (int)$this->getProjectDepth(), + 'parent' => $parent_ref, 'icon' => array( 'key' => $this->getDisplayIconKey(), 'name' => $this->getDisplayIconName(), @@ -777,6 +811,20 @@ final class PhabricatorProject extends PhabricatorProjectDAO ->setAttachmentKey('members'), id(new PhabricatorProjectsWatchersSearchEngineAttachment()) ->setAttachmentKey('watchers'), + id(new PhabricatorProjectsAncestorsSearchEngineAttachment()) + ->setAttachmentKey('ancestors'), + ); + } + + /** + * Get an abbreviated representation of this project for use in providing + * "parent" and "ancestor" information. + */ + public function getRefForConduit() { + return array( + 'id' => (int)$this->getID(), + 'phid' => $this->getPHID(), + 'name' => $this->getName(), ); } diff --git a/src/applications/project/storage/PhabricatorProjectColumn.php b/src/applications/project/storage/PhabricatorProjectColumn.php index 660aafcc1f..7ddfb4351d 100644 --- a/src/applications/project/storage/PhabricatorProjectColumn.php +++ b/src/applications/project/storage/PhabricatorProjectColumn.php @@ -6,7 +6,8 @@ final class PhabricatorProjectColumn PhabricatorApplicationTransactionInterface, PhabricatorPolicyInterface, PhabricatorDestructibleInterface, - PhabricatorExtendedPolicyInterface { + PhabricatorExtendedPolicyInterface, + PhabricatorConduitResultInterface { const STATUS_ACTIVE = 0; const STATUS_HIDDEN = 1; @@ -183,6 +184,48 @@ final class PhabricatorProjectColumn return sprintf('%s%012d', $group, $sequence); } +/* -( PhabricatorConduitResultInterface )---------------------------------- */ + + public function getFieldSpecificationsForConduit() { + return array( + id(new PhabricatorConduitSearchFieldSpecification()) + ->setKey('name') + ->setType('string') + ->setDescription(pht('The display name of the column.')), + id(new PhabricatorConduitSearchFieldSpecification()) + ->setKey('project') + ->setType('map') + ->setDescription(pht('The project the column belongs to.')), + id(new PhabricatorConduitSearchFieldSpecification()) + ->setKey('proxyPHID') + ->setType('phid?') + ->setDescription( + pht( + 'For columns that proxy another object (like a subproject or '. + 'milestone), the PHID of the object they proxy.')), + ); + } + + public function getFieldValuesForConduit() { + return array( + 'name' => $this->getDisplayName(), + 'proxyPHID' => $this->getProxyPHID(), + 'project' => $this->getProject()->getRefForConduit(), + ); + } + + public function getConduitSearchAttachments() { + return array(); + } + + public function getRefForConduit() { + return array( + 'id' => (int)$this->getID(), + 'phid' => $this->getPHID(), + 'name' => $this->getDisplayName(), + ); + } + /* -( PhabricatorApplicationTransactionInterface )------------------------- */ diff --git a/src/applications/repository/conduit/RepositoryQueryConduitAPIMethod.php b/src/applications/repository/conduit/RepositoryQueryConduitAPIMethod.php index a3c1061e6a..61435e2a1b 100644 --- a/src/applications/repository/conduit/RepositoryQueryConduitAPIMethod.php +++ b/src/applications/repository/conduit/RepositoryQueryConduitAPIMethod.php @@ -8,11 +8,13 @@ final class RepositoryQueryConduitAPIMethod } public function getMethodStatus() { - return self::METHOD_STATUS_UNSTABLE; + return self::METHOD_STATUS_FROZEN; } public function getMethodStatusDescription() { - return pht('Repository methods are new and subject to change.'); + return pht( + 'This method is frozen and will eventually be deprecated. New code '. + 'should use "diffusion.repository.query" instead.'); } public function getMethodDescription() { diff --git a/src/applications/repository/management/PhabricatorRepositoryManagementClusterizeWorkflow.php b/src/applications/repository/management/PhabricatorRepositoryManagementClusterizeWorkflow.php index 7424b84eae..7178564067 100644 --- a/src/applications/repository/management/PhabricatorRepositoryManagementClusterizeWorkflow.php +++ b/src/applications/repository/management/PhabricatorRepositoryManagementClusterizeWorkflow.php @@ -61,6 +61,7 @@ final class PhabricatorRepositoryManagementClusterizeWorkflow array( AlmanacClusterRepositoryServiceType::SERVICETYPE, )) + ->needBindings(true) ->executeOne(); if (!$service) { throw new PhutilArgumentUsageException( @@ -70,9 +71,41 @@ final class PhabricatorRepositoryManagementClusterizeWorkflow } } - if ($service) { $service_phid = $service->getPHID(); + + $bindings = $service->getActiveBindings(); + + $unique_devices = array(); + foreach ($bindings as $binding) { + $unique_devices[$binding->getDevicePHID()] = $binding->getDevice(); + } + + if (count($unique_devices) > 1) { + $device_names = mpull($unique_devices, 'getName'); + + echo id(new PhutilConsoleBlock()) + ->addParagraph( + pht( + 'Service "%s" is actively bound to more than one device (%s).', + $service_name, + implode(', ', $device_names))) + ->addParagraph( + pht( + 'If you clusterize a repository onto this service it may be '. + 'unclear which devices have up-to-date copies of the '. + 'repository. If so, leader/follower ambiguity will freeze the '. + 'repository. You may need to manually promote a device to '. + 'unfreeze it. See "Ambiguous Leaders" in the documentation '. + 'for discussion.')) + ->drawConsoleString(); + + $prompt = pht('Continue anyway?'); + if (!phutil_console_confirm($prompt)) { + throw new PhutilArgumentUsageException( + pht('User aborted the workflow.')); + } + } } else { $service_phid = null; } diff --git a/src/applications/repository/phid/PhabricatorRepositoryCommitPHIDType.php b/src/applications/repository/phid/PhabricatorRepositoryCommitPHIDType.php index df84f2dcfd..b5abd40032 100644 --- a/src/applications/repository/phid/PhabricatorRepositoryCommitPHIDType.php +++ b/src/applications/repository/phid/PhabricatorRepositoryCommitPHIDType.php @@ -81,6 +81,16 @@ final class PhabricatorRepositoryCommitPHIDType extends PhabricatorPHIDType { $handle->setFullName($full_name); $handle->setURI($commit->getURI()); $handle->setTimestamp($commit->getEpoch()); + + $status = $commit->getAuditStatus(); + $icon = PhabricatorAuditCommitStatusConstants::getStatusIcon($status); + $color = PhabricatorAuditCommitStatusConstants::getStatusColor($status); + $name = PhabricatorAuditCommitStatusConstants::getStatusName($status); + + $handle + ->setStateIcon($icon) + ->setStateColor($color) + ->setStateName($name); } } diff --git a/src/applications/repository/storage/PhabricatorRepositoryCommit.php b/src/applications/repository/storage/PhabricatorRepositoryCommit.php index 58b9057449..67f187d9c2 100644 --- a/src/applications/repository/storage/PhabricatorRepositoryCommit.php +++ b/src/applications/repository/storage/PhabricatorRepositoryCommit.php @@ -13,7 +13,8 @@ final class PhabricatorRepositoryCommit HarbormasterCircleCIBuildableInterface, PhabricatorCustomFieldInterface, PhabricatorApplicationTransactionInterface, - PhabricatorFulltextInterface { + PhabricatorFulltextInterface, + PhabricatorConduitResultInterface { protected $repositoryID; protected $phid; @@ -203,6 +204,11 @@ final class PhabricatorRepositoryCommit return $authority_audits; } + public function getAuditorPHIDsForEdit() { + $audits = $this->getAudits(); + return mpull($audits, 'getAuditorPHID'); + } + public function save() { if (!$this->mailKey) { $this->mailKey = Filesystem::readRandomCharacters(20); @@ -251,6 +257,7 @@ final class PhabricatorRepositoryCommit foreach ($requests as $request) { switch ($request->getAuditStatus()) { case PhabricatorAuditStatusConstants::AUDIT_REQUIRED: + case PhabricatorAuditStatusConstants::AUDIT_REQUESTED: $any_need = true; break; case PhabricatorAuditStatusConstants::ACCEPTED: @@ -574,4 +581,26 @@ final class PhabricatorRepositoryCommit return new DiffusionCommitFulltextEngine(); } + +/* -( PhabricatorConduitResultInterface )---------------------------------- */ + + public function getFieldSpecificationsForConduit() { + return array( + id(new PhabricatorConduitSearchFieldSpecification()) + ->setKey('identifier') + ->setType('string') + ->setDescription(pht('The commit identifier.')), + ); + } + + public function getFieldValuesForConduit() { + return array( + 'identifier' => $this->getCommitIdentifier(), + ); + } + + public function getConduitSearchAttachments() { + return array(); + } + } diff --git a/src/applications/search/controller/PhabricatorApplicationSearchController.php b/src/applications/search/controller/PhabricatorApplicationSearchController.php index 8937c124ea..cdd6b9335a 100644 --- a/src/applications/search/controller/PhabricatorApplicationSearchController.php +++ b/src/applications/search/controller/PhabricatorApplicationSearchController.php @@ -239,6 +239,10 @@ final class PhabricatorApplicationSearchController $nux_view = null; } + $is_overflowing = + $pager->willShowPagingControls() && + $engine->getResultBucket($saved_query); + $force_overheated = $request->getBool('overheated'); $is_overheated = $query->getIsOverheated() || $force_overheated; @@ -265,6 +269,11 @@ final class PhabricatorApplicationSearchController if ($list->getInfoView()) { $box->setInfoView($list->getInfoView()); } + + if ($is_overflowing) { + $box->appendChild($this->newOverflowingView()); + } + if ($list->getContent()) { $box->appendChild($list->getContent()); } @@ -545,6 +554,22 @@ final class PhabricatorApplicationSearchController ->setDropdownMenu($action_list); } + private function newOverflowingView() { + $message = pht( + 'The query matched more than one page of results. Results are '. + 'paginated before bucketing, so later pages may contain additional '. + 'results in any bucket.'); + + return id(new PHUIInfoView()) + ->setSeverity(PHUIInfoView::SEVERITY_WARNING) + ->setFlush(true) + ->setTitle(pht('Buckets Overflowing')) + ->setErrors( + array( + $message, + )); + } + private function newOverheatedView(array $results) { if ($results) { $message = pht( diff --git a/src/applications/search/editor/PhabricatorProfileMenuEditEngine.php b/src/applications/search/editor/PhabricatorProfileMenuEditEngine.php index f7b8f76ec7..70ba141967 100644 --- a/src/applications/search/editor/PhabricatorProfileMenuEditEngine.php +++ b/src/applications/search/editor/PhabricatorProfileMenuEditEngine.php @@ -7,6 +7,7 @@ final class PhabricatorProfileMenuEditEngine private $menuEngine; private $profileObject; + private $customPHID; private $newMenuItemConfiguration; private $isBuiltin; @@ -32,6 +33,15 @@ final class PhabricatorProfileMenuEditEngine return $this->profileObject; } + public function setCustomPHID($custom_phid) { + $this->customPHID = $custom_phid; + return $this; + } + + public function getCustomPHID() { + return $this->customPHID; + } + public function setNewMenuItemConfiguration( PhabricatorProfileMenuItemConfiguration $configuration) { $this->newMenuItemConfiguration = $configuration; diff --git a/src/applications/search/engine/PhabricatorApplicationSearchEngine.php b/src/applications/search/engine/PhabricatorApplicationSearchEngine.php index 26a9e0f8e4..d5ca8b7a5e 100644 --- a/src/applications/search/engine/PhabricatorApplicationSearchEngine.php +++ b/src/applications/search/engine/PhabricatorApplicationSearchEngine.php @@ -908,7 +908,7 @@ abstract class PhabricatorApplicationSearchEngine extends Phobject { return array(); } - protected function getResultBucket(PhabricatorSavedQuery $saved) { + public function getResultBucket(PhabricatorSavedQuery $saved) { $key = $saved->getParameter('bucket'); if ($key == self::BUCKET_NONE) { return null; diff --git a/src/applications/search/engine/PhabricatorJumpNavHandler.php b/src/applications/search/engine/PhabricatorJumpNavHandler.php index 6c30af98dd..6f6ea3d0a9 100644 --- a/src/applications/search/engine/PhabricatorJumpNavHandler.php +++ b/src/applications/search/engine/PhabricatorJumpNavHandler.php @@ -6,7 +6,7 @@ final class PhabricatorJumpNavHandler extends Phobject { $jump = trim($jump); $patterns = array( - '/^a$/i' => 'uri:/audit/', + '/^a$/i' => 'uri:/diffusion/commit/', '/^f$/i' => 'uri:/feed/', '/^d$/i' => 'uri:/differential/', '/^r$/i' => 'uri:/diffusion/', diff --git a/src/applications/search/engine/PhabricatorProfileMenuEngine.php b/src/applications/search/engine/PhabricatorProfileMenuEngine.php index d930b31314..c1d4df3b2a 100644 --- a/src/applications/search/engine/PhabricatorProfileMenuEngine.php +++ b/src/applications/search/engine/PhabricatorProfileMenuEngine.php @@ -4,6 +4,7 @@ abstract class PhabricatorProfileMenuEngine extends Phobject { private $viewer; private $profileObject; + private $customPHID; private $items; private $defaultItem; private $controller; @@ -27,6 +28,15 @@ abstract class PhabricatorProfileMenuEngine extends Phobject { return $this->profileObject; } + public function setCustomPHID($custom_phid) { + $this->customPHID = $custom_phid; + return $this; + } + + public function getCustomPHID() { + return $this->customPHID; + } + public function setController(PhabricatorController $controller) { $this->controller = $controller; return $this; @@ -104,6 +114,16 @@ abstract class PhabricatorProfileMenuEngine extends Phobject { return new Aphront404Response(); } break; + case 'edit': + if (!$request->getURIData('id')) { + // If we continue along the "edit" pathway without an ID, we hit an + // unrelated exception because we can not build a new menu item out + // of thin air. For menus, new items are created via the "new" + // action. Just catch this case and 404 early since there's currently + // no clean way to make EditEngine aware of this. + return new Aphront404Response(); + } + break; } $navigation = $this->buildNavigation(); @@ -244,10 +264,18 @@ abstract class PhabricatorProfileMenuEngine extends Phobject { $items = $this->loadBuiltinProfileItems(); - $stored_items = id(new PhabricatorProfileMenuItemConfigurationQuery()) - ->setViewer($viewer) - ->withProfilePHIDs(array($object->getPHID())) - ->execute(); + if ($this->getCustomPHID()) { + $stored_items = id(new PhabricatorProfileMenuItemConfigurationQuery()) + ->setViewer($viewer) + ->withProfilePHIDs(array($object->getPHID())) + ->withCustomPHIDs(array($this->getCustomPHID())) + ->execute(); + } else { + $stored_items = id(new PhabricatorProfileMenuItemConfigurationQuery()) + ->setViewer($viewer) + ->withProfilePHIDs(array($object->getPHID())) + ->execute(); + } foreach ($stored_items as $stored_item) { $impl = $stored_item->getMenuItem(); @@ -720,9 +748,11 @@ abstract class PhabricatorProfileMenuEngine extends Phobject { return new Aphront404Response(); } + $custom_phid = $this->getCustomPHID(); $configuration = PhabricatorProfileMenuItemConfiguration::initializeNewItem( $object, - $item_type); + $item_type, + $custom_phid); $viewer = $this->getViewer(); @@ -737,6 +767,7 @@ abstract class PhabricatorProfileMenuEngine extends Phobject { ->setMenuEngine($this) ->setProfileObject($object) ->setNewMenuItemConfiguration($configuration) + ->setCustomPHID($custom_phid) ->setController($controller) ->buildResponse(); } @@ -750,6 +781,7 @@ abstract class PhabricatorProfileMenuEngine extends Phobject { ->setMenuEngine($this) ->setProfileObject($object) ->setController($controller) + ->setCustomPHID($this->getCustomPHID()) ->buildResponse(); } @@ -782,6 +814,7 @@ abstract class PhabricatorProfileMenuEngine extends Phobject { ->setProfileObject($object) ->setNewMenuItemConfiguration($configuration) ->setController($controller) + ->setCustomPHID($this->getCustomPHID()) ->buildResponse(); } diff --git a/src/applications/search/engine/PhabricatorSearchEngineAPIMethod.php b/src/applications/search/engine/PhabricatorSearchEngineAPIMethod.php index d050e48bff..3d13f2c3d9 100644 --- a/src/applications/search/engine/PhabricatorSearchEngineAPIMethod.php +++ b/src/applications/search/engine/PhabricatorSearchEngineAPIMethod.php @@ -32,17 +32,6 @@ abstract class PhabricatorSearchEngineAPIMethod return PhabricatorApplication::getByClass($class); } - public function getMethodStatus() { - return self::METHOD_STATUS_UNSTABLE; - } - - public function getMethodStatusDescription() { - return pht( - 'ApplicationSearch methods are fairly stable, but were introduced '. - 'relatively recently and may continue to evolve as more applications '. - 'adopt them.'); - } - final protected function defineParamTypes() { return array( 'queryKey' => 'optional string', diff --git a/src/applications/search/query/PhabricatorProfileMenuItemConfigurationQuery.php b/src/applications/search/query/PhabricatorProfileMenuItemConfigurationQuery.php index f8172638c3..eb09044d63 100644 --- a/src/applications/search/query/PhabricatorProfileMenuItemConfigurationQuery.php +++ b/src/applications/search/query/PhabricatorProfileMenuItemConfigurationQuery.php @@ -6,6 +6,7 @@ final class PhabricatorProfileMenuItemConfigurationQuery private $ids; private $phids; private $profilePHIDs; + private $customPHIDs; public function withIDs(array $ids) { $this->ids = $ids; @@ -22,6 +23,11 @@ final class PhabricatorProfileMenuItemConfigurationQuery return $this; } + public function withCustomPHIDs(array $phids) { + $this->customPHIDs = $phids; + return $this; + } + public function newResultObject() { return new PhabricatorProfileMenuItemConfiguration(); } @@ -54,6 +60,13 @@ final class PhabricatorProfileMenuItemConfigurationQuery $this->profilePHIDs); } + if ($this->customPHIDs !== null) { + $where[] = qsprintf( + $conn, + 'customPHID IN (%Ls)', + $this->customPHIDs); + } + return $where; } @@ -67,6 +80,7 @@ final class PhabricatorProfileMenuItemConfigurationQuery continue; } $item_type = clone $item_type; + $item_type->setViewer($this->getViewer()); $item->attachMenuItem($item_type); } diff --git a/src/applications/search/storage/PhabricatorProfileMenuItemConfiguration.php b/src/applications/search/storage/PhabricatorProfileMenuItemConfiguration.php index 3a9c0effc6..78fc380604 100644 --- a/src/applications/search/storage/PhabricatorProfileMenuItemConfiguration.php +++ b/src/applications/search/storage/PhabricatorProfileMenuItemConfiguration.php @@ -12,6 +12,7 @@ final class PhabricatorProfileMenuItemConfiguration protected $builtinKey; protected $menuItemOrder; protected $visibility; + protected $customPHID; protected $menuItemProperties = array(); private $profileObject = self::ATTACHABLE; @@ -33,13 +34,15 @@ final class PhabricatorProfileMenuItemConfiguration public static function initializeNewItem( $profile_object, - PhabricatorProfileMenuItem $item) { + PhabricatorProfileMenuItem $item, + $custom_phid) { return self::initializeNewBuiltin() ->setProfilePHID($profile_object->getPHID()) ->setMenuItemKey($item->getMenuItemKey()) ->attachMenuItem($item) - ->attachProfileObject($profile_object); + ->attachProfileObject($profile_object) + ->setCustomPHID($custom_phid); } protected function getConfiguration() { @@ -52,6 +55,7 @@ final class PhabricatorProfileMenuItemConfiguration 'menuItemKey' => 'text64', 'builtinKey' => 'text64?', 'menuItemOrder' => 'uint32?', + 'customPHID' => 'phid?', 'visibility' => 'text32', ), self::CONFIG_KEY_SCHEMA => array( diff --git a/src/applications/settings/controller/PhabricatorSettingsMainController.php b/src/applications/settings/controller/PhabricatorSettingsMainController.php index fada4a0937..841529a051 100644 --- a/src/applications/settings/controller/PhabricatorSettingsMainController.php +++ b/src/applications/settings/controller/PhabricatorSettingsMainController.php @@ -32,7 +32,7 @@ final class PhabricatorSettingsMainController // Redirect "/panel/XYZ/" to the viewer's personal settings panel. This // was the primary URI before global settings were introduced and allows - // generation of viewer-agnostic URIs for email. + // generation of viewer-agnostic URIs for email and logged-out users. $panel = $request->getURIData('panel'); if ($panel) { $panel = phutil_escape_uri($panel); diff --git a/src/applications/settings/panel/PhabricatorSettingsPanel.php b/src/applications/settings/panel/PhabricatorSettingsPanel.php index 7d86eaf243..eea48e540f 100644 --- a/src/applications/settings/panel/PhabricatorSettingsPanel.php +++ b/src/applications/settings/panel/PhabricatorSettingsPanel.php @@ -229,8 +229,15 @@ abstract class PhabricatorSettingsPanel extends Phobject { $user = $this->getUser(); if ($user) { - $username = $user->getUsername(); - return "/settings/user/{$username}/page/{$key}/{$path}"; + if ($user->isLoggedIn()) { + $username = $user->getUsername(); + return "/settings/user/{$username}/page/{$key}/{$path}"; + } else { + // For logged-out users, we can't put their username in the URI. This + // page will prompt them to login, then redirect them to the correct + // location. + return "/settings/panel/{$key}/"; + } } else { $builtin = $this->getPreferences()->getBuiltinKey(); return "/settings/builtin/{$builtin}/page/{$key}/{$path}"; diff --git a/src/applications/transactions/draft/PhabricatorBuiltinDraftEngine.php b/src/applications/transactions/draft/PhabricatorBuiltinDraftEngine.php new file mode 100644 index 0000000000..590ebea90c --- /dev/null +++ b/src/applications/transactions/draft/PhabricatorBuiltinDraftEngine.php @@ -0,0 +1,4 @@ +viewer = $viewer; + return $this; + } + + final public function getViewer() { + return $this->viewer; + } + + final public function setObject($object) { + $this->object = $object; + return $this; + } + + final public function getObject() { + return $this->object; + } + + final public function setVersionedDraft( + PhabricatorVersionedDraft $draft = null) { + $this->hasVersionedDraft = true; + $this->versionedDraft = $draft; + return $this; + } + + final public function getVersionedDraft() { + if (!$this->hasVersionedDraft) { + $draft = PhabricatorVersionedDraft::loadDraft( + $this->getObject()->getPHID(), + $this->getViewer()->getPHID()); + $this->setVersionedDraft($draft); + } + + return $this->versionedDraft; + } + + protected function hasVersionedDraftContent() { + $draft = $this->getVersionedDraft(); + if (!$draft) { + return false; + } + + if ($draft->getProperty('comment')) { + return true; + } + + if ($draft->getProperty('actions')) { + return true; + } + + return false; + } + + protected function hasCustomDraftContent() { + return false; + } + + final protected function hasAnyDraftContent() { + if ($this->hasVersionedDraftContent()) { + return true; + } + + if ($this->hasCustomDraftContent()) { + return true; + } + + return false; + } + + final public function synchronize() { + $object_phid = $this->getObject()->getPHID(); + $viewer_phid = $this->getViewer()->getPHID(); + + $has_draft = $this->hasAnyDraftContent(); + + $draft_type = PhabricatorObjectHasDraftEdgeType::EDGECONST; + $editor = id(new PhabricatorEdgeEditor()); + + if ($has_draft) { + $editor->addEdge($object_phid, $draft_type, $viewer_phid); + } else { + $editor->removeEdge($object_phid, $draft_type, $viewer_phid); + } + + $editor->save(); + } + +} diff --git a/src/applications/transactions/draft/PhabricatorDraftInterface.php b/src/applications/transactions/draft/PhabricatorDraftInterface.php new file mode 100644 index 0000000000..69b2edf8a0 --- /dev/null +++ b/src/applications/transactions/draft/PhabricatorDraftInterface.php @@ -0,0 +1,7 @@ +hideHeader; } + public function getProfileMenuItemDefault() { + return $this->getEngineKey().'/'.self::EDITENGINECONFIG_DEFAULT; + } + /* -( Managing Fields )---------------------------------------------------- */ @@ -1742,10 +1746,19 @@ abstract class PhabricatorEditEngine $viewer->getPHID(), $current_version); + $is_empty = (!strlen($comment_text) && !$actions); + $draft ->setProperty('comment', $comment_text) ->setProperty('actions', $actions) ->save(); + + $draft_engine = $this->newDraftEngine($object); + if ($draft_engine) { + $draft_engine + ->setVersionedDraft($draft) + ->synchronize(); + } } } @@ -1827,6 +1840,13 @@ abstract class PhabricatorEditEngine $object->getPHID(), $viewer->getPHID(), $this->loadDraftVersion($object)); + + $draft_engine = $this->newDraftEngine($object); + if ($draft_engine) { + $draft_engine + ->setVersionedDraft(null) + ->synchronize(); + } } if ($request->isAjax() && $is_preview) { @@ -1843,6 +1863,20 @@ abstract class PhabricatorEditEngine } } + protected function newDraftEngine($object) { + $viewer = $this->getViewer(); + + if ($object instanceof PhabricatorDraftInterface) { + $engine = $object->newDraftEngine(); + } else { + $engine = new PhabricatorBuiltinDraftEngine(); + } + + return $engine + ->setObject($object) + ->setViewer($viewer); + } + /* -( Conduit )------------------------------------------------------------ */ diff --git a/src/applications/transactions/editengine/PhabricatorEditEngineAPIMethod.php b/src/applications/transactions/editengine/PhabricatorEditEngineAPIMethod.php index de693a4049..3a6dd4b70c 100644 --- a/src/applications/transactions/editengine/PhabricatorEditEngineAPIMethod.php +++ b/src/applications/transactions/editengine/PhabricatorEditEngineAPIMethod.php @@ -11,17 +11,6 @@ abstract class PhabricatorEditEngineAPIMethod return PhabricatorApplication::getByClass($class); } - public function getMethodStatus() { - return self::METHOD_STATUS_UNSTABLE; - } - - public function getMethodStatusDescription() { - return pht( - 'ApplicationEditor methods are fairly stable, but were introduced '. - 'relatively recently and may continue to evolve as more applications '. - 'adopt them.'); - } - final protected function defineParamTypes() { return array( 'transactions' => 'list>', diff --git a/src/applications/transactions/storage/PhabricatorModularTransaction.php b/src/applications/transactions/storage/PhabricatorModularTransaction.php index 035a2585fd..396478c98b 100644 --- a/src/applications/transactions/storage/PhabricatorModularTransaction.php +++ b/src/applications/transactions/storage/PhabricatorModularTransaction.php @@ -110,6 +110,24 @@ abstract class PhabricatorModularTransaction return parent::getTitle(); } + /* final */ public function getActionName() { + $action = $this->getTransactionImplementation()->getActionName(); + if ($action !== null) { + return $action; + } + + return parent::getActionName(); + } + + /* final */ public function getActionStrength() { + $strength = $this->getTransactionImplementation()->getActionStrength(); + if ($strength !== null) { + return $strength; + } + + return parent::getActionStrength(); + } + public function getTitleForMail() { $old_target = $this->getRenderingTarget(); $new_target = self::TARGET_TEXT; diff --git a/src/applications/transactions/storage/PhabricatorModularTransactionType.php b/src/applications/transactions/storage/PhabricatorModularTransactionType.php index 2606264778..f57d91b08f 100644 --- a/src/applications/transactions/storage/PhabricatorModularTransactionType.php +++ b/src/applications/transactions/storage/PhabricatorModularTransactionType.php @@ -59,6 +59,14 @@ abstract class PhabricatorModularTransactionType return null; } + public function getActionName() { + return null; + } + + public function getActionStrength() { + return null; + } + public function getColor() { return null; } diff --git a/src/docs/book/phabricator.book b/src/docs/book/phabricator.book index 2b362853b4..2beef23023 100644 --- a/src/docs/book/phabricator.book +++ b/src/docs/book/phabricator.book @@ -37,10 +37,6 @@ "name": "Arcanist Integration", "include": "(^src/applications/arcanist/)" }, - "audit": { - "name": "Audit", - "include": "(^src/applications/audit/)" - }, "auth": { "name": "Auth", "include": "(^src/applications/auth/)" diff --git a/src/docs/user/cluster/cluster_repositories.diviner b/src/docs/user/cluster/cluster_repositories.diviner index e7e8031677..21aba731cc 100644 --- a/src/docs/user/cluster/cluster_repositories.diviner +++ b/src/docs/user/cluster/cluster_repositories.diviner @@ -37,7 +37,7 @@ Before responding to a write, replicas obtain a global lock, perform the same version check and fetch if necessary, then allow the write to continue. Additionally, repositories passively check other nodes for updates and -replicate changes in the background. After you push a change to a repositroy, +replicate changes in the background. After you push a change to a repository, it will usually spread passively to all other repository nodes within a few minutes. @@ -381,8 +381,8 @@ this: Here, all of the "leader" devices with the most up-to-date copy of the repository have been lost. Phabricator will freeze the repository refuse to -serve requests because it can not serve it consistently, and can not accept new -writes without data loss. +serve requests because it can not serve reads consistently and can not accept +new writes without data loss. The most straightforward way to resolve this issue is to restore any leader to service. The change will be able to replicate to other devices once a leader @@ -422,17 +422,22 @@ Ambiguous Leaders ================= Repository clusters can also freeze if the leader devices are ambiguous. This -can happen if you replace an entire cluster with new devices suddenly, or -make a mistake with the `--demote` flag. This generally arises from some kind -of operator error, like this: +can happen if you replace an entire cluster with new devices suddenly, or make +a mistake with the `--demote` flag. This may arise from some kind of operator +error, like these: - Someone accidentally uses `bin/repository thaw ... --demote` to demote every device in a cluster. - Someone accidentally deletes all the version information for a repository from the database by making a mistake with a `DELETE` or `UPDATE` query. - - Someone accidentally disable all of the devices in a cluster, then add + - Someone accidentally disables all of the devices in a cluster, then adds entirely new ones before repositories can propagate. +If you are moving repositories into cluster services, you can also reach this +state if you use `clusterize` to associate a repository with a service that is +bound to multiple active devices. In this case, Phabricator will not know which +device or devices have up-to-date information. + When Phabricator can not tell which device in a cluster is a leader, it freezes the cluster because it is possible that some devices have less data and others have more, and if it choses a leader arbitrarily it may destroy some data diff --git a/src/docs/user/installation_guide.diviner b/src/docs/user/installation_guide.diviner index 749ca9a63d..8feb8ae1d0 100644 --- a/src/docs/user/installation_guide.diviner +++ b/src/docs/user/installation_guide.diviner @@ -70,8 +70,7 @@ Beyond an operating system, you will need **a webserver**. You will also need: - **MySQL**: You need MySQL. We strongly recommend MySQL 5.5 or newer. - - **PHP**: You need PHP 5.2 or newer, but note that PHP 7 is - **not supported**. + - **PHP**: You need PHP 5.2 or newer. You'll probably also need a **domain name**. In particular, you should read this note: diff --git a/src/docs/user/userguide/audit.diviner b/src/docs/user/userguide/audit.diviner index 9c2f752b08..2ebc26a687 100644 --- a/src/docs/user/userguide/audit.diviner +++ b/src/docs/user/userguide/audit.diviner @@ -23,8 +23,8 @@ track of two things: - **Audit Requests** which ask a user (or some other entity) to audit a commit. These can be triggered in a number of ways (see below). -In the Audit tool's home screen (at `/audit/`) and on the homepage you can see -commits and requests that require your action: +In the Audit tool's home screen and on the homepage you can see commits and +requests that require your action: - **Required Audits** are open audit requests that require you, a project you are a member of, or a package you own to audit a commit. An audit diff --git a/src/infrastructure/diff/view/PHUIDiffInlineCommentPreviewListView.php b/src/infrastructure/diff/view/PHUIDiffInlineCommentPreviewListView.php index 31e6b2b087..4bede1603b 100644 --- a/src/infrastructure/diff/view/PHUIDiffInlineCommentPreviewListView.php +++ b/src/infrastructure/diff/view/PHUIDiffInlineCommentPreviewListView.php @@ -38,8 +38,15 @@ final class PHUIDiffInlineCommentPreviewListView $inlines = $this->getInlineComments(); foreach ($inlines as $key => $inline) { - $inlines[$key] = DifferentialInlineComment::newFromModernComment( - $inline); + // TODO: This is real, real gross. + + if ($inline instanceof DifferentialTransactionComment) { + $inlines[$key] = DifferentialInlineComment::newFromModernComment( + $inline); + } else { + $inlines[$key] = PhabricatorAuditInlineComment::newFromModernComment( + $inline); + } } $engine = new PhabricatorMarkupEngine(); diff --git a/src/view/AphrontTagView.php b/src/view/AphrontTagView.php index a6eb722383..a055fb16ad 100644 --- a/src/view/AphrontTagView.php +++ b/src/view/AphrontTagView.php @@ -92,6 +92,15 @@ abstract class AphrontTagView extends AphrontView { final public function render() { $this->willRender(); + // A tag view may render no tag at all. For example, the HandleListView is + // a container which renders a tag in HTML mode, but can also render in + // text mode without producing a tag. When a tag view has no tag name, just + // return the tag content as though the view did not exist. + $tag_name = $this->getTagName(); + if ($tag_name === null) { + return $this->getTagContent(); + } + $attributes = $this->getTagAttributes(); $implode = array('class', 'sigil'); @@ -147,7 +156,7 @@ abstract class AphrontTagView extends AphrontView { } return javelin_tag( - $this->getTagName(), + $tag_name, $attributes, $this->getTagContent()); } diff --git a/webroot/rsrc/css/application/differential/changeset-view.css b/webroot/rsrc/css/application/differential/changeset-view.css index ae01add690..1bd7d48635 100644 --- a/webroot/rsrc/css/application/differential/changeset-view.css +++ b/webroot/rsrc/css/application/differential/changeset-view.css @@ -122,23 +122,19 @@ } .differential-diff th.old { - background: #ffdddd; - border-right-color: #f8cbcb; + border-right-color: {$old-bright}; } .differential-diff th.new { - background: #dbffdb; - border-right-color: #a6f3a6; + border-right-color: {$new-bright}; } -.differential-diff td.old, -.differential-diff td.old-full { - background: #ffecec; +.differential-diff td.old { + background: {$old-background}; } -.differential-diff td.new, -.differential-diff td.new-full { - background: #eaffea; +.differential-diff td.new { + background: {$new-background}; } .differential-diff td.old-rebase { @@ -150,13 +146,15 @@ } .differential-diff td.old span.bright, +.differential-diff td.old-full, .prose-diff span.old { - background: #f8cbcb; + background: {$old-bright}; } .differential-diff td.new span.bright, +.differential-diff td.new-full, .prose-diff span.new { - background: #a6f3a6; + background: {$new-bright}; } .differential-diff td.copy { @@ -167,12 +165,12 @@ .differential-diff td.new-copy, .differential-diff td.new-copy span.bright { - background: {$lightyellow}; + background: {$copy-background}; } .differential-diff td.new-move, .differential-diff td.new-move span.bright { - background: {$yellow}; + background: {$move-background}; } .differential-diff td.comment { diff --git a/webroot/rsrc/js/application/differential/behavior-add-reviewers-and-ccs.js b/webroot/rsrc/js/application/differential/behavior-add-reviewers-and-ccs.js deleted file mode 100644 index 8f6a031d50..0000000000 --- a/webroot/rsrc/js/application/differential/behavior-add-reviewers-and-ccs.js +++ /dev/null @@ -1,47 +0,0 @@ -/** - * @provides javelin-behavior-differential-add-reviewers-and-ccs - * @requires javelin-behavior - * javelin-dom - * phabricator-prefab - */ - -JX.behavior('differential-add-reviewers-and-ccs', function(config) { - - var dynamic = {}; - for (var k in config.dynamic) { - var props = config.dynamic[k]; - props.id = k; - - var tokenizer = JX.Prefab.buildTokenizer(props).tokenizer; - tokenizer.start(); - - dynamic[k] = { - row : JX.$(props.row), - tokenizer : tokenizer, - actions : props.actions, - labels: props.labels - }; - } - - JX.DOM.listen( - JX.$(config.select), - 'change', - null, - function() { - var v = JX.$(config.select).value; - for (var k in dynamic) { - if (dynamic[k].actions[v]) { - JX.DOM.show(dynamic[k].row); - if (dynamic[k].labels) { - var label_node = JX.DOM.find(dynamic[k].row, 'label'); - if (label_node) { - JX.DOM.setContent(label_node, dynamic[k].labels[v]); - } - } - dynamic[k].tokenizer.refresh(); - } else { - JX.DOM.hide(dynamic[k].row); - } - } - }); -}); diff --git a/webroot/rsrc/js/application/differential/behavior-keyboard-nav.js b/webroot/rsrc/js/application/differential/behavior-keyboard-nav.js index da97d908b2..f48df4d3dc 100644 --- a/webroot/rsrc/js/application/differential/behavior-keyboard-nav.js +++ b/webroot/rsrc/js/application/differential/behavior-keyboard-nav.js @@ -173,16 +173,6 @@ JX.behavior('differential-keyboard-navigation', function(config) { refreshFocus(); }); - var haunt_mode = 0; - function haunt() { - haunt_mode = (haunt_mode + 1) % 3; - - var el = JX.$(config.haunt); - for (var ii = 1; ii <= 2; ii++) { - JX.DOM.alterClass(el, 'differential-haunt-mode-'+ii, (haunt_mode == ii)); - } - } - new JX.KeyboardShortcut('j', 'Jump to next change.') .setHandler(function(manager) { jump(manager, 1); @@ -271,10 +261,4 @@ JX.behavior('differential-keyboard-navigation', function(config) { }) .register(); - if (config.haunt) { - new JX.KeyboardShortcut('z', 'Cycle comment panel haunting modes.') - .setHandler(haunt) - .register(); - } - });