ts('SQL Query'), 'permissions' => ['import SQL datasource'], ]; } /** * This is function is called by the form object to get the DataSource's * form snippet. It should add all fields necesarry to get the data * uploaded to the temporary table in the DB. * * @param CRM_Core_Form $form * * @return void * (operates directly on form argument) */ public function buildQuickForm(&$form) { $form->add('hidden', 'hidden_dataSource', 'CRM_Import_DataSource_SQL'); $form->add('textarea', 'sqlQuery', ts('Specify SQL Query'), ['rows' => 10, 'cols' => 45], TRUE); $form->addFormRule(['CRM_Import_DataSource_SQL', 'formRule'], $form); } /** * @param $fields * @param $files * @param CRM_Core_Form $form * * @return array|bool */ public static function formRule($fields, $files, $form) { $errors = []; // Makeshift query validation (case-insensitive regex matching on word boundaries) $forbidden = ['ALTER', 'CREATE', 'DELETE', 'DESCRIBE', 'DROP', 'SHOW', 'UPDATE', 'REPLACE', 'information_schema']; foreach ($forbidden as $pattern) { if (preg_match("/\\b$pattern\\b/i", $fields['sqlQuery'])) { $errors['sqlQuery'] = ts('The query contains the forbidden %1 command.', [1 => $pattern]); } } return $errors ?: TRUE; } /** * Initialize the datasource, based on the submitted values stored in the user job. * * @throws \API_Exception * @throws \CRM_Core_Exception */ public function initialize(): void { $table = CRM_Utils_SQL_TempTable::build()->setDurable(); $tableName = $table->getName(); $table->createWithQuery($this->getSubmittedValue('sqlQuery')); // Get the names of the fields to be imported. $columnsResult = CRM_Core_DAO::executeQuery( 'SHOW FIELDS FROM ' . $tableName); $columnNames = []; while ($columnsResult->fetch()) { if (strpos($columnsResult->Field, ' ') !== FALSE) { // Remove spaces as the Database object does this // $keys = str_replace(array(".", " "), "_", array_keys($array)); // https://lab.civicrm.org/dev/core/-/issues/1337 $usableColumnName = str_replace(' ', '_', $columnsResult->Field); CRM_Core_DAO::executeQuery('ALTER TABLE ' . $tableName . ' CHANGE `' . $columnsResult->Field . '` ' . $usableColumnName . ' ' . $columnsResult->Type); $columnNames[] = $usableColumnName; } else { $columnNames[] = $columnsResult->Field; } } $this->addTrackingFieldsToTable($tableName); $this->updateUserJobMetadata('DataSource', [ 'table_name' => $tableName, 'column_headers' => $columnNames, 'number_of_columns' => count($columnNames), ]); } }