CRM-18332 add first cut of api for revert actions
authoreileen <emcnaughton@wikimedia.org>
Thu, 31 Mar 2016 08:18:50 +0000 (21:18 +1300)
committerEileen <eileen@fuzion.co.nz>
Sat, 23 Apr 2016 03:51:11 +0000 (03:51 +0000)
Later I expect to add more parameters & deal with situation when log_date is not passed

CRM/Logging/Differ.php
CRM/Logging/ReportDetail.php
CRM/Logging/Reverter.php
CRM/Logging/Schema.php
api/v3/Logging.php [new file with mode: 0644]
api/v3/examples/Logging/Revert.php [new file with mode: 0644]
tests/phpunit/CiviTest/CiviUnitTestCase.php
tests/phpunit/api/v3/LoggingTest.php
tests/phpunit/api/v3/SyntaxConformanceTest.php

index d96db96c9fa2f869e5d68f4cdbf2114ebfe3085b..408841313fd5412bbac4414402fc12741977753e 100644 (file)
@@ -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;
+  }
+
 }
index a77c483079545f574f86b1d3e3496fe777c1cb46..b35ef0e2037aeb97a17019e071b63047def5fa34 100644 (file)
@@ -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));
index 8b2e5668fc874a5649a3b11ed36a1684abc074dc..5fb317d491bfd030fac09f0ce707a5dd5b9b46ef 100644 (file)
@@ -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) {
index 23ab7be37ca058865af4e36d15a5138451061afe..821ecb88f815e6b9068f840b4682c99f8b8e64b0 100644 (file)
@@ -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 (file)
index 0000000..fc2890a
--- /dev/null
@@ -0,0 +1,50 @@
+<?php
+/*
+ +--------------------------------------------------------------------+
+ | CiviCRM version 4.7                                                |
+ +--------------------------------------------------------------------+
+ | Copyright CiviCRM LLC (c) 2004-2015                                |
+ +--------------------------------------------------------------------+
+ | This file is a part of CiviCRM.                                    |
+ |                                                                    |
+ | CiviCRM is free software; you can copy, modify, and distribute it  |
+ | under the terms of the GNU Affero General Public License           |
+ | Version 3, 19 November 2007 and the CiviCRM Licensing Exception.   |
+ |                                                                    |
+ | CiviCRM is distributed in the hope that it will be useful, but     |
+ | WITHOUT ANY WARRANTY; without even the implied warranty of         |
+ | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.               |
+ | See the GNU Affero General Public License for more details.        |
+ |                                                                    |
+ | You should have received a copy of the GNU Affero General Public   |
+ | License and the CiviCRM Licensing Exception along                  |
+ | with this program; if not, contact CiviCRM LLC                     |
+ | at info[AT]civicrm[DOT]org. If you have questions about the        |
+ | GNU Affero General Public License or the licensing of CiviCRM,     |
+ | see the CiviCRM license FAQ at http://civicrm.org/licensing        |
+ +--------------------------------------------------------------------+
+ */
+
+/**
+ * This api exposes functionality for interacting with the logging functionality.
+ *
+ * @package CiviCRM_APIv3
+ */
+
+/**
+ * Revert a log change.
+ *
+ * @param array $params
+ *
+ * @return array
+ *    API Success Array
+ * @throws \API_Exception
+ * @throws \Civi\API\Exception\UnauthorizedException
+ */
+function civicrm_api3_logging_revert($params) {
+  $schema = new CRM_Logging_Schema();
+  $reverter = new CRM_Logging_Reverter($params['log_conn_id'], $params['log_date']);
+  $reverter->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 (file)
index 0000000..fcdf924
--- /dev/null
@@ -0,0 +1,70 @@
+<?php
+/**
+ * Test Generated example demonstrating the Logging.revert API.
+ *
+ * @return array
+ *   API result array
+ */
+function logging_revert_example() {
+  $params = array(
+    'log_conn_id' => '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
+*/
index 1c5cf67e1cdb6e9ec05ce0d3a455c2e4359df3bd..a74bca2c973cf464e645a674f3582e20d402c921 100644 (file)
@@ -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);
     }
   }
index 2b70a35e63f8028b79004b6e0bffe5f141f5710b..6dcfd562d07ca8f73095bbffefb2ee966884f93a 100644 (file)
@@ -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);
+  }
+
 }
index f555743e62fecd385079e6e922f6497f5f5f647f..9463b5a17e2e31652d8a688a990de49946b7b230 100644 (file)
@@ -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;