Page Menu
Home
Phabricator
Search
Configure Global Search
Log In
Files
F17698070
D10713.diff
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Award Token
Flag For Later
Size
35 KB
Referenced Files
None
Subscribers
None
D10713.diff
View Options
diff --git a/resources/sql/autopatches/20141016.almanac.service.sql b/resources/sql/autopatches/20141016.almanac.service.sql
new file mode 100644
--- /dev/null
+++ b/resources/sql/autopatches/20141016.almanac.service.sql
@@ -0,0 +1,14 @@
+CREATE TABLE {$NAMESPACE}_almanac.almanac_service (
+ id INT UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY,
+ phid VARBINARY(64) NOT NULL,
+ name VARCHAR(128) NOT NULL COLLATE utf8_bin,
+ nameIndex BINARY(12) NOT NULL,
+ mailKey BINARY(20) NOT NULL,
+ viewPolicy VARBINARY(64) NOT NULL,
+ editPolicy VARBINARY(64) NOT NULL,
+ dateCreated INT UNSIGNED NOT NULL,
+ dateModified INT UNSIGNED NOT NULL,
+ UNIQUE KEY `key_phid` (phid),
+ UNIQUE KEY `key_name` (nameIndex),
+ KEY `key_nametext` (name)
+) ENGINE=InnoDB, COLLATE utf8_general_ci;
diff --git a/resources/sql/autopatches/20141016.almanac.sxaction.sql b/resources/sql/autopatches/20141016.almanac.sxaction.sql
new file mode 100644
--- /dev/null
+++ b/resources/sql/autopatches/20141016.almanac.sxaction.sql
@@ -0,0 +1,19 @@
+CREATE TABLE {$NAMESPACE}_almanac.almanac_servicetransaction (
+ id INT UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY,
+ phid VARCHAR(64) COLLATE utf8_bin NOT NULL,
+ authorPHID VARCHAR(64) COLLATE utf8_bin NOT NULL,
+ objectPHID VARCHAR(64) COLLATE utf8_bin NOT NULL,
+ viewPolicy VARCHAR(64) COLLATE utf8_bin NOT NULL,
+ editPolicy VARCHAR(64) COLLATE utf8_bin NOT NULL,
+ commentPHID VARCHAR(64) COLLATE utf8_bin DEFAULT NULL,
+ commentVersion INT UNSIGNED NOT NULL,
+ transactionType VARCHAR(32) COLLATE utf8_bin NOT NULL,
+ oldValue LONGTEXT COLLATE utf8_bin NOT NULL,
+ newValue LONGTEXT COLLATE utf8_bin NOT NULL,
+ contentSource LONGTEXT COLLATE utf8_bin NOT NULL,
+ metadata LONGTEXT COLLATE utf8_bin NOT NULL,
+ 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
@@ -10,6 +10,9 @@
'__library_version__' => 2,
'class' => array(
'AlmanacConduitUtil' => 'applications/almanac/util/AlmanacConduitUtil.php',
+ 'AlmanacConsoleController' => 'applications/almanac/controller/AlmanacConsoleController.php',
+ 'AlmanacController' => 'applications/almanac/controller/AlmanacController.php',
+ 'AlmanacCreateServicesCapability' => 'applications/almanac/capability/AlmanacCreateServicesCapability.php',
'AlmanacDAO' => 'applications/almanac/storage/AlmanacDAO.php',
'AlmanacDevice' => 'applications/almanac/storage/AlmanacDevice.php',
'AlmanacDevicePHIDType' => 'applications/almanac/phid/AlmanacDevicePHIDType.php',
@@ -17,6 +20,18 @@
'AlmanacDeviceQuery' => 'applications/almanac/query/AlmanacDeviceQuery.php',
'AlmanacManagementRegisterWorkflow' => 'applications/almanac/management/AlmanacManagementRegisterWorkflow.php',
'AlmanacManagementWorkflow' => 'applications/almanac/management/AlmanacManagementWorkflow.php',
+ 'AlmanacService' => 'applications/almanac/storage/AlmanacService.php',
+ 'AlmanacServiceController' => 'applications/almanac/controller/AlmanacServiceController.php',
+ 'AlmanacServiceEditController' => 'applications/almanac/controller/AlmanacServiceEditController.php',
+ 'AlmanacServiceEditor' => 'applications/almanac/editor/AlmanacServiceEditor.php',
+ 'AlmanacServiceListController' => 'applications/almanac/controller/AlmanacServiceListController.php',
+ 'AlmanacServicePHIDType' => 'applications/almanac/phid/AlmanacServicePHIDType.php',
+ 'AlmanacServiceQuery' => 'applications/almanac/query/AlmanacServiceQuery.php',
+ 'AlmanacServiceSearchEngine' => 'applications/almanac/query/AlmanacServiceSearchEngine.php',
+ 'AlmanacServiceTestCase' => 'applications/almanac/storage/__tests__/AlmanacServiceTestCase.php',
+ 'AlmanacServiceTransaction' => 'applications/almanac/storage/AlmanacServiceTransaction.php',
+ 'AlmanacServiceTransactionQuery' => 'applications/almanac/query/AlmanacServiceTransactionQuery.php',
+ 'AlmanacServiceViewController' => 'applications/almanac/controller/AlmanacServiceViewController.php',
'Aphront304Response' => 'aphront/response/Aphront304Response.php',
'Aphront400Response' => 'aphront/response/Aphront400Response.php',
'Aphront403Response' => 'aphront/response/Aphront403Response.php',
@@ -2904,6 +2919,9 @@
),
'xmap' => array(
'AlmanacConduitUtil' => 'Phobject',
+ 'AlmanacConsoleController' => 'AlmanacController',
+ 'AlmanacController' => 'PhabricatorController',
+ 'AlmanacCreateServicesCapability' => 'PhabricatorPolicyCapability',
'AlmanacDAO' => 'PhabricatorLiskDAO',
'AlmanacDevice' => array(
'AlmanacDAO',
@@ -2914,6 +2932,21 @@
'AlmanacDeviceQuery' => 'PhabricatorCursorPagedPolicyAwareQuery',
'AlmanacManagementRegisterWorkflow' => 'AlmanacManagementWorkflow',
'AlmanacManagementWorkflow' => 'PhabricatorManagementWorkflow',
+ 'AlmanacService' => array(
+ 'AlmanacDAO',
+ 'PhabricatorPolicyInterface',
+ ),
+ 'AlmanacServiceController' => 'AlmanacController',
+ 'AlmanacServiceEditController' => 'AlmanacServiceController',
+ 'AlmanacServiceEditor' => 'PhabricatorApplicationTransactionEditor',
+ 'AlmanacServiceListController' => 'AlmanacServiceController',
+ 'AlmanacServicePHIDType' => 'PhabricatorPHIDType',
+ 'AlmanacServiceQuery' => 'PhabricatorCursorPagedPolicyAwareQuery',
+ 'AlmanacServiceSearchEngine' => 'PhabricatorApplicationSearchEngine',
+ 'AlmanacServiceTestCase' => 'PhabricatorTestCase',
+ 'AlmanacServiceTransaction' => 'PhabricatorApplicationTransaction',
+ 'AlmanacServiceTransactionQuery' => 'PhabricatorApplicationTransactionQuery',
+ 'AlmanacServiceViewController' => 'AlmanacServiceController',
'Aphront304Response' => 'AphrontResponse',
'Aphront400Response' => 'AphrontResponse',
'Aphront403Response' => 'AphrontHTMLResponse',
diff --git a/src/applications/almanac/application/PhabricatorAlmanacApplication.php b/src/applications/almanac/application/PhabricatorAlmanacApplication.php
--- a/src/applications/almanac/application/PhabricatorAlmanacApplication.php
+++ b/src/applications/almanac/application/PhabricatorAlmanacApplication.php
@@ -30,12 +30,25 @@
return true;
}
- public function isLaunchable() {
- return false;
- }
-
public function getRoutes() {
- return array();
+ return array(
+ '/almanac/' => array(
+ '' => 'AlmanacConsoleController',
+ 'service/' => array(
+ '(?:query/(?P<queryKey>[^/]+)/)?' => 'AlmanacServiceListController',
+ 'edit/(?:(?P<id>\d+)/)?' => 'AlmanacServiceEditController',
+ 'view/(?P<name>[^/]+)/' => 'AlmanacServiceViewController',
+ ),
+ ),
+ );
+ }
+
+ protected function getCustomCapabilities() {
+ return array(
+ AlmanacCreateServicesCapability::CAPABILITY => array(
+ 'default' => PhabricatorPolicies::POLICY_ADMIN,
+ ),
+ );
}
}
diff --git a/src/applications/almanac/capability/AlmanacCreateServicesCapability.php b/src/applications/almanac/capability/AlmanacCreateServicesCapability.php
new file mode 100644
--- /dev/null
+++ b/src/applications/almanac/capability/AlmanacCreateServicesCapability.php
@@ -0,0 +1,16 @@
+<?php
+
+final class AlmanacCreateServicesCapability
+ extends PhabricatorPolicyCapability {
+
+ const CAPABILITY = 'almanac.services';
+
+ public function getCapabilityName() {
+ return pht('Can Create Services');
+ }
+
+ public function describeCapabilityRejection() {
+ return pht('You do not have permission to create Almanac services.');
+ }
+
+}
diff --git a/src/applications/almanac/controller/AlmanacConsoleController.php b/src/applications/almanac/controller/AlmanacConsoleController.php
new file mode 100644
--- /dev/null
+++ b/src/applications/almanac/controller/AlmanacConsoleController.php
@@ -0,0 +1,36 @@
+<?php
+
+final class AlmanacConsoleController extends AlmanacController {
+
+ public function shouldAllowPublic() {
+ return true;
+ }
+
+ public function handleRequest(AphrontRequest $request) {
+ $viewer = $request->getViewer();
+
+ $menu = id(new PHUIObjectItemListView())
+ ->setUser($viewer);
+
+ $menu->addItem(
+ id(new PHUIObjectItemView())
+ ->setHeader(pht('Services'))
+ ->setHref($this->getApplicationURI('service/'))
+ ->addAttribute(
+ pht(
+ 'Manage Almanac services.')));
+
+ $crumbs = $this->buildApplicationCrumbs();
+ $crumbs->addTextCrumb(pht('Console'));
+
+ return $this->buildApplicationPage(
+ array(
+ $crumbs,
+ $menu,
+ ),
+ array(
+ 'title' => pht('Almanac Console'),
+ ));
+ }
+
+}
diff --git a/src/applications/almanac/controller/AlmanacController.php b/src/applications/almanac/controller/AlmanacController.php
new file mode 100644
--- /dev/null
+++ b/src/applications/almanac/controller/AlmanacController.php
@@ -0,0 +1,4 @@
+<?php
+
+abstract class AlmanacController
+ extends PhabricatorController {}
diff --git a/src/applications/almanac/controller/AlmanacServiceController.php b/src/applications/almanac/controller/AlmanacServiceController.php
new file mode 100644
--- /dev/null
+++ b/src/applications/almanac/controller/AlmanacServiceController.php
@@ -0,0 +1,14 @@
+<?php
+
+abstract class AlmanacServiceController extends AlmanacController {
+
+ public function buildApplicationCrumbs() {
+ $crumbs = parent::buildApplicationCrumbs();
+
+ $list_uri = $this->getApplicationURI('service/');
+ $crumbs->addTextCrumb(pht('Services'), $list_uri);
+
+ return $crumbs;
+ }
+
+}
diff --git a/src/applications/almanac/controller/AlmanacServiceEditController.php b/src/applications/almanac/controller/AlmanacServiceEditController.php
new file mode 100644
--- /dev/null
+++ b/src/applications/almanac/controller/AlmanacServiceEditController.php
@@ -0,0 +1,142 @@
+<?php
+
+final class AlmanacServiceEditController
+ extends AlmanacServiceController {
+
+ public function handleRequest(AphrontRequest $request) {
+ $viewer = $request->getViewer();
+
+ $list_uri = $this->getApplicationURI('service');
+
+ $id = $request->getURIData('id');
+ if ($id) {
+ $service = id(new AlmanacServiceQuery())
+ ->setViewer($viewer)
+ ->withIDs(array($id))
+ ->requireCapabilities(
+ array(
+ PhabricatorPolicyCapability::CAN_VIEW,
+ PhabricatorPolicyCapability::CAN_EDIT,
+ ))
+ ->executeOne();
+ if (!$service) {
+ return new Aphront404Response();
+ }
+
+ $is_new = false;
+ $service_uri = $service->getURI();
+ $cancel_uri = $service_uri;
+ $title = pht('Edit Service');
+ $save_button = pht('Save Changes');
+ } else {
+ $this->requireApplicationCapability(
+ AlmanacCreateServicesCapability::CAPABILITY);
+
+ $service = AlmanacService::initializeNewService();
+ $is_new = true;
+
+ $cancel_uri = $list_uri;
+ $title = pht('Create Service');
+ $save_button = pht('Create Service');
+ }
+
+ $v_name = $service->getName();
+ $e_name = true;
+ $validation_exception = null;
+
+ if ($request->isFormPost()) {
+ $v_name = $request->getStr('name');
+ $v_view = $request->getStr('viewPolicy');
+ $v_edit = $request->getStr('editPolicy');
+
+ $type_name = AlmanacServiceTransaction::TYPE_NAME;
+ $type_view = PhabricatorTransactions::TYPE_VIEW_POLICY;
+ $type_edit = PhabricatorTransactions::TYPE_EDIT_POLICY;
+
+ $xactions = array();
+
+ $xactions[] = id(new AlmanacServiceTransaction())
+ ->setTransactionType($type_name)
+ ->setNewValue($v_name);
+
+ $xactions[] = id(new AlmanacServiceTransaction())
+ ->setTransactionType($type_view)
+ ->setNewValue($v_view);
+
+ $xactions[] = id(new AlmanacServiceTransaction())
+ ->setTransactionType($type_edit)
+ ->setNewValue($v_edit);
+
+ $editor = id(new AlmanacServiceEditor())
+ ->setActor($viewer)
+ ->setContentSourceFromRequest($request)
+ ->setContinueOnNoEffect(true);
+
+ try {
+ $editor->applyTransactions($service, $xactions);
+
+ $service_uri = $service->getURI();
+ return id(new AphrontRedirectResponse())->setURI($service_uri);
+ } catch (PhabricatorApplicationTransactionValidationException $ex) {
+ $validation_exception = $ex;
+ $e_name = $ex->getShortMessage($type_name);
+
+ $service->setViewPolicy($v_view);
+ $service->setEditPolicy($v_edit);
+ }
+ }
+
+ $policies = id(new PhabricatorPolicyQuery())
+ ->setViewer($viewer)
+ ->setObject($service)
+ ->execute();
+
+ $form = id(new AphrontFormView())
+ ->setUser($viewer)
+ ->appendChild(
+ id(new AphrontFormTextControl())
+ ->setLabel(pht('Name'))
+ ->setName('name')
+ ->setValue($v_name)
+ ->setError($e_name))
+ ->appendChild(
+ id(new AphrontFormPolicyControl())
+ ->setName('viewPolicy')
+ ->setPolicyObject($service)
+ ->setCapability(PhabricatorPolicyCapability::CAN_VIEW)
+ ->setPolicies($policies))
+ ->appendChild(
+ id(new AphrontFormPolicyControl())
+ ->setName('editPolicy')
+ ->setPolicyObject($service)
+ ->setCapability(PhabricatorPolicyCapability::CAN_EDIT)
+ ->setPolicies($policies))
+ ->appendChild(
+ id(new AphrontFormSubmitControl())
+ ->addCancelButton($cancel_uri)
+ ->setValue($save_button));
+
+ $box = id(new PHUIObjectBoxView())
+ ->setValidationException($validation_exception)
+ ->setHeaderText($title)
+ ->appendChild($form);
+
+ $crumbs = $this->buildApplicationCrumbs();
+ if ($is_new) {
+ $crumbs->addTextCrumb(pht('Create Service'));
+ } else {
+ $crumbs->addTextCrumb($service->getName(), $service_uri);
+ $crumbs->addTextCrumb(pht('Edit'));
+ }
+
+ return $this->buildApplicationPage(
+ array(
+ $crumbs,
+ $box,
+ ),
+ array(
+ 'title' => $title,
+ ));
+ }
+
+}
diff --git a/src/applications/almanac/controller/AlmanacServiceListController.php b/src/applications/almanac/controller/AlmanacServiceListController.php
new file mode 100644
--- /dev/null
+++ b/src/applications/almanac/controller/AlmanacServiceListController.php
@@ -0,0 +1,52 @@
+<?php
+
+final class AlmanacServiceListController
+ extends AlmanacServiceController {
+
+ public function shouldAllowPublic() {
+ return true;
+ }
+
+ public function handleRequest(AphrontRequest $request) {
+ $controller = id(new PhabricatorApplicationSearchController())
+ ->setQueryKey($request->getURIData('queryKey'))
+ ->setSearchEngine(new AlmanacServiceSearchEngine())
+ ->setNavigation($this->buildSideNavView());
+
+ return $this->delegateToController($controller);
+ }
+
+ public function buildApplicationCrumbs() {
+ $crumbs = parent::buildApplicationCrumbs();
+
+ $can_create = $this->hasApplicationCapability(
+ AlmanacCreateServicesCapability::CAPABILITY);
+
+ $crumbs->addAction(
+ id(new PHUIListItemView())
+ ->setName(pht('Create Service'))
+ ->setHref($this->getApplicationURI('service/edit/'))
+ ->setIcon('fa-plus-square')
+ ->setDisabled(!$can_create)
+ ->setWorkflow(!$can_create));
+
+ return $crumbs;
+ }
+
+ public function buildSideNavView() {
+ $viewer = $this->getViewer();
+
+ $nav = new AphrontSideNavFilterView();
+ $nav->setBaseURI(new PhutilURI($this->getApplicationURI()));
+
+ id(new AlmanacServiceSearchEngine())
+ ->setViewer($viewer)
+ ->addNavigationItems($nav->getMenu());
+
+ $nav->selectFilter(null);
+
+ return $nav;
+ }
+
+
+}
diff --git a/src/applications/almanac/controller/AlmanacServiceViewController.php b/src/applications/almanac/controller/AlmanacServiceViewController.php
new file mode 100644
--- /dev/null
+++ b/src/applications/almanac/controller/AlmanacServiceViewController.php
@@ -0,0 +1,95 @@
+<?php
+
+final class AlmanacServiceViewController
+ extends AlmanacServiceController {
+
+ public function shouldAllowPublic() {
+ return true;
+ }
+
+ public function handleRequest(AphrontRequest $request) {
+ $viewer = $request->getViewer();
+
+ $name = $request->getURIData('name');
+
+ $service = id(new AlmanacServiceQuery())
+ ->setViewer($viewer)
+ ->withNames(array($name))
+ ->executeOne();
+ if (!$service) {
+ return new Aphront404Response();
+ }
+
+ $title = pht('Service %s', $service->getName());
+
+ $property_list = $this->buildPropertyList($service);
+ $action_list = $this->buildActionList($service);
+ $property_list->setActionList($action_list);
+
+ $header = id(new PHUIHeaderView())
+ ->setUser($viewer)
+ ->setHeader($service->getName())
+ ->setPolicyObject($service);
+
+ $box = id(new PHUIObjectBoxView())
+ ->setHeader($header)
+ ->addPropertyList($property_list);
+
+ $crumbs = $this->buildApplicationCrumbs();
+ $crumbs->addTextCrumb($service->getName());
+
+ $xactions = id(new AlmanacServiceTransactionQuery())
+ ->setViewer($viewer)
+ ->withObjectPHIDs(array($service->getPHID()))
+ ->execute();
+
+ $xaction_view = id(new PhabricatorApplicationTransactionView())
+ ->setUser($viewer)
+ ->setObjectPHID($service->getPHID())
+ ->setTransactions($xactions)
+ ->setShouldTerminate(true);
+
+ return $this->buildApplicationPage(
+ array(
+ $crumbs,
+ $box,
+ $xaction_view,
+ ),
+ array(
+ 'title' => $title,
+ ));
+ }
+
+ private function buildPropertyList(AlmanacService $service) {
+ $viewer = $this->getViewer();
+
+ $properties = id(new PHUIPropertyListView())
+ ->setUser($viewer);
+
+ return $properties;
+ }
+
+ private function buildActionList(AlmanacService $service) {
+ $viewer = $this->getViewer();
+ $id = $service->getID();
+
+ $can_edit = PhabricatorPolicyFilter::hasCapability(
+ $viewer,
+ $service,
+ PhabricatorPolicyCapability::CAN_EDIT);
+
+ $actions = id(new PhabricatorActionListView())
+ ->setUser($viewer);
+
+ $actions->addAction(
+ id(new PhabricatorActionView())
+ ->setIcon('fa-pencil')
+ ->setName(pht('Edit Service'))
+ ->setHref($this->getApplicationURI("service/edit/{$id}/"))
+ ->setWorkflow(!$can_edit)
+ ->setDisabled(!$can_edit));
+
+ return $actions;
+ }
+
+}
diff --git a/src/applications/almanac/editor/AlmanacServiceEditor.php b/src/applications/almanac/editor/AlmanacServiceEditor.php
new file mode 100644
--- /dev/null
+++ b/src/applications/almanac/editor/AlmanacServiceEditor.php
@@ -0,0 +1,143 @@
+<?php
+
+final class AlmanacServiceEditor
+ extends PhabricatorApplicationTransactionEditor {
+
+ public function getEditorApplicationClass() {
+ return 'PhabricatorAlmanacApplication';
+ }
+
+ public function getEditorObjectsDescription() {
+ return pht('Almanac Service');
+ }
+
+ public function getTransactionTypes() {
+ $types = parent::getTransactionTypes();
+
+ $types[] = AlmanacServiceTransaction::TYPE_NAME;
+ $types[] = PhabricatorTransactions::TYPE_VIEW_POLICY;
+ $types[] = PhabricatorTransactions::TYPE_EDIT_POLICY;
+
+ return $types;
+ }
+
+ protected function getCustomTransactionOldValue(
+ PhabricatorLiskDAO $object,
+ PhabricatorApplicationTransaction $xaction) {
+ switch ($xaction->getTransactionType()) {
+ case AlmanacServiceTransaction::TYPE_NAME:
+ return $object->getName();
+ }
+
+ return parent::getCustomTransactionOldValue($object, $xaction);
+ }
+
+ protected function getCustomTransactionNewValue(
+ PhabricatorLiskDAO $object,
+ PhabricatorApplicationTransaction $xaction) {
+
+ switch ($xaction->getTransactionType()) {
+ case AlmanacServiceTransaction::TYPE_NAME:
+ return $xaction->getNewValue();
+ }
+
+ return parent::getCustomTransactionNewValue($object, $xaction);
+ }
+
+ protected function applyCustomInternalTransaction(
+ PhabricatorLiskDAO $object,
+ PhabricatorApplicationTransaction $xaction) {
+
+ switch ($xaction->getTransactionType()) {
+ case AlmanacServiceTransaction::TYPE_NAME:
+ $object->setName($xaction->getNewValue());
+ return;
+ case PhabricatorTransactions::TYPE_VIEW_POLICY:
+ case PhabricatorTransactions::TYPE_EDIT_POLICY:
+ return;
+ }
+
+ return parent::applyCustomInternalTransaction($object, $xaction);
+ }
+
+ protected function applyCustomExternalTransaction(
+ PhabricatorLiskDAO $object,
+ PhabricatorApplicationTransaction $xaction) {
+
+ switch ($xaction->getTransactionType()) {
+ case AlmanacServiceTransaction::TYPE_NAME:
+ 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 AlmanacServiceTransaction::TYPE_NAME:
+ $missing = $this->validateIsEmptyTextField(
+ $object->getName(),
+ $xactions);
+
+ if ($missing) {
+ $error = new PhabricatorApplicationTransactionValidationError(
+ $type,
+ pht('Required'),
+ pht('Service name is required.'),
+ nonempty(last($xactions), null));
+
+ $error->setIsMissingFieldError(true);
+ $errors[] = $error;
+ } else {
+ foreach ($xactions as $xaction) {
+ $message = null;
+
+ try {
+ AlmanacService::validateServiceName($xaction->getNewValue());
+ } catch (Exception $ex) {
+ $message = $ex->getMessage();
+ }
+
+ if ($message !== null) {
+ $error = new PhabricatorApplicationTransactionValidationError(
+ $type,
+ pht('Invalid'),
+ $message,
+ $xaction);
+ $errors[] = $error;
+ }
+ }
+ }
+
+ if ($xactions) {
+ $duplicate = id(new AlmanacServiceQuery())
+ ->setViewer(PhabricatorUser::getOmnipotentUser())
+ ->withNames(array(last($xactions)->getNewValue()))
+ ->executeOne();
+ if ($duplicate && ($duplicate->getID() != $object->getID())) {
+ $error = new PhabricatorApplicationTransactionValidationError(
+ $type,
+ pht('Not Unique'),
+ pht('Almanac services must have unique names.'),
+ last($xactions));
+ $errors[] = $error;
+ }
+ }
+
+ break;
+ }
+
+ return $errors;
+ }
+
+
+
+}
diff --git a/src/applications/almanac/phid/AlmanacServicePHIDType.php b/src/applications/almanac/phid/AlmanacServicePHIDType.php
new file mode 100644
--- /dev/null
+++ b/src/applications/almanac/phid/AlmanacServicePHIDType.php
@@ -0,0 +1,39 @@
+<?php
+
+final class AlmanacServicePHIDType extends PhabricatorPHIDType {
+
+ const TYPECONST = 'ASRV';
+
+ public function getTypeName() {
+ return pht('Almanac Service');
+ }
+
+ public function newObject() {
+ return new AlmanacService();
+ }
+
+ protected function buildQueryForObjects(
+ PhabricatorObjectQuery $query,
+ array $phids) {
+
+ return id(new AlmanacServiceQuery())
+ ->withPHIDs($phids);
+ }
+
+ public function loadHandles(
+ PhabricatorHandleQuery $query,
+ array $handles,
+ array $objects) {
+
+ foreach ($handles as $phid => $handle) {
+ $service = $objects[$phid];
+
+ $id = $service->getID();
+ $name = $service->getName();
+
+ $handle->setObjectName(pht('Service %d', $id));
+ $handle->setName($name);
+ }
+ }
+
+}
diff --git a/src/applications/almanac/query/AlmanacServiceQuery.php b/src/applications/almanac/query/AlmanacServiceQuery.php
new file mode 100644
--- /dev/null
+++ b/src/applications/almanac/query/AlmanacServiceQuery.php
@@ -0,0 +1,78 @@
+<?php
+
+final class AlmanacServiceQuery
+ extends PhabricatorCursorPagedPolicyAwareQuery {
+
+ private $ids;
+ private $phids;
+ private $names;
+
+ public function withIDs(array $ids) {
+ $this->ids = $ids;
+ return $this;
+ }
+
+ public function withPHIDs(array $phids) {
+ $this->phids = $phids;
+ return $this;
+ }
+
+ public function withNames(array $names) {
+ $this->names = $names;
+ return $this;
+ }
+
+ protected function loadPage() {
+ $table = new AlmanacService();
+ $conn_r = $table->establishConnection('r');
+
+ $data = 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($data);
+ }
+
+ protected function buildWhereClause($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->names !== null) {
+ $hashes = array();
+ foreach ($this->names as $name) {
+ $hashes[] = PhabricatorHash::digestForIndex($name);
+ }
+
+ $where[] = qsprintf(
+ $conn_r,
+ 'nameIndex IN (%Ls)',
+ $hashes);
+ }
+
+ $where[] = $this->buildPagingClause($conn_r);
+
+ return $this->formatWhereClause($where);
+ }
+
+ public function getQueryApplicationClass() {
+ return 'PhabricatorAlmanacApplication';
+ }
+
+}
diff --git a/src/applications/almanac/query/AlmanacServiceSearchEngine.php b/src/applications/almanac/query/AlmanacServiceSearchEngine.php
new file mode 100644
--- /dev/null
+++ b/src/applications/almanac/query/AlmanacServiceSearchEngine.php
@@ -0,0 +1,79 @@
+<?php
+
+final class AlmanacServiceSearchEngine
+ extends PhabricatorApplicationSearchEngine {
+
+ public function getResultTypeDescription() {
+ return pht('Almanac Services');
+ }
+
+ public function buildSavedQueryFromRequest(AphrontRequest $request) {
+ $saved = new PhabricatorSavedQuery();
+
+ return $saved;
+ }
+
+ public function buildQueryFromSavedQuery(PhabricatorSavedQuery $saved) {
+ $query = id(new AlmanacServiceQuery());
+
+ return $query;
+ }
+
+ public function buildSearchForm(
+ AphrontFormView $form,
+ PhabricatorSavedQuery $saved_query) {}
+
+ protected function getURI($path) {
+ return '/almanac/service/'.$path;
+ }
+
+ public function getBuiltinQueryNames() {
+ $names = array(
+ 'all' => pht('All Services'),
+ );
+
+ 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 getRequiredHandlePHIDsForResultList(
+ array $services,
+ PhabricatorSavedQuery $query) {
+ return array();
+ }
+
+ protected function renderResultList(
+ array $services,
+ PhabricatorSavedQuery $query,
+ array $handles) {
+ assert_instances_of($services, 'AlmanacService');
+
+ $viewer = $this->requireViewer();
+
+ $list = new PHUIObjectItemListView();
+ $list->setUser($viewer);
+ foreach ($services as $service) {
+ $item = id(new PHUIObjectItemView())
+ ->setObjectName(pht('Service %d', $service->getID()))
+ ->setHeader($service->getName())
+ ->setHref($service->getURI())
+ ->setObject($service);
+
+ $list->addItem($item);
+ }
+
+ return $list;
+ }
+}
diff --git a/src/applications/almanac/query/AlmanacServiceTransactionQuery.php b/src/applications/almanac/query/AlmanacServiceTransactionQuery.php
new file mode 100644
--- /dev/null
+++ b/src/applications/almanac/query/AlmanacServiceTransactionQuery.php
@@ -0,0 +1,10 @@
+<?php
+
+final class AlmanacServiceTransactionQuery
+ extends PhabricatorApplicationTransactionQuery {
+
+ public function getTemplateApplicationTransaction() {
+ return new AlmanacServiceTransaction();
+ }
+
+}
diff --git a/src/applications/almanac/storage/AlmanacService.php b/src/applications/almanac/storage/AlmanacService.php
new file mode 100644
--- /dev/null
+++ b/src/applications/almanac/storage/AlmanacService.php
@@ -0,0 +1,132 @@
+<?php
+
+final class AlmanacService
+ extends AlmanacDAO
+ implements PhabricatorPolicyInterface {
+
+ protected $name;
+ protected $nameIndex;
+ protected $mailKey;
+ protected $viewPolicy;
+ protected $editPolicy;
+
+ public static function initializeNewService() {
+ return id(new AlmanacService())
+ ->setViewPolicy(PhabricatorPolicies::POLICY_USER)
+ ->setEditPolicy(PhabricatorPolicies::POLICY_ADMIN);
+ }
+
+ public function getConfiguration() {
+ return array(
+ self::CONFIG_AUX_PHID => true,
+ self::CONFIG_COLUMN_SCHEMA => array(
+ 'name' => 'text128',
+ 'nameIndex' => 'bytes12',
+ 'mailKey' => 'bytes20',
+ ),
+ self::CONFIG_KEY_SCHEMA => array(
+ 'key_name' => array(
+ 'columns' => array('nameIndex'),
+ 'unique' => true,
+ ),
+ 'key_nametext' => array(
+ 'columns' => array('name'),
+ ),
+ ),
+ ) + parent::getConfiguration();
+ }
+
+ public function generatePHID() {
+ return PhabricatorPHID::generateNewPHID(AlmanacServicePHIDType::TYPECONST);
+ }
+
+ public function save() {
+ self::validateServiceName($this->getName());
+ $this->nameIndex = PhabricatorHash::digestForIndex($this->getName());
+
+ if (!$this->mailKey) {
+ $this->mailKey = Filesystem::readRandomCharacters(20);
+ }
+
+ return parent::save();
+ }
+
+ public function getURI() {
+ return '/almanac/service/view/'.$this->getName().'/';
+ }
+
+ public static function validateServiceName($name) {
+ if (strlen($name) < 3) {
+ throw new Exception(
+ pht('Almanac service names must be at least 3 characters long.'));
+ }
+
+ if (!preg_match('/^[a-z0-9.-]+\z/', $name)) {
+ throw new Exception(
+ pht(
+ 'Almanac service names may only contain lowercase letters, numbers, '.
+ 'hyphens, and periods.'));
+ }
+
+ if (preg_match('/(^|\\.)\d+(\z|\\.)/', $name)) {
+ throw new Exception(
+ pht(
+ 'Almanac service names may not have any segments containing only '.
+ 'digits.'));
+ }
+
+ if (preg_match('/\.\./', $name)) {
+ throw new Exception(
+ pht(
+ 'Almanac service names may not contain multiple consecutive '.
+ 'periods.'));
+ }
+
+ if (preg_match('/\\.-|-\\./', $name)) {
+ throw new Exception(
+ pht(
+ 'Amanac service names may not contain hyphens adjacent to periods.'));
+ }
+
+ if (preg_match('/--/', $name)) {
+ throw new Exception(
+ pht(
+ 'Almanac service names may not contain multiple consecutive '.
+ 'hyphens.'));
+ }
+
+ if (!preg_match('/^[a-z0-9].*[a-z0-9]\z/', $name)) {
+ throw new Exception(
+ pht(
+ 'Almanac service names must begin and end with a letter or number.'));
+ }
+ }
+
+/* -( 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/almanac/storage/AlmanacServiceTransaction.php b/src/applications/almanac/storage/AlmanacServiceTransaction.php
new file mode 100644
--- /dev/null
+++ b/src/applications/almanac/storage/AlmanacServiceTransaction.php
@@ -0,0 +1,45 @@
+<?php
+
+final class AlmanacServiceTransaction
+ extends PhabricatorApplicationTransaction {
+
+ const TYPE_NAME = 'almanac:service:name';
+
+ public function getApplicationName() {
+ return 'almanac';
+ }
+
+ public function getApplicationTransactionType() {
+ return AlmanacServicePHIDType::TYPECONST;
+ }
+
+ public function getApplicationTransactionCommentObject() {
+ return null;
+ }
+
+ public function getTitle() {
+ $author_phid = $this->getAuthorPHID();
+
+ $old = $this->getOldValue();
+ $new = $this->getNewValue();
+
+ switch ($this->getTransactionType()) {
+ case self::TYPE_NAME:
+ if ($old === null) {
+ return pht(
+ '%s created this service.',
+ $this->renderHandleLink($author_phid));
+ } else {
+ return pht(
+ '%s renamed this service from "%s" to "%s".',
+ $this->renderHandleLink($author_phid),
+ $old,
+ $new);
+ }
+ break;
+ }
+
+ return parent::getTitle();
+ }
+
+}
diff --git a/src/applications/almanac/storage/__tests__/AlmanacServiceTestCase.php b/src/applications/almanac/storage/__tests__/AlmanacServiceTestCase.php
new file mode 100644
--- /dev/null
+++ b/src/applications/almanac/storage/__tests__/AlmanacServiceTestCase.php
@@ -0,0 +1,51 @@
+<?php
+
+final class AlmanacServiceTestCase extends PhabricatorTestCase {
+
+ public function testServiceNames() {
+ $map = array(
+ '' => false,
+ 'a' => false,
+ 'ab' => false,
+ '...' => false,
+ 'ab.' => false,
+ '.ab' => false,
+ 'A-B' => false,
+ 'A!B' => false,
+ 'A.B' => false,
+ 'a..b' => false,
+ '1.2' => false,
+ '127.0.0.1' => false,
+ '1.b' => false,
+ 'a.1' => false,
+ 'a.1.b' => false,
+ '-.a' => false,
+ '-a.b' => false,
+ 'a-.b' => false,
+ 'a.-' => false,
+ 'a.-b' => false,
+ 'a.b-' => false,
+ '-.-' => false,
+ 'a--b' => false,
+
+ 'abc' => true,
+ 'a.b' => true,
+ 'db.phacility.instance' => true,
+ 'web002.useast.example.com' => true,
+ 'master.example-corp.com' => true,
+ );
+
+ foreach ($map as $input => $expect) {
+ $caught = null;
+ try {
+ AlmanacService::validateServiceName($input);
+ } catch (Exception $ex) {
+ $caught = $ex;
+ }
+ $this->assertEqual(
+ $expect,
+ !($caught instanceof Exception),
+ $input);
+ }
+ }
+}
diff --git a/src/applications/phortune/controller/PhortuneAccountViewController.php b/src/applications/phortune/controller/PhortuneAccountViewController.php
--- a/src/applications/phortune/controller/PhortuneAccountViewController.php
+++ b/src/applications/phortune/controller/PhortuneAccountViewController.php
@@ -287,5 +287,4 @@
return $crumbs;
}
-
}
File Metadata
Details
Attached
Mime Type
text/plain
Expires
Wed, Jul 16, 6:55 PM (18 h, 7 m)
Storage Engine
amazon-s3
Storage Format
Encrypted (AES-256-CBC)
Storage Handle
phabricator/secure/zt/fq/ow2rxdo6zwda7ra7
Default Alt Text
D10713.diff (35 KB)
Attached To
Mode
D10713: Build AlmanacService
Attached
Detach File
Event Timeline
Log In to Comment