Info card: one-click Deploy/Capture, and no more "Primary Group" - #1712
Merged
Conversation
Two changes to the same strip at the top of the host and group edit pages.
REMOVED: the host card's "Primary Group". It meant
minId($this->obj->get('groups')) -- the lowest-id group the host happened
to be in. There is no primary group. A group GRANTS, and what a host ends
up with is resolved from every group it belongs to at task time, ordered
by groupOrder (ADR 0038, FOG\Assign\Resolver). So the label named a rank
that does not exist and the value picked one membership arbitrarily.
Nothing replaces it. Listing every group instead was considered and
dropped: a host in eight groups blows the card out, which makes the card
worse rather than better, and the Group Associations tab already shows
them properly.
ADDED: Deploy and Capture on the host card, Deploy and Multi-Cast on the
group card. One click, from whichever tab is open, so an admin already
looking at a host does not have to go back to the grid and find the row
again to do the obvious thing to it.
This is not a new pattern. The host LIST has carried the same three
buttons since _quickTaskItems(), for the same reason: these are the task
types that need no options, which is the whole reason they can be one
click. The pairing is the one that method already documents -- Deploy and
Capture are what a single host wants, Deploy and Multi-Cast what a set of
them wants. On a group it is also what the server will accept:
GroupManagement::deployPost() throws "Groups cannot create capture tasks"
outright, so a Capture button there could only produce a toast saying no.
Every button confirms first, and this is the one place it deliberately
differs from the list. There you tick a row to get the buttons; here you
arrive on the page just by clicking a host name, so one stray click would
deploy over a running machine -- or, from a group, over all of them. The
text is built server side because it is translated, and it names the
target: the host, or the group AND its member count, which is the fact
that decides whether you meant to press it and the one thing the button
itself cannot show.
Mechanically:
- FOGPage::$noteActions, a pre-rendered string, echoed by
renderInfoCard() in a right-aligned column. ms-auto, not a float: the
row is display:flex and a float would do nothing there.
- It rides the existing EDIT_INFO_DATA hook alongside notes/noteSources,
so a plugin adding a button does it the same way it already adds a
line. A second event would mean two registrations for one card.
- FOGPageRender::renderQuickTaskActions() builds them, gated on
{node}.task -- the action ?node=X&sub=deploy resolves to through
Authorization::_subToAction(), so the gate here and the gate the POST
hits are the same string by construction.
- The script posts straight to ?node={node}&sub=deploy with
scheduleType=instant, skipping the options form. Nothing on it these
types need, and everything its POST is checked for -- pending host,
assigned and enabled image, protected image on a capture, one image
across a multicast -- is checked in deployPost(), not in the form.
scheduleType is the one field that must be sent: validateScheduleType()
throws on an absent value rather than defaulting.
- Suppressed where the server would refuse anyway: a pending host, an
empty group.
Neutral outline buttons rather than a type color. Nothing here is the
card's commit action -- the General tab's Update is -- and these are
shortcuts in a header strip, not a decision cluster in a form footer. The
weight a red button would carry is carried by the confirmation instead.
tests/info-card-quick-tasks.test.php pins the four things that fail
silently: the permission gate per node (host.task must not unlock the
group card), the confirmation being present AND naming the target, the
values being read through get() rather than as properties, and the pair
each page asks for. Each was verified by reintroducing the defect and
watching it go red.
FOG_BCACHE_VER 360 -> 361 for the changed script.
Co-Authored-By: Claude <noreply@anthropic.com>
btn-outline-secondary keeps #6c757d as the TEXT color. Against the dark card (#212529) that is 3.29:1 -- under the 4.5:1 AA floor for body-sized text. Filled puts white on #6c757d instead and holds 4.69:1 in both themes. Measured in a browser against the shipped adminlte4.min.css and fog-default-ui.min.css, rendering the real renderInfoCard() output, not assumed from the Bootstrap defaults. It is also what the host list's own quick buttons already are, since DataTables draws its button bar filled -- so the two places that offer the same one-click tasking now look the same. The test pins the class rather than the ratio: a browser is what measures contrast and the suite is PHP, but the class is the part a future edit would actually change. Co-Authored-By: Claude <noreply@anthropic.com>
Both checks were red on #1707 before this branch existed, and #1712 inherited them. Neither is a code defect -- both are a baseline that the change which moved it did not update. phpstan pass 1: AgentActivityManagement's constructor takes $name and ignores it, overwriting with _('Agent Activity'). That is the shape EVERY page in src/Pages has -- TaskManagement, HostManagement, GroupManagement, ReportManagement, all of them -- and every one of the others is already in phpstan-baseline.neon under constructor.unusedParameter. The new page just had no entry. Added one, in path order beside its neighbours. Changing the constructor instead would make this page the only one in the directory with a different signature, to satisfy a rule the other 27 are exempt from. phpstan pass 2: 26 entries for the agent test files. Worth saying that CI never reported these -- the job runs both passes as separate steps without continue-on-error, so pass 1 exiting 1 meant pass 2 never ran. Fixing pass 1 is what makes them visible, and they would have turned the check red again on the next push looking like a new regression. They are the usual test-file shapes: PHPStan narrows a literal, so an in_array() that pins a constant against its own list, or a ?? guarding an offset it can prove exists, reads as "always true". phpstan-tests.neon's own header says this pass "adapts to the tests as they are written, it does not ask them to change", so they are baselined, not rewritten. Generated with --generate-baseline into a temp file and MERGED, not applied: a straight regeneration emits only the 26 unignored errors and would have dropped the 196 entries already there. Paths rewritten repo-relative -- the generator writes absolute ones, which would match nothing on a CI checkout. Verified as a set: 196 in, 222 out, 0 lost, 26 added, no deletions in the diff. upgrade rehearsal: 3 constraints landed that the fixture did not know about -- fk_agentEnrollment_aeHostID, fk_agentWake_awSenderID and fk_agentWake_awTargetID, all agent tables referencing hosts. So declared 96 -> 99 and present 94 -> 97, while MISSING stays at the same 2 named constraints and the integrity block is untouched. Nothing regressed; the count moved because the schema grew. The test's own docblock asks for exactly this: "a change that legitimately moves the numbers -- adding a constraint -- updates it in the SAME commit and the diff is visible in review." Reproduced locally first against a throwaway mariadb:11.8 container rather than the lab server, got CI's diff byte for byte, confirmed the three new constraints in information_schema, then updated the fixture and re-ran on a second clean database to check it passes from scratch rather than only on the one it was generated from. Co-Authored-By: Claude <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Two changes to the same strip at the top of the host and group edit pages.
Based on
feat/agent-enroll, and targeted at it, becausefe5c6e73erewrote the exact$this->notesblock this touches (the Last Check-In agent/client logic). Doing it onworking-1.6would have guaranteed a hand-resolved conflict there.Removed: the host card's "Primary Group"
It meant
minId($this->obj->get('groups'))— the lowest-id group the host happened to be in.There is no primary group. A group grants, and what a host ends up with is resolved from every group it belongs to at task time, ordered by
groupOrder(ADR 0038,FOG\Assign\Resolver). So the label named a rank that does not exist and the value picked one membership arbitrarily.Nothing replaces it. Listing every group instead was considered and dropped: a host in eight groups blows the card out, which makes the card worse rather than better, and the Group Associations tab already shows them properly.
Added: one-click tasking in the card
Deploy and Capture on the host card; Deploy and Multi-Cast on the group card. From whichever tab is open, so an admin already looking at a host does not have to go back to the grid and find the row again to do the obvious thing to it.
This is not a new pattern. The host list has carried the same buttons since
_quickTaskItems(), for the reason that method documents: these are the task types that need no options, which is the whole reason they can be one click. The pairing is its own — Deploy and Capture are what a single host wants, Deploy and Multi-Cast what a set of them wants. On a group it is also what the server will accept:GroupManagement::deployPost()throws "Groups cannot create capture tasks" outright.Every button confirms first, and this is the one place it deliberately differs from the list. There you tick a row to get the buttons; here you arrive on the page just by clicking a host name, so one stray click would deploy over a running machine — or, from a group, over all of them. The text is built server side because it is translated, and it names the target: the host, or the group and its member count, which is the fact that decides whether you meant to press it and the one thing the button itself cannot show.
Mechanically
FOGPage::$noteActions, a pre-rendered string, echoed byrenderInfoCard()in a right-aligned column.ms-auto, not a float — the row isdisplay:flexand a float would do nothing there.EDIT_INFO_DATAhook alongsidenotes/noteSources, so a plugin adding a button does it the same way it already adds a line.FOGPageRender::renderQuickTaskActions()builds them, gated on{node}.task— the action?node=X&sub=deployresolves to throughAuthorization::_subToAction(), so the gate here and the gate the POST hits are the same string by construction.?node={node}&sub=deploywithscheduleType=instant, skipping the options form. Everything its POST is checked for — pending host, assigned and enabled image, protected image on a capture, one image across a multicast — is checked indeployPost(), not in the form.scheduleTypeis the one field that must be sent:validateScheduleType()throws on an absent value rather than defaulting.Button color is a contrast decision, not a taste one
btn-outline-secondarykeeps#6c757das the text color; against the dark card (#212529) that is 3.29:1, under the 4.5:1 AA floor for body-sized text. Filledbtn-secondaryputs white on#6c757dand holds 4.69:1 in both themes. Measured in a browser against the shippedadminlte4.min.css+fog-default-ui.min.css, rendering the realrenderInfoCard()output. It is also what the list's own quick buttons are, so the two places offering the same tasking now look the same.Neither is
btn-primary: nothing here is the card's commit action — the General tab's Update is.Verification
tests/info-card-quick-tasks.test.php(19 checks) pins the things that fail silently: the permission gate per node (host.taskmust not unlock the group card), the confirmation being present and naming the target, the values being read throughget()rather than as properties, the pair each page asks for, and the filled class. Each was verified by reintroducing the defect and watching it go red.phpstanpass 1: clean.phpstan: both passes clean.fogproject / upgrade rehearsal: green, reproduced locally against a throwawaymariadb:11.8container and re-run on a second clean database.It also fixes the two failures it inherited
feat/agent-enrollwas red onphpstanandupgrade rehearsalbefore this branch existed, so this one inherited both. Neither was a code defect; both were a baseline the change that moved it did not update. Fixed here in7c0c45199, so they clear on the base too when this merges.phpstanpass 1 —AgentActivityManagement's constructor takes$nameand ignores it. That is the shape every page insrc/Pageshas, and all 27 others are already inphpstan-baseline.neonunderconstructor.unusedParameter; the new page just had no entry. Added one. Changing the constructor instead would leave this page the only one in the directory with a different signature.phpstanpass 2 — 26 entries for the agent test files. CI never reported these: the job runs the passes as separate steps with nocontinue-on-error, so pass 1 exiting 1 meant pass 2 never ran. They are the usual test shapes (PHPStan narrows a literal, so anin_array()pinning a constant reads as always-true), andphpstan-tests.neon's own header says this pass "adapts to the tests as they are written, it does not ask them to change" — so baselined, not rewritten. Generated into a temp file and merged: a straight regeneration emits only the 26 unignored errors and would have dropped the 196 already there, and it writes absolute paths that match nothing on a CI checkout. Verified as a set — 196 in, 222 out, 0 lost, no deletions in the diff.upgrade rehearsal— three constraints landed that the fixture did not know about:fk_agentEnrollment_aeHostID,fk_agentWake_awSenderID,fk_agentWake_awTargetID, all agent tables referencinghosts. Declared 96 → 99, present 94 → 97;MISSINGstays at the same 2 named constraints and the integrity block is untouched. The test's own docblock asks for exactly this update in the same commit.tests/run-all.sh: 332 pass, 1 fail —certificate-table.test.php, which fails identically on an untouchedfeat/agent-enrolland onworking-1.6, and passes in CI. Local environment, not this change.renderInfoCard()and screenshotted in both themes against the live stylesheets.FOG_BCACHE_VER360 → 361 for the changed script. It also covers the Agent Activity JS that arrived in the merge, which did not bump it.working-1.6still sits at 360, so re-check the value against the base whenfeat/agent-enrollmerges up — two branches bumping 360 → 361 merge cleanly and silently collide.No REST route changed, so
OpenAPI::document()is unaffected. No downstream class-list change, so nothing for FogApi.🤖 Generated with Claude Code
https://claude.ai/code/session_01JWJMQYE2br8E7Ehr55SJp2