Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
33 changes: 33 additions & 0 deletions packages/web/commons/schema.php
Original file line number Diff line number Diff line change
Expand Up @@ -7586,4 +7586,37 @@ function () {
// is a service account reachable only through an issued Bearer token.
"ALTER TABLE `users` "
. "ADD COLUMN `uAPIOnly` ENUM('0','1') NOT NULL DEFAULT '0'",
// The Font Awesome 7 migration renamed the icon *classes* in PHP and JS,
// but six task types and one task state carry their icon as DATA -- seeded
// by steps 2907-2987 above and rendered as `fa fa-<stored name>` by
// fog.task.list.js, the host and group task menus, and Task Management.
// Every one of the seven is an FA4 outline variant whose `-o` suffix FA7
// dropped outright, so after the migration they resolve to nothing and the
// icon renders blank. The prefix is fine: `fa` is still the solid alias.
//
// Appended rather than corrected in place. Editing steps 2907-2987 would
// fix a fresh install and leave every existing one broken, because an
// install that has already run them never replays them.
//
// Guarded on the old value so an administrator who has already picked
// their own icon for one of these keeps it -- this repairs FOG's seed, it
// does not impose a choice.
"UPDATE `taskTypes` SET `ttIcon`='square-plus' "
. "WHERE `ttID`=4 AND `ttIcon`='plus-square-o'",
"UPDATE `taskTypes` SET `ttIcon`='hard-drive' "
. "WHERE `ttID`=5 AND `ttIcon`='hdd-o'",
"UPDATE `taskTypes` SET `ttIcon`='circle-arrow-down' "
. "WHERE `ttID`=15 AND `ttIcon`='arrow-circle-o-down'",
"UPDATE `taskTypes` SET `ttIcon`='circle-arrow-up' "
. "WHERE `ttID`=16 AND `ttIcon`='arrow-circle-o-up'",
// Fast/Normal/Full Wipe are read as a set. Normal already holds
// `hourglass-2`, which FA7 still resolves as an alias of hourglass-half,
// and Full holds `hourglass`, so `hourglass-start` here restores the
// progression rather than just picking any surviving hourglass.
"UPDATE `taskTypes` SET `ttIcon`='hourglass-start' "
. "WHERE `ttID`=18 AND `ttIcon`='hourglass-o'",
"UPDATE `taskTypes` SET `ttIcon`='flag' "
. "WHERE `ttID`=22 AND `ttIcon`='flag-o'",
"UPDATE `taskStates` SET `tsIcon`='bookmark' "
. "WHERE `tsID`=1 AND `tsIcon`='bookmark-o'",
];
6 changes: 3 additions & 3 deletions packages/web/lib/fog/system.class.php
Original file line number Diff line number Diff line change
Expand Up @@ -94,8 +94,8 @@ public function __construct()
// 1.5.x carried count does, see SchemaReconciler's docstring -- is
// permanently "up to date" from the updater's point of view and will
// never run another indexed step, whatever this constant says.
define('FOG_SCHEMA', 360);
define('FOG_BCACHE_VER', 313);
define('FOG_SCHEMA', 367);
define('FOG_BCACHE_VER', 314);
define('FOG_CLIENT_VERSION', '0.13.0');
// GH-959: iPXE lives in FOGProject/fog-ipxe and its binaries arrive as
// a release asset. Pinned here rather than tracked as "latest" so a
Expand All @@ -108,7 +108,7 @@ public function __construct()
// installer reads this to pick which release to download, so a given
// FOG release ships a known set of plugins rather than whatever the
// default branch held on the day someone installed.
define('FOG_PLUGINS_VERSION', 'v1.6.15');
define('FOG_PLUGINS_VERSION', 'v1.6.16');
// GH-850: FOG_BASE_DIR is now installer-driven. Initiator loads
// commons/fogpaths.php (written from the installer's $fogprogramdir)
// before the autoloader runs, so in a normal boot these are already
Expand Down
2 changes: 1 addition & 1 deletion packages/web/lib/pages/groupmanagement.page.php
Original file line number Diff line number Diff line change
Expand Up @@ -2649,7 +2649,7 @@ public function groupTasks()
. $id
. '&type='
. $TaskType->id
. '" class="taskitem"><i class="fa fa-'
. '" class="taskitem"><i class="fas fa-'
. $TaskType->icon
. ' fa-2x"></i><br/>'
. $TaskType->name
Expand Down
2 changes: 1 addition & 1 deletion packages/web/lib/pages/hostmanagement.page.php
Original file line number Diff line number Diff line change
Expand Up @@ -3991,7 +3991,7 @@ public function hostTasks()
. $id
. '&type='
. $TaskType->id
. '" class="taskitem"><i class="fa fa-'
. '" class="taskitem"><i class="fas fa-'
. $TaskType->icon
. ' fa-2x"></i><br/>'
. $TaskType->name
Expand Down
4 changes: 2 additions & 2 deletions packages/web/management/js/fog/ipxe/fog.ipxe.list.js
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@
}
return '<span class="badge bg-'
+ label
+ '"><i class="fa fa-'
+ '"><i class="fas fa-'
+ check
+ '"></i></span>';
},
Expand All @@ -59,7 +59,7 @@
}
return '<span class="badge bg-'
+ label
+ '"><i class="fa fa-'
+ '"><i class="fas fa-'
+ check
+ '"></i></span>';
},
Expand Down
18 changes: 9 additions & 9 deletions packages/web/management/js/fog/task/fog.task.list.js
Original file line number Diff line number Diff line change
Expand Up @@ -149,14 +149,14 @@
{
render: function(data, type, row) {
return row.tasktypename
+ ' <i class="fa fa-' + row.tasktypeicon + '"></i> '
+ ' <i class="fas fa-' + row.tasktypeicon + '"></i> '
},
targets: 6
},
{
render: function(data, type, row) {
return row.taskstatename
+ ' <i class="fa fa-' + row.taskstateicon + '"></i> '
+ ' <i class="fas fa-' + row.taskstateicon + '"></i> '
},
targets: 7
},
Expand Down Expand Up @@ -223,7 +223,7 @@
},
{
render: function(data, type, row) {
return '<i class="fa fa-' + row.taskstateicon + '"></i>';
return '<i class="fas fa-' + row.taskstateicon + '"></i>';
},
targets: 3
}
Expand Down Expand Up @@ -273,7 +273,7 @@
{
render: function(data, type, row) {
return data
+ ' <i class="fa fa-'
+ ' <i class="fas fa-'
+ row.taskstateicon
+ '"></i>';
},
Expand Down Expand Up @@ -368,7 +368,7 @@
{
render: function(data, type, row) {
return $.escapeHtml(data || '')
+ ' <i class="fa fa-' + $.escapeHtml(row.tasktypeicon || '') + '"></i> ';
+ ' <i class="fas fa-' + $.escapeHtml(row.tasktypeicon || '') + '"></i> ';
},
targets: 2
},
Expand All @@ -384,7 +384,7 @@
{
render: function(data, type, row) {
return $.escapeHtml(data || '')
+ ' <i class="fa fa-' + $.escapeHtml(row.taskstateicon || '') + '"></i> ';
+ ' <i class="fas fa-' + $.escapeHtml(row.taskstateicon || '') + '"></i> ';
},
targets: 4
},
Expand Down Expand Up @@ -460,14 +460,14 @@
{
render: function(data, type, row) {
return $.escapeHtml(data || '')
+ ' <i class="fa fa-' + $.escapeHtml(row.tasktypeicon || '') + '"></i> ';
+ ' <i class="fas fa-' + $.escapeHtml(row.tasktypeicon || '') + '"></i> ';
},
targets: 2
},
{
render: function(data, type, row) {
return $.escapeHtml(data || '')
+ ' <i class="fa fa-' + $.escapeHtml(row.taskstateicon || '') + '"></i> ';
+ ' <i class="fas fa-' + $.escapeHtml(row.taskstateicon || '') + '"></i> ';
},
targets: 3
},
Expand Down Expand Up @@ -539,7 +539,7 @@
$.escapeHtml(row.hostname || '')],
['Task', $.escapeHtml(String(row.taskid || '')) + ' &mdash; ' + $.escapeHtml(row.tasktypename || '')],
['State at the time', $.escapeHtml(row.taskstatename || '')
+ ' <i class="fa fa-' + $.escapeHtml(row.taskstateicon || '') + '"></i>'],
+ ' <i class="fas fa-' + $.escapeHtml(row.taskstateicon || '') + '"></i>'],
['Type', '<span class="badge ' + (badge[row.logtype] || 'bg-secondary') + '">'
+ $.escapeHtml(row.logtype || '') + '</span>'],
['Recorded by', $.escapeHtml(row.createdBy || '')]
Expand Down
77 changes: 77 additions & 0 deletions tests/fontawesome7-icon-names.test.php
Original file line number Diff line number Diff line change
Expand Up @@ -228,4 +228,81 @@
&& (bool)preg_match("/closer: 'fas fa-xmark'/", $common)
);

// ---------------------------------------------------------------------------
// 5. Icon names that live in the DATABASE, not in a class attribute.
//
// The check above scans source for a literal `fas fa-name`, so it is blind to
// the icons FOG stores as data. taskTypes.ttIcon and taskStates.tsIcon hold a
// bare icon name -- no prefix -- seeded by commons/schema.php and rendered by
// fog.task.list.js and the host/group task menus as `fas fa-<stored name>`.
//
// That blind spot shipped: the FA7 migration renamed every class in core and
// left seven seeded values on FA4 outline names FA7 dropped (plus-square-o,
// hdd-o, arrow-circle-o-down, arrow-circle-o-up, hourglass-o, flag-o and
// bookmark-o), so six task types and the Queued state rendered blank on every
// upgraded and every fresh install. Nothing in the suite could see it, because
// nothing in the suite reads the seed.
//
// Pinned against the shipped stylesheet, the same authority section 3 uses.
// The final value for an id is whichever step sets it last, so the seed is
// replayed in file order rather than collected -- taking every literal would
// fail on the historical steps that are SUPPOSED to hold the old names, which
// are deliberately never edited (an install that has run them never replays
// them, so a correction has to be appended instead).
// ---------------------------------------------------------------------------
$schemaSrc = file_get_contents($root . '/packages/web/commons/schema.php');
// Statements in this file are written as PHP string concatenations wrapped
// across lines, so the glue is folded away first -- otherwise the pattern
// below silently sees only the steps that happen to fit on one line, which is
// every historical step and none of the appended corrections. That reads as
// the bug still being present.
$schemaSrc = preg_replace('/[\x27"]\s*\.\s*[\x27"]/', '', $schemaSrc);
$seeded = [];
$pattern = '/`(taskTypes|taskStates)`\s+SET\s+`(ttIcon|tsIcon)`\s*=\s*'
. "'([^']+)'\s+WHERE\s+`(ttID|tsID)`\s*=\s*(\d+)/i";
if (preg_match_all($pattern, $schemaSrc, $mm, PREG_SET_ORDER)) {
foreach ($mm as $row) {
// Last write wins, exactly as a replay from step 0 would leave it.
$seeded[$row[1] . '#' . $row[5]] = $row[3];
}
}
$t->check(
sprintf('the seeded icon names were found (%d)', count($seeded)),
count($seeded) >= 20
);

$deadSeed = [];
foreach ($seeded as $where => $value) {
// A stored value may carry modifiers -- taskStates 3 is
// "spinner fa-pulse fa-fw" -- and only the first token is the icon.
$name = strtok(trim($value), ' ');
if (!preg_match('/\.fa-' . preg_quote($name, '/') . '[,:{ ]/', $cssSrc)) {
$deadSeed[] = $where . ' => ' . $value;
}
}
$t->check(
sprintf(
'every seeded icon name resolves in the shipped CSS%s',
[] === $deadSeed ? '' : ' -- DEAD: ' . implode(', ', $deadSeed)
),
[] === $deadSeed
);

// The renderers that compose those values must not be left on the old prefix
// either. They evade the bare-form check in section 2 because the name is
// concatenated on, so the literal ends at the quote and matches nothing.
$concat = [];
foreach ($files as $path) {
if (preg_match_all('/\bfa fa-[\x27"]/', file_get_contents($path))) {
$concat[] = str_replace($root . '/', '', $path);
}
}
$t->check(
sprintf(
'no renderer concatenates onto the bare "fa fa-" prefix%s',
[] === $concat ? '' : ' -- FOUND: ' . implode(', ', $concat)
),
[] === $concat
);

$t->finish();
Loading