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

Add View, Edit and Join policies to PhabricatorProject

Summary:
- In ProjectQuery, always load the viewer's membership in the project because we need it to perform a CAN_VIEW test.
- Add storage for the view, edit and join policies.
- A user can always view a project if they are a member.
- A user can always join a project if they can edit it.
- Editing a project requires both "view" and "edit" permissions, and edit does not imply view.
- This has no effect on the application yet.

Test Plan: See next diff.

Reviewers: vrana, btrahan

Reviewed By: btrahan

CC: aran

Maniphest Tasks: T603

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

+106 -16
+8
resources/sql/patches/policy-project.sql
··· 1 + ALTER TABLE `{$NAMESPACE}_project`.`project` 2 + ADD `viewPolicy` varchar(64) COLLATE utf8_bin; 3 + 4 + ALTER TABLE `{$NAMESPACE}_project`.`project` 5 + ADD `editPolicy` varchar(64) COLLATE utf8_bin; 6 + 7 + ALTER TABLE `{$NAMESPACE}_project`.`project` 8 + ADD `joinPolicy` varchar(64) COLLATE utf8_bin;
+47 -15
src/applications/project/query/PhabricatorProjectQuery.php
··· 68 68 $table = new PhabricatorProject(); 69 69 $conn_r = $table->establishConnection('r'); 70 70 71 + // NOTE: Because visibility checks for projects depend on whether or not 72 + // the user is a project member, we always load their membership. If we're 73 + // loading all members anyway we can piggyback on that; otherwise we 74 + // do an explicit join. 75 + 76 + $select_clause = ''; 77 + if (!$this->needMembers) { 78 + $select_clause = ', vm.dst viewerIsMember'; 79 + } 80 + 71 81 $data = queryfx_all( 72 82 $conn_r, 73 - 'SELECT p.* FROM %T p %Q %Q %Q %Q %Q', 83 + 'SELECT p.* %Q FROM %T p %Q %Q %Q %Q %Q', 84 + $select_clause, 74 85 $table->getTableName(), 75 86 $this->buildJoinClause($conn_r), 76 87 $this->buildWhereClause($conn_r), 77 88 $this->buildGroupClause($conn_r), 78 - 'ORDER BY name', 89 + $this->buildOrderClause($conn_r), 79 90 $this->buildLimitClause($conn_r)); 80 91 81 92 $projects = $table->loadAllFromArray($data); 82 93 83 - if ($projects && $this->needMembers) { 84 - $etype = PhabricatorEdgeConfig::TYPE_PROJ_MEMBER; 85 - $members = id(new PhabricatorEdgeQuery()) 86 - ->withSourcePHIDs(mpull($projects, 'getPHID')) 87 - ->withEdgeTypes(array($etype)) 88 - ->execute(); 89 - foreach ($projects as $project) { 90 - $phid = $project->getPHID(); 91 - $project->attachMemberPHIDs(array_keys($members[$phid][$etype])); 94 + if ($projects) { 95 + $viewer_phid = $this->getViewer()->getPHID(); 96 + if ($this->needMembers) { 97 + $etype = PhabricatorEdgeConfig::TYPE_PROJ_MEMBER; 98 + $members = id(new PhabricatorEdgeQuery()) 99 + ->withSourcePHIDs(mpull($projects, 'getPHID')) 100 + ->withEdgeTypes(array($etype)) 101 + ->execute(); 102 + foreach ($projects as $project) { 103 + $phid = $project->getPHID(); 104 + $project->attachMemberPHIDs(array_keys($members[$phid][$etype])); 105 + $project->setIsUserMember( 106 + $viewer_phid, 107 + isset($members[$phid][$etype][$viewer_phid])); 108 + } 109 + } else { 110 + foreach ($data as $row) { 111 + $projects[$row['id']]->setIsUserMember( 112 + $viewer_phid, 113 + ($row['viewerIsMember'] !== null)); 114 + } 92 115 } 93 116 } 94 117 ··· 139 162 if ($this->memberPHIDs) { 140 163 $where[] = qsprintf( 141 164 $conn_r, 142 - 'e.type = %s AND e.dst IN (%Ls)', 143 - PhabricatorEdgeConfig::TYPE_PROJ_MEMBER, 165 + 'e.dst IN (%Ls)', 144 166 $this->memberPHIDs); 145 167 } 146 168 ··· 160 182 private function buildJoinClause($conn_r) { 161 183 $joins = array(); 162 184 185 + if (!$this->needMembers) { 186 + $joins[] = qsprintf( 187 + $conn_r, 188 + 'LEFT JOIN %T vm ON vm.src = p.phid AND vm.type = %d AND vm.dst = %s', 189 + PhabricatorEdgeConfig::TABLE_NAME_EDGE, 190 + PhabricatorEdgeConfig::TYPE_PROJ_MEMBER, 191 + $this->getViewer()->getPHID()); 192 + } 193 + 163 194 if ($this->memberPHIDs) { 164 195 $joins[] = qsprintf( 165 196 $conn_r, 166 - 'JOIN %T e ON e.src = p.phid', 167 - PhabricatorEdgeConfig::TABLE_NAME_EDGE); 197 + 'JOIN %T e ON e.src = p.phid AND e.type = %d', 198 + PhabricatorEdgeConfig::TABLE_NAME_EDGE, 199 + PhabricatorEdgeConfig::TYPE_PROJ_MEMBER); 168 200 } 169 201 170 202 return implode(' ', $joins);
+47 -1
src/applications/project/storage/PhabricatorProject.php
··· 26 26 protected $subprojectPHIDs = array(); 27 27 protected $phrictionSlug; 28 28 29 + protected $viewPolicy; 30 + protected $editPolicy; 31 + protected $joinPolicy; 32 + 29 33 private $subprojectsNeedUpdate; 30 34 private $memberPHIDs; 35 + private $sparseMembers = array(); 31 36 32 37 public function getCapabilities() { 33 38 return array( 34 39 PhabricatorPolicyCapability::CAN_VIEW, 40 + PhabricatorPolicyCapability::CAN_EDIT, 41 + PhabricatorPolicyCapability::CAN_JOIN, 35 42 ); 36 43 } 37 44 38 45 public function getPolicy($capability) { 39 - return PhabricatorPolicies::POLICY_USER; 46 + switch ($capability) { 47 + case PhabricatorPolicyCapability::CAN_VIEW: 48 + return $this->getViewPolicy(); 49 + case PhabricatorPolicyCapability::CAN_EDIT: 50 + return $this->getEditPolicy(); 51 + case PhabricatorPolicyCapability::CAN_JOIN: 52 + return $this->getJoinPolicy(); 53 + } 40 54 } 41 55 42 56 public function hasAutomaticCapability($capability, PhabricatorUser $viewer) { 57 + 58 + switch ($capability) { 59 + case PhabricatorPolicyCapability::CAN_VIEW: 60 + if ($this->isUserMember($viewer->getPHID())) { 61 + // Project members can always view a project. 62 + return true; 63 + } 64 + break; 65 + case PhabricatorPolicyCapability::CAN_EDIT: 66 + break; 67 + case PhabricatorPolicyCapability::CAN_JOIN: 68 + $can_edit = PhabricatorPolicyCapability::CAN_EDIT; 69 + if (PhabricatorPolicyFilter::hasCapability($viewer, $this, $can_edit)) { 70 + // Project editors can always join a project. 71 + return true; 72 + } 73 + break; 74 + } 75 + 43 76 return false; 77 + } 78 + 79 + public function isUserMember($user_phid) { 80 + if (!isset($this->sparseMembers[$user_phid])) { 81 + throw new Exception( 82 + "Call setIsUserMember() before isUserMember()!"); 83 + } 84 + return $this->sparseMembers[$user_phid]; 85 + } 86 + 87 + public function setIsUserMember($user_phid, $is_member) { 88 + $this->sparseMembers[$user_phid] = $is_member; 89 + return $this; 44 90 } 45 91 46 92 public function getConfiguration() {
+4
src/infrastructure/storage/patch/PhabricatorBuiltinPatchList.php
··· 960 960 'type' => 'sql', 961 961 'name' => $this->getPatchPath('ponder.sql') 962 962 ), 963 + 'policy-project.sql' => array( 964 + 'type' => 'sql', 965 + 'name' => $this->getPatchPath('policy-project.sql'), 966 + ), 963 967 ); 964 968 } 965 969