From ace511f2a5894f5698b6789a20dcde0486657f06 Mon Sep 17 00:00:00 2001 From: Coleman Watts Date: Thu, 2 Dec 2021 14:42:45 -0500 Subject: [PATCH] ContactType - Use standard delete function which calls hooks --- CRM/Contact/BAO/ContactType.php | 85 +++++++++++-------- .../phpunit/api/v4/Entity/ContactTypeTest.php | 44 +++++++++- 2 files changed, 94 insertions(+), 35 deletions(-) diff --git a/CRM/Contact/BAO/ContactType.php b/CRM/Contact/BAO/ContactType.php index 3b7cc38870..f47c901ba3 100644 --- a/CRM/Contact/BAO/ContactType.php +++ b/CRM/Contact/BAO/ContactType.php @@ -14,7 +14,7 @@ * @package CRM * @copyright CiviCRM LLC https://civicrm.org/licensing */ -class CRM_Contact_BAO_ContactType extends CRM_Contact_DAO_ContactType { +class CRM_Contact_BAO_ContactType extends CRM_Contact_DAO_ContactType implements \Civi\Test\HookInterface { /** * Fetch object based on array of properties. @@ -460,52 +460,69 @@ WHERE subtype.name IN ('" . implode("','", $subType) . "' )"; * * @param int $contactTypeId * ID of the Contact Subtype to be deleted. - * + * @deprecated * @return bool */ public static function del($contactTypeId) { - if (!$contactTypeId) { return FALSE; } - - $params = ['id' => $contactTypeId]; - self::retrieve($params, $typeInfo); - $name = $typeInfo['name']; - // check if any custom group - $custom = new CRM_Core_DAO_CustomGroup(); - $custom->whereAdd("extends_entity_column_value LIKE '%" . - CRM_Core_DAO::VALUE_SEPARATOR . - $name . - CRM_Core_DAO::VALUE_SEPARATOR . "%'" - ); - if ($custom->find()) { + try { + static::deleteRecord(['id' => $contactTypeId]); + return TRUE; + } + catch (CRM_Core_Exception $e) { return FALSE; } + } - // remove subtype for existing contacts - $sql = " + /** + * Callback for hook_civicrm_pre(). + * @param \Civi\Core\Event\PreEvent $event + * @throws CRM_Core_Exception + */ + public static function self_hook_civicrm_pre(\Civi\Core\Event\PreEvent $event) { + // Before deleting a contactType, check references by custom groups + if ($event->action === 'delete') { + $name = CRM_Core_DAO::getFieldValue('CRM_Contact_DAO_ContactType', $event->id); + $sep = CRM_Core_DAO::VALUE_SEPARATOR; + $custom = new CRM_Core_DAO_CustomGroup(); + $custom->whereAdd("extends_entity_column_value LIKE '%{$sep}{$name}{$sep}%'"); + if ($custom->find()) { + throw new CRM_Core_Exception(ts("You can not delete this contact type -- it is used by %1 custom field group(s). The custom fields must be deleted first.", [1 => $custom->N])); + } + } + } + + /** + * Callback for hook_civicrm_post(). + * @param \Civi\Core\Event\PostEvent $event + */ + public static function self_hook_civicrm_post(\Civi\Core\Event\PostEvent $event) { + if ($event->action === 'delete') { + $sep = CRM_Core_DAO::VALUE_SEPARATOR; + $subType = "$sep{$event->object->name}$sep"; + // For contacts with just the one sub-type, set to null + $sql = " UPDATE civicrm_contact SET contact_sub_type = NULL -WHERE contact_sub_type = '$name'"; - CRM_Core_DAO::executeQuery($sql); +WHERE contact_sub_type = '$subType'"; + CRM_Core_DAO::executeQuery($sql); + // For contacts with multipe sub-types, remove this one + $sql = " +UPDATE civicrm_contact SET contact_sub_type = REPLACE(contact_sub_type, '$subType', '$sep') +WHERE contact_sub_type LIKE '%{$subType}%'"; + CRM_Core_DAO::executeQuery($sql); + + // remove navigation entry which was auto-created when this sub-type was added + \Civi\Api4\Navigation::delete(FALSE) + ->addWhere('name', '=', "New {$event->object->name}") + ->addWhere('url', 'LIKE', 'civicrm/contact/add%') + // Overide the default which limits to a single domain + ->addWhere('domain_id', '>', 0) + ->execute(); - // remove subtype from contact type table - $contactType = new CRM_Contact_DAO_ContactType(); - $contactType->id = $contactTypeId; - $contactType->delete(); - - // remove navigation entry if any - if ($name) { - $sql = ' -DELETE -FROM civicrm_navigation -WHERE name = %1'; - $params = [1 => ["New $name", 'String']]; - CRM_Core_DAO::executeQuery($sql, $params); - CRM_Core_BAO_Navigation::resetNavigation(); Civi::cache('contactTypes')->clear(); } - return TRUE; } /** diff --git a/tests/phpunit/api/v4/Entity/ContactTypeTest.php b/tests/phpunit/api/v4/Entity/ContactTypeTest.php index 49cf86344a..cdb4f492ae 100644 --- a/tests/phpunit/api/v4/Entity/ContactTypeTest.php +++ b/tests/phpunit/api/v4/Entity/ContactTypeTest.php @@ -21,12 +21,54 @@ namespace api\v4\Entity; use Civi\Api4\Contact; use api\v4\UnitTestCase; +use Civi\Api4\ContactType; use Civi\Api4\Email; +use Civi\Api4\Navigation; +use Civi\Test\TransactionalInterface; /** * @group headless */ -class ContactTypeTest extends UnitTestCase { +class ContactTypeTest extends UnitTestCase implements TransactionalInterface { + + public function testMenuItemWillBeCreatedAndDeleted() { + ContactType::create(FALSE) + ->addValue('name', 'Tester') + ->addValue('label', 'Tester') + ->addValue('parent_id.name', 'Individual') + ->execute(); + // Menu item should have been auto-created + $this->assertCount(1, Navigation::get(FALSE)->addWhere('name', '=', 'New Tester')->execute()); + + ContactType::delete(FALSE) + ->addWhere('name', '=', 'Tester') + ->execute(); + // Menu item should be gone + $this->assertCount(0, Navigation::get(FALSE)->addWhere('name', '=', 'New Tester')->execute()); + } + + public function testSubTypeWillBeRemovedFromExistingContacts() { + foreach (['TesterA', 'TesterB'] as $name) { + ContactType::create(FALSE) + ->addValue('name', $name) + ->addValue('label', $name) + ->addValue('parent_id.name', 'Individual') + ->execute(); + } + $c1 = Contact::create(FALSE) + ->addValue('contact_sub_type', ['TesterA']) + ->execute()->first()['id']; + $c2 = Contact::create(FALSE) + ->addValue('contact_sub_type', ['TesterA', 'TesterB']) + ->execute()->first()['id']; + + ContactType::delete(FALSE) + ->addWhere('name', '=', 'TesterA') + ->execute(); + + $this->assertNull(Contact::get(FALSE)->addWhere('id', '=', $c1)->execute()->first()['contact_sub_type']); + $this->assertEquals(['TesterB'], Contact::get(FALSE)->addWhere('id', '=', $c2)->execute()->first()['contact_sub_type']); + } public function testGetReturnsFieldsAppropriateToEachContactType() { $indiv = Contact::create() -- 2.25.1