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

Migrate project membership to edges

Summary:
- Store project members in edges.
- Migrate existing members to edge storage.
- Delete PhabricatorProjectAffiliation.
- I left the actual underlying data around just in case something goes wrong; we can delete it evenutally.

Test Plan:
- Ran migration.
- Created a new project.
- Joined and left a project.
- Added and removed project members.
- Manually called PhabricatorOwnersOwner::loadAffiliatedUserPHIDs() to verify its behavior.

Reviewers: vrana, btrahan

Reviewed By: vrana

CC: aran

Maniphest Tasks: T603

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

+162 -163
+49
resources/sql/patches/migrate-project-edges.php
··· 1 + <?php 2 + 3 + /* 4 + * Copyright 2012 Facebook, Inc. 5 + * 6 + * Licensed under the Apache License, Version 2.0 (the "License"); 7 + * you may not use this file except in compliance with the License. 8 + * You may obtain a copy of the License at 9 + * 10 + * http://www.apache.org/licenses/LICENSE-2.0 11 + * 12 + * Unless required by applicable law or agreed to in writing, software 13 + * distributed under the License is distributed on an "AS IS" BASIS, 14 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 + * See the License for the specific language governing permissions and 16 + * limitations under the License. 17 + */ 18 + 19 + echo "Migrating project members to edges...\n"; 20 + foreach (new LiskMigrationIterator(new PhabricatorProject()) as $proj) { 21 + $id = $proj->getID(); 22 + echo "Project {$id}: "; 23 + 24 + $members = queryfx_all( 25 + $proj->establishConnection('r'), 26 + 'SELECT userPHID FROM %T WHERE projectPHID = %s', 27 + 'project_affiliation', 28 + $proj->getPHID()); 29 + 30 + if (!$members) { 31 + echo "-\n"; 32 + continue; 33 + } 34 + 35 + $members = ipull($members, 'userPHID'); 36 + 37 + $editor = new PhabricatorEdgeEditor(); 38 + $editor->setSuppressEvents(true); 39 + foreach ($members as $user_phid) { 40 + $editor->addEdge( 41 + $proj->getPHID(), 42 + PhabricatorEdgeConfig::TYPE_PROJ_MEMBER, 43 + $user_phid); 44 + } 45 + $editor->save(); 46 + echo "OKAY\n"; 47 + } 48 + 49 + echo "Done.\n";
-2
src/__phutil_library_map__.php
··· 894 894 'PhabricatorPolicyTestQuery' => 'applications/policy/__tests__/PhabricatorPolicyTestQuery.php', 895 895 'PhabricatorProfileHeaderView' => 'view/layout/PhabricatorProfileHeaderView.php', 896 896 'PhabricatorProject' => 'applications/project/storage/PhabricatorProject.php', 897 - 'PhabricatorProjectAffiliation' => 'applications/project/storage/PhabricatorProjectAffiliation.php', 898 897 'PhabricatorProjectConstants' => 'applications/project/constants/PhabricatorProjectConstants.php', 899 898 'PhabricatorProjectController' => 'applications/project/controller/PhabricatorProjectController.php', 900 899 'PhabricatorProjectCreateController' => 'applications/project/controller/PhabricatorProjectCreateController.php', ··· 1933 1932 'PhabricatorPolicyTestQuery' => 'PhabricatorPolicyQuery', 1934 1933 'PhabricatorProfileHeaderView' => 'AphrontView', 1935 1934 'PhabricatorProject' => 'PhabricatorProjectDAO', 1936 - 'PhabricatorProjectAffiliation' => 'PhabricatorProjectDAO', 1937 1935 'PhabricatorProjectController' => 'PhabricatorController', 1938 1936 'PhabricatorProjectCreateController' => 'PhabricatorProjectController', 1939 1937 'PhabricatorProjectDAO' => 'PhabricatorLiskDAO',
+7 -6
src/applications/owners/storage/PhabricatorOwnersOwner.php
··· 58 58 array()); 59 59 60 60 $users_in_project_phids = array(); 61 - if (idx($all_phids, PhabricatorPHIDConstants::PHID_TYPE_PROJ)) { 62 - $users_in_project_phids = mpull( 63 - id(new PhabricatorProjectAffiliation())->loadAllWhere( 64 - 'projectPHID IN (%Ls)', 65 - idx($all_phids, PhabricatorPHIDConstants::PHID_TYPE_PROJ, array())), 66 - 'getUserPHID'); 61 + $project_phids = idx($all_phids, PhabricatorPHIDConstants::PHID_TYPE_PROJ); 62 + if ($project_phids) { 63 + $query = id(new PhabricatorEdgeQuery()) 64 + ->withSourcePHIDs($project_phids) 65 + ->withEdgeTypes(array(PhabricatorEdgeConfig::TYPE_PROJ_MEMBER)); 66 + $query->execute(); 67 + $users_in_project_phids = $query->getDestinationPHIDs(); 67 68 } 68 69 69 70 return array_unique(array_merge($users_in_project_phids, $user_phids));
+7 -7
src/applications/project/controller/PhabricatorProjectCreateController.php
··· 35 35 36 36 try { 37 37 $xactions = array(); 38 + 38 39 $xaction = new PhabricatorProjectTransaction(); 39 40 $xaction->setTransactionType( 40 41 PhabricatorProjectTransactionType::TYPE_NAME); 41 42 $xaction->setNewValue($request->getStr('name')); 43 + $xactions[] = $xaction; 44 + 45 + $xaction = new PhabricatorProjectTransaction(); 46 + $xaction->setTransactionType( 47 + PhabricatorProjectTransactionType::TYPE_MEMBERS); 48 + $xaction->setNewValue(array($user->getPHID())); 42 49 $xactions[] = $xaction; 43 50 44 51 $editor = new PhabricatorProjectEditor($project); ··· 55 62 $project->save(); 56 63 $profile->setProjectPHID($project->getPHID()); 57 64 $profile->save(); 58 - 59 - id(new PhabricatorProjectAffiliation()) 60 - ->setUserPHID($user->getPHID()) 61 - ->setProjectPHID($project->getPHID()) 62 - ->setRole('Owner') 63 - ->setIsOwner(true) 64 - ->save(); 65 65 66 66 if ($request->isAjax()) { 67 67 return id(new AphrontAjaxResponse())
+7 -6
src/applications/project/controller/PhabricatorProjectListController.php
··· 81 81 $profiles = mpull($profiles, null, 'getProjectPHID'); 82 82 } 83 83 84 - $affil_groups = array(); 84 + $edge_query = new PhabricatorEdgeQuery(); 85 85 if ($projects) { 86 - $affil_groups = PhabricatorProjectAffiliation::loadAllForProjectPHIDs( 87 - $project_phids); 86 + $edge_query 87 + ->withSourcePHIDs($project_phids) 88 + ->withEdgeTypes(array(PhabricatorEdgeConfig::TYPE_PROJ_MEMBER)) 89 + ->execute(); 88 90 } 89 91 90 92 $tasks = array(); ··· 104 106 } 105 107 } 106 108 107 - 108 109 $rows = array(); 109 110 foreach ($projects as $project) { 110 111 $phid = $project->getPHID(); 111 112 112 113 $profile = idx($profiles, $phid); 113 - $affiliations = $affil_groups[$phid]; 114 + $members = $edge_query->getDestinationPHIDs(array($phid)); 114 115 115 116 $group = idx($groups, $phid, array()); 116 117 $task_count = count($group); 117 118 118 - $population = count($affiliations); 119 + $population = count($members); 119 120 120 121 if ($profile) { 121 122 $blurb = $profile->getBlurb();
+25 -41
src/applications/project/editor/PhabricatorProjectEditor.php
··· 22 22 private $user; 23 23 private $projectName; 24 24 25 - private $addAffiliations; 26 - private $remAffiliations; 25 + private $addEdges = array(); 26 + private $remEdges = array(); 27 27 28 28 public function __construct(PhabricatorProject $project) { 29 29 $this->project = $project; ··· 67 67 } 68 68 69 69 try { 70 - $project->save(); 70 + $project->openTransaction(); 71 + $project->save(); 71 72 72 - foreach ($transactions as $xaction) { 73 - $xaction->setAuthorPHID($user->getPHID()); 74 - $xaction->setProjectID($project->getID()); 75 - $xaction->save(); 76 - } 77 - 78 - foreach ($this->remAffiliations as $affil) { 79 - $affil->delete(); 80 - } 73 + $edge_type = PhabricatorEdgeConfig::TYPE_PROJ_MEMBER; 74 + $editor = new PhabricatorEdgeEditor(); 75 + $editor->setUser($this->user); 76 + foreach ($this->remEdges as $phid) { 77 + $editor->removeEdge($project->getPHID(), $edge_type, $phid); 78 + } 79 + foreach ($this->addEdges as $phid) { 80 + $editor->addEdge($project->getPHID(), $edge_type, $phid); 81 + } 82 + $editor->save(); 81 83 82 - foreach ($this->addAffiliations as $affil) { 83 - $affil->setProjectPHID($project->getPHID()); 84 - $affil->save(); 85 - } 84 + foreach ($transactions as $xaction) { 85 + $xaction->setAuthorPHID($user->getPHID()); 86 + $xaction->setProjectID($project->getID()); 87 + $xaction->save(); 88 + } 89 + $project->saveTransaction(); 86 90 87 91 foreach ($transactions as $xaction) { 88 92 $this->publishTransactionStory($project, $xaction); ··· 142 146 $xaction->setOldValue($project->getStatus()); 143 147 break; 144 148 case PhabricatorProjectTransactionType::TYPE_MEMBERS: 145 - $affils = $project->loadAffiliations(); 146 - $project->attachAffiliations($affils); 149 + $member_phids = $project->loadMemberPHIDs(); 150 + $project->attachMemberPHIDs($member_phids); 147 151 148 - $old_value = mpull($affils, 'getUserPHID'); 149 - $old_value = array_values($old_value); 152 + $old_value = array_values($member_phids); 150 153 $xaction->setOldValue($old_value); 151 154 152 155 $new_value = $xaction->getNewValue(); ··· 178 181 case PhabricatorProjectTransactionType::TYPE_MEMBERS: 179 182 $old = array_fill_keys($xaction->getOldValue(), true); 180 183 $new = array_fill_keys($xaction->getNewValue(), true); 181 - 182 - $add = array(); 183 - $rem = array(); 184 - 185 - foreach ($project->getAffiliations() as $affil) { 186 - if (empty($new[$affil->getUserPHID()])) { 187 - $rem[] = $affil; 188 - } 189 - } 190 - 191 - foreach ($new as $phid => $ignored) { 192 - if (empty($old[$phid])) { 193 - $affil = new PhabricatorProjectAffiliation(); 194 - $affil->setRole(''); 195 - $affil->setUserPHID($phid); 196 - $add[] = $affil; 197 - } 198 - } 199 - 200 - $this->addAffiliations = $add; 201 - $this->remAffiliations = $rem; 184 + $this->addEdges = array_keys(array_diff_key($new, $old)); 185 + $this->remEdges = array_keys(array_diff_key($old, $new)); 202 186 break; 203 187 default: 204 188 throw new Exception("Unknown transaction type '{$type}'!");
+41 -41
src/applications/project/query/PhabricatorProjectQuery.php
··· 60 60 $table = new PhabricatorProject(); 61 61 $conn_r = $table->establishConnection('r'); 62 62 63 - $where = $this->buildWhereClause($conn_r); 64 - $joins = $this->buildJoinsClause($conn_r); 65 - $order = 'ORDER BY name'; 66 - 67 63 $data = queryfx_all( 68 64 $conn_r, 69 - 'SELECT p.* FROM %T p %Q %Q %Q %Q', 65 + 'SELECT p.* FROM %T p %Q %Q %Q %Q %Q', 70 66 $table->getTableName(), 71 - $joins, 72 - $where, 73 - $order, 67 + $this->buildJoinClause($conn_r), 68 + $this->buildWhereClause($conn_r), 69 + $this->buildGroupClause($conn_r), 70 + 'ORDER BY name', 74 71 $this->buildLimitClause($conn_r)); 75 72 76 73 $projects = $table->loadAllFromArray($data); 77 74 78 75 if ($projects && $this->needMembers) { 79 - $members = PhabricatorProjectAffiliation::loadAllForProjectPHIDs( 80 - mpull($projects, 'getPHID')); 76 + $members = id(new PhabricatorEdgeQuery()) 77 + ->withSourcePHIDs(mpull($projects, 'getPHID')) 78 + ->withTypes(array(PhabricatorEdgeConfig::TYPE_PROJ_MEMBER)) 79 + ->execute(); 81 80 foreach ($projects as $project) { 82 - $project->attachAffiliations( 83 - array_values(idx($members, $project->getPHID(), array()))); 81 + $phid = $project->getPHID(); 82 + $project->attachMemberPHIDs(array_keys($members[$phid])); 84 83 } 85 84 } 86 85 ··· 93 92 if ($this->status != self::STATUS_ANY) { 94 93 switch ($this->status) { 95 94 case self::STATUS_OPEN: 96 - $where[] = qsprintf( 97 - $conn_r, 98 - 'status IN (%Ld)', 99 - array( 100 - PhabricatorProjectStatus::STATUS_ACTIVE, 101 - )); 102 - break; 103 - case self::STATUS_CLOSED: 104 - $where[] = qsprintf( 105 - $conn_r, 106 - 'status IN (%Ld)', 107 - array( 108 - PhabricatorProjectStatus::STATUS_ARCHIVED, 109 - )); 110 - break; 111 95 case self::STATUS_ACTIVE: 112 - $where[] = qsprintf( 113 - $conn_r, 114 - 'status = %d', 115 - PhabricatorProjectStatus::STATUS_ACTIVE); 96 + $filter = array( 97 + PhabricatorProjectStatus::STATUS_ACTIVE, 98 + ); 116 99 break; 100 + case self::STATUS_CLOSED: 117 101 case self::STATUS_ARCHIVED: 118 - $where[] = qsprintf( 119 - $conn_r, 120 - 'status = %d', 121 - PhabricatorProjectStatus::STATUS_ARCHIVED); 102 + $filter = array( 103 + PhabricatorProjectStatus::STATUS_ARCHIVED, 104 + ); 122 105 break; 123 106 default: 124 107 throw new Exception( 125 108 "Unknown project status '{$this->status}'!"); 126 109 } 110 + $where[] = qsprintf( 111 + $conn_r, 112 + 'status IN (%Ld)', 113 + $filter); 127 114 } 128 115 129 116 if ($this->ids) { ··· 140 127 $this->phids); 141 128 } 142 129 130 + if ($this->memberPHIDs) { 131 + $where[] = qsprintf( 132 + $conn_r, 133 + 'e.type = %s AND e.dst IN (%Ls)', 134 + PhabricatorEdgeConfig::TYPE_PROJ_MEMBER, 135 + $this->memberPHIDs); 136 + } 137 + 143 138 return $this->formatWhereClause($where); 144 139 } 145 140 146 - private function buildJoinsClause($conn_r) { 147 - $affil_table = new PhabricatorProjectAffiliation(); 141 + private function buildGroupClause($conn_r) { 142 + if ($this->memberPHIDs) { 143 + return 'GROUP BY p.id'; 144 + } else { 145 + return ''; 146 + } 147 + } 148 148 149 + private function buildJoinClause($conn_r) { 149 150 $joins = array(); 151 + 150 152 if ($this->memberPHIDs) { 151 153 $joins[] = qsprintf( 152 154 $conn_r, 153 - 'JOIN %T member ON member.projectPHID = p.phid 154 - AND member.userPHID in (%Ls)', 155 - $affil_table->getTableName(), 156 - $this->memberPHIDs); 155 + 'JOIN %T e ON e.src = p.phid', 156 + PhabricatorEdgeConfig::TABLE_NAME_EDGE); 157 157 } 158 158 159 159 return implode(' ', $joins);
+16 -21
src/applications/project/storage/PhabricatorProject.php
··· 25 25 protected $subprojectPHIDs = array(); 26 26 protected $phrictionSlug; 27 27 28 - private $affiliations; 28 + private $subprojectsNeedUpdate; 29 + private $memberPHIDs; 29 30 30 31 public function getConfiguration() { 31 32 return array( ··· 48 49 return $profile; 49 50 } 50 51 51 - public function getMemberPHIDs() { 52 - return mpull($this->getAffiliations(), 'getUserPHID'); 52 + public function attachMemberPHIDs(array $phids) { 53 + $this->memberPHIDs = $phids; 54 + return $this; 53 55 } 54 56 55 - public function loadMemberPHIDs() { 56 - return mpull($this->loadAffiliations(), 'getUserPHID'); 57 + public function getMemberPHIDs() { 58 + if ($this->memberPHIDs === null) { 59 + throw new Exception("Call attachMemberPHIDs() first!"); 60 + } 61 + return $this->memberPHIDs; 57 62 } 58 63 59 - public function getAffiliations() { 60 - if ($this->affiliations === null) { 61 - throw new Exception('Attach affiliations first!'); 64 + public function loadMemberPHIDs() { 65 + if (!$this->getPHID()) { 66 + return array(); 62 67 } 63 - return $this->affiliations; 64 - } 65 - 66 - public function attachAffiliations(array $affiliations) { 67 - assert_instances_of($affiliations, 'PhabricatorProjectAffiliation'); 68 - $this->affiliations = $affiliations; 69 - return $this; 70 - } 71 - 72 - public function loadAffiliations() { 73 - $affils = PhabricatorProjectAffiliation::loadAllForProjectPHIDs( 74 - array($this->getPHID())); 75 - return $affils[$this->getPHID()]; 68 + return PhabricatorEdgeQuery::loadDestinationPHIDs( 69 + $this->getPHID(), 70 + PhabricatorEdgeConfig::TYPE_PROJ_MEMBER); 76 71 } 77 72 78 73 public function setPhrictionSlug($slug) {
-39
src/applications/project/storage/PhabricatorProjectAffiliation.php
··· 1 - <?php 2 - 3 - /* 4 - * Copyright 2012 Facebook, Inc. 5 - * 6 - * Licensed under the Apache License, Version 2.0 (the "License"); 7 - * you may not use this file except in compliance with the License. 8 - * You may obtain a copy of the License at 9 - * 10 - * http://www.apache.org/licenses/LICENSE-2.0 11 - * 12 - * Unless required by applicable law or agreed to in writing, software 13 - * distributed under the License is distributed on an "AS IS" BASIS, 14 - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 - * See the License for the specific language governing permissions and 16 - * limitations under the License. 17 - */ 18 - 19 - final class PhabricatorProjectAffiliation extends PhabricatorProjectDAO { 20 - 21 - protected $projectPHID; 22 - protected $userPHID; 23 - protected $role; 24 - protected $isOwner = 0; 25 - 26 - public static function loadAllForProjectPHIDs($phids) { 27 - if (!$phids) { 28 - return array(); 29 - } 30 - $default = array_fill_keys($phids, array()); 31 - 32 - $affiliations = id(new PhabricatorProjectAffiliation())->loadAllWhere( 33 - 'projectPHID IN (%Ls) ORDER BY dateCreated', 34 - $phids); 35 - 36 - return mgroup($affiliations, 'getProjectPHID') + $default; 37 - } 38 - 39 - }
+6
src/infrastructure/edges/constants/PhabricatorEdgeConfig.php
··· 38 38 const TYPE_TASK_HAS_RELATED_DREV = 11; 39 39 const TYPE_DREV_HAS_RELATED_TASK = 12; 40 40 41 + const TYPE_PROJ_MEMBER = 13; 42 + const TYPE_MEMBER_OF_PROJ = 14; 43 + 41 44 const TYPE_TEST_NO_CYCLE = 9000; 42 45 43 46 public static function getInverse($edge_type) { ··· 58 61 59 62 self::TYPE_TASK_HAS_RELATED_DREV => self::TYPE_DREV_HAS_RELATED_TASK, 60 63 self::TYPE_DREV_HAS_RELATED_TASK => self::TYPE_TASK_HAS_RELATED_DREV, 64 + 65 + self::TYPE_PROJ_MEMBER => self::TYPE_MEMBER_OF_PROJ, 66 + self::TYPE_MEMBER_OF_PROJ => self::TYPE_PROJ_MEMBER, 61 67 ); 62 68 63 69 return idx($map, $edge_type);
+4
src/infrastructure/storage/patch/PhabricatorBuiltinPatchList.php
··· 940 940 'type' => 'sql', 941 941 'name' => $this->getPatchPath('symbolcontexts.sql'), 942 942 ), 943 + 'migrate-project-edges.php' => array( 944 + 'type' => 'php', 945 + 'name' => $this->getPatchPath('migrate-project-edges.php'), 946 + ), 943 947 ); 944 948 } 945 949