a13f7ca6a6
Generic: Repo Health / Site Health (push) Has been cancelled
Generic: Repo Health / Access control (push) Has been cancelled
Universal: Auto Version Bump / Version Bump (push) Has been cancelled
Generic: Repo Health / Release configuration (push) Has been cancelled
Generic: Repo Health / Scripts governance (push) Has been cancelled
Generic: Repo Health / Repository health (push) Has been cancelled
Generic: Repo Health / Report Issues (push) Has been cancelled
Update all references in Makefile, manifest.xml, .gitignore, and CI workflows (ci-joomla, pr-check, repo-health) to use source/ as the primary directory with src/ as a fallback for compatibility. Authored-by: Moko Consulting Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
148 lines
5.0 KiB
PHP
148 lines
5.0 KiB
PHP
<?php
|
|
|
|
/**
|
|
* @package MokoJoomBackup
|
|
* @subpackage com_mokobackup
|
|
* @author Moko Consulting <hello@mokoconsulting.tech>
|
|
* @copyright Copyright (C) 2026 Moko Consulting. All rights reserved.
|
|
* @license GNU General Public License version 3 or later; see LICENSE
|
|
*/
|
|
|
|
namespace Joomla\Component\MokoBackup\Administrator\Field;
|
|
|
|
defined('_JEXEC') or die;
|
|
|
|
use Joomla\CMS\Factory;
|
|
use Joomla\CMS\Form\FormField;
|
|
use Joomla\CMS\Language\Text;
|
|
|
|
class DatabaseTablesField extends FormField
|
|
{
|
|
protected $type = 'DatabaseTables';
|
|
|
|
protected function getInput(): string
|
|
{
|
|
$db = Factory::getDbo();
|
|
$tables = $db->getTableList();
|
|
$prefix = $db->getPrefix();
|
|
|
|
// Parse current exclusions (newline-separated, with optional :data-only suffix)
|
|
$excludeData = [];
|
|
$excludeStructure = [];
|
|
|
|
if (!empty($this->value)) {
|
|
$lines = array_filter(array_map('trim', explode("\n", str_replace("\r", '', $this->value))));
|
|
|
|
foreach ($lines as $line) {
|
|
// Normalize table name to real prefix for comparison
|
|
if (str_ends_with($line, ':data-only')) {
|
|
$tableName = str_replace('#__', $prefix, substr($line, 0, -10));
|
|
$excludeData[$tableName] = true;
|
|
} elseif (str_ends_with($line, ':structure-only')) {
|
|
$tableName = str_replace('#__', $prefix, substr($line, 0, -15));
|
|
$excludeStructure[$tableName] = true;
|
|
} else {
|
|
// No suffix = exclude both (backward compatible)
|
|
$tableName = str_replace('#__', $prefix, $line);
|
|
$excludeData[$tableName] = true;
|
|
$excludeStructure[$tableName] = true;
|
|
}
|
|
}
|
|
}
|
|
|
|
$id = htmlspecialchars($this->id, ENT_QUOTES, 'UTF-8');
|
|
$name = htmlspecialchars($this->name, ENT_QUOTES, 'UTF-8');
|
|
|
|
$html = '<div class="mb-2">';
|
|
$html .= '<input type="hidden" name="' . $name . '" id="' . $id . '" value="" />';
|
|
$html .= '<div class="form-text mb-2">' . Text::_('COM_MOKOBACKUP_FIELD_EXCLUDE_TABLES_HELP') . '</div>';
|
|
$html .= '<div class="table-responsive" style="max-height:400px; overflow-y:auto;">';
|
|
$html .= '<table class="table table-sm table-hover mb-0">';
|
|
$html .= '<thead class="sticky-top bg-white"><tr>';
|
|
$html .= '<th class="w-1"><input type="checkbox" id="' . $id . '_toggleData" title="Toggle all data" /></th>';
|
|
$html .= '<th class="w-1">' . Text::_('COM_MOKOBACKUP_FIELD_EXCLUDE_DATA') . '</th>';
|
|
$html .= '<th class="w-1"><input type="checkbox" id="' . $id . '_toggleStructure" title="Toggle all structure" /></th>';
|
|
$html .= '<th class="w-1">' . Text::_('COM_MOKOBACKUP_FIELD_EXCLUDE_STRUCTURE') . '</th>';
|
|
$html .= '<th>' . Text::_('COM_MOKOBACKUP_FIELD_TABLE_NAME') . '</th>';
|
|
$html .= '</tr></thead><tbody>';
|
|
|
|
foreach ($tables as $table) {
|
|
$dataChecked = isset($excludeData[$table]) ? ' checked' : '';
|
|
$structureChecked = isset($excludeStructure[$table]) ? ' checked' : '';
|
|
|
|
// Convert to #__ notation for storage
|
|
$storeValue = $table;
|
|
|
|
if (str_starts_with($table, $prefix)) {
|
|
$storeValue = '#__' . substr($table, \strlen($prefix));
|
|
}
|
|
|
|
$safeValue = htmlspecialchars($storeValue, ENT_QUOTES, 'UTF-8');
|
|
$safeTable = htmlspecialchars($table, ENT_QUOTES, 'UTF-8');
|
|
|
|
$html .= '<tr>';
|
|
$html .= '<td></td>';
|
|
$html .= '<td><input type="checkbox" class="' . $id . '_data" value="' . $safeValue . '"' . $dataChecked . ' /></td>';
|
|
$html .= '<td></td>';
|
|
$html .= '<td><input type="checkbox" class="' . $id . '_structure" value="' . $safeValue . '"' . $structureChecked . ' /></td>';
|
|
$html .= '<td><code>' . $safeTable . '</code></td>';
|
|
$html .= '</tr>';
|
|
}
|
|
|
|
$html .= '</tbody></table></div></div>';
|
|
|
|
// Script to sync checkboxes to hidden field
|
|
$html .= <<<SCRIPT
|
|
<script>
|
|
(function() {
|
|
var hidden = document.getElementById('{$id}');
|
|
var dataCbs = document.querySelectorAll('.{$id}_data');
|
|
var structCbs = document.querySelectorAll('.{$id}_structure');
|
|
var toggleData = document.getElementById('{$id}_toggleData');
|
|
var toggleStructure = document.getElementById('{$id}_toggleStructure');
|
|
|
|
function sync() {
|
|
var result = {};
|
|
dataCbs.forEach(function(cb) {
|
|
if (cb.checked) result[cb.value] = (result[cb.value] || 0) | 1;
|
|
});
|
|
structCbs.forEach(function(cb) {
|
|
if (cb.checked) result[cb.value] = (result[cb.value] || 0) | 2;
|
|
});
|
|
var lines = [];
|
|
for (var table in result) {
|
|
if (result[table] === 3) {
|
|
lines.push(table);
|
|
} else if (result[table] === 1) {
|
|
lines.push(table + ':data-only');
|
|
} else if (result[table] === 2) {
|
|
lines.push(table + ':structure-only');
|
|
}
|
|
}
|
|
hidden.value = lines.join('\\n');
|
|
}
|
|
|
|
dataCbs.forEach(function(cb) { cb.addEventListener('change', sync); });
|
|
structCbs.forEach(function(cb) { cb.addEventListener('change', sync); });
|
|
|
|
toggleData.addEventListener('change', function() {
|
|
var state = this.checked;
|
|
dataCbs.forEach(function(cb) { cb.checked = state; });
|
|
sync();
|
|
});
|
|
|
|
toggleStructure.addEventListener('change', function() {
|
|
var state = this.checked;
|
|
structCbs.forEach(function(cb) { cb.checked = state; });
|
|
sync();
|
|
});
|
|
|
|
sync();
|
|
})();
|
|
</script>
|
|
SCRIPT;
|
|
|
|
return $html;
|
|
}
|
|
}
|