From 93afbc3ad84dfc0e1f0e4f7c274b27db63cbe5e9 Mon Sep 17 00:00:00 2001 From: eileen Date: Thu, 31 Mar 2016 21:18:50 +1300 Subject: [PATCH] CRM-18332 add first cut of api for revert actions Later I expect to add more parameters & deal with situation when log_date is not passed --- CRM/Logging/Differ.php | 29 ++++++++ CRM/Logging/ReportDetail.php | 3 +- CRM/Logging/Reverter.php | 30 ++++++-- CRM/Logging/Schema.php | 11 +++ api/v3/Logging.php | 50 +++++++++++++ api/v3/examples/Logging/Revert.php | 70 +++++++++++++++++++ tests/phpunit/CiviTest/CiviUnitTestCase.php | 11 +-- tests/phpunit/api/v3/LoggingTest.php | 19 +++++ .../phpunit/api/v3/SyntaxConformanceTest.php | 5 ++ 9 files changed, 216 insertions(+), 12 deletions(-) create mode 100644 api/v3/Logging.php create mode 100644 api/v3/examples/Logging/Revert.php diff --git a/CRM/Logging/Differ.php b/CRM/Logging/Differ.php index d96db96c9f..408841313f 100644 --- a/CRM/Logging/Differ.php +++ b/CRM/Logging/Differ.php @@ -388,4 +388,33 @@ ORDER BY log_date return array($titles, $values); } + /** + * Get all changes made in the connection. + * + * @param array $tables + * Array of tables to inspect. + * + * @return array + */ + public function getAllChangesForConnection($tables) { + $params = array(1 => array($this->log_conn_id, 'String')); + foreach ($tables as $table) { + if (empty($sql)) { + $sql = " SELECT '{$table}' as table_name, id FROM {$this->db}.log_{$table} WHERE log_conn_id = %1"; + } + else { + $sql .= " UNION SELECT '{$table}' as table_name, id FROM {$this->db}.log_{$table} WHERE log_conn_id = %1"; + } + } + $diffs = array(); + $dao = CRM_Core_DAO::executeQuery($sql, $params); + while ($dao->fetch()) { + if (empty($this->log_date)) { + $this->log_date = CRM_Core_DAO::singleValueQuery("SELECT log_date FROM {$this->db}.log_{$table} WHERE log_conn_id = %1 LIMIT 1", $params); + } + $diffs = array_merge($diffs, $this->diffsInTableForId($dao->table_name, $dao->id)); + } + return $diffs; + } + } diff --git a/CRM/Logging/ReportDetail.php b/CRM/Logging/ReportDetail.php index a77c483079..b35ef0e203 100644 --- a/CRM/Logging/ReportDetail.php +++ b/CRM/Logging/ReportDetail.php @@ -238,7 +238,8 @@ class CRM_Logging_ReportDetail extends CRM_Report_Form { */ protected function revert() { $reverter = new CRM_Logging_Reverter($this->log_conn_id, $this->log_date); - $reverter->revert($this->tables); + $reverter->calculateDiffsFromLogConnAndDate($this->tables); + $reverter->revert(); CRM_Core_Session::setStatus(ts('The changes have been reverted.'), ts('Reverted'), 'success'); if ($this->cid) { CRM_Utils_System::redirect(CRM_Utils_System::url('civicrm/contact/view', "reset=1&selectedChild=log&cid={$this->cid}", FALSE, NULL, FALSE)); diff --git a/CRM/Logging/Reverter.php b/CRM/Logging/Reverter.php index 8b2e5668fc..5fb317d491 100644 --- a/CRM/Logging/Reverter.php +++ b/CRM/Logging/Reverter.php @@ -35,6 +35,13 @@ class CRM_Logging_Reverter { private $log_conn_id; private $log_date; + /** + * The diffs to be reverted. + * + * @var array + */ + private $diffs = array(); + /** * Class constructor. * @@ -49,11 +56,24 @@ class CRM_Logging_Reverter { } /** - * Revert changes in the array of diffs in $this->diffs. * - * @param $tables + * Calculate a set of diffs based on the connection_id and changes at a close time. + * + * @param array $tables + */ + public function calculateDiffsFromLogConnAndDate($tables) { + $differ = new CRM_Logging_Differ($this->log_conn_id, $this->log_date); + $this->diffs = $differ->diffsInTables($tables); + } + + public function setDiffs($diffs) { + $this->diffs = $diffs; + } + + /** + * Revert changes in the array of diffs in $this->diffs. */ - public function revert($tables) { + public function revert() { // get custom data tables, columns and types $ctypes = array(); @@ -65,9 +85,7 @@ class CRM_Logging_Reverter { $ctypes[$dao->table_name][$dao->column_name] = $dao->data_type; } - $differ = new CRM_Logging_Differ($this->log_conn_id, $this->log_date); - $diffs = $differ->diffsInTables($tables); - + $diffs = $this->diffs; $deletes = array(); $reverts = array(); foreach ($diffs as $table => $changes) { diff --git a/CRM/Logging/Schema.php b/CRM/Logging/Schema.php index 23ab7be37c..821ecb88f8 100644 --- a/CRM/Logging/Schema.php +++ b/CRM/Logging/Schema.php @@ -921,4 +921,15 @@ COLS; } } + /** + * Get all the log tables that reference civicrm_contact. + * + * Note that it might make sense to wrap this in a getLogTablesForEntity + * but this is the only entity currently available... + */ + public function getLogTablesForContact() { + $tables = array_keys(CRM_Dedupe_Merger::cidRefs()); + return array_intersect($tables, $this->tables); + } + } diff --git a/api/v3/Logging.php b/api/v3/Logging.php new file mode 100644 index 0000000000..fc2890a0f4 --- /dev/null +++ b/api/v3/Logging.php @@ -0,0 +1,50 @@ +calculateDiffsFromLogConnAndDate($schema->getLogTablesForContact()); + $reverter->revert(); + return civicrm_api3_create_success(1); +} diff --git a/api/v3/examples/Logging/Revert.php b/api/v3/examples/Logging/Revert.php new file mode 100644 index 0000000000..fcdf924195 --- /dev/null +++ b/api/v3/examples/Logging/Revert.php @@ -0,0 +1,70 @@ + 'woot', + 'log_date' => '2016-04-05 23:52:59', + ); + + try{ + $result = civicrm_api3('Logging', 'revert', $params); + } + catch (CiviCRM_API3_Exception $e) { + // Handle error here. + $errorMessage = $e->getMessage(); + $errorCode = $e->getErrorCode(); + $errorData = $e->getExtraParams(); + return array( + 'error' => $errorMessage, + 'error_code' => $errorCode, + 'error_data' => $errorData, + ); + } + + return $result; +} + +/** + * Function returns array of result expected from previous function. + * + * @return array + * API result array + */ +function logging_revert_expectedresult() { + + $expectedResult = array( + 'is_error' => 0, + 'version' => 3, + 'count' => 1, + 'values' => 1, + ); + + return $expectedResult; +} + +/* +* This example has been generated from the API test suite. +* The test that created it is called "/Users/emcnaughton/buildkit/build/dmaster/sites/all/modules/civicrm/tests/phpunit/api/v3/LoggingTest.php" +* and can be found at: +* https://github.com/civicrm/civicrm-core/blob/master/tests/phpunit/api/v3/Revert +* +* You can see the outcome of the API tests at +* https://test.civicrm.org/job/CiviCRM-master-git/ +* +* To Learn about the API read +* http://wiki.civicrm.org/confluence/display/CRMDOC/Using+the+API +* +* Browse the api on your own site with the api explorer +* http://MYSITE.ORG/path/to/civicrm/api +* +* Read more about testing here +* http://wiki.civicrm.org/confluence/display/CRM/Testing +* +* API Standards documentation: +* http://wiki.civicrm.org/confluence/display/CRM/API+Architecture+Standards +*/ diff --git a/tests/phpunit/CiviTest/CiviUnitTestCase.php b/tests/phpunit/CiviTest/CiviUnitTestCase.php index 1c5cf67e1c..a74bca2c97 100644 --- a/tests/phpunit/CiviTest/CiviUnitTestCase.php +++ b/tests/phpunit/CiviTest/CiviUnitTestCase.php @@ -2268,12 +2268,13 @@ class CiviUnitTestCase extends PHPUnit_Extensions_Database_TestCase { $smarty->assign('result', $result); $smarty->assign('action', $action); - if (file_exists('../tests/templates/documentFunction.tpl')) { - if (!is_dir("../api/v3/examples/$entity")) { - mkdir("../api/v3/examples/$entity"); + global $civicrm_root; + if (file_exists($civicrm_root . '/tests/templates/documentFunction.tpl')) { + if (!is_dir($civicrm_root . '/api/v3/examples/$entity')) { + mkdir($civicrm_root . '/api/v3/examples/$entity'); } - $f = fopen("../api/v3/examples/$entity/$exampleName.php", "w+b"); - fwrite($f, $smarty->fetch('../tests/templates/documentFunction.tpl')); + $f = fopen($civicrm_root . "/api/v3/examples/$entity/$exampleName.php", "w+b"); + fwrite($f, $smarty->fetch($civicrm_root . '/tests/templates/documentFunction.tpl')); fclose($f); } } diff --git a/tests/phpunit/api/v3/LoggingTest.php b/tests/phpunit/api/v3/LoggingTest.php index 2b70a35e63..6dcfd562d0 100644 --- a/tests/phpunit/api/v3/LoggingTest.php +++ b/tests/phpunit/api/v3/LoggingTest.php @@ -226,4 +226,23 @@ class api_v3_LoggingTest extends CiviUnitTestCase { "); } + /** + * Test changes can be reverted. + */ + public function testRevert() { + $contactId = $this->individualCreate(); + $this->callAPISuccess('Setting', 'create', array('logging' => TRUE)); + CRM_Core_DAO::executeQuery("SET @uniqueID = 'woot'"); + $timeStamp = date('Y-m-d H:i:s'); + $this->callAPISuccess('Contact', 'create', array( + 'id' => $contactId, + 'first_name' => 'Dopey', + 'api.email.create' => array('email' => 'dopey@mail.com')) + ); + $email = $this->callAPISuccessGetSingle('email', array('email' => 'dopey@mail.com')); + $this->callAPIAndDocument('Logging', 'revert', array('log_conn_id' => 'woot', 'log_date' => $timeStamp), __FILE__, 'Revert'); + $this->assertEquals('Anthony', $this->callAPISuccessGetValue('contact', array('id' => $contactId, 'return' => 'first_name'))); + $this->callAPISuccessGetCount('Email', array('id' => $email['id']), 0); + } + } diff --git a/tests/phpunit/api/v3/SyntaxConformanceTest.php b/tests/phpunit/api/v3/SyntaxConformanceTest.php index f555743e62..9463b5a17e 100644 --- a/tests/phpunit/api/v3/SyntaxConformanceTest.php +++ b/tests/phpunit/api/v3/SyntaxConformanceTest.php @@ -91,6 +91,7 @@ class api_v3_SyntaxConformanceTest extends CiviUnitTestCase { 'System', 'Setting', 'Payment', + 'Logging', ); $this->toBeImplemented['create'] = array( 'Cxn', @@ -108,6 +109,7 @@ class api_v3_SyntaxConformanceTest extends CiviUnitTestCase { 'Payment', 'Order', 'SavedSearch', //work fine in local + 'Logging', ); $this->toBeImplemented['delete'] = array( 'Cxn', @@ -343,6 +345,7 @@ class api_v3_SyntaxConformanceTest extends CiviUnitTestCase { 'CustomValue', 'Setting', 'User', + 'Logging', ); if ($sequential === TRUE) { return $entitiesWithout; @@ -391,6 +394,7 @@ class api_v3_SyntaxConformanceTest extends CiviUnitTestCase { 'Extension', 'ReportTemplate', 'System', + 'Logging', ); if ($sequential === TRUE) { return $entitiesWithoutGet; @@ -464,6 +468,7 @@ class api_v3_SyntaxConformanceTest extends CiviUnitTestCase { 'MailingContact', 'SystemLog', //skip this because it doesn't make sense to update logs, + 'Logging', ); if ($sequential === TRUE) { return $entitiesWithout; -- 2.25.1