@recaptime-dev's working patches + fork for Phorge, a community fork of Phabricator. (Upstream dev and stable branches are at upstream/main and upstream/stable respectively.) hq.recaptime.dev/wiki/Phorge
phorge phabricator
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

Build AlmanacService

Summary: Ref T5833. See that task for functional goals and some discussion of design.

Test Plan: See screenshots.

Reviewers: btrahan

Reviewed By: btrahan

Subscribers: epriestley

Maniphest Tasks: T5833

Differential Revision: https://secure.phabricator.com/D10713

+1019 -5
+14
resources/sql/autopatches/20141016.almanac.service.sql
··· 1 + CREATE TABLE {$NAMESPACE}_almanac.almanac_service ( 2 + id INT UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY, 3 + phid VARBINARY(64) NOT NULL, 4 + name VARCHAR(128) NOT NULL COLLATE utf8_bin, 5 + nameIndex BINARY(12) NOT NULL, 6 + mailKey BINARY(20) NOT NULL, 7 + viewPolicy VARBINARY(64) NOT NULL, 8 + editPolicy VARBINARY(64) NOT NULL, 9 + dateCreated INT UNSIGNED NOT NULL, 10 + dateModified INT UNSIGNED NOT NULL, 11 + UNIQUE KEY `key_phid` (phid), 12 + UNIQUE KEY `key_name` (nameIndex), 13 + KEY `key_nametext` (name) 14 + ) ENGINE=InnoDB, COLLATE utf8_general_ci;
+19
resources/sql/autopatches/20141016.almanac.sxaction.sql
··· 1 + CREATE TABLE {$NAMESPACE}_almanac.almanac_servicetransaction ( 2 + id INT UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY, 3 + phid VARCHAR(64) COLLATE utf8_bin NOT NULL, 4 + authorPHID VARCHAR(64) COLLATE utf8_bin NOT NULL, 5 + objectPHID VARCHAR(64) COLLATE utf8_bin NOT NULL, 6 + viewPolicy VARCHAR(64) COLLATE utf8_bin NOT NULL, 7 + editPolicy VARCHAR(64) COLLATE utf8_bin NOT NULL, 8 + commentPHID VARCHAR(64) COLLATE utf8_bin DEFAULT NULL, 9 + commentVersion INT UNSIGNED NOT NULL, 10 + transactionType VARCHAR(32) COLLATE utf8_bin NOT NULL, 11 + oldValue LONGTEXT COLLATE utf8_bin NOT NULL, 12 + newValue LONGTEXT COLLATE utf8_bin NOT NULL, 13 + contentSource LONGTEXT COLLATE utf8_bin NOT NULL, 14 + metadata LONGTEXT COLLATE utf8_bin NOT NULL, 15 + dateCreated INT UNSIGNED NOT NULL, 16 + dateModified INT UNSIGNED NOT NULL, 17 + UNIQUE KEY `key_phid` (`phid`), 18 + KEY `key_object` (`objectPHID`) 19 + ) ENGINE=InnoDB, COLLATE utf8_general_ci;
+33
src/__phutil_library_map__.php
··· 10 10 '__library_version__' => 2, 11 11 'class' => array( 12 12 'AlmanacConduitUtil' => 'applications/almanac/util/AlmanacConduitUtil.php', 13 + 'AlmanacConsoleController' => 'applications/almanac/controller/AlmanacConsoleController.php', 14 + 'AlmanacController' => 'applications/almanac/controller/AlmanacController.php', 15 + 'AlmanacCreateServicesCapability' => 'applications/almanac/capability/AlmanacCreateServicesCapability.php', 13 16 'AlmanacDAO' => 'applications/almanac/storage/AlmanacDAO.php', 14 17 'AlmanacDevice' => 'applications/almanac/storage/AlmanacDevice.php', 15 18 'AlmanacDevicePHIDType' => 'applications/almanac/phid/AlmanacDevicePHIDType.php', ··· 17 20 'AlmanacDeviceQuery' => 'applications/almanac/query/AlmanacDeviceQuery.php', 18 21 'AlmanacManagementRegisterWorkflow' => 'applications/almanac/management/AlmanacManagementRegisterWorkflow.php', 19 22 'AlmanacManagementWorkflow' => 'applications/almanac/management/AlmanacManagementWorkflow.php', 23 + 'AlmanacService' => 'applications/almanac/storage/AlmanacService.php', 24 + 'AlmanacServiceController' => 'applications/almanac/controller/AlmanacServiceController.php', 25 + 'AlmanacServiceEditController' => 'applications/almanac/controller/AlmanacServiceEditController.php', 26 + 'AlmanacServiceEditor' => 'applications/almanac/editor/AlmanacServiceEditor.php', 27 + 'AlmanacServiceListController' => 'applications/almanac/controller/AlmanacServiceListController.php', 28 + 'AlmanacServicePHIDType' => 'applications/almanac/phid/AlmanacServicePHIDType.php', 29 + 'AlmanacServiceQuery' => 'applications/almanac/query/AlmanacServiceQuery.php', 30 + 'AlmanacServiceSearchEngine' => 'applications/almanac/query/AlmanacServiceSearchEngine.php', 31 + 'AlmanacServiceTestCase' => 'applications/almanac/storage/__tests__/AlmanacServiceTestCase.php', 32 + 'AlmanacServiceTransaction' => 'applications/almanac/storage/AlmanacServiceTransaction.php', 33 + 'AlmanacServiceTransactionQuery' => 'applications/almanac/query/AlmanacServiceTransactionQuery.php', 34 + 'AlmanacServiceViewController' => 'applications/almanac/controller/AlmanacServiceViewController.php', 20 35 'Aphront304Response' => 'aphront/response/Aphront304Response.php', 21 36 'Aphront400Response' => 'aphront/response/Aphront400Response.php', 22 37 'Aphront403Response' => 'aphront/response/Aphront403Response.php', ··· 2904 2919 ), 2905 2920 'xmap' => array( 2906 2921 'AlmanacConduitUtil' => 'Phobject', 2922 + 'AlmanacConsoleController' => 'AlmanacController', 2923 + 'AlmanacController' => 'PhabricatorController', 2924 + 'AlmanacCreateServicesCapability' => 'PhabricatorPolicyCapability', 2907 2925 'AlmanacDAO' => 'PhabricatorLiskDAO', 2908 2926 'AlmanacDevice' => array( 2909 2927 'AlmanacDAO', ··· 2914 2932 'AlmanacDeviceQuery' => 'PhabricatorCursorPagedPolicyAwareQuery', 2915 2933 'AlmanacManagementRegisterWorkflow' => 'AlmanacManagementWorkflow', 2916 2934 'AlmanacManagementWorkflow' => 'PhabricatorManagementWorkflow', 2935 + 'AlmanacService' => array( 2936 + 'AlmanacDAO', 2937 + 'PhabricatorPolicyInterface', 2938 + ), 2939 + 'AlmanacServiceController' => 'AlmanacController', 2940 + 'AlmanacServiceEditController' => 'AlmanacServiceController', 2941 + 'AlmanacServiceEditor' => 'PhabricatorApplicationTransactionEditor', 2942 + 'AlmanacServiceListController' => 'AlmanacServiceController', 2943 + 'AlmanacServicePHIDType' => 'PhabricatorPHIDType', 2944 + 'AlmanacServiceQuery' => 'PhabricatorCursorPagedPolicyAwareQuery', 2945 + 'AlmanacServiceSearchEngine' => 'PhabricatorApplicationSearchEngine', 2946 + 'AlmanacServiceTestCase' => 'PhabricatorTestCase', 2947 + 'AlmanacServiceTransaction' => 'PhabricatorApplicationTransaction', 2948 + 'AlmanacServiceTransactionQuery' => 'PhabricatorApplicationTransactionQuery', 2949 + 'AlmanacServiceViewController' => 'AlmanacServiceController', 2917 2950 'Aphront304Response' => 'AphrontResponse', 2918 2951 'Aphront400Response' => 'AphrontResponse', 2919 2952 'Aphront403Response' => 'AphrontHTMLResponse',
+17 -4
src/applications/almanac/application/PhabricatorAlmanacApplication.php
··· 30 30 return true; 31 31 } 32 32 33 - public function isLaunchable() { 34 - return false; 33 + public function getRoutes() { 34 + return array( 35 + '/almanac/' => array( 36 + '' => 'AlmanacConsoleController', 37 + 'service/' => array( 38 + '(?:query/(?P<queryKey>[^/]+)/)?' => 'AlmanacServiceListController', 39 + 'edit/(?:(?P<id>\d+)/)?' => 'AlmanacServiceEditController', 40 + 'view/(?P<name>[^/]+)/' => 'AlmanacServiceViewController', 41 + ), 42 + ), 43 + ); 35 44 } 36 45 37 - public function getRoutes() { 38 - return array(); 46 + protected function getCustomCapabilities() { 47 + return array( 48 + AlmanacCreateServicesCapability::CAPABILITY => array( 49 + 'default' => PhabricatorPolicies::POLICY_ADMIN, 50 + ), 51 + ); 39 52 } 40 53 41 54 }
+16
src/applications/almanac/capability/AlmanacCreateServicesCapability.php
··· 1 + <?php 2 + 3 + final class AlmanacCreateServicesCapability 4 + extends PhabricatorPolicyCapability { 5 + 6 + const CAPABILITY = 'almanac.services'; 7 + 8 + public function getCapabilityName() { 9 + return pht('Can Create Services'); 10 + } 11 + 12 + public function describeCapabilityRejection() { 13 + return pht('You do not have permission to create Almanac services.'); 14 + } 15 + 16 + }
+36
src/applications/almanac/controller/AlmanacConsoleController.php
··· 1 + <?php 2 + 3 + final class AlmanacConsoleController extends AlmanacController { 4 + 5 + public function shouldAllowPublic() { 6 + return true; 7 + } 8 + 9 + public function handleRequest(AphrontRequest $request) { 10 + $viewer = $request->getViewer(); 11 + 12 + $menu = id(new PHUIObjectItemListView()) 13 + ->setUser($viewer); 14 + 15 + $menu->addItem( 16 + id(new PHUIObjectItemView()) 17 + ->setHeader(pht('Services')) 18 + ->setHref($this->getApplicationURI('service/')) 19 + ->addAttribute( 20 + pht( 21 + 'Manage Almanac services.'))); 22 + 23 + $crumbs = $this->buildApplicationCrumbs(); 24 + $crumbs->addTextCrumb(pht('Console')); 25 + 26 + return $this->buildApplicationPage( 27 + array( 28 + $crumbs, 29 + $menu, 30 + ), 31 + array( 32 + 'title' => pht('Almanac Console'), 33 + )); 34 + } 35 + 36 + }
+4
src/applications/almanac/controller/AlmanacController.php
··· 1 + <?php 2 + 3 + abstract class AlmanacController 4 + extends PhabricatorController {}
+14
src/applications/almanac/controller/AlmanacServiceController.php
··· 1 + <?php 2 + 3 + abstract class AlmanacServiceController extends AlmanacController { 4 + 5 + public function buildApplicationCrumbs() { 6 + $crumbs = parent::buildApplicationCrumbs(); 7 + 8 + $list_uri = $this->getApplicationURI('service/'); 9 + $crumbs->addTextCrumb(pht('Services'), $list_uri); 10 + 11 + return $crumbs; 12 + } 13 + 14 + }
+142
src/applications/almanac/controller/AlmanacServiceEditController.php
··· 1 + <?php 2 + 3 + final class AlmanacServiceEditController 4 + extends AlmanacServiceController { 5 + 6 + public function handleRequest(AphrontRequest $request) { 7 + $viewer = $request->getViewer(); 8 + 9 + $list_uri = $this->getApplicationURI('service'); 10 + 11 + $id = $request->getURIData('id'); 12 + if ($id) { 13 + $service = id(new AlmanacServiceQuery()) 14 + ->setViewer($viewer) 15 + ->withIDs(array($id)) 16 + ->requireCapabilities( 17 + array( 18 + PhabricatorPolicyCapability::CAN_VIEW, 19 + PhabricatorPolicyCapability::CAN_EDIT, 20 + )) 21 + ->executeOne(); 22 + if (!$service) { 23 + return new Aphront404Response(); 24 + } 25 + 26 + $is_new = false; 27 + $service_uri = $service->getURI(); 28 + $cancel_uri = $service_uri; 29 + $title = pht('Edit Service'); 30 + $save_button = pht('Save Changes'); 31 + } else { 32 + $this->requireApplicationCapability( 33 + AlmanacCreateServicesCapability::CAPABILITY); 34 + 35 + $service = AlmanacService::initializeNewService(); 36 + $is_new = true; 37 + 38 + $cancel_uri = $list_uri; 39 + $title = pht('Create Service'); 40 + $save_button = pht('Create Service'); 41 + } 42 + 43 + $v_name = $service->getName(); 44 + $e_name = true; 45 + $validation_exception = null; 46 + 47 + if ($request->isFormPost()) { 48 + $v_name = $request->getStr('name'); 49 + $v_view = $request->getStr('viewPolicy'); 50 + $v_edit = $request->getStr('editPolicy'); 51 + 52 + $type_name = AlmanacServiceTransaction::TYPE_NAME; 53 + $type_view = PhabricatorTransactions::TYPE_VIEW_POLICY; 54 + $type_edit = PhabricatorTransactions::TYPE_EDIT_POLICY; 55 + 56 + $xactions = array(); 57 + 58 + $xactions[] = id(new AlmanacServiceTransaction()) 59 + ->setTransactionType($type_name) 60 + ->setNewValue($v_name); 61 + 62 + $xactions[] = id(new AlmanacServiceTransaction()) 63 + ->setTransactionType($type_view) 64 + ->setNewValue($v_view); 65 + 66 + $xactions[] = id(new AlmanacServiceTransaction()) 67 + ->setTransactionType($type_edit) 68 + ->setNewValue($v_edit); 69 + 70 + $editor = id(new AlmanacServiceEditor()) 71 + ->setActor($viewer) 72 + ->setContentSourceFromRequest($request) 73 + ->setContinueOnNoEffect(true); 74 + 75 + try { 76 + $editor->applyTransactions($service, $xactions); 77 + 78 + $service_uri = $service->getURI(); 79 + return id(new AphrontRedirectResponse())->setURI($service_uri); 80 + } catch (PhabricatorApplicationTransactionValidationException $ex) { 81 + $validation_exception = $ex; 82 + $e_name = $ex->getShortMessage($type_name); 83 + 84 + $service->setViewPolicy($v_view); 85 + $service->setEditPolicy($v_edit); 86 + } 87 + } 88 + 89 + $policies = id(new PhabricatorPolicyQuery()) 90 + ->setViewer($viewer) 91 + ->setObject($service) 92 + ->execute(); 93 + 94 + $form = id(new AphrontFormView()) 95 + ->setUser($viewer) 96 + ->appendChild( 97 + id(new AphrontFormTextControl()) 98 + ->setLabel(pht('Name')) 99 + ->setName('name') 100 + ->setValue($v_name) 101 + ->setError($e_name)) 102 + ->appendChild( 103 + id(new AphrontFormPolicyControl()) 104 + ->setName('viewPolicy') 105 + ->setPolicyObject($service) 106 + ->setCapability(PhabricatorPolicyCapability::CAN_VIEW) 107 + ->setPolicies($policies)) 108 + ->appendChild( 109 + id(new AphrontFormPolicyControl()) 110 + ->setName('editPolicy') 111 + ->setPolicyObject($service) 112 + ->setCapability(PhabricatorPolicyCapability::CAN_EDIT) 113 + ->setPolicies($policies)) 114 + ->appendChild( 115 + id(new AphrontFormSubmitControl()) 116 + ->addCancelButton($cancel_uri) 117 + ->setValue($save_button)); 118 + 119 + $box = id(new PHUIObjectBoxView()) 120 + ->setValidationException($validation_exception) 121 + ->setHeaderText($title) 122 + ->appendChild($form); 123 + 124 + $crumbs = $this->buildApplicationCrumbs(); 125 + if ($is_new) { 126 + $crumbs->addTextCrumb(pht('Create Service')); 127 + } else { 128 + $crumbs->addTextCrumb($service->getName(), $service_uri); 129 + $crumbs->addTextCrumb(pht('Edit')); 130 + } 131 + 132 + return $this->buildApplicationPage( 133 + array( 134 + $crumbs, 135 + $box, 136 + ), 137 + array( 138 + 'title' => $title, 139 + )); 140 + } 141 + 142 + }
+52
src/applications/almanac/controller/AlmanacServiceListController.php
··· 1 + <?php 2 + 3 + final class AlmanacServiceListController 4 + extends AlmanacServiceController { 5 + 6 + public function shouldAllowPublic() { 7 + return true; 8 + } 9 + 10 + public function handleRequest(AphrontRequest $request) { 11 + $controller = id(new PhabricatorApplicationSearchController()) 12 + ->setQueryKey($request->getURIData('queryKey')) 13 + ->setSearchEngine(new AlmanacServiceSearchEngine()) 14 + ->setNavigation($this->buildSideNavView()); 15 + 16 + return $this->delegateToController($controller); 17 + } 18 + 19 + public function buildApplicationCrumbs() { 20 + $crumbs = parent::buildApplicationCrumbs(); 21 + 22 + $can_create = $this->hasApplicationCapability( 23 + AlmanacCreateServicesCapability::CAPABILITY); 24 + 25 + $crumbs->addAction( 26 + id(new PHUIListItemView()) 27 + ->setName(pht('Create Service')) 28 + ->setHref($this->getApplicationURI('service/edit/')) 29 + ->setIcon('fa-plus-square') 30 + ->setDisabled(!$can_create) 31 + ->setWorkflow(!$can_create)); 32 + 33 + return $crumbs; 34 + } 35 + 36 + public function buildSideNavView() { 37 + $viewer = $this->getViewer(); 38 + 39 + $nav = new AphrontSideNavFilterView(); 40 + $nav->setBaseURI(new PhutilURI($this->getApplicationURI())); 41 + 42 + id(new AlmanacServiceSearchEngine()) 43 + ->setViewer($viewer) 44 + ->addNavigationItems($nav->getMenu()); 45 + 46 + $nav->selectFilter(null); 47 + 48 + return $nav; 49 + } 50 + 51 + 52 + }
+95
src/applications/almanac/controller/AlmanacServiceViewController.php
··· 1 + <?php 2 + 3 + final class AlmanacServiceViewController 4 + extends AlmanacServiceController { 5 + 6 + public function shouldAllowPublic() { 7 + return true; 8 + } 9 + 10 + public function handleRequest(AphrontRequest $request) { 11 + $viewer = $request->getViewer(); 12 + 13 + $name = $request->getURIData('name'); 14 + 15 + $service = id(new AlmanacServiceQuery()) 16 + ->setViewer($viewer) 17 + ->withNames(array($name)) 18 + ->executeOne(); 19 + if (!$service) { 20 + return new Aphront404Response(); 21 + } 22 + 23 + $title = pht('Service %s', $service->getName()); 24 + 25 + $property_list = $this->buildPropertyList($service); 26 + $action_list = $this->buildActionList($service); 27 + $property_list->setActionList($action_list); 28 + 29 + $header = id(new PHUIHeaderView()) 30 + ->setUser($viewer) 31 + ->setHeader($service->getName()) 32 + ->setPolicyObject($service); 33 + 34 + $box = id(new PHUIObjectBoxView()) 35 + ->setHeader($header) 36 + ->addPropertyList($property_list); 37 + 38 + $crumbs = $this->buildApplicationCrumbs(); 39 + $crumbs->addTextCrumb($service->getName()); 40 + 41 + $xactions = id(new AlmanacServiceTransactionQuery()) 42 + ->setViewer($viewer) 43 + ->withObjectPHIDs(array($service->getPHID())) 44 + ->execute(); 45 + 46 + $xaction_view = id(new PhabricatorApplicationTransactionView()) 47 + ->setUser($viewer) 48 + ->setObjectPHID($service->getPHID()) 49 + ->setTransactions($xactions) 50 + ->setShouldTerminate(true); 51 + 52 + return $this->buildApplicationPage( 53 + array( 54 + $crumbs, 55 + $box, 56 + $xaction_view, 57 + ), 58 + array( 59 + 'title' => $title, 60 + )); 61 + } 62 + 63 + private function buildPropertyList(AlmanacService $service) { 64 + $viewer = $this->getViewer(); 65 + 66 + $properties = id(new PHUIPropertyListView()) 67 + ->setUser($viewer); 68 + 69 + return $properties; 70 + } 71 + 72 + private function buildActionList(AlmanacService $service) { 73 + $viewer = $this->getViewer(); 74 + $id = $service->getID(); 75 + 76 + $can_edit = PhabricatorPolicyFilter::hasCapability( 77 + $viewer, 78 + $service, 79 + PhabricatorPolicyCapability::CAN_EDIT); 80 + 81 + $actions = id(new PhabricatorActionListView()) 82 + ->setUser($viewer); 83 + 84 + $actions->addAction( 85 + id(new PhabricatorActionView()) 86 + ->setIcon('fa-pencil') 87 + ->setName(pht('Edit Service')) 88 + ->setHref($this->getApplicationURI("service/edit/{$id}/")) 89 + ->setWorkflow(!$can_edit) 90 + ->setDisabled(!$can_edit)); 91 + 92 + return $actions; 93 + } 94 + 95 + }
+143
src/applications/almanac/editor/AlmanacServiceEditor.php
··· 1 + <?php 2 + 3 + final class AlmanacServiceEditor 4 + extends PhabricatorApplicationTransactionEditor { 5 + 6 + public function getEditorApplicationClass() { 7 + return 'PhabricatorAlmanacApplication'; 8 + } 9 + 10 + public function getEditorObjectsDescription() { 11 + return pht('Almanac Service'); 12 + } 13 + 14 + public function getTransactionTypes() { 15 + $types = parent::getTransactionTypes(); 16 + 17 + $types[] = AlmanacServiceTransaction::TYPE_NAME; 18 + $types[] = PhabricatorTransactions::TYPE_VIEW_POLICY; 19 + $types[] = PhabricatorTransactions::TYPE_EDIT_POLICY; 20 + 21 + return $types; 22 + } 23 + 24 + protected function getCustomTransactionOldValue( 25 + PhabricatorLiskDAO $object, 26 + PhabricatorApplicationTransaction $xaction) { 27 + switch ($xaction->getTransactionType()) { 28 + case AlmanacServiceTransaction::TYPE_NAME: 29 + return $object->getName(); 30 + } 31 + 32 + return parent::getCustomTransactionOldValue($object, $xaction); 33 + } 34 + 35 + protected function getCustomTransactionNewValue( 36 + PhabricatorLiskDAO $object, 37 + PhabricatorApplicationTransaction $xaction) { 38 + 39 + switch ($xaction->getTransactionType()) { 40 + case AlmanacServiceTransaction::TYPE_NAME: 41 + return $xaction->getNewValue(); 42 + } 43 + 44 + return parent::getCustomTransactionNewValue($object, $xaction); 45 + } 46 + 47 + protected function applyCustomInternalTransaction( 48 + PhabricatorLiskDAO $object, 49 + PhabricatorApplicationTransaction $xaction) { 50 + 51 + switch ($xaction->getTransactionType()) { 52 + case AlmanacServiceTransaction::TYPE_NAME: 53 + $object->setName($xaction->getNewValue()); 54 + return; 55 + case PhabricatorTransactions::TYPE_VIEW_POLICY: 56 + case PhabricatorTransactions::TYPE_EDIT_POLICY: 57 + return; 58 + } 59 + 60 + return parent::applyCustomInternalTransaction($object, $xaction); 61 + } 62 + 63 + protected function applyCustomExternalTransaction( 64 + PhabricatorLiskDAO $object, 65 + PhabricatorApplicationTransaction $xaction) { 66 + 67 + switch ($xaction->getTransactionType()) { 68 + case AlmanacServiceTransaction::TYPE_NAME: 69 + case PhabricatorTransactions::TYPE_VIEW_POLICY: 70 + case PhabricatorTransactions::TYPE_EDIT_POLICY: 71 + return; 72 + } 73 + 74 + return parent::applyCustomExternalTransaction($object, $xaction); 75 + } 76 + 77 + protected function validateTransaction( 78 + PhabricatorLiskDAO $object, 79 + $type, 80 + array $xactions) { 81 + 82 + $errors = parent::validateTransaction($object, $type, $xactions); 83 + 84 + switch ($type) { 85 + case AlmanacServiceTransaction::TYPE_NAME: 86 + $missing = $this->validateIsEmptyTextField( 87 + $object->getName(), 88 + $xactions); 89 + 90 + if ($missing) { 91 + $error = new PhabricatorApplicationTransactionValidationError( 92 + $type, 93 + pht('Required'), 94 + pht('Service name is required.'), 95 + nonempty(last($xactions), null)); 96 + 97 + $error->setIsMissingFieldError(true); 98 + $errors[] = $error; 99 + } else { 100 + foreach ($xactions as $xaction) { 101 + $message = null; 102 + 103 + try { 104 + AlmanacService::validateServiceName($xaction->getNewValue()); 105 + } catch (Exception $ex) { 106 + $message = $ex->getMessage(); 107 + } 108 + 109 + if ($message !== null) { 110 + $error = new PhabricatorApplicationTransactionValidationError( 111 + $type, 112 + pht('Invalid'), 113 + $message, 114 + $xaction); 115 + $errors[] = $error; 116 + } 117 + } 118 + } 119 + 120 + if ($xactions) { 121 + $duplicate = id(new AlmanacServiceQuery()) 122 + ->setViewer(PhabricatorUser::getOmnipotentUser()) 123 + ->withNames(array(last($xactions)->getNewValue())) 124 + ->executeOne(); 125 + if ($duplicate && ($duplicate->getID() != $object->getID())) { 126 + $error = new PhabricatorApplicationTransactionValidationError( 127 + $type, 128 + pht('Not Unique'), 129 + pht('Almanac services must have unique names.'), 130 + last($xactions)); 131 + $errors[] = $error; 132 + } 133 + } 134 + 135 + break; 136 + } 137 + 138 + return $errors; 139 + } 140 + 141 + 142 + 143 + }
+39
src/applications/almanac/phid/AlmanacServicePHIDType.php
··· 1 + <?php 2 + 3 + final class AlmanacServicePHIDType extends PhabricatorPHIDType { 4 + 5 + const TYPECONST = 'ASRV'; 6 + 7 + public function getTypeName() { 8 + return pht('Almanac Service'); 9 + } 10 + 11 + public function newObject() { 12 + return new AlmanacService(); 13 + } 14 + 15 + protected function buildQueryForObjects( 16 + PhabricatorObjectQuery $query, 17 + array $phids) { 18 + 19 + return id(new AlmanacServiceQuery()) 20 + ->withPHIDs($phids); 21 + } 22 + 23 + public function loadHandles( 24 + PhabricatorHandleQuery $query, 25 + array $handles, 26 + array $objects) { 27 + 28 + foreach ($handles as $phid => $handle) { 29 + $service = $objects[$phid]; 30 + 31 + $id = $service->getID(); 32 + $name = $service->getName(); 33 + 34 + $handle->setObjectName(pht('Service %d', $id)); 35 + $handle->setName($name); 36 + } 37 + } 38 + 39 + }
+78
src/applications/almanac/query/AlmanacServiceQuery.php
··· 1 + <?php 2 + 3 + final class AlmanacServiceQuery 4 + extends PhabricatorCursorPagedPolicyAwareQuery { 5 + 6 + private $ids; 7 + private $phids; 8 + private $names; 9 + 10 + public function withIDs(array $ids) { 11 + $this->ids = $ids; 12 + return $this; 13 + } 14 + 15 + public function withPHIDs(array $phids) { 16 + $this->phids = $phids; 17 + return $this; 18 + } 19 + 20 + public function withNames(array $names) { 21 + $this->names = $names; 22 + return $this; 23 + } 24 + 25 + protected function loadPage() { 26 + $table = new AlmanacService(); 27 + $conn_r = $table->establishConnection('r'); 28 + 29 + $data = queryfx_all( 30 + $conn_r, 31 + 'SELECT * FROM %T %Q %Q %Q', 32 + $table->getTableName(), 33 + $this->buildWhereClause($conn_r), 34 + $this->buildOrderClause($conn_r), 35 + $this->buildLimitClause($conn_r)); 36 + 37 + return $table->loadAllFromArray($data); 38 + } 39 + 40 + protected function buildWhereClause($conn_r) { 41 + $where = array(); 42 + 43 + if ($this->ids !== null) { 44 + $where[] = qsprintf( 45 + $conn_r, 46 + 'id IN (%Ld)', 47 + $this->ids); 48 + } 49 + 50 + if ($this->phids !== null) { 51 + $where[] = qsprintf( 52 + $conn_r, 53 + 'phid IN (%Ls)', 54 + $this->phids); 55 + } 56 + 57 + if ($this->names !== null) { 58 + $hashes = array(); 59 + foreach ($this->names as $name) { 60 + $hashes[] = PhabricatorHash::digestForIndex($name); 61 + } 62 + 63 + $where[] = qsprintf( 64 + $conn_r, 65 + 'nameIndex IN (%Ls)', 66 + $hashes); 67 + } 68 + 69 + $where[] = $this->buildPagingClause($conn_r); 70 + 71 + return $this->formatWhereClause($where); 72 + } 73 + 74 + public function getQueryApplicationClass() { 75 + return 'PhabricatorAlmanacApplication'; 76 + } 77 + 78 + }
+79
src/applications/almanac/query/AlmanacServiceSearchEngine.php
··· 1 + <?php 2 + 3 + final class AlmanacServiceSearchEngine 4 + extends PhabricatorApplicationSearchEngine { 5 + 6 + public function getResultTypeDescription() { 7 + return pht('Almanac Services'); 8 + } 9 + 10 + public function buildSavedQueryFromRequest(AphrontRequest $request) { 11 + $saved = new PhabricatorSavedQuery(); 12 + 13 + return $saved; 14 + } 15 + 16 + public function buildQueryFromSavedQuery(PhabricatorSavedQuery $saved) { 17 + $query = id(new AlmanacServiceQuery()); 18 + 19 + return $query; 20 + } 21 + 22 + public function buildSearchForm( 23 + AphrontFormView $form, 24 + PhabricatorSavedQuery $saved_query) {} 25 + 26 + protected function getURI($path) { 27 + return '/almanac/service/'.$path; 28 + } 29 + 30 + public function getBuiltinQueryNames() { 31 + $names = array( 32 + 'all' => pht('All Services'), 33 + ); 34 + 35 + return $names; 36 + } 37 + 38 + public function buildSavedQueryFromBuiltin($query_key) { 39 + 40 + $query = $this->newSavedQuery(); 41 + $query->setQueryKey($query_key); 42 + 43 + switch ($query_key) { 44 + case 'all': 45 + return $query; 46 + } 47 + 48 + return parent::buildSavedQueryFromBuiltin($query_key); 49 + } 50 + 51 + protected function getRequiredHandlePHIDsForResultList( 52 + array $services, 53 + PhabricatorSavedQuery $query) { 54 + return array(); 55 + } 56 + 57 + protected function renderResultList( 58 + array $services, 59 + PhabricatorSavedQuery $query, 60 + array $handles) { 61 + assert_instances_of($services, 'AlmanacService'); 62 + 63 + $viewer = $this->requireViewer(); 64 + 65 + $list = new PHUIObjectItemListView(); 66 + $list->setUser($viewer); 67 + foreach ($services as $service) { 68 + $item = id(new PHUIObjectItemView()) 69 + ->setObjectName(pht('Service %d', $service->getID())) 70 + ->setHeader($service->getName()) 71 + ->setHref($service->getURI()) 72 + ->setObject($service); 73 + 74 + $list->addItem($item); 75 + } 76 + 77 + return $list; 78 + } 79 + }
+10
src/applications/almanac/query/AlmanacServiceTransactionQuery.php
··· 1 + <?php 2 + 3 + final class AlmanacServiceTransactionQuery 4 + extends PhabricatorApplicationTransactionQuery { 5 + 6 + public function getTemplateApplicationTransaction() { 7 + return new AlmanacServiceTransaction(); 8 + } 9 + 10 + }
+132
src/applications/almanac/storage/AlmanacService.php
··· 1 + <?php 2 + 3 + final class AlmanacService 4 + extends AlmanacDAO 5 + implements PhabricatorPolicyInterface { 6 + 7 + protected $name; 8 + protected $nameIndex; 9 + protected $mailKey; 10 + protected $viewPolicy; 11 + protected $editPolicy; 12 + 13 + public static function initializeNewService() { 14 + return id(new AlmanacService()) 15 + ->setViewPolicy(PhabricatorPolicies::POLICY_USER) 16 + ->setEditPolicy(PhabricatorPolicies::POLICY_ADMIN); 17 + } 18 + 19 + public function getConfiguration() { 20 + return array( 21 + self::CONFIG_AUX_PHID => true, 22 + self::CONFIG_COLUMN_SCHEMA => array( 23 + 'name' => 'text128', 24 + 'nameIndex' => 'bytes12', 25 + 'mailKey' => 'bytes20', 26 + ), 27 + self::CONFIG_KEY_SCHEMA => array( 28 + 'key_name' => array( 29 + 'columns' => array('nameIndex'), 30 + 'unique' => true, 31 + ), 32 + 'key_nametext' => array( 33 + 'columns' => array('name'), 34 + ), 35 + ), 36 + ) + parent::getConfiguration(); 37 + } 38 + 39 + public function generatePHID() { 40 + return PhabricatorPHID::generateNewPHID(AlmanacServicePHIDType::TYPECONST); 41 + } 42 + 43 + public function save() { 44 + self::validateServiceName($this->getName()); 45 + $this->nameIndex = PhabricatorHash::digestForIndex($this->getName()); 46 + 47 + if (!$this->mailKey) { 48 + $this->mailKey = Filesystem::readRandomCharacters(20); 49 + } 50 + 51 + return parent::save(); 52 + } 53 + 54 + public function getURI() { 55 + return '/almanac/service/view/'.$this->getName().'/'; 56 + } 57 + 58 + public static function validateServiceName($name) { 59 + if (strlen($name) < 3) { 60 + throw new Exception( 61 + pht('Almanac service names must be at least 3 characters long.')); 62 + } 63 + 64 + if (!preg_match('/^[a-z0-9.-]+\z/', $name)) { 65 + throw new Exception( 66 + pht( 67 + 'Almanac service names may only contain lowercase letters, numbers, '. 68 + 'hyphens, and periods.')); 69 + } 70 + 71 + if (preg_match('/(^|\\.)\d+(\z|\\.)/', $name)) { 72 + throw new Exception( 73 + pht( 74 + 'Almanac service names may not have any segments containing only '. 75 + 'digits.')); 76 + } 77 + 78 + if (preg_match('/\.\./', $name)) { 79 + throw new Exception( 80 + pht( 81 + 'Almanac service names may not contain multiple consecutive '. 82 + 'periods.')); 83 + } 84 + 85 + if (preg_match('/\\.-|-\\./', $name)) { 86 + throw new Exception( 87 + pht( 88 + 'Amanac service names may not contain hyphens adjacent to periods.')); 89 + } 90 + 91 + if (preg_match('/--/', $name)) { 92 + throw new Exception( 93 + pht( 94 + 'Almanac service names may not contain multiple consecutive '. 95 + 'hyphens.')); 96 + } 97 + 98 + if (!preg_match('/^[a-z0-9].*[a-z0-9]\z/', $name)) { 99 + throw new Exception( 100 + pht( 101 + 'Almanac service names must begin and end with a letter or number.')); 102 + } 103 + } 104 + 105 + /* -( PhabricatorPolicyInterface )----------------------------------------- */ 106 + 107 + 108 + public function getCapabilities() { 109 + return array( 110 + PhabricatorPolicyCapability::CAN_VIEW, 111 + PhabricatorPolicyCapability::CAN_EDIT, 112 + ); 113 + } 114 + 115 + public function getPolicy($capability) { 116 + switch ($capability) { 117 + case PhabricatorPolicyCapability::CAN_VIEW: 118 + return $this->getViewPolicy(); 119 + case PhabricatorPolicyCapability::CAN_EDIT: 120 + return $this->getEditPolicy(); 121 + } 122 + } 123 + 124 + public function hasAutomaticCapability($capability, PhabricatorUser $viewer) { 125 + return false; 126 + } 127 + 128 + public function describeAutomaticCapability($capability) { 129 + return null; 130 + } 131 + 132 + }
+45
src/applications/almanac/storage/AlmanacServiceTransaction.php
··· 1 + <?php 2 + 3 + final class AlmanacServiceTransaction 4 + extends PhabricatorApplicationTransaction { 5 + 6 + const TYPE_NAME = 'almanac:service:name'; 7 + 8 + public function getApplicationName() { 9 + return 'almanac'; 10 + } 11 + 12 + public function getApplicationTransactionType() { 13 + return AlmanacServicePHIDType::TYPECONST; 14 + } 15 + 16 + public function getApplicationTransactionCommentObject() { 17 + return null; 18 + } 19 + 20 + public function getTitle() { 21 + $author_phid = $this->getAuthorPHID(); 22 + 23 + $old = $this->getOldValue(); 24 + $new = $this->getNewValue(); 25 + 26 + switch ($this->getTransactionType()) { 27 + case self::TYPE_NAME: 28 + if ($old === null) { 29 + return pht( 30 + '%s created this service.', 31 + $this->renderHandleLink($author_phid)); 32 + } else { 33 + return pht( 34 + '%s renamed this service from "%s" to "%s".', 35 + $this->renderHandleLink($author_phid), 36 + $old, 37 + $new); 38 + } 39 + break; 40 + } 41 + 42 + return parent::getTitle(); 43 + } 44 + 45 + }
+51
src/applications/almanac/storage/__tests__/AlmanacServiceTestCase.php
··· 1 + <?php 2 + 3 + final class AlmanacServiceTestCase extends PhabricatorTestCase { 4 + 5 + public function testServiceNames() { 6 + $map = array( 7 + '' => false, 8 + 'a' => false, 9 + 'ab' => false, 10 + '...' => false, 11 + 'ab.' => false, 12 + '.ab' => false, 13 + 'A-B' => false, 14 + 'A!B' => false, 15 + 'A.B' => false, 16 + 'a..b' => false, 17 + '1.2' => false, 18 + '127.0.0.1' => false, 19 + '1.b' => false, 20 + 'a.1' => false, 21 + 'a.1.b' => false, 22 + '-.a' => false, 23 + '-a.b' => false, 24 + 'a-.b' => false, 25 + 'a.-' => false, 26 + 'a.-b' => false, 27 + 'a.b-' => false, 28 + '-.-' => false, 29 + 'a--b' => false, 30 + 31 + 'abc' => true, 32 + 'a.b' => true, 33 + 'db.phacility.instance' => true, 34 + 'web002.useast.example.com' => true, 35 + 'master.example-corp.com' => true, 36 + ); 37 + 38 + foreach ($map as $input => $expect) { 39 + $caught = null; 40 + try { 41 + AlmanacService::validateServiceName($input); 42 + } catch (Exception $ex) { 43 + $caught = $ex; 44 + } 45 + $this->assertEqual( 46 + $expect, 47 + !($caught instanceof Exception), 48 + $input); 49 + } 50 + } 51 + }
-1
src/applications/phortune/controller/PhortuneAccountViewController.php
··· 287 287 return $crumbs; 288 288 } 289 289 290 - 291 290 }