1
0
Fork 0
mirror of https://we.phorge.it/source/phorge.git synced 2025-01-19 19:21:10 +01:00
phorge-phorge/src/applications/owners/storage/PhabricatorOwnersPackage.php

272 lines
6.9 KiB
PHP
Raw Normal View History

2011-04-03 23:48:36 +02:00
<?php
Add PhabricatorOwnersPackageQuery Summary: This is a step toward unearthing Project queries enough that I can make them policy-aware. Right now, some ProjectQuery callsites do not have reasonable access to the viewer. In particular, Owners packages need to issue Project queries because we allow projects to own packages and resolve project members inside of some package queries. Currently, we have a very unmodern approach to querying packages, with a large number of one-off static load methods: PhabricatorOwnersPackage::loadAffectedPackages() PhabricatorOwnersPackage::loadOwningPackages() PhabricatorOwnersPackage::loadPackagesForPaths() PhabricatorOwnersOwner::loadAllForPackages() PhabricatorOwnersOwner::loadAffiliatedUserPHIDs() PhabricatorOwnersOwner::loadAffiliatedPackages() ConduitAPI_owners_query_Method::queryAll() ConduitAPI_owners_query_Method::queryByOwner() ConduitAPI_owners_query_Method::queryByAffiliatedUser() ConduitAPI_owners_query_Method::queryByPath() We should replace `PhabricatorOwnersOwner` with an Edge and move all of these calls to a Query class. I'm going to try to do as little of this work as I can for now since I'm much more interested in getting a functional policy implementation into other applications, but ProjectQuery needs to be policy-aware before I can do that and I need to dig some at least some of the callsites out enough that I can get a viewer in there without making the code worse than it is. This adds a PhabricatorOwnersPackageQuery class and removes one callsite of one of those static methods. I also intend to dissolve the two separate concepts of an "owner" (direct owner) and an "affiliated user" (indirect owner via project membership) since I think we're always fine with "affiliated users" owners. Test Plan: Loaded home page / audit tool, which use the modified path. Ran queries manually via script. Made sure results included directly owned packages and packages owned through project membership. Reviewers: vrana, btrahan, meitros Reviewed By: btrahan CC: aran Maniphest Tasks: T603 Differential Revision: https://secure.phabricator.com/D3193
2012-08-08 21:25:11 +02:00
final class PhabricatorOwnersPackage extends PhabricatorOwnersDAO
implements PhabricatorPolicyInterface {
2011-04-03 23:48:36 +02:00
protected $name;
protected $originalName;
protected $auditingEnabled;
2011-04-03 23:48:36 +02:00
protected $description;
protected $primaryOwnerPHID;
private $unsavedOwners = self::ATTACHABLE;
private $unsavedPaths = self::ATTACHABLE;
private $actorPHID;
private $oldPrimaryOwnerPHID;
private $oldAuditingEnabled;
2011-04-04 07:03:27 +02:00
Add PhabricatorOwnersPackageQuery Summary: This is a step toward unearthing Project queries enough that I can make them policy-aware. Right now, some ProjectQuery callsites do not have reasonable access to the viewer. In particular, Owners packages need to issue Project queries because we allow projects to own packages and resolve project members inside of some package queries. Currently, we have a very unmodern approach to querying packages, with a large number of one-off static load methods: PhabricatorOwnersPackage::loadAffectedPackages() PhabricatorOwnersPackage::loadOwningPackages() PhabricatorOwnersPackage::loadPackagesForPaths() PhabricatorOwnersOwner::loadAllForPackages() PhabricatorOwnersOwner::loadAffiliatedUserPHIDs() PhabricatorOwnersOwner::loadAffiliatedPackages() ConduitAPI_owners_query_Method::queryAll() ConduitAPI_owners_query_Method::queryByOwner() ConduitAPI_owners_query_Method::queryByAffiliatedUser() ConduitAPI_owners_query_Method::queryByPath() We should replace `PhabricatorOwnersOwner` with an Edge and move all of these calls to a Query class. I'm going to try to do as little of this work as I can for now since I'm much more interested in getting a functional policy implementation into other applications, but ProjectQuery needs to be policy-aware before I can do that and I need to dig some at least some of the callsites out enough that I can get a viewer in there without making the code worse than it is. This adds a PhabricatorOwnersPackageQuery class and removes one callsite of one of those static methods. I also intend to dissolve the two separate concepts of an "owner" (direct owner) and an "affiliated user" (indirect owner via project membership) since I think we're always fine with "affiliated users" owners. Test Plan: Loaded home page / audit tool, which use the modified path. Ran queries manually via script. Made sure results included directly owned packages and packages owned through project membership. Reviewers: vrana, btrahan, meitros Reviewed By: btrahan CC: aran Maniphest Tasks: T603 Differential Revision: https://secure.phabricator.com/D3193
2012-08-08 21:25:11 +02:00
public function getCapabilities() {
return array(
PhabricatorPolicyCapability::CAN_VIEW,
);
}
public function getPolicy($capability) {
return PhabricatorPolicies::POLICY_USER;
}
public function hasAutomaticCapability($capability, PhabricatorUser $viewer) {
return false;
}
public function describeAutomaticCapability($capability) {
return null;
}
protected function getConfiguration() {
2011-04-03 23:48:36 +02:00
return array(
// This information is better available from the history table.
self::CONFIG_TIMESTAMPS => false,
self::CONFIG_AUX_PHID => true,
self::CONFIG_COLUMN_SCHEMA => array(
'name' => 'text128',
'originalName' => 'text255',
'description' => 'text',
'primaryOwnerPHID' => 'phid?',
'auditingEnabled' => 'bool',
),
self::CONFIG_KEY_SCHEMA => array(
'key_phid' => null,
'phid' => array(
'columns' => array('phid'),
'unique' => true,
),
'name' => array(
'columns' => array('name'),
'unique' => true,
),
),
2011-04-04 07:03:27 +02:00
) + parent::getConfiguration();
2011-04-03 23:48:36 +02:00
}
public function generatePHID() {
2011-04-04 07:03:27 +02:00
return PhabricatorPHID::generateNewPHID('OPKG');
}
public function attachUnsavedOwners(array $owners) {
$this->unsavedOwners = $owners;
return $this;
}
public function getUnsavedOwners() {
return $this->assertAttached($this->unsavedOwners);
}
2011-04-04 07:03:27 +02:00
public function attachUnsavedPaths(array $paths) {
$this->unsavedPaths = $paths;
return $this;
}
public function getUnsavedPaths() {
return $this->assertAttached($this->unsavedPaths);
}
public function attachActorPHID($actor_phid) {
$this->actorPHID = $actor_phid;
return $this;
}
public function getActorPHID() {
return $this->actorPHID;
}
public function attachOldPrimaryOwnerPHID($old_primary) {
$this->oldPrimaryOwnerPHID = $old_primary;
return $this;
}
public function getOldPrimaryOwnerPHID() {
return $this->oldPrimaryOwnerPHID;
}
public function attachOldAuditingEnabled($auditing_enabled) {
$this->oldAuditingEnabled = $auditing_enabled;
return $this;
}
public function getOldAuditingEnabled() {
return $this->oldAuditingEnabled;
}
public function setName($name) {
$this->name = $name;
if (!$this->getID()) {
$this->originalName = $name;
}
return $this;
}
2011-04-04 07:03:27 +02:00
public function loadOwners() {
if (!$this->getID()) {
return array();
}
return id(new PhabricatorOwnersOwner())->loadAllWhere(
'packageID = %d',
$this->getID());
}
public function loadPaths() {
if (!$this->getID()) {
return array();
}
return id(new PhabricatorOwnersPath())->loadAllWhere(
'packageID = %d',
$this->getID());
}
public static function loadAffectedPackages(
PhabricatorRepository $repository,
array $paths) {
if (!$paths) {
return array();
}
return self::loadPackagesForPaths($repository, $paths);
}
public static function loadOwningPackages($repository, $path) {
if (empty($path)) {
return array();
}
return self::loadPackagesForPaths($repository, array($path), 1);
}
private static function loadPackagesForPaths(
PhabricatorRepository $repository,
array $paths,
$limit = 0) {
$fragments = array();
foreach ($paths as $path) {
foreach (self::splitPath($path) as $fragment) {
$fragments[$fragment][$path] = true;
}
}
$package = new PhabricatorOwnersPackage();
$path = new PhabricatorOwnersPath();
$conn = $package->establishConnection('r');
$repository_clause = qsprintf(
$conn,
'AND p.repositoryPHID = %s',
$repository->getPHID());
// NOTE: The list of $paths may be very large if we're coming from
// the OwnersWorker and processing, e.g., an SVN commit which created a new
// branch. Break it apart so that it will fit within 'max_allowed_packet',
// and then merge results in PHP.
$rows = array();
foreach (array_chunk(array_keys($fragments), 128) as $chunk) {
$rows[] = queryfx_all(
$conn,
'SELECT pkg.id, p.excluded, p.path
FROM %T pkg JOIN %T p ON p.packageID = pkg.id
WHERE p.path IN (%Ls) %Q',
$package->getTableName(),
$path->getTableName(),
$chunk,
$repository_clause);
}
$rows = array_mergev($rows);
$ids = self::findLongestPathsPerPackage($rows, $fragments);
if (!$ids) {
return array();
}
arsort($ids);
if ($limit) {
$ids = array_slice($ids, 0, $limit, $preserve_keys = true);
}
$ids = array_keys($ids);
$packages = $package->loadAllWhere('id in (%Ld)', $ids);
$packages = array_select_keys($packages, $ids);
return $packages;
}
public static function loadPackagesForRepository($repository) {
$package = new PhabricatorOwnersPackage();
$ids = ipull(
queryfx_all(
$package->establishConnection('r'),
'SELECT DISTINCT packageID FROM %T WHERE repositoryPHID = %s',
id(new PhabricatorOwnersPath())->getTableName(),
$repository->getPHID()),
'packageID');
return $package->loadAllWhere('id in (%Ld)', $ids);
}
public static function findLongestPathsPerPackage(array $rows, array $paths) {
$ids = array();
foreach (igroup($rows, 'id') as $id => $package_paths) {
$relevant_paths = array_select_keys(
$paths,
ipull($package_paths, 'path'));
// For every package, remove all excluded paths.
$remove = array();
foreach ($package_paths as $package_path) {
if ($package_path['excluded']) {
$remove += idx($relevant_paths, $package_path['path'], array());
unset($relevant_paths[$package_path['path']]);
}
}
if ($remove) {
foreach ($relevant_paths as $fragment => $fragment_paths) {
$relevant_paths[$fragment] = array_diff_key($fragment_paths, $remove);
}
}
$relevant_paths = array_filter($relevant_paths);
if ($relevant_paths) {
$ids[$id] = max(array_map('strlen', array_keys($relevant_paths)));
}
}
return $ids;
}
private static function splitPath($path) {
$result = array('/');
$trailing_slash = preg_match('@/$@', $path) ? '/' : '';
$path = trim($path, '/');
$parts = explode('/', $path);
while (count($parts)) {
$result[] = '/'.implode('/', $parts).$trailing_slash;
$trailing_slash = '/';
array_pop($parts);
}
return $result;
}
2011-04-03 23:48:36 +02:00
}