mirror of
https://github.com/unraid/api.git
synced 2026-01-06 08:39:54 -06:00
fix: missing translations for expiring trials (#1800)
- Removed translation function calls from the UI components for reboot
type text, replacing them with direct references to the computed
properties.
- Enhanced ineligible update messages by integrating localization for
various conditions, ensuring clearer user feedback regarding update
eligibility.
- Added new localization strings for ineligible update scenarios in the
English locale file.
<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit
* **New Features**
* Added new localization keys for OS update eligibility, reboot labels,
changelog link, and expanded uptime/trial expiry messages.
* **Bug Fixes**
* Restored translated strings and added locale-aware release date
formatting for update/ineligible messaging and badges.
* **Theme & UI**
* Streamlined theme initialization and server-driven theme application;
removed legacy CSS-variable persistence and adjusted dark/banner
behavior.
* **Tests**
* Added i18n and date/locale formatting tests and improved
local-storage-like test mocks.
* **Chores**
* Removed an auto-registered global component and strengthened
script/theme initialization and CSS-variable validation.
<sub>✏️ Tip: You can customize this high-level summary in your review
settings.</sub>
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
This commit is contained in:
@@ -120,10 +120,14 @@ class ExtractorTest {
|
||||
file_put_contents($this->testDir . '/extractor.php', $extractorContent);
|
||||
}
|
||||
|
||||
private function getExtractorOutput() {
|
||||
private function getExtractorOutput($resetStatic = false) {
|
||||
$_SERVER['DOCUMENT_ROOT'] = '/usr/local/emhttp';
|
||||
require_once $this->testDir . '/extractor.php';
|
||||
|
||||
if ($resetStatic && class_exists('WebComponentsExtractor')) {
|
||||
WebComponentsExtractor::resetScriptsOutput();
|
||||
}
|
||||
|
||||
$extractor = WebComponentsExtractor::getInstance();
|
||||
return $extractor->getScriptTagHtml();
|
||||
}
|
||||
@@ -299,6 +303,11 @@ class ExtractorTest {
|
||||
preg_match('/<link[^>]+id="unraid-[^"]*-css-[^"]+"[^>]+data-unraid="1"/', $output) > 0
|
||||
);
|
||||
|
||||
// Test: CSS Variable Validation
|
||||
echo "\nTest: CSS Variable Validation\n";
|
||||
echo "------------------------------\n";
|
||||
$this->testCssVariableValidation();
|
||||
|
||||
// Test: Duplicate Prevention
|
||||
echo "\nTest: Duplicate Prevention\n";
|
||||
echo "---------------------------\n";
|
||||
@@ -317,6 +326,114 @@ class ExtractorTest {
|
||||
);
|
||||
}
|
||||
|
||||
private function testCssVariableValidation() {
|
||||
$_SERVER['DOCUMENT_ROOT'] = '/usr/local/emhttp';
|
||||
require_once $this->testDir . '/extractor.php';
|
||||
|
||||
$extractor = WebComponentsExtractor::getInstance();
|
||||
$reflection = new ReflectionClass('WebComponentsExtractor');
|
||||
$method = $reflection->getMethod('renderThemeVars');
|
||||
$method->setAccessible(true);
|
||||
|
||||
// Test valid CSS variable names
|
||||
$validVars = [
|
||||
'--header-text-primary' => '#ffffff',
|
||||
'--header-text-secondary' => '#cccccc',
|
||||
'--header-background-color' => '#000000',
|
||||
'--test-var' => 'value',
|
||||
'--test_var' => 'value',
|
||||
'--test123' => 'value',
|
||||
'--A-Z_a-z0-9' => 'value',
|
||||
];
|
||||
$output = $method->invoke($extractor, $validVars, 'test');
|
||||
$this->test(
|
||||
"Accepts valid CSS variable names starting with --",
|
||||
strpos($output, '--header-text-primary') !== false &&
|
||||
strpos($output, '--test-var') !== false &&
|
||||
strpos($output, '--test_var') !== false &&
|
||||
strpos($output, '--test123') !== false
|
||||
);
|
||||
|
||||
// Test invalid CSS variable names (should be rejected)
|
||||
$invalidVars = [
|
||||
'not-a-var' => 'value',
|
||||
'-not-a-var' => 'value',
|
||||
'--var with spaces' => 'value',
|
||||
'--var<script>' => 'value',
|
||||
'--var"quote' => 'value',
|
||||
'--var\'quote' => 'value',
|
||||
'--var;injection' => 'value',
|
||||
'--var:colon' => 'value',
|
||||
'--var.value' => 'value',
|
||||
'--var/value' => 'value',
|
||||
'--var\\backslash' => 'value',
|
||||
'' => 'value',
|
||||
'--' => 'value',
|
||||
];
|
||||
$output = $method->invoke($extractor, $invalidVars, 'test');
|
||||
$this->test(
|
||||
"Rejects CSS variable names without -- prefix",
|
||||
strpos($output, 'not-a-var') === false &&
|
||||
strpos($output, '-not-a-var') === false
|
||||
);
|
||||
$this->test(
|
||||
"Rejects CSS variable names with spaces",
|
||||
strpos($output, 'var with spaces') === false
|
||||
);
|
||||
$this->test(
|
||||
"Rejects CSS variable names with script tags",
|
||||
strpos($output, '<script>') === false &&
|
||||
strpos($output, 'var<script>') === false
|
||||
);
|
||||
$this->test(
|
||||
"Rejects CSS variable names with quotes",
|
||||
strpos($output, 'var"quote') === false &&
|
||||
strpos($output, "var'quote") === false
|
||||
);
|
||||
$this->test(
|
||||
"Rejects CSS variable names with semicolons",
|
||||
strpos($output, 'var;injection') === false
|
||||
);
|
||||
$this->test(
|
||||
"Rejects CSS variable names with dots",
|
||||
strpos($output, 'var.value') === false
|
||||
);
|
||||
$this->test(
|
||||
"Rejects empty or minimal invalid keys",
|
||||
strpos($output, ': --;') === false
|
||||
);
|
||||
|
||||
// Test mixed valid and invalid (only valid should appear)
|
||||
$mixedVars = [
|
||||
'--valid-var' => 'value1',
|
||||
'invalid-var' => 'value2',
|
||||
'--another-valid' => 'value3',
|
||||
'--invalid<script>' => 'value4',
|
||||
];
|
||||
$output = $method->invoke($extractor, $mixedVars, 'test');
|
||||
$this->test(
|
||||
"Accepts valid variables and rejects invalid ones in mixed input",
|
||||
strpos($output, '--valid-var') !== false &&
|
||||
strpos($output, '--another-valid') !== false &&
|
||||
strpos($output, 'invalid-var') === false &&
|
||||
strpos($output, '<script>') === false
|
||||
);
|
||||
|
||||
// Test non-string keys (should be rejected)
|
||||
$nonStringKeys = [
|
||||
'--valid' => 'value',
|
||||
123 => 'value',
|
||||
true => 'value',
|
||||
null => 'value',
|
||||
];
|
||||
$output = $method->invoke($extractor, $nonStringKeys, 'test');
|
||||
$this->test(
|
||||
"Rejects non-string keys",
|
||||
strpos($output, '--valid') !== false &&
|
||||
strpos($output, '123') === false
|
||||
);
|
||||
}
|
||||
|
||||
private function test($name, $condition) {
|
||||
if ($condition) {
|
||||
echo " " . self::GREEN . "✓" . self::NC . " " . $name . "\n";
|
||||
@@ -352,6 +469,19 @@ class ExtractorTest {
|
||||
return preg_replace('/[^a-zA-Z0-9-]/', '-', $input);
|
||||
}
|
||||
|
||||
private function resetExtractor() {
|
||||
// Reset singleton instance
|
||||
if (class_exists('WebComponentsExtractor')) {
|
||||
$reflection = new ReflectionClass('WebComponentsExtractor');
|
||||
$instance = $reflection->getProperty('instance');
|
||||
$instance->setAccessible(true);
|
||||
$instance->setValue(null, null);
|
||||
|
||||
// Reset static flag
|
||||
WebComponentsExtractor::resetScriptsOutput();
|
||||
}
|
||||
}
|
||||
|
||||
private function reportResults() {
|
||||
echo "\n";
|
||||
echo "========================================\n";
|
||||
|
||||
Reference in New Issue
Block a user