debug('Begin', ['=start' => 'ContactCatSync', '=timed' => 1]); $settings = Civi::settings()->get('contact_categories'); $settings = Setting::get(FALSE) ->addSelect('contact_categories') ->execute()->first()['value'] ?? NULL; if (empty($settings['groupIDs'])) { throw new \API_Exception('Unconfigured'); } if ($settings['updateAfter'] > time()) { // not needed yet. return; } // Load category names $catNames = OptionValue::get(FALSE) ->addWhere('option_group_id:name', '=', 'contact_categories') ->addSelect('value', 'label') ->execute()->indexBy('value')->column('label'); $groups = Group::get(FALSE) ->addWhere('id', 'IN', $settings['groupIDs']) ->execute()->indexBy('id')->getArrayCopy(); $smartGroups = []; foreach ($groups as $groupID => $group) { if (!empty($group['saved_search_id'])) { $smartGroups[] = $groupID; } } if ($smartGroups) { Civi::log()->debug('Refreshing smart groups', ['=' => 'timed', 'groups' => $smartGroups]); \CRM_Contact_BAO_GroupContactCache::loadAll($smartGroups); Civi::log()->debug('', ['=' => 'pop']); } Civi::log()->debug('Resetting table', ['=' => 'start,timed']); // clear out temp space. CRM_Core_DAO::executeQuery("UPDATE civicrm_contact_category SET next_category = 0;")->free(); // ensure we have all our contacts covered. CRM_Core_DAO::executeQuery(<<free(); Civi::log()->debug('', ['=' => 'pop']); foreach ($settings['groupIDs'] as $groupID) { if ($groupID == 0) { // The default 'group' isn't a ... group. continue; } // Get contacts in group. if (!$groups[$groupID]) { Civi::log()->warning("Group $groupID no longer exists."); continue; } $isSmart = !empty($groups[$groupID]['saved_search_id']); Civi::log()->debug($groups[$groupID]['title'], ['=' => 'timed', '=start' => "group$groupID"] + compact('isSmart')); if (!$isSmart) { $sql = <<free(); Civi::log()->debug('', ['=' => 'pop']); } Civi::log()->debug("Calculate changes", ['=' => 'timed', '=start' => "changes"]); $changes = CRM_Core_DAO::executeQuery(<< category ORDER BY category, next_category SQL); $lastChange = [0, 0]; $batch = []; $n = 0; $writeBatch = function() use (&$lastChange, &$batch, $catNames) { if (empty($batch)) { return; } $newCategoryId = (int) ($lastChange[1] ?? 0); // Create activity $a = Activity::create(FALSE) ->addValue('target_contact_id', $batch[0]) ->addValue('activity_type_id:name', 'changed_contact_category') ->addValue('source_contact_id', \CRM_Core_BAO_Domain::getDomain()->contact_id) ->addValue('status_id:name', 'Completed') ->addValue('subject', $catNames[$lastChange[0] ?? 0] . ' → ' . $catNames[$newCategoryId]) ->execute()->first(); $activityID = (int) $a['id']; // Join activity to all relevant contacts CRM_Core_DAO::executeQuery("INSERT INTO civicrm_activity_contact (contact_id, activity_id) SELECT id contact_id, $activityID activity_id FROM civicrm_contact_category cc WHERE cc.next_category = $newCategoryId ")->free(); Civi::log()->debug(count($batch) . " changes: " . $catNames[$lastChange[0] ?? 0] . ' → ' . $catNames[$lastChange[1] ?? 0]); }; while ($changes->fetch()) { $n++; if ($lastChange[0] !== $changes->category || $lastChange[1] !== $changes->next_category) { $writeBatch(); $lastChange = [$changes->category, $changes->next_category]; $batch = []; } $batch[] = $changes->id; } $changes->free(); $writeBatch(); Civi::log()->debug('Apply changes', ['=change' => 'applyChanges', '=timed' => 1]); CRM_Core_DAO::executeQuery("UPDATE civicrm_contact_category SET category = next_category WHERE category <> next_category")->free(); Civi::log()->debug('', ['=pop' => 1]); $summary = CRM_Core_DAO::executeQuery("SELECT next_category, count(*) from civicrm_contact_category group by next_category")->fetchAll(); $summary['changes'] = $n; $_ = memory_get_peak_usage(TRUE); $summary['memory_use'] = @round($_ / pow(1024, ($i = floor(log($_, 1024)))), 2) . ' ' . ['b', 'kb', 'mb', 'gb', 'tb', 'pb'][$i]; Civi::log()->debug('Complete.', ['=' => 'set']); $result->exchangeArray($summary); } }