diff --git a/Civi/Api4/Action/FormProcessorEntity/FormProcessorGetFieldsAction.php b/Civi/Api4/Action/FormProcessorEntity/FormProcessorGetFieldsAction.php new file mode 100644 index 0000000000000000000000000000000000000000..f8570b4dde3ca980446a3a664ed40f88ec54dcd7 --- /dev/null +++ b/Civi/Api4/Action/FormProcessorEntity/FormProcessorGetFieldsAction.php @@ -0,0 +1,44 @@ + + * @license http://www.gnu.org/licenses/agpl-3.0.html + */ + +namespace Civi\Api4\Action\FormProcessorEntity; + +use Civi\Api4\Generic\AbstractSaveAction; +use Civi\Api4\Generic\BasicGetFieldsAction; +use Civi\Api4\Generic\Result; +use Civi\FormProcessor\Type\OptionListInterface; + +class FormProcessorGetFieldsAction extends BasicGetFieldsAction { + + /** + * @return array[] + */ + public function getRecords() { + $name = substr($this->getEntityName(), strlen('FormProcessor_')); + $result = []; + + $processor = \Civi\FormProcessor\API\FormProcessor::getFormProcessor($name); + /** @var \CRM_FormProcessor_BAO_FormProcessorInput $input */ + foreach ($processor['inputs'] as $input) { + $field = [ + 'name' => $input['name'], + 'type' => 'Field', + 'title' => $input['title'], + 'label' => $input['title'], + 'required' => !empty($input['is_required']), + 'default_value' => $input['default_value'] ?? NULL, + 'data_type' => \CRM_Utils_Type::typeToString($input['type']->getCrmType()), + 'suffixes' => ['name', 'label'], + ]; + if ($input['type'] instanceof OptionListInterface) { + $field['options'] = $input['type']->getOptions([]); + } + $result[] = $input['type']->alterApi4FieldSpec($field); + } + return $result; + } + +} diff --git a/Civi/Api4/Action/FormProcessorEntity/FormProcessorSaveAction.php b/Civi/Api4/Action/FormProcessorEntity/FormProcessorSaveAction.php new file mode 100644 index 0000000000000000000000000000000000000000..eea41e2b87241bd44ebc134662c013bcbcc5b361 --- /dev/null +++ b/Civi/Api4/Action/FormProcessorEntity/FormProcessorSaveAction.php @@ -0,0 +1,31 @@ + + * @license http://www.gnu.org/licenses/agpl-3.0.html + */ + +namespace Civi\Api4\Action\FormProcessorEntity; + +use Civi\Api4\Generic\AbstractSaveAction; +use Civi\Api4\Generic\Result; + +class FormProcessorSaveAction extends AbstractSaveAction { + + /** + * @param \Civi\Api4\Generic\Result $result + */ + public function _run(Result $result) { + $name = substr($this->getEntityName(), strlen('FormProcessor_')); + foreach ($this->getRecords() as $record) { + civicrm_api3('FormProcessor', $name, $record); + $result[] = []; // TODO: Return something? + } + } + + public function getPermissions() { + $name = substr($this->getEntityName(), strlen('FormProcessor_')); + $processor = \Civi\FormProcessor\API\FormProcessor::getFormProcessor($name); + return (array) ($processor['permission'] ?: NULL); + } + +} diff --git a/Civi/Api4/FormProcessorEntity.php b/Civi/Api4/FormProcessorEntity.php new file mode 100644 index 0000000000000000000000000000000000000000..c4d24d949bd6ed1a4801d38cd8ac61c0c9bd404b --- /dev/null +++ b/Civi/Api4/FormProcessorEntity.php @@ -0,0 +1,63 @@ + + * @license http://www.gnu.org/licenses/agpl-3.0.html + */ + +namespace Civi\Api4; + +use Civi\Api4\Action\FormProcessorEntity\FormProcessorGetFieldsAction; +use Civi\Api4\Action\FormProcessorEntity\FormProcessorSaveAction; +use Civi\Api4\Action\GetActions; +use Civi\Api4\Generic\CheckAccessAction; + +class FormProcessorEntity { + + /** + * @param string $name + * @param bool $checkPermissions + * @return FormProcessorGetFieldsAction + */ + public static function getFields(string $name, $checkPermissions = TRUE) { + return (new FormProcessorGetFieldsAction('FormProcessor_' . $name, __FUNCTION__)) + ->setCheckPermissions($checkPermissions); + } + + /** + * @param string $name + * @param bool $checkPermissions + * @return FormProcessorSaveAction + * @throws \API_Exception + */ + public static function save(string $name, $checkPermissions = TRUE) { + return (new FormProcessorSaveAction('FormProcessor_' . $name, __FUNCTION__)) + ->setCheckPermissions($checkPermissions); + } + + /** + * @param string $name + * @param bool $checkPermissions + * @return GetActions + */ + public static function getActions(string $name, $checkPermissions = TRUE) { + return (new GetActions('FormProcessor_' . $name, __FUNCTION__)) + ->setCheckPermissions($checkPermissions); + } + + /** + * @param string $name + * @return CheckAccessAction + * @throws \API_Exception + */ + public static function checkAccess(string $name) { + return new CheckAccessAction('FormProcessor_' . $name, __FUNCTION__); + } + + /** + * @return array + */ + public static function permissions() { + return []; + } + +} diff --git a/Civi/FormProcessor/API/FormProcessor.php b/Civi/FormProcessor/API/FormProcessor.php index 2d6b8ab674c17697a32d33d15642c9eba4f01016..07bee002d8f7163d9f6942a197c8886603a289eb 100644 --- a/Civi/FormProcessor/API/FormProcessor.php +++ b/Civi/FormProcessor/API/FormProcessor.php @@ -182,7 +182,7 @@ * @throws \API_Exception * @throws \CRM_Core_Exception */ - protected static function getFormProcessor($name) { + public static function getFormProcessor($name) { $cachekey = 'FormProcessor.run.get.'.$name; if (!$formProcessor = Cache::get($cachekey)) { // Find the form processor diff --git a/Civi/FormProcessor/API4/FormProcessorProvider.php b/Civi/FormProcessor/API4/FormProcessorProvider.php new file mode 100644 index 0000000000000000000000000000000000000000..397f04d21e61b947ac2d3706b08e75c9336738ff --- /dev/null +++ b/Civi/FormProcessor/API4/FormProcessorProvider.php @@ -0,0 +1,129 @@ + + * @license http://www.gnu.org/licenses/agpl-3.0.html + */ + + namespace Civi\FormProcessor\API4; + + use Civi\ActionProvider\Action\AbstractAction; + use Civi\ActionProvider\Parameter\ParameterBag; + use Civi\AfformAdmin\AfformAdminMeta; + use Civi\API\Event\ResolveEvent; + use Civi\API\Event\RespondEvent; + use Civi\API\Events; + use Civi\API\Provider\ProviderInterface as API_ProviderInterface; + use Civi\Core\Event\GenericHookEvent; + use Civi\FormProcessor\DataBag; + use Civi\FormProcessor\Type\OptionListInterface; + use Civi\FormProcessor\Utils\Cache; + use Symfony\Component\EventDispatcher\EventSubscriberInterface; + + use CRM_FormProcessor_ExtensionUtil as E; + +/** + * This class connects the enabled form processors to + * the the api interface of CiviCRM. That way the form processors + * could be executed over the api. + */ + class FormProcessorProvider implements API_ProviderInterface, EventSubscriberInterface { + + public function __construct() { + } + + /** + * @return array[] + */ + public static function getSubscribedEvents() { + return [ + 'civi.api4.createRequest' => [['onApi4CreateRequest', Events::W_EARLY]], + 'civi.api4.entityTypes' => [['onApi4EntityTypes', Events::W_EARLY]], + ]; + } + + /** + * Register each FormProcessor as an APIv4 entity. + * + * Callback for `civi.api4.entityTypes` event. + * + * @param GenericHookEvent $event + */ + public function onApi4EntityTypes(GenericHookEvent $event) { + foreach (\CRM_FormProcessor_BAO_FormProcessorInstance::getValues(['is_active' => 1]) as $processor) { + $name = 'FormProcessor_' . $processor['name']; + $event->entities[$name] = [ + 'name' => $name, + 'title' => $processor['title'], + 'title_plural' => $processor['title'], + 'description' => (string) ($processor['description'] ?? ''), + 'primary_key' => ['id'], + 'type' => ['FormProcessor'], + 'table_name' => NULL, + 'class_args' => [$processor['name']], + 'label_field' => NULL, + 'searchable' => 'secondary', + 'paths' => [], + 'class' => 'Civi\Api4\FormProcessorEntity', + 'icon' => 'fa-gears', + ]; + } + } + + /** + * Callback for `civi.api4.createRequest` event. + * + * @param \Civi\Api4\Event\CreateApi4RequestEvent $event + */ + public function onApi4CreateRequest($event) { + if (strpos($event->entityName, 'FormProcessor_') === 0) { + $name = substr($event->entityName, strlen('FormProcessor_')); + $event->className = 'Civi\Api4\FormProcessorEntity'; + $event->args = [$name]; + } + } + + /** + * @param GenericHookEvent $e + */ + public static function afformEntityTypes(GenericHookEvent $e) { + foreach (\CRM_FormProcessor_BAO_FormProcessorInstance::getValues(['is_active' => 1]) as $processor) { + $name = 'FormProcessor_' . $processor['name']; + $e->entities[$name] = [ + 'entity' => $name, + 'label' => $processor['title'], + 'icon' => 'fa-gears', + 'type' => 'primary', + 'defaults' => '{}', + ]; + } + } + + /** + * Not needed for APIv4 + * @param int $version + * @return array + */ + public function getEntityNames($version) { + return []; + } + + /** + * Not needed for APIv4 + * @param int $version + * @param string $entity + * @return array + */ + public function getActionNames($version, $entity) { + return []; + } + + /** + * Not needed for APIv4 + */ + public function invoke($apiRequest) { + } + + + + + } diff --git a/Civi/FormProcessor/Utils/Cache.php b/Civi/FormProcessor/Utils/Cache.php index feef49a2d43d5b8f3e57a168458f06d6e064db6c..6a5bfd7685de53d9ed9afd396c99933c4ff07245 100644 --- a/Civi/FormProcessor/Utils/Cache.php +++ b/Civi/FormProcessor/Utils/Cache.php @@ -45,6 +45,8 @@ class Cache { * @throws \CRM_Core_Exception */ public static function clear() { + // Also clear metadata cache for APIv4 entities created from FormProcessors + \Civi::cache('metadata')->clear(); $cache = self::getCacheClass(); return $cache->clear(); } diff --git a/form_processor.php b/form_processor.php index ef112e62b3ed0f78c10138792809c11a4fc93769..86fcfce972b2f410866508b2bdb11a2f6e038df8 100644 --- a/form_processor.php +++ b/form_processor.php @@ -35,8 +35,14 @@ function form_processor_civicrm_container(ContainerBuilder $container) { $apiKernelDefinition->addMethodCall('registerApiProvider', array($apiProviderDefinition)); $apiProviderDefaultsDefinition = DefinitionAdapter::createDefinitionClass('Civi\FormProcessor\API\FormProcessorDefaults'); $apiKernelDefinition->addMethodCall('registerApiProvider', array($apiProviderDefaultsDefinition)); + // Register APIv4 provider + $api4Definition = DefinitionAdapter::createDefinitionClass('Civi\FormProcessor\API4\FormProcessorProvider'); + $apiKernelDefinition->addMethodCall('registerApiProvider', [$api4Definition]); + + $container->findDefinition('dispatcher') + ->addMethodCall('addListener', ['civi.afform_admin.metadata', ['Civi\FormProcessor\API4\FormProcessorProvider', 'afformEntityTypes']]) ->addMethodCall('addSubscriber', [new \Symfony\Component\DependencyInjection\Definition('Civi\FormProcessor\EventListener\FormatSubscriber')]); } diff --git a/tests/phpunit/api/v4/FormProcessorEntity/FormProcessorEntityTest.php b/tests/phpunit/api/v4/FormProcessorEntity/FormProcessorEntityTest.php new file mode 100644 index 0000000000000000000000000000000000000000..cd5a1ec122ded65b36cabdbf3a7711de411d92cf --- /dev/null +++ b/tests/phpunit/api/v4/FormProcessorEntity/FormProcessorEntityTest.php @@ -0,0 +1,53 @@ +installMe(__DIR__) + ->apply(); + } + + public function testCreateEntityType():void { + // TODO: APIv4 + $instance = civicrm_api3('FormProcessorInstance', 'create', [ + 'name' => 'my_processor', + 'title' => 'My Processor', + 'is_active' => 1, + 'description' => 'Test processor entity', + 'output_handler' => 'OutputAllActionOutput', + 'output_handler_configuration' => [], + 'default_data_output_configuration' => [], + 'permission' => 'add contacts', + ]); + civicrm_api3('FormProcessorInput', 'create', [ + 'form_processor_instance_id' => $instance['id'], + 'title' => 'Test 1', + 'name' => 'test_1', + 'type' => 'Integer', + 'is_required' => 1, + 'configuration' => [], + ]); + + // Ensure APIv4 entity has been created + $entity = Entity::get(FALSE) + ->addWhere('name', '=', 'FormProcessor_my_processor') + ->setChain(['fields' => ['$name', 'getFields']]) + ->execute()->single(); + + $this->assertEquals('Test processor entity', $entity['description']); + $this->assertCount(1, $entity['fields']); + } + +}