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:
parent
8b8a3142b3
commit
a067f64ebb
8 changed files with 233 additions and 0 deletions
|
@ -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',
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -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 )------------------------------------------------------------ */
|
||||
|
||||
|
||||
|
|
|
@ -496,5 +496,8 @@ abstract class PhabricatorStandardCustomField
|
|||
return $this->getFieldValue();
|
||||
}
|
||||
|
||||
public function newExportData() {
|
||||
return $this->getFieldValue();
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -124,4 +124,8 @@ final class PhabricatorStandardCustomFieldInt
|
|||
return new ConduitIntParameterType();
|
||||
}
|
||||
|
||||
protected function newExportFieldType() {
|
||||
return new PhabricatorIntExportField();
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -76,4 +76,8 @@ final class PhabricatorStandardCustomFieldText
|
|||
return new ConduitStringParameterType();
|
||||
}
|
||||
|
||||
protected function newExportFieldType() {
|
||||
return new PhabricatorStringExportField();
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
|
||||
}
|
|
@ -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();
|
||||
}
|
||||
|
||||
}
|
Loading…
Reference in a new issue