Skip to content

Commit

Permalink
MDL-46455 logstore_database: added backup/restore support
Browse files Browse the repository at this point in the history
  • Loading branch information
mdjnelson committed Oct 9, 2015
1 parent ea6c564 commit bcfa51c
Show file tree
Hide file tree
Showing 7 changed files with 285 additions and 52 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -34,4 +34,67 @@
* 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;
}
}
Original file line number Diff line number Diff line change
@@ -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 <[email protected]>
* @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;
}
}
Original file line number Diff line number Diff line change
@@ -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 <[email protected]>
* @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);
}
}
}
24 changes: 24 additions & 0 deletions admin/tool/log/store/database/classes/log/store.php
Original file line number Diff line number Diff line change
Expand Up @@ -258,6 +258,30 @@ public function get_events_select_count($selectwhere, array $params) {
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?
*
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -60,56 +60,10 @@ protected function define_logstore_subplugin_structure() {
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);
}
}
6 changes: 3 additions & 3 deletions backup/util/dbops/backup_structure_dbops.class.php
Original file line number Diff line number Diff line change
Expand Up @@ -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 &&
Expand All @@ -48,10 +48,10 @@ public static function get_iterator($element, $params, $processor) {
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();
Expand Down
Loading

0 comments on commit bcfa51c

Please sign in to comment.