mirror of
https://we.phorge.it/source/phorge.git
synced 2024-12-18 19:40: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:
parent
7a9174fa21
commit
541b4c86b4
21 changed files with 1045 additions and 0 deletions
12
resources/sql/autopatches/20150601.spaces.1.namespace.sql
Normal file
12
resources/sql/autopatches/20150601.spaces.1.namespace.sql
Normal 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};
|
19
resources/sql/autopatches/20150601.spaces.2.xaction.sql
Normal file
19
resources/sql/autopatches/20150601.spaces.2.xaction.sql
Normal 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};
|
|
@ -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',
|
||||
|
|
|
@ -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,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
}
|
|
@ -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.');
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,12 @@
|
|||
<?php
|
||||
|
||||
final class PhabricatorSpacesCapabilityDefaultEdit
|
||||
extends PhabricatorPolicyCapability {
|
||||
|
||||
const CAPABILITY = 'spaces.default.edit';
|
||||
|
||||
public function getCapabilityName() {
|
||||
return pht('Default Edit Policy');
|
||||
}
|
||||
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,3 @@
|
|||
<?php
|
||||
|
||||
abstract class PhabricatorSpacesController extends PhabricatorController {}
|
|
@ -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,
|
||||
));
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,3 @@
|
|||
<?php
|
||||
|
||||
interface PhabricatorSpacesInterface extends PhabricatorPHIDInterface {}
|
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,10 @@
|
|||
<?php
|
||||
|
||||
final class PhabricatorSpacesNamespaceTransactionQuery
|
||||
extends PhabricatorApplicationTransactionQuery {
|
||||
|
||||
public function getTemplateApplicationTransaction() {
|
||||
return new PhabricatorSpacesNamespaceTransaction();
|
||||
}
|
||||
|
||||
}
|
9
src/applications/spaces/storage/PhabricatorSpacesDAO.php
Normal file
9
src/applications/spaces/storage/PhabricatorSpacesDAO.php
Normal file
|
@ -0,0 +1,9 @@
|
|||
<?php
|
||||
|
||||
abstract class PhabricatorSpacesDAO extends PhabricatorLiskDAO {
|
||||
|
||||
public function getApplicationName() {
|
||||
return 'spaces';
|
||||
}
|
||||
|
||||
}
|
106
src/applications/spaces/storage/PhabricatorSpacesNamespace.php
Normal file
106
src/applications/spaces/storage/PhabricatorSpacesNamespace.php
Normal 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;
|
||||
}
|
||||
|
||||
}
|
|
@ -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();
|
||||
}
|
||||
|
||||
}
|
|
@ -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,
|
||||
),
|
||||
|
|
Loading…
Reference in a new issue