CRM-12193 - Add pick() function randomly choose among options (provided that permissi...
authorTim Otten <totten@civicrm.org>
Sat, 30 Mar 2013 02:13:21 +0000 (22:13 -0400)
committerTim Otten <totten@civicrm.org>
Sat, 30 Mar 2013 02:13:21 +0000 (22:13 -0400)
----------------------------------------
* CRM-12193: In-app fundraising for CiviCRM
  http://issues.civicrm.org/jira/browse/CRM-12193

CRM/Core/CommunityMessages.php
CRM/Core/Permission.php
tests/phpunit/CRM/Core/CommunityMessagesTest.php
tests/phpunit/CiviTest/CiviUnitTestCase.php

index 21b3dc73449d048ba5ebbcd812a1cd55a2e4511d..bf7ef91cb882010194702d3ffebcf19a8c817423 100644 (file)
@@ -31,6 +31,7 @@
 class CRM_Core_CommunityMessages {
 
   const DEFAULT_MESSAGES_URL = 'http://alert.civicrm.org/alert?prot=1&ver={ver}&uf={uf}&sid={sid}';
+  const DEFAULT_PERMISSION = 'administer CiviCRM';
 
   /**
    * Default time to wait before retrying
@@ -134,8 +135,32 @@ class CRM_Core_CommunityMessages {
    * @param array $components
    * @return NULL|array
    */
-  public function pick($permChecker, $components) {
-    throw new Exception('not implemented');
+  public function pick() {
+    $document = $this->getDocument();
+    $messages = array();
+    foreach ($document['messages'] as $message) {
+      if (!isset($message['perms'])) {
+        $message['perms'] = array(self::DEFAULT_PERMISSION);
+      }
+      if (!CRM_Core_Permission::checkAnyPerm($message['perms'])) {
+        continue;
+      }
+
+      if (isset($message['components'])) {
+        $enabled = array_keys(CRM_Core_Component::getEnabledComponents());
+        if (count(array_intersect($enabled, $message['components'])) == 0) {
+          continue;
+        }
+      }
+
+      $messages[] = $message;
+    }
+    if (empty($messages)) {
+      return NULL;
+    }
+
+    $idx = rand(0, count($messages) - 1);
+    return $messages[$idx];
   }
 
   /**
index 4fd201a112e5f656f8989a1d05b97b4e07519116..519721257912d6eae26e32b51fc4066bdb5d5f48 100644 (file)
@@ -77,6 +77,21 @@ class CRM_Core_Permission {
     return $config->userPermissionClass->check( $str );
   }
 
+  /**
+   * Determine if any one of the permissions strings applies to current user
+   *
+   * @param array $perms
+   * @return bool
+   */
+  public static function checkAnyPerm($perms) {
+    foreach ($perms as $perm) {
+      if (CRM_Core_Permission::check($perm)) {
+        return TRUE;
+      }
+    }
+    return FALSE;
+  }
+
   /**
    * Given a group/role array, check for access requirements
    *
index 96736fd19ab54b44a367749ade5cd0fded8873a1..fccf92c302db161acbe954d253e7f23c393871e6 100644 (file)
@@ -87,6 +87,40 @@ class CRM_Core_CommunityMessagesTest extends CiviUnitTestCase {
             ),
           ))
         ),
+        'two-messages' => array(
+          CRM_Utils_HttpClient::STATUS_OK,
+          json_encode(array(
+            'ttl' => 600,
+            'retry' => 600,
+            'messages' => array(
+              array(
+                'markup' => '<h1>One</h1>',
+                'components' => array('CiviMail'),
+              ),
+              array(
+                'markup' => '<h1>Two</h1>',
+                'components' => array('CiviMail'),
+              ),
+            ),
+          ))
+        ),
+        'two-messages-halfbadcomp' => array(
+          CRM_Utils_HttpClient::STATUS_OK,
+          json_encode(array(
+            'ttl' => 600,
+            'retry' => 600,
+            'messages' => array(
+              array(
+                'markup' => '<h1>One</h1>',
+                'components' => array('NotARealComponent'),
+              ),
+              array(
+                'markup' => '<h1>Two</h1>',
+                'components' => array('CiviMail'),
+              ),
+            ),
+          ))
+        ),
       );
     }
     return self::$webResponses;
@@ -252,6 +286,56 @@ class CRM_Core_CommunityMessagesTest extends CiviUnitTestCase {
     $this->assertEquals(strtotime('2013-03-01 12:20:02'), $doc4['expires']);
   }
 
+  /**
+   * Randomly pick among two options
+   */
+  public function testPick_rand() {
+    $communityMessages = new CRM_Core_CommunityMessages(
+      $this->cache,
+      $this->expectOneHttpRequest(self::$webResponses['two-messages'])
+    );
+    $doc1 = $communityMessages->getDocument();
+    $this->assertEquals('<h1>One</h1>', $doc1['messages'][0]['markup']);
+    $this->assertEquals('<h1>Two</h1>', $doc1['messages'][1]['markup']);
+
+    // randomly pick many times
+    $trials = 40;
+    $freq = array(); // array($message => $count)
+    for ($i = 0; $i < $trials; $i++) {
+      $message = $communityMessages->pick();
+      $freq[$message['markup']]++;
+    }
+
+    // assert the probabilities
+    $this->assertApproxEquals(0.5, $freq['<h1>One</h1>'] / $trials, 0.2);
+    $this->assertApproxEquals(0.5, $freq['<h1>Two</h1>'] / $trials, 0.2);
+    $this->assertEquals($trials, $freq['<h1>One</h1>'] + $freq['<h1>Two</h1>']);
+  }
+
+  /**
+   * When presented with two options using component filters, always
+   * choose the one which references an active component.
+   */
+  public function testPick_componentFilter() {
+    $communityMessages = new CRM_Core_CommunityMessages(
+      $this->cache,
+      $this->expectOneHttpRequest(self::$webResponses['two-messages-halfbadcomp'])
+    );
+    $doc1 = $communityMessages->getDocument();
+    $this->assertEquals('<h1>One</h1>', $doc1['messages'][0]['markup']);
+    $this->assertEquals('<h1>Two</h1>', $doc1['messages'][1]['markup']);
+
+    // randomly pick many times
+    $trials = 10;
+    $freq = array(); // array($message => $count)
+    for ($i = 0; $i < $trials; $i++) {
+      $message = $communityMessages->pick();
+      $freq[$message['markup']]++;
+    }
+
+    $this->assertEquals($trials, $freq['<h1>Two</h1>']);
+  }
+
   /**
    * Generate a mock HTTP client with the expectation that it is never called.
    *
index 1abe16eae686c8ae65d56a543eca462b95729f64..1b8304330c67b2b0113ef33da93d55ab0aa6369a 100644 (file)
@@ -552,6 +552,21 @@ class CiviUnitTestCase extends PHPUnit_Extensions_Database_TestCase {
     $this->assertEquals($e, $a);
   }
 
+  /**
+   * Assert that two numbers are approximately equal
+   *
+   * @param int|float $expected
+   * @param int|float $actual
+   * @param int|float $tolerance
+   * @param string $message
+   */
+  function assertApproxEquals($expected, $actual, $tolerance, $message = NULL) {
+    if ($message === NULL) {
+      $message = sprintf("approx-equals: expected=[%.3f] actual=[%.3f] tolerance=[%.3f]", $expected, $actual, $tolerance);
+    }
+    $this->assertTrue(abs($actual - $expected) < $tolerance, $message);
+  }
+
   function assertAttributesEquals($expectedValues, $actualValues, $message = NULL) {
     foreach ($expectedValues as $paramName => $paramValue) {
       if (isset($actualValues[$paramName])) {