Page MenuHomestyx hydra

No OneTemporary

diff --git a/src/infrastructure/customfield/field/PhabricatorCustomFieldList.php b/src/infrastructure/customfield/field/PhabricatorCustomFieldList.php
index bb1c445bec..bb030154d1 100644
--- a/src/infrastructure/customfield/field/PhabricatorCustomFieldList.php
+++ b/src/infrastructure/customfield/field/PhabricatorCustomFieldList.php
@@ -1,260 +1,263 @@
<?php
/**
* Convenience class to perform operations on an entire field list, like reading
* all values from storage.
*
* $field_list = new PhabricatorCustomFieldList($fields);
*
*/
final class PhabricatorCustomFieldList extends Phobject {
private $fields;
public function __construct(array $fields) {
assert_instances_of($fields, 'PhabricatorCustomField');
$this->fields = $fields;
}
public function getFields() {
return $this->fields;
}
public function setViewer(PhabricatorUser $viewer) {
foreach ($this->getFields() as $field) {
$field->setViewer($viewer);
}
return $this;
}
+
/**
* Read stored values for all fields which support storage.
*
* @param PhabricatorCustomFieldInterface Object to read field values for.
* @return void
*/
public function readFieldsFromStorage(
PhabricatorCustomFieldInterface $object) {
$keys = array();
foreach ($this->fields as $field) {
if ($field->shouldEnableForRole(PhabricatorCustomField::ROLE_STORAGE)) {
$keys[$field->getFieldIndex()] = $field;
}
}
if (!$keys) {
return;
}
// NOTE: We assume all fields share the same storage. This isn't guaranteed
// to be true, but always is for now.
$table = head($keys)->newStorageObject();
$objects = array();
if ($object->getPHID()) {
$objects = $table->loadAllWhere(
'objectPHID = %s AND fieldIndex IN (%Ls)',
$object->getPHID(),
array_keys($keys));
$objects = mpull($objects, null, 'getFieldIndex');
}
foreach ($keys as $key => $field) {
$storage = idx($objects, $key);
if ($storage) {
$field->setValueFromStorage($storage->getFieldValue());
- } else {
+ } else if ($object->getPHID()) {
+ // NOTE: We set this only if the object exists. Otherwise, we allow the
+ // field to retain any default value it may have.
$field->setValueFromStorage(null);
}
}
}
public function appendFieldsToForm(AphrontFormView $form) {
foreach ($this->fields as $field) {
if ($field->shouldEnableForRole(PhabricatorCustomField::ROLE_EDIT)) {
$form->appendChild($field->renderEditControl());
}
}
}
public function appendFieldsToPropertyList(
PhabricatorCustomFieldInterface $object,
PhabricatorUser $viewer,
PhabricatorPropertyListView $view) {
$this->readFieldsFromStorage($object);
$fields = $this->fields;
foreach ($fields as $field) {
$field->setViewer($viewer);
}
// Move all the blocks to the end, regardless of their configuration order,
// because it always looks silly to render a block in the middle of a list
// of properties.
$head = array();
$tail = array();
foreach ($fields as $key => $field) {
$style = $field->getStyleForPropertyView();
switch ($style) {
case 'property':
case 'header':
$head[$key] = $field;
break;
case 'block':
$tail[$key] = $field;
break;
default:
throw new Exception(
"Unknown field property view style '{$style}'; valid styles are ".
"'block' and 'property'.");
}
}
$fields = $head + $tail;
$add_header = null;
foreach ($fields as $field) {
$label = $field->renderPropertyViewLabel();
$value = $field->renderPropertyViewValue();
if ($value !== null) {
switch ($field->getStyleForPropertyView()) {
case 'header':
// We want to hide headers if the fields the're assciated with
// don't actually produce any visible properties. For example, in a
// list like this:
//
// Header A
// Prop A: Value A
// Header B
// Prop B: Value B
//
// ...if the "Prop A" field returns `null` when rendering its
// property value and we rendered naively, we'd get this:
//
// Header A
// Header B
// Prop B: Value B
//
// This is silly. Instead, we hide "Header A".
$add_header = $value;
break;
case 'property':
if ($add_header !== null) {
// Add the most recently seen header.
$view->addSectionHeader($add_header);
$add_header = null;
}
$view->addProperty($label, $value);
break;
case 'block':
$view->invokeWillRenderEvent();
if ($label !== null) {
$view->addSectionHeader($label);
}
$view->addTextContent($value);
break;
}
}
}
}
public function buildFieldTransactionsFromRequest(
PhabricatorApplicationTransaction $template,
AphrontRequest $request) {
$xactions = array();
$role = PhabricatorCustomField::ROLE_APPLICATIONTRANSACTIONS;
foreach ($this->fields as $field) {
if (!$field->shouldEnableForRole($role)) {
continue;
}
$old_value = $field->getOldValueForApplicationTransactions();
$field->readValueFromRequest($request);
$xaction = id(clone $template)
->setTransactionType(PhabricatorTransactions::TYPE_CUSTOMFIELD)
->setMetadataValue('customfield:key', $field->getFieldKey())
->setOldValue($old_value)
->setNewValue($field->getNewValueForApplicationTransactions());
$xactions[] = $xaction;
}
return $xactions;
}
/**
* Publish field indexes into index tables, so ApplicationSearch can search
* them.
*
* @return void
*/
public function rebuildIndexes(PhabricatorCustomFieldInterface $object) {
$indexes = array();
$index_keys = array();
$phid = $object->getPHID();
$role = PhabricatorCustomField::ROLE_APPLICATIONSEARCH;
foreach ($this->fields as $field) {
if (!$field->shouldEnableForRole($role)) {
continue;
}
$index_keys[$field->getFieldIndex()] = true;
foreach ($field->buildFieldIndexes() as $index) {
$index->setObjectPHID($phid);
$indexes[$index->getTableName()][] = $index;
}
}
if (!$indexes) {
return;
}
$any_index = head(head($indexes));
$conn_w = $any_index->establishConnection('w');
foreach ($indexes as $table => $index_list) {
$sql = array();
foreach ($index_list as $index) {
$sql[] = $index->formatForInsert($conn_w);
}
$indexes[$table] = $sql;
}
$any_index->openTransaction();
foreach ($indexes as $table => $sql_list) {
queryfx(
$conn_w,
'DELETE FROM %T WHERE objectPHID = %s AND indexKey IN (%Ls)',
$table,
$phid,
array_keys($index_keys));
if (!$sql_list) {
continue;
}
foreach (PhabricatorLiskDAO::chunkSQL($sql_list) as $chunk) {
queryfx(
$conn_w,
'INSERT INTO %T (objectPHID, indexKey, indexValue) VALUES %Q',
$table,
$chunk);
}
}
$any_index->saveTransaction();
}
}
diff --git a/src/infrastructure/customfield/standard/PhabricatorStandardCustomField.php b/src/infrastructure/customfield/standard/PhabricatorStandardCustomField.php
index 7d73234655..2f79bfd22b 100644
--- a/src/infrastructure/customfield/standard/PhabricatorStandardCustomField.php
+++ b/src/infrastructure/customfield/standard/PhabricatorStandardCustomField.php
@@ -1,300 +1,303 @@
<?php
abstract class PhabricatorStandardCustomField
extends PhabricatorCustomField {
private $fieldKey;
private $fieldName;
private $fieldValue;
private $fieldDescription;
private $fieldConfig;
private $applicationField;
private $strings;
private $caption;
private $fieldError;
private $required;
private $default;
abstract public function getFieldType();
public static function buildStandardFields(
PhabricatorCustomField $template,
array $config) {
$types = id(new PhutilSymbolLoader())
->setAncestorClass(__CLASS__)
->loadObjects();
$types = mpull($types, null, 'getFieldType');
$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])
->setFieldKey($full_key)
->setFieldConfig($value)
->setApplicationField($template);
$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 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':
$this->setRequired($value);
$this->setFieldError(true);
break;
+ case 'default':
+ $this->setFieldValue($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;
}
/* -( 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 shouldUseStorage() {
return true;
}
public function getValueForStorage() {
return $this->getFieldValue();
}
public function setValueFromStorage($value) {
return $this->setFieldValue($value);
}
public function shouldAppearInApplicationTransactions() {
return true;
}
public function shouldAppearInEditView() {
return $this->getFieldConfigValue('edit', true);
}
public function readValueFromRequest(AphrontRequest $request) {
$value = $request->getStr($this->getFieldKey());
if (!strlen($value)) {
$value = null;
}
$this->setFieldValue($value);
}
public function renderEditControl() {
return id(new AphrontFormTextControl())
->setName($this->getFieldKey())
->setCaption($this->getCaption())
->setValue($this->getFieldValue())
->setError($this->getFieldError())
->setLabel($this->getFieldName());
}
public function newStorageObject() {
return $this->getApplicationField()->newStorageObject();
}
public function shouldAppearInPropertyView() {
return $this->getFieldConfigValue('view', true);
}
public function renderPropertyViewValue() {
return $this->getFieldValue();
}
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 readApplicationSearchValueFromRequest(
PhabricatorApplicationSearchEngine $engine,
AphrontRequest $request) {
return;
}
public function applyApplicationSearchConstraintToQuery(
PhabricatorApplicationSearchEngine $engine,
PhabricatorCursorPagedPolicyAwareQuery $query,
$value) {
return;
}
public function appendToApplicationSearchForm(
PhabricatorApplicationSearchEngine $engine,
AphrontFormView $form,
$value,
array $handles) {
return;
}
public function validateApplicationTransactions(
PhabricatorApplicationTransactionEditor $editor,
$type,
array $xactions) {
$this->setFieldError(null);
$errors = parent::validateApplicationTransactions(
$editor,
$type,
$xactions);
if ($this->getRequired()) {
$value = null;
$transaction = null;
foreach ($xactions as $xaction) {
$value = $xaction->getNewValue();
if (!$this->isValueEmpty($value)) {
$transaction = $xaction;
break;
}
}
if ($this->isValueEmpty($value)) {
$errors[] = new PhabricatorApplicationTransactionValidationError(
$type,
pht('Required'),
pht('%s is required.', $this->getFieldName()),
$transaction);
$this->setFieldError(pht('Required'));
}
}
return $errors;
}
protected function isValueEmpty($value) {
if (is_array($value)) {
return empty($value);
}
return !strlen($value);
}
}

File Metadata

Mime Type
text/x-diff
Expires
Wed, Jun 11, 1:02 AM (1 h, 31 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
141548
Default Alt Text
(15 KB)

Event Timeline