@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.

Implement a "Project Members" object policy rule

Summary:
Fixes T9019. Pretty much ripped from D14467. I added the "policy hint" stuff so that you can create a project with this policy immediately.

I really dislike how the "hint" code works, but we //almost// never need to use it and the badness feels fairly well-contained.

Also pick up a quick feedback fix from D14863.

Test Plan:
- Added test coverage, got it to pass.
- Created a project with "Visible To: Project Members".

Reviewers: joshuaspence, chad

Reviewed By: chad

Maniphest Tasks: T9019

Differential Revision: https://secure.phabricator.com/D14869

+198 -5
+2
src/__phutil_library_map__.php
··· 2859 2859 'PhabricatorProjectMemberOfProjectEdgeType' => 'applications/project/edge/PhabricatorProjectMemberOfProjectEdgeType.php', 2860 2860 'PhabricatorProjectMembersDatasource' => 'applications/project/typeahead/PhabricatorProjectMembersDatasource.php', 2861 2861 'PhabricatorProjectMembersEditController' => 'applications/project/controller/PhabricatorProjectMembersEditController.php', 2862 + 'PhabricatorProjectMembersPolicyRule' => 'applications/project/policyrule/PhabricatorProjectMembersPolicyRule.php', 2862 2863 'PhabricatorProjectMembersRemoveController' => 'applications/project/controller/PhabricatorProjectMembersRemoveController.php', 2863 2864 'PhabricatorProjectMoveController' => 'applications/project/controller/PhabricatorProjectMoveController.php', 2864 2865 'PhabricatorProjectNoProjectsDatasource' => 'applications/project/typeahead/PhabricatorProjectNoProjectsDatasource.php', ··· 7195 7196 'PhabricatorProjectMemberOfProjectEdgeType' => 'PhabricatorEdgeType', 7196 7197 'PhabricatorProjectMembersDatasource' => 'PhabricatorTypeaheadCompositeDatasource', 7197 7198 'PhabricatorProjectMembersEditController' => 'PhabricatorProjectController', 7199 + 'PhabricatorProjectMembersPolicyRule' => 'PhabricatorPolicyRule', 7198 7200 'PhabricatorProjectMembersRemoveController' => 'PhabricatorProjectController', 7199 7201 'PhabricatorProjectMoveController' => 'PhabricatorProjectController', 7200 7202 'PhabricatorProjectNoProjectsDatasource' => 'PhabricatorTypeaheadDatasource',
+5
src/applications/policy/filter/PhabricatorPolicyFilter.php
··· 608 608 609 609 $rules = PhabricatorPolicyQuery::getObjectPolicyRules(null); 610 610 611 + // Make sure we have clean, empty policy rule objects. 612 + foreach ($rules as $key => $rule) { 613 + $rules[$key] = clone $rule; 614 + } 615 + 611 616 $results = array(); 612 617 foreach ($map as $key => $object_list) { 613 618 $rule = idx($rules, $key);
+45 -1
src/applications/project/__tests__/PhabricatorProjectCoreTestCase.php
··· 287 287 288 288 // In this second case, set the name first and then the slugs separately. 289 289 $name2 = 'slugproject2'; 290 - 291 290 $xactions = array(); 292 291 293 292 $xactions[] = id(new PhabricatorProjectTransaction()) ··· 394 393 } 395 394 396 395 $this->assertTrue((bool)$caught); 396 + } 397 + 398 + public function testProjectMembersVisibility() { 399 + // This is primarily testing that you can create a project and set the 400 + // visibility or edit policy to "Project Members" immediately. 401 + 402 + $user1 = $this->createUser(); 403 + $user1->save(); 404 + 405 + $user2 = $this->createUser(); 406 + $user2->save(); 407 + 408 + $project = PhabricatorProject::initializeNewProject($user1); 409 + $name = pht('Test Project %d', mt_rand()); 410 + 411 + $xactions = array(); 412 + 413 + $xactions[] = id(new PhabricatorProjectTransaction()) 414 + ->setTransactionType(PhabricatorProjectTransaction::TYPE_NAME) 415 + ->setNewValue($name); 416 + 417 + $xactions[] = id(new PhabricatorProjectTransaction()) 418 + ->setTransactionType(PhabricatorTransactions::TYPE_VIEW_POLICY) 419 + ->setNewValue( 420 + id(new PhabricatorProjectMembersPolicyRule()) 421 + ->getObjectPolicyFullKey()); 422 + 423 + $edge_type = PhabricatorProjectProjectHasMemberEdgeType::EDGECONST; 424 + 425 + $xactions[] = id(new PhabricatorProjectTransaction()) 426 + ->setTransactionType(PhabricatorTransactions::TYPE_EDGE) 427 + ->setMetadataValue('edge:type', $edge_type) 428 + ->setNewValue( 429 + array( 430 + '=' => array($user1->getPHID() => $user1->getPHID()), 431 + )); 432 + 433 + $this->applyTransactions($project, $user1, $xactions); 434 + 435 + $this->assertTrue((bool)$this->refreshProject($project, $user1)); 436 + $this->assertFalse((bool)$this->refreshProject($project, $user2)); 437 + 438 + $this->leaveProject($project, $user1); 439 + 440 + $this->assertFalse((bool)$this->refreshProject($project, $user1)); 397 441 } 398 442 399 443 public function testParentProject() {
+49
src/applications/project/editor/PhabricatorProjectTransactionEditor.php
··· 692 692 return $slugs; 693 693 } 694 694 695 + protected function adjustObjectForPolicyChecks( 696 + PhabricatorLiskDAO $object, 697 + array $xactions) { 698 + 699 + $copy = parent::adjustObjectForPolicyChecks($object, $xactions); 700 + 701 + $type_edge = PhabricatorTransactions::TYPE_EDGE; 702 + $edgetype_member = PhabricatorProjectProjectHasMemberEdgeType::EDGECONST; 703 + 704 + $member_xaction = null; 705 + foreach ($xactions as $xaction) { 706 + if ($xaction->getTransactionType() !== $type_edge) { 707 + continue; 708 + } 709 + 710 + $edgetype = $xaction->getMetadataValue('edge:type'); 711 + if ($edgetype !== $edgetype_member) { 712 + continue; 713 + } 714 + 715 + $member_xaction = $xaction; 716 + } 717 + 718 + if ($member_xaction) { 719 + $object_phid = $object->getPHID(); 720 + 721 + if ($object_phid) { 722 + $members = PhabricatorEdgeQuery::loadDestinationPHIDs( 723 + $object_phid, 724 + PhabricatorProjectProjectHasMemberEdgeType::EDGECONST); 725 + } else { 726 + $members = array(); 727 + } 728 + 729 + $clone_xaction = clone $member_xaction; 730 + $hint = $this->getPHIDTransactionNewValue($clone_xaction, $members); 731 + $rule = new PhabricatorProjectMembersPolicyRule(); 732 + 733 + $hint = array_fuse($hint); 734 + 735 + PhabricatorPolicyRule::passTransactionHintToRule( 736 + $copy, 737 + $rule, 738 + $hint); 739 + } 740 + 741 + return $copy; 742 + } 743 + 695 744 }
+97
src/applications/project/policyrule/PhabricatorProjectMembersPolicyRule.php
··· 1 + <?php 2 + 3 + final class PhabricatorProjectMembersPolicyRule extends PhabricatorPolicyRule { 4 + 5 + private $memberships = array(); 6 + 7 + public function getRuleDescription() { 8 + return pht('members of project'); 9 + } 10 + 11 + public function willApplyRules( 12 + PhabricatorUser $viewer, 13 + array $values, 14 + array $objects) { 15 + 16 + $viewer_phid = $viewer->getPHID(); 17 + if (!$viewer_phid) { 18 + return; 19 + } 20 + 21 + if (empty($this->memberships[$viewer_phid])) { 22 + $this->memberships[$viewer_phid] = array(); 23 + } 24 + 25 + foreach ($objects as $key => $object) { 26 + $cache = $this->getTransactionHint($object); 27 + if ($cache === null) { 28 + continue; 29 + } 30 + 31 + unset($objects[$key]); 32 + 33 + if (isset($cache[$viewer_phid])) { 34 + $this->memberships[$viewer_phid][$object->getPHID()] = true; 35 + } 36 + } 37 + 38 + if (!$objects) { 39 + return; 40 + } 41 + 42 + $object_phids = mpull($objects, 'getPHID'); 43 + $edge_query = id(new PhabricatorEdgeQuery()) 44 + ->withSourcePHIDs(array($viewer_phid)) 45 + ->withDestinationPHIDs($object_phids) 46 + ->withEdgeTypes( 47 + array( 48 + PhabricatorProjectMemberOfProjectEdgeType::EDGECONST, 49 + )); 50 + $edge_query->execute(); 51 + 52 + $memberships = $edge_query->getDestinationPHIDs(); 53 + if (!$memberships) { 54 + return; 55 + } 56 + 57 + $this->memberships[$viewer_phid] += array_fill_keys($memberships, true); 58 + } 59 + 60 + public function applyRule( 61 + PhabricatorUser $viewer, 62 + $value, 63 + PhabricatorPolicyInterface $object) { 64 + $viewer_phid = $viewer->getPHID(); 65 + if (!$viewer_phid) { 66 + return false; 67 + } 68 + 69 + $memberships = idx($this->memberships, $viewer_phid); 70 + return isset($memberships[$object->getPHID()]); 71 + } 72 + 73 + public function getValueControlType() { 74 + return self::CONTROL_TYPE_NONE; 75 + } 76 + 77 + public function canApplyToObject(PhabricatorPolicyInterface $object) { 78 + return ($object instanceof PhabricatorProject); 79 + } 80 + 81 + public function getObjectPolicyKey() { 82 + return 'project.members'; 83 + } 84 + 85 + public function getObjectPolicyName() { 86 + return pht('Project Members'); 87 + } 88 + 89 + public function getObjectPolicyIcon() { 90 + return 'fa-users'; 91 + } 92 + 93 + public function getPolicyExplanation() { 94 + return pht('Project members can take this action.'); 95 + } 96 + 97 + }
-4
src/applications/project/query/PhabricatorProjectQuery.php
··· 95 95 return $this; 96 96 } 97 97 98 - public function getProperty() { 99 - return $this->property; 100 - } 101 - 102 98 public function withDepthBetween($min, $max) { 103 99 $this->minDepth = $min; 104 100 $this->maxDepth = $max;