From f806379ba12d846edb8057257e478bc49f7f8287 Mon Sep 17 00:00:00 2001 From: Tim Otten Date: Wed, 16 Sep 2015 20:59:01 -0700 Subject: [PATCH] CRM-16373 - Migrate config_backend to civicrm_setting There are two steps at which we convert config_backend: * In loading the upgrade environment, we may need access to old settings. So SettingsBag supports reading config_backend for the active domain. * As a specific upgrade step (for 4.7.alpha1), we migrate all values for all domains (unless there's already a setting). --- CRM/Core/Config/MagicMerge.php | 8 +++ CRM/Upgrade/Form.php | 11 +--- CRM/Upgrade/Incremental/php/FourSeven.php | 77 ++++++++++++++++++++++ Civi/Core/SettingsBag.php | 36 +++++++--- sql/test_data_second_domain.mysql | 54 --------------- tests/phpunit/CRM/Core/BAO/SettingTest.php | 2 +- xml/schema/Core/Setting.xml | 11 ++++ 7 files changed, 127 insertions(+), 72 deletions(-) diff --git a/CRM/Core/Config/MagicMerge.php b/CRM/Core/Config/MagicMerge.php index 1f43b5c4c2..9b01f25825 100644 --- a/CRM/Core/Config/MagicMerge.php +++ b/CRM/Core/Config/MagicMerge.php @@ -67,6 +67,14 @@ class CRM_Core_Config_MagicMerge { } /** + * Get a list of $config properties and the entities to which they map. + * + * This is used for two purposes: + * + * 1. Runtime: Provide backward-compatible interface for reading these + * properties. + * 2. Upgrade: Migrate old properties of config_backend into settings. + * * @return array */ public static function getPropertyMap() { diff --git a/CRM/Upgrade/Form.php b/CRM/Upgrade/Form.php index 099431f6da..aeafa50b3f 100644 --- a/CRM/Upgrade/Form.php +++ b/CRM/Upgrade/Form.php @@ -722,15 +722,8 @@ SET version = '$version' // Seems extraneous in context, but we'll preserve old behavior $upgrade->setVersion($latestVer); - // lets rebuild the config array in case we've made a few changes in the - // code base - // this also helps us always store the latest version of civi in the DB - $params = array(); - CRM_Core_BAO_ConfigSetting::add($params); - - // CRM-12804 comment-51411 : add any missing settings - // at the end of upgrade - CRM_Core_BAO_Setting::updateSettingsFromMetaData(); + // Clear cached metadata. + Civi::service('settings_manager')->flush(); // cleanup caches CRM-8739 $config = CRM_Core_Config::singleton(); diff --git a/CRM/Upgrade/Incremental/php/FourSeven.php b/CRM/Upgrade/Incremental/php/FourSeven.php index 6b969b93f0..8eca09799e 100644 --- a/CRM/Upgrade/Incremental/php/FourSeven.php +++ b/CRM/Upgrade/Incremental/php/FourSeven.php @@ -102,6 +102,7 @@ class CRM_Upgrade_Incremental_php_FourSeven extends CRM_Upgrade_Incremental_Base public function upgrade_4_7_alpha1($rev) { $this->addTask(ts('Migrate \'on behalf of\' information to module_data'), 'migrateOnBehalfOfInfo'); $this->addTask(ts('Upgrade DB to %1: SQL', array(1 => $rev)), 'runSql', $rev); + $this->addTask(ts('Migrate Settings to %1', array(1 => $rev)), 'migrateSettings', $rev); $this->addTask(ts('Add Getting Started dashlet to %1: SQL', array(1 => $rev)), 'addGettingStartedDashlet', $rev); } @@ -120,6 +121,82 @@ class CRM_Upgrade_Incremental_php_FourSeven extends CRM_Upgrade_Incremental_Base return $editorID; } + /** + * Migrate any last remaining options from `civicrm_domain.config_backend` to `civicrm_setting`. + * Cleanup setting schema. + * + * @param CRM_Queue_TaskContext $ctx + * @return bool + */ + public function migrateSettings(CRM_Queue_TaskContext $ctx) { + CRM_Core_DAO::executeQuery('ALTER TABLE civicrm_setting DROP INDEX index_group_name'); + CRM_Core_DAO::executeQuery('ALTER TABLE civicrm_setting DROP COLUMN group_name'); + CRM_Core_DAO::executeQuery('ALTER TABLE civicrm_setting ADD UNIQUE INDEX index_domain_contact_name (domain_id, contact_id, name)'); + + $domainDao = CRM_Core_DAO::executeQuery('SELECT id, config_backend FROM civicrm_domain'); + while ($domainDao->fetch()) { + $settings = CRM_Upgrade_Incremental_php_FourSeven::convertBackendToSettings($domainDao->id, $domainDao->config_backend); + CRM_Core_Error::debug_var('convertBackendToSettings', array( + 'domainId' => $domainDao->id, + 'backend' => $domainDao->config_backend, + 'settings' => $settings, + )); + + foreach ($settings as $name => $value) { + $rowParams = array( + 1 => array($domainDao->id, 'Positive'), + 2 => array($name, 'String'), + 3 => array(serialize($value), 'String'), + ); + $settingId = CRM_Core_DAO::singleValueQuery( + 'SELECT id FROM civicrm_setting WHERE domain_id = %1 AND name = %2', + $rowParams); + if (!$settingId) { + CRM_Core_DAO::executeQuery( + 'INSERT INTO civicrm_setting (domain_id, name, value, is_domain) VALUES (%1,%2,%3,1)', + $rowParams); + } + } + } + + // TODO Should drop config_backend, but keeping it during alpha/beta cycle in case we miss something. + // CRM_Core_DAO::executeQuery('ALTER TABLE civicrm_domain DROP COLUMN config_backend'); + + return TRUE; + } + + /** + * Take a config_backend blob and produce an equivalent list of settings. + * + * @param int $domainId + * Domain ID. + * @param string $config_backend + * Serialized blob. + * @return array + */ + public static function convertBackendToSettings($domainId, $config_backend) { + if (!$config_backend) { + return array(); + } + + $backend = unserialize($config_backend); + if (!$backend) { + return array(); + } + + $mappings = \CRM_Core_Config_MagicMerge::getPropertyMap(); + $settings = array(); + foreach ($backend as $propertyName => $propertyValue) { + if (isset($mappings[$propertyName][0]) && preg_match('/^setting/', $mappings[$propertyName][0])) { + // $mapping format: $propertyName => Array(0 => $type, 1 => $setting|NULL). + $settingName = isset($mappings[$propertyName][1]) ? $mappings[$propertyName][1] : $propertyName; + $settings[$settingName] = $propertyValue; + } + } + + return $settings; + } + /** * Add Getting Started dashlet to dashboard * diff --git a/Civi/Core/SettingsBag.php b/Civi/Core/SettingsBag.php index ab54ebdebc..3448b32b8f 100644 --- a/Civi/Core/SettingsBag.php +++ b/Civi/Core/SettingsBag.php @@ -130,14 +130,35 @@ class SettingsBag { * @return $this */ public function loadValues() { - $this->values = array(); - // Note: Don't use Setting DAO. It requires fields() which requires + // Note: Don't use DAO child classes. They require fields() which require // translations -- which are keyed off settings! - $dao = \CRM_Core_DAO::executeQuery($this->createQuery()->toSQL()); - while ($dao->fetch()) { - $this->values[$dao->name] = ($dao->value !== NULL) ? unserialize($dao->value) : NULL; - } + + $this->values = array(); $this->combined = NULL; + + // Ordinarily, we just load values from `civicrm_setting`. But upgrades require care. + // In v4.0 and earlier, all values were stored in `civicrm_domain.config_backend`. + // In v4.1-v4.6, values were split between `civicrm_domain` and `civicrm_setting`. + // In v4.7+, all values are stored in `civicrm_setting`. + // Whenever a value is available in civicrm_setting, it will take precedence. + + $isUpgradeMode = \CRM_Core_Config::isUpgradeMode(); + + if ($isUpgradeMode && empty($this->contactId) && \CRM_Core_DAO::checkFieldExists('civicrm_domain', 'config_backend', FALSE)) { + $config_backend = \CRM_Core_DAO::singleValueQuery('SELECT config_backend FROM civicrm_domain WHERE id = %1', + array(1 => array($this->domainId, 'Positive'))); + $oldSettings = \CRM_Upgrade_Incremental_php_FourSeven::convertBackendToSettings($this->domainId, $config_backend); + \CRM_Utils_Array::extend($this->values, $oldSettings); + } + + // Normal case. Aside: Short-circuit prevents unnecessary query. + if (!$isUpgradeMode || \CRM_Core_DAO::checkTableExists('civicrm_setting')) { + $dao = \CRM_Core_DAO::executeQuery($this->createQuery()->toSQL()); + while ($dao->fetch()) { + $this->values[$dao->name] = ($dao->value !== NULL) ? unserialize($dao->value) : NULL; + } + } + return $this; } @@ -265,7 +286,7 @@ class SettingsBag { */ protected function createQuery() { $select = \CRM_Utils_SQL_Select::from('civicrm_setting') - ->select('id, group_name, name, value, domain_id, contact_id, is_domain, component_id, created_date, created_id') + ->select('id, name, value, domain_id, contact_id, is_domain, component_id, created_date, created_id') ->where('domain_id = #id', array( 'id' => $this->domainId, )); @@ -336,7 +357,6 @@ class SettingsBag { $dao->is_domain = 1; } $dao->find(TRUE); - $dao->group_name = ''; if (isset($metadata['on_change'])) { foreach ($metadata['on_change'] as $callback) { diff --git a/sql/test_data_second_domain.mysql b/sql/test_data_second_domain.mysql index fab8dc60f6..1c95d245b9 100644 --- a/sql/test_data_second_domain.mysql +++ b/sql/test_data_second_domain.mysql @@ -61,60 +61,6 @@ VALUES ( @domainID, 'Always' , NULL, 'Process Survey Respondents', 'Releases reserved survey respondents when they have been reserved for longer than the Release Frequency days specified for that survey.', 'job', 'process_respondent',NULL, 0), ( @domainID, 'Hourly' , NULL, 'Clean-up Temporary Data and Files','Removes temporary data and files, and clears old data from cache tables. Recommend running this job every hour to help prevent database and file system bloat.', 'job', 'cleanup', NULL, 0); -- Initial default state of system settings -INSERT INTO civicrm_setting - ( domain_id, contact_id, is_domain, group_name, name, value ) -VALUES - ( @domainID, NULL, 1, 'CiviCRM Preferences', 'contact_view_options', 's:28:"123456789101113";' ), - ( @domainID, NULL, 1, 'CiviCRM Preferences', 'contact_edit_options', 's:22:"12345678911";' ), - ( @domainID, NULL, 1, 'CiviCRM Preferences', 'advanced_search_options', 's:46:"123456789101112131516171819";' ), - ( @domainID, NULL, 1, 'CiviCRM Preferences', 'user_dashboard_options', 's:15:"1234578";' ), - ( @domainID, NULL, 1, 'CiviCRM Preferences', 'address_options', 's:23:"123456891011";' ), - ( @domainID, NULL, 1, 'CiviCRM Preferences', 'address_format', 's:198:"{contact.address_name} -{contact.street_address} -{contact.supplemental_address_1} -{contact.supplemental_address_2} -{contact.city}{, }{contact.state_province}{ }{contact.postal_code} -{contact.country}";' ), - ( @domainID, NULL, 1, 'CiviCRM Preferences', 'mailing_format', 's:195:"{contact.addressee} -{contact.street_address} -{contact.supplemental_address_1} -{contact.supplemental_address_2} -{contact.city}{, }{contact.state_province}{ }{contact.postal_code} -{contact.country}";' ), - ( @domainID, NULL, 1, 'CiviCRM Preferences', 'display_name_format', 's:102:"{contact.individual_prefix}{ }{contact.first_name}{ }{contact.last_name}{ }{contact.individual_suffix}";' ), - ( @domainID, NULL, 1, 'CiviCRM Preferences', 'sort_name_format', 's:43:"{contact.last_name}{, }{contact.first_name}";' ), - ( @domainID, NULL, 1, 'CiviCRM Preferences', 'editor_id', 's:1:"2";' ), - ( @domainID, NULL, 1, 'CiviCRM Preferences', 'contact_ajax_check_similar', 's:1:"1";' ), - ( @domainID, NULL, 1, 'CiviCRM Preferences', 'activity_assignee_notification', 's:1:"1";' ), - ( @domainID, NULL, 1, 'CiviCRM Preferences', 'contact_autocomplete_options', 's:5:"12";' ), - ( @domainID, NULL, 1, 'CiviCRM Preferences', 'contact_reference_options', 's:5:"12";' ), - ( @domainID, NULL, 1, 'Address Standardization Preferences', 'address_standardization_provider', NULL ), - ( @domainID, NULL, 1, 'Address Standardization Preferences', 'address_standardization_userid', NULL ), - ( @domainID, NULL, 1, 'Address Standardization Preferences', 'address_standardization_url', NULL ), - ( @domainID, NULL, 1, 'Campaign Preferences', 'tag_unconfirmed', 's:11:"Unconfirmed";' ), - ( @domainID, NULL, 1, 'Campaign Preferences', 'petition_contacts', 's:17:"Petition Contacts";' ), - ( @domainID, NULL, 1, 'Event Preferences' , 'enable_cart', 's:1:"0";' ), - ( @domainID, NULL, 1, 'Mailing Preferences', 'profile_double_optin', 's:1:"1";' ), - ( @domainID, NULL, 1, 'Mailing Preferences', 'profile_add_to_group_double_optin', 's:1:"0";' ), - ( @domainID, NULL, 1, 'Mailing Preferences', 'track_civimail_replies', 's:1:"0";' ), - ( @domainID, NULL, 1, 'Mailing Preferences', 'civimail_workflow', 's:1:"0";' ), - ( @domainID, NULL, 1, 'Mailing Preferences', 'civimail_server_wide_lock', 's:1:"0";' ), - ( @domainID, NULL, 1, 'Mailing Preferences', 'mailing_backend', 'a:1:{s:15:"outBound_option";s:1:"3";}' ), - ( @domainID, NULL, 1, 'Member Preferences' , 'default_renewal_contribution_page', NULL ), - ( @domainID, NULL, 1, 'Multi Site Preferences', 'is_enabled', 's:1:"0";' ), - ( @domainID, NULL, 1, 'Multi Site Preferences', 'uniq_email_per_site', 's:1:"0";' ), - ( @domainID, NULL, 1, 'Multi Site Preferences', 'domain_group_id', 's:1:"0";' ), - ( @domainID, NULL, 1, 'Multi Site Preferences', 'event_price_set_domain_id', 's:1:"0";' ), - ( @domainID, NULL, 1, 'Directory Preferences', 'uploadDir' , NULL ), - ( @domainID, NULL, 1, 'Directory Preferences', 'imageUploadDir' , NULL ), - ( @domainID, NULL, 1, 'Directory Preferences', 'customFileUploadDir', NULL ), - ( @domainID, NULL, 1, 'Directory Preferences', 'customTemplateDir' , NULL ), - ( @domainID, NULL, 1, 'Directory Preferences', 'customPHPPathDir' , NULL ), - ( @domainID, NULL, 1, 'Directory Preferences', 'extensionsDir' , NULL ), - ( @domainID, NULL, 1, 'URL Preferences', 'userFrameworkResourceURL' , NULL ), - ( @domainID, NULL, 1, 'URL Preferences', 'imageUploadURL' , NULL ), - ( @domainID, NULL, 1, 'URL Preferences', 'customCSSURL' , NULL ), - ( @domainID, NULL, 1, 'URL Preferences', 'extensionsURL' , NULL ); INSERT INTO `civicrm_dashboard` ( `domain_id`, `label`, `url`, `permission`, `permission_operator`, `column_no`, `is_minimized`, `is_active`, `weight`, `fullscreen_url`, `is_fullscreen`, `is_reserved`) diff --git a/tests/phpunit/CRM/Core/BAO/SettingTest.php b/tests/phpunit/CRM/Core/BAO/SettingTest.php index 18310aa74f..58985fe21c 100644 --- a/tests/phpunit/CRM/Core/BAO/SettingTest.php +++ b/tests/phpunit/CRM/Core/BAO/SettingTest.php @@ -98,7 +98,7 @@ class CRM_Core_BAO_SettingTest extends CiviUnitTestCase { $this->assertEquals('/test/dataride', $values['databaseSetting']); // CRM-14974 tear down unset($civicrm_setting['Test Preferences']); - $query = "DELETE FROM civicrm_setting WHERE group_name = 'Test Preferences';"; + $query = "DELETE FROM civicrm_setting WHERE name IN ('overrideSetting', 'databaseSetting');"; CRM_Core_DAO::executeQuery($query); } diff --git a/xml/schema/Core/Setting.xml b/xml/schema/Core/Setting.xml index e50318fa55..d2865d352b 100644 --- a/xml/schema/Core/Setting.xml +++ b/xml/schema/Core/Setting.xml @@ -25,6 +25,7 @@ true group name for setting element, useful in caching setting elements 4.1 + 4.7 name @@ -39,6 +40,7 @@ group_name name 4.1 + 4.7 value @@ -129,4 +131,13 @@ 4.1 SET NULL + + index_domain_contact_name + domain_id + contact_id + name + true + 4.7 + + -- 2.25.1