Page MenuHomePhabricator

D13843.diff
No OneTemporary

D13843.diff

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

Mime Type
text/plain
Expires
Wed, Mar 26, 10:26 AM (4 w, 1 d ago)
Storage Engine
blob
Storage Format
Encrypted (AES-256-CBC)
Storage Handle
7723631
Default Alt Text
D13843.diff (25 KB)

Event Timeline