addChunk('Settings', function() use ($ui)
{
global $smcFunc, $db_connection, $db_type, $sourcedir; // Must remain globals
// First some settings file stuff...
$dumpvars = array('mbname', 'boardurl', 'db_server', 'db_name', 'db_prefix', 'language', 'db_type', 'db_character_set', 'db_mb4');
$settings = array();
$settings[0] = array('Variable','Value');
foreach($dumpvars AS $var)
{
if (!isset($ui->getSettingsFile()[$var]))
$value = 'NOT SET';
elseif (is_null($ui->getSettingsFile()[$var]))
$value = 'null';
elseif ($ui->getSettingsFile()[$var] === false)
$value = 'false';
elseif ($ui->getSettingsFile()[$var] === true)
$value = 'true';
else
$value = $ui->getSettingsFile()[$var];
$settings[] = array($var, $value);
}
$ui->dumpTable($settings);
});
$ui->addChunk('Specify URLs and Paths', function() use ($ui)
{
// Starting values...
$ui->oldURL = 'http://your/old/url';
$ui->newURL = $ui->getSettingsFile()['boardurl'];
$ui->oldDir = '/your/old/dir';
$ui->newDir = $ui->getSettingsFile()['boarddir'];
// If in session, use those......
if (isset($_SESSION['oldurl']) && is_string($_SESSION['oldurl']))
$ui->oldURL = $ui->cleanseText($_SESSION['oldurl']);
if (isset($_SESSION['newurl']) && is_string($_SESSION['newurl']))
$ui->newURL = $ui->cleanseText($_SESSION['newurl']);
if (isset($_SESSION['olddir']) && is_string($_SESSION['olddir']))
$ui->oldDir = $ui->cleanseText($_SESSION['olddir']);
if (isset($_SESSION['newdir']) && is_string($_SESSION['newdir']))
$ui->newDir = $ui->cleanseText($_SESSION['newdir']);
echo '
';
// Some edits...
if (empty($ui->oldURL))
{
unset($_SESSION['preview']);
unset($_SESSION['proceed']);
$ui->addError('Old URL is required.');
}
// Make sure empty means empty strings...
if (empty($ui->newURL))
$ui->newURL = '';
if (empty($ui->oldDir))
$ui->oldDir = '';
if (empty($ui->newDir))
$ui->newDir = '';
// Dirs must be both empty or both provided...
if ((empty($ui->newDir) && !empty($ui->oldDir)) || (!empty($ui->newDir) && empty($ui->oldDir)))
{
unset($_SESSION['preview']);
unset($_SESSION['proceed']);
$ui->addError('Dirs must be both empty or both provided.');
}
});
$ui->addChunk('Results', function() use ($ui)
{
if (empty($_SESSION['preview']) && empty($_SESSION['proceed']))
return;
if (!empty($_SESSION['proceed']))
echo '
Executing:
';
else
echo '
Preview:
';
doSettings($ui);
doThemes($ui);
doMessages($ui);
doPMs($ui);
doSignatures($ui);
echo "
Path & URL updates completed!
";
});
//*** Do settings
function doSettings($ui)
{
global $smcFunc, $db_type, $db_connection, $db_prefix, $db_name;
$sql = "SELECT variable, value FROM " . $db_prefix . "settings;";
$result = $smcFunc['db_query']('', $sql);
while($row = $smcFunc['db_fetch_assoc']($result))
{
$stringPos = mb_stripos($row['value'], $ui->oldURL);
if ($stringPos !== false)
{
$settings = array();
$settings[] = array('Variable: ', $row['variable']);
$settings[] = array('Old Value: ', shortString($row['value'], $stringPos));
$newval = str_ireplace($ui->oldURL, $ui->newURL, $row['value']);
$settings[] = array('New Value: ', shortString($newval, $stringPos));
$ui->dumpTable($settings);
if (!empty($_SESSION['proceed']))
{
$newval = $smcFunc['db_escape_string']($newval);
$sql = "UPDATE " . $db_prefix . "settings SET value = '" . $newval
. "' WHERE variable = '" . $row['variable'] . "';";
$smcFunc['db_query']('', $sql);
}
}
if (empty($ui->oldDir))
continue;
$stringPos = mb_stripos($row['value'], $ui->oldDir);
if ($stringPos !== false)
{
$settings = array();
$settings[] = array('Variable: ', $row['variable']);
$settings[] = array('Old Value: ', shortString($row['value'], $stringPos));
$newval = str_ireplace($ui->oldDir, $ui->newDir, $row['value']);
$settings[] = array('New Value: ', shortString($newval, $stringPos));
$ui->dumpTable($settings);
if (!empty($_SESSION['proceed']))
{
$newval = $smcFunc['db_escape_string']($newval);
$sql = "UPDATE " . $db_prefix . "settings SET value = '" . $newval
. "' WHERE variable = '" . $row['variable'] . "';";
$smcFunc['db_query']('', $sql);
}
}
}
$smcFunc['db_free_result']($result);
return;
}
//*** Do themes
function doThemes($ui)
{
global $smcFunc, $db_type, $db_connection, $db_prefix, $db_name;
$sql = "SELECT id_member, id_theme, variable, value FROM " . $db_prefix . "themes;";
$result = $smcFunc['db_query']('', $sql);
while($row = $smcFunc['db_fetch_assoc']($result))
{
$stringPos = mb_stripos($row['value'], $ui->oldURL);
if ($stringPos !== false)
{
$settings = array();
$settings[] = array('Member: ', $row['id_member']);
$settings[] = array('Theme: ', $row['id_theme']);
$settings[] = array('Variable: ', $row['variable']);
$settings[] = array('Old Value: ', shortString($row['value'], $stringPos));
$newval = str_ireplace($ui->oldURL, $ui->newURL, $row['value']);
$settings[] = array('New Value: ', shortString($newval, $stringPos));
$ui->dumpTable($settings);
if (!empty($_SESSION['proceed']))
{
$newval = $smcFunc['db_escape_string']($newval);
$sql = "UPDATE " . $db_prefix . "themes SET value = '" . $newval
. "' WHERE variable = '" . $row['variable']
. "' AND id_member = '" . $row['id_member']
. "' AND id_theme = '" . $row['id_theme'] . "';";
$smcFunc['db_query']('', $sql);
}
}
if (empty($ui->oldDir))
continue;
$stringPos = mb_stripos($row['value'], $ui->oldDir);
if ($stringPos !== false)
{
$settings = array();
$settings[] = array('Member: ', $row['id_member']);
$settings[] = array('Theme: ', $row['id_theme']);
$settings[] = array('Variable: ', $row['variable']);
$settings[] = array('Old Value: ', shortString($row['value'], $stringPos));
$newval = str_ireplace($ui->oldDir, $ui->newDir, $row['value']);
$settings[] = array('New Value: ', shortString($newval, $stringPos));
$ui->dumpTable($settings);
if (!empty($_SESSION['proceed']))
{
$newval = $smcFunc['db_escape_string']($newval);
$sql = "UPDATE " . $db_prefix . "themes SET value = '" . $newval
. "' WHERE variable = '" . $row['variable']
. "' AND id_member = '" . $row['id_member']
. "' AND id_theme = '" . $row['id_theme'] . "';";
$smcFunc['db_query']('', $sql);
}
}
}
$smcFunc['db_free_result']($result);
return;
}
//*** Do messages
function doMessages($ui)
{
global $smcFunc, $db_type, $db_connection, $db_prefix, $db_name;
$sql = "SELECT id_msg, subject, body FROM " . $db_prefix . "messages;";
$result = $smcFunc['db_query']('', $sql);
while($row = $smcFunc['db_fetch_assoc']($result))
{
$stringPos = mb_stripos($row['body'], $ui->oldURL);
if ($stringPos !== false)
{
$settings = array();
$settings[] = array('Message: ', $row['id_msg']);
$settings[] = array('Old body: ', shortString($row['body'], $stringPos));
$newbody = str_ireplace($ui->oldURL, $ui->newURL, $row['body']);
$settings[] = array('New body: ', shortString($newbody, $stringPos));
$ui->dumpTable($settings);
if (!empty($_SESSION['proceed']))
{
$newbody = $smcFunc['db_escape_string']($newbody);
$sql = "UPDATE " . $db_prefix . "messages SET body = '" . $newbody
. "' WHERE id_msg = '" . $row['id_msg'] . "';";
$smcFunc['db_query']('', $sql);
}
}
$stringPos = mb_stripos($row['subject'], $ui->oldURL);
if ($stringPos !== false)
{
$settings = array();
$settings[] = array('Message: ', $row['id_msg']);
$settings[] = array('Old subject: ', shortString($row['subject'], $stringPos));
$newsubject = str_ireplace($ui->oldURL, $ui->newURL, $row['subject']);
$settings[] = array('New subject: ', shortString($newsubject, $stringPos));
$ui->dumpTable($settings);
if (!empty($_SESSION['proceed']))
{
$newsubject = $smcFunc['db_escape_string']($newsubject);
$sql = "UPDATE " . $db_prefix . "messages SET subject = '" . $newsubject
. "' WHERE id_msg = '" . $row['id_msg'] . "';";
$smcFunc['db_query']('', $sql);
}
}
}
$smcFunc['db_free_result']($result);
return;
}
//*** Do personal messages
function doPMs($ui)
{
global $smcFunc, $db_type, $db_connection, $db_prefix, $db_name;
$sql = "SELECT id_pm, subject, body FROM " . $db_prefix . "personal_messages;";
$result = $smcFunc['db_query']('', $sql);
while($row = $smcFunc['db_fetch_assoc']($result))
{
$stringPos = mb_stripos($row['body'], $ui->oldURL);
if ($stringPos !== false)
{
$settings = array();
$settings[] = array('PM: ', $row['id_pm']);
$settings[] = array('Old body: ', shortString($row['body'], $stringPos));
$newbody = str_ireplace($ui->oldURL, $ui->newURL, $row['body']);
$settings[] = array('New body: ', shortString($newbody, $stringPos));
$ui->dumpTable($settings);
if (!empty($_SESSION['proceed']))
{
$newbody = $smcFunc['db_escape_string']($newbody);
$sql = "UPDATE " . $db_prefix . "personal_messages SET body = '" . $newbody
. "' WHERE id_pm = '" . $row['id_pm'] . "';";
$smcFunc['db_query']('', $sql);
}
}
$stringPos = mb_stripos($row['subject'], $ui->oldURL);
if ($stringPos !== false)
{
$settings = array();
$settings[] = array('PM: ', $row['id_pm']);
$settings[] = array('Old subject: ', shortString($row['subject'], $stringPos));
$newsubject = str_ireplace($ui->oldURL, $ui->newURL, $row['subject']);
$settings[] = array('New subject: ', shortString($newsubject, $stringPos));
$ui->dumpTable($settings);
if (!empty($_SESSION['proceed']))
{
$newsubject = $smcFunc['db_escape_string']($newsubject);
$sql = "UPDATE " . $db_prefix . "personal_messages SET subject = '" . $newsubject
. "' WHERE id_pm = '" . $row['id_pm'] . "';";
$smcFunc['db_query']('', $sql);
}
}
}
$smcFunc['db_free_result']($result);
return;
}
//*** Do signatures
function doSignatures($ui)
{
global $smcFunc, $db_type, $db_connection, $db_prefix, $db_name;
$sql = "SELECT id_member, member_name, signature FROM " . $db_prefix . "members;";
$result = $smcFunc['db_query']('', $sql);
while($row = $smcFunc['db_fetch_assoc']($result))
{
$stringPos = mb_stripos($row['signature'], $ui->oldURL);
if ($stringPos !== false)
{
$settings = array();
$settings[] = array('Member: ', $row['id_member']);
$settings[] = array('Name: ', $row['member_name']);
$settings[] = array('Old Signature: ', shortString($row['signature'], $stringPos));
$newval = str_ireplace($ui->oldURL, $ui->newURL, $row['signature']);
$settings[] = array('New Signature: ', shortString($newval, $stringPos));
$ui->dumpTable($settings);
if (!empty($_SESSION['proceed']))
{
$newval = $smcFunc['db_escape_string']($newval);
$sql = "UPDATE " . $db_prefix . "members SET signature = '" . $newval
. "' WHERE id_member = '" . $row['id_member'] . "';";
$smcFunc['db_query']('', $sql);
}
}
}
$smcFunc['db_free_result']($result);
return;
}
//*** For display purposes, return only relevant portion of a string (first chars near 1st instance of search string)
function shortString($targetStr, $stringPos)
{
static $maxlen = 100;
static $buffer = 10;
$length = mb_strlen($targetStr);
if ($length > $maxlen)
{
if ($stringPos > $buffer)
{
$stringPos = $stringPos - $buffer;
$beforetext = '... ';
}
else
{
$stringPos = 0;
$beforetext = '';
}
if ($length - $stringPos > $maxlen)
$aftertext = ' ...';
else
$aftertext = '';
$targetStr = $beforetext . mb_substr($targetStr, $stringPos, $maxlen) . $aftertext;
}
return $targetStr;
}
$ui->go();
/**
* SimpleSmfUI
*
* A simple basic abstracted UI for utilities.
*
* Copyright 2021-2023 Shawn Bulen
*
* This file is part of the sjrbTools library.
*
* SimpleSmfUI 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.
*
* SimpleSmfUI 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 SimpleSmfUI. If not, see .
*
*/
// This oughtta hold us off until php 9.0...
#[AllowDynamicProperties]
class SimpleSmfUI
{
/*
* Properties
*/
protected $site_title = 'Simple UI';
protected $max_width = 1200;
protected $db_needed;
protected $txt = array(
'err_no_title' => 'Site title is required and must be a string!',
'err_width' => 'Funky width specified!',
'err_no_settings' => 'Could not find Settings.php! Place this file in the same folder as Settings.php.',
'err_no_db' => 'Could not establish connection with the database!',
'err_no_chunk_title' => 'Invalid chunk title!',
'err_no_chunk_func' => 'Invalid chunk function!',
'errors' => 'Errors',
);
protected $chunks = array();
protected $errors = array();
/*
* SMF Properties
*/
protected $settings_file;
/**
* Constructor
*
* Builds a SimpleSmfUI object
*
* @param string title
* @param bool db_needed
* @return void
*/
function __construct($title, $db_needed = null, $max_width = 800)
{
// Might as well try...
@set_time_limit(6000);
@ini_set('memory_limit', '512M');
// Title...
if (is_string($title))
$this->site_title = $title;
else
$this->addError('err_no_title');
// db_needed...
if (empty($db_needed))
$this->db_needed = false;
else
$this->db_needed = true;
// Width...
if (is_numeric($max_width))
$this->max_width = $max_width;
else
$this->addError('err_width');
// Error handler
// Note that php error suppression - @ - still calls the error handler. It will return 0 as it does so (pre php8).
// Note error handling in php8+ no longer fails silently on many errors, but error_reporting()
// will return 4437 (E_ERROR | E_CORE_ERROR | E_COMPILE_ERROR | E_USER_ERROR | E_RECOVERABLE_ERROR | E_PARSE)
// as it does so.
set_error_handler(
function($errno, $errstr, $errfile, $errline)
{
if ((error_reporting() != 0) && (error_reporting() != (E_ERROR | E_CORE_ERROR | E_COMPILE_ERROR | E_USER_ERROR | E_RECOVERABLE_ERROR | E_PARSE)))
$this->addError($errstr . ' (' . $errno . ')');
// Always try & report errors gracefully...
return true;
}
);
// DB...
define('SMF', 1);
define('SMF_VERSION', '2.x');
define('SMF_FULL_VERSION', 'SMF ' . SMF_VERSION);
define('SMF_SOFTWARE_YEAR', '2021');
define('POSTGRE_TITLE', 'PostgreSQL');
define('MYSQL_TITLE', 'MySQL');
define('SMF_USER_AGENT', 'Mozilla/5.0 (' . php_uname('s') . ' ' . php_uname('m') . ') AppleWebKit/605.1.15 (KHTML, like Gecko) SMF/' . strtr(SMF_VERSION, ' ', '.'));
// These must remain globals when calling SMF funcs...
global $smcFunc, $db_connection, $db_prefix, $db_name, $db_type, $sourcedir, $cachedir, $db_character_set, $db_port;
$smcFunc = array();
$this->settings_file = array();
if ($this->db_needed)
{
// Load & save off settings file contents
if (file_exists('Settings.php'))
{
include_once('Settings.php');
$dumpvars = array('mbname', 'db_server', 'db_name', 'db_prefix', 'db_type', 'db_character_set', 'db_mb4', 'language',
'boardurl', 'boarddir', 'sourcedir', 'packagesdir', 'tasksdir', 'cachedir',
'maintenance', 'mtitle', 'mmessage',
'cookiename', 'db_persist', 'db_error_send',
'cache_accelerator', 'cache_enable', 'cache_memcached',
'image_proxy_enabled', 'image_proxy_secret', 'image_proxy_maxsize');
foreach($dumpvars as $setting)
$this->settings_file[$setting] = (isset(${$setting}) ? ${$setting} : 'NOT SET');
}
else
$this->addError('err_no_settings');
if (!empty($sourcedir))
{
// Get the database going!
if (empty($db_type) || $db_type == 'mysqli')
$db_type = 'mysql';
// Add in the port if needed
$db_options = array();
if (!empty($db_port))
$db_options['port'] = $db_port;
// Make the connection...
require_once($sourcedir . '/Subs-Db-' . $db_type . '.php');
$db_connection = smf_db_initiate($db_server, $db_name, $db_user, $db_passwd, $db_prefix, $db_options);
if (empty($db_connection))
$this->addError('err_no_db');
// Set names...
if (!empty($db_character_set))
$smcFunc['db_query']('', '
SET NAMES {string:db_character_set}',
array(
'db_character_set' => $db_character_set,
)
);
}
}
}
/**
* Render chunk
*
* Display one portion of the form
*
* @return void
*/
protected function doChunk($ix, $chunk)
{
echo '';
}
/**
* Display errors
*
* Display errors in current display area
*
* @return void
*/
protected function renderErrors()
{
echo '' . $this->txt['errors'] . '
';
foreach ($this->errors AS $error)
echo $error . '
';
echo '
';
}
/**
* Render header
*
* Spits out the head, title, style & starts the body
*
* @return void
*/
protected function renderHeader()
{
echo '
' . $this->site_title . '
';
}
/**
* Render header
*
* Closes out the body & html tags
*
* @return void
*/
protected function renderFooter()
{
// Close out body & html tags
echo 'Remove when not in use
';
echo 'sbulen/sjrbTools
';
echo '
';
}
/**
* Cleanse text
*
* Some basic hygiene for user-entered input
*
* @param string input
* @param bool gtlt - whether to leave > and < alone (e.g., for queries)
* @return string cleansed
*/
public function cleanseText($input, $gtlt = false)
{
$input = trim($input);
$input = htmlspecialchars($input);
if ($gtlt)
{
$input = str_replace('>', '>', $input);
$input = str_replace('>', '>', $input);
$input = str_replace('<', '<', $input);
$input = str_replace('<', '<', $input);
}
return $input;
}
/**
* Dump table
*
* Render a simple 2-d array in table form
*
* @param array passed_array
* @return void
*/
public function dumpTable($passed_array)
{
static $special_cells = array('NOT SET', 'null', 'true', 'false');
$header = true;
echo '
';
foreach($passed_array as $row)
{
// Some cleansing...
foreach ($row AS $ix => $cell)
{
// Treat NOT SET, null, true, & false special...
if (in_array($cell, $special_cells))
$row[$ix] = $cell;
else
{
$row[$ix] = htmlspecialchars($cell);
// Undo any line breaks you just broke...
$row[$ix] = str_replace('<br>', '
', $row[$ix]);
$row[$ix] = str_replace('<br />', '
', $row[$ix]);
}
}
if ($header)
echo '
';
}
/**
* Add Chunk
*
* Adds an entry to the internal chunk array.
* Each chunk will display a header, do some logic, & display some content.
* If errors are encountered, ideally they should be added to the errors display and displayed at the end.
*
* @param string title - title to display above this chunk
* @param function logic - what to execute, passed as an anonymous function
* @return void
*/
public function addChunk($title, $func)
{
if (!is_string($title))
{
$title = '';
$this->addError('err_no_chunk_title');
}
if (!is_callable($func))
{
$func = function() {};
$this->addError('err_no_chunk_func');
}
$this->chunks[] = array('title' => $title, 'function' => $func);
}
/**
* Get Settings File contents
*
* @return array
*/
public function getSettingsFile()
{
return $this->settings_file;
}
/**
* Add Error
*
* Add error to internal log
*
* @param string key - is key to $txt array
* @param string more - is additional info to be added to output string if needed
* @return void
*/
public function addError($key, $more = '')
{
if (!is_string($key))
$key = '';
if (!is_string($more))
$more = '';
if (!empty($this->txt[$key]))
$key = $this->txt[$key];
$this->errors[] = $key . ' ' . $more;
}
/**
* Go
*
* Got everything, now do it...
*
* @return void
*/
public function go()
{
global $db_connection;
// Responding to a POST? Cleanse info, put in session and redirect
session_start();
if ($_POST)
{
$_SESSION = array();
foreach($_POST as $var => $val)
$_SESSION[$this->cleanseText($var)] = $this->cleanseText($val);
// Redirect to this page
header("Location: {$_SERVER['REQUEST_URI']}", true, 302);
exit();
}
// OK, display stuff...
$this->renderHeader();
// Execute the chunks...
// Note if db_needed & no connection, do not process chunks, just display the errors
if (!$this->db_needed || ($this->db_needed && !empty($db_connection)))
{
foreach($this->chunks AS $ix => $chunk)
$this->doChunk($ix, $chunk);
}
// Display any errors...
if (!empty($this->errors))
$this->renderErrors();
$this->renderFooter();
// Ensure refreshes actually refresh!
$_SESSION = array();
}
}