608aeb3641
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
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
- Add Dashboard as first submenu entry in component manifest - Add [DEFAULT_DIR] placeholder to PlaceholderResolver for portable profiles - Add live AJAX directory permission checking on backup_dir field changes - Add web-accessible warning badge on backup download buttons - Auto-create .htaccess protection in web-accessible backup dirs on profile save - Auto-create .htaccess protection at backup time in both engines - Add checkDir AJAX endpoint for real-time directory validation - Fix script.php warnMissingLicenseKey running on uninstall
125 lines
3.7 KiB
PHP
125 lines
3.7 KiB
PHP
<?php
|
|
|
|
/**
|
|
* @package MokoJoomBackup
|
|
* @subpackage com_mokojoombackup
|
|
* @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
|
|
*
|
|
* Resolves placeholders like [host], [date], [profile_name] in backup
|
|
* directory paths and archive filename formats.
|
|
*/
|
|
|
|
namespace Joomla\Component\MokoJoomBackup\Administrator\Engine;
|
|
|
|
defined('_JEXEC') or die;
|
|
|
|
use Joomla\CMS\Factory;
|
|
|
|
class PlaceholderResolver
|
|
{
|
|
/**
|
|
* Supported placeholders and their descriptions (for documentation).
|
|
*/
|
|
public const PLACEHOLDERS = [
|
|
'[host]' => 'Server hostname',
|
|
'[date]' => 'Date as Ymd (e.g. 20260604)',
|
|
'[time]' => 'Time as His (e.g. 143025)',
|
|
'[datetime]' => 'Date and time as Ymd_His',
|
|
'[year]' => 'Four-digit year',
|
|
'[month]' => 'Two-digit month',
|
|
'[day]' => 'Two-digit day',
|
|
'[hour]' => 'Two-digit hour (24h)',
|
|
'[minute]' => 'Two-digit minute',
|
|
'[second]' => 'Two-digit second',
|
|
'[profile_id]' => 'Backup profile ID',
|
|
'[profile_name]' => 'Profile title (sanitized)',
|
|
'[site_name]' => 'Joomla site name (sanitized)',
|
|
'[type]' => 'Backup type (full, database, files, differential)',
|
|
'[random]' => 'Random 6-character hex string',
|
|
'[DEFAULT_DIR]' => 'Default backup directory (administrator/components/com_mokojoombackup/backups)',
|
|
];
|
|
|
|
private array $replacements;
|
|
|
|
/**
|
|
* @param object $profile The backup profile object
|
|
*/
|
|
public function __construct(object $profile)
|
|
{
|
|
$now = new \DateTimeImmutable('now');
|
|
$hostname = preg_replace('/[^a-zA-Z0-9._-]/', '', $_SERVER['HTTP_HOST'] ?? $_SERVER['SERVER_NAME'] ?? php_uname('n'));
|
|
|
|
$siteName = '';
|
|
|
|
try {
|
|
$siteName = Factory::getApplication()->get('sitename', '');
|
|
} catch (\Throwable $e) {
|
|
// Fallback: not critical
|
|
}
|
|
|
|
$this->replacements = [
|
|
'[host]' => $hostname,
|
|
'[date]' => $now->format('Ymd'),
|
|
'[time]' => $now->format('His'),
|
|
'[datetime]' => $now->format('Ymd_His'),
|
|
'[year]' => $now->format('Y'),
|
|
'[month]' => $now->format('m'),
|
|
'[day]' => $now->format('d'),
|
|
'[hour]' => $now->format('H'),
|
|
'[minute]' => $now->format('i'),
|
|
'[second]' => $now->format('s'),
|
|
'[profile_id]' => (string) ($profile->id ?? '0'),
|
|
'[profile_name]' => $this->sanitize($profile->title ?? 'default'),
|
|
'[site_name]' => $this->sanitize($siteName ?: 'joomla'),
|
|
'[type]' => $profile->backup_type ?? 'full',
|
|
'[random]' => bin2hex(random_bytes(3)),
|
|
'[DEFAULT_DIR]' => JPATH_ADMINISTRATOR . '/components/com_mokojoombackup/backups',
|
|
];
|
|
}
|
|
|
|
/**
|
|
* Replace all placeholders in a string.
|
|
*
|
|
* @param string $template String containing [placeholder] tokens
|
|
*
|
|
* @return string Resolved string
|
|
*/
|
|
public function resolve(string $template): string
|
|
{
|
|
return str_replace(
|
|
array_keys($this->replacements),
|
|
array_values($this->replacements),
|
|
$template
|
|
);
|
|
}
|
|
|
|
/**
|
|
* Get the raw hostname value (for backward compatibility).
|
|
*/
|
|
public function getHostname(): string
|
|
{
|
|
return $this->replacements['[host]'];
|
|
}
|
|
|
|
/**
|
|
* Get the datetime tag value (for backward compatibility).
|
|
*/
|
|
public function getTag(): string
|
|
{
|
|
return $this->replacements['[datetime]'];
|
|
}
|
|
|
|
/**
|
|
* Sanitize a string for use in filenames/paths.
|
|
* Keeps alphanumerics, dots, hyphens, underscores. Replaces spaces with hyphens.
|
|
*/
|
|
private function sanitize(string $value): string
|
|
{
|
|
$value = str_replace(' ', '-', trim($value));
|
|
|
|
return preg_replace('/[^a-zA-Z0-9._-]/', '', $value);
|
|
}
|
|
}
|