diff --git a/ext/afform/core/CRM/Afform/BAO/AfformSubmission.php b/ext/afform/core/CRM/Afform/BAO/AfformSubmission.php new file mode 100644 index 000000000000..5e2bc34bb7c0 --- /dev/null +++ b/ext/afform/core/CRM/Afform/BAO/AfformSubmission.php @@ -0,0 +1,6 @@ +__table = 'civicrm_afform_submission'; + parent::__construct(); + } + + /** + * Returns localized title of this entity. + * + * @param bool $plural + * Whether to return the plural version of the title. + */ + public static function getEntityTitle($plural = FALSE) { + return $plural ? E::ts('Form Builder Submissions') : E::ts('Form Builder Submission'); + } + + /** + * Returns foreign keys and entity references. + * + * @return array + * [CRM_Core_Reference_Interface] + */ + public static function getReferenceColumns() { + if (!isset(Civi::$statics[__CLASS__]['links'])) { + Civi::$statics[__CLASS__]['links'] = static::createReferenceColumns(__CLASS__); + Civi::$statics[__CLASS__]['links'][] = new CRM_Core_Reference_Basic(self::getTableName(), 'contact_id', 'civicrm_contact', 'id'); + CRM_Core_DAO_AllCoreTables::invoke(__CLASS__, 'links_callback', Civi::$statics[__CLASS__]['links']); + } + return Civi::$statics[__CLASS__]['links']; + } + + /** + * Returns all the column names of this table + * + * @return array + */ + public static function &fields() { + if (!isset(Civi::$statics[__CLASS__]['fields'])) { + Civi::$statics[__CLASS__]['fields'] = [ + 'id' => [ + 'name' => 'id', + 'type' => CRM_Utils_Type::T_INT, + 'description' => E::ts('Unique Submission ID'), + 'required' => TRUE, + 'where' => 'civicrm_afform_submission.id', + 'table_name' => 'civicrm_afform_submission', + 'entity' => 'AfformSubmission', + 'bao' => 'CRM_Afform_DAO_AfformSubmission', + 'localizable' => 0, + 'html' => [ + 'type' => 'Number', + ], + 'readonly' => TRUE, + 'add' => '5.41', + ], + 'contact_id' => [ + 'name' => 'contact_id', + 'type' => CRM_Utils_Type::T_INT, + 'title' => E::ts('User Contact ID'), + 'where' => 'civicrm_afform_submission.contact_id', + 'table_name' => 'civicrm_afform_submission', + 'entity' => 'AfformSubmission', + 'bao' => 'CRM_Afform_DAO_AfformSubmission', + 'localizable' => 0, + 'FKClassName' => 'CRM_Contact_DAO_Contact', + 'add' => '5.41', + ], + 'data' => [ + 'name' => 'data', + 'type' => CRM_Utils_Type::T_TEXT, + 'title' => E::ts('Submission Data'), + 'description' => E::ts('IDs of saved entities'), + 'where' => 'civicrm_afform_submission.data', + 'table_name' => 'civicrm_afform_submission', + 'entity' => 'AfformSubmission', + 'bao' => 'CRM_Afform_DAO_AfformSubmission', + 'localizable' => 0, + 'serialize' => self::SERIALIZE_JSON, + 'add' => '5.41', + ], + 'submission_date' => [ + 'name' => 'submission_date', + 'type' => CRM_Utils_Type::T_TIMESTAMP, + 'title' => E::ts('Submission Date/Time'), + 'where' => 'civicrm_afform_submission.submission_date', + 'default' => 'CURRENT_TIMESTAMP', + 'table_name' => 'civicrm_afform_submission', + 'entity' => 'AfformSubmission', + 'bao' => 'CRM_Afform_DAO_AfformSubmission', + 'localizable' => 0, + 'readonly' => TRUE, + 'add' => '5.41', + ], + ]; + CRM_Core_DAO_AllCoreTables::invoke(__CLASS__, 'fields_callback', Civi::$statics[__CLASS__]['fields']); + } + return Civi::$statics[__CLASS__]['fields']; + } + + /** + * Return a mapping from field-name to the corresponding key (as used in fields()). + * + * @return array + * Array(string $name => string $uniqueName). + */ + public static function &fieldKeys() { + if (!isset(Civi::$statics[__CLASS__]['fieldKeys'])) { + Civi::$statics[__CLASS__]['fieldKeys'] = array_flip(CRM_Utils_Array::collect('name', self::fields())); + } + return Civi::$statics[__CLASS__]['fieldKeys']; + } + + /** + * Returns the names of this table + * + * @return string + */ + public static function getTableName() { + return self::$_tableName; + } + + /** + * Returns if this table needs to be logged + * + * @return bool + */ + public function getLog() { + return self::$_log; + } + + /** + * Returns the list of fields that can be imported + * + * @param bool $prefix + * + * @return array + */ + public static function &import($prefix = FALSE) { + $r = CRM_Core_DAO_AllCoreTables::getImports(__CLASS__, 'afform_submission', $prefix, []); + return $r; + } + + /** + * Returns the list of fields that can be exported + * + * @param bool $prefix + * + * @return array + */ + public static function &export($prefix = FALSE) { + $r = CRM_Core_DAO_AllCoreTables::getExports(__CLASS__, 'afform_submission', $prefix, []); + return $r; + } + + /** + * Returns the list of indices + * + * @param bool $localize + * + * @return array + */ + public static function indices($localize = TRUE) { + $indices = []; + return ($localize && !empty($indices)) ? CRM_Core_DAO_AllCoreTables::multilingualize(__CLASS__, $indices) : $indices; + } + +} diff --git a/ext/afform/core/CRM/Afform/Upgrader.php b/ext/afform/core/CRM/Afform/Upgrader.php new file mode 100644 index 000000000000..d173d2cd9384 --- /dev/null +++ b/ext/afform/core/CRM/Afform/Upgrader.php @@ -0,0 +1,21 @@ +ctx->log->info('Applying update 1000 - install civicrm_afform_submission table.'); + if (!CRM_Core_DAO::singleValueQuery("SHOW TABLES LIKE 'civicrm_afform_submission'")) { + $this->executeSqlFile('sql/auto_install.sql'); + } + return TRUE; + } + +} diff --git a/ext/afform/core/CRM/Afform/Upgrader/Base.php b/ext/afform/core/CRM/Afform/Upgrader/Base.php new file mode 100644 index 000000000000..207520ddd58b --- /dev/null +++ b/ext/afform/core/CRM/Afform/Upgrader/Base.php @@ -0,0 +1,396 @@ +ctx = array_shift($args); + $instance->queue = $instance->ctx->queue; + $method = array_shift($args); + return call_user_func_array([$instance, $method], $args); + } + + /** + * CRM_Afform_Upgrader_Base constructor. + * + * @param $extensionName + * @param $extensionDir + */ + public function __construct($extensionName, $extensionDir) { + $this->extensionName = $extensionName; + $this->extensionDir = $extensionDir; + } + + // ******** Task helpers ******** + + /** + * Run a CustomData file. + * + * @param string $relativePath + * the CustomData XML file path (relative to this extension's dir) + * @return bool + */ + public function executeCustomDataFile($relativePath) { + $xml_file = $this->extensionDir . '/' . $relativePath; + return $this->executeCustomDataFileByAbsPath($xml_file); + } + + /** + * Run a CustomData file + * + * @param string $xml_file + * the CustomData XML file path (absolute path) + * + * @return bool + */ + protected function executeCustomDataFileByAbsPath($xml_file) { + $import = new CRM_Utils_Migrate_Import(); + $import->run($xml_file); + return TRUE; + } + + /** + * Run a SQL file. + * + * @param string $relativePath + * the SQL file path (relative to this extension's dir) + * + * @return bool + */ + public function executeSqlFile($relativePath) { + CRM_Utils_File::sourceSQLFile( + CIVICRM_DSN, + $this->extensionDir . DIRECTORY_SEPARATOR . $relativePath + ); + return TRUE; + } + + /** + * Run the sql commands in the specified file. + * + * @param string $tplFile + * The SQL file path (relative to this extension's dir). + * Ex: "sql/mydata.mysql.tpl". + * + * @return bool + * @throws \CRM_Core_Exception + */ + public function executeSqlTemplate($tplFile) { + // Assign multilingual variable to Smarty. + $upgrade = new CRM_Upgrade_Form(); + + $tplFile = CRM_Utils_File::isAbsolute($tplFile) ? $tplFile : $this->extensionDir . DIRECTORY_SEPARATOR . $tplFile; + $smarty = CRM_Core_Smarty::singleton(); + $smarty->assign('domainID', CRM_Core_Config::domainID()); + CRM_Utils_File::sourceSQLFile( + CIVICRM_DSN, $smarty->fetch($tplFile), NULL, TRUE + ); + return TRUE; + } + + /** + * Run one SQL query. + * + * This is just a wrapper for CRM_Core_DAO::executeSql, but it + * provides syntactic sugar for queueing several tasks that + * run different queries + * + * @return bool + */ + public function executeSql($query, $params = []) { + // FIXME verify that we raise an exception on error + CRM_Core_DAO::executeQuery($query, $params); + return TRUE; + } + + /** + * Syntactic sugar for enqueuing a task which calls a function in this class. + * + * The task is weighted so that it is processed + * as part of the currently-pending revision. + * + * After passing the $funcName, you can also pass parameters that will go to + * the function. Note that all params must be serializable. + */ + public function addTask($title) { + $args = func_get_args(); + $title = array_shift($args); + $task = new CRM_Queue_Task( + [get_class($this), '_queueAdapter'], + $args, + $title + ); + return $this->queue->createItem($task, ['weight' => -1]); + } + + // ******** Revision-tracking helpers ******** + + /** + * Determine if there are any pending revisions. + * + * @return bool + */ + public function hasPendingRevisions() { + $revisions = $this->getRevisions(); + $currentRevision = $this->getCurrentRevision(); + + if (empty($revisions)) { + return FALSE; + } + if (empty($currentRevision)) { + return TRUE; + } + + return ($currentRevision < max($revisions)); + } + + /** + * Add any pending revisions to the queue. + * + * @param CRM_Queue_Queue $queue + */ + public function enqueuePendingRevisions(CRM_Queue_Queue $queue) { + $this->queue = $queue; + + $currentRevision = $this->getCurrentRevision(); + foreach ($this->getRevisions() as $revision) { + if ($revision > $currentRevision) { + $title = E::ts('Upgrade %1 to revision %2', [ + 1 => $this->extensionName, + 2 => $revision, + ]); + + // note: don't use addTask() because it sets weight=-1 + + $task = new CRM_Queue_Task( + [get_class($this), '_queueAdapter'], + ['upgrade_' . $revision], + $title + ); + $this->queue->createItem($task); + + $task = new CRM_Queue_Task( + [get_class($this), '_queueAdapter'], + ['setCurrentRevision', $revision], + $title + ); + $this->queue->createItem($task); + } + } + } + + /** + * Get a list of revisions. + * + * @return array + * revisionNumbers sorted numerically + */ + public function getRevisions() { + if (!is_array($this->revisions)) { + $this->revisions = []; + + $clazz = new ReflectionClass(get_class($this)); + $methods = $clazz->getMethods(); + foreach ($methods as $method) { + if (preg_match('/^upgrade_(.*)/', $method->name, $matches)) { + $this->revisions[] = $matches[1]; + } + } + sort($this->revisions, SORT_NUMERIC); + } + + return $this->revisions; + } + + public function getCurrentRevision() { + $revision = CRM_Core_BAO_Extension::getSchemaVersion($this->extensionName); + if (!$revision) { + $revision = $this->getCurrentRevisionDeprecated(); + } + return $revision; + } + + private function getCurrentRevisionDeprecated() { + $key = $this->extensionName . ':version'; + if ($revision = \Civi::settings()->get($key)) { + $this->revisionStorageIsDeprecated = TRUE; + } + return $revision; + } + + public function setCurrentRevision($revision) { + CRM_Core_BAO_Extension::setSchemaVersion($this->extensionName, $revision); + // clean up legacy schema version store (CRM-19252) + $this->deleteDeprecatedRevision(); + return TRUE; + } + + private function deleteDeprecatedRevision() { + if ($this->revisionStorageIsDeprecated) { + $setting = new CRM_Core_BAO_Setting(); + $setting->name = $this->extensionName . ':version'; + $setting->delete(); + CRM_Core_Error::debug_log_message("Migrated extension schema revision ID for {$this->extensionName} from civicrm_setting (deprecated) to civicrm_extension.\n"); + } + } + + // ******** Hook delegates ******** + + /** + * @see https://docs.civicrm.org/dev/en/latest/hooks/hook_civicrm_install + */ + public function onInstall() { + $files = glob($this->extensionDir . '/sql/*_install.sql'); + if (is_array($files)) { + foreach ($files as $file) { + CRM_Utils_File::sourceSQLFile(CIVICRM_DSN, $file); + } + } + $files = glob($this->extensionDir . '/sql/*_install.mysql.tpl'); + if (is_array($files)) { + foreach ($files as $file) { + $this->executeSqlTemplate($file); + } + } + $files = glob($this->extensionDir . '/xml/*_install.xml'); + if (is_array($files)) { + foreach ($files as $file) { + $this->executeCustomDataFileByAbsPath($file); + } + } + if (is_callable([$this, 'install'])) { + $this->install(); + } + } + + /** + * @see https://docs.civicrm.org/dev/en/latest/hooks/hook_civicrm_postInstall + */ + public function onPostInstall() { + $revisions = $this->getRevisions(); + if (!empty($revisions)) { + $this->setCurrentRevision(max($revisions)); + } + if (is_callable([$this, 'postInstall'])) { + $this->postInstall(); + } + } + + /** + * @see https://docs.civicrm.org/dev/en/latest/hooks/hook_civicrm_uninstall + */ + public function onUninstall() { + $files = glob($this->extensionDir . '/sql/*_uninstall.mysql.tpl'); + if (is_array($files)) { + foreach ($files as $file) { + $this->executeSqlTemplate($file); + } + } + if (is_callable([$this, 'uninstall'])) { + $this->uninstall(); + } + $files = glob($this->extensionDir . '/sql/*_uninstall.sql'); + if (is_array($files)) { + foreach ($files as $file) { + CRM_Utils_File::sourceSQLFile(CIVICRM_DSN, $file); + } + } + } + + /** + * @see https://docs.civicrm.org/dev/en/latest/hooks/hook_civicrm_enable + */ + public function onEnable() { + // stub for possible future use + if (is_callable([$this, 'enable'])) { + $this->enable(); + } + } + + /** + * @see https://docs.civicrm.org/dev/en/latest/hooks/hook_civicrm_disable + */ + public function onDisable() { + // stub for possible future use + if (is_callable([$this, 'disable'])) { + $this->disable(); + } + } + + public function onUpgrade($op, CRM_Queue_Queue $queue = NULL) { + switch ($op) { + case 'check': + return [$this->hasPendingRevisions()]; + + case 'enqueue': + return $this->enqueuePendingRevisions($queue); + + default: + } + } + +} diff --git a/ext/afform/core/Civi/Api4/Action/Afform/Submit.php b/ext/afform/core/Civi/Api4/Action/Afform/Submit.php index c33cb29bb470..1a09dd2388f7 100644 --- a/ext/afform/core/Civi/Api4/Action/Afform/Submit.php +++ b/ext/afform/core/Civi/Api4/Action/Afform/Submit.php @@ -3,6 +3,7 @@ namespace Civi\Api4\Action\Afform; use Civi\Afform\Event\AfformSubmitEvent; +use Civi\Api4\AfformSubmission; /** * Class Submit @@ -59,8 +60,11 @@ protected function processForm() { \Civi::dispatcher()->dispatch('civi.afform.submit', $event); } - // What should I return? - return []; + // Return submission + return (array) AfformSubmission::create(FALSE) + ->addValue('contact_id', \CRM_Core_Session::getLoggedInContactID()) + ->addValue('data', $this->_entityIds) + ->execute(); } /** diff --git a/ext/afform/core/Civi/Api4/AfformSubmission.php b/ext/afform/core/Civi/Api4/AfformSubmission.php new file mode 100644 index 000000000000..f909d127c8df --- /dev/null +++ b/ext/afform/core/Civi/Api4/AfformSubmission.php @@ -0,0 +1,14 @@ + [ + 'name' => 'AfformSubmission', + 'class' => 'CRM_Afform_DAO_AfformSubmission', + 'table' => 'civicrm_afform_submission', + ], + ]); } diff --git a/ext/afform/core/ang/af/fields/File.html b/ext/afform/core/ang/af/fields/File.html new file mode 100644 index 000000000000..9ac8a7a74607 --- /dev/null +++ b/ext/afform/core/ang/af/fields/File.html @@ -0,0 +1 @@ + diff --git a/ext/afform/core/sql/auto_install.sql b/ext/afform/core/sql/auto_install.sql new file mode 100644 index 000000000000..6087894be24b --- /dev/null +++ b/ext/afform/core/sql/auto_install.sql @@ -0,0 +1,44 @@ +-- +--------------------------------------------------------------------+ +-- | Copyright CiviCRM LLC. All rights reserved. | +-- | | +-- | This work is published under the GNU AGPLv3 license with some | +-- | permitted exceptions and without any warranty. For full license | +-- | and copyright information, see https://civicrm.org/licensing | +-- +--------------------------------------------------------------------+ +-- +-- Generated from schema.tpl +-- DO NOT EDIT. Generated by CRM_Core_CodeGen +-- +-- /******************************************************* +-- * +-- * Clean up the existing tables - this section generated from drop.tpl +-- * +-- *******************************************************/ + +SET FOREIGN_KEY_CHECKS=0; + +DROP TABLE IF EXISTS `civicrm_afform_submission`; + +SET FOREIGN_KEY_CHECKS=1; +-- /******************************************************* +-- * +-- * Create new tables +-- * +-- *******************************************************/ + +-- /******************************************************* +-- * +-- * civicrm_afform_submission +-- * +-- * Recorded form submissions +-- * +-- *******************************************************/ +CREATE TABLE `civicrm_afform_submission` ( + `id` int unsigned NOT NULL AUTO_INCREMENT COMMENT 'Unique Submission ID', + `contact_id` int unsigned, + `data` text COMMENT 'IDs of saved entities', + `submission_date` timestamp DEFAULT CURRENT_TIMESTAMP, + PRIMARY KEY (`id`), + CONSTRAINT FK_civicrm_afform_submission_contact_id FOREIGN KEY (`contact_id`) REFERENCES `civicrm_contact`(`id`) ON DELETE SET NULL +) +ENGINE=InnoDB ROW_FORMAT=DYNAMIC; diff --git a/ext/afform/core/sql/auto_uninstall.sql b/ext/afform/core/sql/auto_uninstall.sql new file mode 100644 index 000000000000..90004586bc19 --- /dev/null +++ b/ext/afform/core/sql/auto_uninstall.sql @@ -0,0 +1,20 @@ +-- +--------------------------------------------------------------------+ +-- | Copyright CiviCRM LLC. All rights reserved. | +-- | | +-- | This work is published under the GNU AGPLv3 license with some | +-- | permitted exceptions and without any warranty. For full license | +-- | and copyright information, see https://civicrm.org/licensing | +-- +--------------------------------------------------------------------+ +-- +-- Generated from drop.tpl +-- DO NOT EDIT. Generated by CRM_Core_CodeGen +---- /******************************************************* +-- * +-- * Clean up the existing tables-- * +-- *******************************************************/ + +SET FOREIGN_KEY_CHECKS=0; + +DROP TABLE IF EXISTS `civicrm_afform_submission`; + +SET FOREIGN_KEY_CHECKS=1; \ No newline at end of file diff --git a/ext/afform/core/xml/schema/CRM/Afform/AfformSubmission.entityType.php b/ext/afform/core/xml/schema/CRM/Afform/AfformSubmission.entityType.php new file mode 100644 index 000000000000..7b3362da3f8f --- /dev/null +++ b/ext/afform/core/xml/schema/CRM/Afform/AfformSubmission.entityType.php @@ -0,0 +1,10 @@ + 'AfformSubmission', + 'class' => 'CRM_Afform_DAO_AfformSubmission', + 'table' => 'civicrm_afform_submission', + ], +]; diff --git a/ext/afform/core/xml/schema/CRM/Afform/AfformSubmission.xml b/ext/afform/core/xml/schema/CRM/Afform/AfformSubmission.xml new file mode 100644 index 000000000000..0ca31256c0d2 --- /dev/null +++ b/ext/afform/core/xml/schema/CRM/Afform/AfformSubmission.xml @@ -0,0 +1,57 @@ + + +