Angular Loader: Allow modules to specify permissions to add client-side
authorColeman Watts <coleman@civicrm.org>
Tue, 13 Oct 2020 12:52:34 +0000 (08:52 -0400)
committerColeman Watts <coleman@civicrm.org>
Tue, 13 Oct 2020 16:40:37 +0000 (12:40 -0400)
CRM/Mailing/Info.php
Civi/Angular/AngularLoader.php
Civi/Angular/Manager.php
ang/crmMailing.ang.php
tests/phpunit/Civi/Angular/LoaderTest.php

index e339405c56a668ff793ec19e547e90399073fd06..fa647e4679cde83681de3ffca161bc3aff698774 100644 (file)
@@ -159,18 +159,6 @@ class CRM_Mailing_Info extends CRM_Core_Component_Info {
     $result['crmMailingAB'] = include "$civicrm_root/ang/crmMailingAB.ang.php";
     $result['crmD3'] = include "$civicrm_root/ang/crmD3.ang.php";
 
-    CRM_Core_Resources::singleton()
-      ->addPermissions([
-        'view all contacts',
-        'edit all contacts',
-        'access CiviMail',
-        'create mailings',
-        'schedule mailings',
-        'approve mailings',
-        'delete in CiviMail',
-        'edit message templates',
-      ]);
-
     return $result;
   }
 
index be80912a623bd9f0af802c8bc3408456a8164fec..11ef57276c5956ea8622b148c2885752dd95df83 100644 (file)
@@ -120,8 +120,18 @@ class AngularLoader {
       foreach ($angular->getResources($moduleNames, 'settingsFactory', 'settingsFactory') as $moduleName => $factory) {
         $settingsByModule[$moduleName] = array_merge($settingsByModule[$moduleName] ?? [], $factory());
       }
+      // Add clientside permissions
+      $permissions = [];
+      $toCheck  = $angular->getResources($moduleNames, 'permissions', 'permissions');
+      foreach ($toCheck as $perms) {
+        foreach ((array) $perms as $perm) {
+          if (!isset($permissions[$perm])) {
+            $permissions[$perm] = \CRM_Core_Permission::check($perm);
+          }
+        }
+      }
       // TODO optimization; client-side caching
-      return array_merge($settingsByModule, [
+      return array_merge($settingsByModule, ['permissions' => $permissions], [
         'resourceUrls' => \CRM_Extension_System::singleton()->getMapper()->getActiveModuleUrls(),
         'angular' => [
           'modules' => $moduleNames,
index f925b7d343707870d3c2d23e87825560f5ab3f2d..ab9600175a885e120fe515f22d32e0439102b057 100644 (file)
@@ -31,6 +31,8 @@ class Manager {
    *     List of settings to preload.
    *   - settingsFactory: callable
    *     Callback function to fetch settings.
+   *   - permissions: array
+   *     List of permissions to make available client-side
    *   - requires: array
    *     List of other modules required
    */
@@ -413,6 +415,7 @@ class Manager {
             case 'settings':
             case 'settingsFactory':
             case 'requires':
+            case 'permissions':
               if (!empty($module[$resType])) {
                 $result[$moduleName] = $module[$resType];
               }
index ebd23e5f7c46a7a0b4d9088de6550a9204eeb3e8..a91d495221145168616754f1f209b7d2b1e6ea67 100644 (file)
@@ -16,4 +16,14 @@ return [
   'partials' => ['ang/crmMailing'],
   'settingsFactory' => ['CRM_Mailing_Info', 'createAngularSettings'],
   'requires' => ['crmUtil', 'crmAttachment', 'crmAutosave', 'ngRoute', 'ui.utils', 'crmUi', 'dialogService', 'crmResource'],
+  'permissions' => [
+    'view all contacts',
+    'edit all contacts',
+    'access CiviMail',
+    'create mailings',
+    'schedule mailings',
+    'approve mailings',
+    'delete in CiviMail',
+    'edit message templates',
+  ],
 ];
index 07857bd040736c08294b126cd30f79c092397378..c6c144b6a2face448a6398a8e190f9fc132929ca 100644 (file)
@@ -29,9 +29,9 @@ class LoaderTest extends \CiviUnitTestCase {
 
   public function factoryScenarios() {
     return [
-      ['dummy1', 2, 1],
-      ['dummy2', 2, 0],
-      ['dummy3', 2, 2],
+      ['dummy1', 2, 1, ['access CiviCRM', 'administer CiviCRM']],
+      ['dummy2', 2, 0, []],
+      ['dummy3', 2, 2, ['access CiviCRM', 'administer CiviCRM', 'view debug output']],
     ];
   }
 
@@ -43,25 +43,29 @@ class LoaderTest extends \CiviUnitTestCase {
    * @param $module
    * @param $expectedSettingCount
    * @param $expectedCallbackCount
+   * @param $expectedPermissions
    */
-  public function testSettingFactory($module, $expectedSettingCount, $expectedCallbackCount) {
+  public function testSettingFactory($module, $expectedSettingCount, $expectedCallbackCount, $expectedPermissions) {
     (new \Civi\Angular\AngularLoader())
       ->setModules([$module])
       ->useApp()
       ->load();
 
     // Run factory callbacks
-    $factorySettings = \Civi::resources()->getSettings();
+    $actual = \Civi::resources()->getSettings();
 
     // Dummy1 module's factory setting should be set if it is loaded directly or required by dummy3
-    $this->assertTrue(($expectedCallbackCount > 0) === isset($factorySettings['dummy1']['dummy_setting_factory']));
+    $this->assertTrue(($expectedCallbackCount > 0) === isset($actual['dummy1']['dummy_setting_factory']));
     // Dummy3 module's factory setting should be set if it is loaded directly
-    $this->assertTrue(($expectedCallbackCount > 1) === isset($factorySettings['dummy3']['dummy_setting_factory']));
+    $this->assertTrue(($expectedCallbackCount > 1) === isset($actual['dummy3']['dummy_setting_factory']));
 
     // Dummy1 module's regular setting should be set if it is loaded directly or required by dummy3
-    $this->assertTrue(($module !== 'dummy2') === isset($factorySettings['dummy1']['dummy_setting']));
+    $this->assertTrue(($module !== 'dummy2') === isset($actual['dummy1']['dummy_setting']));
     // Dummy2 module's regular setting should be set if loaded
-    $this->assertTrue(($module === 'dummy2') === isset($factorySettings['dummy2']['dummy_setting']));
+    $this->assertTrue(($module === 'dummy2') === isset($actual['dummy2']['dummy_setting']));
+
+    // Assert appropriate permissions have been added
+    $this->assertEquals($expectedPermissions, array_keys($actual['permissions']));
 
     // Assert the callback functions ran the expected number of times
     $this->assertEquals($expectedSettingCount, self::$dummy_setting_count);
@@ -72,6 +76,7 @@ class LoaderTest extends \CiviUnitTestCase {
     $modules['dummy1'] = [
       'ext' => 'civicrm',
       'settings' => $this->getDummySetting(),
+      'permissions' => ['access CiviCRM', 'administer CiviCRM'],
       'settingsFactory' => [self::class, 'getDummySettingFactory'],
     ];
     $modules['dummy2'] = [
@@ -82,6 +87,8 @@ class LoaderTest extends \CiviUnitTestCase {
       'ext' => 'civicrm',
       // The string self::class is preferred but passing object $this should also work
       'settingsFactory' => [$this, 'getDummySettingFactory'],
+      // This should get merged with dummy1's permissions
+      'permissions' => ['view debug output', 'administer CiviCRM'],
       'requires' => ['dummy1'],
     ];
   }