Page Menu
Home
Phabricator
Search
Configure Global Search
Log In
Files
F15520252
D13843.diff
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Award Token
Flag For Later
Size
25 KB
Referenced Files
None
Subscribers
None
D13843.diff
View Options
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
@@ -19,6 +19,7 @@
'AlmanacBindingTransaction' => 'applications/almanac/storage/AlmanacBindingTransaction.php',
'AlmanacBindingTransactionQuery' => 'applications/almanac/query/AlmanacBindingTransactionQuery.php',
'AlmanacBindingViewController' => 'applications/almanac/controller/AlmanacBindingViewController.php',
+ 'AlmanacBuildPoolServiceType' => 'applications/almanac/servicetype/AlmanacBuildPoolServiceType.php',
'AlmanacClusterDatabaseServiceType' => 'applications/almanac/servicetype/AlmanacClusterDatabaseServiceType.php',
'AlmanacClusterRepositoryServiceType' => 'applications/almanac/servicetype/AlmanacClusterRepositoryServiceType.php',
'AlmanacClusterServiceType' => 'applications/almanac/servicetype/AlmanacClusterServiceType.php',
@@ -796,6 +797,7 @@
'DoorkeeperTagView' => 'applications/doorkeeper/view/DoorkeeperTagView.php',
'DoorkeeperTagsController' => 'applications/doorkeeper/controller/DoorkeeperTagsController.php',
'DrydockAllocatorWorker' => 'applications/drydock/worker/DrydockAllocatorWorker.php',
+ 'DrydockAlmanacServiceHostBlueprintImplementation' => 'applications/drydock/blueprint/DrydockAlmanacServiceHostBlueprintImplementation.php',
'DrydockApacheWebrootInterface' => 'applications/drydock/interface/webroot/DrydockApacheWebrootInterface.php',
'DrydockBlueprint' => 'applications/drydock/storage/DrydockBlueprint.php',
'DrydockBlueprintController' => 'applications/drydock/controller/DrydockBlueprintController.php',
@@ -846,7 +848,6 @@
'DrydockManagementLeaseWorkflow' => 'applications/drydock/management/DrydockManagementLeaseWorkflow.php',
'DrydockManagementReleaseWorkflow' => 'applications/drydock/management/DrydockManagementReleaseWorkflow.php',
'DrydockManagementWorkflow' => 'applications/drydock/management/DrydockManagementWorkflow.php',
- 'DrydockPreallocatedHostBlueprintImplementation' => 'applications/drydock/blueprint/DrydockPreallocatedHostBlueprintImplementation.php',
'DrydockQuery' => 'applications/drydock/query/DrydockQuery.php',
'DrydockResource' => 'applications/drydock/storage/DrydockResource.php',
'DrydockResourceCloseController' => 'applications/drydock/controller/DrydockResourceCloseController.php',
@@ -2884,6 +2885,7 @@
'PhabricatorStandardCustomFieldRemarkup' => 'infrastructure/customfield/standard/PhabricatorStandardCustomFieldRemarkup.php',
'PhabricatorStandardCustomFieldSelect' => 'infrastructure/customfield/standard/PhabricatorStandardCustomFieldSelect.php',
'PhabricatorStandardCustomFieldText' => 'infrastructure/customfield/standard/PhabricatorStandardCustomFieldText.php',
+ 'PhabricatorStandardCustomFieldTypeahead' => 'infrastructure/customfield/standard/PhabricatorStandardCustomFieldTypeahead.php',
'PhabricatorStandardCustomFieldUsers' => 'infrastructure/customfield/standard/PhabricatorStandardCustomFieldUsers.php',
'PhabricatorStandardPageView' => 'view/page/PhabricatorStandardPageView.php',
'PhabricatorStandardSelectCustomFieldDatasource' => 'infrastructure/customfield/datasource/PhabricatorStandardSelectCustomFieldDatasource.php',
@@ -3586,6 +3588,7 @@
'AlmanacBindingTransaction' => 'PhabricatorApplicationTransaction',
'AlmanacBindingTransactionQuery' => 'PhabricatorApplicationTransactionQuery',
'AlmanacBindingViewController' => 'AlmanacServiceController',
+ 'AlmanacBuildPoolServiceType' => 'AlmanacServiceType',
'AlmanacClusterDatabaseServiceType' => 'AlmanacClusterServiceType',
'AlmanacClusterRepositoryServiceType' => 'AlmanacClusterServiceType',
'AlmanacClusterServiceType' => 'AlmanacServiceType',
@@ -4451,6 +4454,7 @@
'DoorkeeperTagView' => 'AphrontView',
'DoorkeeperTagsController' => 'PhabricatorController',
'DrydockAllocatorWorker' => 'PhabricatorWorker',
+ 'DrydockAlmanacServiceHostBlueprintImplementation' => 'DrydockBlueprintImplementation',
'DrydockApacheWebrootInterface' => 'DrydockWebrootInterface',
'DrydockBlueprint' => array(
'DrydockDAO',
@@ -4515,7 +4519,6 @@
'DrydockManagementLeaseWorkflow' => 'DrydockManagementWorkflow',
'DrydockManagementReleaseWorkflow' => 'DrydockManagementWorkflow',
'DrydockManagementWorkflow' => 'PhabricatorManagementWorkflow',
- 'DrydockPreallocatedHostBlueprintImplementation' => 'DrydockBlueprintImplementation',
'DrydockQuery' => 'PhabricatorCursorPagedPolicyAwareQuery',
'DrydockResource' => array(
'DrydockDAO',
@@ -6938,6 +6941,7 @@
'PhabricatorStandardCustomFieldRemarkup' => 'PhabricatorStandardCustomField',
'PhabricatorStandardCustomFieldSelect' => 'PhabricatorStandardCustomField',
'PhabricatorStandardCustomFieldText' => 'PhabricatorStandardCustomField',
+ 'PhabricatorStandardCustomFieldTypeahead' => 'PhabricatorStandardCustomFieldPHIDs',
'PhabricatorStandardCustomFieldUsers' => 'PhabricatorStandardCustomFieldPHIDs',
'PhabricatorStandardPageView' => 'PhabricatorBarePageView',
'PhabricatorStandardSelectCustomFieldDatasource' => 'PhabricatorTypeaheadDatasource',
diff --git a/src/applications/almanac/servicetype/AlmanacBuildPoolServiceType.php b/src/applications/almanac/servicetype/AlmanacBuildPoolServiceType.php
new file mode 100644
--- /dev/null
+++ b/src/applications/almanac/servicetype/AlmanacBuildPoolServiceType.php
@@ -0,0 +1,52 @@
+<?php
+
+final class AlmanacBuildPoolServiceType extends AlmanacServiceType {
+
+ public function getServiceTypeShortName() {
+ return pht('Build Pool');
+ }
+
+ public function getServiceTypeName() {
+ return pht('Drydock: Preallocated Build Machines');
+ }
+
+ public function getServiceTypeDescription() {
+ return pht(
+ 'Defines a build pool service that offers preallocated '.
+ 'hosts for Drydock.');
+ }
+
+ public function getFieldSpecifications() {
+ return array(
+ 'credential' => array(
+ 'name' => pht('SSH Credential'),
+ 'type' => 'credential',
+ 'required' => true,
+ 'credential.provides'
+ => PassphraseSSHPrivateKeyCredentialType::PROVIDES_TYPE,
+ ),
+ 'winrm-auth' => array(
+ 'name' => pht('WinRM Credentials'),
+ 'type' => 'credential',
+ 'credential.provides'
+ => PassphrasePasswordCredentialType::PROVIDES_TYPE,
+ 'caption' => pht(
+ 'This is only required if the platform is set to "windows".'),
+ ),
+ 'platform' => array(
+ 'name' => pht('Host Platform'),
+ 'type' => 'text',
+ 'caption' => pht(
+ 'Must be "windows" for Windows-based systems, or '.
+ 'any other value for UNIX-based systems.'),
+ ),
+ 'path' => array(
+ 'name' => pht('Storage Path'),
+ 'type' => 'text',
+ 'caption' => pht(
+ 'The path to store leases in.'),
+ ),
+ );
+ }
+
+}
diff --git a/src/applications/almanac/typeahead/AlmanacServiceDatasource.php b/src/applications/almanac/typeahead/AlmanacServiceDatasource.php
--- a/src/applications/almanac/typeahead/AlmanacServiceDatasource.php
+++ b/src/applications/almanac/typeahead/AlmanacServiceDatasource.php
@@ -3,6 +3,13 @@
final class AlmanacServiceDatasource
extends PhabricatorTypeaheadDatasource {
+ private $serviceClasses = null;
+
+ public function withServiceClasses(array $service_classes) {
+ $this->serviceClasses = $service_classes;
+ return $this;
+ }
+
public function getBrowseTitle() {
return pht('Browse Services');
}
@@ -23,6 +30,11 @@
->withNamePrefix($raw_query)
->setOrder('name');
+ if ($this->serviceClasses !== null) {
+ $services->withServiceClasses(
+ $this->serviceClasses);
+ }
+
$services = $this->executeQuery($services);
if ($services) {
diff --git a/src/applications/drydock/blueprint/DrydockAlmanacServiceHostBlueprintImplementation.php b/src/applications/drydock/blueprint/DrydockAlmanacServiceHostBlueprintImplementation.php
new file mode 100644
--- /dev/null
+++ b/src/applications/drydock/blueprint/DrydockAlmanacServiceHostBlueprintImplementation.php
@@ -0,0 +1,224 @@
+<?php
+
+final class DrydockAlmanacServiceHostBlueprintImplementation
+ extends DrydockBlueprintImplementation {
+
+ public function isEnabled() {
+ return true;
+ }
+
+ public function getBlueprintName() {
+ return pht('Almanac Build Hosts');
+ }
+
+ public function getDescription() {
+ return pht(
+ 'Allows Drydock to run on hosts registered '.
+ 'in an Almanac build pool.');
+ }
+
+ public function canAllocateMoreResources(array $pool) {
+ return true;
+ }
+
+ protected function executeAllocateResource(
+ DrydockResource $resource,
+ DrydockLease $lease) {
+
+ print_r($this->getDetail('service'));
+
+ $service = id(new AlmanacServiceQuery())
+ ->setViewer(PhabricatorUser::getOmnipotentUser())
+ ->withPHIDs(phutil_json_decode($this->getDetail('service')))
+ ->executeOne();
+
+ if ($service === null) {
+ throw new Exception('Specified Almanac service does not exist!');
+ }
+
+ $bindings = id(new AlmanacBindingQuery())
+ ->setViewer(PhabricatorUser::getOmnipotentUser())
+ ->withServicePHIDs(array($service->getPHID()))
+ ->execute();
+
+ // We have to filter available bindings by platform so that it
+ // matches the lease platform. If the service platform itself
+ // matches, then we include all bindings that are bound to it,
+ // unless the binding has overrridden the platform property. If
+ // the service platform does not match, then we look at all of
+ // the bindings which have overrridden the platform property
+ // to match.
+ $available_bindings = array();
+
+ if ($service->getAlmanacPropertyValue('platform') ===
+ $lease->getAttribute('platform')) {
+
+ // All bindings match by default, unless the binding overrides
+ // the platform property.
+ $available_bindings = $bindings;
+ foreach ($available_bindings as $key => $binding) {
+ if ($binding->hasAlmanacProperty('platform')) {
+ if ($binding->getAlmanacPropertyValue('platform') !==
+ $lease->getAttribute('platform')) {
+ unset($available_bindings[$key]);
+ }
+ }
+ }
+ } else {
+ // No bindings match by default, unless the binding overrides
+ // the platform property.
+ $available_bindings = array();
+ foreach ($bindings as $binding) {
+ if ($binding->hasAlmanacProperty('platform')) {
+ if ($binding->getAlmanacPropertyValue('platform') ===
+ $lease->getAttribute('platform')) {
+ $available_bindings[] = $binding;
+ }
+ }
+ }
+ }
+
+ // TODO: This probably isn't right; we might need to filter
+ // bindings by those that aren't already used by other
+ // Drydock resources.
+ shuffle($available_bindings);
+
+ $binding = head($available_bindings);
+
+ $host = $binding->getInterface()->getAddress();
+ $port = $binding->getInterface()->getPort();
+ $platform = $binding->getAlmanacPropertyValue(
+ 'platform',
+ $service->getAlmanacPropertyValue('platform'));
+ $path = $binding->getAlmanacPropertyValue(
+ 'path',
+ $service->getAlmanacPropertyValue('path'));
+ $credential = $binding->getAlmanacPropertyValue(
+ 'credential',
+ $service->getAlmanacPropertyValue('credential'));
+ $winrm_auth = $binding->getAlmanacPropertyValue(
+ 'winrm-auth',
+ $service->getAlmanacPropertyValue('winrm-auth'));
+
+ $credential = id(new PassphraseCredentialQuery())
+ ->setViewer(PhabricatorUser::getOmnipotentUser())
+ ->withPHIDs(array($credential))
+ ->executeOne();
+ if ($credential === null) {
+ throw new Exception('Specified credential does not exist!');
+ }
+
+ $winrm_auth_id = null;
+ if ($winrm_auth !== null) {
+ $winrm_auth = id(new PassphraseCredentialQuery())
+ ->setViewer(PhabricatorUser::getOmnipotentUser())
+ ->withPHIDs(array($winrm_auth))
+ ->executeOne();
+ if ($winrm_auth === null) {
+ throw new Exception('Specified credential does not exist!');
+ }
+ $winrm_auth_id = $winrm_auth->getID();
+ }
+
+ $resource
+ ->setName($binding->getInterface()->renderDisplayAddress())
+ ->setStatus(DrydockResourceStatus::STATUS_OPEN)
+ ->setAttribute('platform', $platform)
+ ->setAttribute('host', $host)
+ ->setAttribute('port', $port)
+ ->setAttribute('path', $path)
+ ->setAttribute('credential', $credential->getID())
+ ->setAttribute('winrm-auth', $winrm_auth_id)
+ ->save();
+ }
+
+ protected function canAllocateLease(
+ DrydockResource $resource,
+ DrydockLease $lease) {
+ return
+ $lease->getAttribute('platform') === $resource->getAttribute('platform');
+ }
+
+ protected function shouldAllocateLease(
+ DrydockResource $resource,
+ DrydockLease $lease,
+ array $other_leases) {
+ return true;
+ }
+
+ protected function executeAcquireLease(
+ DrydockResource $resource,
+ DrydockLease $lease) {
+
+ $platform = $resource->getAttribute('platform');
+ $path = $resource->getAttribute('path');
+
+ $lease_id = $lease->getID();
+
+ // Can't use DIRECTORY_SEPERATOR here because that is relevant to
+ // the platform we're currently running on, not the platform we are
+ // remoting to.
+ $separator = '/';
+ if ($platform === 'windows') {
+ $separator = '\\';
+ }
+
+ // Clean up the directory path a little.
+ $base_path = rtrim($path, '/');
+ $base_path = rtrim($base_path, '\\');
+ $full_path = $base_path.$separator.$lease_id;
+
+ $cmd = $lease->getInterface('command');
+
+ $cmd->execx('mkdir %s', $full_path);
+
+ $lease->setAttribute('path', $full_path);
+ }
+
+ public function getType() {
+ return 'host';
+ }
+
+ public function getInterface(
+ DrydockResource $resource,
+ DrydockLease $lease,
+ $type) {
+
+ switch ($type) {
+ case 'command':
+ return id(new DrydockSSHCommandInterface())
+ ->setConfiguration(array(
+ 'host' => $resource->getAttribute('host'),
+ 'port' => $resource->getAttribute('port'),
+ 'credential' => $resource->getAttribute('credential'),
+ 'platform' => $resource->getAttribute('platform'),
+ ))
+ ->setWorkingDirectory($lease->getAttribute('path'));
+ case 'filesystem':
+ return id(new DrydockSFTPFilesystemInterface())
+ ->setConfiguration(array(
+ 'host' => $resource->getAttribute('host'),
+ 'port' => $resource->getAttribute('port'),
+ 'credential' => $resource->getAttribute('credential'),
+ ));
+ }
+
+ throw new Exception(pht("No interface of type '%s'.", $type));
+ }
+
+ public function getFieldSpecifications() {
+ return array(
+ 'service' => array(
+ 'name' => pht('Almanac Service'),
+ 'type' => 'typeahead',
+ 'datasource' => id(new AlmanacServiceDatasource())
+ ->withServiceClasses(array(
+ 'AlmanacBuildPoolServiceType',
+ )),
+ 'limit' => 1,
+ 'required' => true,
+ ),
+ ) + parent::getFieldSpecifications();
+ }
+
+}
diff --git a/src/applications/drydock/blueprint/DrydockBlueprintImplementation.php b/src/applications/drydock/blueprint/DrydockBlueprintImplementation.php
--- a/src/applications/drydock/blueprint/DrydockBlueprintImplementation.php
+++ b/src/applications/drydock/blueprint/DrydockBlueprintImplementation.php
@@ -42,7 +42,7 @@
return $lease;
}
- protected function getInstance() {
+ public function getInstance() {
if (!$this->instance) {
throw new Exception(
pht('Attach the blueprint instance to the implementation.'));
@@ -301,10 +301,14 @@
return true;
}
- abstract protected function executeAllocateResource(DrydockLease $lease);
+ abstract protected function executeAllocateResource(
+ DrydockResource $resource,
+ DrydockLease $lease);
+ final public function allocateResource(
+ DrydockResource $resource,
+ DrydockLease $lease) {
- final public function allocateResource(DrydockLease $lease) {
$scope = $this->pushActiveScope(null, $lease);
$this->log(
@@ -314,7 +318,7 @@
$lease->getLeaseName()));
try {
- $resource = $this->executeAllocateResource($lease);
+ $this->executeAllocateResource($resource, $lease);
$this->validateAllocatedResource($resource);
} catch (Exception $ex) {
$this->logException($ex);
@@ -389,25 +393,6 @@
return idx(self::getAllBlueprintImplementations(), $class);
}
- protected function newResourceTemplate($name) {
- $resource = id(new DrydockResource())
- ->setBlueprintPHID($this->getInstance()->getPHID())
- ->setBlueprintClass($this->getBlueprintClass())
- ->setType($this->getType())
- ->setStatus(DrydockResourceStatus::STATUS_PENDING)
- ->setName($name)
- ->save();
-
- $this->activeResource = $resource;
-
- $this->log(
- pht(
- "Blueprint '%s': Created New Template",
- $this->getBlueprintClass()));
-
- return $resource;
- }
-
/**
* Sanity checks that the blueprint is implemented properly.
*/
diff --git a/src/applications/drydock/blueprint/DrydockPreallocatedHostBlueprintImplementation.php b/src/applications/drydock/blueprint/DrydockPreallocatedHostBlueprintImplementation.php
deleted file mode 100644
--- a/src/applications/drydock/blueprint/DrydockPreallocatedHostBlueprintImplementation.php
+++ /dev/null
@@ -1,116 +0,0 @@
-<?php
-
-final class DrydockPreallocatedHostBlueprintImplementation
- extends DrydockBlueprintImplementation {
-
- public function isEnabled() {
- return true;
- }
-
- public function getBlueprintName() {
- return pht('Preallocated Remote Hosts');
- }
-
- public function getDescription() {
- return pht('Allows Drydock to run on specific remote hosts you configure.');
- }
-
- public function canAllocateMoreResources(array $pool) {
- return false;
- }
-
- protected function executeAllocateResource(DrydockLease $lease) {
- throw new Exception(
- pht("Preallocated hosts can't be dynamically allocated."));
- }
-
- protected function canAllocateLease(
- DrydockResource $resource,
- DrydockLease $lease) {
- return
- $lease->getAttribute('platform') === $resource->getAttribute('platform');
- }
-
- protected function shouldAllocateLease(
- DrydockResource $resource,
- DrydockLease $lease,
- array $other_leases) {
- return true;
- }
-
- protected function executeAcquireLease(
- DrydockResource $resource,
- DrydockLease $lease) {
-
- // Because preallocated resources are manually created, we should verify
- // we have all the information we need.
- PhutilTypeSpec::checkMap(
- $resource->getAttributesForTypeSpec(
- array('platform', 'host', 'port', 'credential', 'path')),
- array(
- 'platform' => 'string',
- 'host' => 'string',
- 'port' => 'string', // Value is a string from the command line
- 'credential' => 'string',
- 'path' => 'string',
- ));
- $v_platform = $resource->getAttribute('platform');
- $v_path = $resource->getAttribute('path');
-
- // Similar to DrydockLocalHostBlueprint, we create a folder
- // on the remote host that the lease can use.
-
- $lease_id = $lease->getID();
-
- // Can't use DIRECTORY_SEPERATOR here because that is relevant to
- // the platform we're currently running on, not the platform we are
- // remoting to.
- $separator = '/';
- if ($v_platform === 'windows') {
- $separator = '\\';
- }
-
- // Clean up the directory path a little.
- $base_path = rtrim($v_path, '/');
- $base_path = rtrim($base_path, '\\');
- $full_path = $base_path.$separator.$lease_id;
-
- $cmd = $lease->getInterface('command');
-
- $cmd->execx('mkdir %s', $full_path);
-
- $lease->setAttribute('path', $full_path);
- }
-
- public function getType() {
- return 'host';
- }
-
- public function getInterface(
- DrydockResource $resource,
- DrydockLease $lease,
- $type) {
-
- switch ($type) {
- case 'command':
- return id(new DrydockSSHCommandInterface())
- ->setConfiguration(array(
- 'host' => $resource->getAttribute('host'),
- 'port' => $resource->getAttribute('port'),
- 'credential' => $resource->getAttribute('credential'),
- 'platform' => $resource->getAttribute('platform'),
- ))
- ->setWorkingDirectory($lease->getAttribute('path'));
- case 'filesystem':
- return id(new DrydockSFTPFilesystemInterface())
- ->setConfiguration(array(
- 'host' => $resource->getAttribute('host'),
- 'port' => $resource->getAttribute('port'),
- 'credential' => $resource->getAttribute('credential'),
- ));
- }
-
- throw new Exception(pht("No interface of type '%s'.", $type));
- }
-
-}
diff --git a/src/applications/drydock/blueprint/DrydockWorkingCopyBlueprintImplementation.php b/src/applications/drydock/blueprint/DrydockWorkingCopyBlueprintImplementation.php
--- a/src/applications/drydock/blueprint/DrydockWorkingCopyBlueprintImplementation.php
+++ b/src/applications/drydock/blueprint/DrydockWorkingCopyBlueprintImplementation.php
@@ -33,7 +33,10 @@
return !$other_leases;
}
- protected function executeAllocateResource(DrydockLease $lease) {
+ protected function executeAllocateResource(
+ DrydockResource $resource,
+ DrydockLease $lease) {
+
$repository_id = $lease->getAttribute('repositoryID');
if (!$repository_id) {
throw new Exception(
@@ -79,10 +82,9 @@
$this->log(pht('Complete.'));
- $resource = $this->newResourceTemplate(
- pht(
- 'Working Copy (%s)',
- $repository->getCallsign()));
+ $resource->setName(pht(
+ 'Working Copy (%s)',
+ $repository->getCallsign()));
$resource->setStatus(DrydockResourceStatus::STATUS_OPEN);
$resource->setAttribute('lease.host', $host_lease->getID());
$resource->setAttribute('path', $path);
diff --git a/src/applications/drydock/worker/DrydockAllocatorWorker.php b/src/applications/drydock/worker/DrydockAllocatorWorker.php
--- a/src/applications/drydock/worker/DrydockAllocatorWorker.php
+++ b/src/applications/drydock/worker/DrydockAllocatorWorker.php
@@ -116,8 +116,10 @@
}
if (!$resource) {
- $blueprints = DrydockBlueprintImplementation
- ::getAllBlueprintImplementationsForResource($type);
+ $blueprints = id(new DrydockBlueprintQuery())
+ ->setViewer(PhabricatorUser::getOmnipotentUser())
+ ->execute();
+ $blueprints = mpull($blueprints, 'getImplementation', 'getPHID');
$this->logToDrydock(
pht('Found %d Blueprints', count($blueprints)));
@@ -159,7 +161,14 @@
shuffle($blueprints);
$blueprint = head($blueprints);
- $resource = $blueprint->allocateResource($lease);
+
+ $resource = id(new DrydockResource())
+ ->setBlueprintPHID($blueprint->getInstance()->getPHID())
+ ->setType($blueprint->getType())
+ ->setName(pht('Pending Allocation'))
+ ->setStatus(DrydockResourceStatus::STATUS_PENDING)
+ ->save();
+ $blueprint->allocateResource($resource, $lease);
if (!$blueprint->allocateLease($resource, $lease)) {
// TODO: This "should" happen only if we lost a race with another lease,
diff --git a/src/infrastructure/customfield/standard/PhabricatorStandardCustomFieldTypeahead.php b/src/infrastructure/customfield/standard/PhabricatorStandardCustomFieldTypeahead.php
new file mode 100644
--- /dev/null
+++ b/src/infrastructure/customfield/standard/PhabricatorStandardCustomFieldTypeahead.php
@@ -0,0 +1,52 @@
+<?php
+
+final class PhabricatorStandardCustomFieldTypeahead
+ extends PhabricatorStandardCustomFieldPHIDs {
+
+ public function getFieldType() {
+ return 'typeahead';
+ }
+
+ public function renderEditControl(array $handles) {
+ $value = $this->getFieldValue();
+
+ $control = id(new AphrontFormTokenizerControl())
+ ->setUser($this->getViewer())
+ ->setLabel($this->getFieldName())
+ ->setName($this->getFieldKey())
+ ->setDatasource(
+ $this->getFieldConfigValue('datasource'))
+ ->setCaption($this->getCaption())
+ ->setValue(nonempty($value, array()));
+
+ $limit = $this->getFieldConfigValue('limit');
+ if ($limit) {
+ $control->setLimit($limit);
+ }
+
+ return $control;
+ }
+
+ public function appendToApplicationSearchForm(
+ PhabricatorApplicationSearchEngine $engine,
+ AphrontFormView $form,
+ $value) {
+
+ $control = id(new AphrontFormTokenizerControl())
+ ->setLabel($this->getFieldName())
+ ->setName($this->getFieldKey())
+ ->setDatasource(
+ $this->getFieldConfigValue('datasource'))
+ ->setValue(nonempty($value, array()));
+
+ $form->appendControl($control);
+ }
+
+ public function getHeraldFieldValueType($condition) {
+ return id(new HeraldTokenizerFieldValue())
+ ->setKey('custom.'.$this->getFieldKey())
+ ->setDatasource(
+ $this->getFieldConfigValue('datasource'));
+ }
+
+}
File Metadata
Details
Attached
Mime Type
text/plain
Expires
Mon, Apr 21, 4:58 AM (3 d, 22 h ago)
Storage Engine
blob
Storage Format
Encrypted (AES-256-CBC)
Storage Handle
7723631
Default Alt Text
D13843.diff (25 KB)
Attached To
Mode
D13843: [drydock/almanac] Implement blueprint that uses Almanac services
Attached
Detach File
Event Timeline
Log In to Comment