@recaptime-dev's working patches + fork for Phorge, a community fork of Phabricator. (Upstream dev and stable branches are at upstream/main and upstream/stable respectively.) hq.recaptime.dev/wiki/Phorge
phorge phabricator
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

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

+565 -56
+45 -45
src/__celerity_resource_map__.php
··· 836 836 ), 837 837 'aphront-dialog-view-css' => 838 838 array( 839 - 'uri' => '/res/337bd2a9/rsrc/css/aphront/dialog-view.css', 839 + 'uri' => '/res/609ccc78/rsrc/css/aphront/dialog-view.css', 840 840 'type' => 'css', 841 841 'requires' => 842 842 array( ··· 1176 1176 ), 1177 1177 'herald-rule-editor' => 1178 1178 array( 1179 - 'uri' => '/res/f8ee0e9c/rsrc/js/application/herald/HeraldRuleEditor.js', 1179 + 'uri' => '/res/36222dde/rsrc/js/application/herald/HeraldRuleEditor.js', 1180 1180 'type' => 'js', 1181 1181 'requires' => 1182 1182 array( ··· 4162 4162 ), array( 4163 4163 'packages' => 4164 4164 array( 4165 - '15affac5' => 4165 + 'de5898ae' => 4166 4166 array( 4167 4167 'name' => 'core.pkg.css', 4168 4168 'symbols' => ··· 4209 4209 39 => 'phabricator-property-list-view-css', 4210 4210 40 => 'phabricator-tag-view-css', 4211 4211 ), 4212 - 'uri' => '/res/pkg/15affac5/core.pkg.css', 4212 + 'uri' => '/res/pkg/de5898ae/core.pkg.css', 4213 4213 'type' => 'css', 4214 4214 ), 4215 4215 '8977e356' => ··· 4399 4399 ), 4400 4400 'reverse' => 4401 4401 array( 4402 - 'aphront-dialog-view-css' => '15affac5', 4403 - 'aphront-error-view-css' => '15affac5', 4404 - 'aphront-list-filter-view-css' => '15affac5', 4405 - 'aphront-pager-view-css' => '15affac5', 4406 - 'aphront-panel-view-css' => '15affac5', 4407 - 'aphront-table-view-css' => '15affac5', 4408 - 'aphront-tokenizer-control-css' => '15affac5', 4409 - 'aphront-tooltip-css' => '15affac5', 4410 - 'aphront-typeahead-control-css' => '15affac5', 4402 + 'aphront-dialog-view-css' => 'de5898ae', 4403 + 'aphront-error-view-css' => 'de5898ae', 4404 + 'aphront-list-filter-view-css' => 'de5898ae', 4405 + 'aphront-pager-view-css' => 'de5898ae', 4406 + 'aphront-panel-view-css' => 'de5898ae', 4407 + 'aphront-table-view-css' => 'de5898ae', 4408 + 'aphront-tokenizer-control-css' => 'de5898ae', 4409 + 'aphront-tooltip-css' => 'de5898ae', 4410 + 'aphront-typeahead-control-css' => 'de5898ae', 4411 4411 'differential-changeset-view-css' => '44bfe40c', 4412 4412 'differential-core-view-css' => '44bfe40c', 4413 4413 'differential-inline-comment-editor' => '5e9e5c4e', ··· 4421 4421 'differential-table-of-contents-css' => '44bfe40c', 4422 4422 'diffusion-commit-view-css' => 'c8ce2d88', 4423 4423 'diffusion-icons-css' => 'c8ce2d88', 4424 - 'global-drag-and-drop-css' => '15affac5', 4424 + 'global-drag-and-drop-css' => 'de5898ae', 4425 4425 'inline-comment-summary-css' => '44bfe40c', 4426 4426 'javelin-aphlict' => '8977e356', 4427 4427 'javelin-behavior' => '9564fa17', ··· 4494 4494 'javelin-util' => '9564fa17', 4495 4495 'javelin-vector' => '9564fa17', 4496 4496 'javelin-workflow' => '9564fa17', 4497 - 'lightbox-attachment-css' => '15affac5', 4497 + 'lightbox-attachment-css' => 'de5898ae', 4498 4498 'maniphest-task-summary-css' => '49898640', 4499 - 'phabricator-action-list-view-css' => '15affac5', 4500 - 'phabricator-application-launch-view-css' => '15affac5', 4499 + 'phabricator-action-list-view-css' => 'de5898ae', 4500 + 'phabricator-application-launch-view-css' => 'de5898ae', 4501 4501 'phabricator-busy' => '8977e356', 4502 4502 'phabricator-content-source-view-css' => '44bfe40c', 4503 - 'phabricator-core-css' => '15affac5', 4504 - 'phabricator-crumbs-view-css' => '15affac5', 4503 + 'phabricator-core-css' => 'de5898ae', 4504 + 'phabricator-crumbs-view-css' => 'de5898ae', 4505 4505 'phabricator-drag-and-drop-file-upload' => '5e9e5c4e', 4506 4506 'phabricator-dropdown-menu' => '8977e356', 4507 4507 'phabricator-file-upload' => '8977e356', 4508 - 'phabricator-filetree-view-css' => '15affac5', 4509 - 'phabricator-flag-css' => '15affac5', 4508 + 'phabricator-filetree-view-css' => 'de5898ae', 4509 + 'phabricator-flag-css' => 'de5898ae', 4510 4510 'phabricator-hovercard' => '8977e356', 4511 - 'phabricator-jump-nav' => '15affac5', 4511 + 'phabricator-jump-nav' => 'de5898ae', 4512 4512 'phabricator-keyboard-shortcut' => '8977e356', 4513 4513 'phabricator-keyboard-shortcut-manager' => '8977e356', 4514 - 'phabricator-main-menu-view' => '15affac5', 4514 + 'phabricator-main-menu-view' => 'de5898ae', 4515 4515 'phabricator-menu-item' => '8977e356', 4516 - 'phabricator-nav-view-css' => '15affac5', 4516 + 'phabricator-nav-view-css' => 'de5898ae', 4517 4517 'phabricator-notification' => '8977e356', 4518 - 'phabricator-notification-css' => '15affac5', 4519 - 'phabricator-notification-menu-css' => '15affac5', 4518 + 'phabricator-notification-css' => 'de5898ae', 4519 + 'phabricator-notification-menu-css' => 'de5898ae', 4520 4520 'phabricator-object-selector-css' => '44bfe40c', 4521 4521 'phabricator-phtize' => '8977e356', 4522 4522 'phabricator-prefab' => '8977e356', 4523 4523 'phabricator-project-tag-css' => '49898640', 4524 - 'phabricator-property-list-view-css' => '15affac5', 4525 - 'phabricator-remarkup-css' => '15affac5', 4524 + 'phabricator-property-list-view-css' => 'de5898ae', 4525 + 'phabricator-remarkup-css' => 'de5898ae', 4526 4526 'phabricator-shaped-request' => '5e9e5c4e', 4527 - 'phabricator-side-menu-view-css' => '15affac5', 4528 - 'phabricator-standard-page-view' => '15affac5', 4529 - 'phabricator-tag-view-css' => '15affac5', 4527 + 'phabricator-side-menu-view-css' => 'de5898ae', 4528 + 'phabricator-standard-page-view' => 'de5898ae', 4529 + 'phabricator-tag-view-css' => 'de5898ae', 4530 4530 'phabricator-textareautils' => '8977e356', 4531 4531 'phabricator-tooltip' => '8977e356', 4532 - 'phabricator-transaction-view-css' => '15affac5', 4533 - 'phabricator-zindex-css' => '15affac5', 4534 - 'phui-button-css' => '15affac5', 4535 - 'phui-form-css' => '15affac5', 4536 - 'phui-form-view-css' => '15affac5', 4537 - 'phui-header-view-css' => '15affac5', 4538 - 'phui-icon-view-css' => '15affac5', 4539 - 'phui-object-item-list-view-css' => '15affac5', 4540 - 'phui-spacing-css' => '15affac5', 4541 - 'sprite-apps-large-css' => '15affac5', 4542 - 'sprite-gradient-css' => '15affac5', 4543 - 'sprite-icons-css' => '15affac5', 4544 - 'sprite-menu-css' => '15affac5', 4545 - 'syntax-highlighting-css' => '15affac5', 4532 + 'phabricator-transaction-view-css' => 'de5898ae', 4533 + 'phabricator-zindex-css' => 'de5898ae', 4534 + 'phui-button-css' => 'de5898ae', 4535 + 'phui-form-css' => 'de5898ae', 4536 + 'phui-form-view-css' => 'de5898ae', 4537 + 'phui-header-view-css' => 'de5898ae', 4538 + 'phui-icon-view-css' => 'de5898ae', 4539 + 'phui-object-item-list-view-css' => 'de5898ae', 4540 + 'phui-spacing-css' => 'de5898ae', 4541 + 'sprite-apps-large-css' => 'de5898ae', 4542 + 'sprite-gradient-css' => 'de5898ae', 4543 + 'sprite-icons-css' => 'de5898ae', 4544 + 'sprite-menu-css' => 'de5898ae', 4545 + 'syntax-highlighting-css' => 'de5898ae', 4546 4546 ), 4547 4547 ));
+6
src/__phutil_library_map__.php
··· 846 846 'PhabricatorApplicationPhortune' => 'applications/phortune/application/PhabricatorApplicationPhortune.php', 847 847 'PhabricatorApplicationPhrequent' => 'applications/phrequent/application/PhabricatorApplicationPhrequent.php', 848 848 'PhabricatorApplicationPhriction' => 'applications/phriction/application/PhabricatorApplicationPhriction.php', 849 + 'PhabricatorApplicationPolicy' => 'applications/policy/application/PhabricatorApplicationPolicy.php', 849 850 'PhabricatorApplicationPonder' => 'applications/ponder/application/PhabricatorApplicationPonder.php', 850 851 'PhabricatorApplicationProject' => 'applications/project/application/PhabricatorApplicationProject.php', 851 852 'PhabricatorApplicationReleeph' => 'applications/releeph/application/PhabricatorApplicationReleeph.php', ··· 1459 1460 'PhabricatorPolicyCapability' => 'applications/policy/constants/PhabricatorPolicyCapability.php', 1460 1461 'PhabricatorPolicyConfigOptions' => 'applications/config/option/PhabricatorPolicyConfigOptions.php', 1461 1462 'PhabricatorPolicyConstants' => 'applications/policy/constants/PhabricatorPolicyConstants.php', 1463 + 'PhabricatorPolicyController' => 'applications/policy/controller/PhabricatorPolicyController.php', 1462 1464 'PhabricatorPolicyDataTestCase' => 'applications/policy/__tests__/PhabricatorPolicyDataTestCase.php', 1463 1465 'PhabricatorPolicyException' => 'applications/policy/exception/PhabricatorPolicyException.php', 1466 + 'PhabricatorPolicyExplainController' => 'applications/policy/controller/PhabricatorPolicyExplainController.php', 1464 1467 'PhabricatorPolicyFilter' => 'applications/policy/filter/PhabricatorPolicyFilter.php', 1465 1468 'PhabricatorPolicyInterface' => 'applications/policy/interface/PhabricatorPolicyInterface.php', 1466 1469 'PhabricatorPolicyQuery' => 'applications/policy/query/PhabricatorPolicyQuery.php', ··· 2941 2944 'PhabricatorApplicationPhortune' => 'PhabricatorApplication', 2942 2945 'PhabricatorApplicationPhrequent' => 'PhabricatorApplication', 2943 2946 'PhabricatorApplicationPhriction' => 'PhabricatorApplication', 2947 + 'PhabricatorApplicationPolicy' => 'PhabricatorApplication', 2944 2948 'PhabricatorApplicationPonder' => 'PhabricatorApplication', 2945 2949 'PhabricatorApplicationProject' => 'PhabricatorApplication', 2946 2950 'PhabricatorApplicationReleeph' => 'PhabricatorApplication', ··· 3612 3616 'PhabricatorPolicyAwareTestQuery' => 'PhabricatorPolicyAwareQuery', 3613 3617 'PhabricatorPolicyCapability' => 'PhabricatorPolicyConstants', 3614 3618 'PhabricatorPolicyConfigOptions' => 'PhabricatorApplicationConfigOptions', 3619 + 'PhabricatorPolicyController' => 'PhabricatorController', 3615 3620 'PhabricatorPolicyDataTestCase' => 'PhabricatorTestCase', 3616 3621 'PhabricatorPolicyException' => 'Exception', 3622 + 'PhabricatorPolicyExplainController' => 'PhabricatorPolicyController', 3617 3623 'PhabricatorPolicyQuery' => 'PhabricatorQuery', 3618 3624 'PhabricatorPolicyTestCase' => 'PhabricatorTestCase', 3619 3625 'PhabricatorPolicyTestObject' => 'PhabricatorPolicyInterface',
+4
src/applications/auth/storage/PhabricatorAuthProviderConfig.php
··· 81 81 return false; 82 82 } 83 83 84 + public function describeAutomaticCapability($capability) { 85 + return null; 86 + } 87 + 84 88 }
+4
src/applications/chatlog/storage/PhabricatorChatLogChannel.php
··· 32 32 return false; 33 33 } 34 34 35 + public function describeAutomaticCapability($capability) { 36 + return null; 37 + } 38 + 35 39 } 36 40
+4
src/applications/chatlog/storage/PhabricatorChatLogEvent.php
··· 28 28 return false; 29 29 } 30 30 31 + public function describeAutomaticCapability($capability) { 32 + return null; 33 + } 34 + 31 35 public function getConfiguration() { 32 36 return array( 33 37 self::CONFIG_TIMESTAMPS => false,
+4
src/applications/conduit/method/ConduitAPIMethod.php
··· 183 183 return true; 184 184 } 185 185 186 + public function describeAutomaticCapability($capability) { 187 + return null; 188 + } 189 + 186 190 }
+4
src/applications/conduit/storage/PhabricatorConduitMethodCallLog.php
··· 30 30 return false; 31 31 } 32 32 33 + public function describeAutomaticCapability($capability) { 34 + return null; 35 + } 36 + 33 37 }
+4
src/applications/config/storage/PhabricatorConfigEntry.php
··· 59 59 return false; 60 60 } 61 61 62 + public function describeAutomaticCapability($capability) { 63 + return null; 64 + } 65 + 62 66 }
+4
src/applications/conpherence/storage/ConpherenceThread.php
··· 189 189 return isset($participants[$user->getPHID()]); 190 190 } 191 191 192 + public function describeAutomaticCapability($capability) { 193 + return pht("Participants in a thread can always view and edit it."); 194 + } 195 + 192 196 }
+4
src/applications/countdown/storage/PhabricatorCountdown.php
··· 53 53 return ($viewer->getPHID() == $this->getAuthorPHID()); 54 54 } 55 55 56 + public function describeAutomaticCapability($capability) { 57 + return pht('The author of a countdown can always view and edit it.'); 58 + } 59 + 56 60 }
+4
src/applications/daemon/storage/PhabricatorDaemonLog.php
··· 41 41 return false; 42 42 } 43 43 44 + public function describeAutomaticCapability($capability) { 45 + return null; 46 + } 47 + 44 48 }
+8
src/applications/differential/storage/DifferentialDiff.php
··· 320 320 return false; 321 321 } 322 322 323 + public function describeAutomaticCapability($capability) { 324 + if ($this->getRevision()) { 325 + return pht( 326 + 'This diff is attached to a revision, and inherits its policies.'); 327 + } 328 + return null; 329 + } 330 + 323 331 }
+20
src/applications/differential/storage/DifferentialRevision.php
··· 329 329 return false; 330 330 } 331 331 332 + public function describeAutomaticCapability($capability) { 333 + $description = array( 334 + pht('The owner of a revision can always view and edit it.'), 335 + ); 336 + 337 + switch ($capability) { 338 + case PhabricatorPolicyCapability::CAN_VIEW: 339 + $description[] = pht( 340 + "A revision's reviewers can always view it."); 341 + if ($this->getRepository()) { 342 + $description[] = pht( 343 + 'This revision belongs to a repository. Other users must be able '. 344 + 'to view the repository in order to view this revision.'); 345 + } 346 + break; 347 + } 348 + 349 + return $description; 350 + } 351 + 332 352 public function getUsersToNotifyOfTokenGiven() { 333 353 return array( 334 354 $this->getAuthorPHID(),
+4
src/applications/diviner/storage/DivinerLiveBook.php
··· 61 61 return false; 62 62 } 63 63 64 + public function describeAutomaticCapability($capability) { 65 + return null; 66 + } 67 + 64 68 }
+4
src/applications/diviner/storage/DivinerLiveSymbol.php
··· 143 143 return $this->getBook()->hasAutomaticCapability($capability, $viewer); 144 144 } 145 145 146 + public function describeAutomaticCapability($capability) { 147 + return pht('Atoms inherit the policies of the books they are part of.'); 148 + } 149 + 146 150 147 151 /* -( Markup Interface )--------------------------------------------------- */ 148 152
+4
src/applications/doorkeeper/storage/DoorkeeperExternalObject.php
··· 77 77 return false; 78 78 } 79 79 80 + public function describeAutomaticCapability($capability) { 81 + return null; 82 + } 83 + 80 84 }
+4
src/applications/feed/story/PhabricatorFeedStory.php
··· 324 324 return false; 325 325 } 326 326 327 + public function describeAutomaticCapability($capability) { 328 + return null; 329 + } 330 + 327 331 }
+4
src/applications/files/storage/PhabricatorFile.php
··· 830 830 return false; 831 831 } 832 832 833 + public function describeAutomaticCapability($capability) { 834 + return null; 835 + } 836 + 833 837 834 838 /* -( PhabricatorSubscribableInterface Implementation )-------------------- */ 835 839
+4
src/applications/flag/storage/PhabricatorFlag.php
··· 50 50 return ($viewer->getPHID() == $this->getOwnerPHID()); 51 51 } 52 52 53 + public function describeAutomaticCapability($capability) { 54 + return pht('Flags are private. Only you can view or edit your flags.'); 55 + } 56 + 53 57 }
+6
src/applications/herald/storage/HeraldRule.php
··· 202 202 } 203 203 } 204 204 205 + public function describeAutomaticCapability($capability) { 206 + // TODO: (T603) Sort this out. 207 + return null; 208 + } 209 + 210 + 205 211 }
+6
src/applications/legalpad/storage/LegalpadDocument.php
··· 96 96 return ($user->getPHID() == $this->getCreatorPHID()); 97 97 } 98 98 99 + public function describeAutomaticCapability($capability) { 100 + return pht( 101 + 'The author of a document can always view and edit it.'); 102 + } 103 + 104 + 99 105 /* -( PhabricatorApplicationTransactionInterface )------------------------- */ 100 106 101 107 public function getApplicationTransactionEditor() {
+4
src/applications/macro/storage/PhabricatorFileImageMacro.php
··· 61 61 return false; 62 62 } 63 63 64 + public function describeAutomaticCapability($capability) { 65 + return null; 66 + } 67 + 64 68 } 65 69
+4
src/applications/mailinglists/storage/PhabricatorMetaMTAMailingList.php
··· 37 37 return false; 38 38 } 39 39 40 + public function describeAutomaticCapability($capability) { 41 + return null; 42 + } 43 + 40 44 }
+5
src/applications/maniphest/storage/ManiphestTask.php
··· 221 221 return false; 222 222 } 223 223 224 + public function describeAutomaticCapability($capability) { 225 + return pht( 226 + 'The owner of a task can always view and edit it.'); 227 + } 228 + 224 229 225 230 /* -( PhabricatorTokenReceiverInterface )---------------------------------- */ 226 231
+4
src/applications/owners/storage/PhabricatorOwnersPackage.php
··· 30 30 return false; 31 31 } 32 32 33 + public function describeAutomaticCapability($capability) { 34 + return null; 35 + } 36 + 33 37 public function getConfiguration() { 34 38 return array( 35 39 // This information is better available from the history table.
+5
src/applications/paste/storage/PhabricatorPaste.php
··· 61 61 return ($user->getPHID() == $this->getAuthorPHID()); 62 62 } 63 63 64 + public function describeAutomaticCapability($capability) { 65 + return pht( 66 + 'The author of a paste can always view and edit it.'); 67 + } 68 + 64 69 public function getFullName() { 65 70 $title = $this->getTitle(); 66 71 if (!$title) {
+5
src/applications/people/storage/PhabricatorExternalAccount.php
··· 102 102 return ($viewer->getPHID() == $this->getUserPHID()); 103 103 } 104 104 105 + public function describeAutomaticCapability($capability) { 106 + // TODO: (T603) This is complicated. 107 + return null; 108 + } 109 + 105 110 }
+9
src/applications/people/storage/PhabricatorUser.php
··· 834 834 return $this->getPHID() && ($viewer->getPHID() === $this->getPHID()); 835 835 } 836 836 837 + public function describeAutomaticCapability($capability) { 838 + switch ($capability) { 839 + case PhabricatorPolicyCapability::CAN_EDIT: 840 + return pht('Only you can edit your information.'); 841 + default: 842 + return null; 843 + } 844 + } 845 + 837 846 838 847 /* -( PhabricatorCustomFieldInterface )------------------------------------ */ 839 848
+14
src/applications/phame/storage/PhameBlog.php
··· 188 188 } 189 189 190 190 191 + public function describeAutomaticCapability($capability) { 192 + switch ($capability) { 193 + case PhabricatorPolicyCapability::CAN_VIEW: 194 + return pht( 195 + 'Users who can edit or post on a blog can always view it.'); 196 + case PhabricatorPolicyCapability::CAN_JOIN: 197 + return pht( 198 + 'Users who can edit a blog can always post on it.'); 199 + } 200 + 201 + return null; 202 + } 203 + 204 + 191 205 /* -( PhabricatorMarkupInterface Implementation )-------------------------- */ 192 206 193 207
+6
src/applications/phame/storage/PhamePost.php
··· 150 150 } 151 151 152 152 153 + public function describeAutomaticCapability($capability) { 154 + return pht( 155 + 'The author of a blog post can always view and edit it.'); 156 + } 157 + 158 + 153 159 /* -( PhabricatorMarkupInterface Implementation )-------------------------- */ 154 160 155 161
+4
src/applications/phid/PhabricatorObjectHandle.php
··· 236 236 return true; 237 237 } 238 238 239 + public function describeAutomaticCapability($capability) { 240 + return null; 241 + } 242 + 239 243 }
+4
src/applications/phlux/storage/PhluxVariable.php
··· 45 45 return false; 46 46 } 47 47 48 + public function describeAutomaticCapability($capability) { 49 + return null; 50 + } 51 + 48 52 }
+4
src/applications/pholio/storage/PholioImage.php
··· 104 104 return $this->getMock()->hasAutomaticCapability($capability, $viewer); 105 105 } 106 106 107 + public function describeAutomaticCapability($capability) { 108 + return null; 109 + } 110 + 107 111 }
+4
src/applications/pholio/storage/PholioMock.php
··· 149 149 return ($viewer->getPHID() == $this->getAuthorPHID()); 150 150 } 151 151 152 + public function describeAutomaticCapability($capability) { 153 + return pht("A mock's owner can always view and edit it."); 154 + } 155 + 152 156 153 157 /* -( PhabricatorMarkupInterface )----------------------------------------- */ 154 158
+6 -1
src/applications/phortune/storage/PhortuneAccount.php
··· 46 46 } 47 47 48 48 public function getPolicy($capability) { 49 - return false; 49 + return PhabricatorPolicies::POLICY_NOONE; 50 50 } 51 51 52 52 public function hasAutomaticCapability($capability, PhabricatorUser $viewer) { 53 53 $members = array_fuse($this->getMemberPHIDs()); 54 54 return isset($members[$viewer->getPHID()]); 55 55 } 56 + 57 + public function describeAutomaticCapability($capability) { 58 + return pht('Members of an account can always view and edit it.'); 59 + } 60 + 56 61 57 62 }
+5
src/applications/phortune/storage/PhortunePaymentMethod.php
··· 107 107 $viewer); 108 108 } 109 109 110 + public function describeAutomaticCapability($capability) { 111 + return pht( 112 + 'Members of an account can always view and edit its payment methods.'); 113 + } 114 + 110 115 }
+4
src/applications/phortune/storage/PhortuneProduct.php
··· 73 73 return false; 74 74 } 75 75 76 + public function describeAutomaticCapability($capability) { 77 + return null; 78 + } 79 + 76 80 }
+8
src/applications/phriction/storage/PhrictionDocument.php
··· 126 126 return false; 127 127 } 128 128 129 + public function describeAutomaticCapability($capability) { 130 + if ($this->hasProject()) { 131 + return pht( 132 + "This is a project wiki page, and inherits the project's policies."); 133 + } 134 + return null; 135 + } 136 + 129 137 public function isAutomaticallySubscribed($phid) { 130 138 return false; 131 139 }
+4
src/applications/policy/__tests__/PhabricatorPolicyTestObject.php
··· 38 38 return $this; 39 39 } 40 40 41 + public function describeAutomaticCapability($capability) { 42 + return null; 43 + } 44 + 41 45 }
+23
src/applications/policy/application/PhabricatorApplicationPolicy.php
··· 1 + <?php 2 + 3 + final class PhabricatorApplicationPolicy extends PhabricatorApplication { 4 + 5 + public function shouldAppearInLaunchView() { 6 + return false; 7 + } 8 + 9 + public function canUninstall() { 10 + return false; 11 + } 12 + 13 + public function getRoutes() { 14 + return array( 15 + '/policy/' => array( 16 + 'explain/(?P<phid>[^/]+)/(?P<capability>[^/]+)/' 17 + => 'PhabricatorPolicyExplainController', 18 + ), 19 + ); 20 + } 21 + 22 + } 23 +
+5
src/applications/policy/controller/PhabricatorPolicyController.php
··· 1 + <?php 2 + 3 + abstract class PhabricatorPolicyController extends PhabricatorController { 4 + 5 + }
+73
src/applications/policy/controller/PhabricatorPolicyExplainController.php
··· 1 + <?php 2 + 3 + final class PhabricatorPolicyExplainController 4 + extends PhabricatorPolicyController { 5 + 6 + private $phid; 7 + private $capability; 8 + 9 + public function shouldAllowPublic() { 10 + return true; 11 + } 12 + 13 + public function willProcessRequest(array $data) { 14 + $this->phid = $data['phid']; 15 + $this->capability = $data['capability']; 16 + } 17 + 18 + public function processRequest() { 19 + $request = $this->getRequest(); 20 + $viewer = $request->getUser(); 21 + 22 + $phid = $this->phid; 23 + $capability = $this->capability; 24 + 25 + $object = id(new PhabricatorObjectQuery()) 26 + ->setViewer($viewer) 27 + ->withPHIDs(array($phid)) 28 + ->executeOne(); 29 + if (!$object) { 30 + return new Aphront404Response(); 31 + } 32 + 33 + $policies = PhabricatorPolicyQuery::loadPolicies( 34 + $viewer, 35 + $object); 36 + 37 + $policy = idx($policies, $capability); 38 + if (!$policy) { 39 + return new Aphront404Response(); 40 + } 41 + 42 + $handle = id(new PhabricatorHandleQuery()) 43 + ->setViewer($viewer) 44 + ->withPHIDs(array($phid)) 45 + ->executeOne(); 46 + $object_uri = $handle->getURI(); 47 + 48 + $explanation = $policy->getExplanation($capability); 49 + $auto_info = (array)$object->describeAutomaticCapability($capability); 50 + 51 + foreach ($auto_info as $key => $info) { 52 + $auto_info[$key] = phutil_tag('li', array(), $info); 53 + } 54 + if ($auto_info) { 55 + $auto_info = phutil_tag('ul', array(), $auto_info); 56 + } 57 + 58 + $content = array( 59 + $explanation, 60 + $auto_info, 61 + ); 62 + 63 + $dialog = id(new AphrontDialogView()) 64 + ->setUser($viewer) 65 + ->setClass('aphront-access-dialog') 66 + ->setTitle(pht('Policy Details')) 67 + ->appendChild($content) 68 + ->addCancelButton($object_uri, pht('Done')); 69 + 70 + return id(new AphrontDialogResponse())->setDialog($dialog); 71 + } 72 + 73 + }
+29
src/applications/policy/filter/PhabricatorPolicy.php
··· 130 130 return $this->getName(); 131 131 } 132 132 133 + public function getExplanation($capability) { 134 + switch ($capability) { 135 + case PhabricatorPolicyCapability::CAN_VIEW: 136 + switch ($this->getPHID()) { 137 + case PhabricatorPolicies::POLICY_PUBLIC: 138 + return pht('Visible to the entire internet.'); 139 + case PhabricatorPolicies::POLICY_USER: 140 + return pht('Visible to all logged in users.'); 141 + case PhabricatorPolicies::POLICY_ADMIN: 142 + return pht('Visible to all administrators.'); 143 + case PhabricatorPolicies::POLICY_NOONE: 144 + return pht('Not visible to anyone by default.'); 145 + } 146 + 147 + switch ($this->getType()) { 148 + case PhabricatorPolicyType::TYPE_PROJECT: 149 + return pht( 150 + 'Visible to members of the project "%s".', 151 + $this->getName()); 152 + case PhabricatorPolicyType::TYPE_MASKED: 153 + return pht('Other: %s', $this->getName()); 154 + } 155 + break; 156 + } 157 + 158 + 159 + return pht('?'); 160 + } 161 + 133 162 public function getFullName() { 134 163 switch ($this->getType()) { 135 164 case PhabricatorPolicyType::TYPE_PROJECT:
+28
src/applications/policy/interface/PhabricatorPolicyInterface.php
··· 6 6 public function getPolicy($capability); 7 7 public function hasAutomaticCapability($capability, PhabricatorUser $viewer); 8 8 9 + /** 10 + * Describe exceptions to an object's policy setting. 11 + * 12 + * The intent of this method is to explain and inform users about special 13 + * cases which override configured policy settings. If this object has any 14 + * such exceptions, explain them by returning one or more human-readable 15 + * strings which describe the exception in a broad, categorical way. For 16 + * example: 17 + * 18 + * - "The owner of an X can always view and edit it." 19 + * - "Members of a Y can always view it." 20 + * 21 + * You can return `null`, a single string, or a list of strings. 22 + * 23 + * The relevant capability to explain (like "view") is passed as a parameter. 24 + * You should tailor any messages to be relevant to that capability, although 25 + * they do not need to exclusively describe the capability, and in some cases 26 + * being more general ("The author can view and edit...") will be more clear. 27 + * 28 + * Messages should describe general rules, not specific objects, because the 29 + * main goal is to teach the user the rules. For example, write "the author", 30 + * not the specific author's name. 31 + * 32 + * @param const @{class:PhabricatorPolicyCapability} constant. 33 + * @return wild Description of policy exceptions. See above. 34 + */ 35 + public function describeAutomaticCapability($capability); 36 + 9 37 }
+18 -5
src/applications/policy/query/PhabricatorPolicyQuery.php
··· 15 15 return $this; 16 16 } 17 17 18 - public static function renderPolicyDescriptions( 18 + public static function loadPolicies( 19 19 PhabricatorUser $viewer, 20 - PhabricatorPolicyInterface $object, 21 - $icon=false) { 20 + PhabricatorPolicyInterface $object) { 22 21 23 22 $results = array(); 24 23 $policies = null; ··· 32 31 } 33 32 34 33 if (isset($global[$policy])) { 35 - $results[$capability] = $global[$policy]->renderDescription($icon); 34 + $results[$capability] = $global[$policy]; 36 35 continue; 37 36 } 38 37 ··· 45 44 ->execute(); 46 45 } 47 46 48 - $results[$capability] = $policies[$policy]->renderDescription($icon); 47 + $results[$capability] = $policies[$policy]; 49 48 } 50 49 51 50 return $results; 51 + } 52 + 53 + public static function renderPolicyDescriptions( 54 + PhabricatorUser $viewer, 55 + PhabricatorPolicyInterface $object, 56 + $icon = false) { 57 + 58 + $policies = self::loadPolicies($viewer, $object); 59 + 60 + foreach ($policies as $capability => $policy) { 61 + $policies[$capability] = $policy->renderDescription($icon); 62 + } 63 + 64 + return $policies; 52 65 } 53 66 54 67 public function execute() {
+16
src/applications/ponder/storage/PonderAnswer.php
··· 145 145 public function hasAutomaticCapability($capability, PhabricatorUser $viewer) { 146 146 switch ($capability) { 147 147 case PhabricatorPolicyCapability::CAN_VIEW: 148 + if ($this->getAuthorPHID() == $viewer->getPHID()) { 149 + return true; 150 + } 148 151 return $this->getQuestion()->hasAutomaticCapability( 149 152 $capability, 150 153 $viewer); 151 154 case PhabricatorPolicyCapability::CAN_EDIT: 152 155 return ($this->getAuthorPHID() == $viewer->getPHID()); 153 156 } 157 + } 158 + 159 + 160 + public function describeAutomaticCapability($capability) { 161 + $out = array(); 162 + $out[] = pht("The author of an answer can always view and edit it."); 163 + switch ($capability) { 164 + case PhabricatorPolicyCapability::CAN_VIEW: 165 + $out[] = pht( 166 + "The user who asks a question can always view the answers."); 167 + break; 168 + } 169 + return $out; 154 170 } 155 171 156 172
+6
src/applications/ponder/storage/PonderQuestion.php
··· 203 203 return ($viewer->getPHID() == $this->getAuthorPHID()); 204 204 } 205 205 206 + 207 + public function describeAutomaticCapability($capability) { 208 + return pht( 209 + 'The user who asked a question can always view and edit it.'); 210 + } 211 + 206 212 public function getOriginalTitle() { 207 213 // TODO: Make this actually save/return the original title. 208 214 return $this->getTitle();
+10
src/applications/project/storage/PhabricatorProject.php
··· 60 60 return false; 61 61 } 62 62 63 + public function describeAutomaticCapability($capability) { 64 + switch ($capability) { 65 + case PhabricatorPolicyCapability::CAN_VIEW: 66 + return pht("Members of a project can always view it."); 67 + case PhabricatorPolicyCapability::CAN_JOIN: 68 + return pht("Users who can edit a project can always join it."); 69 + } 70 + return null; 71 + } 72 + 63 73 public function isUserMember($user_phid) { 64 74 return $this->assertAttachedKey($this->sparseMembers, $user_phid); 65 75 }
+5
src/applications/releeph/storage/ReleephBranch.php
··· 189 189 return $this->getProject()->hasAutomaticCapability($capability, $viewer); 190 190 } 191 191 192 + public function describeAutomaticCapability($capability) { 193 + return null; 194 + } 195 + 196 + 192 197 }
+5
src/applications/releeph/storage/ReleephProject.php
··· 193 193 return false; 194 194 } 195 195 196 + public function describeAutomaticCapability($capability) { 197 + return null; 198 + } 199 + 200 + 196 201 }
+5
src/applications/releeph/storage/ReleephRequest.php
··· 306 306 return false; 307 307 } 308 308 309 + public function describeAutomaticCapability($capability) { 310 + return null; 311 + } 312 + 313 + 309 314 310 315 /* -( PhabricatorCustomFieldInterface )------------------------------------ */ 311 316
+5
src/applications/repository/storage/PhabricatorRepository.php
··· 701 701 return false; 702 702 } 703 703 704 + public function describeAutomaticCapability($capability) { 705 + return null; 706 + } 707 + 708 + 704 709 705 710 /* -( PhabricatorMarkupInterface )----------------------------------------- */ 706 711
+4
src/applications/repository/storage/PhabricatorRepositoryArcanistProject.php
··· 87 87 return false; 88 88 } 89 89 90 + public function describeAutomaticCapability($capability) { 91 + return null; 92 + } 93 + 90 94 }
+6
src/applications/repository/storage/PhabricatorRepositoryCommit.php
··· 167 167 return $this->getRepository()->hasAutomaticCapability($capability, $viewer); 168 168 } 169 169 170 + public function describeAutomaticCapability($capability) { 171 + return pht( 172 + 'Commits inherit the policies of the repository they belong to.'); 173 + } 174 + 175 + 170 176 /* -( PhabricatorTokenReceiverInterface )---------------------------------- */ 171 177 172 178 public function getUsersToNotifyOfTokenGiven() {
+7
src/applications/search/storage/PhabricatorNamedQuery.php
··· 39 39 return false; 40 40 } 41 41 42 + public function describeAutomaticCapability($capability) { 43 + return pht( 44 + 'The queries you have saved are private. Only you can view or edit '. 45 + 'them.'); 46 + } 47 + 48 + 42 49 }
+4
src/applications/search/storage/PhabricatorSavedQuery.php
··· 63 63 return false; 64 64 } 65 65 66 + public function describeAutomaticCapability($capability) { 67 + return null; 68 + } 69 + 66 70 }
+6
src/applications/slowvote/storage/PhabricatorSlowvotePoll.php
··· 96 96 return ($viewer->getPHID() == $this->getAuthorPHID()); 97 97 } 98 98 99 + public function describeAutomaticCapability($capability) { 100 + return pht( 101 + 'The author of a poll can always view and edit it.'); 102 + } 103 + 104 + 99 105 100 106 /* -( PhabricatorSubscribableInterface )----------------------------------- */ 101 107
+4
src/applications/tokens/storage/PhabricatorToken.php
··· 27 27 return false; 28 28 } 29 29 30 + public function describeAutomaticCapability($capability) { 31 + return null; 32 + } 33 + 30 34 public function renderIcon() { 31 35 // TODO: Maybe move to a View class? 32 36
+13
src/applications/tokens/storage/PhabricatorTokenGiven.php
··· 48 48 } 49 49 } 50 50 51 + public function describeAutomaticCapability($capability) { 52 + switch ($capability) { 53 + case PhabricatorPolicyCapability::CAN_VIEW: 54 + return pht( 55 + 'A token inherits the policies of the object it is awarded to.'); 56 + case PhabricatorPolicyCapability::CAN_EDIT: 57 + return pht( 58 + 'The user who gave a token can always edit it.'); 59 + } 60 + return null; 61 + } 62 + 63 + 51 64 }
+6
src/applications/transactions/storage/PhabricatorApplicationTransaction.php
··· 537 537 return ($viewer->getPHID() == $this->getAuthorPHID()); 538 538 } 539 539 540 + public function describeAutomaticCapability($capability) { 541 + // TODO: (T603) Exact policies are unclear here. 542 + return null; 543 + } 544 + 545 + 540 546 }
+5
src/applications/transactions/storage/PhabricatorApplicationTransactionComment.php
··· 113 113 return ($viewer->getPHID() == $this->getAuthorPHID()); 114 114 } 115 115 116 + public function describeAutomaticCapability($capability) { 117 + // TODO: (T603) Policies are murky. 118 + return null; 119 + } 120 + 116 121 }
+27 -5
src/view/phui/PHUIHeaderView.php
··· 148 148 } 149 149 150 150 if ($this->policyObject) { 151 - $descriptions = PhabricatorPolicyQuery::renderPolicyDescriptions( 152 - $this->getUser(), 153 - $this->policyObject, 154 - true); 155 - $property_list[] = $descriptions[PhabricatorPolicyCapability::CAN_VIEW]; 151 + $property_list[] = $this->renderPolicyProperty($this->policyObject); 156 152 } 157 153 158 154 $header[] = phutil_tag( ··· 179 175 )); 180 176 } 181 177 178 + private function renderPolicyProperty(PhabricatorPolicyInterface $object) { 179 + $policies = PhabricatorPolicyQuery::loadPolicies( 180 + $this->getUser(), 181 + $object); 182 + 183 + $view_capability = PhabricatorPolicyCapability::CAN_VIEW; 184 + $policy = idx($policies, $view_capability); 185 + if (!$policy) { 186 + return null; 187 + } 182 188 189 + $phid = $object->getPHID(); 190 + 191 + $icon = id(new PHUIIconView()) 192 + ->setSpriteSheet(PHUIIconView::SPRITE_STATUS) 193 + ->setSpriteIcon($policy->getIcon()); 194 + 195 + $link = javelin_tag( 196 + 'a', 197 + array( 198 + 'href' => '/policy/explain/'.$phid.'/'.$view_capability.'/', 199 + 'sigil' => 'workflow', 200 + ), 201 + $policy->getName()); 202 + 203 + return array($icon, $link); 204 + } 183 205 }
+5
webroot/rsrc/css/aphront/dialog-view.css
··· 111 111 .aphront-access-dialog { 112 112 width: 50%; 113 113 } 114 + 115 + .aphront-access-dialog ul { 116 + margin: 12px 24px; 117 + list-style: circle; 118 + }