From 2e5ac128b3ee953fb0287db83d9fc81ed19ff9d3 Mon Sep 17 00:00:00 2001 From: epriestley Date: Fri, 27 Sep 2013 08:43:41 -0700 Subject: [PATCH] Explain policy exception rules to users Summary: Ref T603. Adds clarifying text which expands on policies and explains exceptions and rules. The goal is to provide an easy way for users to learn about special policy rules, like "task owners can always see a task". This presentation might be a little aggressive. That's probably OK as we introduce policies, but something a little more tempered might be better down the road. Test Plan: See screenshot. Reviewers: btrahan, chad Reviewed By: chad CC: aran Maniphest Tasks: T603 Differential Revision: https://secure.phabricator.com/D7150 --- src/__celerity_resource_map__.php | 90 +++++++++---------- src/__phutil_library_map__.php | 6 ++ .../storage/PhabricatorAuthProviderConfig.php | 4 + .../storage/PhabricatorChatLogChannel.php | 4 + .../storage/PhabricatorChatLogEvent.php | 4 + .../conduit/method/ConduitAPIMethod.php | 4 + .../PhabricatorConduitMethodCallLog.php | 4 + .../config/storage/PhabricatorConfigEntry.php | 4 + .../conpherence/storage/ConpherenceThread.php | 4 + .../storage/PhabricatorCountdown.php | 4 + .../daemon/storage/PhabricatorDaemonLog.php | 4 + .../differential/storage/DifferentialDiff.php | 8 ++ .../storage/DifferentialRevision.php | 20 +++++ .../diviner/storage/DivinerLiveBook.php | 4 + .../diviner/storage/DivinerLiveSymbol.php | 4 + .../storage/DoorkeeperExternalObject.php | 4 + .../feed/story/PhabricatorFeedStory.php | 4 + .../files/storage/PhabricatorFile.php | 4 + .../flag/storage/PhabricatorFlag.php | 4 + .../herald/storage/HeraldRule.php | 6 ++ .../legalpad/storage/LegalpadDocument.php | 6 ++ .../storage/PhabricatorFileImageMacro.php | 4 + .../storage/PhabricatorMetaMTAMailingList.php | 4 + .../maniphest/storage/ManiphestTask.php | 5 ++ .../storage/PhabricatorOwnersPackage.php | 4 + .../paste/storage/PhabricatorPaste.php | 5 ++ .../storage/PhabricatorExternalAccount.php | 5 ++ .../people/storage/PhabricatorUser.php | 9 ++ src/applications/phame/storage/PhameBlog.php | 14 +++ src/applications/phame/storage/PhamePost.php | 6 ++ .../phid/PhabricatorObjectHandle.php | 4 + .../phlux/storage/PhluxVariable.php | 4 + .../pholio/storage/PholioImage.php | 4 + .../pholio/storage/PholioMock.php | 4 + .../phortune/storage/PhortuneAccount.php | 7 +- .../storage/PhortunePaymentMethod.php | 5 ++ .../phortune/storage/PhortuneProduct.php | 4 + .../phriction/storage/PhrictionDocument.php | 8 ++ .../__tests__/PhabricatorPolicyTestObject.php | 4 + .../PhabricatorApplicationPolicy.php | 23 +++++ .../PhabricatorPolicyController.php | 5 ++ .../PhabricatorPolicyExplainController.php | 73 +++++++++++++++ .../policy/filter/PhabricatorPolicy.php | 29 ++++++ .../interface/PhabricatorPolicyInterface.php | 28 ++++++ .../policy/query/PhabricatorPolicyQuery.php | 23 +++-- .../ponder/storage/PonderAnswer.php | 16 ++++ .../ponder/storage/PonderQuestion.php | 6 ++ .../project/storage/PhabricatorProject.php | 10 +++ .../releeph/storage/ReleephBranch.php | 5 ++ .../releeph/storage/ReleephProject.php | 5 ++ .../releeph/storage/ReleephRequest.php | 5 ++ .../storage/PhabricatorRepository.php | 5 ++ .../PhabricatorRepositoryArcanistProject.php | 4 + .../storage/PhabricatorRepositoryCommit.php | 6 ++ .../search/storage/PhabricatorNamedQuery.php | 7 ++ .../search/storage/PhabricatorSavedQuery.php | 4 + .../storage/PhabricatorSlowvotePoll.php | 6 ++ .../tokens/storage/PhabricatorToken.php | 4 + .../tokens/storage/PhabricatorTokenGiven.php | 13 +++ .../PhabricatorApplicationTransaction.php | 6 ++ ...abricatorApplicationTransactionComment.php | 5 ++ src/view/phui/PHUIHeaderView.php | 32 +++++-- webroot/rsrc/css/aphront/dialog-view.css | 5 ++ 63 files changed, 565 insertions(+), 56 deletions(-) create mode 100644 src/applications/policy/application/PhabricatorApplicationPolicy.php create mode 100644 src/applications/policy/controller/PhabricatorPolicyController.php create mode 100644 src/applications/policy/controller/PhabricatorPolicyExplainController.php diff --git a/src/__celerity_resource_map__.php b/src/__celerity_resource_map__.php index d9ba87c6b4..e663d3ac1d 100644 --- a/src/__celerity_resource_map__.php +++ b/src/__celerity_resource_map__.php @@ -836,7 +836,7 @@ celerity_register_resource_map(array( ), 'aphront-dialog-view-css' => array( - 'uri' => '/res/337bd2a9/rsrc/css/aphront/dialog-view.css', + 'uri' => '/res/609ccc78/rsrc/css/aphront/dialog-view.css', 'type' => 'css', 'requires' => array( @@ -1176,7 +1176,7 @@ celerity_register_resource_map(array( ), 'herald-rule-editor' => array( - 'uri' => '/res/f8ee0e9c/rsrc/js/application/herald/HeraldRuleEditor.js', + 'uri' => '/res/36222dde/rsrc/js/application/herald/HeraldRuleEditor.js', 'type' => 'js', 'requires' => array( @@ -4162,7 +4162,7 @@ celerity_register_resource_map(array( ), array( 'packages' => array( - '15affac5' => + 'de5898ae' => array( 'name' => 'core.pkg.css', 'symbols' => @@ -4209,7 +4209,7 @@ celerity_register_resource_map(array( 39 => 'phabricator-property-list-view-css', 40 => 'phabricator-tag-view-css', ), - 'uri' => '/res/pkg/15affac5/core.pkg.css', + 'uri' => '/res/pkg/de5898ae/core.pkg.css', 'type' => 'css', ), '8977e356' => @@ -4399,15 +4399,15 @@ celerity_register_resource_map(array( ), 'reverse' => array( - 'aphront-dialog-view-css' => '15affac5', - 'aphront-error-view-css' => '15affac5', - 'aphront-list-filter-view-css' => '15affac5', - 'aphront-pager-view-css' => '15affac5', - 'aphront-panel-view-css' => '15affac5', - 'aphront-table-view-css' => '15affac5', - 'aphront-tokenizer-control-css' => '15affac5', - 'aphront-tooltip-css' => '15affac5', - 'aphront-typeahead-control-css' => '15affac5', + 'aphront-dialog-view-css' => 'de5898ae', + 'aphront-error-view-css' => 'de5898ae', + 'aphront-list-filter-view-css' => 'de5898ae', + 'aphront-pager-view-css' => 'de5898ae', + 'aphront-panel-view-css' => 'de5898ae', + 'aphront-table-view-css' => 'de5898ae', + 'aphront-tokenizer-control-css' => 'de5898ae', + 'aphront-tooltip-css' => 'de5898ae', + 'aphront-typeahead-control-css' => 'de5898ae', 'differential-changeset-view-css' => '44bfe40c', 'differential-core-view-css' => '44bfe40c', 'differential-inline-comment-editor' => '5e9e5c4e', @@ -4421,7 +4421,7 @@ celerity_register_resource_map(array( 'differential-table-of-contents-css' => '44bfe40c', 'diffusion-commit-view-css' => 'c8ce2d88', 'diffusion-icons-css' => 'c8ce2d88', - 'global-drag-and-drop-css' => '15affac5', + 'global-drag-and-drop-css' => 'de5898ae', 'inline-comment-summary-css' => '44bfe40c', 'javelin-aphlict' => '8977e356', 'javelin-behavior' => '9564fa17', @@ -4494,54 +4494,54 @@ celerity_register_resource_map(array( 'javelin-util' => '9564fa17', 'javelin-vector' => '9564fa17', 'javelin-workflow' => '9564fa17', - 'lightbox-attachment-css' => '15affac5', + 'lightbox-attachment-css' => 'de5898ae', 'maniphest-task-summary-css' => '49898640', - 'phabricator-action-list-view-css' => '15affac5', - 'phabricator-application-launch-view-css' => '15affac5', + 'phabricator-action-list-view-css' => 'de5898ae', + 'phabricator-application-launch-view-css' => 'de5898ae', 'phabricator-busy' => '8977e356', 'phabricator-content-source-view-css' => '44bfe40c', - 'phabricator-core-css' => '15affac5', - 'phabricator-crumbs-view-css' => '15affac5', + 'phabricator-core-css' => 'de5898ae', + 'phabricator-crumbs-view-css' => 'de5898ae', 'phabricator-drag-and-drop-file-upload' => '5e9e5c4e', 'phabricator-dropdown-menu' => '8977e356', 'phabricator-file-upload' => '8977e356', - 'phabricator-filetree-view-css' => '15affac5', - 'phabricator-flag-css' => '15affac5', + 'phabricator-filetree-view-css' => 'de5898ae', + 'phabricator-flag-css' => 'de5898ae', 'phabricator-hovercard' => '8977e356', - 'phabricator-jump-nav' => '15affac5', + 'phabricator-jump-nav' => 'de5898ae', 'phabricator-keyboard-shortcut' => '8977e356', 'phabricator-keyboard-shortcut-manager' => '8977e356', - 'phabricator-main-menu-view' => '15affac5', + 'phabricator-main-menu-view' => 'de5898ae', 'phabricator-menu-item' => '8977e356', - 'phabricator-nav-view-css' => '15affac5', + 'phabricator-nav-view-css' => 'de5898ae', 'phabricator-notification' => '8977e356', - 'phabricator-notification-css' => '15affac5', - 'phabricator-notification-menu-css' => '15affac5', + 'phabricator-notification-css' => 'de5898ae', + 'phabricator-notification-menu-css' => 'de5898ae', 'phabricator-object-selector-css' => '44bfe40c', 'phabricator-phtize' => '8977e356', 'phabricator-prefab' => '8977e356', 'phabricator-project-tag-css' => '49898640', - 'phabricator-property-list-view-css' => '15affac5', - 'phabricator-remarkup-css' => '15affac5', + 'phabricator-property-list-view-css' => 'de5898ae', + 'phabricator-remarkup-css' => 'de5898ae', 'phabricator-shaped-request' => '5e9e5c4e', - 'phabricator-side-menu-view-css' => '15affac5', - 'phabricator-standard-page-view' => '15affac5', - 'phabricator-tag-view-css' => '15affac5', + 'phabricator-side-menu-view-css' => 'de5898ae', + 'phabricator-standard-page-view' => 'de5898ae', + 'phabricator-tag-view-css' => 'de5898ae', 'phabricator-textareautils' => '8977e356', 'phabricator-tooltip' => '8977e356', - 'phabricator-transaction-view-css' => '15affac5', - 'phabricator-zindex-css' => '15affac5', - 'phui-button-css' => '15affac5', - 'phui-form-css' => '15affac5', - 'phui-form-view-css' => '15affac5', - 'phui-header-view-css' => '15affac5', - 'phui-icon-view-css' => '15affac5', - 'phui-object-item-list-view-css' => '15affac5', - 'phui-spacing-css' => '15affac5', - 'sprite-apps-large-css' => '15affac5', - 'sprite-gradient-css' => '15affac5', - 'sprite-icons-css' => '15affac5', - 'sprite-menu-css' => '15affac5', - 'syntax-highlighting-css' => '15affac5', + 'phabricator-transaction-view-css' => 'de5898ae', + 'phabricator-zindex-css' => 'de5898ae', + 'phui-button-css' => 'de5898ae', + 'phui-form-css' => 'de5898ae', + 'phui-form-view-css' => 'de5898ae', + 'phui-header-view-css' => 'de5898ae', + 'phui-icon-view-css' => 'de5898ae', + 'phui-object-item-list-view-css' => 'de5898ae', + 'phui-spacing-css' => 'de5898ae', + 'sprite-apps-large-css' => 'de5898ae', + 'sprite-gradient-css' => 'de5898ae', + 'sprite-icons-css' => 'de5898ae', + 'sprite-menu-css' => 'de5898ae', + 'syntax-highlighting-css' => 'de5898ae', ), )); diff --git a/src/__phutil_library_map__.php b/src/__phutil_library_map__.php index 881a225ae5..d40f9ff77a 100644 --- a/src/__phutil_library_map__.php +++ b/src/__phutil_library_map__.php @@ -846,6 +846,7 @@ phutil_register_library_map(array( 'PhabricatorApplicationPhortune' => 'applications/phortune/application/PhabricatorApplicationPhortune.php', 'PhabricatorApplicationPhrequent' => 'applications/phrequent/application/PhabricatorApplicationPhrequent.php', 'PhabricatorApplicationPhriction' => 'applications/phriction/application/PhabricatorApplicationPhriction.php', + 'PhabricatorApplicationPolicy' => 'applications/policy/application/PhabricatorApplicationPolicy.php', 'PhabricatorApplicationPonder' => 'applications/ponder/application/PhabricatorApplicationPonder.php', 'PhabricatorApplicationProject' => 'applications/project/application/PhabricatorApplicationProject.php', 'PhabricatorApplicationReleeph' => 'applications/releeph/application/PhabricatorApplicationReleeph.php', @@ -1459,8 +1460,10 @@ phutil_register_library_map(array( 'PhabricatorPolicyCapability' => 'applications/policy/constants/PhabricatorPolicyCapability.php', 'PhabricatorPolicyConfigOptions' => 'applications/config/option/PhabricatorPolicyConfigOptions.php', 'PhabricatorPolicyConstants' => 'applications/policy/constants/PhabricatorPolicyConstants.php', + 'PhabricatorPolicyController' => 'applications/policy/controller/PhabricatorPolicyController.php', 'PhabricatorPolicyDataTestCase' => 'applications/policy/__tests__/PhabricatorPolicyDataTestCase.php', 'PhabricatorPolicyException' => 'applications/policy/exception/PhabricatorPolicyException.php', + 'PhabricatorPolicyExplainController' => 'applications/policy/controller/PhabricatorPolicyExplainController.php', 'PhabricatorPolicyFilter' => 'applications/policy/filter/PhabricatorPolicyFilter.php', 'PhabricatorPolicyInterface' => 'applications/policy/interface/PhabricatorPolicyInterface.php', 'PhabricatorPolicyQuery' => 'applications/policy/query/PhabricatorPolicyQuery.php', @@ -2941,6 +2944,7 @@ phutil_register_library_map(array( 'PhabricatorApplicationPhortune' => 'PhabricatorApplication', 'PhabricatorApplicationPhrequent' => 'PhabricatorApplication', 'PhabricatorApplicationPhriction' => 'PhabricatorApplication', + 'PhabricatorApplicationPolicy' => 'PhabricatorApplication', 'PhabricatorApplicationPonder' => 'PhabricatorApplication', 'PhabricatorApplicationProject' => 'PhabricatorApplication', 'PhabricatorApplicationReleeph' => 'PhabricatorApplication', @@ -3612,8 +3616,10 @@ phutil_register_library_map(array( 'PhabricatorPolicyAwareTestQuery' => 'PhabricatorPolicyAwareQuery', 'PhabricatorPolicyCapability' => 'PhabricatorPolicyConstants', 'PhabricatorPolicyConfigOptions' => 'PhabricatorApplicationConfigOptions', + 'PhabricatorPolicyController' => 'PhabricatorController', 'PhabricatorPolicyDataTestCase' => 'PhabricatorTestCase', 'PhabricatorPolicyException' => 'Exception', + 'PhabricatorPolicyExplainController' => 'PhabricatorPolicyController', 'PhabricatorPolicyQuery' => 'PhabricatorQuery', 'PhabricatorPolicyTestCase' => 'PhabricatorTestCase', 'PhabricatorPolicyTestObject' => 'PhabricatorPolicyInterface', diff --git a/src/applications/auth/storage/PhabricatorAuthProviderConfig.php b/src/applications/auth/storage/PhabricatorAuthProviderConfig.php index b4595d96d0..4678e2b170 100644 --- a/src/applications/auth/storage/PhabricatorAuthProviderConfig.php +++ b/src/applications/auth/storage/PhabricatorAuthProviderConfig.php @@ -81,4 +81,8 @@ final class PhabricatorAuthProviderConfig extends PhabricatorAuthDAO return false; } + public function describeAutomaticCapability($capability) { + return null; + } + } diff --git a/src/applications/chatlog/storage/PhabricatorChatLogChannel.php b/src/applications/chatlog/storage/PhabricatorChatLogChannel.php index 33c6ab09fa..e9ea837379 100644 --- a/src/applications/chatlog/storage/PhabricatorChatLogChannel.php +++ b/src/applications/chatlog/storage/PhabricatorChatLogChannel.php @@ -32,5 +32,9 @@ final class PhabricatorChatLogChannel return false; } + public function describeAutomaticCapability($capability) { + return null; + } + } diff --git a/src/applications/chatlog/storage/PhabricatorChatLogEvent.php b/src/applications/chatlog/storage/PhabricatorChatLogEvent.php index 94d641a4e9..c889fd19fe 100644 --- a/src/applications/chatlog/storage/PhabricatorChatLogEvent.php +++ b/src/applications/chatlog/storage/PhabricatorChatLogEvent.php @@ -28,6 +28,10 @@ final class PhabricatorChatLogEvent return false; } + public function describeAutomaticCapability($capability) { + return null; + } + public function getConfiguration() { return array( self::CONFIG_TIMESTAMPS => false, diff --git a/src/applications/conduit/method/ConduitAPIMethod.php b/src/applications/conduit/method/ConduitAPIMethod.php index da5c6d7b01..f9537c48da 100644 --- a/src/applications/conduit/method/ConduitAPIMethod.php +++ b/src/applications/conduit/method/ConduitAPIMethod.php @@ -183,4 +183,8 @@ abstract class ConduitAPIMethod return true; } + public function describeAutomaticCapability($capability) { + return null; + } + } diff --git a/src/applications/conduit/storage/PhabricatorConduitMethodCallLog.php b/src/applications/conduit/storage/PhabricatorConduitMethodCallLog.php index 8edbaac7b9..7a2c36277c 100644 --- a/src/applications/conduit/storage/PhabricatorConduitMethodCallLog.php +++ b/src/applications/conduit/storage/PhabricatorConduitMethodCallLog.php @@ -30,4 +30,8 @@ final class PhabricatorConduitMethodCallLog extends PhabricatorConduitDAO return false; } + public function describeAutomaticCapability($capability) { + return null; + } + } diff --git a/src/applications/config/storage/PhabricatorConfigEntry.php b/src/applications/config/storage/PhabricatorConfigEntry.php index 86d01a37df..a25c7dfec6 100644 --- a/src/applications/config/storage/PhabricatorConfigEntry.php +++ b/src/applications/config/storage/PhabricatorConfigEntry.php @@ -59,4 +59,8 @@ final class PhabricatorConfigEntry extends PhabricatorConfigEntryDAO return false; } + public function describeAutomaticCapability($capability) { + return null; + } + } diff --git a/src/applications/conpherence/storage/ConpherenceThread.php b/src/applications/conpherence/storage/ConpherenceThread.php index e5657c9232..fd4188e070 100644 --- a/src/applications/conpherence/storage/ConpherenceThread.php +++ b/src/applications/conpherence/storage/ConpherenceThread.php @@ -189,4 +189,8 @@ final class ConpherenceThread extends ConpherenceDAO return isset($participants[$user->getPHID()]); } + public function describeAutomaticCapability($capability) { + return pht("Participants in a thread can always view and edit it."); + } + } diff --git a/src/applications/countdown/storage/PhabricatorCountdown.php b/src/applications/countdown/storage/PhabricatorCountdown.php index ea4999d79a..551c2c848c 100644 --- a/src/applications/countdown/storage/PhabricatorCountdown.php +++ b/src/applications/countdown/storage/PhabricatorCountdown.php @@ -53,4 +53,8 @@ final class PhabricatorCountdown return ($viewer->getPHID() == $this->getAuthorPHID()); } + public function describeAutomaticCapability($capability) { + return pht('The author of a countdown can always view and edit it.'); + } + } diff --git a/src/applications/daemon/storage/PhabricatorDaemonLog.php b/src/applications/daemon/storage/PhabricatorDaemonLog.php index b836d9ae81..f951df99cc 100644 --- a/src/applications/daemon/storage/PhabricatorDaemonLog.php +++ b/src/applications/daemon/storage/PhabricatorDaemonLog.php @@ -41,4 +41,8 @@ final class PhabricatorDaemonLog extends PhabricatorDaemonDAO return false; } + public function describeAutomaticCapability($capability) { + return null; + } + } diff --git a/src/applications/differential/storage/DifferentialDiff.php b/src/applications/differential/storage/DifferentialDiff.php index 32cded90fe..9e0a36dce9 100644 --- a/src/applications/differential/storage/DifferentialDiff.php +++ b/src/applications/differential/storage/DifferentialDiff.php @@ -320,4 +320,12 @@ final class DifferentialDiff return false; } + public function describeAutomaticCapability($capability) { + if ($this->getRevision()) { + return pht( + 'This diff is attached to a revision, and inherits its policies.'); + } + return null; + } + } diff --git a/src/applications/differential/storage/DifferentialRevision.php b/src/applications/differential/storage/DifferentialRevision.php index ae768b8a6a..ba9e7a0383 100644 --- a/src/applications/differential/storage/DifferentialRevision.php +++ b/src/applications/differential/storage/DifferentialRevision.php @@ -329,6 +329,26 @@ final class DifferentialRevision extends DifferentialDAO return false; } + public function describeAutomaticCapability($capability) { + $description = array( + pht('The owner of a revision can always view and edit it.'), + ); + + switch ($capability) { + case PhabricatorPolicyCapability::CAN_VIEW: + $description[] = pht( + "A revision's reviewers can always view it."); + if ($this->getRepository()) { + $description[] = pht( + 'This revision belongs to a repository. Other users must be able '. + 'to view the repository in order to view this revision.'); + } + break; + } + + return $description; + } + public function getUsersToNotifyOfTokenGiven() { return array( $this->getAuthorPHID(), diff --git a/src/applications/diviner/storage/DivinerLiveBook.php b/src/applications/diviner/storage/DivinerLiveBook.php index cdf8f10637..70affac00c 100644 --- a/src/applications/diviner/storage/DivinerLiveBook.php +++ b/src/applications/diviner/storage/DivinerLiveBook.php @@ -61,4 +61,8 @@ final class DivinerLiveBook extends DivinerDAO return false; } + public function describeAutomaticCapability($capability) { + return null; + } + } diff --git a/src/applications/diviner/storage/DivinerLiveSymbol.php b/src/applications/diviner/storage/DivinerLiveSymbol.php index 3ca6cc0798..f2de0d0974 100644 --- a/src/applications/diviner/storage/DivinerLiveSymbol.php +++ b/src/applications/diviner/storage/DivinerLiveSymbol.php @@ -143,6 +143,10 @@ final class DivinerLiveSymbol extends DivinerDAO return $this->getBook()->hasAutomaticCapability($capability, $viewer); } + public function describeAutomaticCapability($capability) { + return pht('Atoms inherit the policies of the books they are part of.'); + } + /* -( Markup Interface )--------------------------------------------------- */ diff --git a/src/applications/doorkeeper/storage/DoorkeeperExternalObject.php b/src/applications/doorkeeper/storage/DoorkeeperExternalObject.php index af46a3e543..0b1934d360 100644 --- a/src/applications/doorkeeper/storage/DoorkeeperExternalObject.php +++ b/src/applications/doorkeeper/storage/DoorkeeperExternalObject.php @@ -77,4 +77,8 @@ final class DoorkeeperExternalObject extends DoorkeeperDAO return false; } + public function describeAutomaticCapability($capability) { + return null; + } + } diff --git a/src/applications/feed/story/PhabricatorFeedStory.php b/src/applications/feed/story/PhabricatorFeedStory.php index 3d815364ef..eb6b928866 100644 --- a/src/applications/feed/story/PhabricatorFeedStory.php +++ b/src/applications/feed/story/PhabricatorFeedStory.php @@ -324,4 +324,8 @@ abstract class PhabricatorFeedStory implements PhabricatorPolicyInterface { return false; } + public function describeAutomaticCapability($capability) { + return null; + } + } diff --git a/src/applications/files/storage/PhabricatorFile.php b/src/applications/files/storage/PhabricatorFile.php index d694f115a5..962e2f14e3 100644 --- a/src/applications/files/storage/PhabricatorFile.php +++ b/src/applications/files/storage/PhabricatorFile.php @@ -830,6 +830,10 @@ final class PhabricatorFile extends PhabricatorFileDAO return false; } + public function describeAutomaticCapability($capability) { + return null; + } + /* -( PhabricatorSubscribableInterface Implementation )-------------------- */ diff --git a/src/applications/flag/storage/PhabricatorFlag.php b/src/applications/flag/storage/PhabricatorFlag.php index 235260ae32..a1b65911d7 100644 --- a/src/applications/flag/storage/PhabricatorFlag.php +++ b/src/applications/flag/storage/PhabricatorFlag.php @@ -50,4 +50,8 @@ final class PhabricatorFlag extends PhabricatorFlagDAO return ($viewer->getPHID() == $this->getOwnerPHID()); } + public function describeAutomaticCapability($capability) { + return pht('Flags are private. Only you can view or edit your flags.'); + } + } diff --git a/src/applications/herald/storage/HeraldRule.php b/src/applications/herald/storage/HeraldRule.php index dc81e149f6..733de01ccf 100644 --- a/src/applications/herald/storage/HeraldRule.php +++ b/src/applications/herald/storage/HeraldRule.php @@ -202,4 +202,10 @@ final class HeraldRule extends HeraldDAO } } + public function describeAutomaticCapability($capability) { + // TODO: (T603) Sort this out. + return null; + } + + } diff --git a/src/applications/legalpad/storage/LegalpadDocument.php b/src/applications/legalpad/storage/LegalpadDocument.php index 665370333d..45bdbefc87 100644 --- a/src/applications/legalpad/storage/LegalpadDocument.php +++ b/src/applications/legalpad/storage/LegalpadDocument.php @@ -96,6 +96,12 @@ final class LegalpadDocument extends LegalpadDAO return ($user->getPHID() == $this->getCreatorPHID()); } + public function describeAutomaticCapability($capability) { + return pht( + 'The author of a document can always view and edit it.'); + } + + /* -( PhabricatorApplicationTransactionInterface )------------------------- */ public function getApplicationTransactionEditor() { diff --git a/src/applications/macro/storage/PhabricatorFileImageMacro.php b/src/applications/macro/storage/PhabricatorFileImageMacro.php index b708bc9668..83527ef294 100644 --- a/src/applications/macro/storage/PhabricatorFileImageMacro.php +++ b/src/applications/macro/storage/PhabricatorFileImageMacro.php @@ -61,5 +61,9 @@ final class PhabricatorFileImageMacro extends PhabricatorFileDAO return false; } + public function describeAutomaticCapability($capability) { + return null; + } + } diff --git a/src/applications/mailinglists/storage/PhabricatorMetaMTAMailingList.php b/src/applications/mailinglists/storage/PhabricatorMetaMTAMailingList.php index d9034295c2..2770a93b30 100644 --- a/src/applications/mailinglists/storage/PhabricatorMetaMTAMailingList.php +++ b/src/applications/mailinglists/storage/PhabricatorMetaMTAMailingList.php @@ -37,4 +37,8 @@ final class PhabricatorMetaMTAMailingList extends PhabricatorMetaMTADAO return false; } + public function describeAutomaticCapability($capability) { + return null; + } + } diff --git a/src/applications/maniphest/storage/ManiphestTask.php b/src/applications/maniphest/storage/ManiphestTask.php index 7b656cb081..b9750b06dd 100644 --- a/src/applications/maniphest/storage/ManiphestTask.php +++ b/src/applications/maniphest/storage/ManiphestTask.php @@ -221,6 +221,11 @@ final class ManiphestTask extends ManiphestDAO return false; } + public function describeAutomaticCapability($capability) { + return pht( + 'The owner of a task can always view and edit it.'); + } + /* -( PhabricatorTokenReceiverInterface )---------------------------------- */ diff --git a/src/applications/owners/storage/PhabricatorOwnersPackage.php b/src/applications/owners/storage/PhabricatorOwnersPackage.php index 90af440eec..f4132aa3b1 100644 --- a/src/applications/owners/storage/PhabricatorOwnersPackage.php +++ b/src/applications/owners/storage/PhabricatorOwnersPackage.php @@ -30,6 +30,10 @@ final class PhabricatorOwnersPackage extends PhabricatorOwnersDAO return false; } + public function describeAutomaticCapability($capability) { + return null; + } + public function getConfiguration() { return array( // This information is better available from the history table. diff --git a/src/applications/paste/storage/PhabricatorPaste.php b/src/applications/paste/storage/PhabricatorPaste.php index ba1081f53a..25678e008f 100644 --- a/src/applications/paste/storage/PhabricatorPaste.php +++ b/src/applications/paste/storage/PhabricatorPaste.php @@ -61,6 +61,11 @@ final class PhabricatorPaste extends PhabricatorPasteDAO return ($user->getPHID() == $this->getAuthorPHID()); } + public function describeAutomaticCapability($capability) { + return pht( + 'The author of a paste can always view and edit it.'); + } + public function getFullName() { $title = $this->getTitle(); if (!$title) { diff --git a/src/applications/people/storage/PhabricatorExternalAccount.php b/src/applications/people/storage/PhabricatorExternalAccount.php index 1e0c347418..94ca59fb02 100644 --- a/src/applications/people/storage/PhabricatorExternalAccount.php +++ b/src/applications/people/storage/PhabricatorExternalAccount.php @@ -102,4 +102,9 @@ final class PhabricatorExternalAccount extends PhabricatorUserDAO return ($viewer->getPHID() == $this->getUserPHID()); } + public function describeAutomaticCapability($capability) { + // TODO: (T603) This is complicated. + return null; + } + } diff --git a/src/applications/people/storage/PhabricatorUser.php b/src/applications/people/storage/PhabricatorUser.php index 921b4a6780..0602ac2175 100644 --- a/src/applications/people/storage/PhabricatorUser.php +++ b/src/applications/people/storage/PhabricatorUser.php @@ -834,6 +834,15 @@ EOBODY; return $this->getPHID() && ($viewer->getPHID() === $this->getPHID()); } + public function describeAutomaticCapability($capability) { + switch ($capability) { + case PhabricatorPolicyCapability::CAN_EDIT: + return pht('Only you can edit your information.'); + default: + return null; + } + } + /* -( PhabricatorCustomFieldInterface )------------------------------------ */ diff --git a/src/applications/phame/storage/PhameBlog.php b/src/applications/phame/storage/PhameBlog.php index a7f37f6781..9270843d5c 100644 --- a/src/applications/phame/storage/PhameBlog.php +++ b/src/applications/phame/storage/PhameBlog.php @@ -188,6 +188,20 @@ final class PhameBlog extends PhameDAO } + public function describeAutomaticCapability($capability) { + switch ($capability) { + case PhabricatorPolicyCapability::CAN_VIEW: + return pht( + 'Users who can edit or post on a blog can always view it.'); + case PhabricatorPolicyCapability::CAN_JOIN: + return pht( + 'Users who can edit a blog can always post on it.'); + } + + return null; + } + + /* -( PhabricatorMarkupInterface Implementation )-------------------------- */ diff --git a/src/applications/phame/storage/PhamePost.php b/src/applications/phame/storage/PhamePost.php index f71cb6b2de..fc3b1d1721 100644 --- a/src/applications/phame/storage/PhamePost.php +++ b/src/applications/phame/storage/PhamePost.php @@ -150,6 +150,12 @@ final class PhamePost extends PhameDAO } + public function describeAutomaticCapability($capability) { + return pht( + 'The author of a blog post can always view and edit it.'); + } + + /* -( PhabricatorMarkupInterface Implementation )-------------------------- */ diff --git a/src/applications/phid/PhabricatorObjectHandle.php b/src/applications/phid/PhabricatorObjectHandle.php index 1a31ed91c8..05eddbd592 100644 --- a/src/applications/phid/PhabricatorObjectHandle.php +++ b/src/applications/phid/PhabricatorObjectHandle.php @@ -236,4 +236,8 @@ final class PhabricatorObjectHandle return true; } + public function describeAutomaticCapability($capability) { + return null; + } + } diff --git a/src/applications/phlux/storage/PhluxVariable.php b/src/applications/phlux/storage/PhluxVariable.php index f279700224..2c1b8df12b 100644 --- a/src/applications/phlux/storage/PhluxVariable.php +++ b/src/applications/phlux/storage/PhluxVariable.php @@ -45,4 +45,8 @@ final class PhluxVariable extends PhluxDAO return false; } + public function describeAutomaticCapability($capability) { + return null; + } + } diff --git a/src/applications/pholio/storage/PholioImage.php b/src/applications/pholio/storage/PholioImage.php index 74d16ef1b7..a77aff84cc 100644 --- a/src/applications/pholio/storage/PholioImage.php +++ b/src/applications/pholio/storage/PholioImage.php @@ -104,4 +104,8 @@ final class PholioImage extends PholioDAO return $this->getMock()->hasAutomaticCapability($capability, $viewer); } + public function describeAutomaticCapability($capability) { + return null; + } + } diff --git a/src/applications/pholio/storage/PholioMock.php b/src/applications/pholio/storage/PholioMock.php index 8bec0a8e09..814ad99017 100644 --- a/src/applications/pholio/storage/PholioMock.php +++ b/src/applications/pholio/storage/PholioMock.php @@ -149,6 +149,10 @@ final class PholioMock extends PholioDAO return ($viewer->getPHID() == $this->getAuthorPHID()); } + public function describeAutomaticCapability($capability) { + return pht("A mock's owner can always view and edit it."); + } + /* -( PhabricatorMarkupInterface )----------------------------------------- */ diff --git a/src/applications/phortune/storage/PhortuneAccount.php b/src/applications/phortune/storage/PhortuneAccount.php index cdd597d94f..6fbb752d13 100644 --- a/src/applications/phortune/storage/PhortuneAccount.php +++ b/src/applications/phortune/storage/PhortuneAccount.php @@ -46,7 +46,7 @@ final class PhortuneAccount extends PhortuneDAO } public function getPolicy($capability) { - return false; + return PhabricatorPolicies::POLICY_NOONE; } public function hasAutomaticCapability($capability, PhabricatorUser $viewer) { @@ -54,4 +54,9 @@ final class PhortuneAccount extends PhortuneDAO return isset($members[$viewer->getPHID()]); } + public function describeAutomaticCapability($capability) { + return pht('Members of an account can always view and edit it.'); + } + + } diff --git a/src/applications/phortune/storage/PhortunePaymentMethod.php b/src/applications/phortune/storage/PhortunePaymentMethod.php index 59799fbe11..be71203c98 100644 --- a/src/applications/phortune/storage/PhortunePaymentMethod.php +++ b/src/applications/phortune/storage/PhortunePaymentMethod.php @@ -107,4 +107,9 @@ final class PhortunePaymentMethod extends PhortuneDAO $viewer); } + public function describeAutomaticCapability($capability) { + return pht( + 'Members of an account can always view and edit its payment methods.'); + } + } diff --git a/src/applications/phortune/storage/PhortuneProduct.php b/src/applications/phortune/storage/PhortuneProduct.php index 8bf42a8da9..716ae93486 100644 --- a/src/applications/phortune/storage/PhortuneProduct.php +++ b/src/applications/phortune/storage/PhortuneProduct.php @@ -73,4 +73,8 @@ final class PhortuneProduct extends PhortuneDAO return false; } + public function describeAutomaticCapability($capability) { + return null; + } + } diff --git a/src/applications/phriction/storage/PhrictionDocument.php b/src/applications/phriction/storage/PhrictionDocument.php index c7b5bd7c0d..24e3e7bfa7 100644 --- a/src/applications/phriction/storage/PhrictionDocument.php +++ b/src/applications/phriction/storage/PhrictionDocument.php @@ -126,6 +126,14 @@ final class PhrictionDocument extends PhrictionDAO return false; } + public function describeAutomaticCapability($capability) { + if ($this->hasProject()) { + return pht( + "This is a project wiki page, and inherits the project's policies."); + } + return null; + } + public function isAutomaticallySubscribed($phid) { return false; } diff --git a/src/applications/policy/__tests__/PhabricatorPolicyTestObject.php b/src/applications/policy/__tests__/PhabricatorPolicyTestObject.php index 826c9c3e54..b010f88d01 100644 --- a/src/applications/policy/__tests__/PhabricatorPolicyTestObject.php +++ b/src/applications/policy/__tests__/PhabricatorPolicyTestObject.php @@ -38,4 +38,8 @@ final class PhabricatorPolicyTestObject return $this; } + public function describeAutomaticCapability($capability) { + return null; + } + } diff --git a/src/applications/policy/application/PhabricatorApplicationPolicy.php b/src/applications/policy/application/PhabricatorApplicationPolicy.php new file mode 100644 index 0000000000..c9eec4bcef --- /dev/null +++ b/src/applications/policy/application/PhabricatorApplicationPolicy.php @@ -0,0 +1,23 @@ + array( + 'explain/(?P[^/]+)/(?P[^/]+)/' + => 'PhabricatorPolicyExplainController', + ), + ); + } + +} + diff --git a/src/applications/policy/controller/PhabricatorPolicyController.php b/src/applications/policy/controller/PhabricatorPolicyController.php new file mode 100644 index 0000000000..26f86b5250 --- /dev/null +++ b/src/applications/policy/controller/PhabricatorPolicyController.php @@ -0,0 +1,5 @@ +phid = $data['phid']; + $this->capability = $data['capability']; + } + + public function processRequest() { + $request = $this->getRequest(); + $viewer = $request->getUser(); + + $phid = $this->phid; + $capability = $this->capability; + + $object = id(new PhabricatorObjectQuery()) + ->setViewer($viewer) + ->withPHIDs(array($phid)) + ->executeOne(); + if (!$object) { + return new Aphront404Response(); + } + + $policies = PhabricatorPolicyQuery::loadPolicies( + $viewer, + $object); + + $policy = idx($policies, $capability); + if (!$policy) { + return new Aphront404Response(); + } + + $handle = id(new PhabricatorHandleQuery()) + ->setViewer($viewer) + ->withPHIDs(array($phid)) + ->executeOne(); + $object_uri = $handle->getURI(); + + $explanation = $policy->getExplanation($capability); + $auto_info = (array)$object->describeAutomaticCapability($capability); + + foreach ($auto_info as $key => $info) { + $auto_info[$key] = phutil_tag('li', array(), $info); + } + if ($auto_info) { + $auto_info = phutil_tag('ul', array(), $auto_info); + } + + $content = array( + $explanation, + $auto_info, + ); + + $dialog = id(new AphrontDialogView()) + ->setUser($viewer) + ->setClass('aphront-access-dialog') + ->setTitle(pht('Policy Details')) + ->appendChild($content) + ->addCancelButton($object_uri, pht('Done')); + + return id(new AphrontDialogResponse())->setDialog($dialog); + } + +} diff --git a/src/applications/policy/filter/PhabricatorPolicy.php b/src/applications/policy/filter/PhabricatorPolicy.php index c61fce4469..a0bbc5e8ab 100644 --- a/src/applications/policy/filter/PhabricatorPolicy.php +++ b/src/applications/policy/filter/PhabricatorPolicy.php @@ -130,6 +130,35 @@ final class PhabricatorPolicy { return $this->getName(); } + public function getExplanation($capability) { + switch ($capability) { + case PhabricatorPolicyCapability::CAN_VIEW: + switch ($this->getPHID()) { + case PhabricatorPolicies::POLICY_PUBLIC: + return pht('Visible to the entire internet.'); + case PhabricatorPolicies::POLICY_USER: + return pht('Visible to all logged in users.'); + case PhabricatorPolicies::POLICY_ADMIN: + return pht('Visible to all administrators.'); + case PhabricatorPolicies::POLICY_NOONE: + return pht('Not visible to anyone by default.'); + } + + switch ($this->getType()) { + case PhabricatorPolicyType::TYPE_PROJECT: + return pht( + 'Visible to members of the project "%s".', + $this->getName()); + case PhabricatorPolicyType::TYPE_MASKED: + return pht('Other: %s', $this->getName()); + } + break; + } + + + return pht('?'); + } + public function getFullName() { switch ($this->getType()) { case PhabricatorPolicyType::TYPE_PROJECT: diff --git a/src/applications/policy/interface/PhabricatorPolicyInterface.php b/src/applications/policy/interface/PhabricatorPolicyInterface.php index b56d223c62..fb64180815 100644 --- a/src/applications/policy/interface/PhabricatorPolicyInterface.php +++ b/src/applications/policy/interface/PhabricatorPolicyInterface.php @@ -6,4 +6,32 @@ interface PhabricatorPolicyInterface { public function getPolicy($capability); public function hasAutomaticCapability($capability, PhabricatorUser $viewer); + /** + * Describe exceptions to an object's policy setting. + * + * The intent of this method is to explain and inform users about special + * cases which override configured policy settings. If this object has any + * such exceptions, explain them by returning one or more human-readable + * strings which describe the exception in a broad, categorical way. For + * example: + * + * - "The owner of an X can always view and edit it." + * - "Members of a Y can always view it." + * + * You can return `null`, a single string, or a list of strings. + * + * The relevant capability to explain (like "view") is passed as a parameter. + * You should tailor any messages to be relevant to that capability, although + * they do not need to exclusively describe the capability, and in some cases + * being more general ("The author can view and edit...") will be more clear. + * + * Messages should describe general rules, not specific objects, because the + * main goal is to teach the user the rules. For example, write "the author", + * not the specific author's name. + * + * @param const @{class:PhabricatorPolicyCapability} constant. + * @return wild Description of policy exceptions. See above. + */ + public function describeAutomaticCapability($capability); + } diff --git a/src/applications/policy/query/PhabricatorPolicyQuery.php b/src/applications/policy/query/PhabricatorPolicyQuery.php index 93e139fe5b..be35d6c1e4 100644 --- a/src/applications/policy/query/PhabricatorPolicyQuery.php +++ b/src/applications/policy/query/PhabricatorPolicyQuery.php @@ -15,10 +15,9 @@ final class PhabricatorPolicyQuery extends PhabricatorQuery { return $this; } - public static function renderPolicyDescriptions( + public static function loadPolicies( PhabricatorUser $viewer, - PhabricatorPolicyInterface $object, - $icon=false) { + PhabricatorPolicyInterface $object) { $results = array(); $policies = null; @@ -32,7 +31,7 @@ final class PhabricatorPolicyQuery extends PhabricatorQuery { } if (isset($global[$policy])) { - $results[$capability] = $global[$policy]->renderDescription($icon); + $results[$capability] = $global[$policy]; continue; } @@ -45,12 +44,26 @@ final class PhabricatorPolicyQuery extends PhabricatorQuery { ->execute(); } - $results[$capability] = $policies[$policy]->renderDescription($icon); + $results[$capability] = $policies[$policy]; } return $results; } + public static function renderPolicyDescriptions( + PhabricatorUser $viewer, + PhabricatorPolicyInterface $object, + $icon = false) { + + $policies = self::loadPolicies($viewer, $object); + + foreach ($policies as $capability => $policy) { + $policies[$capability] = $policy->renderDescription($icon); + } + + return $policies; + } + public function execute() { if (!$this->viewer) { throw new Exception('Call setViewer() before execute()!'); diff --git a/src/applications/ponder/storage/PonderAnswer.php b/src/applications/ponder/storage/PonderAnswer.php index edaa0f4b8e..0a8d12176c 100644 --- a/src/applications/ponder/storage/PonderAnswer.php +++ b/src/applications/ponder/storage/PonderAnswer.php @@ -145,6 +145,9 @@ final class PonderAnswer extends PonderDAO public function hasAutomaticCapability($capability, PhabricatorUser $viewer) { switch ($capability) { case PhabricatorPolicyCapability::CAN_VIEW: + if ($this->getAuthorPHID() == $viewer->getPHID()) { + return true; + } return $this->getQuestion()->hasAutomaticCapability( $capability, $viewer); @@ -154,6 +157,19 @@ final class PonderAnswer extends PonderDAO } + public function describeAutomaticCapability($capability) { + $out = array(); + $out[] = pht("The author of an answer can always view and edit it."); + switch ($capability) { + case PhabricatorPolicyCapability::CAN_VIEW: + $out[] = pht( + "The user who asks a question can always view the answers."); + break; + } + return $out; + } + + /* -( PhabricatorTokenReceiverInterface )---------------------------------- */ diff --git a/src/applications/ponder/storage/PonderQuestion.php b/src/applications/ponder/storage/PonderQuestion.php index 09864aada9..138ddef1f1 100644 --- a/src/applications/ponder/storage/PonderQuestion.php +++ b/src/applications/ponder/storage/PonderQuestion.php @@ -203,6 +203,12 @@ final class PonderQuestion extends PonderDAO return ($viewer->getPHID() == $this->getAuthorPHID()); } + + public function describeAutomaticCapability($capability) { + return pht( + 'The user who asked a question can always view and edit it.'); + } + public function getOriginalTitle() { // TODO: Make this actually save/return the original title. return $this->getTitle(); diff --git a/src/applications/project/storage/PhabricatorProject.php b/src/applications/project/storage/PhabricatorProject.php index cd93909e84..40074e801d 100644 --- a/src/applications/project/storage/PhabricatorProject.php +++ b/src/applications/project/storage/PhabricatorProject.php @@ -60,6 +60,16 @@ final class PhabricatorProject extends PhabricatorProjectDAO return false; } + public function describeAutomaticCapability($capability) { + switch ($capability) { + case PhabricatorPolicyCapability::CAN_VIEW: + return pht("Members of a project can always view it."); + case PhabricatorPolicyCapability::CAN_JOIN: + return pht("Users who can edit a project can always join it."); + } + return null; + } + public function isUserMember($user_phid) { return $this->assertAttachedKey($this->sparseMembers, $user_phid); } diff --git a/src/applications/releeph/storage/ReleephBranch.php b/src/applications/releeph/storage/ReleephBranch.php index b373467ee6..799e658b2c 100644 --- a/src/applications/releeph/storage/ReleephBranch.php +++ b/src/applications/releeph/storage/ReleephBranch.php @@ -189,4 +189,9 @@ final class ReleephBranch extends ReleephDAO return $this->getProject()->hasAutomaticCapability($capability, $viewer); } + public function describeAutomaticCapability($capability) { + return null; + } + + } diff --git a/src/applications/releeph/storage/ReleephProject.php b/src/applications/releeph/storage/ReleephProject.php index ec5b0dd085..eed3cca7a9 100644 --- a/src/applications/releeph/storage/ReleephProject.php +++ b/src/applications/releeph/storage/ReleephProject.php @@ -193,4 +193,9 @@ final class ReleephProject extends ReleephDAO return false; } + public function describeAutomaticCapability($capability) { + return null; + } + + } diff --git a/src/applications/releeph/storage/ReleephRequest.php b/src/applications/releeph/storage/ReleephRequest.php index f8c6de032e..fd2486055b 100644 --- a/src/applications/releeph/storage/ReleephRequest.php +++ b/src/applications/releeph/storage/ReleephRequest.php @@ -306,6 +306,11 @@ final class ReleephRequest extends ReleephDAO return false; } + public function describeAutomaticCapability($capability) { + return null; + } + + /* -( PhabricatorCustomFieldInterface )------------------------------------ */ diff --git a/src/applications/repository/storage/PhabricatorRepository.php b/src/applications/repository/storage/PhabricatorRepository.php index e6b0225189..32782b65d4 100644 --- a/src/applications/repository/storage/PhabricatorRepository.php +++ b/src/applications/repository/storage/PhabricatorRepository.php @@ -701,6 +701,11 @@ final class PhabricatorRepository extends PhabricatorRepositoryDAO return false; } + public function describeAutomaticCapability($capability) { + return null; + } + + /* -( PhabricatorMarkupInterface )----------------------------------------- */ diff --git a/src/applications/repository/storage/PhabricatorRepositoryArcanistProject.php b/src/applications/repository/storage/PhabricatorRepositoryArcanistProject.php index fc02ffb9c5..37ee0f6ebc 100644 --- a/src/applications/repository/storage/PhabricatorRepositoryArcanistProject.php +++ b/src/applications/repository/storage/PhabricatorRepositoryArcanistProject.php @@ -87,4 +87,8 @@ final class PhabricatorRepositoryArcanistProject return false; } + public function describeAutomaticCapability($capability) { + return null; + } + } diff --git a/src/applications/repository/storage/PhabricatorRepositoryCommit.php b/src/applications/repository/storage/PhabricatorRepositoryCommit.php index a7815a950c..82340ffdb2 100644 --- a/src/applications/repository/storage/PhabricatorRepositoryCommit.php +++ b/src/applications/repository/storage/PhabricatorRepositoryCommit.php @@ -167,6 +167,12 @@ final class PhabricatorRepositoryCommit return $this->getRepository()->hasAutomaticCapability($capability, $viewer); } + public function describeAutomaticCapability($capability) { + return pht( + 'Commits inherit the policies of the repository they belong to.'); + } + + /* -( PhabricatorTokenReceiverInterface )---------------------------------- */ public function getUsersToNotifyOfTokenGiven() { diff --git a/src/applications/search/storage/PhabricatorNamedQuery.php b/src/applications/search/storage/PhabricatorNamedQuery.php index cc1e199336..12c16feb37 100644 --- a/src/applications/search/storage/PhabricatorNamedQuery.php +++ b/src/applications/search/storage/PhabricatorNamedQuery.php @@ -39,4 +39,11 @@ final class PhabricatorNamedQuery extends PhabricatorSearchDAO return false; } + public function describeAutomaticCapability($capability) { + return pht( + 'The queries you have saved are private. Only you can view or edit '. + 'them.'); + } + + } diff --git a/src/applications/search/storage/PhabricatorSavedQuery.php b/src/applications/search/storage/PhabricatorSavedQuery.php index 2421eae11a..0fc1296c86 100644 --- a/src/applications/search/storage/PhabricatorSavedQuery.php +++ b/src/applications/search/storage/PhabricatorSavedQuery.php @@ -63,4 +63,8 @@ final class PhabricatorSavedQuery extends PhabricatorSearchDAO return false; } + public function describeAutomaticCapability($capability) { + return null; + } + } diff --git a/src/applications/slowvote/storage/PhabricatorSlowvotePoll.php b/src/applications/slowvote/storage/PhabricatorSlowvotePoll.php index ad55b8bacc..24700fd9c7 100644 --- a/src/applications/slowvote/storage/PhabricatorSlowvotePoll.php +++ b/src/applications/slowvote/storage/PhabricatorSlowvotePoll.php @@ -96,6 +96,12 @@ final class PhabricatorSlowvotePoll extends PhabricatorSlowvoteDAO return ($viewer->getPHID() == $this->getAuthorPHID()); } + public function describeAutomaticCapability($capability) { + return pht( + 'The author of a poll can always view and edit it.'); + } + + /* -( PhabricatorSubscribableInterface )----------------------------------- */ diff --git a/src/applications/tokens/storage/PhabricatorToken.php b/src/applications/tokens/storage/PhabricatorToken.php index a22db91551..81dbbbbf73 100644 --- a/src/applications/tokens/storage/PhabricatorToken.php +++ b/src/applications/tokens/storage/PhabricatorToken.php @@ -27,6 +27,10 @@ final class PhabricatorToken extends PhabricatorTokenDAO return false; } + public function describeAutomaticCapability($capability) { + return null; + } + public function renderIcon() { // TODO: Maybe move to a View class? diff --git a/src/applications/tokens/storage/PhabricatorTokenGiven.php b/src/applications/tokens/storage/PhabricatorTokenGiven.php index b3f2b21480..0b35e8fcdd 100644 --- a/src/applications/tokens/storage/PhabricatorTokenGiven.php +++ b/src/applications/tokens/storage/PhabricatorTokenGiven.php @@ -48,4 +48,17 @@ final class PhabricatorTokenGiven extends PhabricatorTokenDAO } } + public function describeAutomaticCapability($capability) { + switch ($capability) { + case PhabricatorPolicyCapability::CAN_VIEW: + return pht( + 'A token inherits the policies of the object it is awarded to.'); + case PhabricatorPolicyCapability::CAN_EDIT: + return pht( + 'The user who gave a token can always edit it.'); + } + return null; + } + + } diff --git a/src/applications/transactions/storage/PhabricatorApplicationTransaction.php b/src/applications/transactions/storage/PhabricatorApplicationTransaction.php index 3ed95c6661..b6f99d33cf 100644 --- a/src/applications/transactions/storage/PhabricatorApplicationTransaction.php +++ b/src/applications/transactions/storage/PhabricatorApplicationTransaction.php @@ -537,4 +537,10 @@ abstract class PhabricatorApplicationTransaction return ($viewer->getPHID() == $this->getAuthorPHID()); } + public function describeAutomaticCapability($capability) { + // TODO: (T603) Exact policies are unclear here. + return null; + } + + } diff --git a/src/applications/transactions/storage/PhabricatorApplicationTransactionComment.php b/src/applications/transactions/storage/PhabricatorApplicationTransactionComment.php index 818beb9c4d..7f0f01af60 100644 --- a/src/applications/transactions/storage/PhabricatorApplicationTransactionComment.php +++ b/src/applications/transactions/storage/PhabricatorApplicationTransactionComment.php @@ -113,4 +113,9 @@ abstract class PhabricatorApplicationTransactionComment return ($viewer->getPHID() == $this->getAuthorPHID()); } + public function describeAutomaticCapability($capability) { + // TODO: (T603) Policies are murky. + return null; + } + } diff --git a/src/view/phui/PHUIHeaderView.php b/src/view/phui/PHUIHeaderView.php index 3242709cdf..c86c10476c 100644 --- a/src/view/phui/PHUIHeaderView.php +++ b/src/view/phui/PHUIHeaderView.php @@ -148,11 +148,7 @@ final class PHUIHeaderView extends AphrontView { } if ($this->policyObject) { - $descriptions = PhabricatorPolicyQuery::renderPolicyDescriptions( - $this->getUser(), - $this->policyObject, - true); - $property_list[] = $descriptions[PhabricatorPolicyCapability::CAN_VIEW]; + $property_list[] = $this->renderPolicyProperty($this->policyObject); } $header[] = phutil_tag( @@ -179,5 +175,31 @@ final class PHUIHeaderView extends AphrontView { )); } + private function renderPolicyProperty(PhabricatorPolicyInterface $object) { + $policies = PhabricatorPolicyQuery::loadPolicies( + $this->getUser(), + $object); + $view_capability = PhabricatorPolicyCapability::CAN_VIEW; + $policy = idx($policies, $view_capability); + if (!$policy) { + return null; + } + + $phid = $object->getPHID(); + + $icon = id(new PHUIIconView()) + ->setSpriteSheet(PHUIIconView::SPRITE_STATUS) + ->setSpriteIcon($policy->getIcon()); + + $link = javelin_tag( + 'a', + array( + 'href' => '/policy/explain/'.$phid.'/'.$view_capability.'/', + 'sigil' => 'workflow', + ), + $policy->getName()); + + return array($icon, $link); + } } diff --git a/webroot/rsrc/css/aphront/dialog-view.css b/webroot/rsrc/css/aphront/dialog-view.css index 8cc0d58303..e42d1682eb 100644 --- a/webroot/rsrc/css/aphront/dialog-view.css +++ b/webroot/rsrc/css/aphront/dialog-view.css @@ -111,3 +111,8 @@ .aphront-access-dialog { width: 50%; } + +.aphront-access-dialog ul { + margin: 12px 24px; + list-style: circle; +}