Move membershipRenewalDate to own function
[civicrm-core.git] / tests / phpunit / CiviTest / CiviUnitTestCase.php
1 <?php
2 /**
3 * File for the CiviUnitTestCase class
4 *
5 * (PHP 5)
6 *
7 * @copyright Copyright CiviCRM LLC (C) 2009
8 * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html
9 * GNU Affero General Public License version 3
10 * @package CiviCRM
11 *
12 * This file is part of CiviCRM
13 *
14 * CiviCRM is free software; you can redistribute it and/or
15 * modify it under the terms of the GNU Affero General Public License
16 * as published by the Free Software Foundation; either version 3 of
17 * the License, or (at your option) any later version.
18 *
19 * CiviCRM is distributed in the hope that it will be useful,
20 * but WITHOUT ANY WARRANTY; without even the implied warranty of
21 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
22 * GNU Affero General Public License for more details.
23 *
24 * You should have received a copy of the GNU Affero General Public
25 * License along with this program. If not, see
26 * <http://www.gnu.org/licenses/>.
27 */
28
29 use Civi\Payment\System;
30
31 /**
32 * Include class definitions
33 */
34 require_once 'api/api.php';
35 define('API_LATEST_VERSION', 3);
36
37 /**
38 * Base class for CiviCRM unit tests
39 *
40 * This class supports two (mutually-exclusive) techniques for cleaning up test data. Subclasses
41 * may opt for one or neither:
42 *
43 * 1. quickCleanup() is a helper which truncates a series of tables. Call quickCleanup()
44 * as part of setUp() and/or tearDown(). quickCleanup() is thorough - but it can
45 * be cumbersome to use (b/c you must identify the tables to cleanup) and slow to execute.
46 * 2. useTransaction() executes the test inside a transaction. It's easier to use
47 * (because you don't need to identify specific tables), but it doesn't work for tests
48 * which manipulate schema or truncate data -- and could behave inconsistently
49 * for tests which specifically examine DB transactions.
50 *
51 * Common functions for unit tests
52 * @package CiviCRM
53 */
54 class CiviUnitTestCase extends PHPUnit_Extensions_Database_TestCase {
55
56 /**
57 * Api version - easier to override than just a define
58 */
59 protected $_apiversion = API_LATEST_VERSION;
60 /**
61 * Database has been initialized.
62 *
63 * @var boolean
64 */
65 private static $dbInit = FALSE;
66
67 /**
68 * Database connection.
69 *
70 * @var PHPUnit_Extensions_Database_DB_IDatabaseConnection
71 */
72 protected $_dbconn;
73
74 /**
75 * The database name.
76 *
77 * @var string
78 */
79 static protected $_dbName;
80
81 /**
82 * Track tables we have modified during a test.
83 */
84 protected $_tablesToTruncate = array();
85
86 /**
87 * @var array of temporary directory names
88 */
89 protected $tempDirs;
90
91 /**
92 * @var boolean populateOnce allows to skip db resets in setUp
93 *
94 * WARNING! USE WITH CAUTION - IT'LL RENDER DATA DEPENDENCIES
95 * BETWEEN TESTS WHEN RUN IN SUITE. SUITABLE FOR LOCAL, LIMITED
96 * "CHECK RUNS" ONLY!
97 *
98 * IF POSSIBLE, USE $this->DBResetRequired = FALSE IN YOUR TEST CASE!
99 *
100 * see also: http://forum.civicrm.org/index.php/topic,18065.0.html
101 */
102 public static $populateOnce = FALSE;
103
104 /**
105 * @var boolean DBResetRequired allows skipping DB reset
106 * in specific test case. If you still need
107 * to reset single test (method) of such case, call
108 * $this->cleanDB() in the first line of this
109 * test (method).
110 */
111 public $DBResetRequired = TRUE;
112
113 /**
114 * @var CRM_Core_Transaction|NULL
115 */
116 private $tx = NULL;
117
118 /**
119 * @var CRM_Utils_Hook_UnitTests hookClass
120 * example of setting a method for a hook
121 * $this->hookClass->setHook('civicrm_aclWhereClause', array($this, 'aclWhereHookAllResults'));
122 */
123 public $hookClass = NULL;
124
125 /**
126 * @var array common values to be re-used multiple times within a class - usually to create the relevant entity
127 */
128 protected $_params = array();
129
130 /**
131 * @var CRM_Extension_System
132 */
133 protected $origExtensionSystem;
134
135 /**
136 * Array of IDs created during test setup routine.
137 *
138 * The cleanUpSetUpIds method can be used to clear these at the end of the test.
139 *
140 * @var array
141 */
142 public $setupIDs = array();
143
144 /**
145 * Constructor.
146 *
147 * Because we are overriding the parent class constructor, we
148 * need to show the same arguments as exist in the constructor of
149 * PHPUnit_Framework_TestCase, since
150 * PHPUnit_Framework_TestSuite::createTest() creates a
151 * ReflectionClass of the Test class and checks the constructor
152 * of that class to decide how to set up the test.
153 *
154 * @param string $name
155 * @param array $data
156 * @param string $dataName
157 */
158 public function __construct($name = NULL, array$data = array(), $dataName = '') {
159 parent::__construct($name, $data, $dataName);
160
161 // we need full error reporting
162 error_reporting(E_ALL & ~E_NOTICE);
163
164 self::$_dbName = self::getDBName();
165
166 // also load the class loader
167 require_once 'CRM/Core/ClassLoader.php';
168 CRM_Core_ClassLoader::singleton()->register();
169 if (function_exists('_civix_phpunit_setUp')) {
170 // FIXME: loosen coupling
171 _civix_phpunit_setUp();
172 }
173 }
174
175 /**
176 * Override to run the test and assert its state.
177 * @return mixed
178 * @throws \Exception
179 * @throws \PHPUnit_Framework_IncompleteTest
180 * @throws \PHPUnit_Framework_SkippedTest
181 */
182 protected function runTest() {
183 try {
184 return parent::runTest();
185 }
186 catch (PEAR_Exception $e) {
187 // PEAR_Exception has metadata in funny places, and PHPUnit won't log it nicely
188 throw new Exception(\CRM_Core_Error::formatTextException($e), $e->getCode());
189 }
190 }
191
192 /**
193 * @return bool
194 */
195 public function requireDBReset() {
196 return $this->DBResetRequired;
197 }
198
199 /**
200 * @return string
201 */
202 public static function getDBName() {
203 static $dbName = NULL;
204 if ($dbName === NULL) {
205 require_once "DB.php";
206 $dsninfo = DB::parseDSN(CIVICRM_DSN);
207 $dbName = $dsninfo['database'];
208 }
209 return $dbName;
210 }
211
212 /**
213 * Create database connection for this instance.
214 *
215 * Initialize the test database if it hasn't been initialized
216 *
217 * @return PHPUnit_Extensions_Database_DB_IDatabaseConnection connection
218 */
219 protected function getConnection() {
220 $dbName = self::$_dbName;
221 if (!self::$dbInit) {
222 $dbName = self::getDBName();
223
224 // install test database
225 echo PHP_EOL . "Installing {$dbName} database" . PHP_EOL;
226
227 static::_populateDB(FALSE, $this);
228
229 self::$dbInit = TRUE;
230 }
231
232 return $this->createDefaultDBConnection(Civi\Test::pdo(), $dbName);
233 }
234
235 /**
236 * Required implementation of abstract method.
237 */
238 protected function getDataSet() {
239 }
240
241 /**
242 * @param bool $perClass
243 * @param null $object
244 * @return bool
245 * TRUE if the populate logic runs; FALSE if it is skipped
246 */
247 protected static function _populateDB($perClass = FALSE, &$object = NULL) {
248 if (CIVICRM_UF !== 'UnitTests') {
249 throw new \RuntimeException("_populateDB requires CIVICRM_UF=UnitTests");
250 }
251
252 if ($perClass || $object == NULL) {
253 $dbreset = TRUE;
254 }
255 else {
256 $dbreset = $object->requireDBReset();
257 }
258
259 if (self::$populateOnce || !$dbreset) {
260 return FALSE;
261 }
262 self::$populateOnce = NULL;
263
264 Civi\Test::data()->populate();
265
266 return TRUE;
267 }
268
269 public static function setUpBeforeClass() {
270 static::_populateDB(TRUE);
271
272 // also set this global hack
273 $GLOBALS['_PEAR_ERRORSTACK_OVERRIDE_CALLBACK'] = array();
274 }
275
276 /**
277 * Common setup functions for all unit tests.
278 */
279 protected function setUp() {
280 $session = CRM_Core_Session::singleton();
281 $session->set('userID', NULL);
282
283 $this->errorScope = CRM_Core_TemporaryErrorScope::useException(); // REVERT
284 // Use a temporary file for STDIN
285 $GLOBALS['stdin'] = tmpfile();
286 if ($GLOBALS['stdin'] === FALSE) {
287 echo "Couldn't open temporary file\n";
288 exit(1);
289 }
290
291 // Get and save a connection to the database
292 $this->_dbconn = $this->getConnection();
293
294 // reload database before each test
295 // $this->_populateDB();
296
297 // "initialize" CiviCRM to avoid problems when running single tests
298 // FIXME: look at it closer in second stage
299
300 $GLOBALS['civicrm_setting']['domain']['fatalErrorHandler'] = 'CiviUnitTestCase_fatalErrorHandler';
301 $GLOBALS['civicrm_setting']['domain']['backtrace'] = 1;
302
303 // disable any left-over test extensions
304 CRM_Core_DAO::executeQuery('DELETE FROM civicrm_extension WHERE full_name LIKE "test.%"');
305
306 // reset all the caches
307 CRM_Utils_System::flushCache();
308
309 // initialize the object once db is loaded
310 \Civi::reset();
311 $config = CRM_Core_Config::singleton(TRUE, TRUE); // ugh, performance
312
313 // when running unit tests, use mockup user framework
314 $this->hookClass = CRM_Utils_Hook::singleton();
315
316 // Make sure the DB connection is setup properly
317 $config->userSystem->setMySQLTimeZone();
318 $env = new CRM_Utils_Check_Component_Env();
319 CRM_Utils_Check::singleton()->assertValid($env->checkMysqlTime());
320
321 // clear permissions stub to not check permissions
322 $config->userPermissionClass->permissions = NULL;
323
324 //flush component settings
325 CRM_Core_Component::getEnabledComponents(TRUE);
326
327 error_reporting(E_ALL);
328
329 $this->_sethtmlGlobals();
330 }
331
332 /**
333 * Read everything from the datasets directory and insert into the db.
334 */
335 public function loadAllFixtures() {
336 $fixturesDir = __DIR__ . '/../../fixtures';
337
338 $this->getConnection()->getConnection()->query("SET FOREIGN_KEY_CHECKS = 0;");
339
340 $xmlFiles = glob($fixturesDir . '/*.xml');
341 foreach ($xmlFiles as $xmlFixture) {
342 $op = new PHPUnit_Extensions_Database_Operation_Insert();
343 $dataset = $this->createXMLDataSet($xmlFixture);
344 $this->_tablesToTruncate = array_merge($this->_tablesToTruncate, $dataset->getTableNames());
345 $op->execute($this->_dbconn, $dataset);
346 }
347
348 $yamlFiles = glob($fixturesDir . '/*.yaml');
349 foreach ($yamlFiles as $yamlFixture) {
350 $op = new PHPUnit_Extensions_Database_Operation_Insert();
351 $dataset = new PHPUnit_Extensions_Database_DataSet_YamlDataSet($yamlFixture);
352 $this->_tablesToTruncate = array_merge($this->_tablesToTruncate, $dataset->getTableNames());
353 $op->execute($this->_dbconn, $dataset);
354 }
355
356 $this->getConnection()->getConnection()->query("SET FOREIGN_KEY_CHECKS = 1;");
357 }
358
359 /**
360 * Emulate a logged in user since certain functions use that.
361 * value to store a record in the DB (like activity)
362 * CRM-8180
363 *
364 * @return int
365 * Contact ID of the created user.
366 */
367 public function createLoggedInUser() {
368 $params = array(
369 'first_name' => 'Logged In',
370 'last_name' => 'User ' . rand(),
371 'contact_type' => 'Individual',
372 );
373 $contactID = $this->individualCreate($params);
374 $this->callAPISuccess('UFMatch', 'create', array(
375 'contact_id' => $contactID,
376 'uf_name' => 'superman',
377 'uf_id' => 6,
378 ));
379
380 $session = CRM_Core_Session::singleton();
381 $session->set('userID', $contactID);
382 return $contactID;
383 }
384
385 /**
386 * Create default domain contacts for the two domains added during test class.
387 * database population.
388 */
389 public function createDomainContacts() {
390 $this->organizationCreate();
391 $this->organizationCreate(array('organization_name' => 'Second Domain'));
392 }
393
394 /**
395 * Common teardown functions for all unit tests.
396 */
397 protected function tearDown() {
398 error_reporting(E_ALL & ~E_NOTICE);
399 CRM_Utils_Hook::singleton()->reset();
400 $this->hookClass->reset();
401 $session = CRM_Core_Session::singleton();
402 $session->set('userID', NULL);
403
404 if ($this->tx) {
405 $this->tx->rollback()->commit();
406 $this->tx = NULL;
407
408 CRM_Core_Transaction::forceRollbackIfEnabled();
409 \Civi\Core\Transaction\Manager::singleton(TRUE);
410 }
411 else {
412 CRM_Core_Transaction::forceRollbackIfEnabled();
413 \Civi\Core\Transaction\Manager::singleton(TRUE);
414
415 $tablesToTruncate = array('civicrm_contact', 'civicrm_uf_match');
416 $this->quickCleanup($tablesToTruncate);
417 $this->createDomainContacts();
418 }
419
420 $this->cleanTempDirs();
421 $this->unsetExtensionSystem();
422 }
423
424 /**
425 * Generic function to compare expected values after an api call to retrieved.
426 * DB values.
427 *
428 * @daoName string DAO Name of object we're evaluating.
429 * @id int Id of object
430 * @match array Associative array of field name => expected value. Empty if asserting
431 * that a DELETE occurred
432 * @delete boolean True if we're checking that a DELETE action occurred.
433 * @param $daoName
434 * @param $id
435 * @param $match
436 * @param bool $delete
437 * @throws \PHPUnit_Framework_AssertionFailedError
438 */
439 public function assertDBState($daoName, $id, $match, $delete = FALSE) {
440 if (empty($id)) {
441 // adding this here since developers forget to check for an id
442 // and hence we get the first value in the db
443 $this->fail('ID not populated. Please fix your assertDBState usage!!!');
444 }
445
446 $object = new $daoName();
447 $object->id = $id;
448 $verifiedCount = 0;
449
450 // If we're asserting successful record deletion, make sure object is NOT found.
451 if ($delete) {
452 if ($object->find(TRUE)) {
453 $this->fail("Object not deleted by delete operation: $daoName, $id");
454 }
455 return;
456 }
457
458 // Otherwise check matches of DAO field values against expected values in $match.
459 if ($object->find(TRUE)) {
460 $fields = &$object->fields();
461 foreach ($fields as $name => $value) {
462 $dbName = $value['name'];
463 if (isset($match[$name])) {
464 $verifiedCount++;
465 $this->assertEquals($object->$dbName, $match[$name]);
466 }
467 elseif (isset($match[$dbName])) {
468 $verifiedCount++;
469 $this->assertEquals($object->$dbName, $match[$dbName]);
470 }
471 }
472 }
473 else {
474 $this->fail("Could not retrieve object: $daoName, $id");
475 }
476 $object->free();
477 $matchSize = count($match);
478 if ($verifiedCount != $matchSize) {
479 $this->fail("Did not verify all fields in match array: $daoName, $id. Verified count = $verifiedCount. Match array size = $matchSize");
480 }
481 }
482
483 /**
484 * Request a record from the DB by seachColumn+searchValue. Success if a record is found.
485 * @param string $daoName
486 * @param $searchValue
487 * @param $returnColumn
488 * @param $searchColumn
489 * @param $message
490 *
491 * @return null|string
492 * @throws PHPUnit_Framework_AssertionFailedError
493 */
494 public function assertDBNotNull($daoName, $searchValue, $returnColumn, $searchColumn, $message) {
495 if (empty($searchValue)) {
496 $this->fail("empty value passed to assertDBNotNull");
497 }
498 $value = CRM_Core_DAO::getFieldValue($daoName, $searchValue, $returnColumn, $searchColumn, TRUE);
499 $this->assertNotNull($value, $message);
500
501 return $value;
502 }
503
504 /**
505 * Request a record from the DB by seachColumn+searchValue. Success if returnColumn value is NULL.
506 * @param string $daoName
507 * @param $searchValue
508 * @param $returnColumn
509 * @param $searchColumn
510 * @param $message
511 */
512 public function assertDBNull($daoName, $searchValue, $returnColumn, $searchColumn, $message) {
513 $value = CRM_Core_DAO::getFieldValue($daoName, $searchValue, $returnColumn, $searchColumn, TRUE);
514 $this->assertNull($value, $message);
515 }
516
517 /**
518 * Request a record from the DB by id. Success if row not found.
519 * @param string $daoName
520 * @param int $id
521 * @param null $message
522 */
523 public function assertDBRowNotExist($daoName, $id, $message = NULL) {
524 $message = $message ? $message : "$daoName (#$id) should not exist";
525 $value = CRM_Core_DAO::getFieldValue($daoName, $id, 'id', 'id', TRUE);
526 $this->assertNull($value, $message);
527 }
528
529 /**
530 * Request a record from the DB by id. Success if row not found.
531 * @param string $daoName
532 * @param int $id
533 * @param null $message
534 */
535 public function assertDBRowExist($daoName, $id, $message = NULL) {
536 $message = $message ? $message : "$daoName (#$id) should exist";
537 $value = CRM_Core_DAO::getFieldValue($daoName, $id, 'id', 'id', TRUE);
538 $this->assertEquals($id, $value, $message);
539 }
540
541 /**
542 * Compare a single column value in a retrieved DB record to an expected value.
543 * @param string $daoName
544 * @param $searchValue
545 * @param $returnColumn
546 * @param $searchColumn
547 * @param $expectedValue
548 * @param $message
549 */
550 public function assertDBCompareValue(
551 $daoName, $searchValue, $returnColumn, $searchColumn,
552 $expectedValue, $message
553 ) {
554 $value = CRM_Core_DAO::getFieldValue($daoName, $searchValue, $returnColumn, $searchColumn, TRUE);
555 $this->assertEquals($expectedValue, $value, $message);
556 }
557
558 /**
559 * Compare all values in a single retrieved DB record to an array of expected values.
560 * @param string $daoName
561 * @param array $searchParams
562 * @param $expectedValues
563 */
564 public function assertDBCompareValues($daoName, $searchParams, $expectedValues) {
565 //get the values from db
566 $dbValues = array();
567 CRM_Core_DAO::commonRetrieve($daoName, $searchParams, $dbValues);
568
569 // compare db values with expected values
570 self::assertAttributesEquals($expectedValues, $dbValues);
571 }
572
573 /**
574 * Assert that a SQL query returns a given value.
575 *
576 * The first argument is an expected value. The remaining arguments are passed
577 * to CRM_Core_DAO::singleValueQuery
578 *
579 * Example: $this->assertSql(2, 'select count(*) from foo where foo.bar like "%1"',
580 * array(1 => array("Whiz", "String")));
581 * @param $expected
582 * @param $query
583 * @param array $params
584 * @param string $message
585 */
586 public function assertDBQuery($expected, $query, $params = array(), $message = '') {
587 if ($message) {
588 $message .= ': ';
589 }
590 $actual = CRM_Core_DAO::singleValueQuery($query, $params);
591 $this->assertEquals($expected, $actual,
592 sprintf('%sexpected=[%s] actual=[%s] query=[%s]',
593 $message, $expected, $actual, CRM_Core_DAO::composeQuery($query, $params, FALSE)
594 )
595 );
596 }
597
598 /**
599 * Assert that two array-trees are exactly equal, notwithstanding
600 * the sorting of keys
601 *
602 * @param array $expected
603 * @param array $actual
604 */
605 public function assertTreeEquals($expected, $actual) {
606 $e = array();
607 $a = array();
608 CRM_Utils_Array::flatten($expected, $e, '', ':::');
609 CRM_Utils_Array::flatten($actual, $a, '', ':::');
610 ksort($e);
611 ksort($a);
612
613 $this->assertEquals($e, $a);
614 }
615
616 /**
617 * Assert that two numbers are approximately equal.
618 *
619 * @param int|float $expected
620 * @param int|float $actual
621 * @param int|float $tolerance
622 * @param string $message
623 */
624 public function assertApproxEquals($expected, $actual, $tolerance, $message = NULL) {
625 if ($message === NULL) {
626 $message = sprintf("approx-equals: expected=[%.3f] actual=[%.3f] tolerance=[%.3f]", $expected, $actual, $tolerance);
627 }
628 $this->assertTrue(abs($actual - $expected) < $tolerance, $message);
629 }
630
631 /**
632 * Assert attributes are equal.
633 *
634 * @param $expectedValues
635 * @param $actualValues
636 * @param string $message
637 *
638 * @throws PHPUnit_Framework_AssertionFailedError
639 */
640 public function assertAttributesEquals($expectedValues, $actualValues, $message = NULL) {
641 foreach ($expectedValues as $paramName => $paramValue) {
642 if (isset($actualValues[$paramName])) {
643 $this->assertEquals($paramValue, $actualValues[$paramName], "Value Mismatch On $paramName - value 1 is " . print_r($paramValue, TRUE) . " value 2 is " . print_r($actualValues[$paramName], TRUE));
644 }
645 else {
646 $this->assertNull($expectedValues[$paramName], "Attribute '$paramName' not present in actual array and we expected it to be " . $expectedValues[$paramName]);
647 }
648 }
649 }
650
651 /**
652 * @param $key
653 * @param $list
654 */
655 public function assertArrayKeyExists($key, &$list) {
656 $result = isset($list[$key]) ? TRUE : FALSE;
657 $this->assertTrue($result, ts("%1 element exists?",
658 array(1 => $key)
659 ));
660 }
661
662 /**
663 * @param $key
664 * @param $list
665 */
666 public function assertArrayValueNotNull($key, &$list) {
667 $this->assertArrayKeyExists($key, $list);
668
669 $value = isset($list[$key]) ? $list[$key] : NULL;
670 $this->assertTrue($value,
671 ts("%1 element not null?",
672 array(1 => $key)
673 )
674 );
675 }
676
677 /**
678 * Check that api returned 'is_error' => 0.
679 *
680 * @param array $apiResult
681 * Api result.
682 * @param string $prefix
683 * Extra test to add to message.
684 */
685 public function assertAPISuccess($apiResult, $prefix = '') {
686 if (!empty($prefix)) {
687 $prefix .= ': ';
688 }
689 $errorMessage = empty($apiResult['error_message']) ? '' : " " . $apiResult['error_message'];
690
691 if (!empty($apiResult['debug_information'])) {
692 $errorMessage .= "\n " . print_r($apiResult['debug_information'], TRUE);
693 }
694 if (!empty($apiResult['trace'])) {
695 $errorMessage .= "\n" . print_r($apiResult['trace'], TRUE);
696 }
697 $this->assertEquals(0, $apiResult['is_error'], $prefix . $errorMessage);
698 }
699
700 /**
701 * Check that api returned 'is_error' => 1.
702 *
703 * @param array $apiResult
704 * Api result.
705 * @param string $prefix
706 * Extra test to add to message.
707 * @param null $expectedError
708 */
709 public function assertAPIFailure($apiResult, $prefix = '', $expectedError = NULL) {
710 if (!empty($prefix)) {
711 $prefix .= ': ';
712 }
713 if ($expectedError && !empty($apiResult['is_error'])) {
714 $this->assertEquals($expectedError, $apiResult['error_message'], 'api error message not as expected' . $prefix);
715 }
716 $this->assertEquals(1, $apiResult['is_error'], "api call should have failed but it succeeded " . $prefix . (print_r($apiResult, TRUE)));
717 $this->assertNotEmpty($apiResult['error_message']);
718 }
719
720 /**
721 * @param $expected
722 * @param $actual
723 * @param string $message
724 */
725 public function assertType($expected, $actual, $message = '') {
726 return $this->assertInternalType($expected, $actual, $message);
727 }
728
729 /**
730 * Check that a deleted item has been deleted.
731 *
732 * @param $entity
733 * @param $id
734 */
735 public function assertAPIDeleted($entity, $id) {
736 $this->callAPISuccess($entity, 'getcount', array('id' => $id), 0);
737 }
738
739
740 /**
741 * Check that api returned 'is_error' => 1
742 * else provide full message
743 * @param array $result
744 * @param $expected
745 * @param array $valuesToExclude
746 * @param string $prefix
747 * Extra test to add to message.
748 */
749 public function assertAPIArrayComparison($result, $expected, $valuesToExclude = array(), $prefix = '') {
750 $valuesToExclude = array_merge($valuesToExclude, array('debug', 'xdebug', 'sequential'));
751 foreach ($valuesToExclude as $value) {
752 if (isset($result[$value])) {
753 unset($result[$value]);
754 }
755 if (isset($expected[$value])) {
756 unset($expected[$value]);
757 }
758 }
759 $this->assertEquals($result, $expected, "api result array comparison failed " . $prefix . print_r($result, TRUE) . ' was compared to ' . print_r($expected, TRUE));
760 }
761
762 /**
763 * A stub for the API interface. This can be overriden by subclasses to change how the API is called.
764 *
765 * @param $entity
766 * @param $action
767 * @param array $params
768 * @return array|int
769 */
770 public function civicrm_api($entity, $action, $params) {
771 return civicrm_api($entity, $action, $params);
772 }
773
774 /**
775 * Create a batch of external API calls which can
776 * be executed concurrently.
777 *
778 * @code
779 * $calls = $this->createExternalAPI()
780 * ->addCall('Contact', 'get', ...)
781 * ->addCall('Contact', 'get', ...)
782 * ...
783 * ->run()
784 * ->getResults();
785 * @endcode
786 *
787 * @return \Civi\API\ExternalBatch
788 * @throws PHPUnit_Framework_SkippedTestError
789 */
790 public function createExternalAPI() {
791 global $civicrm_root;
792 $defaultParams = array(
793 'version' => $this->_apiversion,
794 'debug' => 1,
795 );
796
797 $calls = new \Civi\API\ExternalBatch($defaultParams);
798
799 if (!$calls->isSupported()) {
800 $this->markTestSkipped('The test relies on Civi\API\ExternalBatch. This is unsupported in the local environment.');
801 }
802
803 return $calls;
804 }
805
806 /**
807 * wrap api functions.
808 * so we can ensure they succeed & throw exceptions without litterering the test with checks
809 *
810 * @param string $entity
811 * @param string $action
812 * @param array $params
813 * @param mixed $checkAgainst
814 * Optional value to check result against, implemented for getvalue,.
815 * getcount, getsingle. Note that for getvalue the type is checked rather than the value
816 * for getsingle the array is compared against an array passed in - the id is not compared (for
817 * better or worse )
818 *
819 * @return array|int
820 */
821 public function callAPISuccess($entity, $action, $params, $checkAgainst = NULL) {
822 $params = array_merge(array(
823 'version' => $this->_apiversion,
824 'debug' => 1,
825 ),
826 $params
827 );
828 switch (strtolower($action)) {
829 case 'getvalue':
830 return $this->callAPISuccessGetValue($entity, $params, $checkAgainst);
831
832 case 'getsingle':
833 return $this->callAPISuccessGetSingle($entity, $params, $checkAgainst);
834
835 case 'getcount':
836 return $this->callAPISuccessGetCount($entity, $params, $checkAgainst);
837 }
838 $result = $this->civicrm_api($entity, $action, $params);
839 $this->assertAPISuccess($result, "Failure in api call for $entity $action");
840 return $result;
841 }
842
843 /**
844 * This function exists to wrap api getValue function & check the result
845 * so we can ensure they succeed & throw exceptions without litterering the test with checks
846 * There is a type check in this
847 *
848 * @param string $entity
849 * @param array $params
850 * @param string $type
851 * Per http://php.net/manual/en/function.gettype.php possible types.
852 * - boolean
853 * - integer
854 * - double
855 * - string
856 * - array
857 * - object
858 *
859 * @return array|int
860 */
861 public function callAPISuccessGetValue($entity, $params, $type = NULL) {
862 $params += array(
863 'version' => $this->_apiversion,
864 'debug' => 1,
865 );
866 $result = $this->civicrm_api($entity, 'getvalue', $params);
867 if ($type) {
868 if ($type == 'integer') {
869 // api seems to return integers as strings
870 $this->assertTrue(is_numeric($result), "expected a numeric value but got " . print_r($result, 1));
871 }
872 else {
873 $this->assertType($type, $result, "returned result should have been of type $type but was ");
874 }
875 }
876 return $result;
877 }
878
879 /**
880 * This function exists to wrap api getsingle function & check the result
881 * so we can ensure they succeed & throw exceptions without litterering the test with checks
882 *
883 * @param string $entity
884 * @param array $params
885 * @param array $checkAgainst
886 * Array to compare result against.
887 * - boolean
888 * - integer
889 * - double
890 * - string
891 * - array
892 * - object
893 *
894 * @throws Exception
895 * @return array|int
896 */
897 public function callAPISuccessGetSingle($entity, $params, $checkAgainst = NULL) {
898 $params += array(
899 'version' => $this->_apiversion,
900 );
901 $result = $this->civicrm_api($entity, 'getsingle', $params);
902 if (!is_array($result) || !empty($result['is_error']) || isset($result['values'])) {
903 throw new Exception('Invalid getsingle result' . print_r($result, TRUE));
904 }
905 if ($checkAgainst) {
906 // @todo - have gone with the fn that unsets id? should we check id?
907 $this->checkArrayEquals($result, $checkAgainst);
908 }
909 return $result;
910 }
911
912 /**
913 * This function exists to wrap api getValue function & check the result
914 * so we can ensure they succeed & throw exceptions without litterering the test with checks
915 * There is a type check in this
916 * @param string $entity
917 * @param array $params
918 * @param null $count
919 * @throws Exception
920 * @return array|int
921 */
922 public function callAPISuccessGetCount($entity, $params, $count = NULL) {
923 $params += array(
924 'version' => $this->_apiversion,
925 'debug' => 1,
926 );
927 $result = $this->civicrm_api($entity, 'getcount', $params);
928 if (!is_int($result) || !empty($result['is_error']) || isset($result['values'])) {
929 throw new Exception('Invalid getcount result : ' . print_r($result, TRUE) . " type :" . gettype($result));
930 }
931 if (is_int($count)) {
932 $this->assertEquals($count, $result, "incorrect count returned from $entity getcount");
933 }
934 return $result;
935 }
936
937 /**
938 * This function exists to wrap api functions.
939 * so we can ensure they succeed, generate and example & throw exceptions without litterering the test with checks
940 *
941 * @param string $entity
942 * @param string $action
943 * @param array $params
944 * @param string $function
945 * Pass this in to create a generated example.
946 * @param string $file
947 * Pass this in to create a generated example.
948 * @param string $description
949 * @param string|null $exampleName
950 *
951 * @return array|int
952 */
953 public function callAPIAndDocument($entity, $action, $params, $function, $file, $description = "", $exampleName = NULL) {
954 $params['version'] = $this->_apiversion;
955 $result = $this->callAPISuccess($entity, $action, $params);
956 $this->documentMe($entity, $action, $params, $result, $function, $file, $description, $exampleName);
957 return $result;
958 }
959
960 /**
961 * This function exists to wrap api functions.
962 * so we can ensure they fail where expected & throw exceptions without litterering the test with checks
963 * @param string $entity
964 * @param string $action
965 * @param array $params
966 * @param string $expectedErrorMessage
967 * Error.
968 * @param null $extraOutput
969 * @return array|int
970 */
971 public function callAPIFailure($entity, $action, $params, $expectedErrorMessage = NULL, $extraOutput = NULL) {
972 if (is_array($params)) {
973 $params += array(
974 'version' => $this->_apiversion,
975 );
976 }
977 $result = $this->civicrm_api($entity, $action, $params);
978 $this->assertAPIFailure($result, "We expected a failure for $entity $action but got a success", $expectedErrorMessage);
979 return $result;
980 }
981
982 /**
983 * Create required data based on $this->entity & $this->params
984 * This is just a way to set up the test data for delete & get functions
985 * so the distinction between set
986 * up & tested functions is clearer
987 *
988 * @return array
989 * api Result
990 */
991 public function createTestEntity() {
992 return $entity = $this->callAPISuccess($this->entity, 'create', $this->params);
993 }
994
995 /**
996 * Generic function to create Organisation, to be used in test cases
997 *
998 * @param array $params
999 * parameters for civicrm_contact_add api function call
1000 * @param int $seq
1001 * sequence number if creating multiple organizations
1002 *
1003 * @return int
1004 * id of Organisation created
1005 */
1006 public function organizationCreate($params = array(), $seq = 0) {
1007 if (!$params) {
1008 $params = array();
1009 }
1010 $params = array_merge($this->sampleContact('Organization', $seq), $params);
1011 return $this->_contactCreate($params);
1012 }
1013
1014 /**
1015 * Generic function to create Individual, to be used in test cases
1016 *
1017 * @param array $params
1018 * parameters for civicrm_contact_add api function call
1019 * @param int $seq
1020 * sequence number if creating multiple individuals
1021 * @param bool $random
1022 *
1023 * @return int
1024 * id of Individual created
1025 */
1026 public function individualCreate($params = array(), $seq = 0, $random = FALSE) {
1027 $params = array_merge($this->sampleContact('Individual', $seq, $random), $params);
1028 return $this->_contactCreate($params);
1029 }
1030
1031 /**
1032 * Generic function to create Household, to be used in test cases
1033 *
1034 * @param array $params
1035 * parameters for civicrm_contact_add api function call
1036 * @param int $seq
1037 * sequence number if creating multiple households
1038 *
1039 * @return int
1040 * id of Household created
1041 */
1042 public function householdCreate($params = array(), $seq = 0) {
1043 $params = array_merge($this->sampleContact('Household', $seq), $params);
1044 return $this->_contactCreate($params);
1045 }
1046
1047 /**
1048 * Helper function for getting sample contact properties.
1049 *
1050 * @param string $contact_type
1051 * enum contact type: Individual, Organization
1052 * @param int $seq
1053 * sequence number for the values of this type
1054 *
1055 * @return array
1056 * properties of sample contact (ie. $params for API call)
1057 */
1058 public function sampleContact($contact_type, $seq = 0, $random = FALSE) {
1059 $samples = array(
1060 'Individual' => array(
1061 // The number of values in each list need to be coprime numbers to not have duplicates
1062 'first_name' => array('Anthony', 'Joe', 'Terrence', 'Lucie', 'Albert', 'Bill', 'Kim'),
1063 'middle_name' => array('J.', 'M.', 'P', 'L.', 'K.', 'A.', 'B.', 'C.', 'D', 'E.', 'Z.'),
1064 'last_name' => array('Anderson', 'Miller', 'Smith', 'Collins', 'Peterson'),
1065 ),
1066 'Organization' => array(
1067 'organization_name' => array(
1068 'Unit Test Organization',
1069 'Acme',
1070 'Roberts and Sons',
1071 'Cryo Space Labs',
1072 'Sharper Pens',
1073 ),
1074 ),
1075 'Household' => array(
1076 'household_name' => array('Unit Test household'),
1077 ),
1078 );
1079 $params = array('contact_type' => $contact_type);
1080 foreach ($samples[$contact_type] as $key => $values) {
1081 $params[$key] = $values[$seq % count($values)];
1082 if ($random) {
1083 $params[$key] .= substr(sha1(rand()), 0, 5);
1084 }
1085 }
1086 if ($contact_type == 'Individual') {
1087 $params['email'] = strtolower(
1088 $params['first_name'] . '_' . $params['last_name'] . '@civicrm.org'
1089 );
1090 $params['prefix_id'] = 3;
1091 $params['suffix_id'] = 3;
1092 }
1093 return $params;
1094 }
1095
1096 /**
1097 * Private helper function for calling civicrm_contact_add.
1098 *
1099 * @param array $params
1100 * For civicrm_contact_add api function call.
1101 *
1102 * @throws Exception
1103 *
1104 * @return int
1105 * id of Household created
1106 */
1107 private function _contactCreate($params) {
1108 $result = $this->callAPISuccess('contact', 'create', $params);
1109 if (!empty($result['is_error']) || empty($result['id'])) {
1110 throw new Exception('Could not create test contact, with message: ' . CRM_Utils_Array::value('error_message', $result) . "\nBacktrace:" . CRM_Utils_Array::value('trace', $result));
1111 }
1112 return $result['id'];
1113 }
1114
1115 /**
1116 * Delete contact, ensuring it is not the domain contact
1117 *
1118 * @param int $contactID
1119 * Contact ID to delete
1120 */
1121 public function contactDelete($contactID) {
1122 $domain = new CRM_Core_BAO_Domain();
1123 $domain->contact_id = $contactID;
1124 if (!$domain->find(TRUE)) {
1125 $this->callAPISuccess('contact', 'delete', array(
1126 'id' => $contactID,
1127 'skip_undelete' => 1,
1128 ));
1129 }
1130 }
1131
1132 /**
1133 * @param int $contactTypeId
1134 *
1135 * @throws Exception
1136 */
1137 public function contactTypeDelete($contactTypeId) {
1138 $result = CRM_Contact_BAO_ContactType::del($contactTypeId);
1139 if (!$result) {
1140 throw new Exception('Could not delete contact type');
1141 }
1142 }
1143
1144 /**
1145 * @param array $params
1146 *
1147 * @return mixed
1148 */
1149 public function membershipTypeCreate($params = array()) {
1150 CRM_Member_PseudoConstant::flush('membershipType');
1151 CRM_Core_Config::clearDBCache();
1152 $this->setupIDs['contact'] = $memberOfOrganization = $this->organizationCreate();
1153 $params = array_merge(array(
1154 'name' => 'General',
1155 'duration_unit' => 'year',
1156 'duration_interval' => 1,
1157 'period_type' => 'rolling',
1158 'member_of_contact_id' => $memberOfOrganization,
1159 'domain_id' => 1,
1160 'financial_type_id' => 2,
1161 'is_active' => 1,
1162 'sequential' => 1,
1163 'visibility' => 'Public',
1164 ), $params);
1165
1166 $result = $this->callAPISuccess('MembershipType', 'Create', $params);
1167
1168 CRM_Member_PseudoConstant::flush('membershipType');
1169 CRM_Utils_Cache::singleton()->flush();
1170
1171 return $result['id'];
1172 }
1173
1174 /**
1175 * @param array $params
1176 *
1177 * @return mixed
1178 */
1179 public function contactMembershipCreate($params) {
1180 $params = array_merge(array(
1181 'join_date' => '2007-01-21',
1182 'start_date' => '2007-01-21',
1183 'end_date' => '2007-12-21',
1184 'source' => 'Payment',
1185 'membership_type_id' => 'General',
1186 ), $params);
1187 if (!is_numeric($params['membership_type_id'])) {
1188 $membershipTypes = $this->callAPISuccess('Membership', 'getoptions', array('action' => 'create', 'field' => 'membership_type_id'));
1189 if (!in_array($params['membership_type_id'], $membershipTypes['values'])) {
1190 $this->membershipTypeCreate(array('name' => $params['membership_type_id']));
1191 }
1192 }
1193
1194 $result = $this->callAPISuccess('Membership', 'create', $params);
1195 return $result['id'];
1196 }
1197
1198 /**
1199 * Delete Membership Type.
1200 *
1201 * @param array $params
1202 */
1203 public function membershipTypeDelete($params) {
1204 $this->callAPISuccess('MembershipType', 'Delete', $params);
1205 }
1206
1207 /**
1208 * @param int $membershipID
1209 */
1210 public function membershipDelete($membershipID) {
1211 $deleteParams = array('id' => $membershipID);
1212 $result = $this->callAPISuccess('Membership', 'Delete', $deleteParams);
1213 }
1214
1215 /**
1216 * @param string $name
1217 *
1218 * @return mixed
1219 */
1220 public function membershipStatusCreate($name = 'test member status') {
1221 $params['name'] = $name;
1222 $params['start_event'] = 'start_date';
1223 $params['end_event'] = 'end_date';
1224 $params['is_current_member'] = 1;
1225 $params['is_active'] = 1;
1226
1227 $result = $this->callAPISuccess('MembershipStatus', 'Create', $params);
1228 CRM_Member_PseudoConstant::flush('membershipStatus');
1229 return $result['id'];
1230 }
1231
1232 /**
1233 * @param int $membershipStatusID
1234 */
1235 public function membershipStatusDelete($membershipStatusID) {
1236 if (!$membershipStatusID) {
1237 return;
1238 }
1239 $result = $this->callAPISuccess('MembershipStatus', 'Delete', array('id' => $membershipStatusID));
1240 }
1241
1242 public function membershipRenewalDate($durationUnit, $membershipEndDate) {
1243 // We only have an end_date if frequency units match, otherwise membership won't be autorenewed and dates won't be calculated.
1244 $renewedMembershipEndDate = new DateTime($membershipEndDate);
1245 switch ($durationUnit) {
1246 case 'year':
1247 $renewedMembershipEndDate->add(new DateInterval('P1Y'));
1248 break;
1249
1250 case 'month':
1251 // We have to add 1 day first in case it's the end of the month, then subtract afterwards
1252 // eg. 2018-02-28 should renew to 2018-03-31, if we just added 1 month we'd get 2018-03-28
1253 $renewedMembershipEndDate->add(new DateInterval('P1D'));
1254 $renewedMembershipEndDate->add(new DateInterval('P1M'));
1255 $renewedMembershipEndDate->sub(new DateInterval('P1D'));
1256 break;
1257 }
1258 return $renewedMembershipEndDate->format('Y-m-d');
1259 }
1260
1261 /**
1262 * @param array $params
1263 *
1264 * @return mixed
1265 */
1266 public function relationshipTypeCreate($params = array()) {
1267 $params = array_merge(array(
1268 'name_a_b' => 'Relation 1 for relationship type create',
1269 'name_b_a' => 'Relation 2 for relationship type create',
1270 'contact_type_a' => 'Individual',
1271 'contact_type_b' => 'Organization',
1272 'is_reserved' => 1,
1273 'is_active' => 1,
1274 ),
1275 $params
1276 );
1277
1278 $result = $this->callAPISuccess('relationship_type', 'create', $params);
1279 CRM_Core_PseudoConstant::flush('relationshipType');
1280
1281 return $result['id'];
1282 }
1283
1284 /**
1285 * Delete Relatinship Type.
1286 *
1287 * @param int $relationshipTypeID
1288 */
1289 public function relationshipTypeDelete($relationshipTypeID) {
1290 $params['id'] = $relationshipTypeID;
1291 $check = $this->callAPISuccess('relationship_type', 'get', $params);
1292 if (!empty($check['count'])) {
1293 $this->callAPISuccess('relationship_type', 'delete', $params);
1294 }
1295 }
1296
1297 /**
1298 * @param array $params
1299 *
1300 * @return mixed
1301 */
1302 public function paymentProcessorTypeCreate($params = NULL) {
1303 if (is_null($params)) {
1304 $params = array(
1305 'name' => 'API_Test_PP',
1306 'title' => 'API Test Payment Processor',
1307 'class_name' => 'CRM_Core_Payment_APITest',
1308 'billing_mode' => 'form',
1309 'is_recur' => 0,
1310 'is_reserved' => 1,
1311 'is_active' => 1,
1312 );
1313 }
1314 $result = $this->callAPISuccess('payment_processor_type', 'create', $params);
1315
1316 CRM_Core_PseudoConstant::flush('paymentProcessorType');
1317
1318 return $result['id'];
1319 }
1320
1321 /**
1322 * Create test Authorize.net instance.
1323 *
1324 * @param array $params
1325 *
1326 * @return mixed
1327 */
1328 public function paymentProcessorAuthorizeNetCreate($params = array()) {
1329 $params = array_merge(array(
1330 'name' => 'Authorize',
1331 'domain_id' => CRM_Core_Config::domainID(),
1332 'payment_processor_type_id' => 'AuthNet',
1333 'title' => 'AuthNet',
1334 'is_active' => 1,
1335 'is_default' => 0,
1336 'is_test' => 1,
1337 'is_recur' => 1,
1338 'user_name' => '4y5BfuW7jm',
1339 'password' => '4cAmW927n8uLf5J8',
1340 'url_site' => 'https://test.authorize.net/gateway/transact.dll',
1341 'url_recur' => 'https://apitest.authorize.net/xml/v1/request.api',
1342 'class_name' => 'Payment_AuthorizeNet',
1343 'billing_mode' => 1,
1344 ), $params);
1345
1346 $result = $this->callAPISuccess('PaymentProcessor', 'create', $params);
1347 return $result['id'];
1348 }
1349
1350 /**
1351 * Create Participant.
1352 *
1353 * @param array $params
1354 * Array of contact id and event id values.
1355 *
1356 * @return int
1357 * $id of participant created
1358 */
1359 public function participantCreate($params) {
1360 if (empty($params['contact_id'])) {
1361 $params['contact_id'] = $this->individualCreate();
1362 }
1363 if (empty($params['event_id'])) {
1364 $event = $this->eventCreate();
1365 $params['event_id'] = $event['id'];
1366 }
1367 $defaults = array(
1368 'status_id' => 2,
1369 'role_id' => 1,
1370 'register_date' => 20070219,
1371 'source' => 'Wimbeldon',
1372 'event_level' => 'Payment',
1373 'debug' => 1,
1374 );
1375
1376 $params = array_merge($defaults, $params);
1377 $result = $this->callAPISuccess('Participant', 'create', $params);
1378 return $result['id'];
1379 }
1380
1381 /**
1382 * Create Payment Processor.
1383 *
1384 * @return int
1385 * Id Payment Processor
1386 */
1387 public function processorCreate($params = array()) {
1388 $processorParams = array(
1389 'domain_id' => 1,
1390 'name' => 'Dummy',
1391 'payment_processor_type_id' => 'Dummy',
1392 'financial_account_id' => 12,
1393 'is_test' => TRUE,
1394 'is_active' => 1,
1395 'user_name' => '',
1396 'url_site' => 'http://dummy.com',
1397 'url_recur' => 'http://dummy.com',
1398 'billing_mode' => 1,
1399 'sequential' => 1,
1400 'payment_instrument_id' => 'Debit Card',
1401 );
1402 $processorParams = array_merge($processorParams, $params);
1403 $processor = $this->callAPISuccess('PaymentProcessor', 'create', $processorParams);
1404 return $processor['id'];
1405 }
1406
1407 /**
1408 * Create Payment Processor.
1409 *
1410 * @param array $processorParams
1411 *
1412 * @return \CRM_Core_Payment_Dummy
1413 * Instance of Dummy Payment Processor
1414 */
1415 public function dummyProcessorCreate($processorParams = array()) {
1416 $paymentProcessorID = $this->processorCreate($processorParams);
1417 return System::singleton()->getById($paymentProcessorID);
1418 }
1419
1420 /**
1421 * Create contribution page.
1422 *
1423 * @param array $params
1424 * @return array
1425 * Array of contribution page
1426 */
1427 public function contributionPageCreate($params = array()) {
1428 $this->_pageParams = array_merge(array(
1429 'title' => 'Test Contribution Page',
1430 'financial_type_id' => 1,
1431 'currency' => 'USD',
1432 'financial_account_id' => 1,
1433 'is_active' => 1,
1434 'is_allow_other_amount' => 1,
1435 'min_amount' => 10,
1436 'max_amount' => 1000,
1437 ), $params);
1438 return $this->callAPISuccess('contribution_page', 'create', $this->_pageParams);
1439 }
1440
1441 /**
1442 * Create a sample batch.
1443 */
1444 public function batchCreate() {
1445 $params = $this->_params;
1446 $params['name'] = $params['title'] = 'Batch_433397';
1447 $params['status_id'] = 1;
1448 $result = $this->callAPISuccess('batch', 'create', $params);
1449 return $result['id'];
1450 }
1451
1452 /**
1453 * Create Tag.
1454 *
1455 * @param array $params
1456 * @return array
1457 * result of created tag
1458 */
1459 public function tagCreate($params = array()) {
1460 $defaults = array(
1461 'name' => 'New Tag3',
1462 'description' => 'This is description for Our New Tag ',
1463 'domain_id' => '1',
1464 );
1465 $params = array_merge($defaults, $params);
1466 $result = $this->callAPISuccess('Tag', 'create', $params);
1467 return $result['values'][$result['id']];
1468 }
1469
1470 /**
1471 * Delete Tag.
1472 *
1473 * @param int $tagId
1474 * Id of the tag to be deleted.
1475 *
1476 * @return int
1477 */
1478 public function tagDelete($tagId) {
1479 require_once 'api/api.php';
1480 $params = array(
1481 'tag_id' => $tagId,
1482 );
1483 $result = $this->callAPISuccess('Tag', 'delete', $params);
1484 return $result['id'];
1485 }
1486
1487 /**
1488 * Add entity(s) to the tag
1489 *
1490 * @param array $params
1491 *
1492 * @return bool
1493 */
1494 public function entityTagAdd($params) {
1495 $result = $this->callAPISuccess('entity_tag', 'create', $params);
1496 return TRUE;
1497 }
1498
1499 /**
1500 * Create pledge.
1501 *
1502 * @param array $params
1503 * Parameters.
1504 *
1505 * @return int
1506 * id of created pledge
1507 */
1508 public function pledgeCreate($params) {
1509 $params = array_merge(array(
1510 'pledge_create_date' => date('Ymd'),
1511 'start_date' => date('Ymd'),
1512 'scheduled_date' => date('Ymd'),
1513 'amount' => 100.00,
1514 'pledge_status_id' => '2',
1515 'financial_type_id' => '1',
1516 'pledge_original_installment_amount' => 20,
1517 'frequency_interval' => 5,
1518 'frequency_unit' => 'year',
1519 'frequency_day' => 15,
1520 'installments' => 5,
1521 ),
1522 $params);
1523
1524 $result = $this->callAPISuccess('Pledge', 'create', $params);
1525 return $result['id'];
1526 }
1527
1528 /**
1529 * Delete contribution.
1530 *
1531 * @param int $pledgeId
1532 */
1533 public function pledgeDelete($pledgeId) {
1534 $params = array(
1535 'pledge_id' => $pledgeId,
1536 );
1537 $this->callAPISuccess('Pledge', 'delete', $params);
1538 }
1539
1540 /**
1541 * Create contribution.
1542 *
1543 * @param array $params
1544 * Array of parameters.
1545 *
1546 * @return int
1547 * id of created contribution
1548 */
1549 public function contributionCreate($params) {
1550
1551 $params = array_merge(array(
1552 'domain_id' => 1,
1553 'receive_date' => date('Ymd'),
1554 'total_amount' => 100.00,
1555 'fee_amount' => 5.00,
1556 'financial_type_id' => 1,
1557 'payment_instrument_id' => 1,
1558 'non_deductible_amount' => 10.00,
1559 'trxn_id' => 12345,
1560 'invoice_id' => 67890,
1561 'source' => 'SSF',
1562 'contribution_status_id' => 1,
1563 ), $params);
1564
1565 $result = $this->callAPISuccess('contribution', 'create', $params);
1566 return $result['id'];
1567 }
1568
1569 /**
1570 * Delete contribution.
1571 *
1572 * @param int $contributionId
1573 *
1574 * @return array|int
1575 */
1576 public function contributionDelete($contributionId) {
1577 $params = array(
1578 'contribution_id' => $contributionId,
1579 );
1580 $result = $this->callAPISuccess('contribution', 'delete', $params);
1581 return $result;
1582 }
1583
1584 /**
1585 * Create an Event.
1586 *
1587 * @param array $params
1588 * Name-value pair for an event.
1589 *
1590 * @return array
1591 */
1592 public function eventCreate($params = array()) {
1593 // if no contact was passed, make up a dummy event creator
1594 if (!isset($params['contact_id'])) {
1595 $params['contact_id'] = $this->_contactCreate(array(
1596 'contact_type' => 'Individual',
1597 'first_name' => 'Event',
1598 'last_name' => 'Creator',
1599 ));
1600 }
1601
1602 // set defaults for missing params
1603 $params = array_merge(array(
1604 'title' => 'Annual CiviCRM meet',
1605 'summary' => 'If you have any CiviCRM related issues or want to track where CiviCRM is heading, Sign up now',
1606 'description' => 'This event is intended to give brief idea about progess of CiviCRM and giving solutions to common user issues',
1607 'event_type_id' => 1,
1608 'is_public' => 1,
1609 'start_date' => 20081021,
1610 'end_date' => 20081023,
1611 'is_online_registration' => 1,
1612 'registration_start_date' => 20080601,
1613 'registration_end_date' => 20081015,
1614 'max_participants' => 100,
1615 'event_full_text' => 'Sorry! We are already full',
1616 'is_monetary' => 0,
1617 'is_active' => 1,
1618 'is_show_location' => 0,
1619 ), $params);
1620
1621 return $this->callAPISuccess('Event', 'create', $params);
1622 }
1623
1624 /**
1625 * Create a paid event.
1626 *
1627 * @param array $params
1628 *
1629 * @return array
1630 */
1631 protected function eventCreatePaid($params) {
1632 $event = $this->eventCreate($params);
1633 $this->priceSetID = $this->eventPriceSetCreate(55, 0, 'Radio');
1634 CRM_Price_BAO_PriceSet::addTo('civicrm_event', $event['id'], $this->priceSetID);
1635 $priceSet = CRM_Price_BAO_PriceSet::getSetDetail($this->priceSetID, TRUE, FALSE);
1636 $priceSet = CRM_Utils_Array::value($this->priceSetID, $priceSet);
1637 $this->eventFeeBlock = CRM_Utils_Array::value('fields', $priceSet);
1638 return $event;
1639 }
1640
1641 /**
1642 * Delete event.
1643 *
1644 * @param int $id
1645 * ID of the event.
1646 *
1647 * @return array|int
1648 */
1649 public function eventDelete($id) {
1650 $params = array(
1651 'event_id' => $id,
1652 );
1653 return $this->callAPISuccess('event', 'delete', $params);
1654 }
1655
1656 /**
1657 * Delete participant.
1658 *
1659 * @param int $participantID
1660 *
1661 * @return array|int
1662 */
1663 public function participantDelete($participantID) {
1664 $params = array(
1665 'id' => $participantID,
1666 );
1667 $check = $this->callAPISuccess('Participant', 'get', $params);
1668 if ($check['count'] > 0) {
1669 return $this->callAPISuccess('Participant', 'delete', $params);
1670 }
1671 }
1672
1673 /**
1674 * Create participant payment.
1675 *
1676 * @param int $participantID
1677 * @param int $contributionID
1678 * @return int
1679 * $id of created payment
1680 */
1681 public function participantPaymentCreate($participantID, $contributionID = NULL) {
1682 //Create Participant Payment record With Values
1683 $params = array(
1684 'participant_id' => $participantID,
1685 'contribution_id' => $contributionID,
1686 );
1687
1688 $result = $this->callAPISuccess('participant_payment', 'create', $params);
1689 return $result['id'];
1690 }
1691
1692 /**
1693 * Delete participant payment.
1694 *
1695 * @param int $paymentID
1696 */
1697 public function participantPaymentDelete($paymentID) {
1698 $params = array(
1699 'id' => $paymentID,
1700 );
1701 $result = $this->callAPISuccess('participant_payment', 'delete', $params);
1702 }
1703
1704 /**
1705 * Add a Location.
1706 *
1707 * @param int $contactID
1708 * @return int
1709 * location id of created location
1710 */
1711 public function locationAdd($contactID) {
1712 $address = array(
1713 1 => array(
1714 'location_type' => 'New Location Type',
1715 'is_primary' => 1,
1716 'name' => 'Saint Helier St',
1717 'county' => 'Marin',
1718 'country' => 'UNITED STATES',
1719 'state_province' => 'Michigan',
1720 'supplemental_address_1' => 'Hallmark Ct',
1721 'supplemental_address_2' => 'Jersey Village',
1722 'supplemental_address_3' => 'My Town',
1723 ),
1724 );
1725
1726 $params = array(
1727 'contact_id' => $contactID,
1728 'address' => $address,
1729 'location_format' => '2.0',
1730 'location_type' => 'New Location Type',
1731 );
1732
1733 $result = $this->callAPISuccess('Location', 'create', $params);
1734 return $result;
1735 }
1736
1737 /**
1738 * Delete Locations of contact.
1739 *
1740 * @param array $params
1741 * Parameters.
1742 */
1743 public function locationDelete($params) {
1744 $this->callAPISuccess('Location', 'delete', $params);
1745 }
1746
1747 /**
1748 * Add a Location Type.
1749 *
1750 * @param array $params
1751 * @return CRM_Core_DAO_LocationType
1752 * location id of created location
1753 */
1754 public function locationTypeCreate($params = NULL) {
1755 if ($params === NULL) {
1756 $params = array(
1757 'name' => 'New Location Type',
1758 'vcard_name' => 'New Location Type',
1759 'description' => 'Location Type for Delete',
1760 'is_active' => 1,
1761 );
1762 }
1763
1764 $locationType = new CRM_Core_DAO_LocationType();
1765 $locationType->copyValues($params);
1766 $locationType->save();
1767 // clear getfields cache
1768 CRM_Core_PseudoConstant::flush();
1769 $this->callAPISuccess('phone', 'getfields', array('version' => 3, 'cache_clear' => 1));
1770 return $locationType;
1771 }
1772
1773 /**
1774 * Delete a Location Type.
1775 *
1776 * @param int $locationTypeId
1777 */
1778 public function locationTypeDelete($locationTypeId) {
1779 $locationType = new CRM_Core_DAO_LocationType();
1780 $locationType->id = $locationTypeId;
1781 $locationType->delete();
1782 }
1783
1784 /**
1785 * Add a Mapping.
1786 *
1787 * @param array $params
1788 * @return CRM_Core_DAO_Mapping
1789 * Mapping id of created mapping
1790 */
1791 public function mappingCreate($params = NULL) {
1792 if ($params === NULL) {
1793 $params = array(
1794 'name' => 'Mapping name',
1795 'description' => 'Mapping description',
1796 // 'Export Contact' mapping.
1797 'mapping_type_id' => 7,
1798 );
1799 }
1800
1801 $mapping = new CRM_Core_DAO_Mapping();
1802 $mapping->copyValues($params);
1803 $mapping->save();
1804 // clear getfields cache
1805 CRM_Core_PseudoConstant::flush();
1806 $this->callAPISuccess('mapping', 'getfields', array('version' => 3, 'cache_clear' => 1));
1807 return $mapping;
1808 }
1809
1810 /**
1811 * Delete a Mapping
1812 *
1813 * @param int $mappingId
1814 */
1815 public function mappingDelete($mappingId) {
1816 $mapping = new CRM_Core_DAO_Mapping();
1817 $mapping->id = $mappingId;
1818 $mapping->delete();
1819 }
1820
1821 /**
1822 * Add a Group.
1823 *
1824 * @param array $params
1825 * @return int
1826 * groupId of created group
1827 */
1828 public function groupCreate($params = array()) {
1829 $params = array_merge(array(
1830 'name' => 'Test Group 1',
1831 'domain_id' => 1,
1832 'title' => 'New Test Group Created',
1833 'description' => 'New Test Group Created',
1834 'is_active' => 1,
1835 'visibility' => 'Public Pages',
1836 'group_type' => array(
1837 '1' => 1,
1838 '2' => 1,
1839 ),
1840 ), $params);
1841
1842 $result = $this->callAPISuccess('Group', 'create', $params);
1843 return $result['id'];
1844 }
1845
1846 /**
1847 * Prepare class for ACLs.
1848 */
1849 protected function prepareForACLs() {
1850 $config = CRM_Core_Config::singleton();
1851 $config->userPermissionClass->permissions = array();
1852 }
1853
1854 /**
1855 * Reset after ACLs.
1856 */
1857 protected function cleanUpAfterACLs() {
1858 CRM_Utils_Hook::singleton()->reset();
1859 $tablesToTruncate = array(
1860 'civicrm_acl',
1861 'civicrm_acl_cache',
1862 'civicrm_acl_entity_role',
1863 'civicrm_acl_contact_cache',
1864 );
1865 $this->quickCleanup($tablesToTruncate);
1866 $config = CRM_Core_Config::singleton();
1867 unset($config->userPermissionClass->permissions);
1868 }
1869 /**
1870 * Create a smart group.
1871 *
1872 * By default it will be a group of households.
1873 *
1874 * @param array $smartGroupParams
1875 * @param array $groupParams
1876 * @return int
1877 */
1878 public function smartGroupCreate($smartGroupParams = array(), $groupParams = array()) {
1879 $smartGroupParams = array_merge(array(
1880 'formValues' => array('contact_type' => array('IN' => array('Household'))),
1881 ),
1882 $smartGroupParams);
1883 $savedSearch = CRM_Contact_BAO_SavedSearch::create($smartGroupParams);
1884
1885 $groupParams['saved_search_id'] = $savedSearch->id;
1886 return $this->groupCreate($groupParams);
1887 }
1888
1889 /**
1890 * Function to add a Group.
1891 *
1892 * @params array to add group
1893 *
1894 * @param int $groupID
1895 * @param int $totalCount
1896 * @param bool $random
1897 * @return int
1898 * groupId of created group
1899 */
1900 public function groupContactCreate($groupID, $totalCount = 10, $random = FALSE) {
1901 $params = array('group_id' => $groupID);
1902 for ($i = 1; $i <= $totalCount; $i++) {
1903 $contactID = $this->individualCreate(array(), 0, $random);
1904 if ($i == 1) {
1905 $params += array('contact_id' => $contactID);
1906 }
1907 else {
1908 $params += array("contact_id.$i" => $contactID);
1909 }
1910 }
1911 $result = $this->callAPISuccess('GroupContact', 'create', $params);
1912
1913 return $result;
1914 }
1915
1916 /**
1917 * Delete a Group.
1918 *
1919 * @param int $gid
1920 */
1921 public function groupDelete($gid) {
1922
1923 $params = array(
1924 'id' => $gid,
1925 );
1926
1927 $this->callAPISuccess('Group', 'delete', $params);
1928 }
1929
1930 /**
1931 * Create a UFField.
1932 * @param array $params
1933 */
1934 public function uFFieldCreate($params = array()) {
1935 $params = array_merge(array(
1936 'uf_group_id' => 1,
1937 'field_name' => 'first_name',
1938 'is_active' => 1,
1939 'is_required' => 1,
1940 'visibility' => 'Public Pages and Listings',
1941 'is_searchable' => '1',
1942 'label' => 'first_name',
1943 'field_type' => 'Individual',
1944 'weight' => 1,
1945 ), $params);
1946 $this->callAPISuccess('uf_field', 'create', $params);
1947 }
1948
1949 /**
1950 * Add a UF Join Entry.
1951 *
1952 * @param array $params
1953 * @return int
1954 * $id of created UF Join
1955 */
1956 public function ufjoinCreate($params = NULL) {
1957 if ($params === NULL) {
1958 $params = array(
1959 'is_active' => 1,
1960 'module' => 'CiviEvent',
1961 'entity_table' => 'civicrm_event',
1962 'entity_id' => 3,
1963 'weight' => 1,
1964 'uf_group_id' => 1,
1965 );
1966 }
1967 $result = $this->callAPISuccess('uf_join', 'create', $params);
1968 return $result;
1969 }
1970
1971 /**
1972 * Delete a UF Join Entry.
1973 *
1974 * @param array $params
1975 * with missing uf_group_id
1976 */
1977 public function ufjoinDelete($params = NULL) {
1978 if ($params === NULL) {
1979 $params = array(
1980 'is_active' => 1,
1981 'module' => 'CiviEvent',
1982 'entity_table' => 'civicrm_event',
1983 'entity_id' => 3,
1984 'weight' => 1,
1985 'uf_group_id' => '',
1986 );
1987 }
1988
1989 crm_add_uf_join($params);
1990 }
1991
1992 /**
1993 * @param array $params
1994 * Optional parameters.
1995 * @param bool $reloadConfig
1996 * While enabling CiviCampaign component, we shouldn't always forcibly
1997 * reload config as this hinder hook call in test environment
1998 *
1999 * @return int
2000 * Campaign ID.
2001 */
2002 public function campaignCreate($params = array(), $reloadConfig = TRUE) {
2003 $this->enableCiviCampaign($reloadConfig);
2004 $campaign = $this->callAPISuccess('campaign', 'create', array_merge(array(
2005 'name' => 'big_campaign',
2006 'title' => 'Campaign',
2007 ), $params));
2008 return $campaign['id'];
2009 }
2010
2011 /**
2012 * Create Group for a contact.
2013 *
2014 * @param int $contactId
2015 */
2016 public function contactGroupCreate($contactId) {
2017 $params = array(
2018 'contact_id.1' => $contactId,
2019 'group_id' => 1,
2020 );
2021
2022 $this->callAPISuccess('GroupContact', 'Create', $params);
2023 }
2024
2025 /**
2026 * Delete Group for a contact.
2027 *
2028 * @param int $contactId
2029 */
2030 public function contactGroupDelete($contactId) {
2031 $params = array(
2032 'contact_id.1' => $contactId,
2033 'group_id' => 1,
2034 );
2035 $this->civicrm_api('GroupContact', 'Delete', $params);
2036 }
2037
2038 /**
2039 * Create Activity.
2040 *
2041 * @param array $params
2042 * @return array|int
2043 */
2044 public function activityCreate($params = array()) {
2045 $params = array_merge(array(
2046 'subject' => 'Discussion on warm beer',
2047 'activity_date_time' => date('Ymd'),
2048 'duration_hours' => 30,
2049 'duration_minutes' => 20,
2050 'location' => 'Baker Street',
2051 'details' => 'Lets schedule a meeting',
2052 'status_id' => 1,
2053 'activity_name' => 'Meeting',
2054 ), $params);
2055 if (!isset($params['source_contact_id'])) {
2056 $params['source_contact_id'] = $this->individualCreate();
2057 }
2058 if (!isset($params['target_contact_id'])) {
2059 $params['target_contact_id'] = $this->individualCreate(array(
2060 'first_name' => 'Julia',
2061 'Last_name' => 'Anderson',
2062 'prefix' => 'Ms.',
2063 'email' => 'julia_anderson@civicrm.org',
2064 'contact_type' => 'Individual',
2065 ));
2066 }
2067 if (!isset($params['assignee_contact_id'])) {
2068 $params['assignee_contact_id'] = $params['target_contact_id'];
2069 }
2070
2071 $result = $this->callAPISuccess('Activity', 'create', $params);
2072
2073 $result['target_contact_id'] = $params['target_contact_id'];
2074 $result['assignee_contact_id'] = $params['assignee_contact_id'];
2075 return $result;
2076 }
2077
2078 /**
2079 * Create an activity type.
2080 *
2081 * @param array $params
2082 * Parameters.
2083 * @return array
2084 */
2085 public function activityTypeCreate($params) {
2086 return $this->callAPISuccess('ActivityType', 'create', $params);
2087 }
2088
2089 /**
2090 * Delete activity type.
2091 *
2092 * @param int $activityTypeId
2093 * Id of the activity type.
2094 * @return array
2095 */
2096 public function activityTypeDelete($activityTypeId) {
2097 $params['activity_type_id'] = $activityTypeId;
2098 return $this->callAPISuccess('ActivityType', 'delete', $params);
2099 }
2100
2101 /**
2102 * Create custom group.
2103 *
2104 * @param array $params
2105 * @return array
2106 */
2107 public function customGroupCreate($params = array()) {
2108 $defaults = array(
2109 'title' => 'new custom group',
2110 'extends' => 'Contact',
2111 'domain_id' => 1,
2112 'style' => 'Inline',
2113 'is_active' => 1,
2114 );
2115
2116 $params = array_merge($defaults, $params);
2117
2118 //have a crack @ deleting it first in the hope this will prevent derailing our tests
2119 $this->callAPISuccess('custom_group', 'get', array(
2120 'title' => $params['title'],
2121 array('api.custom_group.delete' => 1),
2122 ));
2123
2124 return $this->callAPISuccess('custom_group', 'create', $params);
2125 }
2126
2127 /**
2128 * Existing function doesn't allow params to be over-ridden so need a new one
2129 * this one allows you to only pass in the params you want to change
2130 * @param array $params
2131 * @return array|int
2132 */
2133 public function CustomGroupCreateByParams($params = array()) {
2134 $defaults = array(
2135 'title' => "API Custom Group",
2136 'extends' => 'Contact',
2137 'domain_id' => 1,
2138 'style' => 'Inline',
2139 'is_active' => 1,
2140 );
2141 $params = array_merge($defaults, $params);
2142 return $this->callAPISuccess('custom_group', 'create', $params);
2143 }
2144
2145 /**
2146 * Create custom group with multi fields.
2147 * @param array $params
2148 * @return array|int
2149 */
2150 public function CustomGroupMultipleCreateByParams($params = array()) {
2151 $defaults = array(
2152 'style' => 'Tab',
2153 'is_multiple' => 1,
2154 );
2155 $params = array_merge($defaults, $params);
2156 return $this->CustomGroupCreateByParams($params);
2157 }
2158
2159 /**
2160 * Create custom group with multi fields.
2161 * @param array $params
2162 * @return array
2163 */
2164 public function CustomGroupMultipleCreateWithFields($params = array()) {
2165 // also need to pass on $params['custom_field'] if not set but not in place yet
2166 $ids = array();
2167 $customGroup = $this->CustomGroupMultipleCreateByParams($params);
2168 $ids['custom_group_id'] = $customGroup['id'];
2169
2170 $customField = $this->customFieldCreate(array(
2171 'custom_group_id' => $ids['custom_group_id'],
2172 'label' => 'field_1' . $ids['custom_group_id'],
2173 'in_selector' => 1,
2174 ));
2175
2176 $ids['custom_field_id'][] = $customField['id'];
2177
2178 $customField = $this->customFieldCreate(array(
2179 'custom_group_id' => $ids['custom_group_id'],
2180 'default_value' => '',
2181 'label' => 'field_2' . $ids['custom_group_id'],
2182 'in_selector' => 1,
2183 ));
2184 $ids['custom_field_id'][] = $customField['id'];
2185
2186 $customField = $this->customFieldCreate(array(
2187 'custom_group_id' => $ids['custom_group_id'],
2188 'default_value' => '',
2189 'label' => 'field_3' . $ids['custom_group_id'],
2190 'in_selector' => 1,
2191 ));
2192 $ids['custom_field_id'][] = $customField['id'];
2193
2194 return $ids;
2195 }
2196
2197 /**
2198 * Create a custom group with a single text custom field. See
2199 * participant:testCreateWithCustom for how to use this
2200 *
2201 * @param string $function
2202 * __FUNCTION__.
2203 * @param string $filename
2204 * $file __FILE__.
2205 *
2206 * @return array
2207 * ids of created objects
2208 */
2209 public function entityCustomGroupWithSingleFieldCreate($function, $filename) {
2210 $params = array('title' => $function);
2211 $entity = substr(basename($filename), 0, strlen(basename($filename)) - 8);
2212 $params['extends'] = $entity ? $entity : 'Contact';
2213 $customGroup = $this->CustomGroupCreate($params);
2214 $customField = $this->customFieldCreate(array('custom_group_id' => $customGroup['id'], 'label' => $function));
2215 CRM_Core_PseudoConstant::flush();
2216
2217 return array('custom_group_id' => $customGroup['id'], 'custom_field_id' => $customField['id']);
2218 }
2219
2220 /**
2221 * Delete custom group.
2222 *
2223 * @param int $customGroupID
2224 *
2225 * @return array|int
2226 */
2227 public function customGroupDelete($customGroupID) {
2228 $params['id'] = $customGroupID;
2229 return $this->callAPISuccess('custom_group', 'delete', $params);
2230 }
2231
2232 /**
2233 * Create custom field.
2234 *
2235 * @param array $params
2236 * (custom_group_id) is required.
2237 * @return array
2238 */
2239 public function customFieldCreate($params) {
2240 $params = array_merge(array(
2241 'label' => 'Custom Field',
2242 'data_type' => 'String',
2243 'html_type' => 'Text',
2244 'is_searchable' => 1,
2245 'is_active' => 1,
2246 'default_value' => 'defaultValue',
2247 ), $params);
2248
2249 $result = $this->callAPISuccess('custom_field', 'create', $params);
2250 // these 2 functions are called with force to flush static caches
2251 CRM_Core_BAO_CustomField::getTableColumnGroup($result['id'], 1);
2252 CRM_Core_Component::getEnabledComponents(1);
2253 return $result;
2254 }
2255
2256 /**
2257 * Delete custom field.
2258 *
2259 * @param int $customFieldID
2260 *
2261 * @return array|int
2262 */
2263 public function customFieldDelete($customFieldID) {
2264
2265 $params['id'] = $customFieldID;
2266 return $this->callAPISuccess('custom_field', 'delete', $params);
2267 }
2268
2269 /**
2270 * Create note.
2271 *
2272 * @param int $cId
2273 * @return array
2274 */
2275 public function noteCreate($cId) {
2276 $params = array(
2277 'entity_table' => 'civicrm_contact',
2278 'entity_id' => $cId,
2279 'note' => 'hello I am testing Note',
2280 'contact_id' => $cId,
2281 'modified_date' => date('Ymd'),
2282 'subject' => 'Test Note',
2283 );
2284
2285 return $this->callAPISuccess('Note', 'create', $params);
2286 }
2287
2288 /**
2289 * Enable CiviCampaign Component.
2290 *
2291 * @param bool $reloadConfig
2292 * Force relaod config or not
2293 */
2294 public function enableCiviCampaign($reloadConfig = TRUE) {
2295 CRM_Core_BAO_ConfigSetting::enableComponent('CiviCampaign');
2296 if ($reloadConfig) {
2297 // force reload of config object
2298 $config = CRM_Core_Config::singleton(TRUE, TRUE);
2299 }
2300 //flush cache by calling with reset
2301 $activityTypes = CRM_Core_PseudoConstant::activityType(TRUE, TRUE, TRUE, 'name', TRUE);
2302 }
2303
2304 /**
2305 * Create test generated example in api/v3/examples.
2306 *
2307 * To turn this off (e.g. on the server) set
2308 * define(DONT_DOCUMENT_TEST_CONFIG ,1);
2309 * in your settings file
2310 *
2311 * @param string $entity
2312 * @param string $action
2313 * @param array $params
2314 * Array as passed to civicrm_api function.
2315 * @param array $result
2316 * Array as received from the civicrm_api function.
2317 * @param string $testFunction
2318 * Calling function - generally __FUNCTION__.
2319 * @param string $testFile
2320 * Called from file - generally __FILE__.
2321 * @param string $description
2322 * Descriptive text for the example file.
2323 * @param string $exampleName
2324 * Name for this example file (CamelCase) - if omitted the action name will be substituted.
2325 */
2326 private function documentMe($entity, $action, $params, $result, $testFunction, $testFile, $description = "", $exampleName = NULL) {
2327 if (defined('DONT_DOCUMENT_TEST_CONFIG') && DONT_DOCUMENT_TEST_CONFIG) {
2328 return;
2329 }
2330 $entity = _civicrm_api_get_camel_name($entity);
2331 $action = strtolower($action);
2332
2333 if (empty($exampleName)) {
2334 // Attempt to convert lowercase action name to CamelCase.
2335 // This is clunky/imperfect due to the convention of all lowercase actions.
2336 $exampleName = CRM_Utils_String::convertStringToCamel($action);
2337 $knownPrefixes = array(
2338 'Get',
2339 'Set',
2340 'Create',
2341 'Update',
2342 'Send',
2343 );
2344 foreach ($knownPrefixes as $prefix) {
2345 if (strpos($exampleName, $prefix) === 0 && $prefix != $exampleName) {
2346 $exampleName[strlen($prefix)] = strtoupper($exampleName[strlen($prefix)]);
2347 }
2348 }
2349 }
2350
2351 $this->tidyExampleResult($result);
2352 if (isset($params['version'])) {
2353 unset($params['version']);
2354 }
2355 // Format multiline description as array
2356 $desc = array();
2357 if (is_string($description) && strlen($description)) {
2358 foreach (explode("\n", $description) as $line) {
2359 $desc[] = trim($line);
2360 }
2361 }
2362 $smarty = CRM_Core_Smarty::singleton();
2363 $smarty->assign('testFunction', $testFunction);
2364 $smarty->assign('function', _civicrm_api_get_entity_name_from_camel($entity) . "_$action");
2365 foreach ($params as $index => $param) {
2366 if (is_string($param)) {
2367 $params[$index] = addslashes($param);
2368 }
2369 }
2370 $smarty->assign('params', $params);
2371 $smarty->assign('entity', $entity);
2372 $smarty->assign('testFile', basename($testFile));
2373 $smarty->assign('description', $desc);
2374 $smarty->assign('result', $result);
2375 $smarty->assign('action', $action);
2376
2377 global $civicrm_root;
2378 if (file_exists($civicrm_root . '/tests/templates/documentFunction.tpl')) {
2379 if (!is_dir($civicrm_root . "/api/v3/examples/$entity")) {
2380 mkdir($civicrm_root . "/api/v3/examples/$entity");
2381 }
2382 $f = fopen($civicrm_root . "/api/v3/examples/$entity/$exampleName.php", "w+b");
2383 fwrite($f, $smarty->fetch($civicrm_root . '/tests/templates/documentFunction.tpl'));
2384 fclose($f);
2385 }
2386 }
2387
2388 /**
2389 * Tidy up examples array so that fields that change often ..don't
2390 * and debug related fields are unset
2391 *
2392 * @param array $result
2393 */
2394 public function tidyExampleResult(&$result) {
2395 if (!is_array($result)) {
2396 return;
2397 }
2398 $fieldsToChange = array(
2399 'hash' => '67eac7789eaee00',
2400 'modified_date' => '2012-11-14 16:02:35',
2401 'created_date' => '2013-07-28 08:49:19',
2402 'create_date' => '20120130621222105',
2403 'application_received_date' => '20130728084957',
2404 'in_date' => '2013-07-28 08:50:19',
2405 'scheduled_date' => '20130728085413',
2406 'approval_date' => '20130728085413',
2407 'pledge_start_date_high' => '20130726090416',
2408 'start_date' => '2013-07-29 00:00:00',
2409 'event_start_date' => '2013-07-29 00:00:00',
2410 'end_date' => '2013-08-04 00:00:00',
2411 'event_end_date' => '2013-08-04 00:00:00',
2412 'decision_date' => '20130805000000',
2413 );
2414
2415 $keysToUnset = array('xdebug', 'undefined_fields');
2416 foreach ($keysToUnset as $unwantedKey) {
2417 if (isset($result[$unwantedKey])) {
2418 unset($result[$unwantedKey]);
2419 }
2420 }
2421 if (isset($result['values'])) {
2422 if (!is_array($result['values'])) {
2423 return;
2424 }
2425 $resultArray = &$result['values'];
2426 }
2427 elseif (is_array($result)) {
2428 $resultArray = &$result;
2429 }
2430 else {
2431 return;
2432 }
2433
2434 foreach ($resultArray as $index => &$values) {
2435 if (!is_array($values)) {
2436 continue;
2437 }
2438 foreach ($values as $key => &$value) {
2439 if (substr($key, 0, 3) == 'api' && is_array($value)) {
2440 if (isset($value['is_error'])) {
2441 // we have a std nested result format
2442 $this->tidyExampleResult($value);
2443 }
2444 else {
2445 foreach ($value as &$nestedResult) {
2446 // this is an alternative syntax for nested results a keyed array of results
2447 $this->tidyExampleResult($nestedResult);
2448 }
2449 }
2450 }
2451 if (in_array($key, $keysToUnset)) {
2452 unset($values[$key]);
2453 break;
2454 }
2455 if (array_key_exists($key, $fieldsToChange) && !empty($value)) {
2456 $value = $fieldsToChange[$key];
2457 }
2458 if (is_string($value)) {
2459 $value = addslashes($value);
2460 }
2461 }
2462 }
2463 }
2464
2465 /**
2466 * Create custom field with Option Values.
2467 *
2468 * @param array $customGroup
2469 * @param string $name
2470 * Name of custom field.
2471 * @param array $extraParams
2472 * Additional parameters to pass through.
2473 *
2474 * @return array|int
2475 */
2476 public function customFieldOptionValueCreate($customGroup, $name, $extraParams = array()) {
2477 $fieldParams = array(
2478 'custom_group_id' => $customGroup['id'],
2479 'name' => 'test_custom_group',
2480 'label' => 'Country',
2481 'html_type' => 'Select',
2482 'data_type' => 'String',
2483 'weight' => 4,
2484 'is_required' => 1,
2485 'is_searchable' => 0,
2486 'is_active' => 1,
2487 );
2488
2489 $optionGroup = array(
2490 'domain_id' => 1,
2491 'name' => 'option_group1',
2492 'label' => 'option_group_label1',
2493 );
2494
2495 $optionValue = array(
2496 'option_label' => array('Label1', 'Label2'),
2497 'option_value' => array('value1', 'value2'),
2498 'option_name' => array($name . '_1', $name . '_2'),
2499 'option_weight' => array(1, 2),
2500 'option_status' => 1,
2501 );
2502
2503 $params = array_merge($fieldParams, $optionGroup, $optionValue, $extraParams);
2504
2505 return $this->callAPISuccess('custom_field', 'create', $params);
2506 }
2507
2508 /**
2509 * @param $entities
2510 *
2511 * @return bool
2512 */
2513 public function confirmEntitiesDeleted($entities) {
2514 foreach ($entities as $entity) {
2515
2516 $result = $this->callAPISuccess($entity, 'Get', array());
2517 if ($result['error'] == 1 || $result['count'] > 0) {
2518 // > than $entity[0] to allow a value to be passed in? e.g. domain?
2519 return TRUE;
2520 }
2521 }
2522 return FALSE;
2523 }
2524
2525 /**
2526 * Quick clean by emptying tables created for the test.
2527 *
2528 * @param array $tablesToTruncate
2529 * @param bool $dropCustomValueTables
2530 * @throws \Exception
2531 */
2532 public function quickCleanup($tablesToTruncate, $dropCustomValueTables = FALSE) {
2533 if ($this->tx) {
2534 throw new Exception("CiviUnitTestCase: quickCleanup() is not compatible with useTransaction()");
2535 }
2536 if ($dropCustomValueTables) {
2537 $optionGroupResult = CRM_Core_DAO::executeQuery('SELECT option_group_id FROM civicrm_custom_field');
2538 while ($optionGroupResult->fetch()) {
2539 if (!empty($optionGroupResult->option_group_id)) {
2540 CRM_Core_DAO::executeQuery('DELETE FROM civicrm_option_group WHERE id = ' . $optionGroupResult->option_group_id);
2541 }
2542 }
2543 $tablesToTruncate[] = 'civicrm_custom_group';
2544 $tablesToTruncate[] = 'civicrm_custom_field';
2545 }
2546
2547 $tablesToTruncate = array_unique(array_merge($this->_tablesToTruncate, $tablesToTruncate));
2548
2549 CRM_Core_DAO::executeQuery("SET FOREIGN_KEY_CHECKS = 0;");
2550 foreach ($tablesToTruncate as $table) {
2551 $sql = "TRUNCATE TABLE $table";
2552 CRM_Core_DAO::executeQuery($sql);
2553 }
2554 CRM_Core_DAO::executeQuery("SET FOREIGN_KEY_CHECKS = 1;");
2555
2556 if ($dropCustomValueTables) {
2557 $dbName = self::getDBName();
2558 $query = "
2559 SELECT TABLE_NAME as tableName
2560 FROM INFORMATION_SCHEMA.TABLES
2561 WHERE TABLE_SCHEMA = '{$dbName}'
2562 AND ( TABLE_NAME LIKE 'civicrm_value_%' )
2563 ";
2564
2565 $tableDAO = CRM_Core_DAO::executeQuery($query);
2566 while ($tableDAO->fetch()) {
2567 $sql = "DROP TABLE {$tableDAO->tableName}";
2568 CRM_Core_DAO::executeQuery($sql);
2569 }
2570 }
2571 }
2572
2573 /**
2574 * Clean up financial entities after financial tests (so we remember to get all the tables :-))
2575 */
2576 public function quickCleanUpFinancialEntities() {
2577 $tablesToTruncate = array(
2578 'civicrm_activity',
2579 'civicrm_activity_contact',
2580 'civicrm_contribution',
2581 'civicrm_contribution_soft',
2582 'civicrm_contribution_product',
2583 'civicrm_financial_trxn',
2584 'civicrm_financial_item',
2585 'civicrm_contribution_recur',
2586 'civicrm_line_item',
2587 'civicrm_contribution_page',
2588 'civicrm_payment_processor',
2589 'civicrm_entity_financial_trxn',
2590 'civicrm_membership',
2591 'civicrm_membership_type',
2592 'civicrm_membership_payment',
2593 'civicrm_membership_log',
2594 'civicrm_membership_block',
2595 'civicrm_event',
2596 'civicrm_participant',
2597 'civicrm_participant_payment',
2598 'civicrm_pledge',
2599 'civicrm_pledge_payment',
2600 'civicrm_price_set_entity',
2601 'civicrm_price_field_value',
2602 'civicrm_price_field',
2603 );
2604 $this->quickCleanup($tablesToTruncate);
2605 CRM_Core_DAO::executeQuery("DELETE FROM civicrm_membership_status WHERE name NOT IN('New', 'Current', 'Grace', 'Expired', 'Pending', 'Cancelled', 'Deceased')");
2606 $this->restoreDefaultPriceSetConfig();
2607 $var = TRUE;
2608 CRM_Member_BAO_Membership::createRelatedMemberships($var, $var, TRUE);
2609 $this->disableTaxAndInvoicing();
2610 $this->setCurrencySeparators(',');
2611 CRM_Core_PseudoConstant::flush('taxRates');
2612 System::singleton()->flushProcessors();
2613 }
2614
2615 public function restoreDefaultPriceSetConfig() {
2616 CRM_Core_DAO::executeQuery("DELETE FROM civicrm_price_set WHERE name NOT IN('default_contribution_amount', 'default_membership_type_amount')");
2617 CRM_Core_DAO::executeQuery("UPDATE civicrm_price_set SET id = 1 WHERE name ='default_contribution_amount'");
2618 CRM_Core_DAO::executeQuery("INSERT INTO `civicrm_price_field` (`id`, `price_set_id`, `name`, `label`, `html_type`, `is_enter_qty`, `help_pre`, `help_post`, `weight`, `is_display_amounts`, `options_per_line`, `is_active`, `is_required`, `active_on`, `expire_on`, `javascript`, `visibility_id`) VALUES (1, 1, 'contribution_amount', 'Contribution Amount', 'Text', 0, NULL, NULL, 1, 1, 1, 1, 1, NULL, NULL, NULL, 1)");
2619 CRM_Core_DAO::executeQuery("INSERT INTO `civicrm_price_field_value` (`id`, `price_field_id`, `name`, `label`, `description`, `amount`, `count`, `max_value`, `weight`, `membership_type_id`, `membership_num_terms`, `is_default`, `is_active`, `financial_type_id`, `non_deductible_amount`) VALUES (1, 1, 'contribution_amount', 'Contribution Amount', NULL, '1', NULL, NULL, 1, NULL, NULL, 0, 1, 1, 0.00)");
2620 }
2621 /*
2622 * Function does a 'Get' on the entity & compares the fields in the Params with those returned
2623 * Default behaviour is to also delete the entity
2624 * @param array $params
2625 * Params array to check against.
2626 * @param int $id
2627 * Id of the entity concerned.
2628 * @param string $entity
2629 * Name of entity concerned (e.g. membership).
2630 * @param bool $delete
2631 * Should the entity be deleted as part of this check.
2632 * @param string $errorText
2633 * Text to print on error.
2634 */
2635 /**
2636 * @param array $params
2637 * @param int $id
2638 * @param $entity
2639 * @param int $delete
2640 * @param string $errorText
2641 *
2642 * @throws Exception
2643 */
2644 public function getAndCheck($params, $id, $entity, $delete = 1, $errorText = '') {
2645
2646 $result = $this->callAPISuccessGetSingle($entity, array(
2647 'id' => $id,
2648 ));
2649
2650 if ($delete) {
2651 $this->callAPISuccess($entity, 'Delete', array(
2652 'id' => $id,
2653 ));
2654 }
2655 $dateFields = $keys = $dateTimeFields = array();
2656 $fields = $this->callAPISuccess($entity, 'getfields', array('version' => 3, 'action' => 'get'));
2657 foreach ($fields['values'] as $field => $settings) {
2658 if (array_key_exists($field, $result)) {
2659 $keys[CRM_Utils_Array::Value('name', $settings, $field)] = $field;
2660 }
2661 else {
2662 $keys[CRM_Utils_Array::Value('name', $settings, $field)] = CRM_Utils_Array::value('name', $settings, $field);
2663 }
2664 $type = CRM_Utils_Array::value('type', $settings);
2665 if ($type == CRM_Utils_Type::T_DATE) {
2666 $dateFields[] = $settings['name'];
2667 // we should identify both real names & unique names as dates
2668 if ($field != $settings['name']) {
2669 $dateFields[] = $field;
2670 }
2671 }
2672 if ($type == CRM_Utils_Type::T_DATE + CRM_Utils_Type::T_TIME) {
2673 $dateTimeFields[] = $settings['name'];
2674 // we should identify both real names & unique names as dates
2675 if ($field != $settings['name']) {
2676 $dateTimeFields[] = $field;
2677 }
2678 }
2679 }
2680
2681 if (strtolower($entity) == 'contribution') {
2682 $params['receive_date'] = date('Y-m-d', strtotime($params['receive_date']));
2683 // this is not returned in id format
2684 unset($params['payment_instrument_id']);
2685 $params['contribution_source'] = $params['source'];
2686 unset($params['source']);
2687 }
2688
2689 foreach ($params as $key => $value) {
2690 if ($key == 'version' || substr($key, 0, 3) == 'api' || !array_key_exists($keys[$key], $result)) {
2691 continue;
2692 }
2693 if (in_array($key, $dateFields)) {
2694 $value = date('Y-m-d', strtotime($value));
2695 $result[$key] = date('Y-m-d', strtotime($result[$key]));
2696 }
2697 if (in_array($key, $dateTimeFields)) {
2698 $value = date('Y-m-d H:i:s', strtotime($value));
2699 $result[$keys[$key]] = date('Y-m-d H:i:s', strtotime(CRM_Utils_Array::value($keys[$key], $result, CRM_Utils_Array::value($key, $result))));
2700 }
2701 $this->assertEquals($value, $result[$keys[$key]], $key . " GetandCheck function determines that for key {$key} value: $value doesn't match " . print_r($result[$keys[$key]], TRUE) . $errorText);
2702 }
2703 }
2704
2705 /**
2706 * Get formatted values in the actual and expected result.
2707 * @param array $actual
2708 * Actual calculated values.
2709 * @param array $expected
2710 * Expected values.
2711 */
2712 public function checkArrayEquals(&$actual, &$expected) {
2713 self::unsetId($actual);
2714 self::unsetId($expected);
2715 $this->assertEquals($actual, $expected);
2716 }
2717
2718 /**
2719 * Unset the key 'id' from the array
2720 * @param array $unformattedArray
2721 * The array from which the 'id' has to be unset.
2722 */
2723 public static function unsetId(&$unformattedArray) {
2724 $formattedArray = array();
2725 if (array_key_exists('id', $unformattedArray)) {
2726 unset($unformattedArray['id']);
2727 }
2728 if (!empty($unformattedArray['values']) && is_array($unformattedArray['values'])) {
2729 foreach ($unformattedArray['values'] as $key => $value) {
2730 if (is_array($value)) {
2731 foreach ($value as $k => $v) {
2732 if ($k == 'id') {
2733 unset($value[$k]);
2734 }
2735 }
2736 }
2737 elseif ($key == 'id') {
2738 $unformattedArray[$key];
2739 }
2740 $formattedArray = array($value);
2741 }
2742 $unformattedArray['values'] = $formattedArray;
2743 }
2744 }
2745
2746 /**
2747 * Helper to enable/disable custom directory support
2748 *
2749 * @param array $customDirs
2750 * With members:.
2751 * 'php_path' Set to TRUE to use the default, FALSE or "" to disable support, or a string path to use another path
2752 * 'template_path' Set to TRUE to use the default, FALSE or "" to disable support, or a string path to use another path
2753 */
2754 public function customDirectories($customDirs) {
2755 $config = CRM_Core_Config::singleton();
2756
2757 if (empty($customDirs['php_path']) || $customDirs['php_path'] === FALSE) {
2758 unset($config->customPHPPathDir);
2759 }
2760 elseif ($customDirs['php_path'] === TRUE) {
2761 $config->customPHPPathDir = dirname(dirname(__FILE__)) . '/custom_directories/php/';
2762 }
2763 else {
2764 $config->customPHPPathDir = $php_path;
2765 }
2766
2767 if (empty($customDirs['template_path']) || $customDirs['template_path'] === FALSE) {
2768 unset($config->customTemplateDir);
2769 }
2770 elseif ($customDirs['template_path'] === TRUE) {
2771 $config->customTemplateDir = dirname(dirname(__FILE__)) . '/custom_directories/templates/';
2772 }
2773 else {
2774 $config->customTemplateDir = $template_path;
2775 }
2776 }
2777
2778 /**
2779 * Generate a temporary folder.
2780 *
2781 * @param string $prefix
2782 * @return string
2783 */
2784 public function createTempDir($prefix = 'test-') {
2785 $tempDir = CRM_Utils_File::tempdir($prefix);
2786 $this->tempDirs[] = $tempDir;
2787 return $tempDir;
2788 }
2789
2790 public function cleanTempDirs() {
2791 if (!is_array($this->tempDirs)) {
2792 // fix test errors where this is not set
2793 return;
2794 }
2795 foreach ($this->tempDirs as $tempDir) {
2796 if (is_dir($tempDir)) {
2797 CRM_Utils_File::cleanDir($tempDir, TRUE, FALSE);
2798 }
2799 }
2800 }
2801
2802 /**
2803 * Temporarily replace the singleton extension with a different one.
2804 * @param \CRM_Extension_System $system
2805 */
2806 public function setExtensionSystem(CRM_Extension_System $system) {
2807 if ($this->origExtensionSystem == NULL) {
2808 $this->origExtensionSystem = CRM_Extension_System::singleton();
2809 }
2810 CRM_Extension_System::setSingleton($this->origExtensionSystem);
2811 }
2812
2813 public function unsetExtensionSystem() {
2814 if ($this->origExtensionSystem !== NULL) {
2815 CRM_Extension_System::setSingleton($this->origExtensionSystem);
2816 $this->origExtensionSystem = NULL;
2817 }
2818 }
2819
2820 /**
2821 * Temporarily alter the settings-metadata to add a mock setting.
2822 *
2823 * WARNING: The setting metadata will disappear on the next cache-clear.
2824 *
2825 * @param $extras
2826 * @return void
2827 */
2828 public function setMockSettingsMetaData($extras) {
2829 Civi::service('settings_manager')->flush();
2830
2831 CRM_Utils_Hook::singleton()
2832 ->setHook('civicrm_alterSettingsMetaData', function (&$metadata, $domainId, $profile) use ($extras) {
2833 $metadata = array_merge($metadata, $extras);
2834 });
2835
2836 $fields = $this->callAPISuccess('setting', 'getfields', array());
2837 foreach ($extras as $key => $spec) {
2838 $this->assertNotEmpty($spec['title']);
2839 $this->assertEquals($spec['title'], $fields['values'][$key]['title']);
2840 }
2841 }
2842
2843 /**
2844 * @param string $name
2845 */
2846 public function financialAccountDelete($name) {
2847 $financialAccount = new CRM_Financial_DAO_FinancialAccount();
2848 $financialAccount->name = $name;
2849 if ($financialAccount->find(TRUE)) {
2850 $entityFinancialType = new CRM_Financial_DAO_EntityFinancialAccount();
2851 $entityFinancialType->financial_account_id = $financialAccount->id;
2852 $entityFinancialType->delete();
2853 $financialAccount->delete();
2854 }
2855 }
2856
2857 /**
2858 * FIXME: something NULLs $GLOBALS['_HTML_QuickForm_registered_rules'] when the tests are ran all together
2859 * (NB unclear if this is still required)
2860 */
2861 public function _sethtmlGlobals() {
2862 $GLOBALS['_HTML_QuickForm_registered_rules'] = array(
2863 'required' => array(
2864 'html_quickform_rule_required',
2865 'HTML/QuickForm/Rule/Required.php',
2866 ),
2867 'maxlength' => array(
2868 'html_quickform_rule_range',
2869 'HTML/QuickForm/Rule/Range.php',
2870 ),
2871 'minlength' => array(
2872 'html_quickform_rule_range',
2873 'HTML/QuickForm/Rule/Range.php',
2874 ),
2875 'rangelength' => array(
2876 'html_quickform_rule_range',
2877 'HTML/QuickForm/Rule/Range.php',
2878 ),
2879 'email' => array(
2880 'html_quickform_rule_email',
2881 'HTML/QuickForm/Rule/Email.php',
2882 ),
2883 'regex' => array(
2884 'html_quickform_rule_regex',
2885 'HTML/QuickForm/Rule/Regex.php',
2886 ),
2887 'lettersonly' => array(
2888 'html_quickform_rule_regex',
2889 'HTML/QuickForm/Rule/Regex.php',
2890 ),
2891 'alphanumeric' => array(
2892 'html_quickform_rule_regex',
2893 'HTML/QuickForm/Rule/Regex.php',
2894 ),
2895 'numeric' => array(
2896 'html_quickform_rule_regex',
2897 'HTML/QuickForm/Rule/Regex.php',
2898 ),
2899 'nopunctuation' => array(
2900 'html_quickform_rule_regex',
2901 'HTML/QuickForm/Rule/Regex.php',
2902 ),
2903 'nonzero' => array(
2904 'html_quickform_rule_regex',
2905 'HTML/QuickForm/Rule/Regex.php',
2906 ),
2907 'callback' => array(
2908 'html_quickform_rule_callback',
2909 'HTML/QuickForm/Rule/Callback.php',
2910 ),
2911 'compare' => array(
2912 'html_quickform_rule_compare',
2913 'HTML/QuickForm/Rule/Compare.php',
2914 ),
2915 );
2916 // FIXME: …ditto for $GLOBALS['HTML_QUICKFORM_ELEMENT_TYPES']
2917 $GLOBALS['HTML_QUICKFORM_ELEMENT_TYPES'] = array(
2918 'group' => array(
2919 'HTML/QuickForm/group.php',
2920 'HTML_QuickForm_group',
2921 ),
2922 'hidden' => array(
2923 'HTML/QuickForm/hidden.php',
2924 'HTML_QuickForm_hidden',
2925 ),
2926 'reset' => array(
2927 'HTML/QuickForm/reset.php',
2928 'HTML_QuickForm_reset',
2929 ),
2930 'checkbox' => array(
2931 'HTML/QuickForm/checkbox.php',
2932 'HTML_QuickForm_checkbox',
2933 ),
2934 'file' => array(
2935 'HTML/QuickForm/file.php',
2936 'HTML_QuickForm_file',
2937 ),
2938 'image' => array(
2939 'HTML/QuickForm/image.php',
2940 'HTML_QuickForm_image',
2941 ),
2942 'password' => array(
2943 'HTML/QuickForm/password.php',
2944 'HTML_QuickForm_password',
2945 ),
2946 'radio' => array(
2947 'HTML/QuickForm/radio.php',
2948 'HTML_QuickForm_radio',
2949 ),
2950 'button' => array(
2951 'HTML/QuickForm/button.php',
2952 'HTML_QuickForm_button',
2953 ),
2954 'submit' => array(
2955 'HTML/QuickForm/submit.php',
2956 'HTML_QuickForm_submit',
2957 ),
2958 'select' => array(
2959 'HTML/QuickForm/select.php',
2960 'HTML_QuickForm_select',
2961 ),
2962 'hiddenselect' => array(
2963 'HTML/QuickForm/hiddenselect.php',
2964 'HTML_QuickForm_hiddenselect',
2965 ),
2966 'text' => array(
2967 'HTML/QuickForm/text.php',
2968 'HTML_QuickForm_text',
2969 ),
2970 'textarea' => array(
2971 'HTML/QuickForm/textarea.php',
2972 'HTML_QuickForm_textarea',
2973 ),
2974 'fckeditor' => array(
2975 'HTML/QuickForm/fckeditor.php',
2976 'HTML_QuickForm_FCKEditor',
2977 ),
2978 'tinymce' => array(
2979 'HTML/QuickForm/tinymce.php',
2980 'HTML_QuickForm_TinyMCE',
2981 ),
2982 'dojoeditor' => array(
2983 'HTML/QuickForm/dojoeditor.php',
2984 'HTML_QuickForm_dojoeditor',
2985 ),
2986 'link' => array(
2987 'HTML/QuickForm/link.php',
2988 'HTML_QuickForm_link',
2989 ),
2990 'advcheckbox' => array(
2991 'HTML/QuickForm/advcheckbox.php',
2992 'HTML_QuickForm_advcheckbox',
2993 ),
2994 'date' => array(
2995 'HTML/QuickForm/date.php',
2996 'HTML_QuickForm_date',
2997 ),
2998 'static' => array(
2999 'HTML/QuickForm/static.php',
3000 'HTML_QuickForm_static',
3001 ),
3002 'header' => array(
3003 'HTML/QuickForm/header.php',
3004 'HTML_QuickForm_header',
3005 ),
3006 'html' => array(
3007 'HTML/QuickForm/html.php',
3008 'HTML_QuickForm_html',
3009 ),
3010 'hierselect' => array(
3011 'HTML/QuickForm/hierselect.php',
3012 'HTML_QuickForm_hierselect',
3013 ),
3014 'autocomplete' => array(
3015 'HTML/QuickForm/autocomplete.php',
3016 'HTML_QuickForm_autocomplete',
3017 ),
3018 'xbutton' => array(
3019 'HTML/QuickForm/xbutton.php',
3020 'HTML_QuickForm_xbutton',
3021 ),
3022 'advmultiselect' => array(
3023 'HTML/QuickForm/advmultiselect.php',
3024 'HTML_QuickForm_advmultiselect',
3025 ),
3026 );
3027 }
3028
3029 /**
3030 * Set up an acl allowing contact to see 2 specified groups
3031 * - $this->_permissionedGroup & $this->_permissionedDisabledGroup
3032 *
3033 * You need to have pre-created these groups & created the user e.g
3034 * $this->createLoggedInUser();
3035 * $this->_permissionedDisabledGroup = $this->groupCreate(array('title' => 'pick-me-disabled', 'is_active' => 0, 'name' => 'pick-me-disabled'));
3036 * $this->_permissionedGroup = $this->groupCreate(array('title' => 'pick-me-active', 'is_active' => 1, 'name' => 'pick-me-active'));
3037 *
3038 * @param bool $isProfile
3039 */
3040 public function setupACL($isProfile = FALSE) {
3041 global $_REQUEST;
3042 $_REQUEST = $this->_params;
3043
3044 CRM_Core_Config::singleton()->userPermissionClass->permissions = array('access CiviCRM');
3045 $optionGroupID = $this->callAPISuccessGetValue('option_group', array('return' => 'id', 'name' => 'acl_role'));
3046 $ov = new CRM_Core_DAO_OptionValue();
3047 $ov->option_group_id = $optionGroupID;
3048 $ov->value = 55;
3049 if ($ov->find(TRUE)) {
3050 CRM_Core_DAO::executeQuery("DELETE FROM civicrm_option_value WHERE id = {$ov->id}");
3051 }
3052 $optionValue = $this->callAPISuccess('option_value', 'create', array(
3053 'option_group_id' => $optionGroupID,
3054 'label' => 'pick me',
3055 'value' => 55,
3056 ));
3057
3058 CRM_Core_DAO::executeQuery("
3059 TRUNCATE civicrm_acl_cache
3060 ");
3061
3062 CRM_Core_DAO::executeQuery("
3063 TRUNCATE civicrm_acl_contact_cache
3064 ");
3065
3066 CRM_Core_DAO::executeQuery("
3067 INSERT INTO civicrm_acl_entity_role (
3068 `acl_role_id`, `entity_table`, `entity_id`, `is_active`
3069 ) VALUES (55, 'civicrm_group', {$this->_permissionedGroup}, 1);
3070 ");
3071
3072 if ($isProfile) {
3073 CRM_Core_DAO::executeQuery("
3074 INSERT INTO civicrm_acl (
3075 `name`, `entity_table`, `entity_id`, `operation`, `object_table`, `object_id`, `is_active`
3076 )
3077 VALUES (
3078 'view picked', 'civicrm_acl_role', 55, 'Edit', 'civicrm_uf_group', 0, 1
3079 );
3080 ");
3081 }
3082 else {
3083 CRM_Core_DAO::executeQuery("
3084 INSERT INTO civicrm_acl (
3085 `name`, `entity_table`, `entity_id`, `operation`, `object_table`, `object_id`, `is_active`
3086 )
3087 VALUES (
3088 'view picked', 'civicrm_group', $this->_permissionedGroup , 'Edit', 'civicrm_saved_search', {$this->_permissionedGroup}, 1
3089 );
3090 ");
3091
3092 CRM_Core_DAO::executeQuery("
3093 INSERT INTO civicrm_acl (
3094 `name`, `entity_table`, `entity_id`, `operation`, `object_table`, `object_id`, `is_active`
3095 )
3096 VALUES (
3097 'view picked', 'civicrm_group', $this->_permissionedGroup, 'Edit', 'civicrm_saved_search', {$this->_permissionedDisabledGroup}, 1
3098 );
3099 ");
3100 }
3101
3102 $this->_loggedInUser = CRM_Core_Session::singleton()->get('userID');
3103 $this->callAPISuccess('group_contact', 'create', array(
3104 'group_id' => $this->_permissionedGroup,
3105 'contact_id' => $this->_loggedInUser,
3106 ));
3107
3108 if (!$isProfile) {
3109 //flush cache
3110 CRM_ACL_BAO_Cache::resetCache();
3111 CRM_ACL_API::groupPermission('whatever', 9999, NULL, 'civicrm_saved_search', NULL, NULL, TRUE);
3112 }
3113 }
3114
3115 /**
3116 * Alter default price set so that the field numbers are not all 1 (hiding errors)
3117 */
3118 public function offsetDefaultPriceSet() {
3119 $contributionPriceSet = $this->callAPISuccess('price_set', 'getsingle', array('name' => 'default_contribution_amount'));
3120 $firstID = $contributionPriceSet['id'];
3121 $this->callAPISuccess('price_set', 'create', array(
3122 'id' => $contributionPriceSet['id'],
3123 'is_active' => 0,
3124 'name' => 'old',
3125 ));
3126 unset($contributionPriceSet['id']);
3127 $newPriceSet = $this->callAPISuccess('price_set', 'create', $contributionPriceSet);
3128 $priceField = $this->callAPISuccess('price_field', 'getsingle', array(
3129 'price_set_id' => $firstID,
3130 'options' => array('limit' => 1),
3131 ));
3132 unset($priceField['id']);
3133 $priceField['price_set_id'] = $newPriceSet['id'];
3134 $newPriceField = $this->callAPISuccess('price_field', 'create', $priceField);
3135 $priceFieldValue = $this->callAPISuccess('price_field_value', 'getsingle', array(
3136 'price_set_id' => $firstID,
3137 'sequential' => 1,
3138 'options' => array('limit' => 1),
3139 ));
3140
3141 unset($priceFieldValue['id']);
3142 //create some padding to use up ids
3143 $this->callAPISuccess('price_field_value', 'create', $priceFieldValue);
3144 $this->callAPISuccess('price_field_value', 'create', $priceFieldValue);
3145 $this->callAPISuccess('price_field_value', 'create', array_merge($priceFieldValue, array('price_field_id' => $newPriceField['id'])));
3146 }
3147
3148 /**
3149 * Create an instance of the paypal processor.
3150 * @todo this isn't a great place to put it - but really it belongs on a class that extends
3151 * this parent class & we don't have a structure for that yet
3152 * There is another function to this effect on the PaypalPro test but it appears to be silently failing
3153 * & the best protection against that is the functions this class affords
3154 * @param array $params
3155 * @return int $result['id'] payment processor id
3156 */
3157 public function paymentProcessorCreate($params = array()) {
3158 $params = array_merge(array(
3159 'name' => 'demo',
3160 'domain_id' => CRM_Core_Config::domainID(),
3161 'payment_processor_type_id' => 'PayPal',
3162 'is_active' => 1,
3163 'is_default' => 0,
3164 'is_test' => 1,
3165 'user_name' => 'sunil._1183377782_biz_api1.webaccess.co.in',
3166 'password' => '1183377788',
3167 'signature' => 'APixCoQ-Zsaj-u3IH7mD5Do-7HUqA9loGnLSzsZga9Zr-aNmaJa3WGPH',
3168 'url_site' => 'https://www.sandbox.paypal.com/',
3169 'url_api' => 'https://api-3t.sandbox.paypal.com/',
3170 'url_button' => 'https://www.paypal.com/en_US/i/btn/btn_xpressCheckout.gif',
3171 'class_name' => 'Payment_PayPalImpl',
3172 'billing_mode' => 3,
3173 'financial_type_id' => 1,
3174 'financial_account_id' => 12,
3175 // Credit card = 1 so can pass 'by accident'.
3176 'payment_instrument_id' => 'Debit Card',
3177 ),
3178 $params);
3179 if (!is_numeric($params['payment_processor_type_id'])) {
3180 // really the api should handle this through getoptions but it's not exactly api call so lets just sort it
3181 //here
3182 $params['payment_processor_type_id'] = $this->callAPISuccess('payment_processor_type', 'getvalue', array(
3183 'name' => $params['payment_processor_type_id'],
3184 'return' => 'id',
3185 ), 'integer');
3186 }
3187 $result = $this->callAPISuccess('payment_processor', 'create', $params);
3188 return $result['id'];
3189 }
3190
3191 /**
3192 * Set up initial recurring payment allowing subsequent IPN payments.
3193 */
3194 public function setupRecurringPaymentProcessorTransaction($params = array()) {
3195 $contributionRecur = $this->callAPISuccess('contribution_recur', 'create', array_merge(array(
3196 'contact_id' => $this->_contactID,
3197 'amount' => 1000,
3198 'sequential' => 1,
3199 'installments' => 5,
3200 'frequency_unit' => 'Month',
3201 'frequency_interval' => 1,
3202 'invoice_id' => $this->_invoiceID,
3203 'contribution_status_id' => 2,
3204 'payment_processor_id' => $this->_paymentProcessorID,
3205 // processor provided ID - use contact ID as proxy.
3206 'processor_id' => $this->_contactID,
3207 'api.contribution.create' => array(
3208 'total_amount' => '200',
3209 'invoice_id' => $this->_invoiceID,
3210 'financial_type_id' => 1,
3211 'contribution_status_id' => 'Pending',
3212 'contact_id' => $this->_contactID,
3213 'contribution_page_id' => $this->_contributionPageID,
3214 'payment_processor_id' => $this->_paymentProcessorID,
3215 'is_test' => 0,
3216 ),
3217 ), $params));
3218 $this->_contributionRecurID = $contributionRecur['id'];
3219 $this->_contributionID = $contributionRecur['values']['0']['api.contribution.create']['id'];
3220 }
3221
3222 /**
3223 * We don't have a good way to set up a recurring contribution with a membership so let's just do one then alter it
3224 *
3225 * @param array $params Optionally modify params for membership/recur (duration_unit/frequency_unit)
3226 */
3227 public function setupMembershipRecurringPaymentProcessorTransaction($params = array()) {
3228 $membershipParams = $recurParams = array();
3229 if (!empty($params['duration_unit'])) {
3230 $membershipParams['duration_unit'] = $params['duration_unit'];
3231 }
3232 if (!empty($params['frequency_unit'])) {
3233 $recurParams['frequency_unit'] = $params['frequency_unit'];
3234 }
3235
3236 $this->ids['membership_type'] = $this->membershipTypeCreate($membershipParams);
3237 //create a contribution so our membership & contribution don't both have id = 1
3238 if ($this->callAPISuccess('Contribution', 'getcount', array()) == 0) {
3239 $this->contributionCreate(array(
3240 'contact_id' => $this->_contactID,
3241 'is_test' => 1,
3242 'financial_type_id' => 1,
3243 'invoice_id' => 'abcd',
3244 'trxn_id' => 345,
3245 ));
3246 }
3247 $this->setupRecurringPaymentProcessorTransaction($recurParams);
3248
3249 $this->ids['membership'] = $this->callAPISuccess('membership', 'create', array(
3250 'contact_id' => $this->_contactID,
3251 'membership_type_id' => $this->ids['membership_type'],
3252 'contribution_recur_id' => $this->_contributionRecurID,
3253 'format.only_id' => TRUE,
3254 ));
3255 //CRM-15055 creates line items we don't want so get rid of them so we can set up our own line items
3256 CRM_Core_DAO::executeQuery("TRUNCATE civicrm_line_item");
3257
3258 $this->callAPISuccess('line_item', 'create', array(
3259 'entity_table' => 'civicrm_membership',
3260 'entity_id' => $this->ids['membership'],
3261 'contribution_id' => $this->_contributionID,
3262 'label' => 'General',
3263 'qty' => 1,
3264 'unit_price' => 200,
3265 'line_total' => 200,
3266 'financial_type_id' => 1,
3267 'price_field_id' => $this->callAPISuccess('price_field', 'getvalue', array(
3268 'return' => 'id',
3269 'label' => 'Membership Amount',
3270 'options' => array('limit' => 1, 'sort' => 'id DESC'),
3271 )),
3272 'price_field_value_id' => $this->callAPISuccess('price_field_value', 'getvalue', array(
3273 'return' => 'id',
3274 'label' => 'General',
3275 'options' => array('limit' => 1, 'sort' => 'id DESC'),
3276 )),
3277 ));
3278 $this->callAPISuccess('membership_payment', 'create', array(
3279 'contribution_id' => $this->_contributionID,
3280 'membership_id' => $this->ids['membership'],
3281 ));
3282 }
3283
3284 /**
3285 * @param $message
3286 *
3287 * @throws Exception
3288 */
3289 public function CiviUnitTestCase_fatalErrorHandler($message) {
3290 throw new Exception("{$message['message']}: {$message['code']}");
3291 }
3292
3293 /**
3294 * Helper function to create new mailing.
3295 *
3296 * @param array $params
3297 *
3298 * @return int
3299 */
3300 public function createMailing($params = array()) {
3301 $params = array_merge(array(
3302 'subject' => 'maild' . rand(),
3303 'body_text' => 'bdkfhdskfhduew{domain.address}{action.optOutUrl}',
3304 'name' => 'mailing name' . rand(),
3305 'created_id' => 1,
3306 ), $params);
3307
3308 $result = $this->callAPISuccess('Mailing', 'create', $params);
3309 return $result['id'];
3310 }
3311
3312 /**
3313 * Helper function to delete mailing.
3314 * @param $id
3315 */
3316 public function deleteMailing($id) {
3317 $params = array(
3318 'id' => $id,
3319 );
3320
3321 $this->callAPISuccess('Mailing', 'delete', $params);
3322 }
3323
3324 /**
3325 * Wrap the entire test case in a transaction.
3326 *
3327 * Only subsequent DB statements will be wrapped in TX -- this cannot
3328 * retroactively wrap old DB statements. Therefore, it makes sense to
3329 * call this at the beginning of setUp().
3330 *
3331 * Note: Recall that TRUNCATE and ALTER will force-commit transactions, so
3332 * this option does not work with, e.g., custom-data.
3333 *
3334 * WISHLIST: Monitor SQL queries in unit-tests and generate an exception
3335 * if TRUNCATE or ALTER is called while using a transaction.
3336 *
3337 * @param bool $nest
3338 * Whether to use nesting or reference-counting.
3339 */
3340 public function useTransaction($nest = TRUE) {
3341 if (!$this->tx) {
3342 $this->tx = new CRM_Core_Transaction($nest);
3343 $this->tx->rollback();
3344 }
3345 }
3346
3347 /**
3348 * Assert the attachment exists.
3349 *
3350 * @param bool $exists
3351 * @param array $apiResult
3352 */
3353 protected function assertAttachmentExistence($exists, $apiResult) {
3354 $fileId = $apiResult['id'];
3355 $this->assertTrue(is_numeric($fileId));
3356 $this->assertEquals($exists, file_exists($apiResult['values'][$fileId]['path']));
3357 $this->assertDBQuery($exists ? 1 : 0, 'SELECT count(*) FROM civicrm_file WHERE id = %1', array(
3358 1 => array($fileId, 'Int'),
3359 ));
3360 $this->assertDBQuery($exists ? 1 : 0, 'SELECT count(*) FROM civicrm_entity_file WHERE id = %1', array(
3361 1 => array($fileId, 'Int'),
3362 ));
3363 }
3364
3365 /**
3366 * Create a price set for an event.
3367 *
3368 * @param int $feeTotal
3369 * @param int $minAmt
3370 * @param string $type
3371 *
3372 * @return int
3373 * Price Set ID.
3374 */
3375 protected function eventPriceSetCreate($feeTotal, $minAmt = 0, $type = 'Text') {
3376 // creating price set, price field
3377 $paramsSet['title'] = 'Price Set';
3378 $paramsSet['name'] = CRM_Utils_String::titleToVar('Price Set');
3379 $paramsSet['is_active'] = FALSE;
3380 $paramsSet['extends'] = 1;
3381 $paramsSet['min_amount'] = $minAmt;
3382
3383 $priceSet = CRM_Price_BAO_PriceSet::create($paramsSet);
3384 $this->_ids['price_set'] = $priceSet->id;
3385
3386 $paramsField = array(
3387 'label' => 'Price Field',
3388 'name' => CRM_Utils_String::titleToVar('Price Field'),
3389 'html_type' => $type,
3390 'price' => $feeTotal,
3391 'option_label' => array('1' => 'Price Field'),
3392 'option_value' => array('1' => $feeTotal),
3393 'option_name' => array('1' => $feeTotal),
3394 'option_weight' => array('1' => 1),
3395 'option_amount' => array('1' => 1),
3396 'is_display_amounts' => 1,
3397 'weight' => 1,
3398 'options_per_line' => 1,
3399 'is_active' => array('1' => 1),
3400 'price_set_id' => $this->_ids['price_set'],
3401 'is_enter_qty' => 1,
3402 'financial_type_id' => $this->getFinancialTypeId('Event Fee'),
3403 );
3404 if ($type === 'Radio') {
3405 $paramsField['is_enter_qty'] = 0;
3406 $paramsField['option_value'][2] = $paramsField['option_weight'][2] = $paramsField['option_amount'][2] = 100;
3407 $paramsField['option_label'][2] = $paramsField['option_name'][2] = 'hundy';
3408 }
3409 CRM_Price_BAO_PriceField::create($paramsField);
3410 $fields = $this->callAPISuccess('PriceField', 'get', array('price_set_id' => $this->_ids['price_set']));
3411 $this->_ids['price_field'] = array_keys($fields['values']);
3412 $fieldValues = $this->callAPISuccess('PriceFieldValue', 'get', array('price_field_id' => $this->_ids['price_field'][0]));
3413 $this->_ids['price_field_value'] = array_keys($fieldValues['values']);
3414
3415 return $this->_ids['price_set'];
3416 }
3417
3418 /**
3419 * Add a profile to a contribution page.
3420 *
3421 * @param string $name
3422 * @param int $contributionPageID
3423 */
3424 protected function addProfile($name, $contributionPageID) {
3425 $this->callAPISuccess('UFJoin', 'create', array(
3426 'uf_group_id' => $name,
3427 'module' => 'CiviContribute',
3428 'entity_table' => 'civicrm_contribution_page',
3429 'entity_id' => $contributionPageID,
3430 'weight' => 1,
3431 ));
3432 }
3433
3434 /**
3435 * Add participant with contribution
3436 *
3437 * @return array
3438 */
3439 protected function createParticipantWithContribution() {
3440 // creating price set, price field
3441 $this->_contactId = $this->individualCreate();
3442 $event = $this->eventCreate();
3443 $this->_eventId = $event['id'];
3444 $eventParams = array(
3445 'id' => $this->_eventId,
3446 'financial_type_id' => 4,
3447 'is_monetary' => 1,
3448 );
3449 $this->callAPISuccess('event', 'create', $eventParams);
3450 $priceFields = $this->createPriceSet('event', $this->_eventId);
3451 $participantParams = array(
3452 'financial_type_id' => 4,
3453 'event_id' => $this->_eventId,
3454 'role_id' => 1,
3455 'status_id' => 14,
3456 'fee_currency' => 'USD',
3457 'contact_id' => $this->_contactId,
3458 );
3459 $participant = $this->callAPISuccess('Participant', 'create', $participantParams);
3460 $contributionParams = array(
3461 'total_amount' => 150,
3462 'currency' => 'USD',
3463 'contact_id' => $this->_contactId,
3464 'financial_type_id' => 4,
3465 'contribution_status_id' => 1,
3466 'partial_payment_total' => 300.00,
3467 'partial_amount_to_pay' => 150,
3468 'contribution_mode' => 'participant',
3469 'participant_id' => $participant['id'],
3470 );
3471 foreach ($priceFields['values'] as $key => $priceField) {
3472 $lineItems[1][$key] = array(
3473 'price_field_id' => $priceField['price_field_id'],
3474 'price_field_value_id' => $priceField['id'],
3475 'label' => $priceField['label'],
3476 'field_title' => $priceField['label'],
3477 'qty' => 1,
3478 'unit_price' => $priceField['amount'],
3479 'line_total' => $priceField['amount'],
3480 'financial_type_id' => $priceField['financial_type_id'],
3481 );
3482 }
3483 $contributionParams['line_item'] = $lineItems;
3484 $contribution = $this->callAPISuccess('Contribution', 'create', $contributionParams);
3485 $paymentParticipant = array(
3486 'participant_id' => $participant['id'],
3487 'contribution_id' => $contribution['id'],
3488 );
3489 $this->callAPISuccess('ParticipantPayment', 'create', $paymentParticipant);
3490 return array($lineItems, $contribution);
3491 }
3492
3493 /**
3494 * Create price set
3495 *
3496 * @param string $component
3497 * @param int $componentId
3498 *
3499 * @return array
3500 */
3501 protected function createPriceSet($component = 'contribution_page', $componentId = NULL, $priceFieldOptions = array()) {
3502 $paramsSet['title'] = 'Price Set' . substr(sha1(rand()), 0, 7);
3503 $paramsSet['name'] = CRM_Utils_String::titleToVar($paramsSet['title']);
3504 $paramsSet['is_active'] = TRUE;
3505 $paramsSet['financial_type_id'] = 4;
3506 $paramsSet['extends'] = 1;
3507 $priceSet = $this->callAPISuccess('price_set', 'create', $paramsSet);
3508 $priceSetId = $priceSet['id'];
3509 //Checking for priceset added in the table.
3510 $this->assertDBCompareValue('CRM_Price_BAO_PriceSet', $priceSetId, 'title',
3511 'id', $paramsSet['title'], 'Check DB for created priceset'
3512 );
3513 $paramsField = array_merge(array(
3514 'label' => 'Price Field',
3515 'name' => CRM_Utils_String::titleToVar('Price Field'),
3516 'html_type' => 'CheckBox',
3517 'option_label' => array('1' => 'Price Field 1', '2' => 'Price Field 2'),
3518 'option_value' => array('1' => 100, '2' => 200),
3519 'option_name' => array('1' => 'Price Field 1', '2' => 'Price Field 2'),
3520 'option_weight' => array('1' => 1, '2' => 2),
3521 'option_amount' => array('1' => 100, '2' => 200),
3522 'is_display_amounts' => 1,
3523 'weight' => 1,
3524 'options_per_line' => 1,
3525 'is_active' => array('1' => 1, '2' => 1),
3526 'price_set_id' => $priceSet['id'],
3527 'is_enter_qty' => 1,
3528 'financial_type_id' => $this->getFinancialTypeId('Event Fee'),
3529 ), $priceFieldOptions);
3530
3531 $priceField = CRM_Price_BAO_PriceField::create($paramsField);
3532 if ($componentId) {
3533 CRM_Price_BAO_PriceSet::addTo('civicrm_' . $component, $componentId, $priceSetId);
3534 }
3535 return $this->callAPISuccess('PriceFieldValue', 'get', array('price_field_id' => $priceField->id));
3536 }
3537
3538 /**
3539 * Replace the template with a test-oriented template designed to show all the variables.
3540 *
3541 * @param string $templateName
3542 */
3543 protected function swapMessageTemplateForTestTemplate($templateName = 'contribution_online_receipt') {
3544 $testTemplate = file_get_contents(__DIR__ . '/../../templates/message_templates/' . $templateName . '_html.tpl');
3545 CRM_Core_DAO::executeQuery(
3546 "UPDATE civicrm_option_group og
3547 LEFT JOIN civicrm_option_value ov ON ov.option_group_id = og.id
3548 LEFT JOIN civicrm_msg_template m ON m.workflow_id = ov.id
3549 SET m.msg_html = '{$testTemplate}'
3550 WHERE og.name = 'msg_tpl_workflow_contribution'
3551 AND ov.name = '{$templateName}'
3552 AND m.is_default = 1"
3553 );
3554 }
3555
3556 /**
3557 * Reinstate the default template.
3558 *
3559 * @param string $templateName
3560 */
3561 protected function revertTemplateToReservedTemplate($templateName = 'contribution_online_receipt') {
3562 CRM_Core_DAO::executeQuery(
3563 "UPDATE civicrm_option_group og
3564 LEFT JOIN civicrm_option_value ov ON ov.option_group_id = og.id
3565 LEFT JOIN civicrm_msg_template m ON m.workflow_id = ov.id
3566 LEFT JOIN civicrm_msg_template m2 ON m2.workflow_id = ov.id AND m2.is_reserved = 1
3567 SET m.msg_html = m2.msg_html
3568 WHERE og.name = 'msg_tpl_workflow_contribution'
3569 AND ov.name = '{$templateName}'
3570 AND m.is_default = 1"
3571 );
3572 }
3573
3574 /**
3575 * Flush statics relating to financial type.
3576 */
3577 protected function flushFinancialTypeStatics() {
3578 if (isset(\Civi::$statics['CRM_Financial_BAO_FinancialType'])) {
3579 unset(\Civi::$statics['CRM_Financial_BAO_FinancialType']);
3580 }
3581 if (isset(\Civi::$statics['CRM_Contribute_PseudoConstant'])) {
3582 unset(\Civi::$statics['CRM_Contribute_PseudoConstant']);
3583 }
3584 CRM_Contribute_PseudoConstant::flush('financialType');
3585 CRM_Contribute_PseudoConstant::flush('membershipType');
3586 // Pseudoconstants may be saved to the cache table.
3587 CRM_Core_DAO::executeQuery("TRUNCATE civicrm_cache");
3588 CRM_Financial_BAO_FinancialType::$_statusACLFt = array();
3589 CRM_Financial_BAO_FinancialType::$_availableFinancialTypes = NULL;
3590 }
3591
3592 /**
3593 * Set the permissions to the supplied array.
3594 *
3595 * @param array $permissions
3596 */
3597 protected function setPermissions($permissions) {
3598 CRM_Core_Config::singleton()->userPermissionClass->permissions = $permissions;
3599 $this->flushFinancialTypeStatics();
3600 }
3601
3602 /**
3603 * @param array $params
3604 * @param $context
3605 */
3606 public function _checkFinancialRecords($params, $context) {
3607 $entityParams = array(
3608 'entity_id' => $params['id'],
3609 'entity_table' => 'civicrm_contribution',
3610 );
3611 $contribution = $this->callAPISuccess('contribution', 'getsingle', array('id' => $params['id']));
3612 $this->assertEquals($contribution['total_amount'] - $contribution['fee_amount'], $contribution['net_amount']);
3613 if ($context == 'pending') {
3614 $trxn = CRM_Financial_BAO_FinancialItem::retrieveEntityFinancialTrxn($entityParams);
3615 $this->assertNull($trxn, 'No Trxn to be created until IPN callback');
3616 return;
3617 }
3618 $trxn = current(CRM_Financial_BAO_FinancialItem::retrieveEntityFinancialTrxn($entityParams));
3619 $trxnParams = array(
3620 'id' => $trxn['financial_trxn_id'],
3621 );
3622 if ($context != 'online' && $context != 'payLater') {
3623 $compareParams = array(
3624 'to_financial_account_id' => 6,
3625 'total_amount' => CRM_Utils_Array::value('total_amount', $params, 100),
3626 'status_id' => 1,
3627 );
3628 }
3629 if ($context == 'feeAmount') {
3630 $compareParams['fee_amount'] = 50;
3631 }
3632 elseif ($context == 'online') {
3633 $compareParams = array(
3634 'to_financial_account_id' => 12,
3635 'total_amount' => CRM_Utils_Array::value('total_amount', $params, 100),
3636 'status_id' => 1,
3637 'payment_instrument_id' => CRM_Utils_Array::value('payment_instrument_id', $params, 1),
3638 );
3639 }
3640 elseif ($context == 'payLater') {
3641 $compareParams = array(
3642 'to_financial_account_id' => 7,
3643 'total_amount' => CRM_Utils_Array::value('total_amount', $params, 100),
3644 'status_id' => 2,
3645 );
3646 }
3647 $this->assertDBCompareValues('CRM_Financial_DAO_FinancialTrxn', $trxnParams, $compareParams);
3648 $entityParams = array(
3649 'financial_trxn_id' => $trxn['financial_trxn_id'],
3650 'entity_table' => 'civicrm_financial_item',
3651 );
3652 $entityTrxn = current(CRM_Financial_BAO_FinancialItem::retrieveEntityFinancialTrxn($entityParams));
3653 $fitemParams = array(
3654 'id' => $entityTrxn['entity_id'],
3655 );
3656 $compareParams = array(
3657 'amount' => CRM_Utils_Array::value('total_amount', $params, 100),
3658 'status_id' => 1,
3659 'financial_account_id' => CRM_Utils_Array::value('financial_account_id', $params, 1),
3660 );
3661 if ($context == 'payLater') {
3662 $compareParams = array(
3663 'amount' => CRM_Utils_Array::value('total_amount', $params, 100),
3664 'status_id' => 3,
3665 'financial_account_id' => CRM_Utils_Array::value('financial_account_id', $params, 1),
3666 );
3667 }
3668 $this->assertDBCompareValues('CRM_Financial_DAO_FinancialItem', $fitemParams, $compareParams);
3669 if ($context == 'feeAmount') {
3670 $maxParams = array(
3671 'entity_id' => $params['id'],
3672 'entity_table' => 'civicrm_contribution',
3673 );
3674 $maxTrxn = current(CRM_Financial_BAO_FinancialItem::retrieveEntityFinancialTrxn($maxParams, TRUE));
3675 $trxnParams = array(
3676 'id' => $maxTrxn['financial_trxn_id'],
3677 );
3678 $compareParams = array(
3679 'to_financial_account_id' => 5,
3680 'from_financial_account_id' => 6,
3681 'total_amount' => 50,
3682 'status_id' => 1,
3683 );
3684 $trxnId = CRM_Core_BAO_FinancialTrxn::getFinancialTrxnId($params['id'], 'DESC');
3685 $this->assertDBCompareValues('CRM_Financial_DAO_FinancialTrxn', $trxnParams, $compareParams);
3686 $fitemParams = array(
3687 'entity_id' => $trxnId['financialTrxnId'],
3688 'entity_table' => 'civicrm_financial_trxn',
3689 );
3690 $compareParams = array(
3691 'amount' => 50,
3692 'status_id' => 1,
3693 'financial_account_id' => 5,
3694 );
3695 $this->assertDBCompareValues('CRM_Financial_DAO_FinancialItem', $fitemParams, $compareParams);
3696 }
3697 // This checks that empty Sales tax rows are not being created. If for any reason it needs to be removed the
3698 // line should be copied into all the functions that call this function & evaluated there
3699 // Be really careful not to remove or bypass this without ensuring stray rows do not re-appear
3700 // when calling completeTransaction or repeatTransaction.
3701 $this->callAPISuccessGetCount('FinancialItem', array('description' => 'Sales Tax', 'amount' => 0), 0);
3702 }
3703
3704 /**
3705 * Return financial type id on basis of name
3706 *
3707 * @param string $name Financial type m/c name
3708 *
3709 * @return int
3710 */
3711 public function getFinancialTypeId($name) {
3712 return CRM_Core_DAO::getFieldValue('CRM_Financial_DAO_FinancialType', $name, 'id', 'name');
3713 }
3714
3715 /**
3716 * Cleanup function for contents of $this->ids.
3717 *
3718 * This is a best effort cleanup to use in tear downs etc.
3719 *
3720 * It will not fail if the data has already been removed (some tests may do
3721 * their own cleanup).
3722 */
3723 protected function cleanUpSetUpIDs() {
3724 foreach ($this->setupIDs as $entity => $id) {
3725 try {
3726 civicrm_api3($entity, 'delete', array('id' => $id, 'skip_undelete' => 1));
3727 }
3728 catch (CiviCRM_API3_Exception $e) {
3729 // This is a best-effort cleanup function, ignore.
3730 }
3731 }
3732 }
3733
3734 /**
3735 * Create Financial Type.
3736 *
3737 * @param array $params
3738 *
3739 * @return array
3740 */
3741 protected function createFinancialType($params = array()) {
3742 $params = array_merge($params,
3743 array(
3744 'name' => 'Financial-Type -' . substr(sha1(rand()), 0, 7),
3745 'is_active' => 1,
3746 )
3747 );
3748 return $this->callAPISuccess('FinancialType', 'create', $params);
3749 }
3750
3751 /**
3752 * Enable Tax and Invoicing
3753 */
3754 protected function enableTaxAndInvoicing($params = array()) {
3755 // Enable component contribute setting
3756 $contributeSetting = array_merge($params,
3757 array(
3758 'invoicing' => 1,
3759 'invoice_prefix' => 'INV_',
3760 'credit_notes_prefix' => 'CN_',
3761 'due_date' => 10,
3762 'due_date_period' => 'days',
3763 'notes' => '',
3764 'is_email_pdf' => 1,
3765 'tax_term' => 'Sales Tax',
3766 'tax_display_settings' => 'Inclusive',
3767 )
3768 );
3769 return Civi::settings()->set('contribution_invoice_settings', $contributeSetting);
3770 }
3771
3772 /**
3773 * Enable Tax and Invoicing
3774 */
3775 protected function disableTaxAndInvoicing($params = array()) {
3776 if (!empty(\Civi::$statics['CRM_Core_PseudoConstant']) && isset(\Civi::$statics['CRM_Core_PseudoConstant']['taxRates'])) {
3777 unset(\Civi::$statics['CRM_Core_PseudoConstant']['taxRates']);
3778 }
3779 // Enable component contribute setting
3780 $contributeSetting = array_merge($params,
3781 array(
3782 'invoicing' => 0,
3783 )
3784 );
3785 return Civi::settings()->set('contribution_invoice_settings', $contributeSetting);
3786 }
3787
3788 /**
3789 * Add Sales Tax relation for financial type with financial account.
3790 *
3791 * @param int $financialTypeId
3792 *
3793 * @return obj
3794 */
3795 protected function relationForFinancialTypeWithFinancialAccount($financialTypeId) {
3796 $params = array(
3797 'name' => 'Sales tax account ' . substr(sha1(rand()), 0, 4),
3798 'financial_account_type_id' => key(CRM_Core_PseudoConstant::accountOptionValues('financial_account_type', NULL, " AND v.name LIKE 'Liability' ")),
3799 'is_deductible' => 1,
3800 'is_tax' => 1,
3801 'tax_rate' => 10,
3802 'is_active' => 1,
3803 );
3804 $account = CRM_Financial_BAO_FinancialAccount::add($params);
3805 $entityParams = array(
3806 'entity_table' => 'civicrm_financial_type',
3807 'entity_id' => $financialTypeId,
3808 'account_relationship' => key(CRM_Core_PseudoConstant::accountOptionValues('account_relationship', NULL, " AND v.name LIKE 'Sales Tax Account is' ")),
3809 );
3810
3811 // set tax rate (as 10) for provided financial type ID to static variable, later used to fetch tax rates of all financial types
3812 \Civi::$statics['CRM_Core_PseudoConstant']['taxRates'][$financialTypeId] = 10;
3813
3814 //CRM-20313: As per unique index added in civicrm_entity_financial_account table,
3815 // first check if there's any record on basis of unique key (entity_table, account_relationship, entity_id)
3816 $dao = new CRM_Financial_DAO_EntityFinancialAccount();
3817 $dao->copyValues($entityParams);
3818 $dao->find();
3819 if ($dao->fetch()) {
3820 $entityParams['id'] = $dao->id;
3821 }
3822 $entityParams['financial_account_id'] = $account->id;
3823
3824 return CRM_Financial_BAO_FinancialTypeAccount::add($entityParams);
3825 }
3826
3827 /**
3828 * Create price set with contribution test for test setup.
3829 *
3830 * This could be merged with 4.5 function setup in api_v3_ContributionPageTest::setUpContributionPage
3831 * on parent class at some point (fn is not in 4.4).
3832 *
3833 * @param $entity
3834 * @param array $params
3835 */
3836 public function createPriceSetWithPage($entity = NULL, $params = array()) {
3837 $membershipTypeID = $this->membershipTypeCreate(array('name' => 'Special'));
3838 $contributionPageResult = $this->callAPISuccess('contribution_page', 'create', array(
3839 'title' => "Test Contribution Page",
3840 'financial_type_id' => 1,
3841 'currency' => 'NZD',
3842 'goal_amount' => 50,
3843 'is_pay_later' => 1,
3844 'is_monetary' => TRUE,
3845 'is_email_receipt' => FALSE,
3846 ));
3847 $priceSet = $this->callAPISuccess('price_set', 'create', array(
3848 'is_quick_config' => 0,
3849 'extends' => 'CiviMember',
3850 'financial_type_id' => 1,
3851 'title' => 'my Page',
3852 ));
3853 $priceSetID = $priceSet['id'];
3854
3855 CRM_Price_BAO_PriceSet::addTo('civicrm_contribution_page', $contributionPageResult['id'], $priceSetID);
3856 $priceField = $this->callAPISuccess('price_field', 'create', array(
3857 'price_set_id' => $priceSetID,
3858 'label' => 'Goat Breed',
3859 'html_type' => 'Radio',
3860 ));
3861 $priceFieldValue = $this->callAPISuccess('price_field_value', 'create', array(
3862 'price_set_id' => $priceSetID,
3863 'price_field_id' => $priceField['id'],
3864 'label' => 'Long Haired Goat',
3865 'amount' => 20,
3866 'financial_type_id' => 'Donation',
3867 'membership_type_id' => $membershipTypeID,
3868 'membership_num_terms' => 1,
3869 )
3870 );
3871 $this->_ids['price_field_value'] = array($priceFieldValue['id']);
3872 $priceFieldValue = $this->callAPISuccess('price_field_value', 'create', array(
3873 'price_set_id' => $priceSetID,
3874 'price_field_id' => $priceField['id'],
3875 'label' => 'Shoe-eating Goat',
3876 'amount' => 10,
3877 'financial_type_id' => 'Donation',
3878 'membership_type_id' => $membershipTypeID,
3879 'membership_num_terms' => 2,
3880 )
3881 );
3882 $this->_ids['price_field_value'][] = $priceFieldValue['id'];
3883
3884 $priceFieldValue = $this->callAPISuccess('price_field_value', 'create', array(
3885 'price_set_id' => $priceSetID,
3886 'price_field_id' => $priceField['id'],
3887 'label' => 'Shoe-eating Goat',
3888 'amount' => 10,
3889 'financial_type_id' => 'Donation',
3890 )
3891 );
3892 $this->_ids['price_field_value']['cont'] = $priceFieldValue['id'];
3893
3894 $this->_ids['price_set'] = $priceSetID;
3895 $this->_ids['contribution_page'] = $contributionPageResult['id'];
3896 $this->_ids['price_field'] = array($priceField['id']);
3897
3898 $this->_ids['membership_type'] = $membershipTypeID;
3899 }
3900
3901 /**
3902 * No results returned.
3903 *
3904 * @implements CRM_Utils_Hook::aclWhereClause
3905 *
3906 * @param string $type
3907 * @param array $tables
3908 * @param array $whereTables
3909 * @param int $contactID
3910 * @param string $where
3911 */
3912 public function aclWhereHookNoResults($type, &$tables, &$whereTables, &$contactID, &$where) {
3913 }
3914
3915 /**
3916 * Only specified contact returned.
3917 * @implements CRM_Utils_Hook::aclWhereClause
3918 * @param $type
3919 * @param $tables
3920 * @param $whereTables
3921 * @param $contactID
3922 * @param $where
3923 */
3924 public function aclWhereMultipleContacts($type, &$tables, &$whereTables, &$contactID, &$where) {
3925 $where = " contact_a.id IN (" . implode(', ', $this->allowedContacts) . ")";
3926 }
3927
3928 /**
3929 * @implements CRM_Utils_Hook::selectWhereClause
3930 *
3931 * @param string $entity
3932 * @param array $clauses
3933 */
3934 public function selectWhereClauseHook($entity, &$clauses) {
3935 if ($entity == 'Event') {
3936 $clauses['event_type_id'][] = "IN (2, 3, 4)";
3937 }
3938 }
3939
3940 /**
3941 * An implementation of hook_civicrm_post used with all our test cases.
3942 *
3943 * @param $op
3944 * @param string $objectName
3945 * @param int $objectId
3946 * @param $objectRef
3947 */
3948 public function onPost($op, $objectName, $objectId, &$objectRef) {
3949 if ($op == 'create' && $objectName == 'Individual') {
3950 CRM_Core_DAO::executeQuery(
3951 "UPDATE civicrm_contact SET nick_name = 'munged' WHERE id = %1",
3952 array(
3953 1 => array($objectId, 'Integer'),
3954 )
3955 );
3956 }
3957
3958 if ($op == 'edit' && $objectName == 'Participant') {
3959 $params = array(
3960 1 => array($objectId, 'Integer'),
3961 );
3962 $query = "UPDATE civicrm_participant SET source = 'Post Hook Update' WHERE id = %1";
3963 CRM_Core_DAO::executeQuery($query, $params);
3964 }
3965 }
3966
3967
3968 /**
3969 * Instantiate form object.
3970 *
3971 * We need to instantiate the form to run preprocess, which means we have to trick it about the request method.
3972 *
3973 * @param string $class
3974 * Name of form class.
3975 *
3976 * @return \CRM_Core_Form
3977 */
3978 public function getFormObject($class) {
3979 $form = new $class();
3980 $_SERVER['REQUEST_METHOD'] = 'GET';
3981 $form->controller = new CRM_Core_Controller();
3982 return $form;
3983 }
3984
3985 /**
3986 * Get possible thousand separators.
3987 *
3988 * @return array
3989 */
3990 public function getThousandSeparators() {
3991 return array(array('.'), array(','));
3992 }
3993
3994 /**
3995 * Set the separators for thousands and decimal points.
3996 *
3997 * @param string $thousandSeparator
3998 */
3999 protected function setCurrencySeparators($thousandSeparator) {
4000 Civi::settings()->set('monetaryThousandSeparator', $thousandSeparator);
4001 Civi::settings()
4002 ->set('monetaryDecimalPoint', ($thousandSeparator === ',' ? '.' : ','));
4003 }
4004
4005 /**
4006 * Format money as it would be input.
4007 *
4008 * @param string $amount
4009 *
4010 * @return string
4011 */
4012 protected function formatMoneyInput($amount) {
4013 return CRM_Utils_Money::format($amount, NULL, '%a');
4014 }
4015
4016
4017 /**
4018 * Get the contribution object.
4019 *
4020 * @param int $contributionID
4021 *
4022 * @return \CRM_Contribute_BAO_Contribution
4023 */
4024 protected function getContributionObject($contributionID) {
4025 $contributionObj = new CRM_Contribute_BAO_Contribution();
4026 $contributionObj->id = $contributionID;
4027 $contributionObj->find(TRUE);
4028 return $contributionObj;
4029 }
4030
4031 }