1
0
Fork 0
mirror of https://we.phorge.it/source/phorge.git synced 2025-01-19 03:01:11 +01:00

Modularize complex HTTP parameter types

Summary:
Ref T9132. We have several places in the code that sometimes need to parse complex types. For example, we accept all of these in ApplicationSearch and now in ApplicationEditor:

> /?subscribers=cat,dog
> /?subscribers=PHID-USER-1111
> /?subscribers[]=cat&subscribers[]=PHID-USER-2222

..etc. The logic to parse this stuff isn't too complex, but it isn't trivial either.

Right now it lives in some odd places. Notably, `PhabricatorApplicationSearchEngine` has some weird helper methods for this stuff. Rather than give `EditEngine` the same set of weird helper methods, pull all this stuff out into "HTTPParameterTypes".

Future diffs will add "Projects" and "Users" types where all the custom parsing/lookup logic can live. Then eventually the Search stuff can reuse these.

Generally, this just breaks the code up into smaller pieces that have more specific responsibilities.

Test Plan: {F944142}

Reviewers: chad

Reviewed By: chad

Maniphest Tasks: T9132

Differential Revision: https://secure.phabricator.com/D14402
This commit is contained in:
epriestley 2015-11-04 05:05:10 -08:00
parent 9de4bc6f3a
commit 20e4c3fbd4
15 changed files with 579 additions and 106 deletions

View file

@ -135,6 +135,7 @@ phutil_register_library_map(array(
'AphrontFormView' => 'view/form/AphrontFormView.php', 'AphrontFormView' => 'view/form/AphrontFormView.php',
'AphrontGlyphBarView' => 'view/widget/bars/AphrontGlyphBarView.php', 'AphrontGlyphBarView' => 'view/widget/bars/AphrontGlyphBarView.php',
'AphrontHTMLResponse' => 'aphront/response/AphrontHTMLResponse.php', 'AphrontHTMLResponse' => 'aphront/response/AphrontHTMLResponse.php',
'AphrontHTTPParameterType' => 'aphront/httpparametertype/AphrontHTTPParameterType.php',
'AphrontHTTPProxyResponse' => 'aphront/response/AphrontHTTPProxyResponse.php', 'AphrontHTTPProxyResponse' => 'aphront/response/AphrontHTTPProxyResponse.php',
'AphrontHTTPSink' => 'aphront/sink/AphrontHTTPSink.php', 'AphrontHTTPSink' => 'aphront/sink/AphrontHTTPSink.php',
'AphrontHTTPSinkTestCase' => 'aphront/sink/__tests__/AphrontHTTPSinkTestCase.php', 'AphrontHTTPSinkTestCase' => 'aphront/sink/__tests__/AphrontHTTPSinkTestCase.php',
@ -149,6 +150,8 @@ phutil_register_library_map(array(
'AphrontMultiColumnView' => 'view/layout/AphrontMultiColumnView.php', 'AphrontMultiColumnView' => 'view/layout/AphrontMultiColumnView.php',
'AphrontMySQLDatabaseConnectionTestCase' => 'infrastructure/storage/__tests__/AphrontMySQLDatabaseConnectionTestCase.php', 'AphrontMySQLDatabaseConnectionTestCase' => 'infrastructure/storage/__tests__/AphrontMySQLDatabaseConnectionTestCase.php',
'AphrontNullView' => 'view/AphrontNullView.php', 'AphrontNullView' => 'view/AphrontNullView.php',
'AphrontPHIDHTTPParameterType' => 'aphront/httpparametertype/AphrontPHIDHTTPParameterType.php',
'AphrontPHIDListHTTPParameterType' => 'aphront/httpparametertype/AphrontPHIDListHTTPParameterType.php',
'AphrontPHPHTTPSink' => 'aphront/sink/AphrontPHPHTTPSink.php', 'AphrontPHPHTTPSink' => 'aphront/sink/AphrontPHPHTTPSink.php',
'AphrontPageView' => 'view/page/AphrontPageView.php', 'AphrontPageView' => 'view/page/AphrontPageView.php',
'AphrontPlainTextResponse' => 'aphront/response/AphrontPlainTextResponse.php', 'AphrontPlainTextResponse' => 'aphront/response/AphrontPlainTextResponse.php',
@ -164,10 +167,13 @@ phutil_register_library_map(array(
'AphrontResponseProducerInterface' => 'aphront/interface/AphrontResponseProducerInterface.php', 'AphrontResponseProducerInterface' => 'aphront/interface/AphrontResponseProducerInterface.php',
'AphrontRoutingMap' => 'aphront/site/AphrontRoutingMap.php', 'AphrontRoutingMap' => 'aphront/site/AphrontRoutingMap.php',
'AphrontRoutingResult' => 'aphront/site/AphrontRoutingResult.php', 'AphrontRoutingResult' => 'aphront/site/AphrontRoutingResult.php',
'AphrontSelectHTTPParameterType' => 'aphront/httpparametertype/AphrontSelectHTTPParameterType.php',
'AphrontSideNavFilterView' => 'view/layout/AphrontSideNavFilterView.php', 'AphrontSideNavFilterView' => 'view/layout/AphrontSideNavFilterView.php',
'AphrontSite' => 'aphront/site/AphrontSite.php', 'AphrontSite' => 'aphront/site/AphrontSite.php',
'AphrontStackTraceView' => 'view/widget/AphrontStackTraceView.php', 'AphrontStackTraceView' => 'view/widget/AphrontStackTraceView.php',
'AphrontStandaloneHTMLResponse' => 'aphront/response/AphrontStandaloneHTMLResponse.php', 'AphrontStandaloneHTMLResponse' => 'aphront/response/AphrontStandaloneHTMLResponse.php',
'AphrontStringHTTPParameterType' => 'aphront/httpparametertype/AphrontStringHTTPParameterType.php',
'AphrontStringListHTTPParameterType' => 'aphront/httpparametertype/AphrontStringListHTTPParameterType.php',
'AphrontTableView' => 'view/control/AphrontTableView.php', 'AphrontTableView' => 'view/control/AphrontTableView.php',
'AphrontTagView' => 'view/AphrontTagView.php', 'AphrontTagView' => 'view/AphrontTagView.php',
'AphrontTokenizerTemplateView' => 'view/control/AphrontTokenizerTemplateView.php', 'AphrontTokenizerTemplateView' => 'view/control/AphrontTokenizerTemplateView.php',
@ -1889,6 +1895,7 @@ phutil_register_library_map(array(
'PhabricatorConfigEntryQuery' => 'applications/config/query/PhabricatorConfigEntryQuery.php', 'PhabricatorConfigEntryQuery' => 'applications/config/query/PhabricatorConfigEntryQuery.php',
'PhabricatorConfigFileSource' => 'infrastructure/env/PhabricatorConfigFileSource.php', 'PhabricatorConfigFileSource' => 'infrastructure/env/PhabricatorConfigFileSource.php',
'PhabricatorConfigGroupController' => 'applications/config/controller/PhabricatorConfigGroupController.php', 'PhabricatorConfigGroupController' => 'applications/config/controller/PhabricatorConfigGroupController.php',
'PhabricatorConfigHTTPParameterTypesModule' => 'applications/config/module/PhabricatorConfigHTTPParameterTypesModule.php',
'PhabricatorConfigHistoryController' => 'applications/config/controller/PhabricatorConfigHistoryController.php', 'PhabricatorConfigHistoryController' => 'applications/config/controller/PhabricatorConfigHistoryController.php',
'PhabricatorConfigIgnoreController' => 'applications/config/controller/PhabricatorConfigIgnoreController.php', 'PhabricatorConfigIgnoreController' => 'applications/config/controller/PhabricatorConfigIgnoreController.php',
'PhabricatorConfigIssueListController' => 'applications/config/controller/PhabricatorConfigIssueListController.php', 'PhabricatorConfigIssueListController' => 'applications/config/controller/PhabricatorConfigIssueListController.php',
@ -2250,6 +2257,7 @@ phutil_register_library_map(array(
'PhabricatorGlobalLock' => 'infrastructure/util/PhabricatorGlobalLock.php', 'PhabricatorGlobalLock' => 'infrastructure/util/PhabricatorGlobalLock.php',
'PhabricatorGlobalUploadTargetView' => 'applications/files/view/PhabricatorGlobalUploadTargetView.php', 'PhabricatorGlobalUploadTargetView' => 'applications/files/view/PhabricatorGlobalUploadTargetView.php',
'PhabricatorGoogleAuthProvider' => 'applications/auth/provider/PhabricatorGoogleAuthProvider.php', 'PhabricatorGoogleAuthProvider' => 'applications/auth/provider/PhabricatorGoogleAuthProvider.php',
'PhabricatorHTTPParameterTypeTableView' => 'applications/config/view/PhabricatorHTTPParameterTypeTableView.php',
'PhabricatorHandleList' => 'applications/phid/handle/pool/PhabricatorHandleList.php', 'PhabricatorHandleList' => 'applications/phid/handle/pool/PhabricatorHandleList.php',
'PhabricatorHandleObjectSelectorDataView' => 'applications/phid/handle/view/PhabricatorHandleObjectSelectorDataView.php', 'PhabricatorHandleObjectSelectorDataView' => 'applications/phid/handle/view/PhabricatorHandleObjectSelectorDataView.php',
'PhabricatorHandlePool' => 'applications/phid/handle/pool/PhabricatorHandlePool.php', 'PhabricatorHandlePool' => 'applications/phid/handle/pool/PhabricatorHandlePool.php',
@ -3866,6 +3874,7 @@ phutil_register_library_map(array(
'AphrontFormView' => 'AphrontView', 'AphrontFormView' => 'AphrontView',
'AphrontGlyphBarView' => 'AphrontBarView', 'AphrontGlyphBarView' => 'AphrontBarView',
'AphrontHTMLResponse' => 'AphrontResponse', 'AphrontHTMLResponse' => 'AphrontResponse',
'AphrontHTTPParameterType' => 'Phobject',
'AphrontHTTPProxyResponse' => 'AphrontResponse', 'AphrontHTTPProxyResponse' => 'AphrontResponse',
'AphrontHTTPSink' => 'Phobject', 'AphrontHTTPSink' => 'Phobject',
'AphrontHTTPSinkTestCase' => 'PhabricatorTestCase', 'AphrontHTTPSinkTestCase' => 'PhabricatorTestCase',
@ -3880,6 +3889,8 @@ phutil_register_library_map(array(
'AphrontMultiColumnView' => 'AphrontView', 'AphrontMultiColumnView' => 'AphrontView',
'AphrontMySQLDatabaseConnectionTestCase' => 'PhabricatorTestCase', 'AphrontMySQLDatabaseConnectionTestCase' => 'PhabricatorTestCase',
'AphrontNullView' => 'AphrontView', 'AphrontNullView' => 'AphrontView',
'AphrontPHIDHTTPParameterType' => 'AphrontHTTPParameterType',
'AphrontPHIDListHTTPParameterType' => 'AphrontHTTPParameterType',
'AphrontPHPHTTPSink' => 'AphrontHTTPSink', 'AphrontPHPHTTPSink' => 'AphrontHTTPSink',
'AphrontPageView' => 'AphrontView', 'AphrontPageView' => 'AphrontView',
'AphrontPlainTextResponse' => 'AphrontResponse', 'AphrontPlainTextResponse' => 'AphrontResponse',
@ -3897,10 +3908,13 @@ phutil_register_library_map(array(
'AphrontResponse' => 'Phobject', 'AphrontResponse' => 'Phobject',
'AphrontRoutingMap' => 'Phobject', 'AphrontRoutingMap' => 'Phobject',
'AphrontRoutingResult' => 'Phobject', 'AphrontRoutingResult' => 'Phobject',
'AphrontSelectHTTPParameterType' => 'AphrontHTTPParameterType',
'AphrontSideNavFilterView' => 'AphrontView', 'AphrontSideNavFilterView' => 'AphrontView',
'AphrontSite' => 'Phobject', 'AphrontSite' => 'Phobject',
'AphrontStackTraceView' => 'AphrontView', 'AphrontStackTraceView' => 'AphrontView',
'AphrontStandaloneHTMLResponse' => 'AphrontHTMLResponse', 'AphrontStandaloneHTMLResponse' => 'AphrontHTMLResponse',
'AphrontStringHTTPParameterType' => 'AphrontHTTPParameterType',
'AphrontStringListHTTPParameterType' => 'AphrontHTTPParameterType',
'AphrontTableView' => 'AphrontView', 'AphrontTableView' => 'AphrontView',
'AphrontTagView' => 'AphrontView', 'AphrontTagView' => 'AphrontView',
'AphrontTokenizerTemplateView' => 'AphrontView', 'AphrontTokenizerTemplateView' => 'AphrontView',
@ -5889,6 +5903,7 @@ phutil_register_library_map(array(
'PhabricatorConfigEntryQuery' => 'PhabricatorCursorPagedPolicyAwareQuery', 'PhabricatorConfigEntryQuery' => 'PhabricatorCursorPagedPolicyAwareQuery',
'PhabricatorConfigFileSource' => 'PhabricatorConfigProxySource', 'PhabricatorConfigFileSource' => 'PhabricatorConfigProxySource',
'PhabricatorConfigGroupController' => 'PhabricatorConfigController', 'PhabricatorConfigGroupController' => 'PhabricatorConfigController',
'PhabricatorConfigHTTPParameterTypesModule' => 'PhabricatorConfigModule',
'PhabricatorConfigHistoryController' => 'PhabricatorConfigController', 'PhabricatorConfigHistoryController' => 'PhabricatorConfigController',
'PhabricatorConfigIgnoreController' => 'PhabricatorConfigController', 'PhabricatorConfigIgnoreController' => 'PhabricatorConfigController',
'PhabricatorConfigIssueListController' => 'PhabricatorConfigController', 'PhabricatorConfigIssueListController' => 'PhabricatorConfigController',
@ -6312,6 +6327,7 @@ phutil_register_library_map(array(
'PhabricatorGlobalLock' => 'PhutilLock', 'PhabricatorGlobalLock' => 'PhutilLock',
'PhabricatorGlobalUploadTargetView' => 'AphrontView', 'PhabricatorGlobalUploadTargetView' => 'AphrontView',
'PhabricatorGoogleAuthProvider' => 'PhabricatorOAuth2AuthProvider', 'PhabricatorGoogleAuthProvider' => 'PhabricatorOAuth2AuthProvider',
'PhabricatorHTTPParameterTypeTableView' => 'AphrontView',
'PhabricatorHandleList' => array( 'PhabricatorHandleList' => array(
'Phobject', 'Phobject',
'Iterator', 'Iterator',

View file

@ -0,0 +1,309 @@
<?php
/**
* Defines how to read a complex value from an HTTP request.
*
* Most HTTP parameters are simple (like strings or integers) but some
* parameters accept more complex values (like lists of users or project names).
*
* This class handles reading simple and complex values from a request,
* performing any required parsing or lookups, and returning a result in a
* standard format.
*
* @task read Reading Values from a Request
* @task info Information About the Type
* @task util Parsing Utilities
* @task impl Implementation
*/
abstract class AphrontHTTPParameterType extends Phobject {
private $viewer;
/* -( Reading Values from a Request )-------------------------------------- */
/**
* Set the current viewer.
*
* Some parameter types perform complex parsing involving lookups. For
* example, a type might lookup usernames or project names. These types need
* to use the current viewer to execute queries.
*
* @param PhabricatorUser Current viewer.
* @return this
* @task read
*/
final public function setViewer(PhabricatorUser $viewer) {
$this->viewer = $viewer;
return $this;
}
/**
* Get the current viewer.
*
* @return PhabricatorUser Current viewer.
* @task read
*/
final public function getViewer() {
if (!$this->viewer) {
throw new PhutilInvalidStateException('setViewer');
}
return $this->viewer;
}
/**
* Test if a value is present in a request.
*
* @param AphrontRequest The incoming request.
* @param string The key to examine.
* @return bool True if a readable value is present in the request.
* @task read
*/
final public function getExists(AphrontRequest $request, $key) {
return $this->getParameterExists($request, $key);
}
/**
* Read a value from a request.
*
* If the value is not present, a default value is returned (usually `null`).
* Use @{method:getExists} to test if a value is present.
*
* @param AphrontRequest The incoming request.
* @param string The key to examine.
* @return wild Value, or default if value is not present.
* @task read
*/
final public function getValue(AphrontRequest $request, $key) {
if (!$this->getExists($request, $key)) {
return $this->getParameterDefault();
}
return $this->getParameterValue($request, $key);
}
/**
* Get the default value for this parameter type.
*
* @return wild Default value for this type.
* @task read
*/
final public function getDefaultValue() {
return $this->getParameterDefault();
}
/* -( Information About the Type )----------------------------------------- */
/**
* Get a short name for this type, like `string` or `list<phid>`.
*
* @return string Short type name.
* @task info
*/
final public function getTypeName() {
return $this->getParameterTypeName();
}
/**
* Get a list of human-readable descriptions of acceptable formats for this
* type.
*
* For example, a type might return strings like these:
*
* > Any positive integer.
* > A comma-separated list of PHIDs.
*
* This is used to explain to users how to specify a type when generating
* documentation.
*
* @return list<string> Human-readable list of acceptable formats.
* @task info
*/
final public function getFormatDescriptions() {
return $this->getParameterFormatDescriptions();
}
/**
* Get a list of human-readable examples of how to format this type as an
* HTTP GET parameter.
*
* For example, a type might return strings like these:
*
* > v=123
* > v[]=1&v[]=2
*
* This is used to show users how to specify parameters of this type in
* generated documentation.
*
* @return list<string> Human-readable list of format examples.
* @task info
*/
final public function getExamples() {
return $this->getParameterExamples();
}
/* -( Utilities )---------------------------------------------------------- */
/**
* Call another type's existence check.
*
* This method allows a type to reuse the exitence behavior of a different
* type. For example, a "list of users" type may have the same basic
* existence check that a simpler "list of strings" type has, and can just
* call the simpler type to reuse its behavior.
*
* @param AphrontHTTPParameterType The other type.
* @param AphrontRequest Incoming request.
* @param string Key to examine.
* @return bool True if the parameter exists.
* @task util
*/
final protected function getExistsWithType(
AphrontHTTPParameterType $type,
AphrontRequest $request,
$key) {
$type->setViewer($this->getViewer());
return $type->getParameterExists($request, $key);
}
/**
* Call another type's value parser.
*
* This method allows a type to reuse the parsing behavior of a different
* type. For example, a "list of users" type may start by running the same
* basic parsing that a simpler "list of strings" type does.
*
* @param AphrontHTTPParameterType The other type.
* @param AphrontRequest Incoming request.
* @param string Key to examine.
* @return wild Parsed value.
* @task util
*/
final protected function getValueWithType(
AphrontHTTPParameterType $type,
AphrontRequest $request,
$key) {
$type->setViewer($this->getViewer());
return $type->getValue($request, $key);
}
/**
* Get a list of all available parameter types.
*
* @return list<AphrontHTTPParameterType> List of all available types.
* @task util
*/
final public static function getAllTypes() {
return id(new PhutilClassMapQuery())
->setAncestorClass(__CLASS__)
->setUniqueMethod('getTypeName')
->setSortMethod('getTypeName')
->execute();
}
/* -( Implementation )----------------------------------------------------- */
/**
* Test if a parameter exists in a request.
*
* See @{method:getExists}. By default, this method tests if the key is
* present in the request.
*
* To call another type's behavior in order to perform this check, use
* @{method:getExistsWithType}.
*
* @param AphrontRequest The incoming request.
* @param string The key to examine.
* @return bool True if a readable value is present in the request.
* @task impl
*/
protected function getParameterExists(AphrontRequest $request, $key) {
return $request->getExists($key);
}
/**
* Parse a value from a request.
*
* See @{method:getValue}. This method will //only// be called if this type
* has already asserted that the value exists with
* @{method:getParameterExists}.
*
* To call another type's behavior in order to parse a value, use
* @{method:getValueWithType}.
*
* @param AphrontRequest The incoming request.
* @param string The key to examine.
* @return wild Parsed value.
* @task impl
*/
abstract protected function getParameterValue(AphrontRequest $request, $key);
/**
* Return a simple type name string, like "string" or "list<phid>".
*
* See @{method:getTypeName}.
*
* @return string Short type name.
* @task impl
*/
abstract protected function getParameterTypeName();
/**
* Return a human-readable list of format descriptions.
*
* See @{method:getFormatDescriptions}.
*
* @return list<string> Human-readable list of acceptable formats.
* @task impl
*/
abstract protected function getParameterFormatDescriptions();
/**
* Return a human-readable list of examples.
*
* See @{method:getExamples}.
*
* @return list<string> Human-readable list of format examples.
* @task impl
*/
abstract protected function getParameterExamples();
/**
* Return the default value for this parameter type.
*
* See @{method:getDefaultValue}. If unspecified, the default is `null`.
*
* @return wild Default value.
* @task impl
*/
protected function getParameterDefault() {
return null;
}
}

View file

@ -0,0 +1,26 @@
<?php
final class AphrontPHIDHTTPParameterType
extends AphrontHTTPParameterType {
protected function getParameterValue(AphrontRequest $request, $key) {
return $request->getStr($key);
}
protected function getParameterTypeName() {
return 'phid';
}
protected function getParameterFormatDescriptions() {
return array(
pht('A single object PHID.'),
);
}
protected function getParameterExamples() {
return array(
'v=PHID-XXXX-1111',
);
}
}

View file

@ -0,0 +1,30 @@
<?php
final class AphrontPHIDListHTTPParameterType
extends AphrontHTTPParameterType {
protected function getParameterValue(AphrontRequest $request, $key) {
$type = new AphrontStringListHTTPParameterType();
return $this->getValueWithType($type, $request, $key);
}
protected function getParameterTypeName() {
return 'list<phid>';
}
protected function getParameterFormatDescriptions() {
return array(
pht('Comma-separated list of PHIDs.'),
pht('List of PHIDs, as array.'),
);
}
protected function getParameterExamples() {
return array(
'v=PHID-XXXX-1111',
'v=PHID-XXXX-1111,PHID-XXXX-2222',
'v[]=PHID-XXXX-1111&v[]=PHID-XXXX-2222',
);
}
}

View file

@ -0,0 +1,26 @@
<?php
final class AphrontSelectHTTPParameterType
extends AphrontHTTPParameterType {
protected function getParameterValue(AphrontRequest $request, $key) {
return $request->getStr($key);
}
protected function getParameterTypeName() {
return 'select';
}
protected function getParameterFormatDescriptions() {
return array(
pht('A single value from the allowed set.'),
);
}
protected function getParameterExamples() {
return array(
'v=value',
);
}
}

View file

@ -0,0 +1,27 @@
<?php
final class AphrontStringHTTPParameterType
extends AphrontHTTPParameterType {
protected function getParameterValue(AphrontRequest $request, $key) {
return $request->getStr($key);
}
protected function getParameterTypeName() {
return 'string';
}
protected function getParameterFormatDescriptions() {
return array(
pht('A URL-encoded string.'),
);
}
protected function getParameterExamples() {
return array(
'v=simple',
'v=properly%20escaped%20text',
);
}
}

View file

@ -0,0 +1,38 @@
<?php
final class AphrontStringListHTTPParameterType
extends AphrontHTTPParameterType {
protected function getParameterValue(AphrontRequest $request, $key) {
$list = $request->getArr($key, null);
if ($list === null) {
$list = $request->getStrList($key);
}
return $list;
}
protected function getParameterDefault() {
return array();
}
protected function getParameterTypeName() {
return 'list<string>';
}
protected function getParameterFormatDescriptions() {
return array(
pht('Comma-separated list of strings.'),
pht('List of strings, as array.'),
);
}
protected function getParameterExamples() {
return array(
'v=cat,dog,pig',
'v[]=cat&v[]=dog',
);
}
}

View file

@ -0,0 +1,27 @@
<?php
final class PhabricatorConfigHTTPParameterTypesModule
extends PhabricatorConfigModule {
public function getModuleKey() {
return 'httpparameter';
}
public function getModuleName() {
return pht('HTTP Parameter Types');
}
public function renderModuleStatus(AphrontRequest $request) {
$viewer = $request->getViewer();
$types = AphrontHTTPParameterType::getAllTypes();
$table = id(new PhabricatorHTTPParameterTypeTableView())
->setHTTPParameterTypes($types);
return id(new PHUIObjectBoxView())
->setHeaderText(pht('HTTP Parameter Types'))
->setTable($table);
}
}

View file

@ -0,0 +1,56 @@
<?php
final class PhabricatorHTTPParameterTypeTableView
extends AphrontView {
private $types;
public function setHTTPParameterTypes(array $types) {
assert_instances_of($types, 'AphrontHTTPParameterType');
$this->types = $types;
return $this;
}
public function getHTTPParameterTypes() {
return $this->types;
}
public function render() {
$types = $this->getHTTPParameterTypes();
$types = mpull($types, null, 'getTypeName');
$br = phutil_tag('br');
$rows = array();
foreach ($types as $name => $type) {
$formats = $type->getFormatDescriptions();
$formats = phutil_implode_html($br, $formats);
$examples = $type->getExamples();
$examples = phutil_implode_html($br, $examples);
$rows[] = array(
$name,
$formats,
$examples,
);
}
$table = id(new AphrontTableView($rows))
->setHeaders(
array(
pht('Type'),
pht('Formats'),
pht('Examples'),
))
->setColumnClasses(
array(
'pri top',
'top',
'wide top prewrap',
));
return $table;
}
}

View file

@ -194,35 +194,29 @@ abstract class PhabricatorEditField extends Phobject {
} }
protected function getValueExistsInSubmit(AphrontRequest $request, $key) { protected function getValueExistsInSubmit(AphrontRequest $request, $key) {
return $request->getExists($key); return $this->getHTTPParameterType()->getExists($request, $key);
} }
protected function getValueFromSubmit(AphrontRequest $request, $key) { protected function getValueFromSubmit(AphrontRequest $request, $key) {
return $request->getStr($key); return $this->getHTTPParameterType()->getValue($request, $key);
} }
protected function getDefaultValue() { protected function getDefaultValue() {
return null; return $this->getHTTPParameterType()->getDefaultValue();
} }
protected function getListFromRequest( final public function getHTTPParameterType() {
AphrontRequest $request, $type = $this->newHTTPParameterType();
$key) {
$list = $request->getArr($key, null); if ($type) {
if ($list === null) { $type->setViewer($this->getViewer());
$list = $request->getStrList($key);
} }
if (!$list) { return $type;
return array();
} }
return $list; protected function newHTTPParameterType() {
} return new AphrontStringHTTPParameterType();
public function getHTTPParameterType() {
return 'string';
} }
public function setEditTypeKey($edit_type_key) { public function setEditTypeKey($edit_type_key) {
@ -290,7 +284,7 @@ abstract class PhabricatorEditField extends Phobject {
id(new PhabricatorSimpleEditType()) id(new PhabricatorSimpleEditType())
->setEditType($type_key) ->setEditType($type_key)
->setTransactionType($transaction_type) ->setTransactionType($transaction_type)
->setValueType($this->getHTTPParameterType()) ->setValueType($this->getHTTPParameterType()->getTypeName())
->setDescription($this->getDescription()) ->setDescription($this->getDescription())
->setMetadata($this->metadata), ->setMetadata($this->metadata),
); );

View file

@ -51,8 +51,8 @@ final class PhabricatorPolicyEditField
return $control; return $control;
} }
public function getHTTPParameterType() { protected function newHTTPParameterType() {
return 'phid'; return new AphrontPHIDHTTPParameterType();
} }
} }

View file

@ -22,8 +22,8 @@ final class PhabricatorSelectEditField
->setOptions($this->getOptions()); ->setOptions($this->getOptions());
} }
public function getHTTPParameterType() { protected function newHTTPParameterType() {
return 'select'; return new AphrontSelectHTTPParameterType();
} }
} }

View file

@ -9,8 +9,8 @@ final class PhabricatorSpaceEditField
return null; return null;
} }
public function getHTTPParameterType() { protected function newHTTPParameterType() {
return 'phid'; return new AphrontPHIDHTTPParameterType();
} }
} }

View file

@ -18,11 +18,6 @@ abstract class PhabricatorTokenizerEditField
return $control; return $control;
} }
public function setOriginalValue(array $value) {
$this->originalValue = $value;
return $this;
}
public function setValue($value) { public function setValue($value) {
$this->originalValue = $value; $this->originalValue = $value;
return parent::setValue($value); return parent::setValue($value);
@ -33,11 +28,7 @@ abstract class PhabricatorTokenizerEditField
// correctly is easier? // correctly is easier?
$this->originalValue = $request->getArr($key.'.original'); $this->originalValue = $request->getArr($key.'.original');
return $this->getListFromRequest($request, $key); return parent::getValueFromSubmit($request, $key);
}
protected function getDefaultValue() {
return array();
} }
protected function getValueForTransaction() { protected function getValueForTransaction() {
@ -87,8 +78,8 @@ abstract class PhabricatorTokenizerEditField
return $new; return $new;
} }
public function getHTTPParameterType() { protected function newHTTPParameterType() {
return 'list<phid>'; return new AphrontPHIDListHTTPParameterType();
} }
} }

View file

@ -43,7 +43,7 @@ final class PhabricatorApplicationEditHTTPParameterHelpView
if ($type === null) { if ($type === null) {
unset($fields[$key]); unset($fields[$key]);
} }
$types[$type][] = $field; $types[$type->getTypeName()] = $type;
} }
$intro = pht(<<<EOTEXT $intro = pht(<<<EOTEXT
@ -95,7 +95,7 @@ EOTEXT
$rows[] = array( $rows[] = array(
$field->getLabel(), $field->getLabel(),
$field->getKey(), $field->getKey(),
$field->getHTTPParameterType(), $field->getHTTPParameterType()->getTypeName(),
$field->getDescription(), $field->getDescription(),
); );
} }
@ -234,75 +234,8 @@ shows how to format values for each field type.
EOTEXT EOTEXT
); );
// TODO: This should be formalized and modularized. $types_table = id(new PhabricatorHTTPParameterTypeTableView())
$type_spec = array( ->setHTTPParameterTypes($types);
'string' => array(
'format' => pht('URL encoded text.'),
'examples' => array(
'v=simple',
'v=properly%20escaped%20text',
),
),
'select' => array(
'format' => pht('Value from allowed set.'),
'examples' => array(
'v=value',
),
),
'list<phid>' => array(
'format' => array(
pht('Comma-separated list of PHIDs.'),
pht('List of PHIDs, as array.'),
),
'examples' => array(
'v=PHID-XXXX-1111,PHID-XXXX-2222',
'v[]=PHID-XXXX-1111&v[]=PHID-XXXX-2222',
),
),
'phid' => array(
'format' => pht('Single PHID.'),
'examples' => pht('v=PHID-XXX-1111'),
),
);
$rows = array();
$br = phutil_tag('br');
foreach ($types as $type => $fields) {
$spec = idx($type_spec, $type, array());
$field_list = mpull($fields, 'getKey');
$field_list = phutil_implode_html($br, $field_list);
$format_list = idx($spec, 'format', array());
$format_list = phutil_implode_html($br, (array)$format_list);
$example_list = idx($spec, 'examples', array());
$example_list = phutil_implode_html($br, (array)$example_list);
$rows[] = array(
$type,
$field_list,
$format_list,
$example_list,
);
}
$types_table = id(new AphrontTableView($rows))
->setNoDataString(pht('This object has no fields with types.'))
->setHeaders(
array(
pht('Type'),
pht('Fields'),
pht('Formats'),
pht('Examples'),
))
->setColumnClasses(
array(
'pri top',
'top',
'top',
'wide top prewrap',
));
return array( return array(
$this->renderInstructions($intro), $this->renderInstructions($intro),