Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
43 commits
Select commit Hold shift + click to select a range
029e478
First draft
Squidly271 Oct 28, 2025
a3c9185
script improvements per coderabbit
Squidly271 Oct 28, 2025
4312b66
wrong files / js
Squidly271 Oct 28, 2025
5d3b4ec
Revamp and ignore files with false positives
Squidly271 Oct 28, 2025
19b3b24
Fix variables within translation strings
Squidly271 Oct 28, 2025
4bdcdc9
Tweak sprintf for multiple %s
Squidly271 Oct 28, 2025
923137c
relative paths for context
Squidly271 Oct 28, 2025
1b12c39
Change to more universal sprintf arg specifier
Squidly271 Oct 28, 2025
0a89e9d
Change quoting on sprintf
Squidly271 Oct 28, 2025
b7dd858
Missed some
Squidly271 Oct 28, 2025
36fab68
Make help text responsive
Squidly271 Oct 28, 2025
19051d6
Change inline HTML to be markdown
Squidly271 Oct 28, 2025
8f7eccb
Spelling mistakes
Squidly271 Oct 29, 2025
eb5aa36
More spelling and grammar
Squidly271 Oct 29, 2025
747c98b
More
Squidly271 Oct 29, 2025
6574f15
Add headers
Squidly271 Oct 29, 2025
fa6ec4b
Support plurals in PO and ngettext
Squidly271 Oct 29, 2025
26a56a4
handle edge cases
Squidly271 Oct 29, 2025
5ee0a4c
First draft
Squidly271 Oct 28, 2025
3da4dc9
script improvements per coderabbit
Squidly271 Oct 28, 2025
21df9f8
wrong files / js
Squidly271 Oct 28, 2025
9ca6d53
Revamp and ignore files with false positives
Squidly271 Oct 28, 2025
041cf87
Fix variables within translation strings
Squidly271 Oct 28, 2025
576e1db
Tweak sprintf for multiple %s
Squidly271 Oct 28, 2025
7b78608
relative paths for context
Squidly271 Oct 28, 2025
81142f5
Change to more universal sprintf arg specifier
Squidly271 Oct 28, 2025
970b60d
Change quoting on sprintf
Squidly271 Oct 28, 2025
c1b91cd
Missed some
Squidly271 Oct 28, 2025
4cda58c
Make help text responsive
Squidly271 Oct 28, 2025
b3af9b5
Change inline HTML to be markdown
Squidly271 Oct 28, 2025
8f6f8d4
Spelling mistakes
Squidly271 Oct 29, 2025
6356be1
More spelling and grammar
Squidly271 Oct 29, 2025
f495c80
More
Squidly271 Oct 29, 2025
69bc3fc
Add headers
Squidly271 Oct 29, 2025
27f2fec
Support plurals in PO and ngettext
Squidly271 Oct 29, 2025
8d08075
handle edge cases
Squidly271 Oct 29, 2025
c52f0aa
Add standard translations for date / time to .po
Squidly271 Oct 29, 2025
05f145c
Merge branch 'translations' of https://github.com/Squidly271/webgui i…
Squidly271 Oct 29, 2025
5fbb1f3
update
Squidly271 Oct 29, 2025
5ed0d36
Add translations for page file titles / names
Squidly271 Oct 29, 2025
e9d83fa
Merge branch 'master' into translations
Squidly271 Oct 30, 2025
15b430d
minor tweaks
Squidly271 Oct 31, 2025
7581e8a
Merge branch 'unraid:master' into translations
Squidly271 Dec 9, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
997 changes: 312 additions & 685 deletions emhttp/languages/en_US/helptext.txt
100644 → 100755

Large diffs are not rendered by default.

1 change: 1 addition & 0 deletions emhttp/plugins/dynamix.docker.manager/Docker.page
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ Code="e90b"
Lock="true"
Cond="exec(\"grep -o '^DOCKER_ENABLED=.yes' /boot/config/docker.cfg 2>/dev/null\")"
Tabs="false"
Name="Docker"
---
<?PHP
/* Copyright 2005-2023, Lime Technology
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -820,7 +820,7 @@ function loadTemplate(el) {

function rmTemplate(tmpl) {
var name = tmpl.split(/[\/]+/).pop();
swal({title:"_(Are you sure)_?",text:"_(Remove template)_: "+name,type:"warning",html:true,showCancelButton:true,confirmButtonText:"_(Proceed)_",cancelButtonText:"_(Cancel)_"},function(){$("#rmTemplate").val(tmpl);$("#formTemplate1").submit();});
swal({title:"_(Are you sure?)_",text:"_(Remove template)_: "+name,type:"warning",html:true,showCancelButton:true,confirmButtonText:"_(Proceed)_",cancelButtonText:"_(Cancel)_"},function(){$("#rmTemplate").val(tmpl);$("#formTemplate1").submit();});
}
Comment on lines 821 to 824
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

🧩 Analysis chain

Escape template name before injecting into SweetAlert HTML.

This swal uses html:true and concatenates the unescaped name into text. If tmpl/name ever contains </& etc., this can render as HTML. Encode it first.

Apply this diff:

 function rmTemplate(tmpl) {
   var name = tmpl.split(/[\/]+/).pop();
-  swal({title:"_(Are you sure?)_",text:"_(Remove template)_: "+name,type:"warning",html:true,showCancelButton:true,confirmButtonText:"_(Proceed)_",cancelButtonText:"_(Cancel)_"},function(){$("#rmTemplate").val(tmpl);$("#formTemplate1").submit();});
+  var safeName = $('<div/>').text(name).html(); // encode for HTML
+  swal({title:"_(Are you sure?)_",text:"_(Remove template)_: "+safeName,type:"warning",html:true,showCancelButton:true,confirmButtonText:"_(Proceed)_",cancelButtonText:"_(Cancel)_"},function(){$("#rmTemplate").val(tmpl);$("#formTemplate1").submit();});

Optionally, drop html:true here if no formatting is needed.

Scan for other swal calls with html:true concatenating variables:


🏁 Script executed:

#!/bin/bash
rg -nP "swal\([^)]*html\s*:\s*true" -C2

Length of output: 4008


🏁 Script executed:

#!/bin/bash
# Verify the exact location of rmTemplate in CreateDocker.php
rg -nP "function\s+rmTemplate" emhttp/plugins/dynamix.docker.manager/

Length of output: 78


🏁 Script executed:

#!/bin/bash
# Search for CreateDocker.php anywhere in the repo
fd -t f "CreateDocker.php" 2>/dev/null || echo "File not found"

Length of output: 62


🏁 Script executed:

#!/bin/bash
# Extract lines 821-824 from CreateDocker.php
sed -n '821,824p' emhttp/plugins/dynamix.docker.manager/include/CreateDocker.php

Length of output: 318


🏁 Script executed:

#!/bin/bash
# Check if CreateDocker.php includes or references addDocker.js
grep -n "addDocker\.js" emhttp/plugins/dynamix.docker.manager/include/CreateDocker.php

Length of output: 0


Escape template name before injecting into SweetAlert HTML in both locations.

The rmTemplate function concatenates an unescaped name variable into the swal text field while html:true is enabled. This creates an XSS vulnerability if tmpl/name contains HTML characters like < or &.

Apply this fix to both:

  1. emhttp/plugins/dynamix.docker.manager/include/CreateDocker.php (lines 821-824):
 function rmTemplate(tmpl) {
   var name = tmpl.split(/[\/]+/).pop();
-  swal({title:"_(Are you sure?)_",text:"_(Remove template)_: "+name,type:"warning",html:true,showCancelButton:true,confirmButtonText:"_(Proceed)_",cancelButtonText:"_(Cancel)_"},function(){$("#rmTemplate").val(tmpl);$("#formTemplate1").submit();});
+  var safeName = $('<div/>').text(name).html(); // encode for HTML
+  swal({title:"_(Are you sure?)_",text:"_(Remove template)_: "+safeName,type:"warning",html:true,showCancelButton:true,confirmButtonText:"_(Proceed)_",cancelButtonText:"_(Cancel)_"},function(){$("#rmTemplate").val(tmpl);$("#formTemplate1").submit();});
 }
  1. emhttp/plugins/dynamix.docker.manager/javascript/addDocker.js (line 17):
 function rmTemplate(tmpl) {
 	var name = tmpl.split(/[\/]+/).pop();
-	swal({title:"Are you sure?",text:"Remove template: "+name,type:"warning",html:true,showCancelButton:true},function(){$("#rmTemplate").val(tmpl);$("#formTemplate").submit();});
+	var safeName = $('<div/>').text(name).html();
+	swal({title:"Are you sure?",text:"Remove template: "+safeName,type:"warning",html:true,showCancelButton:true},function(){$("#rmTemplate").val(tmpl);$("#formTemplate").submit();});
 }
🤖 Prompt for AI Agents
emhttp/plugins/dynamix.docker.manager/include/CreateDocker.php (lines 821-824)
and emhttp/plugins/dynamix.docker.manager/javascript/addDocker.js (line 17): the
code injects the raw template name into swal text while html:true is enabled,
creating an XSS risk; update both locations to HTML-escape the name before
concatenation (replace &, <, >, " and ' with their HTML entities) or remove
html:true and pass the name via swal's text-only path so user input is not
interpreted as HTML, then use the escaped value in the swal call and submit
logic.


function openFileBrowser(el, top, root, filter, on_folders, on_files, close_on_select) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -102,7 +102,7 @@ function tailscale_json_dl($file, $url) {

function my_lang_time($text) {
[$number, $text] = my_explode(' ',$text,2);
return sprintf(_("%s $text"),$number);
return sprintf(_('%1$s %2$s'),$number, $text);
}
function my_lang_log($text) {
global $language;
Expand Down
2 changes: 1 addition & 1 deletion emhttp/plugins/dynamix.docker.manager/scripts/dockerupdate
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ if (!isset($check)) {
$new = substr($new, 0, 4).'..'.substr($new, -4, 4);
if ( ! isset($nonotify) ) {
$event = str_replace("&apos;","'",_("Docker")." - $name [$new]");
$subject = str_replace("&apos;","'",sprintf(_("Notice [%s] - Version update %s"),$server,$new));
$subject = str_replace("&apos;","'",sprintf(_('Notice [%1$s] - Version update %2$s'),$server,$new));
$description = str_replace("&apos;","'",sprintf(_("A new version of %s is available"),$name));
exec("$script -e ".escapeshellarg($event)." -s ".escapeshellarg($subject)." -d ".escapeshellarg($description)." -i ".escapeshellarg("normal $output")." -l '/Docker' -x");
}
Expand Down
1 change: 1 addition & 0 deletions emhttp/plugins/dynamix.plugin.manager/Plugins.page
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ Title="Installed Plugins"
Tag="icon-plugins"
Tabs="true"
Code="e944"
Name="Plugins"
---
<?PHP
/* Copyright 2005-2023, Lime Technology
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ foreach (glob("/$tmp/lang-*.xml", GLOB_NOSORT) as $file) {
// silently suppress bad download of XML file
if (strcmp($new, $old) > 0) {
$event = apos(_("Language")." - $lang [$new]");
$subject = apos(sprintf(_("Notice [%s] - Version update %s"),$server,$new));
$subject = apos(sprintf(_('Notice [%1$s] - Version update %2$s'),$server,$new));
$description = apos(sprintf(_("A new version of %s is available"),$lang));
exec("$script -e ".escapeshellarg($event)." -s ".escapeshellarg($subject)." -d ".escapeshellarg($description)." -i ".escapeshellarg("normal $output")." -l '/Apps' -x");
}
Expand Down
2 changes: 1 addition & 1 deletion emhttp/plugins/dynamix.plugin.manager/scripts/plugincheck
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ foreach (glob("/$tmp/*.plg", GLOB_NOSORT) as $file) {
// silently suppress bad download of PLG file
if (strcmp($new, $old)>0 && !version_compare($min,$current['version'],">")) {
$event = apos(_("Plugin")." - $name [$new]");
$subject = apos(sprintf(_("Notice [%s] - Version update %s"),$server,$new));
$subject = apos(sprintf(_('Notice [%1$s] - Version update %2$s'),$server,$new));
$description = apos(sprintf(_("A new version of %s is available"),$name));
exec("$script -e ".escapeshellarg($event)." -s ".escapeshellarg($subject)." -d ".escapeshellarg($description)." -i ".escapeshellarg("normal $output")." -l '/Plugins' -x");
}
Expand Down
14 changes: 7 additions & 7 deletions emhttp/plugins/dynamix.vm.manager/VMTemplates.page
Original file line number Diff line number Diff line change
Expand Up @@ -64,20 +64,20 @@ function removeUserTemplate(template) {
}

function confirmRemoveUserTemplate(template) {
swal({title:"_(Proceed)_?",text:"Remove user template: " + template ,type:'warning',html:true,showCancelButton:true,confirmButtonText:"_(Proceed)_",cancelButtonText:"_(Cancel)_"},function(p){if (p) removeUserTemplate(template); else refresh();});
swal({title:"_(Proceed?)_",text:"Remove user template: " + template ,type:'warning',html:true,showCancelButton:true,confirmButtonText:"_(Proceed)_",cancelButtonText:"_(Cancel)_"},function(p){if (p) removeUserTemplate(template); else refresh();});
}

function saveUserTemplateFile(name,template) {
$.post('/plugins/dynamix.vm.manager/include/VMajax.php',{action:'vm-template-save',name:name,template:template,replace:"no"},function($return){
if ($return.success == false) {
swal({title:"_(File exists)_?",text:"Replace file: " + name ,type:'warning',html:true,showCancelButton:true,confirmButtonText:"_(Proceed)_",cancelButtonText:"_(No)_"},function(p){
swal({title:"_(File exists)_",text:"Replace file: " + name ,type:'warning',html:true,showCancelButton:true,confirmButtonText:"_(Proceed)_",cancelButtonText:"_(No)_"},function(p){
if (p) {
$.post('/plugins/dynamix.vm.manager/include/VMajax.php',{action:'vm-template-save',name:name,template:template,replace:"yes"},function($return){
if ($return.success == false) swal({title:"_(Error occured)_?",text:"Action error " + name + " " + $return.error ,type:'warning',html:true,showCancelButton:true,confirmButtonText:"_(OK)_"});
if ($return.success == false) swal({title:"_(Error occurred)_",text:"Action error " + name + " " + $return.error ,type:'warning',html:true,showCancelButton:true,confirmButtonText:"_(OK)_"});
});
}
else {
if ($return.success == false) swal({title:"_(Error occured)_?",text:"Action error " + name + " " + $return.error ,type:'error',html:true,showCancelButton:true,confirmButtonText:"_(OK)_"});
if ($return.success == false) swal({title:"_(Error occurred)_",text:"Action error " + name + " " + $return.error ,type:'error',html:true,showCancelButton:true,confirmButtonText:"_(OK)_"});
};
});
}
Expand All @@ -87,15 +87,15 @@ function saveUserTemplateFile(name,template) {
function saveUserTemplateImport(name,template) {
$.post('/plugins/dynamix.vm.manager/include/VMajax.php',{action:'vm-template-import',name:name,template:template,replace:"no"},function($return){
if ($return.success == false) {
swal({title:"_(File exists)_?",text:"Replace file: " + name.split(".")[0] ,type:'warning',html:true,showCancelButton:true,confirmButtonText:"_(Proceed)_",cancelButtonText:"_(No)_"},function(p){
swal({title:"_(File exists)_",text:"Replace file: " + name.split(".")[0] ,type:'warning',html:true,showCancelButton:true,confirmButtonText:"_(Proceed)_",cancelButtonText:"_(No)_"},function(p){
if (p) {
$.post('/plugins/dynamix.vm.manager/include/VMajax.php',{action:'vm-template-save',name:name,template:template,replace:"yes"},function($return){
if ($return.success == false) swal({title:"_(Error occured)_?",text:"Action error " + name.split(".")[0] + " " + $return.error ,type:'warning',html:true,showCancelButton:true,confirmButtonText:"_(OK)_"});
if ($return.success == false) swal({title:"_(Error occurred)_?",text:"Action error " + name.split(".")[0] + " " + $return.error ,type:'warning',html:true,showCancelButton:true,confirmButtonText:"_(OK)_"});
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Fix inconsistent punctuation in error dialog title.

This line still has an external question mark after the translation wrapper, while all other "Error occurred" instances (lines 76, 80, 98) have been updated to remove it. This inconsistency should be corrected for uniform localization.

Apply this diff to fix the inconsistency:

-					if ($return.success == false) swal({title:"_(Error occurred)_?",text:"Action error " + name.split(".")[0] + " " + $return.error ,type:'warning',html:true,showCancelButton:true,confirmButtonText:"_(OK)_"});
+					if ($return.success == false) swal({title:"_(Error occurred)_",text:"Action error " + name.split(".")[0] + " " + $return.error ,type:'warning',html:true,showCancelButton:true,confirmButtonText:"_(OK)_"});
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
if ($return.success == false) swal({title:"_(Error occurred)_?",text:"Action error " + name.split(".")[0] + " " + $return.error ,type:'warning',html:true,showCancelButton:true,confirmButtonText:"_(OK)_"});
if ($return.success == false) swal({title:"_(Error occurred)_",text:"Action error " + name.split(".")[0] + " " + $return.error ,type:'warning',html:true,showCancelButton:true,confirmButtonText:"_(OK)_"});
🤖 Prompt for AI Agents
In emhttp/plugins/dynamix.vm.manager/VMTemplates.page around line 93, the swal
error dialog title uses an inconsistent external question mark after the
translation wrapper ("_(Error occurred)_?"); remove the trailing question mark
so the title matches other instances ("_(Error occurred)_"), i.e., update the
title string to use only the translation wrapper without any added punctuation.

if ($return.success == true) refresh();
});
}
else {
if ($return.success == false) swal({title:"_(Error occured)_?",text:"Action error " + name + " " + $return.error ,type:'error',html:true,showCancelButton:true,confirmButtonText:"_(OK)_"});
if ($return.success == false) swal({title:"_(Error occurred)_",text:"Action error " + name + " " + $return.error ,type:'error',html:true,showCancelButton:true,confirmButtonText:"_(OK)_"});
};
});
}
Expand Down
1 change: 1 addition & 0 deletions emhttp/plugins/dynamix.vm.manager/VMs.page
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ Code="e918"
Lock="true"
Tabs="true"
Cond="exec(\"grep -o '^SERVICE=.enable' /boot/config/domain.cfg 2>/dev/null\")"
Name="VMs"
---
<?PHP
/* Copyright 2005-2023, Lime Technology
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1839,7 +1839,7 @@ function vm_clone($vm, $clone ,$overwrite,$start,$edit, $free, $waitID, $regenma
} else {
$lastvmerror = $lv->get_last_error();
$arrResponse = ['xml' => $xml,'error' => $lastvmerror];
write("addLog\0".htmlspecialchars(_("Creating XML Error:$lastvmerror")));
write("addLog\0".htmlspecialchars(sprintf(_("Creating XML Error: %s"),$lastvmerror)));
file_put_contents("/tmp/vmclonertn.debug", json_encode($arrResponse,JSON_PRETTY_PRINT));
}

Expand Down
2 changes: 1 addition & 1 deletion emhttp/plugins/dynamix.vm.manager/scripts/VMAjaxCall.php
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,7 @@ function execCommand_nchan($command,$idx) {
$id = 1 ;
write(implode($style)."<p class='logLine'></p>");
$process = " " ;
write("<p class='logLine'></p>","addLog\0<fieldset class='docker'><legend>"._("Options for Block $action").": </legend><p class='logLine'></p><span id='wait-$waitID'>"._('Please wait')." </span></fieldset>");
write("<p class='logLine'></p>","addLog\0<fieldset class='docker'><legend>".sprintf(_("Options for Block %s"),$action).": </legend><p class='logLine'></p><span id='wait-$waitID'>"._('Please wait')." </span></fieldset>");
write("addLog\0".htmlspecialchars("VMName $name "));
write("addLog\0".htmlspecialchars("SNAP $snapshotname "));
write("addLog\0".htmlspecialchars("Base $targetbase "));
Expand Down
2 changes: 1 addition & 1 deletion emhttp/plugins/dynamix.vm.manager/scripts/VMClone.php
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,7 @@ function execCommand_nchan_clone($command,$idx,$refcmd=false) {
write(implode($style)."<p class='logLine'></p>");
$process = " " ;
$actiontxt = ucfirst($action) ;
write("<p class='logLine'></p>","addLog\0<fieldset class='docker'><legend>"._("Options for $actiontxt").": </legend><p class='logLine'></p></fieldset>");
write("<p class='logLine'></p>","addLog\0<fieldset class='docker'><legend>".sprintf(_("Options for %s"),$actiontxt).": </legend><p class='logLine'></p></fieldset>");
write("addLog\0".htmlspecialchars("Cloning $name to $clone"));

switch ($action) {
Expand Down
10 changes: 5 additions & 5 deletions emhttp/plugins/dynamix.vm.manager/templates/Custom.form.php
Original file line number Diff line number Diff line change
Expand Up @@ -488,7 +488,7 @@

<blockquote class="inline_help">
<p>
Specify the overide storage pool for VM. This option allows you to specify the physical pool/disk used to store the disk images and snapshot data.
Specify the override storage pool for VM. This option allows you to specify the physical pool/disk used to store the disk images and snapshot data.
Default will follow standard processing and store images in the default location for the share defined in the settings.
A pool/disk(Volume) will be the location for images if the default is overridden.
</p>
Expand Down Expand Up @@ -544,14 +544,14 @@
[$cpu1, $cpu2] = my_preg_split('/[,-]/',$pair);
if (!$cpu2) $corecount++; else $corecount=$corecount+2;
}
if (is_array($arrConfig['domain']['vcpu'])) {$coredisable = "disabled"; $vcpubuttontext = "Deselect all";} else {$coredisable = ""; $vcpubuttontext = "Select all";}
if (is_array($arrConfig['domain']['vcpu'])) {$coredisable = "disabled"; $vcpubuttontext = _("Deselect all");} else {$coredisable = ""; $vcpubuttontext = _("Select all");}
?>
<td><span class="advanced">_(vCPUs)_:</span></td>
<td>
<span class="width"><select id="vcpus" <?=$coredisable?> name="domain[vcpus]" class="domain_vcpus narrow">
<?for ($i = 1; $i <= ($corecount); $i++) echo mk_option($arrConfig['domain']['vcpus'], $i, $i);?>
</select>
<input type="button" value="_(<?=$vcpubuttontext?>)_" id="btnvCPUSelect"/></span>
<input type="button" value="<?=$vcpubuttontext?>" id="btnvCPUSelect"/></span>
</td>
<td></td>
</tr>
Expand Down Expand Up @@ -1465,7 +1465,7 @@
</td>
<td></td>
</tr>
<tr id="gpubootvga{{INDEX}}" class="hidden"><td>_(Graphics ROM Needed)_?:</td><td><span class="orange-text"><i class="fa fa-warning"></i> _(GPU is primary adapter, vbios may be required)_.</span></td></tr>
<tr id="gpubootvga{{INDEX}}" class="hidden"><td>_(Graphics ROM Needed)_:</td><td><span class="orange-text"><i class="fa fa-warning"></i> _(GPU is primary adapter, vbios may be required)_.</span></td></tr>
</table>
</script>

Expand Down Expand Up @@ -2583,7 +2583,7 @@ function resetForm() {
}
});

$("#vmform").on("change", ".gpu", function changeGPUEvent(){
$("#vmform").on("change", ".gpu", function changeGPUEvent(){
const ValidGPUs = <?=json_encode($arrValidGPUDevices)?>;
var myvalue = $(this).val();
var mylabel = $(this).children('option:selected').text();
Expand Down
25 changes: 13 additions & 12 deletions emhttp/plugins/dynamix/ArrayOperation.page
Original file line number Diff line number Diff line change
Expand Up @@ -86,11 +86,12 @@ function missing_cache() {
}

function resync($d) {
return in_array($d,['P','Q']) ? 'Parity-Sync' : 'Data-Rebuild';
return in_array($d,['P','Q']) ? _('Parity-Sync') : _('Data-Rebuild');
}

function print_error($error) {
return sprintf(_('Finding **%s** error'.($error==1?'':'s')),$error?:'0');
$n = (int)($error ?: 0);
return sprintf(ngettext('Finding **%d** error','Finding **%d** errors',$n),$n);
}
?>
<script>
Expand Down Expand Up @@ -290,7 +291,7 @@ function stopArray(form) {
$(form).append('<input type="hidden" name="cmdStop" value="Stop">');
<?if ($confirm['stop']):?>
swal({
title:"_(Proceed)_?",
title:"_(Proceed?)_",
text:"_(This will stop the array)_",
type:'warning',
html:true,
Expand All @@ -309,7 +310,7 @@ function stopParity(form, text) {
$(form).append('<input type="hidden" name="cmdCheckCancel" value="">');
<?if ($confirm['stop']):?>
swal({
title:"_(Proceed)_?",
title:"_(Proceed?)_",
text:"_(This will stop the running operation)_: "+text,
type:'warning',
html:true,
Expand Down Expand Up @@ -384,7 +385,7 @@ function shutdown_now(form, cmd) {
break;
}
swal({
title:"_(Proceed)_?",
title:"_(Proceed?)_",
text:text,
type:'warning',
html:true,
Expand Down Expand Up @@ -600,7 +601,7 @@ window.onunload = function(){
if (!$spot):
if ($action[0] == "recon"):
$resync = resync($action[1]);
?> <tr><td></td><td><input type="submit" name="cmdCheckSync" value="_(Sync)_"></td><td>**<?=_('Sync')?>** <?=_("will start **$resync**")?>.</td></tr>
?> <tr><td></td><td><input type="submit" name="cmdCheckSync" value="_(Sync)_"></td><td>**<?=_('Sync')?>** <?=sprintf(_("will start **%s**"),$resync)?>.</td></tr>
<? elseif ($action[0]=="clear"):?>
<tr><td></td><td><input type="submit" name="cmdCheckClear" value="_(Clear)_"></td><td>**_(Clear)_** _(will start **Disk-Clear** of new data disk(s))_.</td></tr>
<? else:
Expand Down Expand Up @@ -647,12 +648,12 @@ window.onunload = function(){
else:
if ($action[0] == "recon"):
$resync = resync($action[1]);
?> <tr><td><?=_("$resync in progress")?>.</td><td>
?> <tr><td><?=sprintf(_("%s in progress"),$resync)?>.</td><td>
<span class="buttons-spaced">
<input type="button" id="pauseButton"<?if (_var($var,'mdResync')):?> value="_(Pause)_" onclick="pauseParity(this.form)"<?else:?> value="_(Resume)_" onclick="resumeParity(this.form)"<?endif;?> disabled>
<input type="button" id="cancelButton" value="_(Cancel)_" onclick="stopParity(this.form,'<?=$resync?>')" disabled>
</span></td>
<td id="cancelText"><?if (_var($var,'mdResync')):?>**<?=_('Pause')?>** <?=_("will pause $resync")?>.<?else:?>**<?=_('Resume')?>** <?=_("will resume $resync")?>.<?endif;?><br>**<?=_('Cancel')?>** <?=_("will stop $resync")?>.
<td id="cancelText"><?if (_var($var,'mdResync')):?>**<?=_('Pause')?>** <?=sprintf(_("will pause %s"),$resync)?>.<?else:?>**<?=_('Resume')?>** <?=sprintf(_("will resume %s"),$resync)?>.<?endif;?><br>**<?=_('Cancel')?>** <?=sprintf(_("will stop %s"),$resync)?>.
<br>_(WARNING: canceling may leave the array unprotected)_!</td></tr>
<? elseif ($action[0] == "clear"):?>
<tr><td>_(Disk-Clear in progress)_.</td><td>
Expand Down Expand Up @@ -681,7 +682,7 @@ window.onunload = function(){
$sbSynced = array_shift($synced) ?: _var($var,'sbSynced');
$sbUpdate = _var($var,'mdResyncDt') ? $sbSynced : _var($var,'sbUpdated');?>
<tr><td></td><td><input type="button" value="_(History)_" onclick="parityHistory()"></td>
<td><?=sprintf(_('Current operation %s on **%s**'),(_var($var,'mdResyncDt')?_('started'):_('paused')),_(my_time($sbUpdate).day_count($sbUpdate),0))?></td></tr>
<td><?=(_('Current operation %1$s on **%2$s**'),(_var($var,'mdResyncDt')?_('started'):_('paused')),_(my_time($sbUpdate).day_count($sbUpdate),0))?></td></tr>
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

Missing sprintf call causes placeholder substitution failure.

Line 685 is missing the sprintf call before the translation function, which means the placeholders %1$s and %2$s will not be substituted with the actual values. This will result in the raw translation string with placeholders being displayed to users.

Apply this diff to fix the issue:

-      <td><?=(_('Current operation %1$s on **%2$s**'),(_var($var,'mdResyncDt')?_('started'):_('paused')),_(my_time($sbUpdate).day_count($sbUpdate),0))?></td></tr>
+      <td><?=sprintf(_('Current operation %1$s on **%2$s**'),(_var($var,'mdResyncDt')?_('started'):_('paused')),_(my_time($sbUpdate).day_count($sbUpdate),0))?></td></tr>
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
<td><?=(_('Current operation %1$s on **%2$s**'),(_var($var,'mdResyncDt')?_('started'):_('paused')),_(my_time($sbUpdate).day_count($sbUpdate),0))?></td></tr>
<td><?=sprintf(_('Current operation %1$s on **%2$s**'),(_var($var,'mdResyncDt')?_('started'):_('paused')),_(my_time($sbUpdate).day_count($sbUpdate),0))?></td></tr>
🤖 Prompt for AI Agents
emhttp/plugins/dynamix/ArrayOperation.page around line 685: the translation call
is passing placeholders but not using sprintf, so %1$s and %2$s are not being
substituted; wrap the translated string in sprintf and pass the two computed
values (the started/paused status and the formatted date string) as arguments to
sprintf so the placeholders are replaced correctly.

<tr><td>_(Total size)_:</td><td id="line0"></td><td></td></tr>
<tr><td>_(Elapsed time)_:</td><td id="line1"></td><td></td></tr>
<tr><td>_(Current position)_:</td><td id="line2"></td><td></td></tr>
Expand All @@ -707,12 +708,12 @@ window.onunload = function(){
case "Copying":?>
<tr><td><?status_indicator()?>**_(Starting, copying)_... <span id="fsState"></span>**</td><td><input type="submit" name="cmdNoCopy" value="_(Cancel)_"></td><td></td></tr>
<tr><td></td><td><input type="button" value="_(History)_" onclick="parityHistory()"></td>
<td><?=sprintf(_('Current operation %s on **%s**'),_('started'),_(my_time(_var($var,'sbUpdated',0)).day_count(_var($var,'sbSynced',0)),0))?></td></tr>
<td><?=sprintf(_('Current operation started on **%s**'),_(my_time(_var($var,'sbUpdated',0)).day_count(_var($var,'sbUpdated',0)),0))?></td></tr>
<? break;
case "Clearing":?>
<tr><td><?status_indicator()?>**_(Started, clearing)_... <span id="fsState"></span>**</td><td><input type="submit" name="cmdNoClear" value="_(Cancel)_"></td><td></td></tr>
<tr><td></td><td><input type="button" value="_(History)_" onclick="parityHistory()"></td>
<td><?=sprintf(_('Current operation %s on **%s**'),_('started'),_(my_time(_var($var,'sbUpdated',0)).day_count(_var($var,'sbSynced',0)),0))?></td></tr>
<td><?=sprintf(_('Current operation started on **%s**'),_(my_time(_var($var,'sbUpdated',0)).day_count(_var($var,'sbUpdated',0)),0))?></td></tr>
<? break;
case "Stopping":?>
<tr><td><?status_indicator()?>**_(Stopping)_...**</td><td><input type="submit" name="cmdStop" value="_(Stop)_" disabled></td><td></td></tr>
Expand Down Expand Up @@ -746,7 +747,7 @@ window.onunload = function(){
if ($action[0] == "recon"):
$resync = resync($action[1]);
?> <tr><td><?status_indicator()?>**_(Stopped)_**. _(Configuration valid)_.</td><td><input type="button" id="cmdStart" value="_(Start)_" onclick="prepareInput(this.form,this)"></td>
<td>**<?=_('Start')?>** <?=_("will bring the array on-line and start **$resync**")?>.</td></tr>
<td>**<?=_('Start')?>** <?=sprintf(_("will bring the array on-line and start **%s**"),$resync)?>.</td></tr>
<? elseif ($action[0] == "clear"):?>
<tr><td><?status_indicator()?>**_(Stopped)_**. _(New data disk(s) detected)_.</td><td><input type="button" id="cmdStart" value="_(Start)_" onclick="prepareInput(this.form,this)"></td>
<td>**_(Start)_** _(will bring the array on-line and start **Disk-Clear** of new data disk(s))_.</td></tr>
Expand Down
Loading
Loading