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 @@ -135,6 +135,7 @@ 'AphrontFormView' => 'view/form/AphrontFormView.php', 'AphrontGlyphBarView' => 'view/widget/bars/AphrontGlyphBarView.php', 'AphrontHTMLResponse' => 'aphront/response/AphrontHTMLResponse.php', + 'AphrontHTTPParameterType' => 'aphront/httpparametertype/AphrontHTTPParameterType.php', 'AphrontHTTPProxyResponse' => 'aphront/response/AphrontHTTPProxyResponse.php', 'AphrontHTTPSink' => 'aphront/sink/AphrontHTTPSink.php', 'AphrontHTTPSinkTestCase' => 'aphront/sink/__tests__/AphrontHTTPSinkTestCase.php', @@ -149,6 +150,8 @@ 'AphrontMultiColumnView' => 'view/layout/AphrontMultiColumnView.php', 'AphrontMySQLDatabaseConnectionTestCase' => 'infrastructure/storage/__tests__/AphrontMySQLDatabaseConnectionTestCase.php', 'AphrontNullView' => 'view/AphrontNullView.php', + 'AphrontPHIDHTTPParameterType' => 'aphront/httpparametertype/AphrontPHIDHTTPParameterType.php', + 'AphrontPHIDListHTTPParameterType' => 'aphront/httpparametertype/AphrontPHIDListHTTPParameterType.php', 'AphrontPHPHTTPSink' => 'aphront/sink/AphrontPHPHTTPSink.php', 'AphrontPageView' => 'view/page/AphrontPageView.php', 'AphrontPlainTextResponse' => 'aphront/response/AphrontPlainTextResponse.php', @@ -164,10 +167,13 @@ 'AphrontResponseProducerInterface' => 'aphront/interface/AphrontResponseProducerInterface.php', 'AphrontRoutingMap' => 'aphront/site/AphrontRoutingMap.php', 'AphrontRoutingResult' => 'aphront/site/AphrontRoutingResult.php', + 'AphrontSelectHTTPParameterType' => 'aphront/httpparametertype/AphrontSelectHTTPParameterType.php', 'AphrontSideNavFilterView' => 'view/layout/AphrontSideNavFilterView.php', 'AphrontSite' => 'aphront/site/AphrontSite.php', 'AphrontStackTraceView' => 'view/widget/AphrontStackTraceView.php', 'AphrontStandaloneHTMLResponse' => 'aphront/response/AphrontStandaloneHTMLResponse.php', + 'AphrontStringHTTPParameterType' => 'aphront/httpparametertype/AphrontStringHTTPParameterType.php', + 'AphrontStringListHTTPParameterType' => 'aphront/httpparametertype/AphrontStringListHTTPParameterType.php', 'AphrontTableView' => 'view/control/AphrontTableView.php', 'AphrontTagView' => 'view/AphrontTagView.php', 'AphrontTokenizerTemplateView' => 'view/control/AphrontTokenizerTemplateView.php', @@ -1889,6 +1895,7 @@ 'PhabricatorConfigEntryQuery' => 'applications/config/query/PhabricatorConfigEntryQuery.php', 'PhabricatorConfigFileSource' => 'infrastructure/env/PhabricatorConfigFileSource.php', 'PhabricatorConfigGroupController' => 'applications/config/controller/PhabricatorConfigGroupController.php', + 'PhabricatorConfigHTTPParameterTypesModule' => 'applications/config/module/PhabricatorConfigHTTPParameterTypesModule.php', 'PhabricatorConfigHistoryController' => 'applications/config/controller/PhabricatorConfigHistoryController.php', 'PhabricatorConfigIgnoreController' => 'applications/config/controller/PhabricatorConfigIgnoreController.php', 'PhabricatorConfigIssueListController' => 'applications/config/controller/PhabricatorConfigIssueListController.php', @@ -2250,6 +2257,7 @@ 'PhabricatorGlobalLock' => 'infrastructure/util/PhabricatorGlobalLock.php', 'PhabricatorGlobalUploadTargetView' => 'applications/files/view/PhabricatorGlobalUploadTargetView.php', 'PhabricatorGoogleAuthProvider' => 'applications/auth/provider/PhabricatorGoogleAuthProvider.php', + 'PhabricatorHTTPParameterTypeTableView' => 'applications/config/view/PhabricatorHTTPParameterTypeTableView.php', 'PhabricatorHandleList' => 'applications/phid/handle/pool/PhabricatorHandleList.php', 'PhabricatorHandleObjectSelectorDataView' => 'applications/phid/handle/view/PhabricatorHandleObjectSelectorDataView.php', 'PhabricatorHandlePool' => 'applications/phid/handle/pool/PhabricatorHandlePool.php', @@ -3866,6 +3874,7 @@ 'AphrontFormView' => 'AphrontView', 'AphrontGlyphBarView' => 'AphrontBarView', 'AphrontHTMLResponse' => 'AphrontResponse', + 'AphrontHTTPParameterType' => 'Phobject', 'AphrontHTTPProxyResponse' => 'AphrontResponse', 'AphrontHTTPSink' => 'Phobject', 'AphrontHTTPSinkTestCase' => 'PhabricatorTestCase', @@ -3880,6 +3889,8 @@ 'AphrontMultiColumnView' => 'AphrontView', 'AphrontMySQLDatabaseConnectionTestCase' => 'PhabricatorTestCase', 'AphrontNullView' => 'AphrontView', + 'AphrontPHIDHTTPParameterType' => 'AphrontHTTPParameterType', + 'AphrontPHIDListHTTPParameterType' => 'AphrontHTTPParameterType', 'AphrontPHPHTTPSink' => 'AphrontHTTPSink', 'AphrontPageView' => 'AphrontView', 'AphrontPlainTextResponse' => 'AphrontResponse', @@ -3897,10 +3908,13 @@ 'AphrontResponse' => 'Phobject', 'AphrontRoutingMap' => 'Phobject', 'AphrontRoutingResult' => 'Phobject', + 'AphrontSelectHTTPParameterType' => 'AphrontHTTPParameterType', 'AphrontSideNavFilterView' => 'AphrontView', 'AphrontSite' => 'Phobject', 'AphrontStackTraceView' => 'AphrontView', 'AphrontStandaloneHTMLResponse' => 'AphrontHTMLResponse', + 'AphrontStringHTTPParameterType' => 'AphrontHTTPParameterType', + 'AphrontStringListHTTPParameterType' => 'AphrontHTTPParameterType', 'AphrontTableView' => 'AphrontView', 'AphrontTagView' => 'AphrontView', 'AphrontTokenizerTemplateView' => 'AphrontView', @@ -5889,6 +5903,7 @@ 'PhabricatorConfigEntryQuery' => 'PhabricatorCursorPagedPolicyAwareQuery', 'PhabricatorConfigFileSource' => 'PhabricatorConfigProxySource', 'PhabricatorConfigGroupController' => 'PhabricatorConfigController', + 'PhabricatorConfigHTTPParameterTypesModule' => 'PhabricatorConfigModule', 'PhabricatorConfigHistoryController' => 'PhabricatorConfigController', 'PhabricatorConfigIgnoreController' => 'PhabricatorConfigController', 'PhabricatorConfigIssueListController' => 'PhabricatorConfigController', @@ -6312,6 +6327,7 @@ 'PhabricatorGlobalLock' => 'PhutilLock', 'PhabricatorGlobalUploadTargetView' => 'AphrontView', 'PhabricatorGoogleAuthProvider' => 'PhabricatorOAuth2AuthProvider', + 'PhabricatorHTTPParameterTypeTableView' => 'AphrontView', 'PhabricatorHandleList' => array( 'Phobject', 'Iterator', diff --git a/src/aphront/httpparametertype/AphrontHTTPParameterType.php b/src/aphront/httpparametertype/AphrontHTTPParameterType.php new file mode 100644 --- /dev/null +++ b/src/aphront/httpparametertype/AphrontHTTPParameterType.php @@ -0,0 +1,309 @@ +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`. + * + * @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 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 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 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". + * + * 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 Human-readable list of acceptable formats. + * @task impl + */ + abstract protected function getParameterFormatDescriptions(); + + + /** + * Return a human-readable list of examples. + * + * See @{method:getExamples}. + * + * @return list 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; + } + +} diff --git a/src/aphront/httpparametertype/AphrontPHIDHTTPParameterType.php b/src/aphront/httpparametertype/AphrontPHIDHTTPParameterType.php new file mode 100644 --- /dev/null +++ b/src/aphront/httpparametertype/AphrontPHIDHTTPParameterType.php @@ -0,0 +1,26 @@ +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', + ); + } + +} diff --git a/src/aphront/httpparametertype/AphrontPHIDListHTTPParameterType.php b/src/aphront/httpparametertype/AphrontPHIDListHTTPParameterType.php new file mode 100644 --- /dev/null +++ b/src/aphront/httpparametertype/AphrontPHIDListHTTPParameterType.php @@ -0,0 +1,30 @@ +getValueWithType($type, $request, $key); + } + + protected function getParameterTypeName() { + return 'list'; + } + + 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', + ); + } + +} diff --git a/src/aphront/httpparametertype/AphrontSelectHTTPParameterType.php b/src/aphront/httpparametertype/AphrontSelectHTTPParameterType.php new file mode 100644 --- /dev/null +++ b/src/aphront/httpparametertype/AphrontSelectHTTPParameterType.php @@ -0,0 +1,26 @@ +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', + ); + } + +} diff --git a/src/aphront/httpparametertype/AphrontStringHTTPParameterType.php b/src/aphront/httpparametertype/AphrontStringHTTPParameterType.php new file mode 100644 --- /dev/null +++ b/src/aphront/httpparametertype/AphrontStringHTTPParameterType.php @@ -0,0 +1,27 @@ +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', + ); + } + +} diff --git a/src/aphront/httpparametertype/AphrontStringListHTTPParameterType.php b/src/aphront/httpparametertype/AphrontStringListHTTPParameterType.php new file mode 100644 --- /dev/null +++ b/src/aphront/httpparametertype/AphrontStringListHTTPParameterType.php @@ -0,0 +1,38 @@ +getArr($key, null); + + if ($list === null) { + $list = $request->getStrList($key); + } + + return $list; + } + + protected function getParameterDefault() { + return array(); + } + + protected function getParameterTypeName() { + return 'list'; + } + + 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', + ); + } + +} diff --git a/src/applications/config/module/PhabricatorConfigHTTPParameterTypesModule.php b/src/applications/config/module/PhabricatorConfigHTTPParameterTypesModule.php new file mode 100644 --- /dev/null +++ b/src/applications/config/module/PhabricatorConfigHTTPParameterTypesModule.php @@ -0,0 +1,27 @@ +getViewer(); + + $types = AphrontHTTPParameterType::getAllTypes(); + + $table = id(new PhabricatorHTTPParameterTypeTableView()) + ->setHTTPParameterTypes($types); + + return id(new PHUIObjectBoxView()) + ->setHeaderText(pht('HTTP Parameter Types')) + ->setTable($table); + } + +} diff --git a/src/applications/config/view/PhabricatorHTTPParameterTypeTableView.php b/src/applications/config/view/PhabricatorHTTPParameterTypeTableView.php new file mode 100644 --- /dev/null +++ b/src/applications/config/view/PhabricatorHTTPParameterTypeTableView.php @@ -0,0 +1,56 @@ +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; + } + +} diff --git a/src/applications/transactions/editfield/PhabricatorEditField.php b/src/applications/transactions/editfield/PhabricatorEditField.php --- a/src/applications/transactions/editfield/PhabricatorEditField.php +++ b/src/applications/transactions/editfield/PhabricatorEditField.php @@ -194,35 +194,29 @@ } protected function getValueExistsInSubmit(AphrontRequest $request, $key) { - return $request->getExists($key); + return $this->getHTTPParameterType()->getExists($request, $key); } protected function getValueFromSubmit(AphrontRequest $request, $key) { - return $request->getStr($key); + return $this->getHTTPParameterType()->getValue($request, $key); } protected function getDefaultValue() { - return null; + return $this->getHTTPParameterType()->getDefaultValue(); } - protected function getListFromRequest( - AphrontRequest $request, - $key) { + final public function getHTTPParameterType() { + $type = $this->newHTTPParameterType(); - $list = $request->getArr($key, null); - if ($list === null) { - $list = $request->getStrList($key); + if ($type) { + $type->setViewer($this->getViewer()); } - if (!$list) { - return array(); - } - - return $list; + return $type; } - public function getHTTPParameterType() { - return 'string'; + protected function newHTTPParameterType() { + return new AphrontStringHTTPParameterType(); } public function setEditTypeKey($edit_type_key) { @@ -290,7 +284,7 @@ id(new PhabricatorSimpleEditType()) ->setEditType($type_key) ->setTransactionType($transaction_type) - ->setValueType($this->getHTTPParameterType()) + ->setValueType($this->getHTTPParameterType()->getTypeName()) ->setDescription($this->getDescription()) ->setMetadata($this->metadata), ); diff --git a/src/applications/transactions/editfield/PhabricatorPolicyEditField.php b/src/applications/transactions/editfield/PhabricatorPolicyEditField.php --- a/src/applications/transactions/editfield/PhabricatorPolicyEditField.php +++ b/src/applications/transactions/editfield/PhabricatorPolicyEditField.php @@ -51,8 +51,8 @@ return $control; } - public function getHTTPParameterType() { - return 'phid'; + protected function newHTTPParameterType() { + return new AphrontPHIDHTTPParameterType(); } } diff --git a/src/applications/transactions/editfield/PhabricatorSelectEditField.php b/src/applications/transactions/editfield/PhabricatorSelectEditField.php --- a/src/applications/transactions/editfield/PhabricatorSelectEditField.php +++ b/src/applications/transactions/editfield/PhabricatorSelectEditField.php @@ -22,8 +22,8 @@ ->setOptions($this->getOptions()); } - public function getHTTPParameterType() { - return 'select'; + protected function newHTTPParameterType() { + return new AphrontSelectHTTPParameterType(); } } diff --git a/src/applications/transactions/editfield/PhabricatorSpaceEditField.php b/src/applications/transactions/editfield/PhabricatorSpaceEditField.php --- a/src/applications/transactions/editfield/PhabricatorSpaceEditField.php +++ b/src/applications/transactions/editfield/PhabricatorSpaceEditField.php @@ -9,8 +9,8 @@ return null; } - public function getHTTPParameterType() { - return 'phid'; + protected function newHTTPParameterType() { + return new AphrontPHIDHTTPParameterType(); } } diff --git a/src/applications/transactions/editfield/PhabricatorTokenizerEditField.php b/src/applications/transactions/editfield/PhabricatorTokenizerEditField.php --- a/src/applications/transactions/editfield/PhabricatorTokenizerEditField.php +++ b/src/applications/transactions/editfield/PhabricatorTokenizerEditField.php @@ -18,11 +18,6 @@ return $control; } - public function setOriginalValue(array $value) { - $this->originalValue = $value; - return $this; - } - public function setValue($value) { $this->originalValue = $value; return parent::setValue($value); @@ -33,11 +28,7 @@ // correctly is easier? $this->originalValue = $request->getArr($key.'.original'); - return $this->getListFromRequest($request, $key); - } - - protected function getDefaultValue() { - return array(); + return parent::getValueFromSubmit($request, $key); } protected function getValueForTransaction() { @@ -87,8 +78,8 @@ return $new; } - public function getHTTPParameterType() { - return 'list'; + protected function newHTTPParameterType() { + return new AphrontPHIDListHTTPParameterType(); } } diff --git a/src/applications/transactions/view/PhabricatorApplicationEditHTTPParameterHelpView.php b/src/applications/transactions/view/PhabricatorApplicationEditHTTPParameterHelpView.php --- a/src/applications/transactions/view/PhabricatorApplicationEditHTTPParameterHelpView.php +++ b/src/applications/transactions/view/PhabricatorApplicationEditHTTPParameterHelpView.php @@ -43,7 +43,7 @@ if ($type === null) { unset($fields[$key]); } - $types[$type][] = $field; + $types[$type->getTypeName()] = $type; } $intro = pht(<<getLabel(), $field->getKey(), - $field->getHTTPParameterType(), + $field->getHTTPParameterType()->getTypeName(), $field->getDescription(), ); } @@ -234,75 +234,8 @@ EOTEXT ); - // TODO: This should be formalized and modularized. - $type_spec = array( - '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' => 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', - )); + $types_table = id(new PhabricatorHTTPParameterTypeTableView()) + ->setHTTPParameterTypes($types); return array( $this->renderInstructions($intro),