Page MenuHomestyx hydra

No OneTemporary

diff --git a/src/docs/user/configuration/custom_fields.diviner b/src/docs/user/configuration/custom_fields.diviner
index 5879931317..fb036d11d9 100644
--- a/src/docs/user/configuration/custom_fields.diviner
+++ b/src/docs/user/configuration/custom_fields.diviner
@@ -1,216 +1,222 @@
@title Configuring Custom Fields
@group config
How to add custom fields to applications which support them.
= Overview =
Several Phorge applications allow the configuration of custom fields. These
fields allow you to add more information to objects, and in some cases reorder
or remove builtin fields.
For example, you could use custom fields to add an "Estimated Hours" field to
tasks, a "Lead" field to projects, or a "T-Shirt Size" field to users.
These applications currently support custom fields:
| Application | Support |
|-------------|---------|
| Differential | Partial Support |
| Diffusion | Limited Support |
| Maniphest | Full Support |
| Owners | Full Support |
| People | Full Support |
| Projects | Full Support |
Custom fields can appear in many interfaces and support search, editing, and
other features.
= Basic Custom Fields =
To get started with custom fields, you can use configuration to select and
reorder fields and to add new simple fields.
If you don't need complicated display controls or sophisticated validation,
these simple fields should cover most use cases. They allow you to attach
things like strings, numbers, and dropdown menus to objects.
The relevant configuration settings are:
| Application | Add Fields | Select Fields |
|-------------|------------|---------------|
| Differential | Planned | `differential.fields` |
| Diffusion | Planned | Planned |
| Maniphest | `maniphest.custom-field-definitions` | `maniphest.fields` |
| Owners | `owners.custom-field-definitions` | `owners.fields` |
| People | `user.custom-field-definitions` | `user.fields` |
| Projects | `projects.custom-field-definitions` | `projects.fields` |
When adding fields, you'll specify a JSON blob like this (for example, as the
value of `maniphest.custom-field-definitions`):
{
"mycompany:estimated-hours": {
"name": "Estimated Hours",
"type": "int",
"caption": "Estimated number of hours this will take.",
"required": true
},
"mycompany:actual-hours": {
"name": "Actual Hours",
"type": "int",
"caption": "Actual number of hours this took."
},
"mycompany:company-jobs": {
"name": "Job Role",
"type": "select",
"options": {
"mycompany:engineer": "Engineer",
"mycompany:nonengineer": "Other"
}
},
"mycompany:favorite-dinosaur": {
"name": "Favorite Dinosaur",
"type": "text"
}
}
The fields will then appear in the other config option for the application
(for example, in `maniphest.fields`) and you can enable, disable, or reorder
them.
For details on how to define a field, see the next section.
= Custom Field Configuration =
When defining custom fields using a configuration option like
`maniphest.custom-field-definitions`, these options are available:
- **name**: Display label for the field on the edit and detail interfaces.
- **description**: Optional text shown when managing the field.
- **type**: Field type. The supported field types are:
- **int**: An integer, rendered as a text field.
- **text**: A string, rendered as a text field.
- **bool**: A boolean value, rendered as a checkbox.
- **select**: Allows the user to select from several options as defined
by **options**, rendered as a dropdown.
- **remarkup**: A text area which allows the user to enter markup.
- **users**: A typeahead which allows multiple users to be input.
- **date**: A date/time picker.
- **header**: Renders a visual divider which you can use to group fields.
- **link**: A text field which allows the user to enter a link.
- **edit**: Show this field on the application's edit interface (this
defaults to `true`).
- **view**: Show this field on the application's view interface (this
defaults to `true`). (Note: Empty fields are not shown.)
- **search**: Show this field on the application's search interface, allowing
users to filter objects by the field value.
- **fulltext**: Index the text in this field as part of the object's global
full-text index. This allows users to find the object by searching for
the field's contents using global search.
- **caption**: A caption to display underneath the field (optional).
- **required**: True if the user should be required to provide a value.
- **options**: If type is set to **select**, provide options for the dropdown
as a dictionary.
- **default**: Default field value.
- **strings**: Allows you to override specific strings based on the field
type. See below.
- **instructions**: Optional block of remarkup text which will appear
above the control when rendered on the edit view.
- **placeholder**: A placeholder text that appears on text boxes. Only
supported in text, int and remarkup fields (optional).
+ - **list**: If set to `icon`, `attribute` or `byline`, the value of the field
+ will be shown in list-view.
+ - **list.icon**: If `list` is set to `icon`, use this icon. These are the
+ same icons that can be used in the `{icon}` syntax for Remarkup.
+ - **list.label**: When rendering value in a list, use this label (instead of
+ `name`).
- **copy**: If true, this field's value will be copied when an object is
created using another object as a template.
- **limit**: For control types which use a tokenizer control to let the user
select a list of values, this limits how many values can be selected. For
example, a "users" field with a limit of "1" will behave like the "Owner"
field in Maniphest and only allow selection of a single user.
The `strings` value supports different strings per control type. They are:
- **bool**
- **edit.checkbox** Text for the edit interface, no default.
- **view.yes** Text for the view interface, defaults to "Yes".
- **search.default** Text for the search interface, defaults to "(Any)".
- **search.require** Text for the search interface, defaults to "Require".
Internally, Phorge implements some additional custom field types and
options. These are not intended for general use and are subject to abrupt
change, but are documented here for completeness:
- **Credentials**: Controls with type `credential` allow selection of a
Passphrase credential which provides `credential.provides`, and creation
of credentials of `credential.type`.
- **Datasource**: Controls with type `datasource` allow selection of tokens
from an arbitrary datasource, controlled with `datasource.class` and
`datasource.parameters`.
= Advanced Custom Fields =
If you want custom fields to have advanced behaviors (sophisticated rendering,
advanced validation, complicated controls, interaction with other systems, etc),
you can write a custom field as an extension and add it to Phorge.
NOTE: This API is somewhat new and fairly large. You should expect that there
will be occasional changes to the API requiring minor updates in your code.
To do this, extend the appropriate `CustomField` class for the application you
want to add a field to:
| Application | Extend |
|-------------|---------|
| Differential | @{class:DifferentialCustomField} |
| Diffusion | @{class:PhabricatorCommitCustomField} |
| Maniphest | @{class:ManiphestCustomField} |
| Owners | @{class:PhabricatorOwnersCustomField} |
| People | @{class:PhabricatorUserCustomField} |
| Projects | @{class:PhabricatorProjectCustomField} |
The easiest way to get started is to drop your subclass into
`phorge/src/extensions/`. If Phorge is configured in development
mode, the class should immediately be available in the UI. If not, you can
restart Phorge (for help, see @{article:Restarting Phorge}).
For example, this is a simple template which adds a custom field to Maniphest:
name=ExampleManiphestCustomField.php
<?php
final class ExampleCustomField extends ManiphestCustomField {
public function getFieldKey() {
return 'example:test';
}
public function shouldAppearInPropertyView() {
return true;
}
public function renderPropertyViewLabel() {
return pht('Example Custom Field');
}
public function renderPropertyViewValue(array $handles) {
return phutil_tag(
'h1',
array(
'style' => 'color: #ff00ff',
),
pht('It worked!'));
}
}
Broadly, you can then add features by overriding more methods and implementing
them. Many of the native fields are implemented on the custom field
architecture, and it may be useful to look at them. For details on available
integrations, see the base class for your application and
@{class:PhabricatorCustomField}.
= Next Steps =
Continue by:
- learning more about extending Phorge with custom code in
@{article@contrib:Adding New Classes};
- or returning to the @{article: Configuration Guide}.
diff --git a/src/infrastructure/customfield/standard/PhabricatorStandardCustomField.php b/src/infrastructure/customfield/standard/PhabricatorStandardCustomField.php
index 87cde00801..8ed6e0f42a 100644
--- a/src/infrastructure/customfield/standard/PhabricatorStandardCustomField.php
+++ b/src/infrastructure/customfield/standard/PhabricatorStandardCustomField.php
@@ -1,521 +1,596 @@
<?php
abstract class PhabricatorStandardCustomField
extends PhabricatorCustomField {
private $rawKey;
private $fieldKey;
private $fieldName;
private $fieldValue;
private $fieldDescription;
private $fieldConfig;
private $applicationField;
private $strings = array();
private $caption;
private $fieldError;
private $required;
private $default;
private $isCopyable;
private $hasStorageValue;
private $isBuiltin;
private $isEnabled = true;
abstract public function getFieldType();
public static function buildStandardFields(
PhabricatorCustomField $template,
array $config,
$builtin = false) {
$types = id(new PhutilClassMapQuery())
->setAncestorClass(__CLASS__)
->setUniqueMethod('getFieldType')
->execute();
$fields = array();
foreach ($config as $key => $value) {
$type = idx($value, 'type', 'text');
if (empty($types[$type])) {
// TODO: We should have better typechecking somewhere, and then make
// this more serious.
continue;
}
$namespace = $template->getStandardCustomFieldNamespace();
$full_key = "std:{$namespace}:{$key}";
$template = clone $template;
$standard = id(clone $types[$type])
->setRawStandardFieldKey($key)
->setFieldKey($full_key)
->setFieldConfig($value)
->setApplicationField($template);
if ($builtin) {
$standard->setIsBuiltin(true);
}
$field = $template->setProxy($standard);
$fields[] = $field;
}
return $fields;
}
public function setApplicationField(
PhabricatorStandardCustomFieldInterface $application_field) {
$this->applicationField = $application_field;
return $this;
}
public function getApplicationField() {
return $this->applicationField;
}
public function setFieldName($name) {
$this->fieldName = $name;
return $this;
}
public function getFieldValue() {
return $this->fieldValue;
}
public function setFieldValue($value) {
$this->fieldValue = $value;
return $this;
}
public function setCaption($caption) {
$this->caption = $caption;
return $this;
}
public function getCaption() {
return $this->caption;
}
public function setFieldDescription($description) {
$this->fieldDescription = $description;
return $this;
}
public function setIsBuiltin($is_builtin) {
$this->isBuiltin = $is_builtin;
return $this;
}
public function getIsBuiltin() {
return $this->isBuiltin;
}
public function setFieldConfig(array $config) {
foreach ($config as $key => $value) {
switch ($key) {
case 'name':
$this->setFieldName($value);
break;
case 'description':
$this->setFieldDescription($value);
break;
case 'strings':
$this->setStrings($value);
break;
case 'caption':
$this->setCaption($value);
break;
case 'required':
if ($value) {
$this->setRequired($value);
$this->setFieldError(true);
}
break;
case 'default':
$this->setFieldValue($value);
break;
case 'copy':
$this->setIsCopyable($value);
break;
case 'type':
// We set this earlier on.
break;
}
}
$this->fieldConfig = $config;
return $this;
}
public function getFieldConfigValue($key, $default = null) {
return idx($this->fieldConfig, $key, $default);
}
public function setFieldError($field_error) {
$this->fieldError = $field_error;
return $this;
}
public function getFieldError() {
return $this->fieldError;
}
public function setRequired($required) {
$this->required = $required;
return $this;
}
public function getRequired() {
return $this->required;
}
public function setRawStandardFieldKey($raw_key) {
$this->rawKey = $raw_key;
return $this;
}
public function getRawStandardFieldKey() {
return $this->rawKey;
}
public function setIsEnabled($is_enabled) {
$this->isEnabled = $is_enabled;
return $this;
}
public function getIsEnabled() {
return $this->isEnabled;
}
public function isFieldEnabled() {
return $this->getIsEnabled();
}
/* -( PhabricatorCustomField )--------------------------------------------- */
public function setFieldKey($field_key) {
$this->fieldKey = $field_key;
return $this;
}
public function getFieldKey() {
return $this->fieldKey;
}
public function getFieldName() {
return coalesce($this->fieldName, parent::getFieldName());
}
public function getFieldDescription() {
return coalesce($this->fieldDescription, parent::getFieldDescription());
}
public function setStrings(array $strings) {
$this->strings = $strings;
return;
}
public function getString($key, $default = null) {
return idx($this->strings, $key, $default);
}
public function setIsCopyable($is_copyable) {
$this->isCopyable = $is_copyable;
return $this;
}
public function getIsCopyable() {
return $this->isCopyable;
}
public function shouldUseStorage() {
try {
$object = $this->newStorageObject();
return true;
} catch (PhabricatorCustomFieldImplementationIncompleteException $ex) {
return false;
}
}
public function getValueForStorage() {
return $this->getFieldValue();
}
public function setValueFromStorage($value) {
return $this->setFieldValue($value);
}
public function didSetValueFromStorage() {
$this->hasStorageValue = true;
return $this;
}
public function getOldValueForApplicationTransactions() {
if ($this->hasStorageValue) {
return $this->getValueForStorage();
} else {
return null;
}
}
public function shouldAppearInApplicationTransactions() {
return true;
}
public function shouldAppearInEditView() {
return $this->getFieldConfigValue('edit', true);
}
public function readValueFromRequest(AphrontRequest $request) {
$value = $request->getStr($this->getFieldKey());
if (!phutil_nonempty_string($value)) {
$value = null;
}
$this->setFieldValue($value);
}
public function getInstructionsForEdit() {
return $this->getFieldConfigValue('instructions');
}
public function getPlaceholder() {
return $this->getFieldConfigValue('placeholder', null);
}
public function renderEditControl(array $handles) {
return id(new AphrontFormTextControl())
->setName($this->getFieldKey())
->setCaption($this->getCaption())
->setValue($this->getFieldValue())
->setError($this->getFieldError())
->setLabel($this->getFieldName())
->setPlaceholder($this->getPlaceholder());
}
public function newStorageObject() {
return $this->getApplicationField()->newStorageObject();
}
public function shouldAppearInPropertyView() {
return $this->getFieldConfigValue('view', true);
}
public function renderPropertyViewValue(array $handles) {
+ return $this->renderValue();
+ }
+
+ protected function renderValue() {
// If your field needs to render anything more complicated then a string,
// then you should override this method.
$value_str = phutil_string_cast($this->getFieldValue());
if (phutil_nonempty_string($value_str)) {
return $value_str;
}
return null;
}
+ public function shouldAppearInListView() {
+ return $this->getFieldConfigValue('list', false);
+ }
+
+ public function getStyleForListItemView() {
+ return $this->getFieldConfigValue('list');
+ }
+
+ public function renderListItemValue() {
+ return $this->renderValue();
+ }
+
+ private function isValue($something) {
+ if (is_object($something)) {
+ return true;
+ }
+ return phutil_nonempty_scalar($something);
+ }
+
+ public function getValueForListItem() {
+ $style = $this->getStyleForListItemView();
+ $value = $this->renderListItemValue();
+ if (!$this->isValue($value) || !$style) {
+ return null;
+ }
+ switch ($style) {
+ case 'icon':
+ // maybe expose 'list.icon.alt' for hover stuff?
+ // also icon's "label", and other features supported by
+ // PHUIObjectItemView::addIcon().
+ return 'fa-'.$this->getFieldConfigValue('list.icon');
+ case 'attribute':
+ case 'byline':
+ $label = $this->getFieldConfigValue(
+ 'list.label',
+ $this->getFieldName());
+ if (phutil_nonempty_string($label)) {
+ return pht('%s: %s', $label, $value);
+ }
+ return $value;
+ default:
+ throw new Exception(
+ pht(
+ "Unknown field list-item view style '%s'; valid styles are ".
+ "'%s', '%s'and '%s'.",
+ $style,
+ 'icon',
+ 'attribute',
+ 'byline'));
+ }
+ }
+
+ public function renderOnListItem(PHUIObjectItemView $view) {
+ $value = $this->getValueForListItem();
+ if (!$this->isValue($value)) {
+ return;
+ }
+
+ switch ($this->getStyleForListItemView()) {
+ case 'icon':
+ $view->addIcon($value);
+ break;
+ case 'attribute':
+ $view->addAttribute($value);
+ break;
+ case 'byline':
+ $view->addByline($value);
+ break;
+ }
+ }
+
public function shouldAppearInApplicationSearch() {
return $this->getFieldConfigValue('search', false);
}
protected function newStringIndexStorage() {
return $this->getApplicationField()->newStringIndexStorage();
}
protected function newNumericIndexStorage() {
return $this->getApplicationField()->newNumericIndexStorage();
}
public function buildFieldIndexes() {
return array();
}
public function buildOrderIndex() {
return null;
}
public function readApplicationSearchValueFromRequest(
PhabricatorApplicationSearchEngine $engine,
AphrontRequest $request) {
return;
}
public function applyApplicationSearchConstraintToQuery(
PhabricatorApplicationSearchEngine $engine,
PhabricatorCursorPagedPolicyAwareQuery $query,
$value) {
return;
}
public function appendToApplicationSearchForm(
PhabricatorApplicationSearchEngine $engine,
AphrontFormView $form,
$value) {
return;
}
public function validateApplicationTransactions(
PhabricatorApplicationTransactionEditor $editor,
$type,
array $xactions) {
$this->setFieldError(null);
$errors = parent::validateApplicationTransactions(
$editor,
$type,
$xactions);
if ($this->getRequired()) {
$value = $this->getOldValueForApplicationTransactions();
$transaction = null;
foreach ($xactions as $xaction) {
$value = $xaction->getNewValue();
if (!$this->isValueEmpty($value)) {
$transaction = $xaction;
break;
}
}
if ($this->isValueEmpty($value)) {
$error = new PhabricatorApplicationTransactionValidationError(
$type,
pht('Required'),
pht('%s is required.', $this->getFieldName()),
$transaction);
$error->setIsMissingFieldError(true);
$errors[] = $error;
$this->setFieldError(pht('Required'));
}
}
return $errors;
}
protected function isValueEmpty($value) {
if (is_array($value)) {
return empty($value);
}
return $value === null || !strlen($value);
}
public function getApplicationTransactionTitle(
PhabricatorApplicationTransaction $xaction) {
$author_phid = $xaction->getAuthorPHID();
$old = $xaction->getOldValue();
$new = $xaction->getNewValue();
if (!$old) {
return pht(
'%s set %s to %s.',
$xaction->renderHandleLink($author_phid),
$this->getFieldName(),
$new);
} else if (!$new) {
return pht(
'%s removed %s.',
$xaction->renderHandleLink($author_phid),
$this->getFieldName());
} else {
return pht(
'%s changed %s from %s to %s.',
$xaction->renderHandleLink($author_phid),
$this->getFieldName(),
$old,
$new);
}
}
public function getApplicationTransactionTitleForFeed(
PhabricatorApplicationTransaction $xaction) {
$author_phid = $xaction->getAuthorPHID();
$object_phid = $xaction->getObjectPHID();
$old = $xaction->getOldValue();
$new = $xaction->getNewValue();
if (!$old) {
return pht(
'%s set %s to %s on %s.',
$xaction->renderHandleLink($author_phid),
$this->getFieldName(),
$new,
$xaction->renderHandleLink($object_phid));
} else if (!$new) {
return pht(
'%s removed %s on %s.',
$xaction->renderHandleLink($author_phid),
$this->getFieldName(),
$xaction->renderHandleLink($object_phid));
} else {
return pht(
'%s changed %s from %s to %s on %s.',
$xaction->renderHandleLink($author_phid),
$this->getFieldName(),
$old,
$new,
$xaction->renderHandleLink($object_phid));
}
}
public function getHeraldFieldValue() {
return $this->getFieldValue();
}
public function getFieldControlID($key = null) {
$key = coalesce($key, $this->getRawStandardFieldKey());
return 'std:control:'.$key;
}
public function shouldAppearInGlobalSearch() {
return $this->getFieldConfigValue('fulltext', false);
}
public function updateAbstractDocument(
PhabricatorSearchAbstractDocument $document) {
$field_key = $this->getFieldConfigValue('fulltext');
// If the caller or configuration didn't specify a valid field key,
// generate one automatically from the field index.
if (!is_string($field_key) || (strlen($field_key) != 4)) {
$field_key = '!'.substr($this->getFieldIndex(), 0, 3);
}
$field_value = $this->getFieldValue();
if (($field_value !== null) && (strlen($field_value))) {
$document->addField($field_key, $field_value);
}
}
protected function newStandardEditField() {
$short = $this->getModernFieldKey();
return parent::newStandardEditField()
->setEditTypeKey($short)
->setIsCopyable($this->getIsCopyable());
}
public function shouldAppearInConduitTransactions() {
return true;
}
public function shouldAppearInConduitDictionary() {
return true;
}
public function getModernFieldKey() {
if ($this->getIsBuiltin()) {
return $this->getRawStandardFieldKey();
} else {
return 'custom.'.$this->getRawStandardFieldKey();
}
}
public function getConduitDictionaryValue() {
return $this->getFieldValue();
}
public function newExportData() {
return $this->getFieldValue();
}
}
diff --git a/src/infrastructure/customfield/standard/PhabricatorStandardCustomFieldBlueprints.php b/src/infrastructure/customfield/standard/PhabricatorStandardCustomFieldBlueprints.php
index ad2bb62d81..95c3c1a3e2 100644
--- a/src/infrastructure/customfield/standard/PhabricatorStandardCustomFieldBlueprints.php
+++ b/src/infrastructure/customfield/standard/PhabricatorStandardCustomFieldBlueprints.php
@@ -1,41 +1,41 @@
<?php
final class PhabricatorStandardCustomFieldBlueprints
extends PhabricatorStandardCustomFieldTokenizer {
public function getFieldType() {
return 'blueprints';
}
public function getDatasource() {
return new DrydockBlueprintDatasource();
}
public function applyApplicationTransactionExternalEffects(
PhabricatorApplicationTransaction $xaction) {
$old = $this->decodeValue($xaction->getOldValue());
$new = $this->decodeValue($xaction->getNewValue());
DrydockAuthorization::applyAuthorizationChanges(
$this->getViewer(),
$xaction->getObjectPHID(),
$old,
$new);
}
- public function renderPropertyViewValue(array $handles) {
+ protected function renderValue() {
$value = $this->getFieldValue();
if (!$value) {
return phutil_tag('em', array(), pht('No authorized blueprints.'));
}
return id(new DrydockObjectAuthorizationView())
->setUser($this->getViewer())
->setObjectPHID($this->getObject()->getPHID())
->setBlueprintPHIDs($value);
}
}
diff --git a/src/infrastructure/customfield/standard/PhabricatorStandardCustomFieldBool.php b/src/infrastructure/customfield/standard/PhabricatorStandardCustomFieldBool.php
index f1d1371a7d..a6bdbeb955 100644
--- a/src/infrastructure/customfield/standard/PhabricatorStandardCustomFieldBool.php
+++ b/src/infrastructure/customfield/standard/PhabricatorStandardCustomFieldBool.php
@@ -1,148 +1,148 @@
<?php
final class PhabricatorStandardCustomFieldBool
extends PhabricatorStandardCustomField {
public function getFieldType() {
return 'bool';
}
public function buildFieldIndexes() {
$indexes = array();
$value = $this->getFieldValue();
if (strlen($value)) {
$indexes[] = $this->newNumericIndex((int)$value);
}
return $indexes;
}
public function buildOrderIndex() {
return $this->newNumericIndex(0);
}
public function readValueFromRequest(AphrontRequest $request) {
$this->setFieldValue((bool)$request->getBool($this->getFieldKey()));
}
public function getValueForStorage() {
$value = $this->getFieldValue();
if ($value !== null) {
return (int)$value;
} else {
return null;
}
}
public function setValueFromStorage($value) {
- if (strlen($value)) {
+ if (phutil_nonempty_scalar($value)) {
$value = (bool)$value;
} else {
$value = null;
}
return $this->setFieldValue($value);
}
public function readApplicationSearchValueFromRequest(
PhabricatorApplicationSearchEngine $engine,
AphrontRequest $request) {
return $request->getStr($this->getFieldKey());
}
public function applyApplicationSearchConstraintToQuery(
PhabricatorApplicationSearchEngine $engine,
PhabricatorCursorPagedPolicyAwareQuery $query,
$value) {
if ($value == 'require') {
$query->withApplicationSearchContainsConstraint(
$this->newNumericIndex(null),
1);
}
}
public function appendToApplicationSearchForm(
PhabricatorApplicationSearchEngine $engine,
AphrontFormView $form,
$value) {
$form->appendChild(
id(new AphrontFormSelectControl())
->setLabel($this->getFieldName())
->setName($this->getFieldKey())
->setValue($value)
->setOptions(
array(
'' => $this->getString('search.default', pht('(Any)')),
'require' => $this->getString('search.require', pht('Require')),
)));
}
public function renderEditControl(array $handles) {
return id(new AphrontFormCheckboxControl())
->setLabel($this->getFieldName())
->setCaption($this->getCaption())
->addCheckbox(
$this->getFieldKey(),
1,
$this->getString('edit.checkbox'),
(bool)$this->getFieldValue());
}
- public function renderPropertyViewValue(array $handles) {
+ protected function renderValue() {
$value = $this->getFieldValue();
if ($value) {
return $this->getString('view.yes', pht('Yes'));
} else {
return null;
}
}
public function getApplicationTransactionTitle(
PhabricatorApplicationTransaction $xaction) {
$author_phid = $xaction->getAuthorPHID();
$old = $xaction->getOldValue();
$new = $xaction->getNewValue();
if ($new) {
return pht(
'%s checked %s.',
$xaction->renderHandleLink($author_phid),
$this->getFieldName());
} else {
return pht(
'%s unchecked %s.',
$xaction->renderHandleLink($author_phid),
$this->getFieldName());
}
}
public function shouldAppearInHerald() {
return true;
}
public function getHeraldFieldConditions() {
return array(
HeraldAdapter::CONDITION_IS_TRUE,
HeraldAdapter::CONDITION_IS_FALSE,
);
}
public function getHeraldFieldStandardType() {
return HeraldField::STANDARD_BOOL;
}
protected function getHTTPParameterType() {
return new AphrontBoolHTTPParameterType();
}
protected function newConduitSearchParameterType() {
return new ConduitBoolParameterType();
}
protected function newConduitEditParameterType() {
return new ConduitBoolParameterType();
}
}
diff --git a/src/infrastructure/customfield/standard/PhabricatorStandardCustomFieldCredential.php b/src/infrastructure/customfield/standard/PhabricatorStandardCustomFieldCredential.php
index c2f958f228..feb3732791 100644
--- a/src/infrastructure/customfield/standard/PhabricatorStandardCustomFieldCredential.php
+++ b/src/infrastructure/customfield/standard/PhabricatorStandardCustomFieldCredential.php
@@ -1,151 +1,151 @@
<?php
final class PhabricatorStandardCustomFieldCredential
extends PhabricatorStandardCustomField {
public function getFieldType() {
return 'credential';
}
public function buildFieldIndexes() {
$indexes = array();
$value = $this->getFieldValue();
if (strlen($value)) {
$indexes[] = $this->newStringIndex($value);
}
return $indexes;
}
public function renderEditControl(array $handles) {
$provides_type = $this->getFieldConfigValue('credential.provides');
$credential_type = $this->getFieldConfigValue('credential.type');
$all_types = PassphraseCredentialType::getAllProvidesTypes();
if (!in_array($provides_type, $all_types)) {
$provides_type = PassphrasePasswordCredentialType::PROVIDES_TYPE;
}
$credentials = id(new PassphraseCredentialQuery())
->setViewer($this->getViewer())
->withIsDestroyed(false)
->withProvidesTypes(array($provides_type))
->execute();
return id(new PassphraseCredentialControl())
->setViewer($this->getViewer())
->setLabel($this->getFieldName())
->setName($this->getFieldKey())
->setCaption($this->getCaption())
->setAllowNull(!$this->getRequired())
->setCredentialType($credential_type)
->setValue($this->getFieldValue())
->setError($this->getFieldError())
->setOptions($credentials);
}
public function getRequiredHandlePHIDsForPropertyView() {
$value = $this->getFieldValue();
if ($value) {
return array($value);
}
return array();
}
- public function renderPropertyViewValue(array $handles) {
+ protected function renderValue() {
$value = $this->getFieldValue();
if ($value) {
- return $handles[$value]->renderLink();
+ return $this->getViewer()->renderHandle($value);
}
return null;
}
public function validateApplicationTransactions(
PhabricatorApplicationTransactionEditor $editor,
$type,
array $xactions) {
$errors = parent::validateApplicationTransactions(
$editor,
$type,
$xactions);
$ok = PassphraseCredentialControl::validateTransactions(
$this->getViewer(),
$xactions);
if (!$ok) {
foreach ($xactions as $xaction) {
$errors[] = new PhabricatorApplicationTransactionValidationError(
$type,
pht('Invalid'),
pht(
'The selected credential does not exist, or you do not have '.
'permission to use it.'),
$xaction);
$this->setFieldError(pht('Invalid'));
}
}
return $errors;
}
public function getApplicationTransactionRequiredHandlePHIDs(
PhabricatorApplicationTransaction $xaction) {
$phids = array();
$old = $xaction->getOldValue();
$new = $xaction->getNewValue();
if ($old) {
$phids[] = $old;
}
if ($new) {
$phids[] = $new;
}
return $phids;
}
public function getApplicationTransactionTitle(
PhabricatorApplicationTransaction $xaction) {
$author_phid = $xaction->getAuthorPHID();
$old = $xaction->getOldValue();
$new = $xaction->getNewValue();
if ($old && !$new) {
return pht(
'%s removed %s as %s.',
$xaction->renderHandleLink($author_phid),
$xaction->renderHandleLink($old),
$this->getFieldName());
} else if ($new && !$old) {
return pht(
'%s set %s to %s.',
$xaction->renderHandleLink($author_phid),
$this->getFieldName(),
$xaction->renderHandleLink($new));
} else {
return pht(
'%s changed %s from %s to %s.',
$xaction->renderHandleLink($author_phid),
$this->getFieldName(),
$xaction->renderHandleLink($old),
$xaction->renderHandleLink($new));
}
}
protected function getHTTPParameterType() {
return new AphrontPHIDHTTPParameterType();
}
protected function newConduitSearchParameterType() {
return new ConduitPHIDParameterType();
}
protected function newConduitEditParameterType() {
return new ConduitPHIDParameterType();
}
}
diff --git a/src/infrastructure/customfield/standard/PhabricatorStandardCustomFieldDate.php b/src/infrastructure/customfield/standard/PhabricatorStandardCustomFieldDate.php
index c437df82fb..389b440e46 100644
--- a/src/infrastructure/customfield/standard/PhabricatorStandardCustomFieldDate.php
+++ b/src/infrastructure/customfield/standard/PhabricatorStandardCustomFieldDate.php
@@ -1,250 +1,250 @@
<?php
final class PhabricatorStandardCustomFieldDate
extends PhabricatorStandardCustomField {
public function getFieldType() {
return 'date';
}
public function buildFieldIndexes() {
$indexes = array();
$value = $this->getFieldValue();
if (phutil_nonempty_scalar($value)) {
$indexes[] = $this->newNumericIndex((int)$value);
}
return $indexes;
}
public function buildOrderIndex() {
return $this->newNumericIndex(0);
}
public function getValueForStorage() {
$value = $this->getFieldValue();
if (phutil_nonempty_scalar($value)) {
return (int)$value;
} else {
return null;
}
}
public function setValueFromStorage($value) {
if (phutil_nonempty_scalar($value)) {
$value = (int)$value;
} else {
$value = null;
}
return $this->setFieldValue($value);
}
public function renderEditControl(array $handles) {
return $this->newDateControl();
}
public function readValueFromRequest(AphrontRequest $request) {
$control = $this->newDateControl();
$control->setUser($request->getUser());
$value = $control->readValueFromRequest($request);
$this->setFieldValue($value);
}
- public function renderPropertyViewValue(array $handles) {
+ protected function renderValue() {
$value = $this->getFieldValue();
if (!$value) {
return null;
}
return phabricator_datetime($value, $this->getViewer());
}
private function newDateControl() {
$control = id(new AphrontFormDateControl())
->setLabel($this->getFieldName())
->setName($this->getFieldKey())
->setUser($this->getViewer())
->setCaption($this->getCaption())
->setAllowNull(!$this->getRequired());
// If the value is already numeric, treat it as an epoch timestamp and set
// it directly. Otherwise, it's likely a field default, which we let users
// specify as a string. Parse the string into an epoch.
$value = $this->getFieldValue();
if ($value !== null && gettype($value) !== 'integer' &&
!ctype_digit($value)) {
$value = PhabricatorTime::parseLocalTime($value, $this->getViewer());
}
// If we don't have anything valid, make sure we pass `null`, since the
// control special-cases that.
$control->setValue(nonempty($value, null));
return $control;
}
public function readApplicationSearchValueFromRequest(
PhabricatorApplicationSearchEngine $engine,
AphrontRequest $request) {
$key = $this->getFieldKey();
return array(
'min' => $request->getStr($key.'.min'),
'max' => $request->getStr($key.'.max'),
);
}
public function applyApplicationSearchConstraintToQuery(
PhabricatorApplicationSearchEngine $engine,
PhabricatorCursorPagedPolicyAwareQuery $query,
$value) {
$viewer = $this->getViewer();
if (!is_array($value)) {
$value = array();
}
$min_str = idx($value, 'min', '');
if (phutil_nonempty_string($min_str)) {
$min = PhabricatorTime::parseLocalTime($min_str, $viewer);
} else {
$min = null;
}
$max_str = idx($value, 'max', '');
if (phutil_nonempty_string($max_str)) {
$max = PhabricatorTime::parseLocalTime($max_str, $viewer);
} else {
$max = null;
}
if (($min !== null) || ($max !== null)) {
$query->withApplicationSearchRangeConstraint(
$this->newNumericIndex(null),
$min,
$max);
}
}
public function appendToApplicationSearchForm(
PhabricatorApplicationSearchEngine $engine,
AphrontFormView $form,
$value) {
if (!is_array($value)) {
$value = array();
}
$form
->appendChild(
id(new AphrontFormTextControl())
->setLabel(pht('%s After', $this->getFieldName()))
->setName($this->getFieldKey().'.min')
->setValue(idx($value, 'min', '')))
->appendChild(
id(new AphrontFormTextControl())
->setLabel(pht('%s Before', $this->getFieldName()))
->setName($this->getFieldKey().'.max')
->setValue(idx($value, 'max', '')));
}
public function getApplicationTransactionTitle(
PhabricatorApplicationTransaction $xaction) {
$author_phid = $xaction->getAuthorPHID();
$old = $xaction->getOldValue();
$new = $xaction->getNewValue();
$viewer = $this->getViewer();
$old_date = null;
if ($old) {
$old_date = phabricator_datetime($old, $viewer);
}
$new_date = null;
if ($new) {
$new_date = phabricator_datetime($new, $viewer);
}
if (!$old) {
return pht(
'%s set %s to %s.',
$xaction->renderHandleLink($author_phid),
$this->getFieldName(),
$new_date);
} else if (!$new && $old) {
return pht(
'%s removed %s which was set to %s.',
$xaction->renderHandleLink($author_phid),
$this->getFieldName(),
$old_date);
} else if (!$new) {
return pht(
'%s removed %s.',
$xaction->renderHandleLink($author_phid),
$this->getFieldName());
} else {
return pht(
'%s changed %s from %s to %s.',
$xaction->renderHandleLink($author_phid),
$this->getFieldName(),
$old_date,
$new_date);
}
}
public function getApplicationTransactionTitleForFeed(
PhabricatorApplicationTransaction $xaction) {
$viewer = $this->getViewer();
$author_phid = $xaction->getAuthorPHID();
$object_phid = $xaction->getObjectPHID();
$old = $xaction->getOldValue();
$new = $xaction->getNewValue();
if (!$old) {
return pht(
'%s set %s to %s on %s.',
$xaction->renderHandleLink($author_phid),
$this->getFieldName(),
phabricator_datetime($new, $viewer),
$xaction->renderHandleLink($object_phid));
} else if (!$new) {
return pht(
'%s removed %s on %s.',
$xaction->renderHandleLink($author_phid),
$this->getFieldName(),
$xaction->renderHandleLink($object_phid));
} else {
return pht(
'%s changed %s from %s to %s on %s.',
$xaction->renderHandleLink($author_phid),
$this->getFieldName(),
phabricator_datetime($old, $viewer),
phabricator_datetime($new, $viewer),
$xaction->renderHandleLink($object_phid));
}
}
protected function newConduitSearchParameterType() {
// TODO: Build a new "pair<epoch|null, epoch|null>" type or similar.
return null;
}
protected function newConduitEditParameterType() {
return id(new ConduitEpochParameterType())
->setAllowNull(!$this->getRequired());
}
protected function newExportFieldType() {
return new PhabricatorEpochExportField();
}
}
diff --git a/src/infrastructure/customfield/standard/PhabricatorStandardCustomFieldHeader.php b/src/infrastructure/customfield/standard/PhabricatorStandardCustomFieldHeader.php
index beb92f9aed..2bd8fea40f 100644
--- a/src/infrastructure/customfield/standard/PhabricatorStandardCustomFieldHeader.php
+++ b/src/infrastructure/customfield/standard/PhabricatorStandardCustomFieldHeader.php
@@ -1,41 +1,41 @@
<?php
final class PhabricatorStandardCustomFieldHeader
extends PhabricatorStandardCustomField {
public function getFieldType() {
return 'header';
}
public function renderEditControl(array $handles) {
$header = phutil_tag(
'div',
array(
'class' => 'phabricator-standard-custom-field-header',
),
$this->getFieldName());
return id(new AphrontFormStaticControl())
->setValue($header);
}
public function shouldUseStorage() {
return false;
}
public function getStyleForPropertyView() {
return 'header';
}
- public function renderPropertyViewValue(array $handles) {
+ protected function renderValue() {
return $this->getFieldName();
}
public function shouldAppearInApplicationSearch() {
return false;
}
public function shouldAppearInConduitTransactions() {
return false;
}
}
diff --git a/src/infrastructure/customfield/standard/PhabricatorStandardCustomFieldLink.php b/src/infrastructure/customfield/standard/PhabricatorStandardCustomFieldLink.php
index a5a1af5013..833b110968 100644
--- a/src/infrastructure/customfield/standard/PhabricatorStandardCustomFieldLink.php
+++ b/src/infrastructure/customfield/standard/PhabricatorStandardCustomFieldLink.php
@@ -1,109 +1,109 @@
<?php
final class PhabricatorStandardCustomFieldLink
extends PhabricatorStandardCustomField {
public function getFieldType() {
return 'link';
}
public function buildFieldIndexes() {
$indexes = array();
$value = $this->getFieldValue();
if (strlen($value)) {
$indexes[] = $this->newStringIndex($value);
}
return $indexes;
}
- public function renderPropertyViewValue(array $handles) {
+ protected function renderValue() {
$value = $this->getFieldValue();
if (!phutil_nonempty_string($value)) {
return null;
}
if (!PhabricatorEnv::isValidRemoteURIForLink($value)) {
return $value;
}
return phutil_tag(
'a',
array(
'href' => $value,
'target' => '_blank',
'rel' => 'noreferrer',
),
$value);
}
public function readApplicationSearchValueFromRequest(
PhabricatorApplicationSearchEngine $engine,
AphrontRequest $request) {
return $request->getStr($this->getFieldKey());
}
public function applyApplicationSearchConstraintToQuery(
PhabricatorApplicationSearchEngine $engine,
PhabricatorCursorPagedPolicyAwareQuery $query,
$value) {
if (is_string($value) && !strlen($value)) {
return;
}
$value = (array)$value;
if ($value) {
$query->withApplicationSearchContainsConstraint(
$this->newStringIndex(null),
$value);
}
}
public function appendToApplicationSearchForm(
PhabricatorApplicationSearchEngine $engine,
AphrontFormView $form,
$value) {
$form->appendChild(
id(new AphrontFormTextControl())
->setLabel($this->getFieldName())
->setName($this->getFieldKey())
->setValue($value));
}
public function shouldAppearInHerald() {
return true;
}
public function getHeraldFieldConditions() {
return array(
HeraldAdapter::CONDITION_CONTAINS,
HeraldAdapter::CONDITION_NOT_CONTAINS,
HeraldAdapter::CONDITION_IS,
HeraldAdapter::CONDITION_IS_NOT,
HeraldAdapter::CONDITION_REGEXP,
HeraldAdapter::CONDITION_NOT_REGEXP,
);
}
public function getHeraldFieldStandardType() {
return HeraldField::STANDARD_TEXT;
}
protected function getHTTPParameterType() {
return new AphrontStringHTTPParameterType();
}
protected function newConduitSearchParameterType() {
return new ConduitStringListParameterType();
}
protected function newConduitEditParameterType() {
return new ConduitStringParameterType();
}
}
diff --git a/src/infrastructure/customfield/standard/PhabricatorStandardCustomFieldPHIDs.php b/src/infrastructure/customfield/standard/PhabricatorStandardCustomFieldPHIDs.php
index e34c3913f0..153facd89f 100644
--- a/src/infrastructure/customfield/standard/PhabricatorStandardCustomFieldPHIDs.php
+++ b/src/infrastructure/customfield/standard/PhabricatorStandardCustomFieldPHIDs.php
@@ -1,270 +1,269 @@
<?php
/**
* Common code for standard field types which store lists of PHIDs.
*/
abstract class PhabricatorStandardCustomFieldPHIDs
extends PhabricatorStandardCustomField {
public function buildFieldIndexes() {
$indexes = array();
$value = $this->getFieldValue();
if (is_array($value)) {
foreach ($value as $phid) {
$indexes[] = $this->newStringIndex($phid);
}
}
return $indexes;
}
public function readValueFromRequest(AphrontRequest $request) {
$value = $request->getArr($this->getFieldKey());
$this->setFieldValue($value);
}
public function getValueForStorage() {
$value = $this->getFieldValue();
if (!$value) {
return null;
}
return json_encode(array_values($value));
}
public function setValueFromStorage($value) {
// NOTE: We're accepting either a JSON string (a real storage value) or
// an array (from HTTP parameter prefilling). This is a little hacky, but
// should hold until this can get cleaned up more thoroughly.
// TODO: Clean this up.
if (is_string($value) && phutil_nonempty_string($value)) {
$value = json_decode($value, true);
}
$this->setFieldValue($value);
return $this;
}
public function readApplicationSearchValueFromRequest(
PhabricatorApplicationSearchEngine $engine,
AphrontRequest $request) {
return $request->getArr($this->getFieldKey());
}
public function applyApplicationSearchConstraintToQuery(
PhabricatorApplicationSearchEngine $engine,
PhabricatorCursorPagedPolicyAwareQuery $query,
$value) {
if ($value) {
$query->withApplicationSearchContainsConstraint(
$this->newStringIndex(null),
$value);
}
}
public function getRequiredHandlePHIDsForPropertyView() {
$value = $this->getFieldValue();
if ($value) {
return $value;
}
return array();
}
- public function renderPropertyViewValue(array $handles) {
+ protected function renderValue() {
$value = $this->getFieldValue();
if (!$value) {
return null;
}
- $handles = mpull($handles, 'renderHovercardLink');
- $handles = phutil_implode_html(', ', $handles);
- return $handles;
+ return $this->getViewer()->renderHandleList($value)
+ ->setAsInline(true);
}
public function getRequiredHandlePHIDsForEdit() {
$value = $this->getFieldValue();
if ($value) {
return $value;
} else {
return array();
}
}
public function getApplicationTransactionRequiredHandlePHIDs(
PhabricatorApplicationTransaction $xaction) {
$old = $this->decodeValue($xaction->getOldValue());
$new = $this->decodeValue($xaction->getNewValue());
$add = array_diff($new, $old);
$rem = array_diff($old, $new);
return array_merge($add, $rem);
}
public function getApplicationTransactionTitle(
PhabricatorApplicationTransaction $xaction) {
$author_phid = $xaction->getAuthorPHID();
$old = $this->decodeValue($xaction->getOldValue());
$new = $this->decodeValue($xaction->getNewValue());
$add = array_diff($new, $old);
$rem = array_diff($old, $new);
if ($add && !$rem) {
return pht(
'%s updated %s, added %d: %s.',
$xaction->renderHandleLink($author_phid),
$this->getFieldName(),
phutil_count($add),
$xaction->renderHandleList($add));
} else if ($rem && !$add) {
return pht(
'%s updated %s, removed %s: %s.',
$xaction->renderHandleLink($author_phid),
$this->getFieldName(),
phutil_count($rem),
$xaction->renderHandleList($rem));
} else {
return pht(
'%s updated %s, added %s: %s; removed %s: %s.',
$xaction->renderHandleLink($author_phid),
$this->getFieldName(),
phutil_count($add),
$xaction->renderHandleList($add),
phutil_count($rem),
$xaction->renderHandleList($rem));
}
}
public function getApplicationTransactionTitleForFeed(
PhabricatorApplicationTransaction $xaction) {
$author_phid = $xaction->getAuthorPHID();
$object_phid = $xaction->getObjectPHID();
$old = $this->decodeValue($xaction->getOldValue());
$new = $this->decodeValue($xaction->getNewValue());
$add = array_diff($new, $old);
$rem = array_diff($old, $new);
if ($add && !$rem) {
return pht(
'%s updated %s for %s, added %d: %s.',
$xaction->renderHandleLink($author_phid),
$this->getFieldName(),
$xaction->renderHandleLink($object_phid),
phutil_count($add),
$xaction->renderHandleList($add));
} else if ($rem && !$add) {
return pht(
'%s updated %s for %s, removed %s: %s.',
$xaction->renderHandleLink($author_phid),
$this->getFieldName(),
$xaction->renderHandleLink($object_phid),
phutil_count($rem),
$xaction->renderHandleList($rem));
} else {
return pht(
'%s updated %s for %s, added %s: %s; removed %s: %s.',
$xaction->renderHandleLink($author_phid),
$this->getFieldName(),
$xaction->renderHandleLink($object_phid),
phutil_count($add),
$xaction->renderHandleList($add),
phutil_count($rem),
$xaction->renderHandleList($rem));
}
}
public function validateApplicationTransactions(
PhabricatorApplicationTransactionEditor $editor,
$type,
array $xactions) {
$errors = parent::validateApplicationTransactions(
$editor,
$type,
$xactions);
// If the user is adding PHIDs, make sure the new PHIDs are valid and
// visible to the actor. It's OK for a user to edit a field which includes
// some invalid or restricted values, but they can't add new ones.
foreach ($xactions as $xaction) {
$old = $this->decodeValue($xaction->getOldValue());
$new = $this->decodeValue($xaction->getNewValue());
$add = array_diff($new, $old);
$invalid = PhabricatorObjectQuery::loadInvalidPHIDsForViewer(
$editor->getActor(),
$add);
if ($invalid) {
$error = new PhabricatorApplicationTransactionValidationError(
$type,
pht('Invalid'),
pht(
'Some of the selected PHIDs in field "%s" are invalid or '.
'restricted: %s.',
$this->getFieldName(),
implode(', ', $invalid)),
$xaction);
$errors[] = $error;
$this->setFieldError(pht('Invalid'));
}
}
return $errors;
}
public function shouldAppearInHerald() {
return true;
}
public function getHeraldFieldConditions() {
return array(
HeraldAdapter::CONDITION_INCLUDE_ALL,
HeraldAdapter::CONDITION_INCLUDE_ANY,
HeraldAdapter::CONDITION_INCLUDE_NONE,
HeraldAdapter::CONDITION_EXISTS,
HeraldAdapter::CONDITION_NOT_EXISTS,
);
}
public function getHeraldFieldStandardType() {
return HeraldField::STANDARD_PHID_NULLABLE;
}
public function getHeraldFieldValue() {
// If the field has a `null` value, make sure we hand an `array()` to
// Herald.
$value = parent::getHeraldFieldValue();
if ($value) {
return $value;
}
return array();
}
protected function decodeValue($value) {
if ($value === null) {
return array();
}
$value = json_decode($value);
if (!is_array($value)) {
$value = array();
}
return $value;
}
protected function getHTTPParameterType() {
return new AphrontPHIDListHTTPParameterType();
}
}
diff --git a/src/infrastructure/customfield/standard/PhabricatorStandardCustomFieldRemarkup.php b/src/infrastructure/customfield/standard/PhabricatorStandardCustomFieldRemarkup.php
index 54f54c7503..cda1595aeb 100644
--- a/src/infrastructure/customfield/standard/PhabricatorStandardCustomFieldRemarkup.php
+++ b/src/infrastructure/customfield/standard/PhabricatorStandardCustomFieldRemarkup.php
@@ -1,114 +1,114 @@
<?php
final class PhabricatorStandardCustomFieldRemarkup
extends PhabricatorStandardCustomField {
public function getFieldType() {
return 'remarkup';
}
public function renderEditControl(array $handles) {
return id(new PhabricatorRemarkupControl())
->setUser($this->getViewer())
->setLabel($this->getFieldName())
->setName($this->getFieldKey())
->setCaption($this->getCaption())
->setValue($this->getFieldValue());
}
public function getStyleForPropertyView() {
return 'block';
}
public function getApplicationTransactionRemarkupBlocks(
PhabricatorApplicationTransaction $xaction) {
return array(
$xaction->getNewValue(),
);
}
- public function renderPropertyViewValue(array $handles) {
+ protected function renderValue() {
$value = $this->getFieldValue();
if (!phutil_nonempty_string($value)) {
return null;
}
// TODO: Once this stabilizes, it would be nice to let fields batch this.
// For now, an extra query here and there on object detail pages isn't the
// end of the world.
$viewer = $this->getViewer();
return new PHUIRemarkupView($viewer, $value);
}
public function getApplicationTransactionTitle(
PhabricatorApplicationTransaction $xaction) {
$author_phid = $xaction->getAuthorPHID();
return pht(
'%s edited %s.',
$xaction->renderHandleLink($author_phid),
$this->getFieldName());
}
public function getApplicationTransactionTitleForFeed(
PhabricatorApplicationTransaction $xaction) {
$author_phid = $xaction->getAuthorPHID();
$object_phid = $xaction->getObjectPHID();
return pht(
'%s edited %s on %s.',
$xaction->renderHandleLink($author_phid),
$this->getFieldName(),
$xaction->renderHandleLink($object_phid));
}
public function getApplicationTransactionHasChangeDetails(
PhabricatorApplicationTransaction $xaction) {
return true;
}
public function getApplicationTransactionChangeDetails(
PhabricatorApplicationTransaction $xaction,
PhabricatorUser $viewer) {
return $xaction->renderTextCorpusChangeDetails(
$viewer,
$xaction->getOldValue(),
$xaction->getNewValue());
}
public function shouldAppearInHerald() {
return true;
}
public function getHeraldFieldConditions() {
return array(
HeraldAdapter::CONDITION_CONTAINS,
HeraldAdapter::CONDITION_NOT_CONTAINS,
HeraldAdapter::CONDITION_IS,
HeraldAdapter::CONDITION_IS_NOT,
HeraldAdapter::CONDITION_REGEXP,
HeraldAdapter::CONDITION_NOT_REGEXP,
);
}
public function getHeraldFieldStandardType() {
return HeraldField::STANDARD_TEXT;
}
protected function getHTTPParameterType() {
return new AphrontStringHTTPParameterType();
}
public function shouldAppearInApplicationSearch() {
return false;
}
public function getConduitEditParameterType() {
return new ConduitStringParameterType();
}
protected function newExportFieldType() {
return new PhabricatorStringExportField();
}
}
diff --git a/src/infrastructure/customfield/standard/PhabricatorStandardCustomFieldSelect.php b/src/infrastructure/customfield/standard/PhabricatorStandardCustomFieldSelect.php
index 958d65d7e7..505eb19c5e 100644
--- a/src/infrastructure/customfield/standard/PhabricatorStandardCustomFieldSelect.php
+++ b/src/infrastructure/customfield/standard/PhabricatorStandardCustomFieldSelect.php
@@ -1,161 +1,161 @@
<?php
final class PhabricatorStandardCustomFieldSelect
extends PhabricatorStandardCustomField {
public function getFieldType() {
return 'select';
}
public function buildFieldIndexes() {
$indexes = array();
$value = $this->getFieldValue();
if (($value !== null) && (strlen($value))) {
$indexes[] = $this->newStringIndex($value);
}
return $indexes;
}
public function readApplicationSearchValueFromRequest(
PhabricatorApplicationSearchEngine $engine,
AphrontRequest $request) {
return $request->getArr($this->getFieldKey());
}
public function applyApplicationSearchConstraintToQuery(
PhabricatorApplicationSearchEngine $engine,
PhabricatorCursorPagedPolicyAwareQuery $query,
$value) {
if ($value) {
$query->withApplicationSearchContainsConstraint(
$this->newStringIndex(null),
$value);
}
}
public function appendToApplicationSearchForm(
PhabricatorApplicationSearchEngine $engine,
AphrontFormView $form,
$value) {
if (!is_array($value)) {
$value = array();
}
$value = array_fuse($value);
$control = id(new AphrontFormCheckboxControl())
->setLabel($this->getFieldName());
foreach ($this->getOptions() as $name => $option) {
$control->addCheckbox(
$this->getFieldKey().'[]',
$name,
$option,
isset($value[$name]));
}
$form->appendChild($control);
}
public function getOptions() {
return $this->getFieldConfigValue('options', array());
}
public function renderEditControl(array $handles) {
return id(new AphrontFormSelectControl())
->setLabel($this->getFieldName())
->setCaption($this->getCaption())
->setName($this->getFieldKey())
->setValue($this->getFieldValue())
->setOptions($this->getOptions());
}
- public function renderPropertyViewValue(array $handles) {
+ protected function renderValue() {
if (!phutil_nonempty_string($this->getFieldValue())) {
return null;
}
return idx($this->getOptions(), $this->getFieldValue());
}
public function getApplicationTransactionTitle(
PhabricatorApplicationTransaction $xaction) {
$author_phid = $xaction->getAuthorPHID();
$old = $xaction->getOldValue();
$new = $xaction->getNewValue();
$old = idx($this->getOptions(), $old, $old);
$new = idx($this->getOptions(), $new, $new);
if (!$old) {
return pht(
'%s set %s to %s.',
$xaction->renderHandleLink($author_phid),
$this->getFieldName(),
$new);
} else if (!$new) {
return pht(
'%s removed %s.',
$xaction->renderHandleLink($author_phid),
$this->getFieldName());
} else {
return pht(
'%s changed %s from %s to %s.',
$xaction->renderHandleLink($author_phid),
$this->getFieldName(),
$old,
$new);
}
}
public function shouldAppearInHerald() {
return true;
}
public function getHeraldFieldConditions() {
return array(
HeraldAdapter::CONDITION_IS_ANY,
HeraldAdapter::CONDITION_IS_NOT_ANY,
);
}
public function getHeraldFieldValueType($condition) {
$parameters = array(
'object' => get_class($this->getObject()),
'role' => PhabricatorCustomField::ROLE_HERALD,
'key' => $this->getFieldKey(),
);
$datasource = id(new PhabricatorStandardSelectCustomFieldDatasource())
->setParameters($parameters);
return id(new HeraldTokenizerFieldValue())
->setKey('custom.'.$this->getFieldKey())
->setDatasource($datasource)
->setValueMap($this->getOptions());
}
protected function getHTTPParameterType() {
return new AphrontSelectHTTPParameterType();
}
protected function newConduitSearchParameterType() {
return new ConduitStringListParameterType();
}
protected function newConduitEditParameterType() {
return new ConduitStringParameterType();
}
protected function newBulkParameterType() {
return id(new BulkSelectParameterType())
->setOptions($this->getOptions());
}
protected function newExportFieldType() {
return id(new PhabricatorOptionExportField())
->setOptions($this->getOptions());
}
}

File Metadata

Mime Type
text/x-diff
Expires
Fri, Nov 22, 9:08 PM (5 h, 40 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
966
Default Alt Text
(62 KB)

Event Timeline