Add DedupeRule, DedupeRuleGroup and DedupeException APIv4 entities
authorMonish Deb <monish.deb@jmaconsulting.biz>
Tue, 1 Jun 2021 10:52:26 +0000 (16:22 +0530)
committerMonish Deb <monish.deb@jmaconsulting.biz>
Wed, 2 Jun 2021 10:14:19 +0000 (15:44 +0530)
20 files changed:
CRM/Core/DAO/AllCoreTables.data.php
CRM/Dedupe/BAO/DedupeException.php [new file with mode: 0644]
CRM/Dedupe/BAO/DedupeRule.php [new file with mode: 0644]
CRM/Dedupe/BAO/DedupeRuleGroup.php [new file with mode: 0644]
CRM/Dedupe/BAO/Exception.php
CRM/Dedupe/BAO/Rule.php
CRM/Dedupe/BAO/RuleGroup.php
CRM/Dedupe/DAO/DedupeException.php [new file with mode: 0644]
CRM/Dedupe/DAO/DedupeRule.php [new file with mode: 0644]
CRM/Dedupe/DAO/DedupeRuleGroup.php [new file with mode: 0644]
CRM/Dedupe/DAO/Exception.php
CRM/Dedupe/DAO/Rule.php
CRM/Dedupe/DAO/RuleGroup.php
CRM/Event/DAO/Event.php
Civi/Api4/DedupeRule.php [new file with mode: 0644]
api/v3/utils.php
xml/schema/Dedupe/DedupeException.xml [moved from xml/schema/Dedupe/Exception.xml with 97% similarity]
xml/schema/Dedupe/DedupeRule.xml [moved from xml/schema/Dedupe/Rule.xml with 98% similarity]
xml/schema/Dedupe/DedupeRuleGroup.xml [moved from xml/schema/Dedupe/RuleGroup.xml with 98% similarity]
xml/schema/Dedupe/files.xml

index fdbda3a704c390e436fbbfa211c3313822355187..b29ef2728c2cd3a52a6ce4238430110a88336e50 100644 (file)
@@ -207,19 +207,19 @@ return [
     'class' => 'CRM_Event_Cart_DAO_Cart',
     'table' => 'civicrm_event_carts',
   ],
-  'CRM_Dedupe_DAO_RuleGroup' => [
-    'name' => 'RuleGroup',
-    'class' => 'CRM_Dedupe_DAO_RuleGroup',
+  'CRM_Dedupe_DAO_DedupeRuleGroup' => [
+    'name' => 'DedupeRuleGroup',
+    'class' => 'CRM_Dedupe_DAO_DedupeRuleGroup',
     'table' => 'civicrm_dedupe_rule_group',
   ],
-  'CRM_Dedupe_DAO_Rule' => [
-    'name' => 'Rule',
-    'class' => 'CRM_Dedupe_DAO_Rule',
+  'CRM_Dedupe_DAO_DedupeRule' => [
+    'name' => 'DedupeRule',
+    'class' => 'CRM_Dedupe_DAO_DedupeRule',
     'table' => 'civicrm_dedupe_rule',
   ],
-  'CRM_Dedupe_DAO_Exception' => [
-    'name' => 'Exception',
-    'class' => 'CRM_Dedupe_DAO_Exception',
+  'CRM_Dedupe_DAO_DedupeException' => [
+    'name' => 'DedupeException',
+    'class' => 'CRM_Dedupe_DAO_DedupeException',
     'table' => 'civicrm_dedupe_exception',
   ],
   'CRM_Case_DAO_CaseType' => [
diff --git a/CRM/Dedupe/BAO/DedupeException.php b/CRM/Dedupe/BAO/DedupeException.php
new file mode 100644 (file)
index 0000000..eeaea33
--- /dev/null
@@ -0,0 +1,56 @@
+<?php
+/*
+ +--------------------------------------------------------------------+
+ | 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       |
+ +--------------------------------------------------------------------+
+ */
+
+/**
+ *
+ * @package CRM
+ * @copyright CiviCRM LLC https://civicrm.org/licensing
+ */
+
+/**
+ * Manages dedupe exceptions - ie pairs marked as non-duplicates.
+ */
+class CRM_Dedupe_BAO_DedupeException extends CRM_Dedupe_DAO_DedupeException {
+
+  /**
+   * Create a dedupe exception record.
+   *
+   * @param array $params
+   *
+   * @return \CRM_Dedupe_BAO_Exception
+   */
+  public static function create($params) {
+    $hook = empty($params['id']) ? 'create' : 'edit';
+    CRM_Utils_Hook::pre($hook, 'Exception', CRM_Utils_Array::value('id', $params), $params);
+    $contact1 = $params['contact_id1'] ?? NULL;
+    $contact2 = $params['contact_id2'] ?? NULL;
+    $dao = new CRM_Dedupe_BAO_Exception();
+    $dao->copyValues($params);
+    if ($contact1 && $contact2) {
+      CRM_Core_DAO::singleValueQuery("
+        DELETE FROM civicrm_prevnext_cache
+        WHERE (entity_id1 = %1 AND entity_id2 = %2)
+        OR (entity_id1 = %2 AND entity_id2 = %2)",
+        [1 => [$contact1, 'Integer'], 2 => [$contact2, 'Integer']]
+      );
+      if ($contact2 < $contact1) {
+        // These are expected to be saved lowest first.
+        $dao->contact_id1 = $contact2;
+        $dao->contact_id2 = $contact1;
+      }
+    }
+    $dao->save();
+
+    CRM_Utils_Hook::post($hook, 'Exception', $dao->id, $dao);
+    return $dao;
+  }
+
+}
diff --git a/CRM/Dedupe/BAO/DedupeRule.php b/CRM/Dedupe/BAO/DedupeRule.php
new file mode 100644 (file)
index 0000000..af7e794
--- /dev/null
@@ -0,0 +1,252 @@
+<?php
+/*
+ +--------------------------------------------------------------------+
+ | 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       |
+ +--------------------------------------------------------------------+
+ */
+
+/**
+ *
+ * @package CRM
+ * @copyright CiviCRM LLC https://civicrm.org/licensing
+ */
+
+/**
+ * The CiviCRM duplicate discovery engine is based on an
+ * algorithm designed by David Strauss <david@fourkitchens.com>.
+ */
+class CRM_Dedupe_BAO_DedupeRule extends CRM_Dedupe_DAO_DedupeRule {
+
+  /**
+   * Ids of the contacts to limit the SQL queries (whole-database queries otherwise)
+   * @var array
+   */
+  public $contactIds = [];
+
+  /**
+   * Params to dedupe against (queries against the whole contact set otherwise)
+   * @var array
+   */
+  public $params = [];
+
+  /**
+   * Return the SQL query for the given rule - either for finding matching
+   * pairs of contacts, or for matching against the $params variable (if set).
+   *
+   * @return string
+   *   SQL query performing the search
+   *
+   * @throws \CRM_Core_Exception
+   * @throws \CiviCRM_API3_Exception
+   */
+  public function sql() {
+    if ($this->params &&
+      (!array_key_exists($this->rule_table, $this->params) ||
+        !array_key_exists($this->rule_field, $this->params[$this->rule_table])
+      )
+    ) {
+      // if params is present and doesn't have an entry for a field, don't construct the clause.
+      return NULL;
+    }
+
+    // we need to initialise WHERE, ON and USING here, as some table types
+    // extend them; $where is an array of required conditions, $on and
+    // $using are arrays of required field matchings (for substring and
+    // full matches, respectively)
+    $where = [];
+    $on = ["SUBSTR(t1.{$this->rule_field}, 1, {$this->rule_length}) = SUBSTR(t2.{$this->rule_field}, 1, {$this->rule_length})"];
+
+    $innerJoinClauses = [
+      "t1.{$this->rule_field} IS NOT NULL",
+      "t2.{$this->rule_field} IS NOT NULL",
+      "t1.{$this->rule_field} = t2.{$this->rule_field}",
+    ];
+
+    if (in_array($this->getFieldType($this->rule_field), CRM_Utils_Type::getTextTypes(), TRUE)) {
+      $innerJoinClauses[] = "t1.{$this->rule_field} <> ''";
+      $innerJoinClauses[] = "t2.{$this->rule_field} <> ''";
+    }
+
+    switch ($this->rule_table) {
+      case 'civicrm_contact':
+        $id = 'id';
+        //we should restrict by contact type in the first step
+        $sql = "SELECT contact_type FROM civicrm_dedupe_rule_group WHERE id = {$this->dedupe_rule_group_id};";
+        $ct = CRM_Core_DAO::singleValueQuery($sql);
+        if ($this->params) {
+          $where[] = "t1.contact_type = '{$ct}'";
+        }
+        else {
+          $where[] = "t1.contact_type = '{$ct}'";
+          $where[] = "t2.contact_type = '{$ct}'";
+        }
+        break;
+
+      case 'civicrm_address':
+      case 'civicrm_email':
+      case 'civicrm_im':
+      case 'civicrm_openid':
+      case 'civicrm_phone':
+        $id = 'contact_id';
+        break;
+
+      case 'civicrm_note':
+        $id = 'entity_id';
+        if ($this->params) {
+          $where[] = "t1.entity_table = 'civicrm_contact'";
+        }
+        else {
+          $where[] = "t1.entity_table = 'civicrm_contact'";
+          $where[] = "t2.entity_table = 'civicrm_contact'";
+        }
+        break;
+
+      default:
+        // custom data tables
+        if (preg_match('/^civicrm_value_/', $this->rule_table) || preg_match('/^custom_value_/', $this->rule_table)) {
+          $id = 'entity_id';
+        }
+        else {
+          throw new CRM_Core_Exception("Unsupported rule_table for civicrm_dedupe_rule.id of {$this->id}");
+        }
+        break;
+    }
+
+    // build SELECT based on the field names containing contact ids
+    // if there are params provided, id1 should be 0
+    if ($this->params) {
+      $select = "t1.$id id1, {$this->rule_weight} weight";
+      $subSelect = 'id1, weight';
+    }
+    else {
+      $select = "t1.$id id1, t2.$id id2, {$this->rule_weight} weight";
+      $subSelect = 'id1, id2, weight';
+    }
+
+    // build FROM (and WHERE, if it's a parametrised search)
+    // based on whether the rule is about substrings or not
+    if ($this->params) {
+      $from = "{$this->rule_table} t1";
+      $str = 'NULL';
+      if (isset($this->params[$this->rule_table][$this->rule_field])) {
+        $str = trim(CRM_Utils_Type::escape($this->params[$this->rule_table][$this->rule_field], 'String'));
+      }
+      if ($this->rule_length) {
+        $where[] = "SUBSTR(t1.{$this->rule_field}, 1, {$this->rule_length}) = SUBSTR('$str', 1, {$this->rule_length})";
+        $where[] = "t1.{$this->rule_field} IS NOT NULL";
+      }
+      else {
+        $where[] = "t1.{$this->rule_field} = '$str'";
+      }
+    }
+    else {
+      if ($this->rule_length) {
+        $from = "{$this->rule_table} t1 JOIN {$this->rule_table} t2 ON (" . implode(' AND ', $on) . ")";
+      }
+      else {
+        $from = "{$this->rule_table} t1 INNER JOIN {$this->rule_table} t2 ON (" . implode(' AND ', $innerJoinClauses) . ")";
+      }
+    }
+
+    // finish building WHERE, also limit the results if requested
+    if (!$this->params) {
+      $where[] = "t1.$id < t2.$id";
+    }
+    $query = "SELECT $select FROM $from WHERE " . implode(' AND ', $where);
+    if ($this->contactIds) {
+      $cids = [];
+      foreach ($this->contactIds as $cid) {
+        $cids[] = CRM_Utils_Type::escape($cid, 'Integer');
+      }
+      if (count($cids) == 1) {
+        $query .= " AND (t1.$id = {$cids[0]}) UNION $query AND t2.$id = {$cids[0]}";
+      }
+      else {
+        $query .= " AND t1.$id IN (" . implode(',', $cids) . ")
+        UNION $query AND  t2.$id IN (" . implode(',', $cids) . ")";
+      }
+      // The `weight` is ambiguous in the context of the union; put the whole
+      // thing in a subquery.
+      $query = "SELECT $subSelect FROM ($query) subunion";
+    }
+
+    return $query;
+  }
+
+  /**
+   * find fields related to a rule group.
+   *
+   * @param array $params contains the rule group property to identify rule group
+   *
+   * @return array
+   *   rule fields array associated to rule group
+   */
+  public static function dedupeRuleFields($params) {
+    $rgBao = new CRM_Dedupe_BAO_RuleGroup();
+    $rgBao->used = $params['used'];
+    $rgBao->contact_type = $params['contact_type'];
+    $rgBao->find(TRUE);
+
+    $ruleBao = new CRM_Dedupe_BAO_Rule();
+    $ruleBao->dedupe_rule_group_id = $rgBao->id;
+    $ruleBao->find();
+    $ruleFields = [];
+    while ($ruleBao->fetch()) {
+      $field_name = $ruleBao->rule_field;
+      if ($field_name == 'phone_numeric') {
+        $field_name = 'phone';
+      }
+      $ruleFields[] = $field_name;
+    }
+    return $ruleFields;
+  }
+
+  /**
+   * @param int $cid
+   * @param int $oid
+   *
+   * @return bool
+   */
+  public static function validateContacts($cid, $oid) {
+    if (!$cid || !$oid) {
+      return NULL;
+    }
+    $exception = new CRM_Dedupe_DAO_Exception();
+    $exception->contact_id1 = $cid;
+    $exception->contact_id2 = $oid;
+    //make sure contact2 > contact1.
+    if ($cid > $oid) {
+      $exception->contact_id1 = $oid;
+      $exception->contact_id2 = $cid;
+    }
+
+    return !$exception->find(TRUE);
+  }
+
+  /**
+   * Get the specification for the given field.
+   *
+   * @param string $fieldName
+   *
+   * @return array
+   * @throws \CiviCRM_API3_Exception
+   */
+  public function getFieldType($fieldName) {
+    $entity = CRM_Core_DAO_AllCoreTables::getBriefName(CRM_Core_DAO_AllCoreTables::getClassForTable($this->rule_table));
+    if (!$entity) {
+      // This means we have stored a custom field rather than an entity name in rule_table, figure out the entity.
+      $entity = civicrm_api3('CustomGroup', 'getvalue', ['table_name' => $this->rule_table, 'return' => 'extends']);
+      if (in_array($entity, ['Individual', 'Household', 'Organization'])) {
+        $entity = 'Contact';
+      }
+      $fieldName = 'custom_' . civicrm_api3('CustomField', 'getvalue', ['column_name' => $fieldName, 'return' => 'id']);
+    }
+    $fields = civicrm_api3($entity, 'getfields', ['action' => 'create'])['values'];
+    return $fields[$fieldName]['type'];
+  }
+
+}
diff --git a/CRM/Dedupe/BAO/DedupeRuleGroup.php b/CRM/Dedupe/BAO/DedupeRuleGroup.php
new file mode 100644 (file)
index 0000000..35a2902
--- /dev/null
@@ -0,0 +1,517 @@
+<?php
+/*
+ +--------------------------------------------------------------------+
+ | 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       |
+ +--------------------------------------------------------------------+
+ */
+
+/**
+ *
+ * @package CRM
+ * @copyright CiviCRM LLC https://civicrm.org/licensing
+ */
+
+/**
+ * The CiviCRM duplicate discovery engine is based on an
+ * algorithm designed by David Strauss <david@fourkitchens.com>.
+ */
+class CRM_Dedupe_BAO_DedupeRuleGroup extends CRM_Dedupe_DAO_DedupeRuleGroup {
+
+  /**
+   * Ids of the contacts to limit the SQL queries (whole-database queries otherwise)
+   * @var array
+   */
+  public $contactIds = [];
+
+  /**
+   * Set the contact IDs to restrict the dedupe to.
+   *
+   * @param array $contactIds
+   */
+  public function setContactIds($contactIds) {
+    $this->contactIds = $contactIds;
+  }
+
+  /**
+   * Params to dedupe against (queries against the whole contact set otherwise)
+   * @var array
+   */
+  public $params = [];
+
+  /**
+   * If there are no rules in rule group.
+   * @var bool
+   */
+  public $noRules = FALSE;
+
+  protected $temporaryTables = [];
+
+  /**
+   * Return a structure holding the supported tables, fields and their titles
+   *
+   * @param string $requestedType
+   *   The requested contact type.
+   *
+   * @return array
+   *   a table-keyed array of field-keyed arrays holding supported fields' titles
+   */
+  public static function supportedFields($requestedType) {
+    static $fields = NULL;
+    if (!$fields) {
+      // this is needed, as we're piggy-backing importableFields() below
+      $replacements = [
+        'civicrm_country.name' => 'civicrm_address.country_id',
+        'civicrm_county.name' => 'civicrm_address.county_id',
+        'civicrm_state_province.name' => 'civicrm_address.state_province_id',
+        'gender.label' => 'civicrm_contact.gender_id',
+        'individual_prefix.label' => 'civicrm_contact.prefix_id',
+        'individual_suffix.label' => 'civicrm_contact.suffix_id',
+        'addressee.label' => 'civicrm_contact.addressee_id',
+        'email_greeting.label' => 'civicrm_contact.email_greeting_id',
+        'postal_greeting.label' => 'civicrm_contact.postal_greeting_id',
+        'civicrm_phone.phone' => 'civicrm_phone.phone_numeric',
+      ];
+      // the table names we support in dedupe rules - a filter for importableFields()
+      $supportedTables = [
+        'civicrm_address',
+        'civicrm_contact',
+        'civicrm_email',
+        'civicrm_im',
+        'civicrm_note',
+        'civicrm_openid',
+        'civicrm_phone',
+      ];
+
+      foreach (['Individual', 'Organization', 'Household'] as $ctype) {
+        // take the table.field pairs and their titles from importableFields() if the table is supported
+        foreach (CRM_Contact_BAO_Contact::importableFields($ctype) as $iField) {
+          if (isset($iField['where'])) {
+            $where = $iField['where'];
+            if (isset($replacements[$where])) {
+              $where = $replacements[$where];
+            }
+            list($table, $field) = explode('.', $where);
+            if (!in_array($table, $supportedTables)) {
+              continue;
+            }
+            $fields[$ctype][$table][$field] = $iField['title'];
+          }
+        }
+        // Note that most of the fields available come from 'importable fields' -
+        // I thought about making this field 'importable' but it felt like there might be unknown consequences
+        // so I opted for just adding it in & securing it with a unit test.
+        /// Example usage of sort_name - It is possible to alter sort name via hook so 2 organization names might differ as in
+        // Justice League vs The Justice League but these could have the same sort_name if 'the the'
+        // exension is installed (https://github.com/eileenmcnaughton/org.wikimedia.thethe)
+        $fields[$ctype]['civicrm_contact']['sort_name'] = ts('Sort Name');
+        // add custom data fields
+        foreach (CRM_Core_BAO_CustomGroup::getTree($ctype, NULL, NULL, -1) as $key => $cg) {
+          if (!is_int($key)) {
+            continue;
+          }
+          foreach ($cg['fields'] as $cf) {
+            $fields[$ctype][$cg['table_name']][$cf['column_name']] = $cf['label'];
+          }
+        }
+      }
+    }
+    CRM_Utils_Hook::dupeQuery(CRM_Core_DAO::$_nullObject, 'supportedFields', $fields);
+    return !empty($fields[$requestedType]) ? $fields[$requestedType] : [];
+  }
+
+  /**
+   * Return the SQL query for dropping the temporary table.
+   */
+  public function tableDropQuery() {
+    return 'DROP TEMPORARY TABLE IF EXISTS dedupe';
+  }
+
+  /**
+   * Return a set of SQL queries whose cummulative weights will mark matched
+   * records for the RuleGroup::threasholdQuery() to retrieve.
+   */
+  public function tableQuery() {
+    // make sure we've got a fetched dbrecord, not sure if this is enforced
+    if (!$this->name == NULL || $this->is_reserved == NULL) {
+      $this->find(TRUE);
+    }
+
+    // Reserved Rule Groups can optionally get special treatment by
+    // implementing an optimization class and returning a query array.
+    if ($this->is_reserved &&
+      CRM_Utils_File::isIncludable("CRM/Dedupe/BAO/QueryBuilder/{$this->name}.php")
+    ) {
+      $command = empty($this->params) ? 'internal' : 'record';
+      $queries = call_user_func(["CRM_Dedupe_BAO_QueryBuilder_{$this->name}", $command], $this);
+    }
+    else {
+      // All other rule groups have queries generated by the member dedupe
+      // rules defined in the administrative interface.
+
+      // Find all rules contained by this script sorted by weight so that
+      // their execution can be short circuited on RuleGroup::fillTable()
+      $bao = new CRM_Dedupe_BAO_Rule();
+      $bao->dedupe_rule_group_id = $this->id;
+      $bao->orderBy('rule_weight DESC');
+      $bao->find();
+
+      // Generate a SQL query for each rule in the rule group that is
+      // tailored to respect the param and contactId options provided.
+      $queries = [];
+      while ($bao->fetch()) {
+        $bao->contactIds = $this->contactIds;
+        $bao->params = $this->params;
+
+        // Skipping empty rules? Empty rules shouldn't exist; why check?
+        if ($query = $bao->sql()) {
+          $queries["{$bao->rule_table}.{$bao->rule_field}.{$bao->rule_weight}"] = $query;
+        }
+      }
+    }
+
+    // if there are no rules in this rule group
+    // add an empty query fulfilling the pattern
+    if (!$queries) {
+      $this->noRules = TRUE;
+      return [];
+    }
+
+    return $queries;
+  }
+
+  public function fillTable() {
+    // get the list of queries handy
+    $tableQueries = $this->tableQuery();
+
+    if ($this->params && !$this->noRules) {
+      $this->temporaryTables['dedupe'] = CRM_Utils_SQL_TempTable::build()
+        ->setCategory('dedupe')
+        ->createWithColumns("id1 int, weight int, UNIQUE UI_id1 (id1)")->getName();
+      $dedupeCopyTemporaryTableObject = CRM_Utils_SQL_TempTable::build()
+        ->setCategory('dedupe');
+      $this->temporaryTables['dedupe_copy'] = $dedupeCopyTemporaryTableObject->getName();
+      $insertClause = "INSERT INTO {$this->temporaryTables['dedupe']}  (id1, weight)";
+      $groupByClause = "GROUP BY id1, weight";
+      $dupeCopyJoin = " JOIN {$this->temporaryTables['dedupe_copy']} ON {$this->temporaryTables['dedupe_copy']}.id1 = t1.column WHERE ";
+    }
+    else {
+      $this->temporaryTables['dedupe'] = CRM_Utils_SQL_TempTable::build()
+        ->setCategory('dedupe')
+        ->createWithColumns("id1 int, id2 int, weight int, UNIQUE UI_id1_id2 (id1, id2)")->getName();
+      $dedupeCopyTemporaryTableObject = CRM_Utils_SQL_TempTable::build()
+        ->setCategory('dedupe');
+      $this->temporaryTables['dedupe_copy'] = $dedupeCopyTemporaryTableObject->getName();
+      $insertClause = "INSERT INTO {$this->temporaryTables['dedupe']}  (id1, id2, weight)";
+      $groupByClause = "GROUP BY id1, id2, weight";
+      $dupeCopyJoin = " JOIN {$this->temporaryTables['dedupe_copy']} ON {$this->temporaryTables['dedupe_copy']}.id1 = t1.column AND {$this->temporaryTables['dedupe_copy']}.id2 = t2.column WHERE ";
+    }
+    $patternColumn = '/t1.(\w+)/';
+    $exclWeightSum = [];
+
+    CRM_Utils_Hook::dupeQuery($this, 'table', $tableQueries);
+
+    while (!empty($tableQueries)) {
+      list($isInclusive, $isDie) = self::isQuerySetInclusive($tableQueries, $this->threshold, $exclWeightSum);
+
+      if ($isInclusive) {
+        // order queries by table count
+        self::orderByTableCount($tableQueries);
+
+        $weightSum = array_sum($exclWeightSum);
+        $searchWithinDupes = !empty($exclWeightSum) ? 1 : 0;
+
+        while (!empty($tableQueries)) {
+          // extract the next query ( and weight ) to be executed
+          $fieldWeight = array_keys($tableQueries);
+          $fieldWeight = $fieldWeight[0];
+          $query = array_shift($tableQueries);
+
+          if ($searchWithinDupes) {
+            // drop dedupe_copy table just in case if its already there.
+            $dedupeCopyTemporaryTableObject->drop();
+            // get prepared to search within already found dupes if $searchWithinDupes flag is set
+            $dedupeCopyTemporaryTableObject->createWithQuery("SELECT * FROM {$this->temporaryTables['dedupe']} WHERE weight >= {$weightSum}");
+
+            preg_match($patternColumn, $query, $matches);
+            $query = str_replace(' WHERE ', str_replace('column', $matches[1], $dupeCopyJoin), $query);
+
+            // CRM-19612: If there's a union, there will be two WHEREs, and you
+            // can't use the temp table twice.
+            if (preg_match('/' . $this->temporaryTables['dedupe_copy'] . '[\S\s]*(union)[\S\s]*' . $this->temporaryTables['dedupe_copy'] . '/i', $query, $matches, PREG_OFFSET_CAPTURE)) {
+              // Make a second temp table:
+              $this->temporaryTables['dedupe_copy_2'] = CRM_Utils_SQL_TempTable::build()
+                ->setCategory('dedupe')
+                ->createWithQuery("SELECT * FROM {$this->temporaryTables['dedupe']} WHERE weight >= {$weightSum}")
+                ->getName();
+              // After the union, use that new temp table:
+              $part1 = substr($query, 0, $matches[1][1]);
+              $query = $part1 . str_replace($this->temporaryTables['dedupe_copy'], $this->temporaryTables['dedupe_copy_2'], substr($query, $matches[1][1]));
+            }
+          }
+          $searchWithinDupes = 1;
+
+          // construct and execute the intermediate query
+          $query = "{$insertClause} {$query} {$groupByClause} ON DUPLICATE KEY UPDATE weight = weight + VALUES(weight)";
+          $dao = CRM_Core_DAO::executeQuery($query);
+
+          // FIXME: we need to be more acurate with affected rows, especially for insert vs duplicate insert.
+          // And that will help optimize further.
+          $affectedRows = $dao->affectedRows();
+
+          // In an inclusive situation, failure of any query means no further processing -
+          if ($affectedRows == 0) {
+            // reset to make sure no further execution is done.
+            $tableQueries = [];
+            break;
+          }
+          $weightSum = substr($fieldWeight, strrpos($fieldWeight, '.') + 1) + $weightSum;
+        }
+        // An exclusive situation -
+      }
+      elseif (!$isDie) {
+        // since queries are already sorted by weights, we can continue as is
+        $fieldWeight = array_keys($tableQueries);
+        $fieldWeight = $fieldWeight[0];
+        $query = array_shift($tableQueries);
+        $query = "{$insertClause} {$query} {$groupByClause} ON DUPLICATE KEY UPDATE weight = weight + VALUES(weight)";
+        $dao = CRM_Core_DAO::executeQuery($query);
+        if ($dao->affectedRows() >= 1) {
+          $exclWeightSum[] = substr($fieldWeight, strrpos($fieldWeight, '.') + 1);
+        }
+      }
+      else {
+        // its a die situation
+        break;
+      }
+    }
+  }
+
+  /**
+   * Function to determine if a given query set contains inclusive or exclusive set of weights.
+   * The function assumes that the query set is already ordered by weight in desc order.
+   * @param $tableQueries
+   * @param $threshold
+   * @param array $exclWeightSum
+   *
+   * @return array
+   */
+  public static function isQuerySetInclusive($tableQueries, $threshold, $exclWeightSum = []) {
+    $input = [];
+    foreach ($tableQueries as $key => $query) {
+      $input[] = substr($key, strrpos($key, '.') + 1);
+    }
+
+    if (!empty($exclWeightSum)) {
+      $input = array_merge($input, $exclWeightSum);
+      rsort($input);
+    }
+
+    if (count($input) == 1) {
+      return [FALSE, $input[0] < $threshold];
+    }
+
+    $totalCombinations = 0;
+    for ($i = 0; $i < count($input); $i++) {
+      $combination = [$input[$i]];
+      if (array_sum($combination) >= $threshold) {
+        $totalCombinations++;
+        continue;
+      }
+      for ($j = $i + 1; $j < count($input); $j++) {
+        $combination[] = $input[$j];
+        if (array_sum($combination) >= $threshold) {
+          $totalCombinations++;
+        }
+      }
+    }
+    return [$totalCombinations == 1, $totalCombinations <= 0];
+  }
+
+  /**
+   * sort queries by number of records for the table associated with them.
+   * @param $tableQueries
+   */
+  public static function orderByTableCount(&$tableQueries) {
+    static $tableCount = [];
+
+    $tempArray = [];
+    foreach ($tableQueries as $key => $query) {
+      $table = explode(".", $key);
+      $table = $table[0];
+      if (!array_key_exists($table, $tableCount)) {
+        $query = "SELECT COUNT(*) FROM {$table}";
+        $tableCount[$table] = CRM_Core_DAO::singleValueQuery($query);
+      }
+      $tempArray[$key] = $tableCount[$table];
+    }
+
+    asort($tempArray);
+    foreach ($tempArray as $key => $count) {
+      $tempArray[$key] = $tableQueries[$key];
+    }
+    $tableQueries = $tempArray;
+  }
+
+  /**
+   * Return the SQL query for getting only the interesting results out of the dedupe table.
+   *
+   * @$checkPermission boolean $params a flag to indicate if permission should be considered.
+   * default is to always check permissioning but public pages for example might not want
+   * permission to be checked for anonymous users. Refer CRM-6211. We might be beaking
+   * Multi-Site dedupe for public pages.
+   *
+   * @param bool $checkPermission
+   *
+   * @return string
+   */
+  public function thresholdQuery($checkPermission = TRUE) {
+    $this->_aclFrom = '';
+    $aclWhere = '';
+
+    if ($this->params && !$this->noRules) {
+      if ($checkPermission) {
+        list($this->_aclFrom, $aclWhere) = CRM_Contact_BAO_Contact_Permission::cacheClause('civicrm_contact');
+        $aclWhere = $aclWhere ? "AND {$aclWhere}" : '';
+      }
+      $query = "SELECT {$this->temporaryTables['dedupe']}.id1 as id
+                FROM {$this->temporaryTables['dedupe']} JOIN civicrm_contact ON {$this->temporaryTables['dedupe']}.id1 = civicrm_contact.id {$this->_aclFrom}
+                WHERE contact_type = '{$this->contact_type}' AND is_deleted = 0 $aclWhere
+                AND weight >= {$this->threshold}";
+    }
+    else {
+      $aclWhere = '';
+      if ($checkPermission) {
+        list($this->_aclFrom, $aclWhere) = CRM_Contact_BAO_Contact_Permission::cacheClause(['c1', 'c2']);
+        $aclWhere = $aclWhere ? "AND {$aclWhere}" : '';
+      }
+      $query = "SELECT IF({$this->temporaryTables['dedupe']}.id1 < {$this->temporaryTables['dedupe']}.id2, {$this->temporaryTables['dedupe']}.id1, {$this->temporaryTables['dedupe']}.id2) as id1,
+                IF({$this->temporaryTables['dedupe']}.id1 < {$this->temporaryTables['dedupe']}.id2, {$this->temporaryTables['dedupe']}.id2, {$this->temporaryTables['dedupe']}.id1) as id2, {$this->temporaryTables['dedupe']}.weight
+                FROM {$this->temporaryTables['dedupe']} JOIN civicrm_contact c1 ON {$this->temporaryTables['dedupe']}.id1 = c1.id
+                            JOIN civicrm_contact c2 ON {$this->temporaryTables['dedupe']}.id2 = c2.id {$this->_aclFrom}
+                       LEFT JOIN civicrm_dedupe_exception exc ON {$this->temporaryTables['dedupe']}.id1 = exc.contact_id1 AND {$this->temporaryTables['dedupe']}.id2 = exc.contact_id2
+                WHERE c1.contact_type = '{$this->contact_type}' AND
+                      c2.contact_type = '{$this->contact_type}'
+                       AND c1.is_deleted = 0 AND c2.is_deleted = 0
+                      {$aclWhere}
+                      AND weight >= {$this->threshold} AND exc.contact_id1 IS NULL";
+    }
+
+    CRM_Utils_Hook::dupeQuery($this, 'threshold', $query);
+    return $query;
+  }
+
+  /**
+   * find fields related to a rule group.
+   *
+   * @param array $params
+   *
+   * @return array
+   *   (rule field => weight) array and threshold associated to rule group
+   */
+  public static function dedupeRuleFieldsWeight($params) {
+    $rgBao = new CRM_Dedupe_BAO_RuleGroup();
+    $rgBao->contact_type = $params['contact_type'];
+    if (!empty($params['id'])) {
+      // accept an ID if provided
+      $rgBao->id = $params['id'];
+    }
+    else {
+      $rgBao->used = $params['used'];
+    }
+    $rgBao->find(TRUE);
+
+    $ruleBao = new CRM_Dedupe_BAO_Rule();
+    $ruleBao->dedupe_rule_group_id = $rgBao->id;
+    $ruleBao->find();
+    $ruleFields = [];
+    while ($ruleBao->fetch()) {
+      $field_name = $ruleBao->rule_field;
+      if ($field_name == 'phone_numeric') {
+        $field_name = 'phone';
+      }
+      $ruleFields[$field_name] = $ruleBao->rule_weight;
+    }
+
+    return [$ruleFields, $rgBao->threshold];
+  }
+
+  /**
+   * Get all of the combinations of fields that would work with a rule.
+   *
+   * @param array $rgFields
+   * @param int $threshold
+   * @param array $combos
+   * @param array $running
+   */
+  public static function combos($rgFields, $threshold, &$combos, $running = []) {
+    foreach ($rgFields as $rgField => $weight) {
+      unset($rgFields[$rgField]);
+      $diff = $threshold - $weight;
+      $runningnow = $running;
+      $runningnow[] = $rgField;
+      if ($diff > 0) {
+        self::combos($rgFields, $diff, $combos, $runningnow);
+      }
+      else {
+        $combos[] = $runningnow;
+      }
+    }
+  }
+
+  /**
+   * Get an array of rule group id to rule group name
+   * for all th groups for that contactType. If contactType
+   * not specified, do it for all
+   *
+   * @param string $contactType
+   *   Individual, Household or Organization.
+   *
+   *
+   * @return array
+   *   id => "nice name" of rule group
+   */
+  public static function getByType($contactType = NULL) {
+    $dao = new CRM_Dedupe_DAO_RuleGroup();
+
+    if ($contactType) {
+      $dao->contact_type = $contactType;
+    }
+
+    $dao->find();
+    $result = [];
+    while ($dao->fetch()) {
+      $title = !empty($dao->title) ? $dao->title : (!empty($dao->name) ? $dao->name : $dao->contact_type);
+
+      $name = "$title - {$dao->used}";
+      $result[$dao->id] = $name;
+    }
+    return $result;
+  }
+
+  /**
+   * Get the cached contact type for a particular rule group.
+   *
+   * @param int $rule_group_id
+   *
+   * @return string
+   */
+  public static function getContactTypeForRuleGroup($rule_group_id) {
+    if (!isset(\Civi::$statics[__CLASS__]) || !isset(\Civi::$statics[__CLASS__]['rule_groups'])) {
+      \Civi::$statics[__CLASS__]['rule_groups'] = [];
+    }
+    if (empty(\Civi::$statics[__CLASS__]['rule_groups'][$rule_group_id])) {
+      \Civi::$statics[__CLASS__]['rule_groups'][$rule_group_id]['contact_type'] = CRM_Core_DAO::getFieldValue(
+        'CRM_Dedupe_DAO_RuleGroup',
+        $rule_group_id,
+        'contact_type'
+      );
+    }
+
+    return \Civi::$statics[__CLASS__]['rule_groups'][$rule_group_id]['contact_type'];
+  }
+
+}
index 6097887055a5d70412ae2d8240384f9a467fb394..e3777e7fe6f88d95fd142cb33e182e744ae2c481 100644 (file)
 /**
  * Manages dedupe exceptions - ie pairs marked as non-duplicates.
  */
-class CRM_Dedupe_BAO_Exception extends CRM_Dedupe_DAO_Exception {
-
-  /**
-   * Create a dedupe exception record.
-   *
-   * @param array $params
-   *
-   * @return \CRM_Dedupe_BAO_Exception
-   */
-  public static function create($params) {
-    $hook = empty($params['id']) ? 'create' : 'edit';
-    CRM_Utils_Hook::pre($hook, 'Exception', CRM_Utils_Array::value('id', $params), $params);
-    $contact1 = $params['contact_id1'] ?? NULL;
-    $contact2 = $params['contact_id2'] ?? NULL;
-    $dao = new CRM_Dedupe_BAO_Exception();
-    $dao->copyValues($params);
-    if ($contact1 && $contact2) {
-      CRM_Core_DAO::singleValueQuery("
-        DELETE FROM civicrm_prevnext_cache
-        WHERE (entity_id1 = %1 AND entity_id2 = %2)
-        OR (entity_id1 = %2 AND entity_id2 = %2)",
-        [1 => [$contact1, 'Integer'], 2 => [$contact2, 'Integer']]
-      );
-      if ($contact2 < $contact1) {
-        // These are expected to be saved lowest first.
-        $dao->contact_id1 = $contact2;
-        $dao->contact_id2 = $contact1;
-      }
-    }
-    $dao->save();
-
-    CRM_Utils_Hook::post($hook, 'Exception', $dao->id, $dao);
-    return $dao;
-  }
-
-}
+class CRM_Dedupe_BAO_Exception extends CRM_Dedupe_BAO_DedupeException {}
index 6b7f63be7b84b468e87845a18bed06185a4269de..0e4d31f94809edf887955ed84c53e868fb861d7a 100644 (file)
  * The CiviCRM duplicate discovery engine is based on an
  * algorithm designed by David Strauss <david@fourkitchens.com>.
  */
-class CRM_Dedupe_BAO_Rule extends CRM_Dedupe_DAO_Rule {
-
-  /**
-   * Ids of the contacts to limit the SQL queries (whole-database queries otherwise)
-   * @var array
-   */
-  public $contactIds = [];
-
-  /**
-   * Params to dedupe against (queries against the whole contact set otherwise)
-   * @var array
-   */
-  public $params = [];
-
-  /**
-   * Return the SQL query for the given rule - either for finding matching
-   * pairs of contacts, or for matching against the $params variable (if set).
-   *
-   * @return string
-   *   SQL query performing the search
-   *
-   * @throws \CRM_Core_Exception
-   * @throws \CiviCRM_API3_Exception
-   */
-  public function sql() {
-    if ($this->params &&
-      (!array_key_exists($this->rule_table, $this->params) ||
-        !array_key_exists($this->rule_field, $this->params[$this->rule_table])
-      )
-    ) {
-      // if params is present and doesn't have an entry for a field, don't construct the clause.
-      return NULL;
-    }
-
-    // we need to initialise WHERE, ON and USING here, as some table types
-    // extend them; $where is an array of required conditions, $on and
-    // $using are arrays of required field matchings (for substring and
-    // full matches, respectively)
-    $where = [];
-    $on = ["SUBSTR(t1.{$this->rule_field}, 1, {$this->rule_length}) = SUBSTR(t2.{$this->rule_field}, 1, {$this->rule_length})"];
-
-    $innerJoinClauses = [
-      "t1.{$this->rule_field} IS NOT NULL",
-      "t2.{$this->rule_field} IS NOT NULL",
-      "t1.{$this->rule_field} = t2.{$this->rule_field}",
-    ];
-
-    if (in_array($this->getFieldType($this->rule_field), CRM_Utils_Type::getTextTypes(), TRUE)) {
-      $innerJoinClauses[] = "t1.{$this->rule_field} <> ''";
-      $innerJoinClauses[] = "t2.{$this->rule_field} <> ''";
-    }
-
-    switch ($this->rule_table) {
-      case 'civicrm_contact':
-        $id = 'id';
-        //we should restrict by contact type in the first step
-        $sql = "SELECT contact_type FROM civicrm_dedupe_rule_group WHERE id = {$this->dedupe_rule_group_id};";
-        $ct = CRM_Core_DAO::singleValueQuery($sql);
-        if ($this->params) {
-          $where[] = "t1.contact_type = '{$ct}'";
-        }
-        else {
-          $where[] = "t1.contact_type = '{$ct}'";
-          $where[] = "t2.contact_type = '{$ct}'";
-        }
-        break;
-
-      case 'civicrm_address':
-      case 'civicrm_email':
-      case 'civicrm_im':
-      case 'civicrm_openid':
-      case 'civicrm_phone':
-        $id = 'contact_id';
-        break;
-
-      case 'civicrm_note':
-        $id = 'entity_id';
-        if ($this->params) {
-          $where[] = "t1.entity_table = 'civicrm_contact'";
-        }
-        else {
-          $where[] = "t1.entity_table = 'civicrm_contact'";
-          $where[] = "t2.entity_table = 'civicrm_contact'";
-        }
-        break;
-
-      default:
-        // custom data tables
-        if (preg_match('/^civicrm_value_/', $this->rule_table) || preg_match('/^custom_value_/', $this->rule_table)) {
-          $id = 'entity_id';
-        }
-        else {
-          throw new CRM_Core_Exception("Unsupported rule_table for civicrm_dedupe_rule.id of {$this->id}");
-        }
-        break;
-    }
-
-    // build SELECT based on the field names containing contact ids
-    // if there are params provided, id1 should be 0
-    if ($this->params) {
-      $select = "t1.$id id1, {$this->rule_weight} weight";
-      $subSelect = 'id1, weight';
-    }
-    else {
-      $select = "t1.$id id1, t2.$id id2, {$this->rule_weight} weight";
-      $subSelect = 'id1, id2, weight';
-    }
-
-    // build FROM (and WHERE, if it's a parametrised search)
-    // based on whether the rule is about substrings or not
-    if ($this->params) {
-      $from = "{$this->rule_table} t1";
-      $str = 'NULL';
-      if (isset($this->params[$this->rule_table][$this->rule_field])) {
-        $str = trim(CRM_Utils_Type::escape($this->params[$this->rule_table][$this->rule_field], 'String'));
-      }
-      if ($this->rule_length) {
-        $where[] = "SUBSTR(t1.{$this->rule_field}, 1, {$this->rule_length}) = SUBSTR('$str', 1, {$this->rule_length})";
-        $where[] = "t1.{$this->rule_field} IS NOT NULL";
-      }
-      else {
-        $where[] = "t1.{$this->rule_field} = '$str'";
-      }
-    }
-    else {
-      if ($this->rule_length) {
-        $from = "{$this->rule_table} t1 JOIN {$this->rule_table} t2 ON (" . implode(' AND ', $on) . ")";
-      }
-      else {
-        $from = "{$this->rule_table} t1 INNER JOIN {$this->rule_table} t2 ON (" . implode(' AND ', $innerJoinClauses) . ")";
-      }
-    }
-
-    // finish building WHERE, also limit the results if requested
-    if (!$this->params) {
-      $where[] = "t1.$id < t2.$id";
-    }
-    $query = "SELECT $select FROM $from WHERE " . implode(' AND ', $where);
-    if ($this->contactIds) {
-      $cids = [];
-      foreach ($this->contactIds as $cid) {
-        $cids[] = CRM_Utils_Type::escape($cid, 'Integer');
-      }
-      if (count($cids) == 1) {
-        $query .= " AND (t1.$id = {$cids[0]}) UNION $query AND t2.$id = {$cids[0]}";
-      }
-      else {
-        $query .= " AND t1.$id IN (" . implode(',', $cids) . ")
-        UNION $query AND  t2.$id IN (" . implode(',', $cids) . ")";
-      }
-      // The `weight` is ambiguous in the context of the union; put the whole
-      // thing in a subquery.
-      $query = "SELECT $subSelect FROM ($query) subunion";
-    }
-
-    return $query;
-  }
-
-  /**
-   * find fields related to a rule group.
-   *
-   * @param array $params contains the rule group property to identify rule group
-   *
-   * @return array
-   *   rule fields array associated to rule group
-   */
-  public static function dedupeRuleFields($params) {
-    $rgBao = new CRM_Dedupe_BAO_RuleGroup();
-    $rgBao->used = $params['used'];
-    $rgBao->contact_type = $params['contact_type'];
-    $rgBao->find(TRUE);
-
-    $ruleBao = new CRM_Dedupe_BAO_Rule();
-    $ruleBao->dedupe_rule_group_id = $rgBao->id;
-    $ruleBao->find();
-    $ruleFields = [];
-    while ($ruleBao->fetch()) {
-      $field_name = $ruleBao->rule_field;
-      if ($field_name == 'phone_numeric') {
-        $field_name = 'phone';
-      }
-      $ruleFields[] = $field_name;
-    }
-    return $ruleFields;
-  }
-
-  /**
-   * @param int $cid
-   * @param int $oid
-   *
-   * @return bool
-   */
-  public static function validateContacts($cid, $oid) {
-    if (!$cid || !$oid) {
-      return NULL;
-    }
-    $exception = new CRM_Dedupe_DAO_Exception();
-    $exception->contact_id1 = $cid;
-    $exception->contact_id2 = $oid;
-    //make sure contact2 > contact1.
-    if ($cid > $oid) {
-      $exception->contact_id1 = $oid;
-      $exception->contact_id2 = $cid;
-    }
-
-    return !$exception->find(TRUE);
-  }
-
-  /**
-   * Get the specification for the given field.
-   *
-   * @param string $fieldName
-   *
-   * @return array
-   * @throws \CiviCRM_API3_Exception
-   */
-  public function getFieldType($fieldName) {
-    $entity = CRM_Core_DAO_AllCoreTables::getBriefName(CRM_Core_DAO_AllCoreTables::getClassForTable($this->rule_table));
-    if (!$entity) {
-      // This means we have stored a custom field rather than an entity name in rule_table, figure out the entity.
-      $entity = civicrm_api3('CustomGroup', 'getvalue', ['table_name' => $this->rule_table, 'return' => 'extends']);
-      if (in_array($entity, ['Individual', 'Household', 'Organization'])) {
-        $entity = 'Contact';
-      }
-      $fieldName = 'custom_' . civicrm_api3('CustomField', 'getvalue', ['column_name' => $fieldName, 'return' => 'id']);
-    }
-    $fields = civicrm_api3($entity, 'getfields', ['action' => 'create'])['values'];
-    return $fields[$fieldName]['type'];
-  }
-
-}
+class CRM_Dedupe_BAO_Rule extends CRM_Dedupe_BAO_DedupeRule {}
index 6cbb407c844b1e5ee4096fcc6f877bb1e09af7c5..1b70dcbffe598467870b25fc44f2de83d19ea798 100644 (file)
  * The CiviCRM duplicate discovery engine is based on an
  * algorithm designed by David Strauss <david@fourkitchens.com>.
  */
-class CRM_Dedupe_BAO_RuleGroup extends CRM_Dedupe_DAO_RuleGroup {
-
-  /**
-   * Ids of the contacts to limit the SQL queries (whole-database queries otherwise)
-   * @var array
-   */
-  public $contactIds = [];
-
-  /**
-   * Set the contact IDs to restrict the dedupe to.
-   *
-   * @param array $contactIds
-   */
-  public function setContactIds($contactIds) {
-    $this->contactIds = $contactIds;
-  }
-
-  /**
-   * Params to dedupe against (queries against the whole contact set otherwise)
-   * @var array
-   */
-  public $params = [];
-
-  /**
-   * If there are no rules in rule group.
-   * @var bool
-   */
-  public $noRules = FALSE;
-
-  protected $temporaryTables = [];
-
-  /**
-   * Return a structure holding the supported tables, fields and their titles
-   *
-   * @param string $requestedType
-   *   The requested contact type.
-   *
-   * @return array
-   *   a table-keyed array of field-keyed arrays holding supported fields' titles
-   */
-  public static function supportedFields($requestedType) {
-    static $fields = NULL;
-    if (!$fields) {
-      // this is needed, as we're piggy-backing importableFields() below
-      $replacements = [
-        'civicrm_country.name' => 'civicrm_address.country_id',
-        'civicrm_county.name' => 'civicrm_address.county_id',
-        'civicrm_state_province.name' => 'civicrm_address.state_province_id',
-        'gender.label' => 'civicrm_contact.gender_id',
-        'individual_prefix.label' => 'civicrm_contact.prefix_id',
-        'individual_suffix.label' => 'civicrm_contact.suffix_id',
-        'addressee.label' => 'civicrm_contact.addressee_id',
-        'email_greeting.label' => 'civicrm_contact.email_greeting_id',
-        'postal_greeting.label' => 'civicrm_contact.postal_greeting_id',
-        'civicrm_phone.phone' => 'civicrm_phone.phone_numeric',
-      ];
-      // the table names we support in dedupe rules - a filter for importableFields()
-      $supportedTables = [
-        'civicrm_address',
-        'civicrm_contact',
-        'civicrm_email',
-        'civicrm_im',
-        'civicrm_note',
-        'civicrm_openid',
-        'civicrm_phone',
-      ];
-
-      foreach (['Individual', 'Organization', 'Household'] as $ctype) {
-        // take the table.field pairs and their titles from importableFields() if the table is supported
-        foreach (CRM_Contact_BAO_Contact::importableFields($ctype) as $iField) {
-          if (isset($iField['where'])) {
-            $where = $iField['where'];
-            if (isset($replacements[$where])) {
-              $where = $replacements[$where];
-            }
-            list($table, $field) = explode('.', $where);
-            if (!in_array($table, $supportedTables)) {
-              continue;
-            }
-            $fields[$ctype][$table][$field] = $iField['title'];
-          }
-        }
-        // Note that most of the fields available come from 'importable fields' -
-        // I thought about making this field 'importable' but it felt like there might be unknown consequences
-        // so I opted for just adding it in & securing it with a unit test.
-        /// Example usage of sort_name - It is possible to alter sort name via hook so 2 organization names might differ as in
-        // Justice League vs The Justice League but these could have the same sort_name if 'the the'
-        // exension is installed (https://github.com/eileenmcnaughton/org.wikimedia.thethe)
-        $fields[$ctype]['civicrm_contact']['sort_name'] = ts('Sort Name');
-        // add custom data fields
-        foreach (CRM_Core_BAO_CustomGroup::getTree($ctype, NULL, NULL, -1) as $key => $cg) {
-          if (!is_int($key)) {
-            continue;
-          }
-          foreach ($cg['fields'] as $cf) {
-            $fields[$ctype][$cg['table_name']][$cf['column_name']] = $cf['label'];
-          }
-        }
-      }
-    }
-    CRM_Utils_Hook::dupeQuery(CRM_Core_DAO::$_nullObject, 'supportedFields', $fields);
-    return !empty($fields[$requestedType]) ? $fields[$requestedType] : [];
-  }
-
-  /**
-   * Return the SQL query for dropping the temporary table.
-   */
-  public function tableDropQuery() {
-    return 'DROP TEMPORARY TABLE IF EXISTS dedupe';
-  }
-
-  /**
-   * Return a set of SQL queries whose cummulative weights will mark matched
-   * records for the RuleGroup::threasholdQuery() to retrieve.
-   */
-  public function tableQuery() {
-    // make sure we've got a fetched dbrecord, not sure if this is enforced
-    if (!$this->name == NULL || $this->is_reserved == NULL) {
-      $this->find(TRUE);
-    }
-
-    // Reserved Rule Groups can optionally get special treatment by
-    // implementing an optimization class and returning a query array.
-    if ($this->is_reserved &&
-      CRM_Utils_File::isIncludable("CRM/Dedupe/BAO/QueryBuilder/{$this->name}.php")
-    ) {
-      $command = empty($this->params) ? 'internal' : 'record';
-      $queries = call_user_func(["CRM_Dedupe_BAO_QueryBuilder_{$this->name}", $command], $this);
-    }
-    else {
-      // All other rule groups have queries generated by the member dedupe
-      // rules defined in the administrative interface.
-
-      // Find all rules contained by this script sorted by weight so that
-      // their execution can be short circuited on RuleGroup::fillTable()
-      $bao = new CRM_Dedupe_BAO_Rule();
-      $bao->dedupe_rule_group_id = $this->id;
-      $bao->orderBy('rule_weight DESC');
-      $bao->find();
-
-      // Generate a SQL query for each rule in the rule group that is
-      // tailored to respect the param and contactId options provided.
-      $queries = [];
-      while ($bao->fetch()) {
-        $bao->contactIds = $this->contactIds;
-        $bao->params = $this->params;
-
-        // Skipping empty rules? Empty rules shouldn't exist; why check?
-        if ($query = $bao->sql()) {
-          $queries["{$bao->rule_table}.{$bao->rule_field}.{$bao->rule_weight}"] = $query;
-        }
-      }
-    }
-
-    // if there are no rules in this rule group
-    // add an empty query fulfilling the pattern
-    if (!$queries) {
-      $this->noRules = TRUE;
-      return [];
-    }
-
-    return $queries;
-  }
-
-  public function fillTable() {
-    // get the list of queries handy
-    $tableQueries = $this->tableQuery();
-
-    if ($this->params && !$this->noRules) {
-      $this->temporaryTables['dedupe'] = CRM_Utils_SQL_TempTable::build()
-        ->setCategory('dedupe')
-        ->createWithColumns("id1 int, weight int, UNIQUE UI_id1 (id1)")->getName();
-      $dedupeCopyTemporaryTableObject = CRM_Utils_SQL_TempTable::build()
-        ->setCategory('dedupe');
-      $this->temporaryTables['dedupe_copy'] = $dedupeCopyTemporaryTableObject->getName();
-      $insertClause = "INSERT INTO {$this->temporaryTables['dedupe']}  (id1, weight)";
-      $groupByClause = "GROUP BY id1, weight";
-      $dupeCopyJoin = " JOIN {$this->temporaryTables['dedupe_copy']} ON {$this->temporaryTables['dedupe_copy']}.id1 = t1.column WHERE ";
-    }
-    else {
-      $this->temporaryTables['dedupe'] = CRM_Utils_SQL_TempTable::build()
-        ->setCategory('dedupe')
-        ->createWithColumns("id1 int, id2 int, weight int, UNIQUE UI_id1_id2 (id1, id2)")->getName();
-      $dedupeCopyTemporaryTableObject = CRM_Utils_SQL_TempTable::build()
-        ->setCategory('dedupe');
-      $this->temporaryTables['dedupe_copy'] = $dedupeCopyTemporaryTableObject->getName();
-      $insertClause = "INSERT INTO {$this->temporaryTables['dedupe']}  (id1, id2, weight)";
-      $groupByClause = "GROUP BY id1, id2, weight";
-      $dupeCopyJoin = " JOIN {$this->temporaryTables['dedupe_copy']} ON {$this->temporaryTables['dedupe_copy']}.id1 = t1.column AND {$this->temporaryTables['dedupe_copy']}.id2 = t2.column WHERE ";
-    }
-    $patternColumn = '/t1.(\w+)/';
-    $exclWeightSum = [];
-
-    CRM_Utils_Hook::dupeQuery($this, 'table', $tableQueries);
-
-    while (!empty($tableQueries)) {
-      list($isInclusive, $isDie) = self::isQuerySetInclusive($tableQueries, $this->threshold, $exclWeightSum);
-
-      if ($isInclusive) {
-        // order queries by table count
-        self::orderByTableCount($tableQueries);
-
-        $weightSum = array_sum($exclWeightSum);
-        $searchWithinDupes = !empty($exclWeightSum) ? 1 : 0;
-
-        while (!empty($tableQueries)) {
-          // extract the next query ( and weight ) to be executed
-          $fieldWeight = array_keys($tableQueries);
-          $fieldWeight = $fieldWeight[0];
-          $query = array_shift($tableQueries);
-
-          if ($searchWithinDupes) {
-            // drop dedupe_copy table just in case if its already there.
-            $dedupeCopyTemporaryTableObject->drop();
-            // get prepared to search within already found dupes if $searchWithinDupes flag is set
-            $dedupeCopyTemporaryTableObject->createWithQuery("SELECT * FROM {$this->temporaryTables['dedupe']} WHERE weight >= {$weightSum}");
-
-            preg_match($patternColumn, $query, $matches);
-            $query = str_replace(' WHERE ', str_replace('column', $matches[1], $dupeCopyJoin), $query);
-
-            // CRM-19612: If there's a union, there will be two WHEREs, and you
-            // can't use the temp table twice.
-            if (preg_match('/' . $this->temporaryTables['dedupe_copy'] . '[\S\s]*(union)[\S\s]*' . $this->temporaryTables['dedupe_copy'] . '/i', $query, $matches, PREG_OFFSET_CAPTURE)) {
-              // Make a second temp table:
-              $this->temporaryTables['dedupe_copy_2'] = CRM_Utils_SQL_TempTable::build()
-                ->setCategory('dedupe')
-                ->createWithQuery("SELECT * FROM {$this->temporaryTables['dedupe']} WHERE weight >= {$weightSum}")
-                ->getName();
-              // After the union, use that new temp table:
-              $part1 = substr($query, 0, $matches[1][1]);
-              $query = $part1 . str_replace($this->temporaryTables['dedupe_copy'], $this->temporaryTables['dedupe_copy_2'], substr($query, $matches[1][1]));
-            }
-          }
-          $searchWithinDupes = 1;
-
-          // construct and execute the intermediate query
-          $query = "{$insertClause} {$query} {$groupByClause} ON DUPLICATE KEY UPDATE weight = weight + VALUES(weight)";
-          $dao = CRM_Core_DAO::executeQuery($query);
-
-          // FIXME: we need to be more acurate with affected rows, especially for insert vs duplicate insert.
-          // And that will help optimize further.
-          $affectedRows = $dao->affectedRows();
-
-          // In an inclusive situation, failure of any query means no further processing -
-          if ($affectedRows == 0) {
-            // reset to make sure no further execution is done.
-            $tableQueries = [];
-            break;
-          }
-          $weightSum = substr($fieldWeight, strrpos($fieldWeight, '.') + 1) + $weightSum;
-        }
-        // An exclusive situation -
-      }
-      elseif (!$isDie) {
-        // since queries are already sorted by weights, we can continue as is
-        $fieldWeight = array_keys($tableQueries);
-        $fieldWeight = $fieldWeight[0];
-        $query = array_shift($tableQueries);
-        $query = "{$insertClause} {$query} {$groupByClause} ON DUPLICATE KEY UPDATE weight = weight + VALUES(weight)";
-        $dao = CRM_Core_DAO::executeQuery($query);
-        if ($dao->affectedRows() >= 1) {
-          $exclWeightSum[] = substr($fieldWeight, strrpos($fieldWeight, '.') + 1);
-        }
-      }
-      else {
-        // its a die situation
-        break;
-      }
-    }
-  }
-
-  /**
-   * Function to determine if a given query set contains inclusive or exclusive set of weights.
-   * The function assumes that the query set is already ordered by weight in desc order.
-   * @param $tableQueries
-   * @param $threshold
-   * @param array $exclWeightSum
-   *
-   * @return array
-   */
-  public static function isQuerySetInclusive($tableQueries, $threshold, $exclWeightSum = []) {
-    $input = [];
-    foreach ($tableQueries as $key => $query) {
-      $input[] = substr($key, strrpos($key, '.') + 1);
-    }
-
-    if (!empty($exclWeightSum)) {
-      $input = array_merge($input, $exclWeightSum);
-      rsort($input);
-    }
-
-    if (count($input) == 1) {
-      return [FALSE, $input[0] < $threshold];
-    }
-
-    $totalCombinations = 0;
-    for ($i = 0; $i < count($input); $i++) {
-      $combination = [$input[$i]];
-      if (array_sum($combination) >= $threshold) {
-        $totalCombinations++;
-        continue;
-      }
-      for ($j = $i + 1; $j < count($input); $j++) {
-        $combination[] = $input[$j];
-        if (array_sum($combination) >= $threshold) {
-          $totalCombinations++;
-        }
-      }
-    }
-    return [$totalCombinations == 1, $totalCombinations <= 0];
-  }
-
-  /**
-   * sort queries by number of records for the table associated with them.
-   * @param $tableQueries
-   */
-  public static function orderByTableCount(&$tableQueries) {
-    static $tableCount = [];
-
-    $tempArray = [];
-    foreach ($tableQueries as $key => $query) {
-      $table = explode(".", $key);
-      $table = $table[0];
-      if (!array_key_exists($table, $tableCount)) {
-        $query = "SELECT COUNT(*) FROM {$table}";
-        $tableCount[$table] = CRM_Core_DAO::singleValueQuery($query);
-      }
-      $tempArray[$key] = $tableCount[$table];
-    }
-
-    asort($tempArray);
-    foreach ($tempArray as $key => $count) {
-      $tempArray[$key] = $tableQueries[$key];
-    }
-    $tableQueries = $tempArray;
-  }
-
-  /**
-   * Return the SQL query for getting only the interesting results out of the dedupe table.
-   *
-   * @$checkPermission boolean $params a flag to indicate if permission should be considered.
-   * default is to always check permissioning but public pages for example might not want
-   * permission to be checked for anonymous users. Refer CRM-6211. We might be beaking
-   * Multi-Site dedupe for public pages.
-   *
-   * @param bool $checkPermission
-   *
-   * @return string
-   */
-  public function thresholdQuery($checkPermission = TRUE) {
-    $this->_aclFrom = '';
-    $aclWhere = '';
-
-    if ($this->params && !$this->noRules) {
-      if ($checkPermission) {
-        list($this->_aclFrom, $aclWhere) = CRM_Contact_BAO_Contact_Permission::cacheClause('civicrm_contact');
-        $aclWhere = $aclWhere ? "AND {$aclWhere}" : '';
-      }
-      $query = "SELECT {$this->temporaryTables['dedupe']}.id1 as id
-                FROM {$this->temporaryTables['dedupe']} JOIN civicrm_contact ON {$this->temporaryTables['dedupe']}.id1 = civicrm_contact.id {$this->_aclFrom}
-                WHERE contact_type = '{$this->contact_type}' AND is_deleted = 0 $aclWhere
-                AND weight >= {$this->threshold}";
-    }
-    else {
-      $aclWhere = '';
-      if ($checkPermission) {
-        list($this->_aclFrom, $aclWhere) = CRM_Contact_BAO_Contact_Permission::cacheClause(['c1', 'c2']);
-        $aclWhere = $aclWhere ? "AND {$aclWhere}" : '';
-      }
-      $query = "SELECT IF({$this->temporaryTables['dedupe']}.id1 < {$this->temporaryTables['dedupe']}.id2, {$this->temporaryTables['dedupe']}.id1, {$this->temporaryTables['dedupe']}.id2) as id1,
-                IF({$this->temporaryTables['dedupe']}.id1 < {$this->temporaryTables['dedupe']}.id2, {$this->temporaryTables['dedupe']}.id2, {$this->temporaryTables['dedupe']}.id1) as id2, {$this->temporaryTables['dedupe']}.weight
-                FROM {$this->temporaryTables['dedupe']} JOIN civicrm_contact c1 ON {$this->temporaryTables['dedupe']}.id1 = c1.id
-                            JOIN civicrm_contact c2 ON {$this->temporaryTables['dedupe']}.id2 = c2.id {$this->_aclFrom}
-                       LEFT JOIN civicrm_dedupe_exception exc ON {$this->temporaryTables['dedupe']}.id1 = exc.contact_id1 AND {$this->temporaryTables['dedupe']}.id2 = exc.contact_id2
-                WHERE c1.contact_type = '{$this->contact_type}' AND
-                      c2.contact_type = '{$this->contact_type}'
-                       AND c1.is_deleted = 0 AND c2.is_deleted = 0
-                      {$aclWhere}
-                      AND weight >= {$this->threshold} AND exc.contact_id1 IS NULL";
-    }
-
-    CRM_Utils_Hook::dupeQuery($this, 'threshold', $query);
-    return $query;
-  }
-
-  /**
-   * find fields related to a rule group.
-   *
-   * @param array $params
-   *
-   * @return array
-   *   (rule field => weight) array and threshold associated to rule group
-   */
-  public static function dedupeRuleFieldsWeight($params) {
-    $rgBao = new CRM_Dedupe_BAO_RuleGroup();
-    $rgBao->contact_type = $params['contact_type'];
-    if (!empty($params['id'])) {
-      // accept an ID if provided
-      $rgBao->id = $params['id'];
-    }
-    else {
-      $rgBao->used = $params['used'];
-    }
-    $rgBao->find(TRUE);
-
-    $ruleBao = new CRM_Dedupe_BAO_Rule();
-    $ruleBao->dedupe_rule_group_id = $rgBao->id;
-    $ruleBao->find();
-    $ruleFields = [];
-    while ($ruleBao->fetch()) {
-      $field_name = $ruleBao->rule_field;
-      if ($field_name == 'phone_numeric') {
-        $field_name = 'phone';
-      }
-      $ruleFields[$field_name] = $ruleBao->rule_weight;
-    }
-
-    return [$ruleFields, $rgBao->threshold];
-  }
-
-  /**
-   * Get all of the combinations of fields that would work with a rule.
-   *
-   * @param array $rgFields
-   * @param int $threshold
-   * @param array $combos
-   * @param array $running
-   */
-  public static function combos($rgFields, $threshold, &$combos, $running = []) {
-    foreach ($rgFields as $rgField => $weight) {
-      unset($rgFields[$rgField]);
-      $diff = $threshold - $weight;
-      $runningnow = $running;
-      $runningnow[] = $rgField;
-      if ($diff > 0) {
-        self::combos($rgFields, $diff, $combos, $runningnow);
-      }
-      else {
-        $combos[] = $runningnow;
-      }
-    }
-  }
-
-  /**
-   * Get an array of rule group id to rule group name
-   * for all th groups for that contactType. If contactType
-   * not specified, do it for all
-   *
-   * @param string $contactType
-   *   Individual, Household or Organization.
-   *
-   *
-   * @return array
-   *   id => "nice name" of rule group
-   */
-  public static function getByType($contactType = NULL) {
-    $dao = new CRM_Dedupe_DAO_RuleGroup();
-
-    if ($contactType) {
-      $dao->contact_type = $contactType;
-    }
-
-    $dao->find();
-    $result = [];
-    while ($dao->fetch()) {
-      $title = !empty($dao->title) ? $dao->title : (!empty($dao->name) ? $dao->name : $dao->contact_type);
-
-      $name = "$title - {$dao->used}";
-      $result[$dao->id] = $name;
-    }
-    return $result;
-  }
-
-  /**
-   * Get the cached contact type for a particular rule group.
-   *
-   * @param int $rule_group_id
-   *
-   * @return string
-   */
-  public static function getContactTypeForRuleGroup($rule_group_id) {
-    if (!isset(\Civi::$statics[__CLASS__]) || !isset(\Civi::$statics[__CLASS__]['rule_groups'])) {
-      \Civi::$statics[__CLASS__]['rule_groups'] = [];
-    }
-    if (empty(\Civi::$statics[__CLASS__]['rule_groups'][$rule_group_id])) {
-      \Civi::$statics[__CLASS__]['rule_groups'][$rule_group_id]['contact_type'] = CRM_Core_DAO::getFieldValue(
-        'CRM_Dedupe_DAO_RuleGroup',
-        $rule_group_id,
-        'contact_type'
-      );
-    }
-
-    return \Civi::$statics[__CLASS__]['rule_groups'][$rule_group_id]['contact_type'];
-  }
-
-}
+class CRM_Dedupe_BAO_RuleGroup extends CRM_Dedupe_BAO_DedupeRuleGroup {}
diff --git a/CRM/Dedupe/DAO/DedupeException.php b/CRM/Dedupe/DAO/DedupeException.php
new file mode 100644 (file)
index 0000000..2a73b7a
--- /dev/null
@@ -0,0 +1,231 @@
+<?php
+
+/**
+ * @package CRM
+ * @copyright CiviCRM LLC https://civicrm.org/licensing
+ *
+ * Generated from xml/schema/CRM/Dedupe/DedupeException.xml
+ * DO NOT EDIT.  Generated by CRM_Core_CodeGen
+ * (GenCodeChecksum:fdf10a930b1f5d921391bc085a8e486b)
+ */
+
+/**
+ * Database access object for the DedupeException entity.
+ */
+class CRM_Dedupe_DAO_DedupeException extends CRM_Core_DAO {
+  const EXT = 'civicrm';
+  const TABLE_ADDED = '3.3';
+
+  /**
+   * Static instance to hold the table name.
+   *
+   * @var string
+   */
+  public static $_tableName = 'civicrm_dedupe_exception';
+
+  /**
+   * Should CiviCRM log any modifications to this table in the civicrm_log table.
+   *
+   * @var bool
+   */
+  public static $_log = FALSE;
+
+  /**
+   * Unique dedupe exception id
+   *
+   * @var int
+   */
+  public $id;
+
+  /**
+   * FK to Contact ID
+   *
+   * @var int
+   */
+  public $contact_id1;
+
+  /**
+   * FK to Contact ID
+   *
+   * @var int
+   */
+  public $contact_id2;
+
+  /**
+   * Class constructor.
+   */
+  public function __construct() {
+    $this->__table = 'civicrm_dedupe_exception';
+    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 ? ts('Dedupe Exceptions') : ts('Dedupe Exception');
+  }
+
+  /**
+   * 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_id1', 'civicrm_contact', 'id');
+      Civi::$statics[__CLASS__]['links'][] = new CRM_Core_Reference_Basic(self::getTableName(), 'contact_id2', '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,
+          'title' => ts('Dedupe Exception ID'),
+          'description' => ts('Unique dedupe exception id'),
+          'required' => TRUE,
+          'where' => 'civicrm_dedupe_exception.id',
+          'table_name' => 'civicrm_dedupe_exception',
+          'entity' => 'DedupeException',
+          'bao' => 'CRM_Dedupe_DAO_DedupeException',
+          'localizable' => 0,
+          'html' => [
+            'type' => 'Number',
+          ],
+          'readonly' => TRUE,
+          'add' => '3.3',
+        ],
+        'contact_id1' => [
+          'name' => 'contact_id1',
+          'type' => CRM_Utils_Type::T_INT,
+          'title' => ts('First Dupe Contact ID'),
+          'description' => ts('FK to Contact ID'),
+          'required' => TRUE,
+          'where' => 'civicrm_dedupe_exception.contact_id1',
+          'table_name' => 'civicrm_dedupe_exception',
+          'entity' => 'DedupeException',
+          'bao' => 'CRM_Dedupe_DAO_DedupeException',
+          'localizable' => 0,
+          'FKClassName' => 'CRM_Contact_DAO_Contact',
+          'html' => [
+            'label' => ts("First Dupe Contact"),
+          ],
+          'add' => '3.3',
+        ],
+        'contact_id2' => [
+          'name' => 'contact_id2',
+          'type' => CRM_Utils_Type::T_INT,
+          'title' => ts('Second Dupe Contact ID'),
+          'description' => ts('FK to Contact ID'),
+          'required' => TRUE,
+          'where' => 'civicrm_dedupe_exception.contact_id2',
+          'table_name' => 'civicrm_dedupe_exception',
+          'entity' => 'DedupeException',
+          'bao' => 'CRM_Dedupe_DAO_DedupeException',
+          'localizable' => 0,
+          'FKClassName' => 'CRM_Contact_DAO_Contact',
+          'html' => [
+            'label' => ts("Second Dupe Contact"),
+          ],
+          'add' => '3.3',
+        ],
+      ];
+      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__, 'dedupe_exception', $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__, 'dedupe_exception', $prefix, []);
+    return $r;
+  }
+
+  /**
+   * Returns the list of indices
+   *
+   * @param bool $localize
+   *
+   * @return array
+   */
+  public static function indices($localize = TRUE) {
+    $indices = [
+      'UI_contact_id1_contact_id2' => [
+        'name' => 'UI_contact_id1_contact_id2',
+        'field' => [
+          0 => 'contact_id1',
+          1 => 'contact_id2',
+        ],
+        'localizable' => FALSE,
+        'unique' => TRUE,
+        'sig' => 'civicrm_dedupe_exception::1::contact_id1::contact_id2',
+      ],
+    ];
+    return ($localize && !empty($indices)) ? CRM_Core_DAO_AllCoreTables::multilingualize(__CLASS__, $indices) : $indices;
+  }
+
+}
diff --git a/CRM/Dedupe/DAO/DedupeRule.php b/CRM/Dedupe/DAO/DedupeRule.php
new file mode 100644 (file)
index 0000000..7dc9120
--- /dev/null
@@ -0,0 +1,284 @@
+<?php
+
+/**
+ * @package CRM
+ * @copyright CiviCRM LLC https://civicrm.org/licensing
+ *
+ * Generated from xml/schema/CRM/Dedupe/DedupeRule.xml
+ * DO NOT EDIT.  Generated by CRM_Core_CodeGen
+ * (GenCodeChecksum:782d96ecc76d4020270fce04574a39b5)
+ */
+
+/**
+ * Database access object for the DedupeRule entity.
+ */
+class CRM_Dedupe_DAO_DedupeRule extends CRM_Core_DAO {
+  const EXT = 'civicrm';
+  const TABLE_ADDED = '1.8';
+
+  /**
+   * Static instance to hold the table name.
+   *
+   * @var string
+   */
+  public static $_tableName = 'civicrm_dedupe_rule';
+
+  /**
+   * Should CiviCRM log any modifications to this table in the civicrm_log table.
+   *
+   * @var bool
+   */
+  public static $_log = FALSE;
+
+  /**
+   * Unique dedupe rule id
+   *
+   * @var int
+   */
+  public $id;
+
+  /**
+   * The id of the rule group this rule belongs to
+   *
+   * @var int
+   */
+  public $dedupe_rule_group_id;
+
+  /**
+   * The name of the table this rule is about
+   *
+   * @var string
+   */
+  public $rule_table;
+
+  /**
+   * The name of the field of the table referenced in rule_table
+   *
+   * @var string
+   */
+  public $rule_field;
+
+  /**
+   * The length of the matching substring
+   *
+   * @var int
+   */
+  public $rule_length;
+
+  /**
+   * The weight of the rule
+   *
+   * @var int
+   */
+  public $rule_weight;
+
+  /**
+   * Class constructor.
+   */
+  public function __construct() {
+    $this->__table = 'civicrm_dedupe_rule';
+    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 ? ts('Dedupe Rules') : ts('Dedupe Rule');
+  }
+
+  /**
+   * 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(), 'dedupe_rule_group_id', 'civicrm_dedupe_rule_group', '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,
+          'title' => ts('Dedupe Rule ID'),
+          'description' => ts('Unique dedupe rule id'),
+          'required' => TRUE,
+          'where' => 'civicrm_dedupe_rule.id',
+          'table_name' => 'civicrm_dedupe_rule',
+          'entity' => 'DedupeRule',
+          'bao' => 'CRM_Dedupe_BAO_DedupeRule',
+          'localizable' => 0,
+          'html' => [
+            'type' => 'Number',
+          ],
+          'readonly' => TRUE,
+          'add' => '1.8',
+        ],
+        'dedupe_rule_group_id' => [
+          'name' => 'dedupe_rule_group_id',
+          'type' => CRM_Utils_Type::T_INT,
+          'title' => ts('Group ID'),
+          'description' => ts('The id of the rule group this rule belongs to'),
+          'required' => TRUE,
+          'where' => 'civicrm_dedupe_rule.dedupe_rule_group_id',
+          'table_name' => 'civicrm_dedupe_rule',
+          'entity' => 'DedupeRule',
+          'bao' => 'CRM_Dedupe_BAO_DedupeRule',
+          'localizable' => 0,
+          'FKClassName' => 'CRM_Dedupe_DAO_DedupeRuleGroup',
+          'html' => [
+            'label' => ts("Group"),
+          ],
+          'add' => '1.8',
+        ],
+        'rule_table' => [
+          'name' => 'rule_table',
+          'type' => CRM_Utils_Type::T_STRING,
+          'title' => ts('Rule Table'),
+          'description' => ts('The name of the table this rule is about'),
+          'required' => TRUE,
+          'maxlength' => 64,
+          'size' => CRM_Utils_Type::BIG,
+          'where' => 'civicrm_dedupe_rule.rule_table',
+          'table_name' => 'civicrm_dedupe_rule',
+          'entity' => 'DedupeRule',
+          'bao' => 'CRM_Dedupe_BAO_DedupeRule',
+          'localizable' => 0,
+          'add' => '1.8',
+        ],
+        'rule_field' => [
+          'name' => 'rule_field',
+          'type' => CRM_Utils_Type::T_STRING,
+          'title' => ts('Rule Field'),
+          'description' => ts('The name of the field of the table referenced in rule_table'),
+          'required' => TRUE,
+          'maxlength' => 64,
+          'size' => CRM_Utils_Type::BIG,
+          'where' => 'civicrm_dedupe_rule.rule_field',
+          'table_name' => 'civicrm_dedupe_rule',
+          'entity' => 'DedupeRule',
+          'bao' => 'CRM_Dedupe_BAO_DedupeRule',
+          'localizable' => 0,
+          'add' => '1.8',
+        ],
+        'rule_length' => [
+          'name' => 'rule_length',
+          'type' => CRM_Utils_Type::T_INT,
+          'title' => ts('Rule Length'),
+          'description' => ts('The length of the matching substring'),
+          'where' => 'civicrm_dedupe_rule.rule_length',
+          'table_name' => 'civicrm_dedupe_rule',
+          'entity' => 'DedupeRule',
+          'bao' => 'CRM_Dedupe_BAO_DedupeRule',
+          'localizable' => 0,
+          'html' => [
+            'type' => 'Text',
+          ],
+          'add' => '1.8',
+        ],
+        'rule_weight' => [
+          'name' => 'rule_weight',
+          'type' => CRM_Utils_Type::T_INT,
+          'title' => ts('Order'),
+          'description' => ts('The weight of the rule'),
+          'required' => TRUE,
+          'where' => 'civicrm_dedupe_rule.rule_weight',
+          'table_name' => 'civicrm_dedupe_rule',
+          'entity' => 'DedupeRule',
+          'bao' => 'CRM_Dedupe_BAO_DedupeRule',
+          'localizable' => 0,
+          'html' => [
+            'type' => 'Text',
+          ],
+          'add' => '1.8',
+        ],
+      ];
+      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__, 'dedupe_rule', $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__, 'dedupe_rule', $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/CRM/Dedupe/DAO/DedupeRuleGroup.php b/CRM/Dedupe/DAO/DedupeRuleGroup.php
new file mode 100644 (file)
index 0000000..55daa8a
--- /dev/null
@@ -0,0 +1,304 @@
+<?php
+
+/**
+ * @package CRM
+ * @copyright CiviCRM LLC https://civicrm.org/licensing
+ *
+ * Generated from xml/schema/CRM/Dedupe/DedupeRuleGroup.xml
+ * DO NOT EDIT.  Generated by CRM_Core_CodeGen
+ * (GenCodeChecksum:d2b969468d4841e3b7595d7beba7cea2)
+ */
+
+/**
+ * Database access object for the DedupeRuleGroup entity.
+ */
+class CRM_Dedupe_DAO_DedupeRuleGroup extends CRM_Core_DAO {
+  const EXT = 'civicrm';
+  const TABLE_ADDED = '1.8';
+
+  /**
+   * Static instance to hold the table name.
+   *
+   * @var string
+   */
+  public static $_tableName = 'civicrm_dedupe_rule_group';
+
+  /**
+   * Should CiviCRM log any modifications to this table in the civicrm_log table.
+   *
+   * @var bool
+   */
+  public static $_log = FALSE;
+
+  /**
+   * Unique dedupe rule group id
+   *
+   * @var int
+   */
+  public $id;
+
+  /**
+   * The type of contacts this group applies to
+   *
+   * @var string
+   */
+  public $contact_type;
+
+  /**
+   * The weight threshold the sum of the rule weights has to cross to consider two contacts the same
+   *
+   * @var int
+   */
+  public $threshold;
+
+  /**
+   * Whether the rule should be used for cases where usage is Unsupervised, Supervised OR General(programatically)
+   *
+   * @var string
+   */
+  public $used;
+
+  /**
+   * Name of the rule group
+   *
+   * @var string
+   */
+  public $name;
+
+  /**
+   * Label of the rule group
+   *
+   * @var string
+   */
+  public $title;
+
+  /**
+   * Is this a reserved rule - a rule group that has been optimized and cannot be changed by the admin
+   *
+   * @var bool
+   */
+  public $is_reserved;
+
+  /**
+   * Class constructor.
+   */
+  public function __construct() {
+    $this->__table = 'civicrm_dedupe_rule_group';
+    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 ? ts('Dedupe Rule Groups') : ts('Dedupe Rule Group');
+  }
+
+  /**
+   * 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,
+          'title' => ts('Rule Group ID'),
+          'description' => ts('Unique dedupe rule group id'),
+          'required' => TRUE,
+          'where' => 'civicrm_dedupe_rule_group.id',
+          'table_name' => 'civicrm_dedupe_rule_group',
+          'entity' => 'DedupeRuleGroup',
+          'bao' => 'CRM_Dedupe_BAO_DedupeRuleGroup',
+          'localizable' => 0,
+          'html' => [
+            'type' => 'Number',
+          ],
+          'readonly' => TRUE,
+          'add' => '1.8',
+        ],
+        'contact_type' => [
+          'name' => 'contact_type',
+          'type' => CRM_Utils_Type::T_STRING,
+          'title' => ts('Contact Type'),
+          'description' => ts('The type of contacts this group applies to'),
+          'maxlength' => 12,
+          'size' => CRM_Utils_Type::TWELVE,
+          'where' => 'civicrm_dedupe_rule_group.contact_type',
+          'table_name' => 'civicrm_dedupe_rule_group',
+          'entity' => 'DedupeRuleGroup',
+          'bao' => 'CRM_Dedupe_BAO_DedupeRuleGroup',
+          'localizable' => 0,
+          'html' => [
+            'type' => 'Select',
+          ],
+          'pseudoconstant' => [
+            'table' => 'civicrm_contact_type',
+            'keyColumn' => 'name',
+            'labelColumn' => 'label',
+            'condition' => 'parent_id IS NULL',
+          ],
+          'add' => '1.8',
+        ],
+        'threshold' => [
+          'name' => 'threshold',
+          'type' => CRM_Utils_Type::T_INT,
+          'title' => ts('Threshold'),
+          'description' => ts('The weight threshold the sum of the rule weights has to cross to consider two contacts the same'),
+          'required' => TRUE,
+          'where' => 'civicrm_dedupe_rule_group.threshold',
+          'table_name' => 'civicrm_dedupe_rule_group',
+          'entity' => 'DedupeRuleGroup',
+          'bao' => 'CRM_Dedupe_BAO_DedupeRuleGroup',
+          'localizable' => 0,
+          'html' => [
+            'type' => 'Text',
+          ],
+          'add' => '1.8',
+        ],
+        'used' => [
+          'name' => 'used',
+          'type' => CRM_Utils_Type::T_STRING,
+          'title' => ts('Length'),
+          'description' => ts('Whether the rule should be used for cases where usage is Unsupervised, Supervised OR General(programatically)'),
+          'required' => TRUE,
+          'maxlength' => 12,
+          'size' => CRM_Utils_Type::TWELVE,
+          'where' => 'civicrm_dedupe_rule_group.used',
+          'table_name' => 'civicrm_dedupe_rule_group',
+          'entity' => 'DedupeRuleGroup',
+          'bao' => 'CRM_Dedupe_BAO_DedupeRuleGroup',
+          'localizable' => 0,
+          'html' => [
+            'type' => 'Radio',
+          ],
+          'pseudoconstant' => [
+            'callback' => 'CRM_Core_SelectValues::getDedupeRuleTypes',
+          ],
+          'add' => '4.3',
+        ],
+        'name' => [
+          'name' => 'name',
+          'type' => CRM_Utils_Type::T_STRING,
+          'title' => ts('Name'),
+          'description' => ts('Name of the rule group'),
+          'maxlength' => 64,
+          'size' => CRM_Utils_Type::BIG,
+          'where' => 'civicrm_dedupe_rule_group.name',
+          'table_name' => 'civicrm_dedupe_rule_group',
+          'entity' => 'DedupeRuleGroup',
+          'bao' => 'CRM_Dedupe_BAO_DedupeRuleGroup',
+          'localizable' => 0,
+          'add' => '2.1',
+        ],
+        'title' => [
+          'name' => 'title',
+          'type' => CRM_Utils_Type::T_STRING,
+          'title' => ts('Title'),
+          'description' => ts('Label of the rule group'),
+          'maxlength' => 255,
+          'size' => CRM_Utils_Type::HUGE,
+          'where' => 'civicrm_dedupe_rule_group.title',
+          'table_name' => 'civicrm_dedupe_rule_group',
+          'entity' => 'DedupeRuleGroup',
+          'bao' => 'CRM_Dedupe_BAO_DedupeRuleGroup',
+          'localizable' => 0,
+          'html' => [
+            'type' => 'Text',
+          ],
+          'add' => '4.1',
+        ],
+        'is_reserved' => [
+          'name' => 'is_reserved',
+          'type' => CRM_Utils_Type::T_BOOLEAN,
+          'title' => ts('Reserved?'),
+          'description' => ts('Is this a reserved rule - a rule group that has been optimized and cannot be changed by the admin'),
+          'where' => 'civicrm_dedupe_rule_group.is_reserved',
+          'table_name' => 'civicrm_dedupe_rule_group',
+          'entity' => 'DedupeRuleGroup',
+          'bao' => 'CRM_Dedupe_BAO_DedupeRuleGroup',
+          'localizable' => 0,
+          'html' => [
+            'type' => 'CheckBox',
+          ],
+          'add' => '4.1',
+        ],
+      ];
+      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__, 'dedupe_rule_group', $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__, 'dedupe_rule_group', $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;
+  }
+
+}
index 32be00eaa0c6a4dad87d703471e7a8c8f5999f4e..fae8f0bae431135f5d157f685b1315e4279cf1be 100644 (file)
 /**
  * Database access object for the Exception entity.
  */
-class CRM_Dedupe_DAO_Exception extends CRM_Core_DAO {
-  const EXT = 'civicrm';
-  const TABLE_ADDED = '3.3';
-
-  /**
-   * Static instance to hold the table name.
-   *
-   * @var string
-   */
-  public static $_tableName = 'civicrm_dedupe_exception';
-
-  /**
-   * Should CiviCRM log any modifications to this table in the civicrm_log table.
-   *
-   * @var bool
-   */
-  public static $_log = FALSE;
-
-  /**
-   * Unique dedupe exception id
-   *
-   * @var int
-   */
-  public $id;
-
-  /**
-   * FK to Contact ID
-   *
-   * @var int
-   */
-  public $contact_id1;
-
-  /**
-   * FK to Contact ID
-   *
-   * @var int
-   */
-  public $contact_id2;
-
-  /**
-   * Class constructor.
-   */
-  public function __construct() {
-    $this->__table = 'civicrm_dedupe_exception';
-    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 ? ts('Exceptions') : ts('Exception');
-  }
-
-  /**
-   * 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_id1', 'civicrm_contact', 'id');
-      Civi::$statics[__CLASS__]['links'][] = new CRM_Core_Reference_Basic(self::getTableName(), 'contact_id2', '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,
-          'title' => ts('Dedupe Exception ID'),
-          'description' => ts('Unique dedupe exception id'),
-          'required' => TRUE,
-          'where' => 'civicrm_dedupe_exception.id',
-          'table_name' => 'civicrm_dedupe_exception',
-          'entity' => 'Exception',
-          'bao' => 'CRM_Dedupe_BAO_Exception',
-          'localizable' => 0,
-          'html' => [
-            'type' => 'Number',
-          ],
-          'readonly' => TRUE,
-          'add' => '3.3',
-        ],
-        'contact_id1' => [
-          'name' => 'contact_id1',
-          'type' => CRM_Utils_Type::T_INT,
-          'title' => ts('First Dupe Contact ID'),
-          'description' => ts('FK to Contact ID'),
-          'required' => TRUE,
-          'where' => 'civicrm_dedupe_exception.contact_id1',
-          'table_name' => 'civicrm_dedupe_exception',
-          'entity' => 'Exception',
-          'bao' => 'CRM_Dedupe_BAO_Exception',
-          'localizable' => 0,
-          'FKClassName' => 'CRM_Contact_DAO_Contact',
-          'html' => [
-            'label' => ts("First Dupe Contact"),
-          ],
-          'add' => '3.3',
-        ],
-        'contact_id2' => [
-          'name' => 'contact_id2',
-          'type' => CRM_Utils_Type::T_INT,
-          'title' => ts('Second Dupe Contact ID'),
-          'description' => ts('FK to Contact ID'),
-          'required' => TRUE,
-          'where' => 'civicrm_dedupe_exception.contact_id2',
-          'table_name' => 'civicrm_dedupe_exception',
-          'entity' => 'Exception',
-          'bao' => 'CRM_Dedupe_BAO_Exception',
-          'localizable' => 0,
-          'FKClassName' => 'CRM_Contact_DAO_Contact',
-          'html' => [
-            'label' => ts("Second Dupe Contact"),
-          ],
-          'add' => '3.3',
-        ],
-      ];
-      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__, 'dedupe_exception', $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__, 'dedupe_exception', $prefix, []);
-    return $r;
-  }
-
-  /**
-   * Returns the list of indices
-   *
-   * @param bool $localize
-   *
-   * @return array
-   */
-  public static function indices($localize = TRUE) {
-    $indices = [
-      'UI_contact_id1_contact_id2' => [
-        'name' => 'UI_contact_id1_contact_id2',
-        'field' => [
-          0 => 'contact_id1',
-          1 => 'contact_id2',
-        ],
-        'localizable' => FALSE,
-        'unique' => TRUE,
-        'sig' => 'civicrm_dedupe_exception::1::contact_id1::contact_id2',
-      ],
-    ];
-    return ($localize && !empty($indices)) ? CRM_Core_DAO_AllCoreTables::multilingualize(__CLASS__, $indices) : $indices;
-  }
-
-}
+class CRM_Dedupe_DAO_Exception extends CRM_Dedupe_DAO_DedupeException {}
index 03780fcbba85fd1105cb19f4024293d35be8c50f..b1d4686fb8357129e33087e13531069cacd05cf1 100644 (file)
@@ -4,281 +4,9 @@
  * @package CRM
  * @copyright CiviCRM LLC https://civicrm.org/licensing
  *
- * Generated from xml/schema/CRM/Dedupe/Rule.xml
- * DO NOT EDIT.  Generated by CRM_Core_CodeGen
- * (GenCodeChecksum:2860c5a16ead4f23809027d8a2812265)
  */
 
 /**
- * Database access object for the Rule entity.
+ * Database access object for the DedupeRule entity.
  */
-class CRM_Dedupe_DAO_Rule extends CRM_Core_DAO {
-  const EXT = 'civicrm';
-  const TABLE_ADDED = '1.8';
-
-  /**
-   * Static instance to hold the table name.
-   *
-   * @var string
-   */
-  public static $_tableName = 'civicrm_dedupe_rule';
-
-  /**
-   * Should CiviCRM log any modifications to this table in the civicrm_log table.
-   *
-   * @var bool
-   */
-  public static $_log = FALSE;
-
-  /**
-   * Unique dedupe rule id
-   *
-   * @var int
-   */
-  public $id;
-
-  /**
-   * The id of the rule group this rule belongs to
-   *
-   * @var int
-   */
-  public $dedupe_rule_group_id;
-
-  /**
-   * The name of the table this rule is about
-   *
-   * @var string
-   */
-  public $rule_table;
-
-  /**
-   * The name of the field of the table referenced in rule_table
-   *
-   * @var string
-   */
-  public $rule_field;
-
-  /**
-   * The length of the matching substring
-   *
-   * @var int
-   */
-  public $rule_length;
-
-  /**
-   * The weight of the rule
-   *
-   * @var int
-   */
-  public $rule_weight;
-
-  /**
-   * Class constructor.
-   */
-  public function __construct() {
-    $this->__table = 'civicrm_dedupe_rule';
-    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 ? ts('Rules') : ts('Rule');
-  }
-
-  /**
-   * 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(), 'dedupe_rule_group_id', 'civicrm_dedupe_rule_group', '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,
-          'title' => ts('Dedupe Rule ID'),
-          'description' => ts('Unique dedupe rule id'),
-          'required' => TRUE,
-          'where' => 'civicrm_dedupe_rule.id',
-          'table_name' => 'civicrm_dedupe_rule',
-          'entity' => 'Rule',
-          'bao' => 'CRM_Dedupe_BAO_Rule',
-          'localizable' => 0,
-          'html' => [
-            'type' => 'Number',
-          ],
-          'readonly' => TRUE,
-          'add' => '1.8',
-        ],
-        'dedupe_rule_group_id' => [
-          'name' => 'dedupe_rule_group_id',
-          'type' => CRM_Utils_Type::T_INT,
-          'title' => ts('Group ID'),
-          'description' => ts('The id of the rule group this rule belongs to'),
-          'required' => TRUE,
-          'where' => 'civicrm_dedupe_rule.dedupe_rule_group_id',
-          'table_name' => 'civicrm_dedupe_rule',
-          'entity' => 'Rule',
-          'bao' => 'CRM_Dedupe_BAO_Rule',
-          'localizable' => 0,
-          'FKClassName' => 'CRM_Dedupe_DAO_RuleGroup',
-          'html' => [
-            'label' => ts("Group"),
-          ],
-          'add' => '1.8',
-        ],
-        'rule_table' => [
-          'name' => 'rule_table',
-          'type' => CRM_Utils_Type::T_STRING,
-          'title' => ts('Rule Table'),
-          'description' => ts('The name of the table this rule is about'),
-          'required' => TRUE,
-          'maxlength' => 64,
-          'size' => CRM_Utils_Type::BIG,
-          'where' => 'civicrm_dedupe_rule.rule_table',
-          'table_name' => 'civicrm_dedupe_rule',
-          'entity' => 'Rule',
-          'bao' => 'CRM_Dedupe_BAO_Rule',
-          'localizable' => 0,
-          'add' => '1.8',
-        ],
-        'rule_field' => [
-          'name' => 'rule_field',
-          'type' => CRM_Utils_Type::T_STRING,
-          'title' => ts('Rule Field'),
-          'description' => ts('The name of the field of the table referenced in rule_table'),
-          'required' => TRUE,
-          'maxlength' => 64,
-          'size' => CRM_Utils_Type::BIG,
-          'where' => 'civicrm_dedupe_rule.rule_field',
-          'table_name' => 'civicrm_dedupe_rule',
-          'entity' => 'Rule',
-          'bao' => 'CRM_Dedupe_BAO_Rule',
-          'localizable' => 0,
-          'add' => '1.8',
-        ],
-        'rule_length' => [
-          'name' => 'rule_length',
-          'type' => CRM_Utils_Type::T_INT,
-          'title' => ts('Rule Length'),
-          'description' => ts('The length of the matching substring'),
-          'where' => 'civicrm_dedupe_rule.rule_length',
-          'table_name' => 'civicrm_dedupe_rule',
-          'entity' => 'Rule',
-          'bao' => 'CRM_Dedupe_BAO_Rule',
-          'localizable' => 0,
-          'html' => [
-            'type' => 'Text',
-          ],
-          'add' => '1.8',
-        ],
-        'rule_weight' => [
-          'name' => 'rule_weight',
-          'type' => CRM_Utils_Type::T_INT,
-          'title' => ts('Order'),
-          'description' => ts('The weight of the rule'),
-          'required' => TRUE,
-          'where' => 'civicrm_dedupe_rule.rule_weight',
-          'table_name' => 'civicrm_dedupe_rule',
-          'entity' => 'Rule',
-          'bao' => 'CRM_Dedupe_BAO_Rule',
-          'localizable' => 0,
-          'html' => [
-            'type' => 'Text',
-          ],
-          'add' => '1.8',
-        ],
-      ];
-      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__, 'dedupe_rule', $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__, 'dedupe_rule', $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;
-  }
-
-}
+class CRM_Dedupe_DAO_Rule extends CRM_Dedupe_DAO_DedupeRule {}
index 901c58061258e5af0b0765a8ce9593092ffb8459..b471cf40efb40d34c4143123918a4dd5603b9f75 100644 (file)
@@ -4,301 +4,9 @@
  * @package CRM
  * @copyright CiviCRM LLC https://civicrm.org/licensing
  *
- * Generated from xml/schema/CRM/Dedupe/RuleGroup.xml
- * DO NOT EDIT.  Generated by CRM_Core_CodeGen
- * (GenCodeChecksum:114051c4c9bd7fd1e7854de7fc110884)
  */
 
 /**
- * Database access object for the RuleGroup entity.
+ * Database access object for the DedupeRuleGroup entity.
  */
-class CRM_Dedupe_DAO_RuleGroup extends CRM_Core_DAO {
-  const EXT = 'civicrm';
-  const TABLE_ADDED = '1.8';
-
-  /**
-   * Static instance to hold the table name.
-   *
-   * @var string
-   */
-  public static $_tableName = 'civicrm_dedupe_rule_group';
-
-  /**
-   * Should CiviCRM log any modifications to this table in the civicrm_log table.
-   *
-   * @var bool
-   */
-  public static $_log = FALSE;
-
-  /**
-   * Unique dedupe rule group id
-   *
-   * @var int
-   */
-  public $id;
-
-  /**
-   * The type of contacts this group applies to
-   *
-   * @var string
-   */
-  public $contact_type;
-
-  /**
-   * The weight threshold the sum of the rule weights has to cross to consider two contacts the same
-   *
-   * @var int
-   */
-  public $threshold;
-
-  /**
-   * Whether the rule should be used for cases where usage is Unsupervised, Supervised OR General(programatically)
-   *
-   * @var string
-   */
-  public $used;
-
-  /**
-   * Name of the rule group
-   *
-   * @var string
-   */
-  public $name;
-
-  /**
-   * Label of the rule group
-   *
-   * @var string
-   */
-  public $title;
-
-  /**
-   * Is this a reserved rule - a rule group that has been optimized and cannot be changed by the admin
-   *
-   * @var bool
-   */
-  public $is_reserved;
-
-  /**
-   * Class constructor.
-   */
-  public function __construct() {
-    $this->__table = 'civicrm_dedupe_rule_group';
-    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 ? ts('Rule Groups') : ts('Rule Group');
-  }
-
-  /**
-   * 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,
-          'title' => ts('Rule Group ID'),
-          'description' => ts('Unique dedupe rule group id'),
-          'required' => TRUE,
-          'where' => 'civicrm_dedupe_rule_group.id',
-          'table_name' => 'civicrm_dedupe_rule_group',
-          'entity' => 'RuleGroup',
-          'bao' => 'CRM_Dedupe_BAO_RuleGroup',
-          'localizable' => 0,
-          'html' => [
-            'type' => 'Number',
-          ],
-          'readonly' => TRUE,
-          'add' => '1.8',
-        ],
-        'contact_type' => [
-          'name' => 'contact_type',
-          'type' => CRM_Utils_Type::T_STRING,
-          'title' => ts('Contact Type'),
-          'description' => ts('The type of contacts this group applies to'),
-          'maxlength' => 12,
-          'size' => CRM_Utils_Type::TWELVE,
-          'where' => 'civicrm_dedupe_rule_group.contact_type',
-          'table_name' => 'civicrm_dedupe_rule_group',
-          'entity' => 'RuleGroup',
-          'bao' => 'CRM_Dedupe_BAO_RuleGroup',
-          'localizable' => 0,
-          'html' => [
-            'type' => 'Select',
-          ],
-          'pseudoconstant' => [
-            'table' => 'civicrm_contact_type',
-            'keyColumn' => 'name',
-            'labelColumn' => 'label',
-            'condition' => 'parent_id IS NULL',
-          ],
-          'add' => '1.8',
-        ],
-        'threshold' => [
-          'name' => 'threshold',
-          'type' => CRM_Utils_Type::T_INT,
-          'title' => ts('Threshold'),
-          'description' => ts('The weight threshold the sum of the rule weights has to cross to consider two contacts the same'),
-          'required' => TRUE,
-          'where' => 'civicrm_dedupe_rule_group.threshold',
-          'table_name' => 'civicrm_dedupe_rule_group',
-          'entity' => 'RuleGroup',
-          'bao' => 'CRM_Dedupe_BAO_RuleGroup',
-          'localizable' => 0,
-          'html' => [
-            'type' => 'Text',
-          ],
-          'add' => '1.8',
-        ],
-        'used' => [
-          'name' => 'used',
-          'type' => CRM_Utils_Type::T_STRING,
-          'title' => ts('Length'),
-          'description' => ts('Whether the rule should be used for cases where usage is Unsupervised, Supervised OR General(programatically)'),
-          'required' => TRUE,
-          'maxlength' => 12,
-          'size' => CRM_Utils_Type::TWELVE,
-          'where' => 'civicrm_dedupe_rule_group.used',
-          'table_name' => 'civicrm_dedupe_rule_group',
-          'entity' => 'RuleGroup',
-          'bao' => 'CRM_Dedupe_BAO_RuleGroup',
-          'localizable' => 0,
-          'html' => [
-            'type' => 'Radio',
-          ],
-          'pseudoconstant' => [
-            'callback' => 'CRM_Core_SelectValues::getDedupeRuleTypes',
-          ],
-          'add' => '4.3',
-        ],
-        'name' => [
-          'name' => 'name',
-          'type' => CRM_Utils_Type::T_STRING,
-          'title' => ts('Name'),
-          'description' => ts('Name of the rule group'),
-          'maxlength' => 64,
-          'size' => CRM_Utils_Type::BIG,
-          'where' => 'civicrm_dedupe_rule_group.name',
-          'table_name' => 'civicrm_dedupe_rule_group',
-          'entity' => 'RuleGroup',
-          'bao' => 'CRM_Dedupe_BAO_RuleGroup',
-          'localizable' => 0,
-          'add' => '2.1',
-        ],
-        'title' => [
-          'name' => 'title',
-          'type' => CRM_Utils_Type::T_STRING,
-          'title' => ts('Title'),
-          'description' => ts('Label of the rule group'),
-          'maxlength' => 255,
-          'size' => CRM_Utils_Type::HUGE,
-          'where' => 'civicrm_dedupe_rule_group.title',
-          'table_name' => 'civicrm_dedupe_rule_group',
-          'entity' => 'RuleGroup',
-          'bao' => 'CRM_Dedupe_BAO_RuleGroup',
-          'localizable' => 0,
-          'html' => [
-            'type' => 'Text',
-          ],
-          'add' => '4.1',
-        ],
-        'is_reserved' => [
-          'name' => 'is_reserved',
-          'type' => CRM_Utils_Type::T_BOOLEAN,
-          'title' => ts('Reserved?'),
-          'description' => ts('Is this a reserved rule - a rule group that has been optimized and cannot be changed by the admin'),
-          'where' => 'civicrm_dedupe_rule_group.is_reserved',
-          'table_name' => 'civicrm_dedupe_rule_group',
-          'entity' => 'RuleGroup',
-          'bao' => 'CRM_Dedupe_BAO_RuleGroup',
-          'localizable' => 0,
-          'html' => [
-            'type' => 'CheckBox',
-          ],
-          'add' => '4.1',
-        ],
-      ];
-      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__, 'dedupe_rule_group', $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__, 'dedupe_rule_group', $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;
-  }
-
-}
+class CRM_Dedupe_DAO_RuleGroup extends CRM_Dedupe_DAO_DedupeRuleGroup {}
index 7b6f30f952c41671f290c967baaf4db2d6be6f49..5403c25a2b45944ce25e0619883758371f7e2abe 100644 (file)
@@ -6,7 +6,7 @@
  *
  * Generated from xml/schema/CRM/Event/Event.xml
  * DO NOT EDIT.  Generated by CRM_Core_CodeGen
- * (GenCodeChecksum:c5779073cea9c6b4a959070ec0e95b6e)
+ * (GenCodeChecksum:e0bee3952ac39a53952b6a7979a79e13)
  */
 
 /**
@@ -1736,7 +1736,7 @@ class CRM_Event_DAO_Event extends CRM_Core_DAO {
           'entity' => 'Event',
           'bao' => 'CRM_Event_BAO_Event',
           'localizable' => 0,
-          'FKClassName' => 'CRM_Dedupe_DAO_RuleGroup',
+          'FKClassName' => 'CRM_Dedupe_DAO_DedupeRuleGroup',
           'html' => [
             'type' => 'Select',
             'label' => ts("Dedupe Rule"),
diff --git a/Civi/Api4/DedupeRule.php b/Civi/Api4/DedupeRule.php
new file mode 100644 (file)
index 0000000..8ef4cc3
--- /dev/null
@@ -0,0 +1,32 @@
+<?php
+
+/*
+ +--------------------------------------------------------------------+
+ | 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       |
+ +--------------------------------------------------------------------+
+ */
+
+/**
+ *
+ * @package CRM
+ * @copyright CiviCRM LLC https://civicrm.org/licensing
+ */
+
+
+namespace Civi\Api4;
+
+/**
+ * DedupeRule entity.
+ *
+ * This api exposes CiviCRM (dedupe) rules.
+ *
+ * @see https://docs.civicrm.org/user/en/latest/organising-your-data/contacts/
+ * @package Civi\Api4
+ */
+class DedupeRule extends Generic\DAOEntity {
+
+}
index d96aae6785bc280e409ee3d3b72edc6509e2f662..ecea48073905b8ad7a699fe787ef17998b4784b3 100644 (file)
@@ -330,6 +330,18 @@ function _civicrm_api3_get_DAO($name) {
   if ($name === 'SmsProvider') {
     return 'CRM_SMS_DAO_Provider';
   }
+  // Entity was renamed to CRM_Dedupe_DAO_DedupeRule for APIv4
+  if ($name === 'Rule') {
+    return 'CRM_Dedupe_DAO_DedupeRule';
+  }
+  // Entity was renamed to CRM_Dedupe_DAO_DedupeRuleGroup for APIv4
+  if ($name === 'RuleGroup') {
+    return 'CRM_Dedupe_DAO_DedupeRuleGroup';
+  }
+  // Entity was renamed to CRM_Dedupe_DAO_DedupeException for APIv4
+  if ($name === 'Exception') {
+    return 'CRM_Dedupe_DAO_DedupeException';
+  }
   // FIXME: DAO names should follow CamelCase convention
   if ($name === 'Im' || $name === 'Acl' || $name === 'Pcp') {
     $name = strtoupper($name);
similarity index 97%
rename from xml/schema/Dedupe/Exception.xml
rename to xml/schema/Dedupe/DedupeException.xml
index 088ae2d4671ef452bf3b20025c6cb4e68925d1dd..4108023124572c708a291c6ba0ad123228c343b3 100644 (file)
@@ -1,7 +1,7 @@
 <?xml version="1.0" encoding="utf-8" ?>
 <table>
   <base>CRM/Dedupe</base>
-  <class>Exception</class>
+  <class>DedupeException</class>
   <name>civicrm_dedupe_exception</name>
   <comment>Dedupe exceptions</comment>
   <add>3.3</add>
similarity index 98%
rename from xml/schema/Dedupe/Rule.xml
rename to xml/schema/Dedupe/DedupeRule.xml
index 0b1649796afd2a4a1e3a0b805d59e14658019edd..396714df40a88a0c2b3cda3b09cb2ec3308f17aa 100644 (file)
@@ -1,7 +1,7 @@
 <?xml version="1.0" encoding="utf-8" ?>
 <table>
   <base>CRM/Dedupe</base>
-  <class>Rule</class>
+  <class>DedupeRule</class>
   <name>civicrm_dedupe_rule</name>
   <comment>Dedupe rules for use by rule groups</comment>
   <add>1.8</add>
similarity index 98%
rename from xml/schema/Dedupe/RuleGroup.xml
rename to xml/schema/Dedupe/DedupeRuleGroup.xml
index 26fe7e63f8c16048856acdd5b7d77dca316351be..1a8722da3334a8b255f4a62418cd10dae638fd0f 100644 (file)
@@ -1,7 +1,7 @@
 <?xml version="1.0" encoding="utf-8" ?>
 <table>
   <base>CRM/Dedupe</base>
-  <class>RuleGroup</class>
+  <class>DedupeRuleGroup</class>
   <name>civicrm_dedupe_rule_group</name>
   <comment>Dedupe rule groups</comment>
   <add>1.8</add>
index 7321e4ce9a38d6c36f324d721b7cb99eaa7a5398..1e0dd1de3947ee42cb15f7bbff91dd5ad6a85062 100644 (file)
@@ -2,7 +2,7 @@
 
 <tables xmlns:xi="http://www.w3.org/2001/XInclude">
 
-<xi:include href="RuleGroup.xml" parse="xml" />
-<xi:include href="Rule.xml"      parse="xml" />
-<xi:include href="Exception.xml" parse="xml" />
+<xi:include href="DedupeRuleGroup.xml" parse="xml" />
+<xi:include href="DedupeRule.xml"      parse="xml" />
+<xi:include href="DedupeException.xml" parse="xml" />
 </tables>