1
0
Fork 0
mirror of https://we.phorge.it/source/phorge.git synced 2024-11-17 12:22:42 +01:00

Add PackagesPackage

Summary:
Ref T8116. A package has:

  - a publisher (like "Phacility"), from the previous revision;
  - a name (like "Arcanist");
  - a package key (like "arcanist").

The package key is immutable, like the publisher key.

This gives a package a full key like "phacility/arcanist".

Policy stuff:

  - You must be able to view a publisher to view a package (currently, everyone can always see all publishers).
  - You must be able to edit a publisher to create a new package inside it.
  - Packages have separate view/edit permissions.

This still does nothing interesting.

Test Plan: {F1731663}

Reviewers: chad

Reviewed By: chad

Subscribers: eadler

Maniphest Tasks: T8116

Differential Revision: https://secure.phabricator.com/D16315
This commit is contained in:
epriestley 2016-07-21 10:39:59 -07:00
parent 9160da1afb
commit 704afea281
27 changed files with 1178 additions and 14 deletions

View file

@ -0,0 +1,13 @@
CREATE TABLE {$NAMESPACE}_packages.packages_package (
id INT UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY,
phid VARBINARY(64) NOT NULL,
name VARCHAR(64) NOT NULL COLLATE {$COLLATE_TEXT},
publisherPHID VARBINARY(64) NOT NULL,
packageKey VARCHAR(64) NOT NULL COLLATE {$COLLATE_SORT},
viewPolicy VARBINARY(64) NOT NULL,
editPolicy VARBINARY(64) NOT NULL,
dateCreated INT UNSIGNED NOT NULL,
dateModified INT UNSIGNED NOT NULL,
UNIQUE KEY `key_phid` (phid),
UNIQUE KEY `key_package` (publisherPHID, packageKey)
) ENGINE=InnoDB, COLLATE {$COLLATE_TEXT};

View file

@ -0,0 +1,19 @@
CREATE TABLE {$NAMESPACE}_packages.packages_packagetransaction (
id INT UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY,
phid VARBINARY(64) NOT NULL,
authorPHID VARBINARY(64) NOT NULL,
objectPHID VARBINARY(64) NOT NULL,
viewPolicy VARBINARY(64) NOT NULL,
editPolicy VARBINARY(64) NOT NULL,
commentPHID VARBINARY(64) DEFAULT NULL,
commentVersion INT UNSIGNED NOT NULL,
transactionType VARCHAR(32) COLLATE {$COLLATE_TEXT} NOT NULL,
oldValue LONGTEXT COLLATE {$COLLATE_TEXT} NOT NULL,
newValue LONGTEXT COLLATE {$COLLATE_TEXT} NOT NULL,
contentSource LONGTEXT COLLATE {$COLLATE_TEXT} NOT NULL,
metadata LONGTEXT COLLATE {$COLLATE_TEXT} NOT NULL,
dateCreated INT UNSIGNED NOT NULL,
dateModified INT UNSIGNED NOT NULL,
UNIQUE KEY `key_phid` (`phid`),
KEY `key_object` (`objectPHID`)
) ENGINE=InnoDB, COLLATE {$COLLATE_TEXT};

View file

@ -2977,8 +2977,27 @@ phutil_register_library_map(array(
'PhabricatorPackagesDAO' => 'applications/packages/storage/PhabricatorPackagesDAO.php',
'PhabricatorPackagesEditEngine' => 'applications/packages/editor/PhabricatorPackagesEditEngine.php',
'PhabricatorPackagesEditor' => 'applications/packages/editor/PhabricatorPackagesEditor.php',
'PhabricatorPackagesPackage' => 'applications/packages/storage/PhabricatorPackagesPackage.php',
'PhabricatorPackagesPackageController' => 'applications/packages/controller/PhabricatorPackagesPackageController.php',
'PhabricatorPackagesPackageEditConduitAPIMethod' => 'applications/packages/conduit/PhabricatorPackagesPackageEditConduitAPIMethod.php',
'PhabricatorPackagesPackageEditController' => 'applications/packages/controller/PhabricatorPackagesPackageEditController.php',
'PhabricatorPackagesPackageEditEngine' => 'applications/packages/editor/PhabricatorPackagesPackageEditEngine.php',
'PhabricatorPackagesPackageEditor' => 'applications/packages/editor/PhabricatorPackagesPackageEditor.php',
'PhabricatorPackagesPackageKeyTransaction' => 'applications/packages/xaction/package/PhabricatorPackagesPackageKeyTransaction.php',
'PhabricatorPackagesPackageListController' => 'applications/packages/controller/PhabricatorPackagesPackageListController.php',
'PhabricatorPackagesPackageNameTransaction' => 'applications/packages/xaction/package/PhabricatorPackagesPackageNameTransaction.php',
'PhabricatorPackagesPackagePHIDType' => 'applications/packages/phid/PhabricatorPackagesPackagePHIDType.php',
'PhabricatorPackagesPackagePublisherTransaction' => 'applications/packages/xaction/package/PhabricatorPackagesPackagePublisherTransaction.php',
'PhabricatorPackagesPackageQuery' => 'applications/packages/query/PhabricatorPackagesPackageQuery.php',
'PhabricatorPackagesPackageSearchConduitAPIMethod' => 'applications/packages/conduit/PhabricatorPackagesPackageSearchConduitAPIMethod.php',
'PhabricatorPackagesPackageSearchEngine' => 'applications/packages/query/PhabricatorPackagesPackageSearchEngine.php',
'PhabricatorPackagesPackageTransaction' => 'applications/packages/storage/PhabricatorPackagesPackageTransaction.php',
'PhabricatorPackagesPackageTransactionQuery' => 'applications/packages/query/PhabricatorPackagesPackageTransactionQuery.php',
'PhabricatorPackagesPackageTransactionType' => 'applications/packages/xaction/package/PhabricatorPackagesPackageTransactionType.php',
'PhabricatorPackagesPackageViewController' => 'applications/packages/controller/PhabricatorPackagesPackageViewController.php',
'PhabricatorPackagesPublisher' => 'applications/packages/storage/PhabricatorPackagesPublisher.php',
'PhabricatorPackagesPublisherController' => 'applications/packages/controller/PhabricatorPackagesPublisherController.php',
'PhabricatorPackagesPublisherDatasource' => 'applications/packages/typeahead/PhabricatorPackagesPublisherDatasource.php',
'PhabricatorPackagesPublisherEditConduitAPIMethod' => 'applications/packages/conduit/PhabricatorPackagesPublisherEditConduitAPIMethod.php',
'PhabricatorPackagesPublisherEditController' => 'applications/packages/controller/PhabricatorPackagesPublisherEditController.php',
'PhabricatorPackagesPublisherEditEngine' => 'applications/packages/editor/PhabricatorPackagesPublisherEditEngine.php',
@ -2994,6 +3013,7 @@ phutil_register_library_map(array(
'PhabricatorPackagesPublisherTransactionQuery' => 'applications/packages/query/PhabricatorPackagesPublisherTransactionQuery.php',
'PhabricatorPackagesPublisherTransactionType' => 'applications/packages/xaction/publisher/PhabricatorPackagesPublisherTransactionType.php',
'PhabricatorPackagesPublisherViewController' => 'applications/packages/controller/PhabricatorPackagesPublisherViewController.php',
'PhabricatorPackagesQuery' => 'applications/packages/query/PhabricatorPackagesQuery.php',
'PhabricatorPackagesSchemaSpec' => 'applications/packages/storage/PhabricatorPackagesSchemaSpec.php',
'PhabricatorPackagesTransactionType' => 'applications/packages/xaction/PhabricatorPackagesTransactionType.php',
'PhabricatorPagerUIExample' => 'applications/uiexample/examples/PhabricatorPagerUIExample.php',
@ -7754,6 +7774,32 @@ phutil_register_library_map(array(
'PhabricatorPackagesDAO' => 'PhabricatorLiskDAO',
'PhabricatorPackagesEditEngine' => 'PhabricatorEditEngine',
'PhabricatorPackagesEditor' => 'PhabricatorApplicationTransactionEditor',
'PhabricatorPackagesPackage' => array(
'PhabricatorPackagesDAO',
'PhabricatorPolicyInterface',
'PhabricatorApplicationTransactionInterface',
'PhabricatorDestructibleInterface',
'PhabricatorSubscribableInterface',
'PhabricatorProjectInterface',
'PhabricatorConduitResultInterface',
),
'PhabricatorPackagesPackageController' => 'PhabricatorPackagesController',
'PhabricatorPackagesPackageEditConduitAPIMethod' => 'PhabricatorEditEngineAPIMethod',
'PhabricatorPackagesPackageEditController' => 'PhabricatorPackagesPackageController',
'PhabricatorPackagesPackageEditEngine' => 'PhabricatorPackagesEditEngine',
'PhabricatorPackagesPackageEditor' => 'PhabricatorPackagesEditor',
'PhabricatorPackagesPackageKeyTransaction' => 'PhabricatorPackagesPackageTransactionType',
'PhabricatorPackagesPackageListController' => 'PhabricatorPackagesPackageController',
'PhabricatorPackagesPackageNameTransaction' => 'PhabricatorPackagesPackageTransactionType',
'PhabricatorPackagesPackagePHIDType' => 'PhabricatorPHIDType',
'PhabricatorPackagesPackagePublisherTransaction' => 'PhabricatorPackagesPackageTransactionType',
'PhabricatorPackagesPackageQuery' => 'PhabricatorPackagesQuery',
'PhabricatorPackagesPackageSearchConduitAPIMethod' => 'PhabricatorSearchEngineAPIMethod',
'PhabricatorPackagesPackageSearchEngine' => 'PhabricatorApplicationSearchEngine',
'PhabricatorPackagesPackageTransaction' => 'PhabricatorModularTransaction',
'PhabricatorPackagesPackageTransactionQuery' => 'PhabricatorApplicationTransactionQuery',
'PhabricatorPackagesPackageTransactionType' => 'PhabricatorPackagesTransactionType',
'PhabricatorPackagesPackageViewController' => 'PhabricatorPackagesPackageController',
'PhabricatorPackagesPublisher' => array(
'PhabricatorPackagesDAO',
'PhabricatorPolicyInterface',
@ -7764,6 +7810,7 @@ phutil_register_library_map(array(
'PhabricatorConduitResultInterface',
),
'PhabricatorPackagesPublisherController' => 'PhabricatorPackagesController',
'PhabricatorPackagesPublisherDatasource' => 'PhabricatorTypeaheadDatasource',
'PhabricatorPackagesPublisherEditConduitAPIMethod' => 'PhabricatorEditEngineAPIMethod',
'PhabricatorPackagesPublisherEditController' => 'PhabricatorPackagesPublisherController',
'PhabricatorPackagesPublisherEditEngine' => 'PhabricatorPackagesEditEngine',
@ -7772,13 +7819,14 @@ phutil_register_library_map(array(
'PhabricatorPackagesPublisherListController' => 'PhabricatorPackagesPublisherController',
'PhabricatorPackagesPublisherNameTransaction' => 'PhabricatorPackagesPublisherTransactionType',
'PhabricatorPackagesPublisherPHIDType' => 'PhabricatorPHIDType',
'PhabricatorPackagesPublisherQuery' => 'PhabricatorCursorPagedPolicyAwareQuery',
'PhabricatorPackagesPublisherQuery' => 'PhabricatorPackagesQuery',
'PhabricatorPackagesPublisherSearchConduitAPIMethod' => 'PhabricatorSearchEngineAPIMethod',
'PhabricatorPackagesPublisherSearchEngine' => 'PhabricatorApplicationSearchEngine',
'PhabricatorPackagesPublisherTransaction' => 'PhabricatorModularTransaction',
'PhabricatorPackagesPublisherTransactionQuery' => 'PhabricatorApplicationTransactionQuery',
'PhabricatorPackagesPublisherTransactionType' => 'PhabricatorPackagesTransactionType',
'PhabricatorPackagesPublisherViewController' => 'PhabricatorPackagesPublisherController',
'PhabricatorPackagesQuery' => 'PhabricatorCursorPagedPolicyAwareQuery',
'PhabricatorPackagesSchemaSpec' => 'PhabricatorConfigSchemaSpec',
'PhabricatorPackagesTransactionType' => 'PhabricatorModularTransactionType',
'PhabricatorPagerUIExample' => 'PhabricatorUIExample',

View file

@ -31,6 +31,9 @@ final class PhabricatorPackagesApplication extends PhabricatorApplication {
'/package/' => array(
'(?P<publisherKey>[^/]+)/' => array(
'' => 'PhabricatorPackagesPublisherViewController',
'(?P<packageKey>[^/]+)/' => array(
'' => 'PhabricatorPackagesPackageViewController',
),
),
),
'/packages/' => array(
@ -40,6 +43,12 @@ final class PhabricatorPackagesApplication extends PhabricatorApplication {
$this->getEditRoutePattern('edit/') =>
'PhabricatorPackagesPublisherEditController',
),
'package/' => array(
$this->getQueryRoutePattern() =>
'PhabricatorPackagesPackageListController',
$this->getEditRoutePattern('edit/') =>
'PhabricatorPackagesPackageEditController',
),
),
);
}

View file

@ -0,0 +1,19 @@
<?php
final class PhabricatorPackagesPackageEditConduitAPIMethod
extends PhabricatorEditEngineAPIMethod {
public function getAPIMethodName() {
return 'packages.package.edit';
}
public function newEditEngine() {
return new PhabricatorPackagesPackageEditEngine();
}
public function getMethodSummary() {
return pht(
'Apply transactions to create a new package or edit an existing one.');
}
}

View file

@ -0,0 +1,18 @@
<?php
final class PhabricatorPackagesPackageSearchConduitAPIMethod
extends PhabricatorSearchEngineAPIMethod {
public function getAPIMethodName() {
return 'packages.package.search';
}
public function newSearchEngine() {
return new PhabricatorPackagesPackageSearchEngine();
}
public function getMethodSummary() {
return pht('Read information about packages.');
}
}

View file

@ -0,0 +1,4 @@
<?php
abstract class PhabricatorPackagesPackageController
extends PhabricatorPackagesController {}

View file

@ -0,0 +1,12 @@
<?php
final class PhabricatorPackagesPackageEditController
extends PhabricatorPackagesPackageController {
public function handleRequest(AphrontRequest $request) {
return id(new PhabricatorPackagesPackageEditEngine())
->setController($this)
->buildResponse();
}
}

View file

@ -0,0 +1,26 @@
<?php
final class PhabricatorPackagesPackageListController
extends PhabricatorPackagesPackageController {
public function shouldAllowPublic() {
return true;
}
public function handleRequest(AphrontRequest $request) {
return id(new PhabricatorPackagesPackageSearchEngine())
->setController($this)
->buildResponse();
}
protected function buildApplicationCrumbs() {
$crumbs = parent::buildApplicationCrumbs();
id(new PhabricatorPackagesPackageEditEngine())
->setViewer($this->getViewer())
->addActionToCrumbs($crumbs);
return $crumbs;
}
}

View file

@ -0,0 +1,87 @@
<?php
final class PhabricatorPackagesPackageViewController
extends PhabricatorPackagesPackageController {
public function shouldAllowPublic() {
return true;
}
public function handleRequest(AphrontRequest $request) {
$viewer = $request->getViewer();
$publisher_key = $request->getURIData('publisherKey');
$package_key = $request->getURIData('packageKey');
$full_key = $publisher_key.'/'.$package_key;
$package = id(new PhabricatorPackagesPackageQuery())
->setViewer($viewer)
->withFullKeys(array($full_key))
->executeOne();
if (!$package) {
return new Aphront404Response();
}
$publisher = $package->getPublisher();
$crumbs = $this->buildApplicationCrumbs()
->addTextCrumb($publisher->getName(), $publisher->getURI())
->addTextCrumb($package->getName())
->setBorder(true);
$header = $this->buildHeaderView($package);
$curtain = $this->buildCurtain($package);
$timeline = $this->buildTransactionTimeline(
$package,
new PhabricatorPackagesPackageTransactionQuery());
$package_view = id(new PHUITwoColumnView())
->setHeader($header)
->setCurtain($curtain)
->setMainColumn($timeline);
return $this->newPage()
->setCrumbs($crumbs)
->setPageObjectPHIDs(
array(
$package->getPHID(),
))
->appendChild($package_view);
}
private function buildHeaderView(PhabricatorPackagesPackage $package) {
$viewer = $this->getViewer();
$name = $package->getName();
return id(new PHUIHeaderView())
->setViewer($viewer)
->setHeader($name)
->setPolicyObject($package)
->setHeaderIcon('fa-gift');
}
private function buildCurtain(PhabricatorPackagesPackage $package) {
$viewer = $this->getViewer();
$curtain = $this->newCurtainView($package);
$can_edit = PhabricatorPolicyFilter::hasCapability(
$viewer,
$package,
PhabricatorPolicyCapability::CAN_EDIT);
$id = $package->getID();
$edit_uri = $this->getApplicationURI("package/edit/{$id}/");
$curtain->addAction(
id(new PhabricatorActionView())
->setName(pht('Edit Package'))
->setIcon('fa-pencil')
->setDisabled(!$can_edit)
->setHref($edit_uri));
return $curtain;
}
}

View file

@ -0,0 +1,104 @@
<?php
final class PhabricatorPackagesPackageEditEngine
extends PhabricatorPackagesEditEngine {
const ENGINECONST = 'packages.package';
public function getEngineName() {
return pht('Package Packages');
}
public function getSummaryHeader() {
return pht('Edit Package Package Configurations');
}
public function getSummaryText() {
return pht('This engine is used to edit Packages packages.');
}
protected function newEditableObject() {
$viewer = $this->getViewer();
return PhabricatorPackagesPackage::initializeNewPackage($viewer);
}
protected function newObjectQuery() {
return new PhabricatorPackagesPackageQuery();
}
protected function getObjectCreateTitleText($object) {
return pht('Create Package');
}
protected function getObjectCreateButtonText($object) {
return pht('Create Package');
}
protected function getObjectEditTitleText($object) {
return pht('Edit Package: %s', $object->getName());
}
protected function getObjectEditShortText($object) {
return pht('Edit Package');
}
protected function getObjectCreateShortText() {
return pht('Create Package');
}
protected function getObjectName() {
return pht('Package');
}
protected function getEditorURI() {
return '/packages/package/edit/';
}
protected function getObjectCreateCancelURI($object) {
return '/packages/package/';
}
protected function getObjectViewURI($object) {
return $object->getURI();
}
protected function buildCustomEditFields($object) {
$fields = array();
if ($this->getIsCreate()) {
$fields[] = id(new PhabricatorDatasourceEditField())
->setKey('publisher')
->setAliases(array('publisherPHID'))
->setLabel(pht('Publisher'))
->setDescription(pht('Publisher for this package.'))
->setTransactionType(
PhabricatorPackagesPackagePublisherTransaction::TRANSACTIONTYPE)
->setIsRequired(true)
->setDatasource(new PhabricatorPackagesPublisherDatasource())
->setSingleValue($object->getPublisherPHID());
}
$fields[] = id(new PhabricatorTextEditField())
->setKey('name')
->setLabel(pht('Name'))
->setDescription(pht('Name of the package.'))
->setTransactionType(
PhabricatorPackagesPackageNameTransaction::TRANSACTIONTYPE)
->setIsRequired(true)
->setValue($object->getName());
if ($this->getIsCreate()) {
$fields[] = id(new PhabricatorTextEditField())
->setKey('packageKey')
->setLabel(pht('Package Key'))
->setDescription(pht('Unique key to identify the package.'))
->setTransactionType(
PhabricatorPackagesPackageKeyTransaction::TRANSACTIONTYPE)
->setIsRequired(true)
->setValue($object->getPackageKey());
}
return $fields;
}
}

View file

@ -0,0 +1,55 @@
<?php
final class PhabricatorPackagesPackageEditor
extends PhabricatorPackagesEditor {
public function getEditorObjectsDescription() {
return pht('Package Packages');
}
public function getCreateObjectTitle($author, $object) {
return pht('%s created this package.', $author);
}
public function getCreateObjectTitleForFeed($author, $object) {
return pht('%s created %s.', $author, $object);
}
public function getTransactionTypes() {
$types = parent::getTransactionTypes();
$types[] = PhabricatorTransactions::TYPE_VIEW_POLICY;
$types[] = PhabricatorTransactions::TYPE_EDIT_POLICY;
return $types;
}
protected function shouldPublishFeedStory(
PhabricatorLiskDAO $object,
array $xactions) {
return true;
}
protected function getMailTo(PhabricatorLiskDAO $object) {
return array();
}
protected function didCatchDuplicateKeyException(
PhabricatorLiskDAO $object,
array $xactions,
Exception $ex) {
$errors = array();
$errors[] = new PhabricatorApplicationTransactionValidationError(
PhabricatorPackagesPackageKeyTransaction::TRANSACTIONTYPE,
pht('Duplicate'),
pht(
'The package key "%s" is already in use by another package provided '.
'by this publisher.',
$object->getPackageKey()),
null);
throw new PhabricatorApplicationTransactionValidationException($errors);
}
}

View file

@ -76,13 +76,13 @@ final class PhabricatorPackagesPublisherEditEngine
if ($this->getIsCreate()) {
$fields[] = id(new PhabricatorTextEditField())
->setKey('publisherKey')
->setLabel(pht('Publisher Key'))
->setDescription(pht('Unique key to identify the publisher.'))
->setTransactionType(
PhabricatorPackagesPublisherKeyTransaction::TRANSACTIONTYPE)
->setIsRequired(true)
->setValue($object->getPublisherKey());
->setKey('publisherKey')
->setLabel(pht('Publisher Key'))
->setDescription(pht('Unique key to identify the publisher.'))
->setTransactionType(
PhabricatorPackagesPublisherKeyTransaction::TRANSACTIONTYPE)
->setIsRequired(true)
->setValue($object->getPublisherKey());
}
return $fields;

View file

@ -0,0 +1,45 @@
<?php
final class PhabricatorPackagesPackagePHIDType
extends PhabricatorPHIDType {
const TYPECONST = 'PPAK';
public function getTypeName() {
return pht('Package');
}
public function newObject() {
return new PhabricatorPackagesPackage();
}
public function getPHIDTypeApplicationClass() {
return 'PhabricatorPackagesApplication';
}
protected function buildQueryForObjects(
PhabricatorObjectQuery $query,
array $phids) {
return id(new PhabricatorPackagesPackageQuery())
->withPHIDs($phids);
}
public function loadHandles(
PhabricatorHandleQuery $query,
array $handles,
array $objects) {
foreach ($handles as $phid => $handle) {
$package = $objects[$phid];
$name = $package->getName();
$uri = $package->getURI();
$handle
->setName($name)
->setURI($uri);
}
}
}

View file

@ -0,0 +1,152 @@
<?php
final class PhabricatorPackagesPackageQuery
extends PhabricatorPackagesQuery {
private $ids;
private $phids;
private $publisherPHIDs;
private $packageKeys;
private $fullKeys;
public function withIDs(array $ids) {
$this->ids = $ids;
return $this;
}
public function withPHIDs(array $phids) {
$this->phids = $phids;
return $this;
}
public function withPublisherPHIDs(array $phids) {
$this->publisherPHIDs = $phids;
return $this;
}
public function withPackageKeys(array $keys) {
$this->packageKeys = $keys;
return $this;
}
public function withFullKeys(array $keys) {
$this->fullKeys = $keys;
return $this;
}
public function newResultObject() {
return new PhabricatorPackagesPackage();
}
protected function loadPage() {
return $this->loadStandardPage($this->newResultObject());
}
protected function buildWhereClauseParts(AphrontDatabaseConnection $conn) {
$where = parent::buildWhereClauseParts($conn);
if ($this->ids !== null) {
$where[] = qsprintf(
$conn,
'p.id IN (%Ld)',
$this->ids);
}
if ($this->phids !== null) {
$where[] = qsprintf(
$conn,
'p.phid IN (%Ls)',
$this->phids);
}
if ($this->publisherPHIDs !== null) {
$where[] = qsprintf(
$conn,
'p.phid IN (%Ls)',
$this->publisherPHIDs);
}
if ($this->packageKeys !== null) {
$where[] = qsprintf(
$conn,
'p.packageKey IN (%Ls)',
$this->packageKeys);
}
if ($this->fullKeys !== null) {
$parts = array();
foreach ($this->fullKeys as $full_key) {
$key_parts = explode('/', $full_key, 2);
if (count($key_parts) != 2) {
continue;
}
$parts[] = qsprintf(
$conn,
'(u.publisherKey = %s AND p.packageKey = %s)',
$key_parts[0],
$key_parts[1]);
}
// If none of the full keys we were provided were valid, we don't
// match any results.
if (!$parts) {
throw new PhabricatorEmptyQueryException();
}
$where[] = qsprintf(
$conn,
'%Q',
implode(' OR ', $parts));
}
return $where;
}
protected function buildJoinClauseParts(AphrontDatabaseConnection $conn) {
$joins = parent::buildJoinClauseParts($conn);
$join_publisher = ($this->fullKeys !== null);
if ($join_publisher) {
$publisher_table = new PhabricatorPackagesPublisher();
$joins[] = qsprintf(
$conn,
'JOIN %T u ON u.phid = p.publisherPHID',
$publisher_table->getTableName());
}
return $joins;
}
protected function willFilterPage(array $packages) {
$publisher_phids = mpull($packages, 'getPublisherPHID');
$publishers = id(new PhabricatorPackagesPublisherQuery())
->setViewer($this->getViewer())
->setParentQuery($this)
->withPHIDs($publisher_phids)
->execute();
$publishers = mpull($publishers, null, 'getPHID');
foreach ($packages as $key => $package) {
$publisher = idx($publishers, $package->getPublisherPHID());
if (!$publisher) {
unset($packages[$key]);
$this->didRejectResult($package);
continue;
}
$package->attachPublisher($publisher);
}
return $packages;
}
protected function getPrimaryTableAlias() {
return 'p';
}
}

View file

@ -0,0 +1,77 @@
<?php
final class PhabricatorPackagesPackageSearchEngine
extends PhabricatorApplicationSearchEngine {
public function getResultTypeDescription() {
return pht('Packages');
}
public function getApplicationClassName() {
return 'PhabricatorPackagesApplication';
}
public function newQuery() {
return id(new PhabricatorPackagesPackageQuery());
}
protected function buildQueryFromParameters(array $map) {
$query = $this->newQuery();
return $query;
}
protected function buildCustomSearchFields() {
return array();
}
protected function getURI($path) {
return '/packages/package/'.$path;
}
protected function getBuiltinQueryNames() {
$names = array(
'all' => pht('All Packages'),
);
return $names;
}
public function buildSavedQueryFromBuiltin($query_key) {
$query = $this->newSavedQuery();
$query->setQueryKey($query_key);
switch ($query_key) {
case 'all':
return $query;
}
return parent::buildSavedQueryFromBuiltin($query_key);
}
protected function renderResultList(
array $packages,
PhabricatorSavedQuery $query,
array $handles) {
assert_instances_of($packages, 'PhabricatorPackagesPackage');
$viewer = $this->requireViewer();
$list = id(new PHUIObjectItemListView())
->setViewer($viewer);
foreach ($packages as $package) {
$item = id(new PHUIObjectItemView())
->setObjectName($package->getFullKey())
->setHeader($package->getName())
->setHref($package->getURI());
$list->addItem($item);
}
return id(new PhabricatorApplicationSearchResultView())
->setObjectList($list)
->setNoDataString(pht('No packages found.'));
}
}

View file

@ -0,0 +1,10 @@
<?php
final class PhabricatorPackagesPackageTransactionQuery
extends PhabricatorApplicationTransactionQuery {
public function getTemplateApplicationTransaction() {
return new PhabricatorPackagesPackageTransaction();
}
}

View file

@ -1,7 +1,7 @@
<?php
final class PhabricatorPackagesPublisherQuery
extends PhabricatorCursorPagedPolicyAwareQuery {
extends PhabricatorPackagesQuery {
private $ids;
private $phids;
@ -57,8 +57,4 @@ final class PhabricatorPackagesPublisherQuery
return $where;
}
public function getQueryApplicationClass() {
return 'PhabricatorPackagesApplication';
}
}

View file

@ -0,0 +1,10 @@
<?php
abstract class PhabricatorPackagesQuery
extends PhabricatorCursorPagedPolicyAwareQuery {
public function getQueryApplicationClass() {
return 'PhabricatorPackagesApplication';
}
}

View file

@ -0,0 +1,222 @@
<?php
final class PhabricatorPackagesPackage
extends PhabricatorPackagesDAO
implements
PhabricatorPolicyInterface,
PhabricatorApplicationTransactionInterface,
PhabricatorDestructibleInterface,
PhabricatorSubscribableInterface,
PhabricatorProjectInterface,
PhabricatorConduitResultInterface {
protected $name;
protected $publisherPHID;
protected $packageKey;
protected $viewPolicy;
protected $editPolicy;
private $publisher = self::ATTACHABLE;
public static function initializeNewPackage(PhabricatorUser $actor) {
return id(new self());
}
protected function getConfiguration() {
return array(
self::CONFIG_AUX_PHID => true,
self::CONFIG_COLUMN_SCHEMA => array(
'name' => 'text64',
'packageKey' => 'sort64',
),
self::CONFIG_KEY_SCHEMA => array(
'key_package' => array(
'columns' => array('publisherPHID', 'packageKey'),
'unique' => true,
),
),
) + parent::getConfiguration();
}
public function generatePHID() {
return PhabricatorPHID::generateNewPHID(
PhabricatorPackagesPackagePHIDType::TYPECONST);
}
public function getURI() {
$full_key = $this->getFullKey();
return "/package/{$full_key}/";
}
public function getFullKey() {
$publisher = $this->getPublisher();
$publisher_key = $publisher->getPublisherKey();
$package_key = $this->getPackageKey();
return "{$publisher_key}/{$package_key}";
}
public function attachPublisher(PhabricatorPackagesPublisher $publisher) {
$this->publisher = $publisher;
return $this;
}
public function getPublisher() {
return $this->assertAttached($this->publisher);
}
public static function assertValidPackageName($value) {
$length = phutil_utf8_strlen($value);
if (!$length) {
throw new Exception(
pht(
'Package name "%s" is not valid: package names are required.',
$value));
}
$max_length = 64;
if ($length > $max_length) {
throw new Exception(
pht(
'Package name "%s" is not valid: package names must not be '.
'more than %s characters long.',
$value,
new PhutilNumber($max_length)));
}
}
public static function assertValidPackageKey($value) {
$length = phutil_utf8_strlen($value);
if (!$length) {
throw new Exception(
pht(
'Package key "%s" is not valid: package keys are required.',
$value));
}
$max_length = 64;
if ($length > $max_length) {
throw new Exception(
pht(
'Package key "%s" is not valid: package keys must not be '.
'more than %s characters long.',
$value,
new PhutilNumber($max_length)));
}
if (!preg_match('/^[a-z]+\z/', $value)) {
throw new Exception(
pht(
'Package key "%s" is not valid: package keys may only contain '.
'lowercase latin letters.',
$value));
}
}
/* -( PhabricatorSubscribableInterface )----------------------------------- */
public function isAutomaticallySubscribed($phid) {
return false;
}
/* -( Policy Interface )--------------------------------------------------- */
public function getCapabilities() {
return array(
PhabricatorPolicyCapability::CAN_VIEW,
PhabricatorPolicyCapability::CAN_EDIT,
);
}
public function getPolicy($capability) {
switch ($capability) {
case PhabricatorPolicyCapability::CAN_VIEW:
return $this->getViewPolicy();
case PhabricatorPolicyCapability::CAN_EDIT:
return $this->getEditPolicy();
}
}
public function hasAutomaticCapability($capability, PhabricatorUser $user) {
return false;
}
public function describeAutomaticCapability($capability) {
return null;
}
/* -( PhabricatorDestructibleInterface )----------------------------------- */
public function destroyObjectPermanently(
PhabricatorDestructionEngine $engine) {
$this->delete();
}
/* -( PhabricatorApplicationTransactionInterface )------------------------- */
public function getApplicationTransactionEditor() {
return new PhabricatorPackagesPackageEditor();
}
public function getApplicationTransactionObject() {
return $this;
}
public function getApplicationTransactionTemplate() {
return new PhabricatorPackagesPackageTransaction();
}
public function willRenderTimeline(
PhabricatorApplicationTransactionView $timeline,
AphrontRequest $request) {
return $timeline;
}
/* -( PhabricatorConduitResultInterface )---------------------------------- */
public function getFieldSpecificationsForConduit() {
return array(
id(new PhabricatorConduitSearchFieldSpecification())
->setKey('name')
->setType('string')
->setDescription(pht('The name of the package.')),
id(new PhabricatorConduitSearchFieldSpecification())
->setKey('packageKey')
->setType('string')
->setDescription(pht('The unique key of the package.')),
);
}
public function getFieldValuesForConduit() {
$publisher = $this->getPublisher();
$publisher_map = array(
'id' => (int)$publisher->getID(),
'phid' => $publisher->getPHID(),
'name' => $publisher->getName(),
'publisherKey' => $publisher->getPublisherKey(),
);
return array(
'name' => $this->getName(),
'packageKey' => $this->getPackageKey(),
'fullKey' => $this->getFullKey(),
'publisher' => $publisher_map,
);
}
public function getConduitSearchAttachments() {
return array();
}
}

View file

@ -0,0 +1,18 @@
<?php
final class PhabricatorPackagesPackageTransaction
extends PhabricatorModularTransaction {
public function getApplicationName() {
return 'packages';
}
public function getApplicationTransactionType() {
return PhabricatorPackagesPackagePHIDType::TYPECONST;
}
public function getBaseTransactionClass() {
return 'PhabricatorPackagesPackageTransactionType';
}
}

View file

@ -134,7 +134,21 @@ final class PhabricatorPackagesPublisher
public function destroyObjectPermanently(
PhabricatorDestructionEngine $engine) {
$this->delete();
$viewer = $engine->getViewer();
$this->openTransaction();
$packages = id(new PhabricatorPackagesPackageQuery())
->setViewer($viewer)
->withPublisherPHIDs(array($this->getPHID()))
->execute();
foreach ($packages as $package) {
$engine->destroyObject($package);
}
$this->delete();
$this->saveTransaction();
}

View file

@ -0,0 +1,35 @@
<?php
final class PhabricatorPackagesPublisherDatasource
extends PhabricatorTypeaheadDatasource {
public function getBrowseTitle() {
return pht('Browse Package Publishers');
}
public function getPlaceholderText() {
return pht('Type a publisher name...');
}
public function getDatasourceApplicationClass() {
return 'PhabricatorPackagesApplication';
}
public function loadResults() {
$viewer = $this->getViewer();
$raw_query = $this->getRawQuery();
$publisher_query = id(new PhabricatorPackagesPublisherQuery());
$publishers = $this->executeQuery($publisher_query);
$results = array();
foreach ($publishers as $publisher) {
$results[] = id(new PhabricatorTypeaheadResult())
->setName($publisher->getName())
->setPHID($publisher->getPHID());
}
return $this->filterResultsAgainstTokens($results);
}
}

View file

@ -0,0 +1,47 @@
<?php
final class PhabricatorPackagesPackageKeyTransaction
extends PhabricatorPackagesPackageTransactionType {
const TRANSACTIONTYPE = 'packages.package.key';
public function generateOldValue($object) {
return $object->getPackageKey();
}
public function applyInternalEffects($object, $value) {
$object->setPackageKey($value);
}
public function validateTransactions($object, array $xactions) {
$errors = array();
if ($this->isEmptyTextTransaction($object->getName(), $xactions)) {
$errors[] = $this->newRequiredError(
pht(
'Each package provided by a publisher must have a '.
'unique package key.'));
return $errors;
}
if (!$this->isNewObject()) {
foreach ($xactions as $xaction) {
$errors[] = $this->newInvalidError(
pht('Once a package is created, its key can not be changed.'),
$xaction);
}
}
foreach ($xactions as $xaction) {
$value = $xaction->getNewValue();
try {
PhabricatorPackagesPackage::assertValidPackageKey($value);
} catch (Exception $ex) {
$errors[] = $this->newInvalidError($ex->getMessage(), $xaction);
}
}
return $errors;
}
}

View file

@ -0,0 +1,54 @@
<?php
final class PhabricatorPackagesPackageNameTransaction
extends PhabricatorPackagesPackageTransactionType {
const TRANSACTIONTYPE = 'packages.package.name';
public function generateOldValue($object) {
return $object->getName();
}
public function applyInternalEffects($object, $value) {
$object->setName($value);
}
public function getTitle() {
return pht(
'%s changed the name of this package from %s to %s.',
$this->renderAuthor(),
$this->renderOldValue(),
$this->renderNewValue());
}
public function getTitleForFeed() {
return pht(
'%s updated the name for %s from %s to %s.',
$this->renderAuthor(),
$this->renderObject(),
$this->renderOldValue(),
$this->renderNewValue());
}
public function validateTransactions($object, array $xactions) {
$errors = array();
if ($this->isEmptyTextTransaction($object->getName(), $xactions)) {
$errors[] = $this->newRequiredError(
pht('Packages must have a name.'));
return $errors;
}
foreach ($xactions as $xaction) {
$value = $xaction->getNewValue();
try {
PhabricatorPackagesPackage::assertValidPackageName($value);
} catch (Exception $ex) {
$errors[] = $this->newInvalidError($ex->getMessage(), $xaction);
}
}
return $errors;
}
}

View file

@ -0,0 +1,66 @@
<?php
final class PhabricatorPackagesPackagePublisherTransaction
extends PhabricatorPackagesPackageTransactionType {
const TRANSACTIONTYPE = 'packages.package.publisher';
public function generateOldValue($object) {
return $object->getPublisherPHID();
}
public function applyInternalEffects($object, $value) {
$object->setPublisherPHID($value);
}
public function validateTransactions($object, array $xactions) {
$errors = array();
if ($this->isEmptyTextTransaction($object->getName(), $xactions)) {
$errors[] = $this->newRequiredError(
pht(
'You must select a publisher when creating a package.'));
return $errors;
}
if (!$this->isNewObject()) {
foreach ($xactions as $xaction) {
$errors[] = $this->newInvalidError(
pht('Once a package is created, its publisher can not be changed.'),
$xaction);
}
}
$viewer = $this->getActor();
foreach ($xactions as $xaction) {
$publisher_phid = $xaction->getNewValue();
$publisher = id(new PhabricatorPackagesPublisherQuery())
->setViewer($viewer)
->withPHIDs(array($publisher_phid))
->setRaisePolicyExceptions(false)
->requireCapabilities(
array(
PhabricatorPolicyCapability::CAN_VIEW,
PhabricatorPolicyCapability::CAN_EDIT,
))
->executeOne();
if (!$publisher) {
$errors[] = $this->newInvalidError(
pht(
'Publisher "%s" is invalid: the publisher must exist and you '.
'must have permission to edit it in order to create a new '.
'package.',
$publisher_phid),
$xaction);
continue;
}
$object->attachPublisher($publisher);
}
return $errors;
}
}

View file

@ -0,0 +1,4 @@
<?php
abstract class PhabricatorPackagesPackageTransactionType
extends PhabricatorPackagesTransactionType {}