Page Menu
Home
Phabricator
Search
Configure Global Search
Log In
Files
F14034567
D9204.diff
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Award Token
Flag For Later
Size
38 KB
Referenced Files
None
Subscribers
None
D9204.diff
View Options
diff --git a/resources/sql/autopatches/20150601.spaces.1.namespace.sql b/resources/sql/autopatches/20150601.spaces.1.namespace.sql
new file mode 100644
--- /dev/null
+++ b/resources/sql/autopatches/20150601.spaces.1.namespace.sql
@@ -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};
diff --git a/resources/sql/autopatches/20150601.spaces.2.xaction.sql b/resources/sql/autopatches/20150601.spaces.2.xaction.sql
new file mode 100644
--- /dev/null
+++ b/resources/sql/autopatches/20150601.spaces.2.xaction.sql
@@ -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};
diff --git a/src/__phutil_library_map__.php b/src/__phutil_library_map__.php
--- a/src/__phutil_library_map__.php
+++ b/src/__phutil_library_map__.php
@@ -1318,6 +1318,7 @@
'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 @@
'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 @@
'PhabricatorApplicationPanelController' => 'PhabricatorApplicationsController',
'PhabricatorApplicationQuery' => 'PhabricatorCursorPagedPolicyAwareQuery',
'PhabricatorApplicationSearchController' => 'PhabricatorSearchBaseController',
+ 'PhabricatorApplicationSpaces' => 'PhabricatorApplication',
'PhabricatorApplicationStatusView' => 'AphrontView',
'PhabricatorApplicationTransaction' => array(
'PhabricatorLiskDAO',
@@ -6027,6 +6045,26 @@
'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',
diff --git a/src/applications/spaces/application/PhabricatorApplicationSpaces.php b/src/applications/spaces/application/PhabricatorApplicationSpaces.php
new file mode 100644
--- /dev/null
+++ b/src/applications/spaces/application/PhabricatorApplicationSpaces.php
@@ -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,
+ ),
+ );
+ }
+
+}
diff --git a/src/applications/spaces/capability/PhabricatorSpacesCapabilityCreateSpaces.php b/src/applications/spaces/capability/PhabricatorSpacesCapabilityCreateSpaces.php
new file mode 100644
--- /dev/null
+++ b/src/applications/spaces/capability/PhabricatorSpacesCapabilityCreateSpaces.php
@@ -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.');
+ }
+
+}
diff --git a/src/applications/spaces/capability/PhabricatorSpacesCapabilityDefaultEdit.php b/src/applications/spaces/capability/PhabricatorSpacesCapabilityDefaultEdit.php
new file mode 100644
--- /dev/null
+++ b/src/applications/spaces/capability/PhabricatorSpacesCapabilityDefaultEdit.php
@@ -0,0 +1,12 @@
+<?php
+
+final class PhabricatorSpacesCapabilityDefaultEdit
+ extends PhabricatorPolicyCapability {
+
+ const CAPABILITY = 'spaces.default.edit';
+
+ public function getCapabilityName() {
+ return pht('Default Edit Policy');
+ }
+
+}
diff --git a/src/applications/spaces/capability/PhabricatorSpacesCapabilityDefaultView.php b/src/applications/spaces/capability/PhabricatorSpacesCapabilityDefaultView.php
new file mode 100644
--- /dev/null
+++ b/src/applications/spaces/capability/PhabricatorSpacesCapabilityDefaultView.php
@@ -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;
+ }
+
+}
diff --git a/src/applications/spaces/controller/PhabricatorSpacesController.php b/src/applications/spaces/controller/PhabricatorSpacesController.php
new file mode 100644
--- /dev/null
+++ b/src/applications/spaces/controller/PhabricatorSpacesController.php
@@ -0,0 +1,3 @@
+<?php
+
+abstract class PhabricatorSpacesController extends PhabricatorController {}
diff --git a/src/applications/spaces/controller/PhabricatorSpacesEditController.php b/src/applications/spaces/controller/PhabricatorSpacesEditController.php
new file mode 100644
--- /dev/null
+++ b/src/applications/spaces/controller/PhabricatorSpacesEditController.php
@@ -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,
+ ));
+ }
+}
diff --git a/src/applications/spaces/controller/PhabricatorSpacesListController.php b/src/applications/spaces/controller/PhabricatorSpacesListController.php
new file mode 100644
--- /dev/null
+++ b/src/applications/spaces/controller/PhabricatorSpacesListController.php
@@ -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;
+ }
+
+}
diff --git a/src/applications/spaces/controller/PhabricatorSpacesViewController.php b/src/applications/spaces/controller/PhabricatorSpacesViewController.php
new file mode 100644
--- /dev/null
+++ b/src/applications/spaces/controller/PhabricatorSpacesViewController.php
@@ -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;
+ }
+
+}
diff --git a/src/applications/spaces/editor/PhabricatorSpacesNamespaceEditor.php b/src/applications/spaces/editor/PhabricatorSpacesNamespaceEditor.php
new file mode 100644
--- /dev/null
+++ b/src/applications/spaces/editor/PhabricatorSpacesNamespaceEditor.php
@@ -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;
+ }
+
+}
diff --git a/src/applications/spaces/interface/PhabricatorSpacesInterface.php b/src/applications/spaces/interface/PhabricatorSpacesInterface.php
new file mode 100644
--- /dev/null
+++ b/src/applications/spaces/interface/PhabricatorSpacesInterface.php
@@ -0,0 +1,3 @@
+<?php
+
+interface PhabricatorSpacesInterface extends PhabricatorPHIDInterface {}
diff --git a/src/applications/spaces/phid/PhabricatorSpacesNamespacePHIDType.php b/src/applications/spaces/phid/PhabricatorSpacesNamespacePHIDType.php
new file mode 100644
--- /dev/null
+++ b/src/applications/spaces/phid/PhabricatorSpacesNamespacePHIDType.php
@@ -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;
+ }
+
+}
diff --git a/src/applications/spaces/query/PhabricatorSpacesNamespaceQuery.php b/src/applications/spaces/query/PhabricatorSpacesNamespaceQuery.php
new file mode 100644
--- /dev/null
+++ b/src/applications/spaces/query/PhabricatorSpacesNamespaceQuery.php
@@ -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);
+ }
+
+}
diff --git a/src/applications/spaces/query/PhabricatorSpacesNamespaceSearchEngine.php b/src/applications/spaces/query/PhabricatorSpacesNamespaceSearchEngine.php
new file mode 100644
--- /dev/null
+++ b/src/applications/spaces/query/PhabricatorSpacesNamespaceSearchEngine.php
@@ -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;
+ }
+
+}
diff --git a/src/applications/spaces/query/PhabricatorSpacesNamespaceTransactionQuery.php b/src/applications/spaces/query/PhabricatorSpacesNamespaceTransactionQuery.php
new file mode 100644
--- /dev/null
+++ b/src/applications/spaces/query/PhabricatorSpacesNamespaceTransactionQuery.php
@@ -0,0 +1,10 @@
+<?php
+
+final class PhabricatorSpacesNamespaceTransactionQuery
+ extends PhabricatorApplicationTransactionQuery {
+
+ public function getTemplateApplicationTransaction() {
+ return new PhabricatorSpacesNamespaceTransaction();
+ }
+
+}
diff --git a/src/applications/spaces/storage/PhabricatorSpacesDAO.php b/src/applications/spaces/storage/PhabricatorSpacesDAO.php
new file mode 100644
--- /dev/null
+++ b/src/applications/spaces/storage/PhabricatorSpacesDAO.php
@@ -0,0 +1,9 @@
+<?php
+
+abstract class PhabricatorSpacesDAO extends PhabricatorLiskDAO {
+
+ public function getApplicationName() {
+ return 'spaces';
+ }
+
+}
diff --git a/src/applications/spaces/storage/PhabricatorSpacesNamespace.php b/src/applications/spaces/storage/PhabricatorSpacesNamespace.php
new file mode 100644
--- /dev/null
+++ b/src/applications/spaces/storage/PhabricatorSpacesNamespace.php
@@ -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;
+ }
+
+}
diff --git a/src/applications/spaces/storage/PhabricatorSpacesNamespaceTransaction.php b/src/applications/spaces/storage/PhabricatorSpacesNamespaceTransaction.php
new file mode 100644
--- /dev/null
+++ b/src/applications/spaces/storage/PhabricatorSpacesNamespaceTransaction.php
@@ -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();
+ }
+
+}
diff --git a/src/infrastructure/storage/patch/PhabricatorBuiltinPatchList.php b/src/infrastructure/storage/patch/PhabricatorBuiltinPatchList.php
--- a/src/infrastructure/storage/patch/PhabricatorBuiltinPatchList.php
+++ b/src/infrastructure/storage/patch/PhabricatorBuiltinPatchList.php
@@ -105,6 +105,7 @@
'db.fund' => array(),
'db.almanac' => array(),
'db.multimeter' => array(),
+ 'db.spaces' => array(),
'0000.legacy.sql' => array(
'legacy' => 0,
),
File Metadata
Details
Attached
Mime Type
text/plain
Expires
Mon, Nov 11, 1:03 AM (1 w, 16 h ago)
Storage Engine
blob
Storage Format
Encrypted (AES-256-CBC)
Storage Handle
6711277
Default Alt Text
D9204.diff (38 KB)
Attached To
Mode
D9204: Add "Spaces", an application for managing policy namespaces
Attached
Detach File
Event Timeline
Log In to Comment