Page Menu
Home
Phabricator
Search
Configure Global Search
Log In
Files
F13175754
D9204.id21866.diff
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Award Token
Flag For Later
Size
37 KB
Referenced Files
None
Subscribers
None
D9204.id21866.diff
View Options
diff --git a/resources/sql/autopatches/20140519.spaces.1.namespace.sql b/resources/sql/autopatches/20140519.spaces.1.namespace.sql
new file mode 100644
--- /dev/null
+++ b/resources/sql/autopatches/20140519.spaces.1.namespace.sql
@@ -0,0 +1,14 @@
+CREATE TABLE {$NAMESPACE}_spaces.spaces_namespace (
+ id INT UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY,
+ phid VARCHAR(64) NOT NULL COLLATE utf8_bin,
+ namespaceName VARCHAR(255) NOT NULL COLLATE utf8_general_ci,
+ viewPolicy VARCHAR(64) NOT NULL COLLATE utf8_bin,
+ editPolicy VARCHAR(64) NOT NULL COLLATE utf8_bin,
+ 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 utf8_general_ci;
diff --git a/resources/sql/autopatches/20140519.spaces.2.xaction.sql b/resources/sql/autopatches/20140519.spaces.2.xaction.sql
new file mode 100644
--- /dev/null
+++ b/resources/sql/autopatches/20140519.spaces.2.xaction.sql
@@ -0,0 +1,21 @@
+CREATE TABLE {$NAMESPACE}_spaces.spaces_namespacetransaction (
+ id INT UNSIGNED NOT NULL PRIMARY KEY AUTO_INCREMENT,
+ phid VARCHAR(64) NOT NULL COLLATE utf8_bin,
+ authorPHID VARCHAR(64) NOT NULL COLLATE utf8_bin,
+ objectPHID VARCHAR(64) NOT NULL COLLATE utf8_bin,
+ viewPolicy VARCHAR(64) NOT NULL COLLATE utf8_bin,
+ editPolicy VARCHAR(64) NOT NULL COLLATE utf8_bin,
+ commentPHID VARCHAR(64) COLLATE utf8_bin,
+ commentVersion INT UNSIGNED NOT NULL,
+ transactionType VARCHAR(32) NOT NULL COLLATE utf8_bin,
+ oldValue LONGTEXT NOT NULL COLLATE utf8_bin,
+ newValue LONGTEXT NOT NULL COLLATE utf8_bin,
+ contentSource LONGTEXT NOT NULL COLLATE utf8_bin,
+ metadata LONGTEXT NOT NULL COLLATE utf8_bin,
+ dateCreated INT UNSIGNED NOT NULL,
+ dateModified INT UNSIGNED NOT NULL,
+
+ UNIQUE KEY `key_phid` (phid),
+ KEY `key_object` (objectPHID)
+
+) ENGINE=InnoDB, COLLATE utf8_general_ci;
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
@@ -1168,6 +1168,7 @@
'PhabricatorApplicationSearchResultsControllerInterface' => 'applications/search/interface/PhabricatorApplicationSearchResultsControllerInterface.php',
'PhabricatorApplicationSettings' => 'applications/settings/application/PhabricatorApplicationSettings.php',
'PhabricatorApplicationSlowvote' => 'applications/slowvote/application/PhabricatorApplicationSlowvote.php',
+ 'PhabricatorApplicationSpaces' => 'applications/spaces/application/PhabricatorApplicationSpaces.php',
'PhabricatorApplicationStatusView' => 'applications/meta/view/PhabricatorApplicationStatusView.php',
'PhabricatorApplicationSubscriptions' => 'applications/subscriptions/application/PhabricatorApplicationSubscriptions.php',
'PhabricatorApplicationSupport' => 'applications/support/application/PhabricatorApplicationSupport.php',
@@ -2183,6 +2184,22 @@
'PhabricatorSlugTestCase' => 'infrastructure/util/__tests__/PhabricatorSlugTestCase.php',
'PhabricatorSortTableExample' => 'applications/uiexample/examples/PhabricatorSortTableExample.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',
+ 'PhabricatorSpacesNamespaceQuery' => 'applications/spaces/query/PhabricatorSpacesNamespaceQuery.php',
+ 'PhabricatorSpacesNamespaceSearchEngine' => 'applications/spaces/query/PhabricatorSpacesNamespaceSearchEngine.php',
+ 'PhabricatorSpacesNamespaceTransaction' => 'applications/spaces/storage/PhabricatorSpacesNamespaceTransaction.php',
+ 'PhabricatorSpacesNamespaceTransactionQuery' => 'applications/spaces/query/PhabricatorSpacesNamespaceTransactionQuery.php',
+ 'PhabricatorSpacesPHIDTypeNamespace' => 'applications/spaces/phid/PhabricatorSpacesPHIDTypeNamespace.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',
@@ -3908,6 +3925,7 @@
'PhabricatorApplicationSearchController' => 'PhabricatorSearchBaseController',
'PhabricatorApplicationSettings' => 'PhabricatorApplication',
'PhabricatorApplicationSlowvote' => 'PhabricatorApplication',
+ 'PhabricatorApplicationSpaces' => 'PhabricatorApplication',
'PhabricatorApplicationStatusView' => 'AphrontView',
'PhabricatorApplicationSubscriptions' => 'PhabricatorApplication',
'PhabricatorApplicationSupport' => 'PhabricatorApplication',
@@ -5028,6 +5046,26 @@
'PhabricatorSlugTestCase' => 'PhabricatorTestCase',
'PhabricatorSortTableExample' => 'PhabricatorUIExample',
'PhabricatorSourceCodeView' => 'AphrontView',
+ 'PhabricatorSpacesCapabilityCreateSpaces' => 'PhabricatorPolicyCapability',
+ 'PhabricatorSpacesCapabilityDefaultEdit' => 'PhabricatorPolicyCapability',
+ 'PhabricatorSpacesCapabilityDefaultView' => 'PhabricatorPolicyCapability',
+ 'PhabricatorSpacesController' => 'PhabricatorController',
+ 'PhabricatorSpacesDAO' => 'PhabricatorLiskDAO',
+ 'PhabricatorSpacesEditController' => 'PhabricatorSpacesController',
+ 'PhabricatorSpacesInterface' => 'PhabricatorPHIDInterface',
+ 'PhabricatorSpacesListController' => 'PhabricatorSpacesController',
+ 'PhabricatorSpacesNamespace' =>
+ array(
+ 0 => 'PhabricatorSpacesDAO',
+ 1 => 'PhabricatorPolicyInterface',
+ ),
+ 'PhabricatorSpacesNamespaceEditor' => 'PhabricatorApplicationTransactionEditor',
+ 'PhabricatorSpacesNamespaceQuery' => 'PhabricatorCursorPagedPolicyAwareQuery',
+ 'PhabricatorSpacesNamespaceSearchEngine' => 'PhabricatorApplicationSearchEngine',
+ 'PhabricatorSpacesNamespaceTransaction' => 'PhabricatorApplicationTransaction',
+ 'PhabricatorSpacesNamespaceTransactionQuery' => 'PhabricatorApplicationTransactionQuery',
+ 'PhabricatorSpacesPHIDTypeNamespace' => 'PhabricatorPHIDType',
+ '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,63 @@
+<?php
+
+final class PhabricatorApplicationSpaces extends PhabricatorApplication {
+
+ public function getBaseURI() {
+ return '/spaces/';
+ }
+
+ public function getShortDescription() {
+ return pht('Policy Spaces');
+ }
+
+ public function getIconName() {
+ return 'spaces';
+ }
+
+ 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 false;
+ }
+
+ public function shouldAppearInLaunchView() {
+ // TODO: Surface this once it works.
+ return false;
+ }
+
+ 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,20 @@
+<?php
+
+final class PhabricatorSpacesCapabilityCreateSpaces
+ extends PhabricatorPolicyCapability {
+
+ const CAPABILITY = 'spaces.create';
+
+ public function getCapabilityKey() {
+ return self::CAPABILITY;
+ }
+
+ 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,16 @@
+<?php
+
+final class PhabricatorSpacesCapabilityDefaultEdit
+ extends PhabricatorPolicyCapability {
+
+ const CAPABILITY = 'spaces.default.edit';
+
+ public function getCapabilityKey() {
+ return self::CAPABILITY;
+ }
+
+ 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,20 @@
+<?php
+
+final class PhabricatorSpacesCapabilityDefaultView
+ extends PhabricatorPolicyCapability {
+
+ const CAPABILITY = 'spaces.default.view';
+
+ public function getCapabilityKey() {
+ return self::CAPABILITY;
+ }
+
+ 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,5 @@
+<?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,185 @@
+<?php
+
+final class PhabricatorSpacesEditController
+ extends PhabricatorSpacesController {
+
+ private $id;
+
+ public function willProcessRequest(array $data) {
+ $this->id = idx($data, 'id');
+ }
+
+ public function processRequest() {
+ $request = $this->getRequest();
+ $viewer = $request->getUser();
+
+ $make_default = false;
+
+ if ($this->id) {
+ $space = id(new PhabricatorSpacesNamespaceQuery())
+ ->setViewer($viewer)
+ ->withIDs(array($this->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();
+
+ 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;
+
+ $space->setEditPolicy($v_edit);
+ $space->setViewPolicy($v_view);
+ }
+ }
+
+ $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.'));
+ }
+
+ if ($validation_exception) {
+ $e_name = $validation_exception->getShortMessage(
+ PhabricatorSpacesNamespaceTransaction::TYPE_NAME);
+ }
+
+ $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)
+ ->setName('viewPolicy'))
+ ->appendChild(
+ id(new AphrontFormPolicyControl())
+ ->setUser($viewer)
+ ->setCapability(PhabricatorPolicyCapability::CAN_EDIT)
+ ->setPolicyObject($space)
+ ->setPolicies($policies)
+ ->setName('editPolicy'));
+
+ $form
+ ->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,
+ 'device' => true,
+ ));
+ }
+}
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,58 @@
+<?php
+
+final class PhabricatorSpacesListController
+ extends PhabricatorSpacesController {
+
+ private $queryKey;
+
+ public function shouldAllowPublic() {
+ return true;
+ }
+
+ public function willProcessRequest(array $data) {
+ $this->queryKey = idx($data, 'queryKey');
+ }
+
+ public function processRequest() {
+ $request = $this->getRequest();
+ $controller = id(new PhabricatorApplicationSearchController($request))
+ ->setQueryKey($this->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,113 @@
+<?php
+
+final class PhabricatorSpacesViewController
+ extends PhabricatorSpacesController {
+
+ private $id;
+
+ public function shouldAllowPublic() {
+ return true;
+ }
+
+ public function willProcessRequest(array $data) {
+ $this->id = $data['id'];
+ }
+
+ public function processRequest() {
+ $request = $this->getRequest();
+ $viewer = $request->getUser();
+
+ $space = id(new PhabricatorSpacesNamespaceQuery())
+ ->setViewer($viewer)
+ ->withIDs(array($this->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 = id(new PhabricatorApplicationTransactionView())
+ ->setUser($viewer)
+ ->setObjectPHID($space->getPHID())
+ ->setTransactions($xactions)
+ ->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()),
+ 'device' => true,
+ ));
+ }
+
+ 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,121 @@
+<?php
+
+final class PhabricatorSpacesNamespaceEditor
+ extends PhabricatorApplicationTransactionEditor {
+
+ 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 : 0;
+ 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 : 0;
+ }
+
+ 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:
+ foreach ($xactions as $xaction) {
+ if (!strlen($xaction->getNewValue())) {
+ $error = new PhabricatorApplicationTransactionValidationError(
+ $type,
+ pht('Required'),
+ pht('Spaces must have a name.'),
+ $xaction);
+ $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,5 @@
+<?php
+
+interface PhabricatorSpacesInterface extends PhabricatorPHIDInterface {
+
+}
diff --git a/src/applications/spaces/phid/PhabricatorSpacesPHIDTypeNamespace.php b/src/applications/spaces/phid/PhabricatorSpacesPHIDTypeNamespace.php
new file mode 100644
--- /dev/null
+++ b/src/applications/spaces/phid/PhabricatorSpacesPHIDTypeNamespace.php
@@ -0,0 +1,68 @@
+<?php
+
+final class PhabricatorSpacesPHIDTypeNamespace
+ extends PhabricatorPHIDType {
+
+ const TYPECONST = 'SPCE';
+
+ public function getTypeConstant() {
+ return self::TYPECONST;
+ }
+
+ 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\d*[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);
+ }
+
+ private 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,75 @@
+<?php
+
+final class PhabricatorSpacesNamespaceSearchEngine
+ extends PhabricatorApplicationSearchEngine {
+
+ 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,73 @@
+<?php
+
+final class PhabricatorSpacesNamespace
+ extends PhabricatorSpacesDAO
+ implements PhabricatorPolicyInterface {
+
+ 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())
+ ->setNamespaceName('')
+ ->setIsDefaultNamespace(null)
+ ->setViewPolicy($view_policy)
+ ->setEditPolicy($edit_policy);
+ }
+
+ public function getConfiguration() {
+ return array(
+ self::CONFIG_AUX_PHID => true,
+ ) + parent::getConfiguration();
+ }
+
+ public function generatePHID() {
+ return PhabricatorPHID::generateNewPHID(
+ PhabricatorSpacesPHIDTypeNamespace::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;
+ }
+
+}
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 PhabricatorSpacesPHIDTypeNamespace::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
@@ -119,6 +119,7 @@
'db.phragment' => array(),
'db.dashboard' => array(),
'db.system' => array(),
+ 'db.spaces' => array(),
'0000.legacy.sql' => array(
'legacy' => 0,
),
File Metadata
Details
Attached
Mime Type
text/plain
Expires
Thu, May 9, 7:13 AM (3 w, 5 d ago)
Storage Engine
blob
Storage Format
Encrypted (AES-256-CBC)
Storage Handle
6274355
Default Alt Text
D9204.id21866.diff (37 KB)
Attached To
Mode
D9204: Add "Spaces", an application for managing policy namespaces
Attached
Detach File
Event Timeline
Log In to Comment