APIv4 - Limit SortableEntity exports by domain
authorColeman Watts <coleman@civicrm.org>
Sun, 28 Nov 2021 21:14:42 +0000 (16:14 -0500)
committerColeman Watts <coleman@civicrm.org>
Mon, 29 Nov 2021 13:46:02 +0000 (08:46 -0500)
CRM/Admin/Form/Options.php
CRM/Campaign/Form/SurveyType.php
CRM/Core/BAO/OptionValue.php
CRM/Core/OptionGroup.php
CRM/Core/OptionValue.php
Civi/Api4/Generic/ExportAction.php
tests/phpunit/api/v4/Entity/ManagedEntityTest.php

index 123eee121fb74bf34457f1aa9944a0e7189f40a3..dc9fe0576810041fc5f664052d12a29495ce4b05 100644 (file)
@@ -69,7 +69,7 @@ class CRM_Admin_Form_Options extends CRM_Admin_Form {
       'name'
     );
     $this->_gLabel = CRM_Core_DAO::getFieldValue('CRM_Core_DAO_OptionGroup', $this->_gid, 'title');
-    $this->_domainSpecific = in_array($this->_gName, CRM_Core_OptionGroup::$_domainIDGroups);
+    $this->_domainSpecific = CRM_Core_OptionGroup::isDomainOptionGroup($this->_gName);
     $url = "civicrm/admin/options/{$this->_gName}";
     $params = "reset=1";
 
@@ -96,7 +96,7 @@ class CRM_Admin_Form_Options extends CRM_Admin_Form {
     $session->pushUserContext(CRM_Utils_System::url($url, $params));
     $this->assign('id', $this->_id);
 
-    if ($this->_id && in_array($this->_gName, CRM_Core_OptionGroup::$_domainIDGroups)) {
+    if ($this->_id && CRM_Core_OptionGroup::isDomainOptionGroup($this->_gName)) {
       $domainID = CRM_Core_DAO::getFieldValue('CRM_Core_DAO_OptionValue', $this->_id, 'domain_id', 'id');
       if (CRM_Core_Config::domainID() != $domainID) {
         CRM_Core_Error::statusBounce(ts('You do not have permission to access this page.'));
index 08c034416994e1ba307e4d043b2f8d76d90e13a7..713830405922e321700bf7f7847dc93215080162 100644 (file)
@@ -58,7 +58,7 @@ class CRM_Campaign_Form_SurveyType extends CRM_Admin_Form {
     $url = CRM_Utils_System::url('civicrm/admin/campaign/surveyType', 'reset=1');
     $session->pushUserContext($url);
 
-    if ($this->_id && in_array($this->_gName, CRM_Core_OptionGroup::$_domainIDGroups)) {
+    if ($this->_id && CRM_Core_OptionGroup::isDomainOptionGroup($this->_gName)) {
       $domainID = CRM_Core_DAO::getFieldValue('CRM_Core_DAO_OptionValue', $this->_id, 'domain_id', 'id');
       if (CRM_Core_Config::domainID() != $domainID) {
         CRM_Core_Error::statusBounce(ts('You do not have permission to access this page.'));
index 6c1901caf9ce20e74c98befad3c4ee6727037213..69d78bf677ac1b5be0950765bb0a0798de612821 100644 (file)
@@ -169,7 +169,7 @@ class CRM_Core_BAO_OptionValue extends CRM_Core_DAO_OptionValue {
     $optionValue = new CRM_Core_DAO_OptionValue();
     $optionValue->copyValues($params);
 
-    $isDomainOptionGroup = in_array($groupName, CRM_Core_OptionGroup::$_domainIDGroups);
+    $isDomainOptionGroup = CRM_Core_OptionGroup::isDomainOptionGroup($groupName);
     // When creating a new option for a group that requires a domain, set default domain
     if ($isDomainOptionGroup && empty($params['id']) && (empty($params['domain_id']) || CRM_Utils_System::isNull($params['domain_id']))) {
       $optionValue->domain_id = CRM_Core_Config::domainID();
@@ -215,7 +215,7 @@ class CRM_Core_BAO_OptionValue extends CRM_Core_DAO_OptionValue {
         // CRM-21737 languages option group does not use unique values but unique names.
         $dao->name = $params['name'];
       }
-      if (in_array($groupName, CRM_Core_OptionGroup::$_domainIDGroups)) {
+      if (CRM_Core_OptionGroup::isDomainOptionGroup($groupName)) {
         $dao->domain_id = $optionValue->domain_id;
       }
       $dao->option_group_id = $params['option_group_id'];
index 6215f8e6906c8c274ec192e6f5352ba3d76edc6e..0b2b3653d5477804db367163004692b7005f0f61 100644 (file)
@@ -21,6 +21,8 @@ class CRM_Core_OptionGroup {
   /**
    * $_domainIDGroups array maintains the list of option groups for whom
    * domainID is to be considered.
+   *
+   * FIXME: Hardcoded list = bad. It would be better to make this a column in the civicrm_option_group table
    * @var array
    */
   public static $_domainIDGroups = [
@@ -28,6 +30,14 @@ class CRM_Core_OptionGroup {
     'grant_type',
   ];
 
+  /**
+   * @param $groupName
+   * @return bool
+   */
+  public static function isDomainOptionGroup($groupName) {
+    return in_array($groupName, self::$_domainIDGroups, TRUE);
+  }
+
   /**
    * @param CRM_Core_DAO $dao
    * @param bool $flip
@@ -106,7 +116,7 @@ class CRM_Core_OptionGroup {
     $orderBy = 'weight'
   ) {
     $cache = CRM_Utils_Cache::singleton();
-    if (in_array($name, self::$_domainIDGroups)) {
+    if (self::isDomainOptionGroup($name)) {
       $cacheKey = self::createCacheKey($name, CRM_Core_I18n::getLocale(), $flip, $grouping, $localize, $condition, $labelColumnName, $onlyActive, $keyColumnName, $orderBy, CRM_Core_Config::domainID());
     }
     else {
@@ -144,7 +154,7 @@ WHERE  v.option_group_id = g.id
       }
       $query .= " AND ($componentClause) ";
     }
-    if (in_array($name, self::$_domainIDGroups)) {
+    if (self::isDomainOptionGroup($name)) {
       $query .= " AND v.domain_id = " . CRM_Core_Config::domainID();
     }
 
@@ -433,7 +443,7 @@ WHERE  v.option_group_id = g.id
   AND  g.is_active       = 1
   AND  v.is_default      = 1
 ";
-    if (in_array($groupName, self::$_domainIDGroups)) {
+    if (self::isDomainOptionGroup($groupName)) {
       $query .= " AND v.domain_id = " . CRM_Core_Config::domainID();
     }
 
index 409e73fc9515827c9bc811ac33c1b5d9c705b865..e1219e982e8485ff5aa576b5d0f6ec29f2818b74 100644 (file)
@@ -82,7 +82,7 @@ class CRM_Core_OptionValue {
     if ($optionGroupID) {
       $dao->option_group_id = $optionGroupID;
 
-      if (in_array($groupName, CRM_Core_OptionGroup::$_domainIDGroups)) {
+      if (CRM_Core_OptionGroup::isDomainOptionGroup($groupName)) {
         $dao->domain_id = CRM_Core_Config::domainID();
       }
 
@@ -438,7 +438,7 @@ FROM
       $params[2] = [$groupName, 'String'];
     }
 
-    if (in_array($groupName, CRM_Core_OptionGroup::$_domainIDGroups)) {
+    if (CRM_Core_OptionGroup::isDomainOptionGroup($groupName)) {
       $where .= " AND option_value.domain_id = " . CRM_Core_Config::domainID();
     }
 
index a935eda87a16e19aac8b8e23c10999a09d47d12e..5c6869ffbf79d74fc6e0a32f3690694b16d128a4 100644 (file)
@@ -110,13 +110,21 @@ class ExportAction extends AbstractAction {
         $record[$fieldName] = NULL;
       }
     }
-    // Special handing of current_domain
+    // Should references be limited to the current domain?
+    $limitRefsByDomain = $entityType === 'OptionGroup' && \CRM_Core_OptionGroup::isDomainOptionGroup($record['name']) ? \CRM_Core_BAO_Domain::getDomain()->id : FALSE;
     foreach ($allFields as $fieldName => $field) {
       if (($field['fk_entity'] ?? NULL) === 'Domain') {
         $alias = $fieldName . '.name';
-        if (isset($record[$alias]) && $record[$alias] === \CRM_Core_BAO_Domain::getDomain()->name) {
-          unset($record[$alias]);
-          $record[$fieldName] = 'current_domain';
+        if (isset($record[$alias])) {
+          // If this entity is for a specific domain, limit references to that same domain
+          if ($fieldName === 'domain_id') {
+            $limitRefsByDomain = \CRM_Core_DAO::getFieldValue('CRM_Core_DAO_Domain', $record[$alias], 'id', 'name');
+          }
+          // Swap current domain for special API keyword
+          if ($record[$alias] === \CRM_Core_BAO_Domain::getDomain()->name) {
+            unset($record[$alias]);
+            $record[$fieldName] = 'current_domain';
+          }
         }
       }
     }
@@ -140,6 +148,15 @@ class ExportAction extends AbstractAction {
     $references = [];
     foreach ($dao->findReferences() as $reference) {
       $refEntity = \CRM_Utils_Array::first($reference::fields())['entity'] ?? '';
+      // Limit references by domain
+      if (property_exists($reference, 'domain_id')) {
+        if (!isset($reference->domain_id)) {
+          $reference->find(TRUE);
+        }
+        if (isset($reference->domain_id) && $reference->domain_id != $limitRefsByDomain) {
+          continue;
+        }
+      }
       $references[$refEntity][] = $reference;
     }
     foreach ($references as $refEntity => $records) {
index 8d185581c6fba4d3295f7a99a9034c2468a980cb..844c578e97bfb98748d948ecb820b9a0e392915c 100644 (file)
@@ -359,9 +359,12 @@ class ManagedEntityTest extends UnitTestCase implements TransactionalInterface,
       ->addChain('export', OptionGroup::export()->setId('$id'))
       ->execute()->first();
     $this->assertEquals('from_email_address', $result['export'][1]['params']['values']['option_group_id.name']);
-    $this->assertEquals('current_domain', $result['export'][1]['params']['values']['domain_id']);
     $this->assertNull($result['export'][1]['params']['values']['visibility_id']);
     $this->assertStringStartsWith('OptionGroup_from_email_address_OptionValue_', $result['export'][1]['name']);
+    // All references should be from the current domain
+    foreach (array_slice($result['export'], 1) as $reference) {
+      $this->assertEquals('current_domain', $reference['params']['values']['domain_id']);
+    }
   }
 
   public function testManagedNavigationWeights() {