MDL-46455 logstore_database: added backup/restore support

This commit is contained in:
Mark Nelson 2015-09-18 16:00:08 -07:00
parent ea6c56437a
commit bcfa51ca41
7 changed files with 285 additions and 52 deletions

View File

@ -34,4 +34,67 @@ defined('MOODLE_INTERNAL') || die();
* for sharing code between all subplugins.
*/
abstract class restore_tool_log_logstore_subplugin extends restore_subplugin {
/**
* Process log entries.
*
* This method proceeds to read, complete, remap and, finally,
* discard or save every log entry.
*
* @param array $data log entry.
* @return object|null $dataobject A data object with values for one or more fields in the record,
* or null if we are not going to process the log.
*/
protected function process_log($data) {
$data = (object) $data;
// Complete the information that does not come from backup.
if (!$data->contextid = $this->get_mappingid('context', $data->contextid)) {
// Something went really wrong, cannot find the context this log belongs to.
return;
}
$context = context::instance_by_id($data->contextid, MUST_EXIST);
$data->contextlevel = $context->contextlevel;
$data->contextinstanceid = $context->instanceid;
$data->courseid = $this->task->get_courseid();
// Remap users.
if (!$data->userid = $this->get_mappingid('user', $data->userid)) {
// Something went really wrong, cannot find the user this log belongs to.
return;
}
if (!empty($data->relateduserid)) { // This is optional.
if (!$data->relateduserid = $this->get_mappingid('user', $data->relateduserid)) {
// Something went really wrong, cannot find the relateduserid this log is about.
return;
}
}
if (!empty($data->realuserid)) { // This is optional.
if (!$data->realuserid = $this->get_mappingid('user', $data->realuserid)) {
// Something went really wrong, cannot find the realuserid this log is logged in as.
return;
}
}
// Roll dates.
$data->timecreated = $this->apply_date_offset($data->timecreated);
// Revert other to its original php way.
$data->other = unserialize(base64_decode($data->other));
// Arrived here, we have both 'objectid' and 'other' to be converted. This is the tricky part.
// Both are pointing to other records id, but the sources are not identified in the
// same way restore mappings work. So we need to delegate them to some resolver that
// will give us the correct restore mapping to be used.
if (!empty($data->objectid)) {
// TODO: Call to the resolver.
return;
}
if (!empty($data->other)) {
// TODO: Call to the resolver.
return;
}
return $data;
}
}

View File

@ -0,0 +1,64 @@
<?php
// This file is part of Moodle - http://moodle.org/
//
// Moodle is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Moodle 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 General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
/**
* Backup implementation for the (tool_log) logstore_database subplugin.
*
* @package logstore_database
* @category backup
* @copyright 2015 Mark Nelson <markn@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
defined('MOODLE_INTERNAL') || die();
class backup_logstore_database_subplugin extends backup_tool_log_logstore_subplugin {
/**
* Returns the subplugin structure to attach to the 'logstore' XML element.
*
* @return backup_subplugin_element the subplugin structure to be attached.
*/
protected function define_logstore_subplugin_structure() {
$subplugin = $this->get_subplugin_element();
$subpluginwrapper = new backup_nested_element($this->get_recommended_name());
// Create the custom (base64 encoded, xml safe) 'other' final element.
$otherelement = new base64_encode_final_element('other');
$subpluginlog = new backup_nested_element('logstore_database_log', array('id'), array(
'eventname', 'component', 'action', 'target', 'objecttable',
'objectid', 'crud', 'edulevel', 'contextid', 'userid', 'relateduserid',
'anonymous', $otherelement, 'timecreated', 'ip', 'realuserid'));
$subplugin->add_child($subpluginwrapper);
$subpluginwrapper->add_child($subpluginlog);
// Get the details for the external database.
$manager = new \tool_log\log\manager();
$store = new \logstore_database\log\store($manager);
$extdb = $store->get_extdb();
if (!$extdb) {
return false;
}
$subpluginlog->set_source_db($extdb);
$subpluginlog->set_source_table($store->get_config_value('dbtable'), array('contextid' => backup::VAR_CONTEXTID));
return $subplugin;
}
}

View File

@ -0,0 +1,102 @@
<?php
// This file is part of Moodle - http://moodle.org/
//
// Moodle is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Moodle 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 General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
/**
* Restore implementation for the (tool_log) logstore_database subplugin.
*
* @package logstore_database
* @category backup
* @copyright 2015 Mark Nelson <markn@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
defined('MOODLE_INTERNAL') || die();
class restore_logstore_database_subplugin extends restore_tool_log_logstore_subplugin {
/**
* @var moodle_database the external database.
*/
private static $extdb = null;
/**
* @var string the external database table name.
*/
private static $extdbtablename = null;
/**
* The constructor for this logstore.
*
* @param string $subplugintype the subplugin type.
* @param string $subpluginname the subplugin name.
* @param restore_structure_step $step.
*/
public function __construct($subplugintype, $subpluginname, $step) {
// Check that the logstore is enabled before setting variables.
$enabledlogstores = explode(',', get_config('tool_log', 'enabled_stores'));
if (in_array('logstore_database', $enabledlogstores)) {
$manager = new \tool_log\log\manager();
$store = new \logstore_database\log\store($manager);
self::$extdb = $store->get_extdb();
self::$extdbtablename = $store->get_config_value('dbtable');
}
parent::__construct($subplugintype, $subpluginname, $step);
}
/**
* Returns the subplugin structure to attach to the 'logstore' XML element.
*
* @return restore_path_element[] array of elements to be processed on restore.
*/
protected function define_logstore_subplugin_structure() {
// If the logstore is not enabled we don't add structures for it.
$enabledlogstores = explode(',', get_config('tool_log', 'enabled_stores'));
if (!in_array('logstore_database', $enabledlogstores)) {
return array(); // The logstore is not enabled, nothing to restore.
}
$paths = array();
$elename = $this->get_namefor('log');
$elepath = $this->get_pathfor('/logstore_database_log');
$paths[] = new restore_path_element($elename, $elepath);
return $paths;
}
/**
* Process logstore_database_log entries.
*
* This method proceeds to read, complete, remap and, finally,
* discard or save every log entry.
*
* @param array() $data log entry.
* @return null if we are not restoring the log.
*/
public function process_logstore_database_log($data) {
// Do not bother processing if we can not add it to a database.
if (!self::$extdb || !self::$extdbtablename) {
return;
}
$data = $this->process_log($data);
if ($data) {
self::$extdb->insert_record(self::$extdbtablename, $data);
}
}
}

View File

@ -258,6 +258,30 @@ class store implements \tool_log\log\writer, \core\log\sql_reader {
return $this->extdb->count_records_select($dbtable, $selectwhere, $params);
}
/**
* Get a config value for the store.
*
* @param string $name Config name
* @param mixed $default default value
* @return mixed config value if set, else the default value.
*/
public function get_config_value($name, $default = null) {
return $this->get_config($name, $default);
}
/**
* Get the external database object.
*
* @return \moodle_database $extdb
*/
public function get_extdb() {
if (!$this->init()) {
return false;
}
return $this->extdb;
}
/**
* Are the new events appearing in the reader?
*

View File

@ -60,56 +60,10 @@ class restore_logstore_standard_subplugin extends restore_tool_log_logstore_subp
public function process_logstore_standard_log($data) {
global $DB;
$data = (object)$data;
$data = $this->process_log($data);
// Complete the information that does not come from backup.
if (! $data->contextid = $this->get_mappingid('context', $data->contextid)) {
// Something went really wrong, cannot find the context this log belongs to.
return;
if ($data) {
$DB->insert_record('logstore_standard_log', $data);
}
$context = context::instance_by_id($data->contextid, MUST_EXIST);
$data->contextlevel = $context->contextlevel;
$data->contextinstanceid = $context->instanceid;
$data->courseid = $this->task->get_courseid();
// Remap users.
if (! $data->userid = $this->get_mappingid('user', $data->userid)) {
// Something went really wrong, cannot find the user this log belongs to.
return;
}
if (!empty($data->relateduserid)) { // This is optional.
if (! $data->relateduserid = $this->get_mappingid('user', $data->relateduserid)) {
// Something went really wrong, cannot find the relateduserid this log is about.
return;
}
}
if (!empty($data->realuserid)) { // This is optional.
if (! $data->realuserid = $this->get_mappingid('user', $data->realuserid)) {
// Something went really wrong, cannot find the realuserid this log is logged in as.
return;
}
}
// Roll dates.
$data->timecreated = $this->apply_date_offset($data->timecreated);
// Revert other to its original php way.
$data->other = unserialize(base64_decode($data->other));
// Arrived here, we have both 'objectid' and 'other' to be converted. This is the tricky part.
// Both are pointing to other records id, but the sources are not identified in the
// same way restore mappings work. So we need to delegate them to some resolver that
// will give us the correct restore mapping to be used.
if (!empty($data->objectid)) {
// TODO: Call to the resolver.
return;
}
if (!empty($data->other)) {
// TODO: Call to the resolver.
return;
}
// Arrived here, everything is now ready to be added to database, let's proceed.
$DB->insert_record('logstore_standard_log', $data);
}
}

View File

@ -33,11 +33,11 @@
abstract class backup_structure_dbops extends backup_dbops {
public static function get_iterator($element, $params, $processor) {
global $DB;
// Check we are going to get_iterator for one backup_nested_element
if (! $element instanceof backup_nested_element) {
throw new base_element_struct_exception('backup_nested_element_expected');
}
// If var_array, table and sql are null, and element has no final elements it is one nested element without source
// Just return one 1 element iterator without information
if ($element->get_source_array() === null && $element->get_source_table() === null &&
@ -48,10 +48,10 @@ abstract class backup_structure_dbops extends backup_dbops {
return new backup_array_iterator($element->get_source_array());
} else if ($element->get_source_table() !== null) { // It's one table, return recordset iterator
return $DB->get_recordset($element->get_source_table(), self::convert_params_to_values($params, $processor), $element->get_source_table_sortby());
return $element->get_source_db()->get_recordset($element->get_source_table(), self::convert_params_to_values($params, $processor), $element->get_source_table_sortby());
} else if ($element->get_source_sql() !== null) { // It's one sql, return recordset iterator
return $DB->get_recordset_sql($element->get_source_sql(), self::convert_params_to_values($params, $processor));
return $element->get_source_db()->get_recordset_sql($element->get_source_sql(), self::convert_params_to_values($params, $processor));
} else { // No sources, supress completely, using null iterator
return new backup_null_iterator();

View File

@ -41,6 +41,11 @@ class backup_nested_element extends base_nested_element implements processable {
protected $results; // Logs the results we encounter during the process.
protected $logs; // Some log messages that could be retrieved later.
/**
* @var \moodle_database $dbtouse
*/
protected $dbtouse;
/**
* Constructor - instantiates one backup_nested_element, specifying its basic info.
*
@ -49,6 +54,8 @@ class backup_nested_element extends base_nested_element implements processable {
* @param array $final_elements this element will handle (optional, defaults to null)
*/
public function __construct($name, $attributes = null, $final_elements = null) {
global $DB;
parent::__construct($name, $attributes, $final_elements);
$this->var_array = null;
$this->table = null;
@ -61,6 +68,7 @@ class backup_nested_element extends base_nested_element implements processable {
$this->counter = 0;
$this->results = array();
$this->logs = array();
$this->dbtouse = $DB;
}
/**
@ -193,6 +201,15 @@ class backup_nested_element extends base_nested_element implements processable {
$this->var_array = $arr;
}
/**
* Set the database we want to use.
*
* @param \moodle_database $db
*/
public function set_source_db($db) {
$this->dbtouse = $db;
}
public function set_source_table($table, $params, $sortby = null) {
if (!is_array($params)) { // Check we are passing array
throw new base_element_struct_exception('setsourcerequiresarrayofparams');
@ -260,6 +277,15 @@ class backup_nested_element extends base_nested_element implements processable {
return $this->var_array;
}
/**
* Get the database we want to use.
*
* @return \moodle_database $db
*/
public function get_source_db() {
return $this->dbtouse;
}
public function get_source_table() {
return $this->table;
}