Repository navigation
[3.0] Renders the templates with Plates (part 2 of 2) - #9728
albertlast wants to merge 20 commits into
Conversation
jdarwood007
left a comment
There was a problem hiding this comment.
I only clicked around and didn't even determine whether this is what we are doing.
I want to see how this handles compatibility with 2.1. It is understood that because we have changed CSS, we may have some presentation issues (we should try to limit those). But being able to call the theme functions and methods (including JS) should still work.
|
|
||
| if ($output_method == 'echo') { | ||
| template_menu(); | ||
| Theme::loadSubTemplate('menu'); |
There was a problem hiding this comment.
We would need a compat function here.
There was a problem hiding this comment.
template_menu() still exists as a function. Sources/TemplateFunctions.php defines it whenever the index template is loaded from Plates, together with template_button_strip(), template_quickbuttons(), theme_linktree(), template_show_list() and the three GenericControls functions. Each of them renders its Plates file, so a mod that calls template_menu() directly keeps working.
This line only changes how SSI itself renders the menu. Theme::loadSubTemplate('menu') renders whichever kind the theme has, so a theme with its own index.template.php still gets its own template_menu(). ssi_examples.php, plus a script that calls ssi_menubar(), template_button_strip(), template_quickbuttons() and theme_linktree() the way a mod would, render byte-identically before and after this PR (.dev/plates/probe.php in #9727).
|
Thanks for looking. I agree 2.1 compatibility needs an answer, but I think the answer has to account for where the templates are going next, not only for this PR. What this PR keeps working
A gap I found while checking
So a 2.1 theme that overrides Why I'd rather not chase full 2.1 theme compatibility
Runtime compatibility with 2.1 themes would mean rebuilding a compatibility layer at each of those steps, and each step makes the next one harder. Over time it costs more and delivers less. What I'd propose instead
Since this decides more than this PR (it applies equally to the CSS work and the JS extraction), could we settle how much 2.1 theme compatibility 3.0 promises before I add more compatibility code here? If the answer is "mods yes, whole themes no", I'll add the three missing helpers and stop there. |
Records what about 230 pages render today, as a guest, a member and an administrator, so that moving the templates to Plates can be checked page by page. A page that renders differently fails, and the diff says how. Pages are compared as DomSnapshot describes them rather than as HTML: whitespace, comments and attribute order are dropped, and the forum's own data - ids, counts, dates, names, sessions, list lengths - is masked. Pages that list content look at fixtures built once per forum, and lists of the whole forum are narrowed to them, so the snapshots do not change when somebody posts, registers or adds a board. The errors a page logs are part of its snapshot. perturb-forum.php fills a throwaway forum with unrelated members, boards and posts. Recorded on a fresh MySQL forum, the snapshots pass there and on a fresh PostgreSQL forum, and on both again after two rounds of it. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Signed-off-by: albertlast <mathiaspapealbert@hotmail.com>
…d-in forum showed HttpTestCase slept for three seconds and retried whenever flood control turned a login or a post away. As suggested in review, submitForm() now deletes the rows in log_floodcontrol first, so there is nothing to wait out and no test depends on how quickly it runs. The retry logic goes. Running the snapshots against a forum that had been used, rather than a new one, found content they still depended on: - the forum administrator's own profile, since the fixtures posted as them; a fixture administrator posts instead, and the fixtures carry a version so that forums holding the old ones rebuild them - the hash in a minified file's name, which \b never matched after "_" - ;boardseen, decimals and thousands in numbers, email addresses, the encoded parameters of a search, and fixture ids in the selectors - the errors a member had logged, on their tracking page The recording itself had two faults: a Settings_bak.php the forum could not write, which put a warning on every settings page, and the password prompt in front of a member's account settings, which was recorded in place of the page. The suite now gets past the prompt for members as it already did for the administrator. Snapshot tests also reuse their signed in browser rather than making a new one per test, which cost a request each and a third of the run. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Signed-off-by: albertlast <mathiaspapealbert@hotmail.com>
docker compose exec starts a fresh environment, so test.sh only hands over the variables it names. SMF_UPDATE_SNAPSHOTS was not one of them, and under the docker runner the documented way to re-record the snapshots compared against them instead. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Signed-off-by: albertlast <mathiaspapealbert@hotmail.com>
The search results snapshots open search2 from a link, and SMF turns away a search made within a few seconds of the last one. With each request taking about a second, as it does over a Windows bind mount, the two search pages were never close enough together to notice. On a Linux filesystem the suite runs about twenty times faster, and the second search came back as a 403. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Signed-off-by: albertlast <mathiaspapealbert@hotmail.com>
profile_track_ip pinned a fatal that SimpleMachines#9718 has fixed, so it now records the page. features_basic and profile_theme show the time zone select grouped by country. themes_copy lists the new TimeZoneSelect template and no longer logs an array to string conversion, which SimpleMachines#9720 fixed. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Signed-off-by: albertlast <mathiaspapealbert@hotmail.com>
Adds the license header to any test file this adds, and writes the docblock of each new test method in the agreed shape: Expected:, Guards: for a regression, and @link lines for the commit that introduced the bug and for this pull request. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Signed-off-by: albertlast <mathiaspapealbert@hotmail.com>
HttpTestCase now shares one guest browser between tests, and signInAsAdmin() signs in a browser of its own. The snapshot classes need a browser per audience, so their first browser is a new one rather than the shared guest, which the member audience would otherwise sign in for every test after it. The admin audience signs in a fresh browser rather than the shared one, since passing the admin security check changes its session, and the audience's browser is kept once signing in is done. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Signed-off-by: albertlast <mathiaspapealbert@hotmail.com>
The time zone pickers on the basic features page and the theme profile page name and order each zone by whether daylight saving is in effect that day, so both snapshots failed when Australia moved its clocks on 4 October, with no template involved. Both cases now ignore the select, which the snapshot still records as being there. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Signed-off-by: albertlast <mathiaspapealbert@hotmail.com>
With SMF_SNAPSHOT_RAW set, every page the suite compares is also saved as it was sent, one directory per engine. The snapshots leave whitespace out on purpose; comparing two of these captures after masking sessions, tokens and numbers shows whether a converted template renders byte for byte what the old one did. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Signed-off-by: albertlast <mathiaspapealbert@hotmail.com>
.dev/convert-templates.sh converts the default theme's *.template.php files into one Plates file per sub-template, starting again from the old files as a commit has them on every run. .dev/capture-pages.sh saves every page the snapshot suite renders, on both engines, plus SSI, XML responses and a probe calling the template helpers the way a mod does; .dev/compare-pages.sh diffs two captures byte for byte, or as a browser renders them. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Signed-off-by: albertlast <mathiaspapealbert@hotmail.com>
Adds league/plates, and SMF\TemplateEngine to hold it. A theme keeps its Plates templates in templates/<Template>/<sub_template>.php, and Theme::loadTemplate() and Theme::loadSubTemplate() render those when the theme has them, falling back to the template_*() functions otherwise. So the templates can move over one at a time. Each file is looked for in the current theme, then the base theme, then the default theme, so a theme can override one sub-template and inherit the rest. Where a Plates template and a template_*() function share a sub-template name, the one whose template was loaded last runs, as the function would have before. The copy page in the theme admin copies a Plates template's directory whole, and the version page lists each of its files. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Signed-off-by: albertlast <mathiaspapealbert@hotmail.com>
Stats.template.php becomes templates/Stats/main.php. The markup and the text are unchanged; only the whitespace between them differs, and none of it is where a browser would show it. The copy page now lists the template as templates/Stats/, and copies it into a theme as a directory. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Signed-off-by: albertlast <mathiaspapealbert@hotmail.com>
A template that calls another sub-template should not have to know whether that one has been converted to Plates yet. TemplateEngine::renderSubTemplate() renders whichever kind a loaded template has, with named parameters for a Plates template and named or positional ones for a template_*() function, and hasSubTemplate() answers function_exists() for both. A Plates template reaches them as $this->subTemplate() and $this->hasSubTemplate(). Theme::loadSubTemplate() goes through the same method, so there is one place that decides which kind runs. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Signed-off-by: albertlast <mathiaspapealbert@hotmail.com>
Converted mechanically from the template_*() functions, one file per sub-template. On every page the snapshot suite covers, on both engines, the HTML is the same byte for byte as before, apart from the theme copy page, which now lists these as templates/<Name>/. Calls between sub-templates go through $this->subTemplate(), so they work whether the sub-template called has been converted or not. The templates converted here are the ones nothing still unconverted calls into. The theme reset page lists a theme only if it has its own Settings template, and now counts a Plates one. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Signed-off-by: albertlast <mathiaspapealbert@hotmail.com>
Some template_*() functions return their HTML rather than output it, for a caller that needs a string: the quick buttons in 'return' mode, and the watched users list's callback. fetchSubTemplate() gets that string from either kind of sub-template, including what a function returns. Parameters are named for Plates templates. A function a mod provides may call its own something else, and named arguments it does not know would be an error, so a function whose parameters do not match gets them in order. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Signed-off-by: albertlast <mathiaspapealbert@hotmail.com>
…Plates Admin, BoardIndex, EventEditor, ManagePermissions, MessageIndex, ModerationCenter and Profile, and TimeZoneSelect, which Admin, EventEditor and Profile call, converted the same way, and each byte for byte the same on every page the snapshot suite covers. Two places in Sources called a template function directly and now go through the engine: the watched users list's column callback, which needs the HTML as a string, and the inline permissions on settings pages. A template call used as a value, rather than as a statement of its own, now uses fetchSubTemplate(); the recent posts pages had four, which worked only because button_strip outputs as it goes. A call through a name built at runtime names its parameters after the sub-templates the name can reach. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Signed-off-by: albertlast <mathiaspapealbert@hotmail.com>
GenericList and GenericControls, which every other converted template calls into for lists, the editor and the verification controls. Byte for byte the same on every page the snapshot suite covers. template_control_verification() returned whether there was more to show when displaying one item at a time. Nothing in SMF uses that, and a Plates template cannot return a value; the output is the same. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Signed-off-by: albertlast <mathiaspapealbert@hotmail.com>
The last two. The default theme has no *.template.php left, and every page the snapshot suite covers renders byte for byte what it did. Mods and themes call template_button_strip(), template_quickbuttons(), template_menu(), theme_linktree(), template_show_list() and the editor and verification controls by name, from templates of their own. Loading the Plates template that holds one of them defines that function, rendering the Plates template, unless the name is already taken by a theme that still has the template as a file. The XML template's stand-ins for when there is no index template are defined the same way. theme_linktree() is the sub-template 'linktree'. The quick buttons are always output, and template_quickbuttons() returns them when asked, as it did. The admin panel read a theme's variants out of index.template.php, and checked for that file to tell whether a directory is a theme; both also look at templates/index/init.php. Creating a copy of the default theme copies its index template directory. SSI's menu bar renders the sub-template rather than calling the function. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Signed-off-by: albertlast <mathiaspapealbert@hotmail.com>
PHP swallows the newline that follows a closing tag, so the conversion kept each one by leaving an empty line after it. Where the next line is indented, that newline and the indentation collapse to the same single space in HTML, so the empty line only costs readability. Inside <script>, <pre> and <textarea> the newline is content or a statement boundary, and stays. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Signed-off-by: albertlast <mathiaspapealbert@hotmail.com>
Adds the license header to any test file this adds, and writes the docblock of each new test method in the agreed shape: Expected:, Guards: for a regression, and @link lines for the commit that introduced the bug and for this pull request. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Signed-off-by: albertlast <mathiaspapealbert@hotmail.com>
Each test method now has the docblock AGENTS.md describes: a summary, Expected: with the call and the result its assertions check, and for a regression test, Guards: with what went wrong, a @link to the commit that introduced the bug and a @link to the issue or pull request that fixed it. The Introduced by line is left out where the faulty code is older than the history (it is already in the 2012 import) or where the commit could not be confirmed from its diff. A comment at the top of a test body that explained why the test exists has moved into the docblock; nothing else in the bodies has changed. ThemeCopyTest is left alone, because #9728 rewrites it. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Signed-off-by: albertlast <mathiaspapealbert@hotmail.com>
Description
This replaces SMF's own template mechanism,
template_*()functions that echo, with Plates (league/plates3.6), and converts every template in the default theme. The forum renders what it rendered before. The one exception is the theme copy page, which had to learn the new layout.This is stacked on #9727 (part 1, the converter), which is stacked on #9712 (the snapshot tests). Until both merge, this PR's diff includes their commits. The ones that belong here start at "Renders a sub-template with Plates when its theme has one".
How to review it
The diff is large, but almost all of it is generated: 54
*.template.phpfiles deleted and 342 Plates files added underThemes/default/templates/. Those are the converter's output (part 1), with one exception, Stats, which was converted by hand as the first test of the engine. The generated files are reproducible from a clean checkout:The part written by hand is about 700 lines in
Sources/, plus tests:Sources/TemplateEngine.phpEnginewith SMF's theme inheritanceSources/Theme.phploadTemplate()/loadSubTemplate()render either kindSources/TemplateFunctions.phptemplate_*()helpers that mods call directly, kept workingSources/Actions/Admin/Themes.php,ACP.php,ThemeChooser.phpServerSideIncludes.php,WatchedUsers.php,Permissions.phptests/Unit/TemplateEngineTest.php,tests/Integration/Http/ThemeCopyTest.phpDesign
Themes/<theme>/templates/<Template>/<sub_template>.php.Theme::loadTemplate('Display')finds the directory, andTheme::loadSubTemplate('main')rendersDisplay/main.php.template_dirs: current theme, then the base theme, then default. A theme overrides a single sub-template by shipping that one file, where it used to copy the whole*.template.php.$this->subTemplate('name', [...]), which works whichever kind has it. Two siblings support that call:$this->fetchSubTemplate()returns the output instead of printing it, and$this->hasSubTemplate()checks whether a sub-template exists.template_button_strip(),template_quickbuttons(),template_menu(),theme_linktree(),template_show_list(),template_control_richedit(),template_control_richedit_buttons()andtemplate_control_verification().Utils::$context,Config,Langand friends, as before. Passing each template its data explicitly is the natural next step, and a separate one: it would change every file by hand and lose the byte-for-byte comparison that makes this PR checkable.Engine::fromTheme(),Engine::exists()andEngine::path()ignore the theme hierarchy, so the engine goes throughTemplate::exists()/path()instead.Backwards compatibility
*.template.phpfiles: unchanged.template_*()functions and load them throughloadTemplate().Themes/default/*.template.phpthrough the package manager break. The files they search no longer exist, and there's no faithful way to redirect a search-and-replace into the new files, which use different syntax. Such mods need updating for 3.0 anyway, but this makes it certain, and it probably deserves a note for mod authors.Verified
--rendered). The only differences are ties in sort order: the stats top tens ([3.0] Ranks ties in the stats top tens in a defined order #9722) and the admin log ([3.0] Lists tied moderation log entries in a defined order #9726).ssi_examples.php, XML responses, and a probe calling the helpers above the way a mod does. All identical.TemplateEngineTestadds 20 unit tests for the engine: inheritance, mixed kinds, which one wins, argument passing, and the legacy helpers.Overlap with other PRs
ThemeCopyTestfiles get merged.admin/profile_track_ipsnapshot from [3.0] Adds template snapshot tests that freeze what each page renders #9712 needs re-recording, because it currently records that page's fatal error.Issues References (Fixes|Related|Closes)
Related: #9712, #9727 (part 1 of 2), #9717.
🤖 Generated with Claude Code