1
0
Fork 0
mirror of https://we.phorge.it/source/phorge.git synced 2024-11-09 16:32:39 +01:00

Support export engine extensions and implement an extension for custom fields

Summary:
Depends on D18953. Ref T13049. Allow applications and infrastructure to supplement exportable fields for objects.

Then, implement an extension for custom fields. Only a couple field types (int, string) are supported for now.

Test Plan: Added some custom fields to Users, populated them, exported users. Saw custom fields in the export.

Reviewers: amckinley

Reviewed By: amckinley

Maniphest Tasks: T13049

Differential Revision: https://secure.phabricator.com/D18954
This commit is contained in:
epriestley 2018-01-28 19:30:41 -08:00
parent 8b8a3142b3
commit a067f64ebb
8 changed files with 233 additions and 0 deletions

View file

@ -2583,6 +2583,7 @@ phutil_register_library_map(array(
'PhabricatorCustomFieldEditEngineExtension' => 'infrastructure/customfield/engineextension/PhabricatorCustomFieldEditEngineExtension.php',
'PhabricatorCustomFieldEditField' => 'infrastructure/customfield/editor/PhabricatorCustomFieldEditField.php',
'PhabricatorCustomFieldEditType' => 'infrastructure/customfield/editor/PhabricatorCustomFieldEditType.php',
'PhabricatorCustomFieldExportEngineExtension' => 'infrastructure/export/PhabricatorCustomFieldExportEngineExtension.php',
'PhabricatorCustomFieldFulltextEngineExtension' => 'infrastructure/customfield/engineextension/PhabricatorCustomFieldFulltextEngineExtension.php',
'PhabricatorCustomFieldHeraldAction' => 'infrastructure/customfield/herald/PhabricatorCustomFieldHeraldAction.php',
'PhabricatorCustomFieldHeraldActionGroup' => 'infrastructure/customfield/herald/PhabricatorCustomFieldHeraldActionGroup.php',
@ -2847,6 +2848,7 @@ phutil_register_library_map(array(
'PhabricatorEventType' => 'infrastructure/events/constant/PhabricatorEventType.php',
'PhabricatorExampleEventListener' => 'infrastructure/events/PhabricatorExampleEventListener.php',
'PhabricatorExecFutureFileUploadSource' => 'applications/files/uploadsource/PhabricatorExecFutureFileUploadSource.php',
'PhabricatorExportEngineExtension' => 'infrastructure/export/PhabricatorExportEngineExtension.php',
'PhabricatorExportField' => 'infrastructure/export/PhabricatorExportField.php',
'PhabricatorExportFormat' => 'infrastructure/export/PhabricatorExportFormat.php',
'PhabricatorExtendedPolicyInterface' => 'applications/policy/interface/PhabricatorExtendedPolicyInterface.php',
@ -7991,6 +7993,7 @@ phutil_register_library_map(array(
'PhabricatorCustomFieldEditEngineExtension' => 'PhabricatorEditEngineExtension',
'PhabricatorCustomFieldEditField' => 'PhabricatorEditField',
'PhabricatorCustomFieldEditType' => 'PhabricatorEditType',
'PhabricatorCustomFieldExportEngineExtension' => 'PhabricatorExportEngineExtension',
'PhabricatorCustomFieldFulltextEngineExtension' => 'PhabricatorFulltextEngineExtension',
'PhabricatorCustomFieldHeraldAction' => 'HeraldAction',
'PhabricatorCustomFieldHeraldActionGroup' => 'HeraldActionGroup',
@ -8280,6 +8283,7 @@ phutil_register_library_map(array(
'PhabricatorEventType' => 'PhutilEventType',
'PhabricatorExampleEventListener' => 'PhabricatorEventListener',
'PhabricatorExecFutureFileUploadSource' => 'PhabricatorFileUploadSource',
'PhabricatorExportEngineExtension' => 'Phobject',
'PhabricatorExportField' => 'Phobject',
'PhabricatorExportFormat' => 'Phobject',
'PhabricatorExtendingPhabricatorConfigOptions' => 'PhabricatorApplicationConfigOptions',

View file

@ -1483,6 +1483,26 @@ abstract class PhabricatorApplicationSearchEngine extends Phobject {
$fields[$key] = $export_field;
}
$extensions = $this->newExportExtensions();
foreach ($extensions as $extension) {
$extension_fields = $extension->newExportFields();
foreach ($extension_fields as $extension_field) {
$key = $extension_field->getKey();
if (isset($fields[$key])) {
throw new Exception(
pht(
'Export engine extension ("%s") defines an export field with '.
'a key ("%s") that collides with another field. Each field '.
'must have a unique key.',
get_class($extension_field),
$key));
}
$fields[$key] = $extension_field;
}
}
return $fields;
}
@ -1514,6 +1534,25 @@ abstract class PhabricatorApplicationSearchEngine extends Phobject {
$maps[$ii] += $export_data[$ii];
}
$extensions = $this->newExportExtensions();
foreach ($extensions as $extension) {
$extension_data = $extension->newExportData($objects);
$extension_data = array_values($extension_data);
if (count($export_data) !== count($objects)) {
throw new Exception(
pht(
'Export engine extension ("%s") exported the wrong number of '.
'objects, expected %s but got %s.',
get_class($extension),
phutil_count($objects),
phutil_count($export_data)));
}
for ($ii = 0; $ii < $n; $ii++) {
$maps[$ii] += $extension_data[$ii];
}
}
return $maps;
}
@ -1525,4 +1564,23 @@ abstract class PhabricatorApplicationSearchEngine extends Phobject {
throw new PhutilMethodNotImplementedException();
}
private function newExportExtensions() {
$object = $this->newResultObject();
$viewer = $this->requireViewer();
$extensions = PhabricatorExportEngineExtension::getAllExtensions();
$supported = array();
foreach ($extensions as $extension) {
$extension = clone $extension;
$extension->setViewer($viewer);
if ($extension->supportsObject($object)) {
$supported[] = $extension;
}
}
return $supported;
}
}

View file

@ -35,6 +35,7 @@ abstract class PhabricatorCustomField extends Phobject {
const ROLE_HERALD = 'herald';
const ROLE_EDITENGINE = 'EditEngine';
const ROLE_HERALDACTION = 'herald.action';
const ROLE_EXPORT = 'export';
/* -( Building Applications with Custom Fields )--------------------------- */
@ -299,6 +300,8 @@ abstract class PhabricatorCustomField extends Phobject {
case self::ROLE_EDITENGINE:
return $this->shouldAppearInEditView() ||
$this->shouldAppearInEditEngine();
case self::ROLE_EXPORT:
return $this->shouldAppearInDataExport();
case self::ROLE_DEFAULT:
return true;
default:
@ -1362,6 +1365,46 @@ abstract class PhabricatorCustomField extends Phobject {
}
/* -( Data Export )-------------------------------------------------------- */
public function shouldAppearInDataExport() {
if ($this->proxy) {
return $this->proxy->shouldAppearInDataExport();
}
try {
$this->newExportFieldType();
return true;
} catch (PhabricatorCustomFieldImplementationIncompleteException $ex) {
return false;
}
}
public function newExportField() {
if ($this->proxy) {
return $this->proxy->newExportField();
}
return $this->newExportFieldType()
->setLabel($this->getFieldName());
}
public function newExportData() {
if ($this->proxy) {
return $this->proxy->newExportData();
}
throw new PhabricatorCustomFieldImplementationIncompleteException($this);
}
protected function newExportFieldType() {
if ($this->proxy) {
return $this->proxy->newExportFieldType();
}
throw new PhabricatorCustomFieldImplementationIncompleteException($this);
}
/* -( Conduit )------------------------------------------------------------ */

View file

@ -496,5 +496,8 @@ abstract class PhabricatorStandardCustomField
return $this->getFieldValue();
}
public function newExportData() {
return $this->getFieldValue();
}
}

View file

@ -124,4 +124,8 @@ final class PhabricatorStandardCustomFieldInt
return new ConduitIntParameterType();
}
protected function newExportFieldType() {
return new PhabricatorIntExportField();
}
}

View file

@ -76,4 +76,8 @@ final class PhabricatorStandardCustomFieldText
return new ConduitStringParameterType();
}
protected function newExportFieldType() {
return new PhabricatorStringExportField();
}
}

View file

@ -0,0 +1,86 @@
<?php
final class PhabricatorCustomFieldExportEngineExtension
extends PhabricatorExportEngineExtension {
const EXTENSIONKEY = 'custom-field';
private $object;
public function supportsObject($object) {
$this->object = $object;
return ($object instanceof PhabricatorCustomFieldInterface);
}
public function newExportFields() {
$prototype = $this->object;
$fields = $this->newCustomFields($prototype);
$results = array();
foreach ($fields as $field) {
$field_key = $field->getModernFieldKey();
$results[] = $field->newExportField()
->setKey($field_key);
}
return $results;
}
public function newExportData(array $objects) {
$viewer = $this->getViewer();
$field_map = array();
foreach ($objects as $object) {
$object_phid = $object->getPHID();
$fields = PhabricatorCustomField::getObjectFields(
$object,
PhabricatorCustomField::ROLE_EXPORT);
$fields
->setViewer($viewer)
->readFieldsFromObject($object);
$field_map[$object_phid] = $fields;
}
$all_fields = array();
foreach ($field_map as $field_list) {
foreach ($field_list->getFields() as $field) {
$all_fields[] = $field;
}
}
id(new PhabricatorCustomFieldStorageQuery())
->addFields($all_fields)
->execute();
$results = array();
foreach ($objects as $object) {
$object_phid = $object->getPHID();
$object_fields = $field_map[$object_phid];
$map = array();
foreach ($object_fields->getFields() as $field) {
$key = $field->getModernFieldKey();
$map[$key] = $field->newExportData();
}
$results[] = $map;
}
return $results;
}
private function newCustomFields($object) {
$fields = PhabricatorCustomField::getObjectFields(
$object,
PhabricatorCustomField::ROLE_EXPORT);
$fields->setViewer($this->getViewer());
return $fields->getFields();
}
}

View file

@ -0,0 +1,31 @@
<?php
abstract class PhabricatorExportEngineExtension extends Phobject {
private $viewer;
final public function getExtensionKey() {
return $this->getPhobjectClassConstant('EXTENSIONKEY');
}
final public function setViewer($viewer) {
$this->viewer = $viewer;
return $this;
}
final public function getViewer() {
return $this->viewer;
}
abstract public function supportsObject($object);
abstract public function newExportFields();
abstract public function newExportData(array $objects);
final public static function getAllExtensions() {
return id(new PhutilClassMapQuery())
->setAncestorClass(__CLASS__)
->setUniqueMethod('getExtensionKey')
->execute();
}
}