CRM-16373 - Migrate config_backend to civicrm_setting
authorTim Otten <totten@civicrm.org>
Thu, 17 Sep 2015 03:59:01 +0000 (20:59 -0700)
committerTim Otten <totten@civicrm.org>
Thu, 17 Sep 2015 22:49:33 +0000 (15:49 -0700)
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
CRM/Upgrade/Form.php
CRM/Upgrade/Incremental/php/FourSeven.php
Civi/Core/SettingsBag.php
sql/test_data_second_domain.mysql
tests/phpunit/CRM/Core/BAO/SettingTest.php
xml/schema/Core/Setting.xml

index 1f43b5c4c2d2fb8cce90b67c0e26b45aba5fc52e..9b01f258254b3e5378d392931b85b8f0f8bae24f 100644 (file)
@@ -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() {
index 099431f6da112022107191193d3edfe71e088d83..aeafa50b3f2cd13f265e34cf001cfa3d93e1fd75 100644 (file)
@@ -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();
index 6b969b93f08373ee773ff3e48d371ab20e399d8e..8eca09799e51dec3d9002eaa908acbfd10021286 100644 (file)
@@ -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
    *
index ab54ebdebc37688b1374db7e04a62d68ab825559..3448b32b8fa031d73ecc3cead7cd9e94f4fe048a 100644 (file)
@@ -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) {
index fab8dc60f6c819b7c0a1242298728aa4e8e74ba2..1c95d245b9a9dd1b284226b7d25d38c932ec2d00 100644 (file)
@@ -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:"\ 11\ 12\ 13\ 14\ 15\ 16\ 17\ 18\ 19\ 110\ 111\ 113\ 1";' ),
-  ( @domainID, NULL, 1, 'CiviCRM Preferences', 'contact_edit_options', 's:22:"\ 11\ 12\ 13\ 14\ 15\ 16\ 17\ 18\ 19\ 111\ 1";' ),
-  ( @domainID, NULL, 1, 'CiviCRM Preferences', 'advanced_search_options', 's:46:"\ 11\ 12\ 13\ 14\ 15\ 16\ 17\ 18\ 19\ 110\ 111\ 112\ 113\ 115\ 116\ 117\ 118\ 119\ 1";' ),
-  ( @domainID, NULL, 1, 'CiviCRM Preferences', 'user_dashboard_options', 's:15:"\ 11\ 12\ 13\ 14\ 15\ 17\ 18\ 1";' ),
-  ( @domainID, NULL, 1, 'CiviCRM Preferences', 'address_options', 's:23:"\ 11\ 12\ 13\ 14\ 15\ 16\ 18\ 19\ 110\ 111\ 1";' ),
-  ( @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:"\ 11\ 12\ 1";' ),
-  ( @domainID, NULL, 1, 'CiviCRM Preferences', 'contact_reference_options', 's:5:"\ 11\ 12\ 1";' ),
-  ( @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`)
index 18310aa74ff897f1349efa19d72e09b866e50d11..58985fe21c988435c1520b9d3eddfe05c1a79189 100644 (file)
@@ -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);
   }
 
index e50318fa556bab248583affb555a2a07908c0b36..d2865d352b245cb719a81d9fb358e37e7e439c3a 100644 (file)
@@ -25,6 +25,7 @@
     <required>true</required>
     <comment>group name for setting element, useful in caching setting elements</comment>
     <add>4.1</add>
+    <drop>4.7</drop>
   </field>
   <field>
     <name>name</name>
@@ -39,6 +40,7 @@
     <fieldName>group_name</fieldName>
     <fieldName>name</fieldName>
     <add>4.1</add>
+    <drop>4.7</drop>
   </index>
   <field>
     <name>value</name>
     <add>4.1</add>
     <onDelete>SET NULL</onDelete>
   </foreignKey>
+  <index>
+    <name>index_domain_contact_name</name>
+    <fieldName>domain_id</fieldName>
+    <fieldName>contact_id</fieldName>
+    <fieldName>name</fieldName>
+    <unique>true</unique>
+    <add>4.7</add>
+  </index>
+
 </table>