1
0
Fork 0
mirror of https://we.phorge.it/source/phorge.git synced 2024-12-18 11:30:55 +01:00

Add "Spaces", an application for managing policy namespaces

Summary: Ref T3820. This doesn't actually do anything yet, but dumps in all the plumbing.

Test Plan:
{F156989}

{F156990}

{F156991}

Reviewers: btrahan

Reviewed By: btrahan

Subscribers: eadler, wienczny, jdloft, devurandom, thz, hwinkel, 20after4, sascha-egerer, seporaitis, joshuaspence, chad, epriestley

Maniphest Tasks: T3820

Differential Revision: https://secure.phabricator.com/D9204
This commit is contained in:
epriestley 2015-06-01 11:28:38 -07:00
parent 7a9174fa21
commit 541b4c86b4
21 changed files with 1045 additions and 0 deletions

View file

@ -0,0 +1,12 @@
CREATE TABLE {$NAMESPACE}_spaces.spaces_namespace (
id INT UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY,
phid VARBINARY(64) NOT NULL,
namespaceName VARCHAR(255) NOT NULL COLLATE {$COLLATE_TEXT},
viewPolicy VARBINARY(64) NOT NULL,
editPolicy VARBINARY(64) NOT NULL,
isDefaultNamespace BOOL,
dateCreated INT UNSIGNED NOT NULL,
dateModified INT UNSIGNED NOT NULL,
UNIQUE KEY `key_phid` (phid),
UNIQUE KEY `key_default` (isDefaultNamespace)
) ENGINE=InnoDB, COLLATE {$COLLATE_TEXT};

View file

@ -0,0 +1,19 @@
CREATE TABLE {$NAMESPACE}_spaces.spaces_namespacetransaction (
id INT UNSIGNED NOT NULL PRIMARY KEY AUTO_INCREMENT,
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),
commentVersion INT UNSIGNED NOT NULL,
transactionType VARCHAR(32) NOT NULL COLLATE {$COLLATE_TEXT},
oldValue LONGTEXT NOT NULL COLLATE {$COLLATE_TEXT},
newValue LONGTEXT NOT NULL COLLATE {$COLLATE_TEXT},
contentSource LONGTEXT NOT NULL COLLATE {$COLLATE_TEXT},
metadata LONGTEXT NOT NULL COLLATE {$COLLATE_TEXT},
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

@ -1318,6 +1318,7 @@ phutil_register_library_map(array(
'PhabricatorApplicationSearchController' => 'applications/search/controller/PhabricatorApplicationSearchController.php',
'PhabricatorApplicationSearchEngine' => 'applications/search/engine/PhabricatorApplicationSearchEngine.php',
'PhabricatorApplicationSearchResultsControllerInterface' => 'applications/search/interface/PhabricatorApplicationSearchResultsControllerInterface.php',
'PhabricatorApplicationSpaces' => 'applications/spaces/application/PhabricatorApplicationSpaces.php',
'PhabricatorApplicationStatusView' => 'applications/meta/view/PhabricatorApplicationStatusView.php',
'PhabricatorApplicationTransaction' => 'applications/transactions/storage/PhabricatorApplicationTransaction.php',
'PhabricatorApplicationTransactionComment' => 'applications/transactions/storage/PhabricatorApplicationTransactionComment.php',
@ -2561,6 +2562,22 @@ phutil_register_library_map(array(
'PhabricatorSlugTestCase' => 'infrastructure/util/__tests__/PhabricatorSlugTestCase.php',
'PhabricatorSortTableUIExample' => 'applications/uiexample/examples/PhabricatorSortTableUIExample.php',
'PhabricatorSourceCodeView' => 'view/layout/PhabricatorSourceCodeView.php',
'PhabricatorSpacesCapabilityCreateSpaces' => 'applications/spaces/capability/PhabricatorSpacesCapabilityCreateSpaces.php',
'PhabricatorSpacesCapabilityDefaultEdit' => 'applications/spaces/capability/PhabricatorSpacesCapabilityDefaultEdit.php',
'PhabricatorSpacesCapabilityDefaultView' => 'applications/spaces/capability/PhabricatorSpacesCapabilityDefaultView.php',
'PhabricatorSpacesController' => 'applications/spaces/controller/PhabricatorSpacesController.php',
'PhabricatorSpacesDAO' => 'applications/spaces/storage/PhabricatorSpacesDAO.php',
'PhabricatorSpacesEditController' => 'applications/spaces/controller/PhabricatorSpacesEditController.php',
'PhabricatorSpacesInterface' => 'applications/spaces/interface/PhabricatorSpacesInterface.php',
'PhabricatorSpacesListController' => 'applications/spaces/controller/PhabricatorSpacesListController.php',
'PhabricatorSpacesNamespace' => 'applications/spaces/storage/PhabricatorSpacesNamespace.php',
'PhabricatorSpacesNamespaceEditor' => 'applications/spaces/editor/PhabricatorSpacesNamespaceEditor.php',
'PhabricatorSpacesNamespacePHIDType' => 'applications/spaces/phid/PhabricatorSpacesNamespacePHIDType.php',
'PhabricatorSpacesNamespaceQuery' => 'applications/spaces/query/PhabricatorSpacesNamespaceQuery.php',
'PhabricatorSpacesNamespaceSearchEngine' => 'applications/spaces/query/PhabricatorSpacesNamespaceSearchEngine.php',
'PhabricatorSpacesNamespaceTransaction' => 'applications/spaces/storage/PhabricatorSpacesNamespaceTransaction.php',
'PhabricatorSpacesNamespaceTransactionQuery' => 'applications/spaces/query/PhabricatorSpacesNamespaceTransactionQuery.php',
'PhabricatorSpacesViewController' => 'applications/spaces/controller/PhabricatorSpacesViewController.php',
'PhabricatorStandardCustomField' => 'infrastructure/customfield/standard/PhabricatorStandardCustomField.php',
'PhabricatorStandardCustomFieldBool' => 'infrastructure/customfield/standard/PhabricatorStandardCustomFieldBool.php',
'PhabricatorStandardCustomFieldCredential' => 'infrastructure/customfield/standard/PhabricatorStandardCustomFieldCredential.php',
@ -4647,6 +4664,7 @@ phutil_register_library_map(array(
'PhabricatorApplicationPanelController' => 'PhabricatorApplicationsController',
'PhabricatorApplicationQuery' => 'PhabricatorCursorPagedPolicyAwareQuery',
'PhabricatorApplicationSearchController' => 'PhabricatorSearchBaseController',
'PhabricatorApplicationSpaces' => 'PhabricatorApplication',
'PhabricatorApplicationStatusView' => 'AphrontView',
'PhabricatorApplicationTransaction' => array(
'PhabricatorLiskDAO',
@ -6027,6 +6045,26 @@ phutil_register_library_map(array(
'PhabricatorSlugTestCase' => 'PhabricatorTestCase',
'PhabricatorSortTableUIExample' => 'PhabricatorUIExample',
'PhabricatorSourceCodeView' => 'AphrontView',
'PhabricatorSpacesCapabilityCreateSpaces' => 'PhabricatorPolicyCapability',
'PhabricatorSpacesCapabilityDefaultEdit' => 'PhabricatorPolicyCapability',
'PhabricatorSpacesCapabilityDefaultView' => 'PhabricatorPolicyCapability',
'PhabricatorSpacesController' => 'PhabricatorController',
'PhabricatorSpacesDAO' => 'PhabricatorLiskDAO',
'PhabricatorSpacesEditController' => 'PhabricatorSpacesController',
'PhabricatorSpacesInterface' => 'PhabricatorPHIDInterface',
'PhabricatorSpacesListController' => 'PhabricatorSpacesController',
'PhabricatorSpacesNamespace' => array(
'PhabricatorSpacesDAO',
'PhabricatorPolicyInterface',
'PhabricatorApplicationTransactionInterface',
),
'PhabricatorSpacesNamespaceEditor' => 'PhabricatorApplicationTransactionEditor',
'PhabricatorSpacesNamespacePHIDType' => 'PhabricatorPHIDType',
'PhabricatorSpacesNamespaceQuery' => 'PhabricatorCursorPagedPolicyAwareQuery',
'PhabricatorSpacesNamespaceSearchEngine' => 'PhabricatorApplicationSearchEngine',
'PhabricatorSpacesNamespaceTransaction' => 'PhabricatorApplicationTransaction',
'PhabricatorSpacesNamespaceTransactionQuery' => 'PhabricatorApplicationTransactionQuery',
'PhabricatorSpacesViewController' => 'PhabricatorSpacesController',
'PhabricatorStandardCustomField' => 'PhabricatorCustomField',
'PhabricatorStandardCustomFieldBool' => 'PhabricatorStandardCustomField',
'PhabricatorStandardCustomFieldCredential' => 'PhabricatorStandardCustomField',

View file

@ -0,0 +1,67 @@
<?php
final class PhabricatorApplicationSpaces extends PhabricatorApplication {
public function getBaseURI() {
return '/spaces/';
}
public function getName() {
return pht('Spaces');
}
public function getShortDescription() {
return pht('Policy Namespaces');
}
public function getFontIcon() {
return 'fa-compass';
}
public function getTitleGlyph() {
return "\xE2\x97\x8B";
}
public function getFlavorText() {
return pht('Control access to groups of objects.');
}
public function getApplicationGroup() {
return self::GROUP_UTILITIES;
}
public function canUninstall() {
return true;
}
public function isPrototype() {
return true;
}
public function getRoutes() {
return array(
'/S(?P<id>[1-9]\d*)' => 'PhabricatorSpacesViewController',
'/spaces/' => array(
'(?:query/(?P<queryKey>[^/]+)/)?' => 'PhabricatorSpacesListController',
'create/' => 'PhabricatorSpacesEditController',
'edit/(?:(?P<id>\d+)/)?' => 'PhabricatorSpacesEditController',
),
);
}
protected function getCustomCapabilities() {
return array(
PhabricatorSpacesCapabilityCreateSpaces::CAPABILITY => array(
'default' => PhabricatorPolicies::POLICY_ADMIN,
),
PhabricatorSpacesCapabilityDefaultView::CAPABILITY => array(
'caption' => pht('Default view policy for newly created spaces.'),
),
PhabricatorSpacesCapabilityDefaultEdit::CAPABILITY => array(
'caption' => pht('Default edit policy for newly created spaces.'),
'default' => PhabricatorPolicies::POLICY_ADMIN,
),
);
}
}

View file

@ -0,0 +1,16 @@
<?php
final class PhabricatorSpacesCapabilityCreateSpaces
extends PhabricatorPolicyCapability {
const CAPABILITY = 'spaces.create';
public function getCapabilityName() {
return pht('Can Create Spaces');
}
public function describeCapabilityRejection() {
return pht('You do not have permission to create spaces.');
}
}

View file

@ -0,0 +1,12 @@
<?php
final class PhabricatorSpacesCapabilityDefaultEdit
extends PhabricatorPolicyCapability {
const CAPABILITY = 'spaces.default.edit';
public function getCapabilityName() {
return pht('Default Edit Policy');
}
}

View file

@ -0,0 +1,16 @@
<?php
final class PhabricatorSpacesCapabilityDefaultView
extends PhabricatorPolicyCapability {
const CAPABILITY = 'spaces.default.view';
public function getCapabilityName() {
return pht('Default View Policy');
}
public function shouldAllowPublicPolicySetting() {
return true;
}
}

View file

@ -0,0 +1,3 @@
<?php
abstract class PhabricatorSpacesController extends PhabricatorController {}

View file

@ -0,0 +1,174 @@
<?php
final class PhabricatorSpacesEditController
extends PhabricatorSpacesController {
public function handleRequest(AphrontRequest $request) {
$viewer = $request->getUser();
$make_default = false;
$id = $request->getURIData('id');
if ($id) {
$space = id(new PhabricatorSpacesNamespaceQuery())
->setViewer($viewer)
->withIDs(array($id))
->requireCapabilities(
array(
PhabricatorPolicyCapability::CAN_VIEW,
PhabricatorPolicyCapability::CAN_EDIT,
))
->executeOne();
if (!$space) {
return new Aphront404Response();
}
$is_new = false;
$cancel_uri = '/'.$space->getMonogram();
$header_text = pht('Edit %s', $space->getNamespaceName());
$title = pht('Edit Space');
$button_text = pht('Save Changes');
} else {
$this->requireApplicationCapability(
PhabricatorSpacesCapabilityCreateSpaces::CAPABILITY);
$space = PhabricatorSpacesNamespace::initializeNewNamespace($viewer);
$is_new = true;
$cancel_uri = $this->getApplicationURI();
$header_text = pht('Create Space');
$title = pht('Create Space');
$button_text = pht('Create Space');
$default = id(new PhabricatorSpacesNamespaceQuery())
->setViewer(PhabricatorUser::getOmnipotentUser())
->withIsDefaultNamespace(true)
->execute();
if (!$default) {
$make_default = true;
}
}
$validation_exception = null;
$e_name = true;
$v_name = $space->getNamespaceName();
$v_view = $space->getViewPolicy();
$v_edit = $space->getEditPolicy();
if ($request->isFormPost()) {
$xactions = array();
$e_name = null;
$v_name = $request->getStr('name');
$v_view = $request->getStr('viewPolicy');
$v_edit = $request->getStr('editPolicy');
$type_name = PhabricatorSpacesNamespaceTransaction::TYPE_NAME;
$type_default = PhabricatorSpacesNamespaceTransaction::TYPE_DEFAULT;
$type_view = PhabricatorTransactions::TYPE_VIEW_POLICY;
$type_edit = PhabricatorTransactions::TYPE_EDIT_POLICY;
$xactions[] = id(new PhabricatorSpacesNamespaceTransaction())
->setTransactionType($type_name)
->setNewValue($v_name);
$xactions[] = id(new PhabricatorSpacesNamespaceTransaction())
->setTransactionType($type_view)
->setNewValue($v_view);
$xactions[] = id(new PhabricatorSpacesNamespaceTransaction())
->setTransactionType($type_edit)
->setNewValue($v_edit);
if ($make_default) {
$xactions[] = id(new PhabricatorSpacesNamespaceTransaction())
->setTransactionType($type_default)
->setNewValue(1);
}
$editor = id(new PhabricatorSpacesNamespaceEditor())
->setActor($viewer)
->setContinueOnNoEffect(true)
->setContentSourceFromRequest($request);
try {
$editor->applyTransactions($space, $xactions);
return id(new AphrontRedirectResponse())
->setURI('/'.$space->getMonogram());
} catch (PhabricatorApplicationTransactionValidationException $ex) {
$validation_exception = $ex;
$e_name = $ex->getShortMessage($type_name);
}
}
$policies = id(new PhabricatorPolicyQuery())
->setViewer($viewer)
->setObject($space)
->execute();
$form = id(new AphrontFormView())
->setUser($viewer);
if ($make_default) {
$form->appendRemarkupInstructions(
pht(
'NOTE: You are creating the **default space**. All existing '.
'objects will be put into this space. You must create a default '.
'space before you can create other spaces.'));
}
$form
->appendChild(
id(new AphrontFormTextControl())
->setLabel(pht('Name'))
->setName('name')
->setValue($v_name)
->setError($e_name))
->appendChild(
id(new AphrontFormPolicyControl())
->setUser($viewer)
->setCapability(PhabricatorPolicyCapability::CAN_VIEW)
->setPolicyObject($space)
->setPolicies($policies)
->setValue($v_view)
->setName('viewPolicy'))
->appendChild(
id(new AphrontFormPolicyControl())
->setUser($viewer)
->setCapability(PhabricatorPolicyCapability::CAN_EDIT)
->setPolicyObject($space)
->setPolicies($policies)
->setValue($v_edit)
->setName('editPolicy'))
->appendChild(
id(new AphrontFormSubmitControl())
->setValue($button_text)
->addCancelButton($cancel_uri));
$box = id(new PHUIObjectBoxView())
->setHeaderText($header_text)
->setValidationException($validation_exception)
->appendChild($form);
$crumbs = $this->buildApplicationCrumbs();
if (!$is_new) {
$crumbs->addTextCrumb(
$space->getMonogram(),
$cancel_uri);
}
$crumbs->addTextCrumb($title);
return $this->buildApplicationPage(
array(
$crumbs,
$box,
),
array(
'title' => $title,
));
}
}

View file

@ -0,0 +1,52 @@
<?php
final class PhabricatorSpacesListController
extends PhabricatorSpacesController {
public function shouldAllowPublic() {
return true;
}
public function handleRequest(AphrontRequest $request) {
$request = $this->getRequest();
$controller = id(new PhabricatorApplicationSearchController($request))
->setQueryKey($request->getURIData('queryKey'))
->setSearchEngine(new PhabricatorSpacesNamespaceSearchEngine())
->setNavigation($this->buildSideNavView());
return $this->delegateToController($controller);
}
public function buildSideNavView($for_app = false) {
$user = $this->getRequest()->getUser();
$nav = new AphrontSideNavFilterView();
$nav->setBaseURI(new PhutilURI($this->getApplicationURI()));
id(new PhabricatorSpacesNamespaceSearchEngine())
->setViewer($user)
->addNavigationItems($nav->getMenu());
$nav->selectFilter(null);
return $nav;
}
public function buildApplicationCrumbs() {
$crumbs = parent::buildApplicationCrumbs();
$can_create = $this->hasApplicationCapability(
PhabricatorSpacesCapabilityCreateSpaces::CAPABILITY);
$crumbs->addAction(
id(new PHUIListItemView())
->setName(pht('Create Space'))
->setHref($this->getApplicationURI('create/'))
->setIcon('fa-plus-square')
->setDisabled(!$can_create)
->setWorkflow(!$can_create));
return $crumbs;
}
}

View file

@ -0,0 +1,104 @@
<?php
final class PhabricatorSpacesViewController
extends PhabricatorSpacesController {
public function shouldAllowPublic() {
return true;
}
public function handleRequest(AphrontRequest $request) {
$viewer = $this->getViewer();
$space = id(new PhabricatorSpacesNamespaceQuery())
->setViewer($viewer)
->withIDs(array($request->getURIData('id')))
->executeOne();
if (!$space) {
return new Aphront404Response();
}
$action_list = $this->buildActionListView($space);
$property_list = $this->buildPropertyListView($space);
$property_list->setActionList($action_list);
$xactions = id(new PhabricatorSpacesNamespaceTransactionQuery())
->setViewer($viewer)
->withObjectPHIDs(array($space->getPHID()))
->execute();
$timeline = $this->buildTransactionTimeline(
$space,
new PhabricatorSpacesNamespaceTransactionQuery());
$timeline->setShouldTerminate(true);
$header = id(new PHUIHeaderView())
->setUser($viewer)
->setHeader($space->getNamespaceName())
->setPolicyObject($space);
$box = id(new PHUIObjectBoxView())
->setHeader($header)
->addPropertyList($property_list);
$crumbs = $this->buildApplicationCrumbs();
$crumbs->addTextCrumb($space->getMonogram());
return $this->buildApplicationPage(
array(
$crumbs,
$box,
$timeline,
),
array(
'title' => array($space->getMonogram(), $space->getNamespaceName()),
));
}
private function buildPropertyListView(PhabricatorSpacesNamespace $space) {
$viewer = $this->getRequest()->getUser();
$list = id(new PHUIPropertyListView())
->setUser($viewer);
$list->addProperty(
pht('Default Space'),
$space->getIsDefaultNamespace()
? pht('Yes')
: pht('No'));
$descriptions = PhabricatorPolicyQuery::renderPolicyDescriptions(
$viewer,
$space);
$list->addProperty(
pht('Editable By'),
$descriptions[PhabricatorPolicyCapability::CAN_EDIT]);
return $list;
}
private function buildActionListView(PhabricatorSpacesNamespace $space) {
$viewer = $this->getRequest()->getUser();
$list = id(new PhabricatorActionListView())
->setUser($viewer)
->setObjectURI('/'.$space->getMonogram());
$can_edit = PhabricatorPolicyFilter::hasCapability(
$viewer,
$space,
PhabricatorPolicyCapability::CAN_EDIT);
$list->addAction(
id(new PhabricatorActionView())
->setName(pht('Edit Space'))
->setIcon('fa-pencil')
->setHref($this->getApplicationURI('edit/'.$space->getID().'/'))
->setWorkflow(!$can_edit)
->setDisabled(!$can_edit));
return $list;
}
}

View file

@ -0,0 +1,132 @@
<?php
final class PhabricatorSpacesNamespaceEditor
extends PhabricatorApplicationTransactionEditor {
public function getEditorApplicationClass() {
return pht('PhabricatorSpacesApplication');
}
public function getEditorObjectsDescription() {
return pht('Spaces');
}
public function getTransactionTypes() {
$types = parent::getTransactionTypes();
$types[] = PhabricatorSpacesNamespaceTransaction::TYPE_NAME;
$types[] = PhabricatorSpacesNamespaceTransaction::TYPE_DEFAULT;
$types[] = PhabricatorTransactions::TYPE_VIEW_POLICY;
$types[] = PhabricatorTransactions::TYPE_EDIT_POLICY;
return $types;
}
protected function getCustomTransactionOldValue(
PhabricatorLiskDAO $object,
PhabricatorApplicationTransaction $xaction) {
switch ($xaction->getTransactionType()) {
case PhabricatorSpacesNamespaceTransaction::TYPE_NAME:
$name = $object->getNamespaceName();
if (!strlen($name)) {
return null;
}
return $name;
case PhabricatorSpacesNamespaceTransaction::TYPE_DEFAULT:
return $object->getIsDefaultNamespace() ? 1 : null;
case PhabricatorTransactions::TYPE_VIEW_POLICY:
return $object->getViewPolicy();
case PhabricatorTransactions::TYPE_EDIT_POLICY:
return $object->getEditPolicy();
}
return parent::getCustomTransactionOldValue($object, $xaction);
}
protected function getCustomTransactionNewValue(
PhabricatorLiskDAO $object,
PhabricatorApplicationTransaction $xaction) {
switch ($xaction->getTransactionType()) {
case PhabricatorSpacesNamespaceTransaction::TYPE_NAME:
case PhabricatorTransactions::TYPE_VIEW_POLICY:
case PhabricatorTransactions::TYPE_EDIT_POLICY:
return $xaction->getNewValue();
case PhabricatorSpacesNamespaceTransaction::TYPE_DEFAULT:
return $xaction->getNewValue() ? 1 : null;
}
return parent::getCustomTransactionNewValue($object, $xaction);
}
protected function applyCustomInternalTransaction(
PhabricatorLiskDAO $object,
PhabricatorApplicationTransaction $xaction) {
$new = $xaction->getNewValue();
switch ($xaction->getTransactionType()) {
case PhabricatorSpacesNamespaceTransaction::TYPE_NAME:
$object->setNamespaceName($new);
return;
case PhabricatorSpacesNamespaceTransaction::TYPE_DEFAULT:
$object->setIsDefaultNamespace($new ? 1 : null);
return;
case PhabricatorTransactions::TYPE_VIEW_POLICY:
$object->setViewPolicy($new);
return;
case PhabricatorTransactions::TYPE_EDIT_POLICY:
$object->setEditPolicy($new);
return;
}
return parent::applyCustomInternalTransaction($object, $xaction);
}
protected function applyCustomExternalTransaction(
PhabricatorLiskDAO $object,
PhabricatorApplicationTransaction $xaction) {
switch ($xaction->getTransactionType()) {
case PhabricatorSpacesNamespaceTransaction::TYPE_NAME:
case PhabricatorSpacesNamespaceTransaction::TYPE_DEFAULT:
case PhabricatorTransactions::TYPE_VIEW_POLICY:
case PhabricatorTransactions::TYPE_EDIT_POLICY:
return;
}
return parent::applyCustomExternalTransaction($object, $xaction);
}
protected function validateTransaction(
PhabricatorLiskDAO $object,
$type,
array $xactions) {
$errors = parent::validateTransaction($object, $type, $xactions);
switch ($type) {
case PhabricatorSpacesNamespaceTransaction::TYPE_NAME:
$missing = $this->validateIsEmptyTextField(
$object->getNamespaceName(),
$xactions);
if ($missing) {
$error = new PhabricatorApplicationTransactionValidationError(
$type,
pht('Required'),
pht('Spaces must have a name.'),
nonempty(last($xactions), null));
$error->setIsMissingFieldError(true);
$errors[] = $error;
}
break;
}
return $errors;
}
}

View file

@ -0,0 +1,3 @@
<?php
interface PhabricatorSpacesInterface extends PhabricatorPHIDInterface {}

View file

@ -0,0 +1,64 @@
<?php
final class PhabricatorSpacesNamespacePHIDType
extends PhabricatorPHIDType {
const TYPECONST = 'SPCE';
public function getTypeName() {
return pht('Space');
}
public function newObject() {
return new PhabricatorSpacesNamespace();
}
protected function buildQueryForObjects(
PhabricatorObjectQuery $query,
array $phids) {
return id(new PhabricatorSpacesNamespaceQuery())
->withPHIDs($phids);
}
public function loadHandles(
PhabricatorHandleQuery $query,
array $handles,
array $objects) {
foreach ($handles as $phid => $handle) {
$namespace = $objects[$phid];
$handle->setName($namespace->getNamespaceName());
}
}
public function canLoadNamedObject($name) {
return preg_match('/^S[1-9]\d*$/i', $name);
}
public function loadNamedObjects(
PhabricatorObjectQuery $query,
array $names) {
$id_map = array();
foreach ($names as $name) {
$id = (int)substr($name, 1);
$id_map[$id][] = $name;
}
$objects = id(new PhabricatorSpacesNamespaceQuery())
->setViewer($query->getViewer())
->withIDs(array_keys($id_map))
->execute();
$results = array();
foreach ($objects as $id => $object) {
foreach (idx($id_map, $id, array()) as $name) {
$results[$name] = $object;
}
}
return $results;
}
}

View file

@ -0,0 +1,77 @@
<?php
final class PhabricatorSpacesNamespaceQuery
extends PhabricatorCursorPagedPolicyAwareQuery {
private $ids;
private $phids;
private $isDefaultNamespace;
public function withIDs(array $ids) {
$this->ids = $ids;
return $this;
}
public function withPHIDs(array $phids) {
$this->phids = $phids;
return $this;
}
public function withIsDefaultNamespace($default) {
$this->isDefaultNamespace = $default;
return $this;
}
public function getQueryApplicationClass() {
return 'PhabricatorApplicationSpaces';
}
protected function loadPage() {
$table = new PhabricatorSpacesNamespace();
$conn_r = $table->establishConnection('r');
$rows = queryfx_all(
$conn_r,
'SELECT * FROM %T %Q %Q %Q',
$table->getTableName(),
$this->buildWhereClause($conn_r),
$this->buildOrderClause($conn_r),
$this->buildLimitClause($conn_r));
return $table->loadAllFromArray($rows);
}
protected function buildWhereClause(AphrontDatabaseConnection $conn_r) {
$where = array();
if ($this->ids !== null) {
$where[] = qsprintf(
$conn_r,
'id IN (%Ld)',
$this->ids);
}
if ($this->phids !== null) {
$where[] = qsprintf(
$conn_r,
'phid IN (%Ls)',
$this->phids);
}
if ($this->isDefaultNamespace !== null) {
if ($this->isDefaultNamespace) {
$where[] = qsprintf(
$conn_r,
'isDefaultNamespace = 1');
} else {
$where[] = qsprintf(
$conn_r,
'isDefaultNamespace IS NULL');
}
}
$where[] = $this->buildPagingClause($conn_r);
return $this->formatWhereClause($where);
}
}

View file

@ -0,0 +1,81 @@
<?php
final class PhabricatorSpacesNamespaceSearchEngine
extends PhabricatorApplicationSearchEngine {
public function getApplicationClassName() {
return 'PhabricatorSpacesApplication';
}
public function getResultTypeDescription() {
return pht('Spaces');
}
public function buildSavedQueryFromRequest(AphrontRequest $request) {
$saved = new PhabricatorSavedQuery();
return $saved;
}
public function buildQueryFromSavedQuery(PhabricatorSavedQuery $saved) {
$query = id(new PhabricatorSpacesNamespaceQuery());
return $query;
}
public function buildSearchForm(
AphrontFormView $form,
PhabricatorSavedQuery $saved_query) {}
protected function getURI($path) {
return '/spaces/'.$path;
}
public function getBuiltinQueryNames() {
$names = array(
'all' => pht('All Spaces'),
);
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 $spaces,
PhabricatorSavedQuery $query,
array $handles) {
assert_instances_of($spaces, 'PhabricatorSpacesNamespace');
$viewer = $this->requireViewer();
$list = new PHUIObjectItemListView();
$list->setUser($viewer);
foreach ($spaces as $space) {
$item = id(new PHUIObjectItemView())
->setObjectName($space->getMonogram())
->setHeader($space->getNamespaceName())
->setHref('/'.$space->getMonogram());
if ($space->getIsDefaultNamespace()) {
$item->addIcon('fa-certificate', pht('Default Space'));
}
$list->addItem($item);
}
return $list;
}
}

View file

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

View file

@ -0,0 +1,9 @@
<?php
abstract class PhabricatorSpacesDAO extends PhabricatorLiskDAO {
public function getApplicationName() {
return 'spaces';
}
}

View file

@ -0,0 +1,106 @@
<?php
final class PhabricatorSpacesNamespace
extends PhabricatorSpacesDAO
implements
PhabricatorPolicyInterface,
PhabricatorApplicationTransactionInterface {
protected $namespaceName;
protected $viewPolicy;
protected $editPolicy;
protected $isDefaultNamespace;
public static function initializeNewNamespace(PhabricatorUser $actor) {
$app = id(new PhabricatorApplicationQuery())
->setViewer($actor)
->withClasses(array('PhabricatorApplicationSpaces'))
->executeOne();
$view_policy = $app->getPolicy(
PhabricatorSpacesCapabilityDefaultView::CAPABILITY);
$edit_policy = $app->getPolicy(
PhabricatorSpacesCapabilityDefaultEdit::CAPABILITY);
return id(new PhabricatorSpacesNamespace())
->setIsDefaultNamespace(null)
->setViewPolicy($view_policy)
->setEditPolicy($edit_policy);
}
public function getConfiguration() {
return array(
self::CONFIG_AUX_PHID => true,
self::CONFIG_COLUMN_SCHEMA => array(
'namespaceName' => 'text255',
'isDefaultNamespace' => 'bool?',
),
self::CONFIG_KEY_SCHEMA => array(
'key_default' => array(
'columns' => array('isDefaultNamespace'),
'unique' => true,
),
),
) + parent::getConfiguration();
}
public function generatePHID() {
return PhabricatorPHID::generateNewPHID(
PhabricatorSpacesNamespacePHIDType::TYPECONST);
}
public function getMonogram() {
return 'S'.$this->getID();
}
/* -( PhabricatorPolicyInterface )----------------------------------------- */
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 $viewer) {
return false;
}
public function describeAutomaticCapability($capability) {
return null;
}
/* -( PhabricatorApplicationTransactionInterface )------------------------- */
public function getApplicationTransactionEditor() {
return new PhabricatorSpacesNamespaceEditor();
}
public function getApplicationTransactionObject() {
return $this;
}
public function getApplicationTransactionTemplate() {
return new PhabricatorSpacesNamespaceTransaction();
}
public function willRenderTimeline(
PhabricatorApplicationTransactionView $timeline,
AphrontRequest $request) {
return $timeline;
}
}

View file

@ -0,0 +1,49 @@
<?php
final class PhabricatorSpacesNamespaceTransaction
extends PhabricatorApplicationTransaction {
const TYPE_NAME = 'spaces:name';
const TYPE_DEFAULT = 'spaces:default';
public function getApplicationName() {
return 'spaces';
}
public function getApplicationTransactionType() {
return PhabricatorSpacesNamespacePHIDType::TYPECONST;
}
public function getApplicationTransactionCommentObject() {
return null;
}
public function getTitle() {
$old = $this->getOldValue();
$new = $this->getNewValue();
$author_phid = $this->getAuthorPHID();
switch ($this->getTransactionType()) {
case self::TYPE_NAME:
if ($old === null) {
return pht(
'%s created this space.',
$this->renderHandleLink($author_phid));
} else {
return pht(
'%s renamed this space from "%s" to "%s".',
$this->renderHandleLink($author_phid),
$old,
$new);
}
case self::TYPE_DEFAULT:
return pht(
'%s made this the default space.',
$this->renderHandleLink($author_phid));
}
return parent::getTitle();
}
}

View file

@ -105,6 +105,7 @@ final class PhabricatorBuiltinPatchList extends PhabricatorSQLPatchList {
'db.fund' => array(),
'db.almanac' => array(),
'db.multimeter' => array(),
'db.spaces' => array(),
'0000.legacy.sql' => array(
'legacy' => 0,
),