Skip to content

Commit

Permalink
Issue #94: Expose db replace to UI
Browse files Browse the repository at this point in the history
  • Loading branch information
petersistrom committed Nov 12, 2024
1 parent 2ac146f commit 5a2474b
Show file tree
Hide file tree
Showing 6 changed files with 214 additions and 60 deletions.
70 changes: 70 additions & 0 deletions classes/form/replace.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
<?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/>.

/**
* Advanced site wide search-replace form.
*
* @package tool_advancedreplace
* @copyright 2024 Catalyst IT Australia Pty Ltd
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/

namespace tool_advancedreplace\form;

use moodleform;

defined('MOODLE_INTERNAL') || die();

require_once("$CFG->libdir/formslib.php");

/**
* Site wide search-replace form.
*/
class replace extends moodleform {

/**
* Form definition
*
* @return void
*/
public function definition(): void {
global $CFG, $DB;

$mform = $this->_form;
$textareasize = ['rows' => 3, 'cols' => 50];
$fullwidth = ['style' => 'width: 100%'];

$mform->addElement('hidden', 'userid');
$mform->setType('userid', PARAM_INT);
$mform->setConstant('userid', $this->_customdata['userid']);

$mform->addElement('hidden', 'origin');
$mform->setType('origin', PARAM_TEXT);
$mform->setConstant('origin', 'web');

$mform->addElement('text', 'name', get_string('field_name', 'tool_advancedreplace'), $fullwidth);
$mform->setType('name', PARAM_RAW);
$mform->setDefault('name', '');
$mform->addElement('static', 'name_help', '', get_string("field_name_help", "tool_advancedreplace"));

// File upload.
$mform->addElement('filepicker', 'csvfile', get_string('selectfile', 'tool_advancedreplace'), null,
['accepted_types' => ['.csv']]);

$this->add_action_buttons(true, get_string('replace', 'tool_advancedreplace'));

}
}
80 changes: 80 additions & 0 deletions classes/helper.php
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,9 @@

use core\exception\moodle_exception;
use core_text;
use csv_import_reader;
use database_column_info;
use progress_bar;
use tool_advancedreplace\db_search;

/**
Expand Down Expand Up @@ -681,4 +683,82 @@ public static function read_last_line(string $filename) {
}
return [$lastline, $linecount];
}

/**
* Takes csv data and replaces all matching strings within the DB
* @param string $data CSV data to be read.
*/
public static function handle_replace_csv(string $data) {
// Load the CSV content.
$iid = csv_import_reader::get_new_iid('tool_advancedreplace');
$csvimport = new csv_import_reader($iid, 'tool_advancedreplace');
$contentcount = $csvimport->load_csv_content($data, 'utf-8', 'comma');

if ($contentcount === false) {
if (CLI_SCRIPT) {
cli_error(get_string('errorinvalidfile', 'tool_advancedreplace'));
} else {
throw new \moodle_exception(get_string('errorinvalidfile', 'tool_advancedreplace'));
}
}

// Read the header.
$header = $csvimport->get_columns();
if (empty($header)) {
if (CLI_SCRIPT) {
cli_error(get_string('errorinvalidfile', 'tool_advancedreplace'));
} else {
throw new \moodle_exception(get_string('errorinvalidfile', 'tool_advancedreplace'));
}
}

// Check if all required columns are present, and show which ones are missing.
$requiredcolumns = ['table', 'column', 'id', 'match', 'replace'];
$missingcolumns = array_diff($requiredcolumns, $header);

if (!empty($missingcolumns)) {
if (CLI_SCRIPT) {
cli_error(get_string('errormissingfields', 'tool_advancedreplace', implode(', ', $missingcolumns)));
} else {
throw new \moodle_exception(get_string('errormissingfields', 'tool_advancedreplace',
implode(', ', $missingcolumns)));
}
}

// Progress bar.
$progress = new progress_bar();
$progress->create();

// Column indexes.
$tableindex = array_search('table', $header);
$columnindex = array_search('column', $header);
$idindex = array_search('id', $header);
$matchindex = array_search('match', $header);
$replaceindex = array_search('replace', $header);

// Read the data and replace the strings.
$csvimport->init();
$rowcount = 0;
$rowskip = 0;
while ($record = $csvimport->next()) {
if (empty($record[$replaceindex])) {
// Skip if 'replace' is empty.
$rowskip++;
} else {
// Replace the string.
self::replace_text_in_a_record($record[$tableindex], $record[$columnindex],
$record[$matchindex], $record[$replaceindex], $record[$idindex]);
}

// Update the progress bar.
$rowcount++;
$progress->update_full(100 * $rowcount / $contentcount, "Processed $rowcount records. Skipped $rowskip records.");
}

// Show progress.
$progress->update_full('100', "Processed $rowcount records. Skipped $rowskip records.");

$csvimport->cleanup();
$csvimport->close();
}
}
61 changes: 1 addition & 60 deletions cli/replace.php
Original file line number Diff line number Diff line change
Expand Up @@ -77,64 +77,5 @@
$fp = fopen($file, 'r');
$data = fread($fp, filesize($file));
fclose($fp);

// Load the CSV content.
$iid = csv_import_reader::get_new_iid('tool_advancedreplace');
$csvimport = new csv_import_reader($iid, 'tool_advancedreplace');
$contentcount = $csvimport->load_csv_content($data, 'utf-8', 'comma');

if ($contentcount === false) {
cli_error(get_string('errorinvalidfile', 'tool_advancedreplace'));
}

// Read the header.
$header = $csvimport->get_columns();
if (empty($header)) {
cli_error(get_string('errorinvalidfile', 'tool_advancedreplace'));
}

// Check if all required columns are present, and show which ones are missing.
$requiredcolumns = ['table', 'column', 'id', 'match', 'replace'];
$missingcolumns = array_diff($requiredcolumns, $header);

if (!empty($missingcolumns)) {
cli_error(get_string('errormissingfields', 'tool_advancedreplace', implode(', ', $missingcolumns)));
}

// Column indexes.
$tableindex = array_search('table', $header);
$columnindex = array_search('column', $header);
$idindex = array_search('id', $header);
$matchindex = array_search('match', $header);
$replaceindex = array_search('replace', $header);

// Progress bar.
$progress = new progress_bar();
$progress->create();

// Read the data and replace the strings.
$csvimport->init();
$rowcount = 0;
$rowskip = 0;
while ($record = $csvimport->next()) {
if (empty($record[$replaceindex])) {
// Skip if 'replace' is empty.
$rowskip++;
} else {
// Replace the string.
helper::replace_text_in_a_record($record[$tableindex], $record[$columnindex],
$record[$matchindex], $record[$replaceindex], $record[$idindex]);
}

// Update the progress bar.
$rowcount++;
$progress->update_full(100 * $rowcount / $contentcount, "Processed $rowcount records. Skipped $rowskip records.");
}

// Show progress.
$progress->update_full('100', "Processed $rowcount records. Skipped $rowskip records.");

$csvimport->cleanup();
$csvimport->close();

helper::handle_replace_csv($data);
exit(0);
54 changes: 54 additions & 0 deletions db_replace.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
<?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/>.

/**
* Advanced search and replace strings throughout all texts in the whole database
*
* @package tool_advancedreplace
* @copyright 2024 Catalyst IT Australia Pty Ltd
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
define('NO_OUTPUT_BUFFERING', true); // Progress bar is used here.

use tool_advancedreplace\helper;

require_once(__DIR__ . '/../../../config.php');
require_once($CFG->libdir . '/adminlib.php');
require_once($CFG->dirroot . '/lib/csvlib.class.php');

$url = new moodle_url('/admin/tool/advancedreplace/db_replace.php');
$PAGE->set_url($url);

admin_externalpage_setup('tool_advancedreplace_search');

$redirect = new moodle_url('/admin/tool/advancedreplace/db_search.php');

$customdata = [
'userid' => $USER->id,
];
$form = new \tool_advancedreplace\form\replace($url->out(false), $customdata);
echo $OUTPUT->header();
if ($form->is_cancelled()) {
redirect($redirect);
} else if ($csvcontent = $form->get_file_content('csvfile')) {
helper::handle_replace_csv($csvcontent);
} else {
// Display form.
echo $OUTPUT->heading(get_string('replacepageheader', 'tool_advancedreplace'));
$form->display();
}

echo $OUTPUT->footer();
5 changes: 5 additions & 0 deletions db_search.php
Original file line number Diff line number Diff line change
Expand Up @@ -97,7 +97,12 @@
$url->param('id', 0);
$newurl = new \moodle_url($url, ['id' => 0]);
$newbutton = new \single_button($newurl, get_string('newsearch', 'tool_advancedreplace'), 'GET');

$replaceurl = new moodle_url('/admin/tool/advancedreplace/db_replace.php');
$replacebutton = new \single_button($replaceurl, get_string('newreplace', 'tool_advancedreplace'), 'GET');

echo $OUTPUT->render($newbutton);
echo $OUTPUT->render($replacebutton);
}

echo $OUTPUT->footer();
4 changes: 4 additions & 0 deletions lang/en/tool_advancedreplace.php
Original file line number Diff line number Diff line change
Expand Up @@ -88,12 +88,16 @@
$string['filespageheader'] = 'Search for text in Moodle files';
$string['lastupdated'] = 'Last updated {$a} ago';
$string['newsearch'] = 'New search';
$string['newreplace'] = 'New replace';
$string['replacepageheader'] = 'Replace text stored in the DB';
$string['replace'] = 'Replace';
$string['strftimedatetimemonthshort'] = '%d %b %Y, %I:%M %p';
$string['searchdeleted'] = 'The selected search was deleted.';
$string['searchpagename'] = 'Seach and Replace in the Database';
$string['searchpageheader'] = 'Search for text stored in the DB';
$string['searchqueued'] = 'Your search has been queued as an adhoc task.';
$string['searchcopy'] = 'Search options have been copied from a previous search. This will be treated as a new search.';
$string['selectfile'] = 'Select file';
$string['settings:logduration'] = 'Search logging';
$string['settings:logduration_help'] = 'Display log information for columns with no matches that take longer than the specified duration.';
$string['settings:excludetables'] = 'Exclude tables';
Expand Down

0 comments on commit 5a2474b

Please sign in to comment.