for logging compare as case-sensitive and accent-sensitive
authordemeritcowboy <demeritcowboy@hotmail.com>
Tue, 17 Nov 2020 23:56:00 +0000 (18:56 -0500)
committerTim Otten <totten@civicrm.org>
Thu, 14 Jan 2021 03:10:34 +0000 (19:10 -0800)
CRM/Logging/Schema.php
tests/phpunit/CRM/Logging/SchemaTest.php

index 6a75405d4c9d163833fed6e74b78dcb42727be21..cd7dfd5b8298f41f936fd6429d116b6e68e448ee 100644 (file)
@@ -948,6 +948,12 @@ COLS;
       }
       $columns = $this->columnsOf($table, $force);
 
+      // Use utf8mb4_bin or utf8_bin, depending on what's in use.
+      $charset = 'utf8';
+      if (stripos(CRM_Core_BAO_SchemaHandler::getInUseCollation(), 'utf8mb4') !== FALSE) {
+        $charset = 'utf8mb4';
+      }
+
       // only do the change if any data has changed
       $cond = [];
       foreach ($columns as $column) {
@@ -958,7 +964,12 @@ COLS;
         $excludeColumn = in_array($column, $tableExceptions) ||
           in_array(str_replace('`', '', $column), $tableExceptions);
         if (!$excludeColumn) {
-          $cond[] = "IFNULL(OLD.$column,'') <> IFNULL(NEW.$column,'')";
+          // The empty string needs charset signalling to avoid errors.
+          // Note that it is not a cast/convert. It just tells mysql
+          // that there isn't a conflict when your system/connection defaults
+          // happen to be different from $charset.
+          // See https://dev.mysql.com/doc/refman/5.7/en/charset-literal.html
+          $cond[] = "IFNULL(OLD.$column,_{$charset}'') <> IFNULL(NEW.$column,_{$charset}'') COLLATE {$charset}_bin";
         }
       }
       $suppressLoggingCond = "@civicrm_disable_logging IS NULL OR @civicrm_disable_logging = 0";
index 5356c14e6d0160107cc3c0c3995199a05f6d68ef..b2f0bea21d29e647e8f265b533d1c940c7f0aa69 100644 (file)
@@ -375,6 +375,51 @@ class CRM_Logging_SchemaTest extends CiviUnitTestCase {
     $this->assertStringContainsString("`{$custom_field['column_name']}` varchar(768)", $dao->Create_Table);
   }
 
+  /**
+   * Test that logging records changes in upper/lower-case and accents.
+   * e.g. changing e to E or e to é
+   * @dataProvider loggingSensitivityProvider
+   *
+   * @param array $input
+   */
+  public function testLoggingSensitivity(array $input) {
+    $schema = new CRM_Logging_Schema();
+    $schema->enableLogging();
+
+    // create a contact with all lower case/no accents
+    $contact_id = $this->individualCreate(['first_name' => 'pierre']);
+
+    $query_params = [
+      1 => [$contact_id, 'Integer'],
+      2 => [$input['first_name'], 'String'],
+    ];
+
+    // Clear out anything that api did twice during initial creation so that
+    // spurious update records don't give false results.
+    CRM_Core_DAO::executeQuery("DELETE FROM log_civicrm_contact WHERE id = %1 AND log_action = 'update'", $query_params);
+
+    // Change the first name
+    CRM_Core_DAO::executeQuery("UPDATE civicrm_contact SET first_name = %2 WHERE id = %1", $query_params);
+    // check the log
+    $dao = CRM_Core_DAO::executeQuery("SELECT first_name FROM log_civicrm_contact WHERE id = %1 AND log_action = 'update'", $query_params);
+    $first_name = NULL;
+    if ($dao->fetch()) {
+      $first_name = $dao->first_name;
+    }
+    $this->assertSame($input['first_name'], $first_name);
+  }
+
+  /**
+   * Dataprovider for testLoggingSensitivity
+   * @return array
+   */
+  public function loggingSensitivityProvider():array {
+    return [
+      'upper' => [['first_name' => 'Pierre']],
+      'accent' => [['first_name' => 'pièrre']],
+    ];
+  }
+
   /**
    * Test creating a table with SchemaHandler::createTable when logging
    * is enabled.