From ce082c3edc70539cc7c7823cf9b8c9a465ca7b6d Mon Sep 17 00:00:00 2001 From: JJ Fullmer <7743340+darksidemilk@users.noreply.github.com> Date: Wed, 19 Aug 2026 13:28:47 -0500 Subject: [PATCH] Say why imaging failed, not just that it did TaskError has been sending ImageName and Reason on HOST_IMAGE_FAIL since the FOS reporting work landed -- Reason being the flattened, MAX_REASON-bounded opening of whatever FOS actually reported. Both bundled listeners ignored all of it and pushed the fixed string "This host has failed to image", which tells an admin nothing the task list did not already show. Confirmed live on 1.6 before this: a report whose stored row read "fog.download: failed to restore partition 2 / partclone.ntfs: /dev/sda2 is busy / ..." produced a push saying only "Failed". So the whole point of storing the trace -- somebody seeing it -- stopped at the server. Host lab01 failed imaging Win11-Lab: fog.download: failed to restore partition 2 partclone.ntfs: /dev/sda2 is busy ... Every added key is read defensively. These events fire only when something has already gone wrong, and a web tree can be older than what writes the payload, so a bare $data['Reason'] would turn the notification into a PHP warning at the worst possible moment. Both keys fall back to a translated placeholder rather than an empty slot, so an older server says "failed imaging an unnamed image: no reason was reported" instead of "failed imaging : ". The substitution happens OUTSIDE _(), with positional specifiers. A msgid built at runtime matches no catalog entry and never translates, silently and permanently; %1$s so a translator can reorder the sentence. Slack's old form had 'Host: %s ' outside the call and translated only the tail, which is not a sentence anyone could translate -- that is now one whole msgid. Ported from FOGProject/fog-plugins#21, which did the same for the 1.6 line where these plugins now live. ntfy has no listener on this branch, so this is two files rather than three. tests/imaging-failure-reason.test.php pins the reads, the defaults, the fallbacks and the gettext shape -- source-level, because these classes extend the plugin's Event base, which extends FOG's, so neither loads without a booted FOG, a session and a database. Six mutations, all killed, each confirmed to have actually applied: stop reading Reason -> never reads Reason read Reason with no default -> read without a default interpolate inside _() -> msgid built at runtime drop the missing-reason default -> renders with an empty slot bare %s instead of %1$s -> translator cannot reorder stop naming the image -> never names the image Full suite: 15 passed, 0 failed. Co-Authored-By: Claude --- packages/web/lib/fog/system.class.php | 2 +- .../events/imagefail_pushbullet.event.php | 28 ++++- .../slack/events/imagefail_slack.event.php | 21 +++- .../de_DE.UTF-8/LC_MESSAGES/messages.po | 23 +++- .../en_US.UTF-8/LC_MESSAGES/messages.po | 23 +++- .../es_ES.UTF-8/LC_MESSAGES/messages.po | 23 +++- .../fr_FR.UTF-8/LC_MESSAGES/messages.po | 23 +++- .../it_IT.UTF-8/LC_MESSAGES/messages.po | 23 +++- .../ja_JP.UTF-8/LC_MESSAGES/messages.po | 22 +++- .../web/management/languages/messages.pot | 20 ++- .../pt_BR.UTF-8/LC_MESSAGES/messages.po | 22 +++- .../zh_CN.UTF-8/LC_MESSAGES/messages.po | 22 +++- tests/imaging-failure-reason.test.php | 118 ++++++++++++++++++ 13 files changed, 333 insertions(+), 37 deletions(-) create mode 100644 tests/imaging-failure-reason.test.php diff --git a/packages/web/lib/fog/system.class.php b/packages/web/lib/fog/system.class.php index 0c254cc285..be592f526d 100644 --- a/packages/web/lib/fog/system.class.php +++ b/packages/web/lib/fog/system.class.php @@ -53,7 +53,7 @@ private static function _versionCompare() public function __construct() { self::_versionCompare(); - define('FOG_VERSION', '1.5.10.2294'); + define('FOG_VERSION', '1.5.10.2301'); define('FOG_SCHEMA', 282); define('FOG_BCACHE_VER', 143); define('FOG_CLIENT_VERSION', '0.13.0'); diff --git a/packages/web/lib/plugins/pushbullet/events/imagefail_pushbullet.event.php b/packages/web/lib/plugins/pushbullet/events/imagefail_pushbullet.event.php index 05c43875c9..02b51264c0 100755 --- a/packages/web/lib/plugins/pushbullet/events/imagefail_pushbullet.event.php +++ b/packages/web/lib/plugins/pushbullet/events/imagefail_pushbullet.event.php @@ -63,8 +63,32 @@ public function __construct() */ public function onEvent($event, $data) { - self::$message = 'This host has failed to image'; - self::$shortdesc = 'Failed'; + // TaskError sends the image and the reason FOS gave, and the reason is + // the only part of this an admin can act on -- "failed to image" says + // nothing the task list did not already show. Read defensively: a + // server whose web tree predates that change sends HostName alone, and + // reading a missing key would turn the notification into a PHP warning + // in an event that only fires when something has already gone wrong. + $image = (string) ($data['ImageName'] ?? ''); + if ('' === $image) { + $image = _('an unnamed image'); + } + $reason = (string) ($data['Reason'] ?? ''); + if ('' === $reason) { + $reason = _('no reason was reported'); + } + self::$shortdesc = _('Imaging Failed'); + // Composed here rather than handed to the base class as a bare + // literal, because the substitution has to happen AFTER translation: + // a msgid built at runtime matches no catalog entry and never + // translates. The base's _() then finds nothing and passes this + // through unchanged. Positional specifiers so a translator can + // reorder the sentence. + self::$message = sprintf( + _('This host failed imaging %1$s: %2$s'), + $image, + $reason + ); parent::onEvent($event, $data); } } diff --git a/packages/web/lib/plugins/slack/events/imagefail_slack.event.php b/packages/web/lib/plugins/slack/events/imagefail_slack.event.php index 1382cbb1b1..b8dbd886f6 100755 --- a/packages/web/lib/plugins/slack/events/imagefail_slack.event.php +++ b/packages/web/lib/plugins/slack/events/imagefail_slack.event.php @@ -62,15 +62,32 @@ public function __construct() */ public function onEvent($event, $data) { + // See the pushbullet listener: the reason is the actionable half of + // this notification, and every added key is read defensively so the + // plugin keeps working against a web tree that still sends HostName + // alone. + $image = (string) ($data['ImageName'] ?? ''); + if ('' === $image) { + $image = _('an unnamed image'); + } + $reason = (string) ($data['Reason'] ?? ''); + if ('' === $reason) { + $reason = _('no reason was reported'); + } foreach ((array)self::getClass('SlackManager') ->find() as &$Token ) { $args = array( 'channel' => $Token->get('name'), + // Whole sentence inside _() with positional specifiers: the + // old form put 'Host: %s ' outside the call and translated + // the tail on its own, which is not a sentence a translator + // can work with. 'text' => sprintf( - 'Host: %s %s', + _('Host %1$s failed imaging %2$s: %3$s'), $data['HostName'], - _('imaging failed.') + $image, + $reason ) ); $Token->call('chat.postMessage', $args); diff --git a/packages/web/management/languages/de_DE.UTF-8/LC_MESSAGES/messages.po b/packages/web/management/languages/de_DE.UTF-8/LC_MESSAGES/messages.po index a6a2f91898..7cfabdc255 100644 --- a/packages/web/management/languages/de_DE.UTF-8/LC_MESSAGES/messages.po +++ b/packages/web/management/languages/de_DE.UTF-8/LC_MESSAGES/messages.po @@ -2536,6 +2536,10 @@ msgstr "Telefonnummer" msgid "Host" msgstr "Host" +#, php-format +msgid "Host %1$s failed imaging %2$s: %3$s" +msgstr "" + #, fuzzy msgid "Host AD Domain" msgstr "Host AD-Domäne" @@ -3075,6 +3079,10 @@ msgstr "Image Task vollständig" msgid "Imaging Duration" msgstr "Imaging Log" +#, fuzzy +msgid "Imaging Failed" +msgstr "Laden fehlgeschlagen: %s" + msgid "Imaging Log" msgstr "Imaging Log" @@ -6595,6 +6603,10 @@ msgstr "Diese Datei funktioniert nur auf Windows" msgid "This host already exists" msgstr "Dieser Host ist bereits vorhanden." +#, fuzzy, php-format +msgid "This host failed imaging %1$s: %2$s" +msgstr "Dieser Host ist bereits vorhanden." + msgid "This installation process may take a few minutes" msgstr "Der Installationsprozess kann ein paar Minuten dauern, " @@ -7345,6 +7357,10 @@ msgstr "vor" msgid "all current storage nodes" msgstr "momentanen Speicherknoten löschen." +#, fuzzy +msgid "an unnamed image" +msgstr "Ungültiges Image" + msgid "and Mac OS X" msgstr "und Mac OS X" @@ -7696,10 +7712,6 @@ msgstr "Images" msgid "images to a storage group" msgstr "bildet ab zu einer Speichergruppe" -#, fuzzy -msgid "imaging failed." -msgstr "Laden fehlgeschlagen: %s" - msgid "in seconds" msgstr "in Sekunden" @@ -7845,6 +7857,9 @@ msgstr "nicht verfügbar" msgid "no login will appear" msgstr "wird kein Login erscheinen." +msgid "no reason was reported" +msgstr "" + msgid "node is the distributor of the images" msgstr "welcher Knoten die Images verteilt." diff --git a/packages/web/management/languages/en_US.UTF-8/LC_MESSAGES/messages.po b/packages/web/management/languages/en_US.UTF-8/LC_MESSAGES/messages.po index 23c8b0b3e7..4054d58a7d 100644 --- a/packages/web/management/languages/en_US.UTF-8/LC_MESSAGES/messages.po +++ b/packages/web/management/languages/en_US.UTF-8/LC_MESSAGES/messages.po @@ -2538,6 +2538,10 @@ msgstr "Home Phone" msgid "Host" msgstr "Host" +#, php-format +msgid "Host %1$s failed imaging %2$s: %3$s" +msgstr "" + #, fuzzy msgid "Host AD Domain" msgstr "AD Domain" @@ -3076,6 +3080,10 @@ msgstr "after task completion" msgid "Imaging Duration" msgstr "Imaging Log" +#, fuzzy +msgid "Imaging Failed" +msgstr "Load failed: %s" + msgid "Imaging Log" msgstr "Imaging Log" @@ -6593,6 +6601,10 @@ msgstr "" msgid "This host already exists" msgstr "Printer already exists" +#, fuzzy, php-format +msgid "This host failed imaging %1$s: %2$s" +msgstr "Printer already exists" + msgid "This installation process may take a few minutes" msgstr "" @@ -7342,6 +7354,10 @@ msgstr " ago" msgid "all current storage nodes" msgstr "Invalid storage node" +#, fuzzy +msgid "an unnamed image" +msgstr "Invalid image" + msgid "and Mac OS X" msgstr "" @@ -7693,10 +7709,6 @@ msgstr "Images" msgid "images to a storage group" msgstr "Primary Storage Group" -#, fuzzy -msgid "imaging failed." -msgstr "Load failed: %s" - msgid "in seconds" msgstr "" @@ -7841,6 +7853,9 @@ msgstr "n/a" msgid "no login will appear" msgstr "" +msgid "no reason was reported" +msgstr "" + msgid "node is the distributor of the images" msgstr "" diff --git a/packages/web/management/languages/es_ES.UTF-8/LC_MESSAGES/messages.po b/packages/web/management/languages/es_ES.UTF-8/LC_MESSAGES/messages.po index fd8f05f67d..11853b988d 100755 --- a/packages/web/management/languages/es_ES.UTF-8/LC_MESSAGES/messages.po +++ b/packages/web/management/languages/es_ES.UTF-8/LC_MESSAGES/messages.po @@ -2530,6 +2530,10 @@ msgstr "" msgid "Host" msgstr "Equipo" +#, php-format +msgid "Host %1$s failed imaging %2$s: %3$s" +msgstr "" + #, fuzzy msgid "Host AD Domain" msgstr "Gestión de equipos" @@ -3097,6 +3101,10 @@ msgstr "Imagen" msgid "Imaging Duration" msgstr "Descripción" +#, fuzzy +msgid "Imaging Failed" +msgstr "Error al cargar" + msgid "Imaging Log" msgstr "" @@ -6578,6 +6586,10 @@ msgstr "" msgid "This host already exists" msgstr "Ya existe este equipo" +#, fuzzy, php-format +msgid "This host failed imaging %1$s: %2$s" +msgstr "Ya existe este equipo" + msgid "This installation process may take a few minutes" msgstr "" @@ -7294,6 +7306,10 @@ msgstr "" msgid "all current storage nodes" msgstr "todos los Nodos de Almacenamiento" +#, fuzzy +msgid "an unnamed image" +msgstr "Imagen inválida" + msgid "and Mac OS X" msgstr "" @@ -7645,10 +7661,6 @@ msgstr "Imágenes" msgid "images to a storage group" msgstr "Añadir grupo de almacenamiento" -#, fuzzy -msgid "imaging failed." -msgstr "Error al cargar" - msgid "in seconds" msgstr "" @@ -7789,6 +7801,9 @@ msgstr "" msgid "no login will appear" msgstr "Gestión de login" +msgid "no reason was reported" +msgstr "" + msgid "node is the distributor of the images" msgstr "exportar imágenes" diff --git a/packages/web/management/languages/fr_FR.UTF-8/LC_MESSAGES/messages.po b/packages/web/management/languages/fr_FR.UTF-8/LC_MESSAGES/messages.po index 7c22664610..0a1d71eba2 100644 --- a/packages/web/management/languages/fr_FR.UTF-8/LC_MESSAGES/messages.po +++ b/packages/web/management/languages/fr_FR.UTF-8/LC_MESSAGES/messages.po @@ -2247,6 +2247,10 @@ msgstr "Téléphone fixe" msgid "Host" msgstr "Machine" +#, php-format +msgid "Host %1$s failed imaging %2$s: %3$s" +msgstr "" + msgid "Host AD Domain" msgstr "Domaine AD machine" @@ -2722,6 +2726,10 @@ msgstr "Clonage terminé" msgid "Imaging Duration" msgstr "Durée du clonage" +#, fuzzy +msgid "Imaging Failed" +msgstr "La connexion a échoué" + msgid "Imaging Log" msgstr "Journal des clonages" @@ -5825,6 +5833,10 @@ msgstr "Ce fichier ne fonctionnera que sous Windows" msgid "This host already exists" msgstr "Cette machine existe déjà" +#, fuzzy, php-format +msgid "This host failed imaging %1$s: %2$s" +msgstr "Cette machine existe déjà" + msgid "This installation process may take a few minutes" msgstr "Ce processus d'installation peut prendre quelques minutes" @@ -6482,6 +6494,10 @@ msgstr "depuis" msgid "all current storage nodes" msgstr "tous les nœuds de stockage actuels" +#, fuzzy +msgid "an unnamed image" +msgstr "Image invalide" + msgid "and Mac OS X" msgstr "and MacOS X" @@ -6791,10 +6807,6 @@ msgstr "images" msgid "images to a storage group" msgstr "images vers un groupe de stockage" -#, fuzzy -msgid "imaging failed." -msgstr "La connexion a échoué" - msgid "in seconds" msgstr "en secondes" @@ -6922,6 +6934,9 @@ msgstr "n/a" msgid "no login will appear" msgstr "aucun login n'apparaîtra" +msgid "no reason was reported" +msgstr "" + msgid "node is the distributor of the images" msgstr "nœud est le distributeur des images" diff --git a/packages/web/management/languages/it_IT.UTF-8/LC_MESSAGES/messages.po b/packages/web/management/languages/it_IT.UTF-8/LC_MESSAGES/messages.po index 18b698663b..91b21d0bf8 100644 --- a/packages/web/management/languages/it_IT.UTF-8/LC_MESSAGES/messages.po +++ b/packages/web/management/languages/it_IT.UTF-8/LC_MESSAGES/messages.po @@ -2305,6 +2305,10 @@ msgstr "Telefono di casa" msgid "Host" msgstr "Host" +#, php-format +msgid "Host %1$s failed imaging %2$s: %3$s" +msgstr "" + msgid "Host AD Domain" msgstr "Host AD Dominio" @@ -2786,6 +2790,10 @@ msgstr "Attività di immagine completata" msgid "Imaging Duration" msgstr "Imaging Log" +#, fuzzy +msgid "Imaging Failed" +msgstr "Caricamento non riuscito" + msgid "Imaging Log" msgstr "Imaging Log" @@ -5947,6 +5955,10 @@ msgstr "Questo file funziona solo in Windows" msgid "This host already exists" msgstr "Questo host esiste già" +#, fuzzy, php-format +msgid "This host failed imaging %1$s: %2$s" +msgstr "Questo host esiste già" + msgid "This installation process may take a few minutes" msgstr "Questo processo di installazione impiegherà alcuni minuti" @@ -6617,6 +6629,10 @@ msgstr "fa" msgid "all current storage nodes" msgstr "tutti i nodi di archiviazione attivi" +#, fuzzy +msgid "an unnamed image" +msgstr "immagine non valido" + msgid "and Mac OS X" msgstr "e Mac OS X" @@ -6933,10 +6949,6 @@ msgstr "immagini" msgid "images to a storage group" msgstr "Immagini in un gruppo di archiviazione" -#, fuzzy -msgid "imaging failed." -msgstr "Caricamento non riuscito" - msgid "in seconds" msgstr "in secondi" @@ -7067,6 +7079,9 @@ msgstr "n/d" msgid "no login will appear" msgstr "nessun login appare" +msgid "no reason was reported" +msgstr "" + msgid "node is the distributor of the images" msgstr "nodo è il distributore delle immagini" diff --git a/packages/web/management/languages/ja_JP.UTF-8/LC_MESSAGES/messages.po b/packages/web/management/languages/ja_JP.UTF-8/LC_MESSAGES/messages.po index 1e50fbdd95..4971d455a3 100644 --- a/packages/web/management/languages/ja_JP.UTF-8/LC_MESSAGES/messages.po +++ b/packages/web/management/languages/ja_JP.UTF-8/LC_MESSAGES/messages.po @@ -2222,6 +2222,10 @@ msgstr "自宅電話番号" msgid "Host" msgstr "ホスト" +#, php-format +msgid "Host %1$s failed imaging %2$s: %3$s" +msgstr "" + msgid "Host AD Domain" msgstr "ホスト AD ドメイン" @@ -2695,6 +2699,10 @@ msgstr "イメージ処理が完了しました" msgid "Imaging Duration" msgstr "イメージ処理時間" +#, fuzzy +msgid "Imaging Failed" +msgstr "イメージ処理に失敗しました。" + msgid "Imaging Log" msgstr "イメージログ" @@ -5769,6 +5777,10 @@ msgstr "このファイルは Windows でのみ使用できます" msgid "This host already exists" msgstr "このホストは既に存在します" +#, fuzzy, php-format +msgid "This host failed imaging %1$s: %2$s" +msgstr "このホストは既に存在します" + msgid "This installation process may take a few minutes" msgstr "このインストール処理には数分かかる場合があります" @@ -6419,6 +6431,10 @@ msgstr "前" msgid "all current storage nodes" msgstr "現在のすべてのストレージノード" +#, fuzzy +msgid "an unnamed image" +msgstr "無効なイメージ" + msgid "and Mac OS X" msgstr "および Mac OS X" @@ -6725,9 +6741,6 @@ msgstr "イメージ" msgid "images to a storage group" msgstr "イメージをストレージグループへ" -msgid "imaging failed." -msgstr "イメージ処理に失敗しました。" - msgid "in seconds" msgstr "秒単位" @@ -6854,6 +6867,9 @@ msgstr "N/A" msgid "no login will appear" msgstr "ログイン画面は表示されません" +msgid "no reason was reported" +msgstr "" + msgid "node is the distributor of the images" msgstr "ノードがイメージの配布元になります" diff --git a/packages/web/management/languages/messages.pot b/packages/web/management/languages/messages.pot index aff4c39b96..2f603d7665 100644 --- a/packages/web/management/languages/messages.pot +++ b/packages/web/management/languages/messages.pot @@ -2205,6 +2205,10 @@ msgstr "" msgid "Host" msgstr "" +#, php-format +msgid "Host %1$s failed imaging %2$s: %3$s" +msgstr "" + msgid "Host AD Domain" msgstr "" @@ -2675,6 +2679,9 @@ msgstr "" msgid "Imaging Duration" msgstr "" +msgid "Imaging Failed" +msgstr "" + msgid "Imaging Log" msgstr "" @@ -5739,6 +5746,10 @@ msgstr "" msgid "This host already exists" msgstr "" +#, php-format +msgid "This host failed imaging %1$s: %2$s" +msgstr "" + msgid "This installation process may take a few minutes" msgstr "" @@ -6389,6 +6400,9 @@ msgstr "" msgid "all current storage nodes" msgstr "" +msgid "an unnamed image" +msgstr "" + msgid "and Mac OS X" msgstr "" @@ -6695,9 +6709,6 @@ msgstr "" msgid "images to a storage group" msgstr "" -msgid "imaging failed." -msgstr "" - msgid "in seconds" msgstr "" @@ -6824,6 +6835,9 @@ msgstr "" msgid "no login will appear" msgstr "" +msgid "no reason was reported" +msgstr "" + msgid "node is the distributor of the images" msgstr "" diff --git a/packages/web/management/languages/pt_BR.UTF-8/LC_MESSAGES/messages.po b/packages/web/management/languages/pt_BR.UTF-8/LC_MESSAGES/messages.po index 751c4521a3..06d7858ec2 100644 --- a/packages/web/management/languages/pt_BR.UTF-8/LC_MESSAGES/messages.po +++ b/packages/web/management/languages/pt_BR.UTF-8/LC_MESSAGES/messages.po @@ -2236,6 +2236,10 @@ msgstr "Telefone Residencial" msgid "Host" msgstr "Host" +#, php-format +msgid "Host %1$s failed imaging %2$s: %3$s" +msgstr "" + msgid "Host AD Domain" msgstr "Domínio AD do Host" @@ -2710,6 +2714,10 @@ msgstr "Criação de Imagem Concluída" msgid "Imaging Duration" msgstr "Duração da Criação de Imagem" +#, fuzzy +msgid "Imaging Failed" +msgstr "criação de imagem falhou." + msgid "Imaging Log" msgstr "Log de Criação de Imagem" @@ -5796,6 +5804,10 @@ msgstr "Este arquivo funcionará apenas no Windows" msgid "This host already exists" msgstr "Este host já existe" +#, fuzzy, php-format +msgid "This host failed imaging %1$s: %2$s" +msgstr "Este host já existe" + msgid "This installation process may take a few minutes" msgstr "Este processo de instalação pode levar alguns minutos" @@ -6447,6 +6459,10 @@ msgstr "atrás" msgid "all current storage nodes" msgstr "todos os nós de armazenamento atuais" +#, fuzzy +msgid "an unnamed image" +msgstr "Imagem inválida" + msgid "and Mac OS X" msgstr "e Mac OS X" @@ -6754,9 +6770,6 @@ msgstr "imagens" msgid "images to a storage group" msgstr "imagens para um grupo de armazenamento" -msgid "imaging failed." -msgstr "criação de imagem falhou." - msgid "in seconds" msgstr "em segundos" @@ -6883,6 +6896,9 @@ msgstr "n/d" msgid "no login will appear" msgstr "nenhum login aparecerá" +msgid "no reason was reported" +msgstr "" + msgid "node is the distributor of the images" msgstr "nó é o distribuidor das imagens" diff --git a/packages/web/management/languages/zh_CN.UTF-8/LC_MESSAGES/messages.po b/packages/web/management/languages/zh_CN.UTF-8/LC_MESSAGES/messages.po index 12ed907735..83d69b761b 100644 --- a/packages/web/management/languages/zh_CN.UTF-8/LC_MESSAGES/messages.po +++ b/packages/web/management/languages/zh_CN.UTF-8/LC_MESSAGES/messages.po @@ -2236,6 +2236,10 @@ msgstr "主页电话" msgid "Host" msgstr "主机" +#, php-format +msgid "Host %1$s failed imaging %2$s: %3$s" +msgstr "" + msgid "Host AD Domain" msgstr "主机AD域" @@ -2710,6 +2714,10 @@ msgstr "映像已完成" msgid "Imaging Duration" msgstr "映像期间" +#, fuzzy +msgid "Imaging Failed" +msgstr "映像失败" + msgid "Imaging Log" msgstr "映像日志" @@ -5796,6 +5804,10 @@ msgstr "这个文件只能在Windows上使用" msgid "This host already exists" msgstr "主机已经存在" +#, fuzzy, php-format +msgid "This host failed imaging %1$s: %2$s" +msgstr "主机已经存在" + msgid "This installation process may take a few minutes" msgstr "安装进程将持续几分钟" @@ -6447,6 +6459,10 @@ msgstr "前" msgid "all current storage nodes" msgstr "所有目前的存储节点" +#, fuzzy +msgid "an unnamed image" +msgstr "镜像无效" + msgid "and Mac OS X" msgstr "和MAC OS X" @@ -6753,9 +6769,6 @@ msgstr "镜像" msgid "images to a storage group" msgstr "存储组镜像" -msgid "imaging failed." -msgstr "映像失败" - msgid "in seconds" msgstr "几秒钟内" @@ -6882,6 +6895,9 @@ msgstr "N/A" msgid "no login will appear" msgstr "不会出现登录" +msgid "no reason was reported" +msgstr "" + msgid "node is the distributor of the images" msgstr "节点是镜像的分发器" diff --git a/tests/imaging-failure-reason.test.php b/tests/imaging-failure-reason.test.php new file mode 100644 index 0000000000..062ba41aaa --- /dev/null +++ b/tests/imaging-failure-reason.test.php @@ -0,0 +1,118 @@ + $root . '/pushbullet/events/imagefail_pushbullet.event.php', + 'slack' => $root . '/slack/events/imagefail_slack.event.php' +]; + +foreach ($files as $plugin => $file) { + if (!is_file($file)) { + $fails[] = "$plugin: imagefail listener is missing at $file"; + continue; + } + $src = file_get_contents($file); + // Comments stripped first, every time: the prose above each of these + // changes names the very thing being looked for and would satisfy the + // search on its own. + $code = preg_replace('#/\*.*?\*/#s', '', $src); + $code = preg_replace('#^\s*//.*$#m', '', $code); + + // Scoped to onEvent(), so a mention anywhere else in the file cannot + // stand in for the listener actually using it. + if (!preg_match('#function onEvent\(.*?\n \}#s', $code, $m)) { + $fails[] = "$plugin: could not read onEvent(), so nothing below is" + . ' actually checked'; + continue; + } + $fn = $m[0]; + + if (false === strpos($fn, "\$data['Reason']")) { + $fails[] = "$plugin: the failure notification never reads Reason, so" + . ' it says only that imaging failed and not why'; + } + if (false === strpos($fn, "\$data['ImageName']")) { + $fails[] = "$plugin: the failure notification never names the image"; + } + // Defensive reads. `?? ''` is the shape; a bare read is the bug. + foreach (['Reason', 'ImageName'] as $key) { + if (!preg_match( + "#\\\$data\['" . $key . "'\]\s*\?\?#", + $fn + )) { + $fails[] = "$plugin: \$data['$key'] is read without a default, so" + . ' a server that still sends only HostName turns this' + . ' notification into a PHP warning'; + } + } + // Both must have a fallback string, or an older server pushes a sentence + // with a hole in it ("failed imaging : "). + if (!preg_match('#\$reason\s*=\s*_\(#', $fn) + || !preg_match('#\$image\s*=\s*_\(#', $fn) + ) { + $fails[] = "$plugin: a missing key falls through without a translated" + . ' placeholder, so the message renders with an empty slot'; + } + // The substitution is outside _(). sprintf(_('...%1$s...'), $x) is right; + // _("...$x...") or _('...' . $x) is the bug this pins. + if (preg_match('#_\(\s*"[^"]*\$#', $fn) + || preg_match("#_\(\s*'[^']*'\s*\.#", $fn) + ) { + $fails[] = "$plugin: a variable is interpolated INSIDE _(), so the" + . ' msgid is built at runtime, matches no catalog entry and never' + . ' translates'; + } + if (!preg_match('#sprintf\(\s*\n?\s*_\(#', $fn)) { + $fails[] = "$plugin: the message is not composed with sprintf() around" + . ' a literal msgid, so the translated sentence cannot carry the' + . ' image and the reason'; + } + // Positional specifiers, so a translator can reorder. %s alone locks the + // sentence to English word order. + if (false === strpos($fn, '%1$s')) { + $fails[] = "$plugin: the msgid uses bare %s rather than positional" + . ' specifiers, so a translator cannot reorder the sentence'; + } +} + +if ($fails) { + echo 'FAIL: ' . count($fails) . " problem(s):\n"; + foreach ($fails as $f) { + echo " - $f\n"; + } + exit(1); +} +echo "ok: both failure notifications report the reason, defensively\n"; +exit(0);