@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
at recaptime-dev/main 435 lines 12 kB view raw
1<?php 2 3/** 4 * @extends PhabricatorCursorPagedPolicyAwareQuery<PhabricatorPolicy> 5 */ 6final class PhabricatorPolicyQuery 7 extends PhabricatorCursorPagedPolicyAwareQuery { 8 9 private $object; 10 private $phids; 11 12 const OBJECT_POLICY_PREFIX = 'obj.'; 13 14 public function setObject(PhabricatorPolicyInterface $object) { 15 $this->object = $object; 16 return $this; 17 } 18 19 public function withPHIDs(array $phids) { 20 $this->phids = $phids; 21 return $this; 22 } 23 24 public static function loadPolicies( 25 PhabricatorUser $viewer, 26 PhabricatorPolicyInterface $object) { 27 28 $results = array(); 29 30 $map = array(); 31 foreach ($object->getCapabilities() as $capability) { 32 $map[$capability] = $object->getPolicy($capability); 33 } 34 35 $policies = id(new PhabricatorPolicyQuery()) 36 ->setViewer($viewer) 37 ->withPHIDs($map) 38 ->execute(); 39 40 foreach ($map as $capability => $phid) { 41 $results[$capability] = $policies[$phid]; 42 } 43 44 return $results; 45 } 46 47 public static function renderPolicyDescriptions( 48 PhabricatorUser $viewer, 49 PhabricatorPolicyInterface $object) { 50 51 $policies = self::loadPolicies($viewer, $object); 52 53 foreach ($policies as $capability => $policy) { 54 $policies[$capability] = $policy->newRef($viewer) 55 ->newCapabilityLink($object, $capability); 56 } 57 58 return $policies; 59 } 60 61 protected function loadPage() { 62 if ($this->object && $this->phids) { 63 throw new Exception( 64 pht( 65 'You can not issue a policy query with both %s and %s.', 66 'setObject()', 67 'setPHIDs()')); 68 } else if ($this->object) { 69 $phids = $this->loadObjectPolicyPHIDs(); 70 } else { 71 $phids = $this->phids; 72 } 73 74 $phids = array_fuse($phids); 75 76 $results = array(); 77 78 // First, load global policies. 79 foreach (self::getGlobalPolicies() as $phid => $policy) { 80 if (isset($phids[$phid])) { 81 $results[$phid] = $policy; 82 unset($phids[$phid]); 83 } 84 } 85 86 // Now, load object policies. 87 foreach (self::getObjectPolicies($this->object) as $phid => $policy) { 88 if (isset($phids[$phid])) { 89 $results[$phid] = $policy; 90 unset($phids[$phid]); 91 } 92 } 93 94 // If we still need policies, we're going to have to fetch data. Bucket 95 // the remaining policies into rule-based policies and handle-based 96 // policies. 97 if ($phids) { 98 $rule_policies = array(); 99 $handle_policies = array(); 100 foreach ($phids as $phid) { 101 $phid_type = phid_get_type($phid); 102 if ($phid_type == PhabricatorPolicyPHIDTypePolicy::TYPECONST) { 103 $rule_policies[$phid] = $phid; 104 } else { 105 $handle_policies[$phid] = $phid; 106 } 107 } 108 109 if ($handle_policies) { 110 $handles = id(new PhabricatorHandleQuery()) 111 ->setViewer($this->getViewer()) 112 ->withPHIDs($handle_policies) 113 ->execute(); 114 foreach ($handle_policies as $phid) { 115 $results[$phid] = PhabricatorPolicy::newFromPolicyAndHandle( 116 $phid, 117 $handles[$phid]); 118 } 119 } 120 121 if ($rule_policies) { 122 $rules = id(new PhabricatorPolicy())->loadAllWhere( 123 'phid IN (%Ls)', 124 $rule_policies); 125 $results += mpull($rules, null, 'getPHID'); 126 } 127 } 128 129 $results = msort($results, 'getSortKey'); 130 131 return $results; 132 } 133 134 public static function isGlobalPolicy($policy) { 135 $global_policies = self::getGlobalPolicies(); 136 137 if (isset($global_policies[$policy])) { 138 return true; 139 } 140 141 return false; 142 } 143 144 public static function getGlobalPolicy($policy) { 145 if (!self::isGlobalPolicy($policy)) { 146 throw new Exception(pht("Policy '%s' is not a global policy!", $policy)); 147 } 148 return idx(self::getGlobalPolicies(), $policy); 149 } 150 151 private static function getGlobalPolicies() { 152 static $constants = array( 153 PhabricatorPolicies::POLICY_PUBLIC, 154 PhabricatorPolicies::POLICY_USER, 155 PhabricatorPolicies::POLICY_ADMIN, 156 PhabricatorPolicies::POLICY_NOONE, 157 ); 158 159 $results = array(); 160 foreach ($constants as $constant) { 161 $results[$constant] = id(new PhabricatorPolicy()) 162 ->setType(PhabricatorPolicyType::TYPE_GLOBAL) 163 ->setPHID($constant) 164 ->setName(self::getGlobalPolicyName($constant)) 165 ->setShortName(self::getGlobalPolicyShortName($constant)) 166 ->makeEphemeral(); 167 } 168 169 return $results; 170 } 171 172 private static function getGlobalPolicyName($policy) { 173 switch ($policy) { 174 case PhabricatorPolicies::POLICY_PUBLIC: 175 return pht('Public (No Login Required)'); 176 case PhabricatorPolicies::POLICY_USER: 177 return pht('All Users'); 178 case PhabricatorPolicies::POLICY_ADMIN: 179 return pht('Administrators'); 180 case PhabricatorPolicies::POLICY_NOONE: 181 return pht('No One'); 182 default: 183 return pht('Unknown Policy'); 184 } 185 } 186 187 private static function getGlobalPolicyShortName($policy) { 188 switch ($policy) { 189 case PhabricatorPolicies::POLICY_PUBLIC: 190 return pht('Public'); 191 default: 192 return null; 193 } 194 } 195 196 private function loadObjectPolicyPHIDs() { 197 $phids = array(); 198 $viewer = $this->getViewer(); 199 200 if ($viewer->getPHID()) { 201 $pref_key = PhabricatorPolicyFavoritesSetting::SETTINGKEY; 202 203 $favorite_limit = 10; 204 $default_limit = 5; 205 206 // If possible, show the user's 10 most recently used projects. 207 $favorites = $viewer->getUserSetting($pref_key); 208 if (!is_array($favorites)) { 209 $favorites = array(); 210 } 211 $favorite_phids = array_keys($favorites); 212 $favorite_phids = array_slice($favorite_phids, -$favorite_limit); 213 214 if ($favorite_phids) { 215 $projects = id(new PhabricatorProjectQuery()) 216 ->setViewer($viewer) 217 ->withPHIDs($favorite_phids) 218 ->withIsMilestone(false) 219 ->setLimit($favorite_limit) 220 ->execute(); 221 $projects = mpull($projects, null, 'getPHID'); 222 } else { 223 $projects = array(); 224 } 225 226 // If we didn't find enough favorites, add some default projects. These 227 // are just arbitrary projects that the viewer is a member of, but may 228 // be useful on smaller installs and for new users until they can use 229 // the control enough time to establish useful favorites. 230 if (count($projects) < $default_limit) { 231 $default_projects = id(new PhabricatorProjectQuery()) 232 ->setViewer($viewer) 233 ->withMemberPHIDs(array($viewer->getPHID())) 234 ->withIsMilestone(false) 235 ->withStatuses( 236 array( 237 PhabricatorProjectStatus::STATUS_ACTIVE, 238 )) 239 ->setLimit($default_limit) 240 ->execute(); 241 $default_projects = mpull($default_projects, null, 'getPHID'); 242 $projects = $projects + $default_projects; 243 $projects = array_slice($projects, 0, $default_limit); 244 } 245 246 foreach ($projects as $project) { 247 $phids[] = $project->getPHID(); 248 } 249 250 // Include the "current viewer" policy. This improves consistency, but 251 // is also useful for creating private instances of normally-shared object 252 // types, like repositories. 253 $phids[] = $viewer->getPHID(); 254 } 255 256 $capabilities = $this->object->getCapabilities(); 257 foreach ($capabilities as $capability) { 258 $policy = $this->object->getPolicy($capability); 259 if (!$policy) { 260 continue; 261 } 262 $phids[] = $policy; 263 } 264 265 // If this install doesn't have "Public" enabled, don't include it as an 266 // option unless the object already has a "Public" policy. In this case we 267 // retain the policy but enforce it as though it was "All Users". 268 $show_public = PhabricatorEnv::getEnvConfig('policy.allow-public'); 269 foreach (self::getGlobalPolicies() as $phid => $policy) { 270 if ($phid == PhabricatorPolicies::POLICY_PUBLIC) { 271 if (!$show_public) { 272 continue; 273 } 274 } 275 $phids[] = $phid; 276 } 277 278 foreach (self::getObjectPolicies($this->object) as $phid => $policy) { 279 $phids[] = $phid; 280 } 281 282 return $phids; 283 } 284 285 protected function shouldDisablePolicyFiltering() { 286 // Policy filtering of policies is currently perilous and not required by 287 // the application. 288 return true; 289 } 290 291 public function getQueryApplicationClass() { 292 return PhabricatorPolicyApplication::class; 293 } 294 295 public static function isSpecialPolicy($identifier) { 296 if ($identifier === null) { 297 return true; 298 } 299 300 if (self::isObjectPolicy($identifier)) { 301 return true; 302 } 303 304 if (self::isGlobalPolicy($identifier)) { 305 return true; 306 } 307 308 return false; 309 } 310 311 312/* -( Object Policies )---------------------------------------------------- */ 313 314 315 public static function isObjectPolicy($identifier) { 316 $prefix = self::OBJECT_POLICY_PREFIX; 317 return !strncmp($identifier, $prefix, strlen($prefix)); 318 } 319 320 public static function getObjectPolicy($identifier) { 321 if (!self::isObjectPolicy($identifier)) { 322 return null; 323 } 324 325 $policies = self::getObjectPolicies(null); 326 return idx($policies, $identifier); 327 } 328 329 public static function getObjectPolicyRule($identifier) { 330 if (!self::isObjectPolicy($identifier)) { 331 return null; 332 } 333 334 $rules = self::getObjectPolicyRules(null); 335 return idx($rules, $identifier); 336 } 337 338 public static function getObjectPolicies($object) { 339 $rule_map = self::getObjectPolicyRules($object); 340 341 $results = array(); 342 foreach ($rule_map as $key => $rule) { 343 $results[$key] = id(new PhabricatorPolicy()) 344 ->setType(PhabricatorPolicyType::TYPE_OBJECT) 345 ->setPHID($key) 346 ->setIcon($rule->getObjectPolicyIcon()) 347 ->setName($rule->getObjectPolicyName()) 348 ->setShortName($rule->getObjectPolicyShortName()) 349 ->makeEphemeral(); 350 } 351 352 return $results; 353 } 354 355 public static function getObjectPolicyRules($object) { 356 $rules = id(new PhutilClassMapQuery()) 357 ->setAncestorClass(PhabricatorPolicyRule::class) 358 ->execute(); 359 360 $results = array(); 361 foreach ($rules as $rule) { 362 $key = $rule->getObjectPolicyKey(); 363 if (!$key) { 364 continue; 365 } 366 367 $full_key = $rule->getObjectPolicyFullKey(); 368 if (isset($results[$full_key])) { 369 throw new Exception( 370 pht( 371 'Two policy rules (of classes "%s" and "%s") define the same '. 372 'object policy key ("%s"), but each object policy rule must use '. 373 'a unique key.', 374 get_class($rule), 375 get_class($results[$full_key]), 376 $key)); 377 } 378 379 $results[$full_key] = $rule; 380 } 381 382 if ($object !== null) { 383 foreach ($results as $key => $rule) { 384 if (!$rule->canApplyToObject($object)) { 385 unset($results[$key]); 386 } 387 } 388 } 389 390 return $results; 391 } 392 393 public static function getDefaultPolicyForObject( 394 PhabricatorUser $viewer, 395 PhabricatorPolicyInterface $object, 396 $capability) { 397 398 $phid = $object->getPHID(); 399 if (!$phid) { 400 return null; 401 } 402 403 $type = phid_get_type($phid); 404 405 $map = self::getDefaultObjectTypePolicyMap(); 406 407 if (empty($map[$type][$capability])) { 408 return null; 409 } 410 411 $policy_phid = $map[$type][$capability]; 412 413 return id(new PhabricatorPolicyQuery()) 414 ->setViewer($viewer) 415 ->withPHIDs(array($policy_phid)) 416 ->executeOne(); 417 } 418 419 private static function getDefaultObjectTypePolicyMap() { 420 static $map; 421 422 if ($map === null) { 423 $map = array(); 424 425 $apps = PhabricatorApplication::getAllApplications(); 426 foreach ($apps as $app) { 427 $map += $app->getDefaultObjectTypePolicyMap(); 428 } 429 } 430 431 return $map; 432 } 433 434 435}