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

Make edge types modular

Summary:
Ref T5245. I want to add a new capability to edge types, which is a good opportunity to move away from `PhabricatorEdgeConfig`, which isn't modular.

This is basically the same as the modularization of PHID types, which has worked well. Add `PhabricatorEdgeType` and provide an adaption layer for the existing code.

This has no runtime changes, except the fixed edge constant.

Test Plan: Ran `var_dump(PhabricatorEdgeType::getAllTypes())` and got reasonable looking output.

Reviewers: chad, btrahan, joshuaspence

Reviewed By: joshuaspence

Subscribers: epriestley

Maniphest Tasks: T5245

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

+402 -1
+4
src/__phutil_library_map__.php
··· 1562 1562 'PhabricatorEdgeGraph' => 'infrastructure/edges/util/PhabricatorEdgeGraph.php', 1563 1563 'PhabricatorEdgeQuery' => 'infrastructure/edges/query/PhabricatorEdgeQuery.php', 1564 1564 'PhabricatorEdgeTestCase' => 'infrastructure/edges/__tests__/PhabricatorEdgeTestCase.php', 1565 + 'PhabricatorEdgeType' => 'infrastructure/edges/type/PhabricatorEdgeType.php', 1565 1566 'PhabricatorEditor' => 'infrastructure/PhabricatorEditor.php', 1566 1567 'PhabricatorEmailLoginController' => 'applications/auth/controller/PhabricatorEmailLoginController.php', 1567 1568 'PhabricatorEmailVerificationController' => 'applications/auth/controller/PhabricatorEmailVerificationController.php', ··· 1712 1713 'PhabricatorJavelinLinter' => 'infrastructure/lint/linter/PhabricatorJavelinLinter.php', 1713 1714 'PhabricatorJumpNavHandler' => 'applications/search/engine/PhabricatorJumpNavHandler.php', 1714 1715 'PhabricatorKeyValueDatabaseCache' => 'applications/cache/PhabricatorKeyValueDatabaseCache.php', 1716 + 'PhabricatorLegacyEdgeType' => 'infrastructure/edges/type/PhabricatorLegacyEdgeType.php', 1715 1717 'PhabricatorLegalpadConfigOptions' => 'applications/legalpad/config/PhabricatorLegalpadConfigOptions.php', 1716 1718 'PhabricatorLegalpadPHIDTypeDocument' => 'applications/legalpad/phid/PhabricatorLegalpadPHIDTypeDocument.php', 1717 1719 'PhabricatorLipsumArtist' => 'applications/lipsum/image/PhabricatorLipsumArtist.php', ··· 4356 4358 'PhabricatorEdgeGraph' => 'AbstractDirectedGraph', 4357 4359 'PhabricatorEdgeQuery' => 'PhabricatorQuery', 4358 4360 'PhabricatorEdgeTestCase' => 'PhabricatorTestCase', 4361 + 'PhabricatorEdgeType' => 'Phobject', 4359 4362 'PhabricatorEditor' => 'Phobject', 4360 4363 'PhabricatorEmailLoginController' => 'PhabricatorAuthController', 4361 4364 'PhabricatorEmailVerificationController' => 'PhabricatorAuthController', ··· 4509 4512 'PhabricatorIteratedMD5PasswordHasher' => 'PhabricatorPasswordHasher', 4510 4513 'PhabricatorJavelinLinter' => 'ArcanistLinter', 4511 4514 'PhabricatorKeyValueDatabaseCache' => 'PhutilKeyValueCache', 4515 + 'PhabricatorLegacyEdgeType' => 'PhabricatorEdgeType', 4512 4516 'PhabricatorLegalpadConfigOptions' => 'PhabricatorApplicationConfigOptions', 4513 4517 'PhabricatorLegalpadPHIDTypeDocument' => 'PhabricatorPHIDType', 4514 4518 'PhabricatorLipsumGenerateWorkflow' => 'PhabricatorLipsumManagementWorkflow',
+49 -1
src/infrastructure/edges/constants/PhabricatorEdgeConfig.php
··· 78 78 const TYPE_OBJECT_NEEDS_SIGNATURE = 49; 79 79 const TYPE_SIGNATURE_NEEDED_BY_OBJECT = 50; 80 80 81 + /* !!!! STOP !!!! STOP !!!! STOP !!!! STOP !!!! STOP !!!! STOP !!!! STOP !!!! */ 82 + 83 + // HEY! DO NOT ADD NEW CONSTANTS HERE! 84 + // Instead, subclass PhabricatorEdgeType. 85 + 86 + /* !!!! STOP !!!! STOP !!!! STOP !!!! STOP !!!! STOP !!!! STOP !!!! STOP !!!! */ 87 + 81 88 const TYPE_TEST_NO_CYCLE = 9000; 82 89 83 90 const TYPE_PHOB_HAS_ASANATASK = 80001; ··· 89 96 const TYPE_PHOB_HAS_JIRAISSUE = 80004; 90 97 const TYPE_JIRAISSUE_HAS_PHOB = 80005; 91 98 99 + 100 + /** 101 + * Build @{class:PhabricatorLegacyEdgeType} objects for edges which have not 102 + * yet been modernized. This allows code to act as though we've completed 103 + * the edge type migration before we actually do all the work, by building 104 + * these fake type objects. 105 + * 106 + * @param list<const> List of edge types that objects should not be built for. 107 + * This is used to avoid constructing duplicate objects for edge constants 108 + * which have migrated and already have a real object. 109 + * @return list<PhabricatorLegacyEdgeType> Real-looking edge type objects for 110 + * unmigrated edge types. 111 + */ 112 + public static function getLegacyTypes(array $exclude) { 113 + $consts = array_merge( 114 + range(1, 50), 115 + array(9000), 116 + range(80000, 80005)); 117 + $consts = array_diff($consts, $exclude); 118 + 119 + $map = array(); 120 + foreach ($consts as $const) { 121 + $prevent_cycles = self::shouldPreventCycles($const); 122 + $inverse_constant = self::getInverse($const); 123 + 124 + $map[$const] = id(new PhabricatorLegacyEdgeType()) 125 + ->setEdgeConstant($const) 126 + ->setShouldPreventCycles($prevent_cycles) 127 + ->setInverseEdgeConstant($inverse_constant) 128 + ->setStrings( 129 + array( 130 + self::getAddStringForEdgeType($const), 131 + self::getRemoveStringForEdgeType($const), 132 + self::getEditStringForEdgeType($const), 133 + self::getFeedStringForEdgeType($const), 134 + )); 135 + } 136 + 137 + return $map; 138 + } 139 + 92 140 public static function getInverse($edge_type) { 93 141 static $map = array( 94 142 self::TYPE_TASK_HAS_COMMIT => self::TYPE_COMMIT_HAS_TASK, ··· 136 184 self::TYPE_DREV_HAS_COMMIT => self::TYPE_COMMIT_HAS_DREV, 137 185 self::TYPE_COMMIT_HAS_DREV => self::TYPE_DREV_HAS_COMMIT, 138 186 139 - self::TYPE_OBJECT_HAS_CONTRIBUTOR => self::TYPE_SUBSCRIBED_TO_OBJECT, 187 + self::TYPE_OBJECT_HAS_CONTRIBUTOR => self::TYPE_CONTRIBUTED_TO_OBJECT, 140 188 self::TYPE_CONTRIBUTED_TO_OBJECT => self::TYPE_OBJECT_HAS_CONTRIBUTOR, 141 189 142 190 self::TYPE_TASK_HAS_MOCK => self::TYPE_MOCK_HAS_TASK,
+230
src/infrastructure/edges/type/PhabricatorEdgeType.php
··· 1 + <?php 2 + 3 + /** 4 + * Defines an edge type. 5 + * 6 + * Edges are typed, directed connections between two objects. They are used to 7 + * represent most simple relationships, like when a user is subscribed to an 8 + * object or an object is a member of a project. 9 + * 10 + * @task load Loading Types 11 + */ 12 + abstract class PhabricatorEdgeType extends Phobject { 13 + 14 + // TODO: Make this final after we remove PhabricatorLegacyEdgeType. 15 + /* final */ public function getEdgeConstant() { 16 + $class = new ReflectionClass($this); 17 + 18 + $const = $class->getConstant('EDGECONST'); 19 + if ($const === false) { 20 + throw new Exception( 21 + pht( 22 + 'EdgeType class "%s" must define an EDGECONST property.', 23 + get_class($this))); 24 + } 25 + 26 + if (!is_int($const) || ($const <= 0)) { 27 + throw new Exception( 28 + pht( 29 + 'EdgeType class "%s" has an invalid EDGECONST property. Edge '. 30 + 'constants must be positive integers.', 31 + get_class($this))); 32 + } 33 + 34 + return $const; 35 + } 36 + 37 + public function getInverseEdgeConstant() { 38 + return null; 39 + } 40 + 41 + public function shouldPreventCycles() { 42 + return false; 43 + } 44 + 45 + public function getTransactionAddString( 46 + $actor, 47 + $add_count, 48 + $add_edges) { 49 + 50 + return pht( 51 + '%s added %s edge(s): %s.', 52 + $actor, 53 + $add_count, 54 + $add_edges); 55 + } 56 + 57 + public function getTransactionRemoveString( 58 + $actor, 59 + $rem_count, 60 + $rem_edges) { 61 + 62 + return pht( 63 + '%s removed %s edge(s): %s.', 64 + $actor, 65 + $rem_count, 66 + $rem_edges); 67 + } 68 + 69 + public function getTransactionEditString( 70 + $actor, 71 + $total_count, 72 + $add_count, 73 + $add_edges, 74 + $rem_count, 75 + $rem_edges) { 76 + 77 + return pht( 78 + '%s edited %s edge(s), added %s: %s; removed %s: %s.', 79 + $actor, 80 + $total_count, 81 + $add_count, 82 + $add_edges, 83 + $rem_count, 84 + $rem_edges); 85 + } 86 + 87 + public function getFeedAddString( 88 + $actor, 89 + $object, 90 + $add_count, 91 + $add_edges) { 92 + 93 + return pht( 94 + '%s added %s edge(s) to %s: %s.', 95 + $actor, 96 + $add_count, 97 + $object, 98 + $add_edges); 99 + } 100 + 101 + public function getFeedRemoveString( 102 + $actor, 103 + $object, 104 + $rem_count, 105 + $rem_edges) { 106 + 107 + return pht( 108 + '%s removed %s edge(s) from %s: %s.', 109 + $actor, 110 + $rem_count, 111 + $object, 112 + $rem_edges); 113 + } 114 + 115 + public function getFeedEditString( 116 + $actor, 117 + $object, 118 + $total_count, 119 + $add_count, 120 + $add_edges, 121 + $rem_count, 122 + $rem_edges) { 123 + 124 + return pht( 125 + '%s edited %s edge(s) for %s, added %s: %s; removed %s: %s.', 126 + $actor, 127 + $total_count, 128 + $object, 129 + $add_count, 130 + $add_edges, 131 + $rem_count, 132 + $rem_edges); 133 + } 134 + 135 + 136 + /* -( Loading Types )------------------------------------------------------ */ 137 + 138 + 139 + /** 140 + * @task load 141 + */ 142 + public static function getAllTypes() { 143 + static $type_map; 144 + 145 + if ($type_map === null) { 146 + $types = id(new PhutilSymbolLoader()) 147 + ->setAncestorClass(__CLASS__) 148 + ->loadObjects(); 149 + 150 + $map = array(); 151 + 152 + 153 + // TODO: Remove this once everything is migrated. 154 + $exclude = mpull($types, 'getEdgeConstant'); 155 + $map = PhabricatorEdgeConfig::getLegacyTypes($exclude); 156 + unset($types['PhabricatorLegacyEdgeType']); 157 + 158 + 159 + foreach ($types as $class => $type) { 160 + $const = $type->getEdgeConstant(); 161 + 162 + if (isset($map[$const])) { 163 + throw new Exception( 164 + pht( 165 + 'Two edge types ("%s", "%s") share the same edge constant '. 166 + '(%d). Each edge type must have a unique constant.', 167 + $class, 168 + get_class($map[$const]), 169 + $const)); 170 + } 171 + 172 + $map[$const] = $type; 173 + } 174 + 175 + // Check that all the inverse edge definitions actually make sense. If 176 + // edge type A says B is its inverse, B must exist and say that A is its 177 + // inverse. 178 + 179 + foreach ($map as $const => $type) { 180 + $inverse = $type->getInverseEdgeConstant(); 181 + if ($inverse === null) { 182 + continue; 183 + } 184 + 185 + if (empty($map[$inverse])) { 186 + throw new Exception( 187 + pht( 188 + 'Edge type "%s" ("%d") defines an inverse type ("%d") which '. 189 + 'does not exist.', 190 + get_class($type), 191 + $const, 192 + $inverse)); 193 + } 194 + 195 + $inverse_inverse = $map[$inverse]->getInverseEdgeConstant(); 196 + if ($inverse_inverse !== $const) { 197 + throw new Exception( 198 + pht( 199 + 'Edge type "%s" ("%d") defines an inverse type ("%d"), but that '. 200 + 'inverse type defines a different type ("%d") as its '. 201 + 'inverse.', 202 + get_class($type), 203 + $const, 204 + $inverse, 205 + $inverse_inverse)); 206 + } 207 + } 208 + 209 + $type_map = $map; 210 + } 211 + 212 + return $type_map; 213 + } 214 + 215 + 216 + /** 217 + * @task load 218 + */ 219 + public static function getByConstant($const) { 220 + $type = idx(self::getAllTypes(), $const); 221 + 222 + if (!$type) { 223 + throw new Exception( 224 + pht('Unknown edge constant "%s"!', $const)); 225 + } 226 + 227 + return $type; 228 + } 229 + 230 + }
+119
src/infrastructure/edges/type/PhabricatorLegacyEdgeType.php
··· 1 + <?php 2 + 3 + /** 4 + * Supports legacy edges. Do not use or extend this class! 5 + * 6 + * TODO: Move all edge constants out of @{class:PhabricatorEdgeConfig}, then 7 + * throw this away. 8 + */ 9 + final class PhabricatorLegacyEdgeType extends PhabricatorEdgeType { 10 + 11 + private $edgeConstant; 12 + private $inverseEdgeConstant; 13 + private $shouldPreventCycles; 14 + private $strings; 15 + 16 + public function getEdgeConstant() { 17 + return $this->edgeConstant; 18 + } 19 + 20 + public function getInverseEdgeConstant() { 21 + return $this->inverseEdgeConstant; 22 + } 23 + 24 + public function shouldPreventCycles() { 25 + return $this->shouldPreventCycles; 26 + } 27 + 28 + public function setEdgeConstant($edge_constant) { 29 + $this->edgeConstant = $edge_constant; 30 + return $this; 31 + } 32 + 33 + public function setInverseEdgeConstant($inverse_edge_constant) { 34 + $this->inverseEdgeConstant = $inverse_edge_constant; 35 + return $this; 36 + } 37 + 38 + public function setShouldPreventCycles($should_prevent_cycles) { 39 + $this->shouldPreventCycles = $should_prevent_cycles; 40 + return $this; 41 + } 42 + 43 + public function setStrings(array $strings) { 44 + $this->strings = $strings; 45 + return $this; 46 + } 47 + 48 + private function getString($idx, array $argv) { 49 + array_unshift($argv, idx($this->strings, $idx, '')); 50 + 51 + // TODO: Burn this class in a fire. Just hiding this from lint for now. 52 + $pht_func = 'pht'; 53 + return call_user_func_array($pht_func, $argv); 54 + } 55 + 56 + public function getTransactionAddString( 57 + $actor, 58 + $add_count, 59 + $add_edges) { 60 + 61 + $args = func_get_args(); 62 + return $this->getString(0, $args); 63 + } 64 + 65 + public function getTransactionRemoveString( 66 + $actor, 67 + $rem_count, 68 + $rem_edges) { 69 + 70 + $args = func_get_args(); 71 + return $this->getString(1, $args); 72 + } 73 + 74 + public function getTransactionEditString( 75 + $actor, 76 + $total_count, 77 + $add_count, 78 + $add_edges, 79 + $rem_count, 80 + $rem_edges) { 81 + 82 + $args = func_get_args(); 83 + return $this->getString(2, $args); 84 + } 85 + 86 + public function getFeedAddString( 87 + $actor, 88 + $object, 89 + $add_count, 90 + $add_edges) { 91 + 92 + $args = func_get_args(); 93 + return $this->getString(3, $args); 94 + } 95 + 96 + public function getFeedRemoveString( 97 + $actor, 98 + $object, 99 + $rem_count, 100 + $rem_edges) { 101 + 102 + $args = func_get_args(); 103 + return $this->getString(3, $args); 104 + } 105 + 106 + public function getFeedEditString( 107 + $actor, 108 + $object, 109 + $total_count, 110 + $add_count, 111 + $add_edges, 112 + $rem_count, 113 + $rem_edges) { 114 + 115 + $args = func_get_args(); 116 + return $this->getString(3, $args); 117 + } 118 + 119 + }