Search page
Status: 85% — form and result rows done, pagination remaining.
What it is
The core search page, search.php. It renders two distinct things through two separate tablerender() calls:
the search form — caption
PAGE_NAME SITENAME, modesearch_headone result block per search area — mode
search_result, called once for every plugin that answered the query
With All Areas selected the visitor therefore sees several stacked cards, one per area, each with its own caption.
Where the template comes from
search.php loads the form template on construction:
$this->template = $this->formTemplate(e107::getCoreTemplate('search'));coreTemplatePath() resolves the file in this order:
THEME/templates/search_template.php— the v2 theme overrideTHEME/search_template.php— v1 legacy overridee107_core/templates/legacy/search_template.php— only ifTHEME_LEGACYe107_core/templates/search_template.php— the default
There is no Bootstrap 5 core template for search. Unlike fpw, signup or user, the bootstrap5/ folder contains no search_template.php, so even a theme that declares BOOTSTRAP 5 falls through to the base file — which is written for Bootstrap 3: form-horizontal, form-group, control-label, input-group-btn, pull-right, <span class="caret">, and col-sm-* columns with no .row parent. Every BS5 theme has to supply its own override. Reported upstream in the search findings discussion.
The form key is replaced, not merged
getCoreTemplate() merges the core template with the theme override, but the merge is array_merge() on top-level keys only. All seven form variants live under the single form key:
Defining even one sub-key in the theme override replaces the whole form array. The override must define all seven, otherwise the missing parts of the form silently disappear. This is the same "an override replaces the file entirely" rule as on the auth pages, one level deeper.
Shortcodes available in the form
The shortcode object is search.php's own class, passed directly to parseTemplate():
{SEARCH_FORM_URL}
form action
{SEARCH_MAIN}
complete input group — input, submit, advanced dropdown
{SEARCH_MAIN_SEARCHFIELD}
the bare <input> only
{SEARCH_MAIN_SUBMIT}
submit button plus the hidden r field
{SEARCH_ENHANCED}
the four enhanced fields, one per form.enhanced
{ENHANCED_DISPLAY}
style="display:none" unless enhanced is active
{SEARCH_MAIN_CHECKBOXES} / {SEARCH_DROPDOWN}
area selector, depending on the selector pref
{SEARCH_ADVANCED}
per-area advanced block
{SEARCH_MESSAGE}
stopword notice
form.enhanced and form.advanced are not parsed for shortcodes — they go through simpleParse(), so only the {ENHANCED_*} and {SEARCH_ADV_*} placeholders work there.
The search field: why not {SEARCH_MAIN}
{SEARCH_MAIN} emits its own input-group, its own btn-primary buttons and a Font Awesome fa-search glyph, none of which can be parameterized. Aragorn builds the input group in the template instead and uses only the bare field:
{SEARCH_MAIN} and {SEARCH_MAIN_SUBMIT} both append <input type="hidden" name="r" value="0">; {SEARCH_MAIN_SEARCHFIELD} does not. Leave it out and pagination breaks — r is the result offset.
The dropdown toggle needs no <span class="caret">; Bootstrap 5 draws the arrow with .dropdown-toggle::after. Only data-bs-toggle is used, since Aragorn ships tabler.min.js (Bootstrap 5.3.7) and has no BS4 alias layer.
Horizontal rows for the secondary fields
The core template intended col-sm-3 / col-sm-9 rows but never wrapped them in .row, so the columns never formed one. Aragorn restores the intent and swaps the Bootstrap 3 control-label for col-form-label, which matches the label's vertical padding to the input height:
col-sm-4 rather than col-sm-3 — at 25% the label Word(s) beginning with wraps to two lines inside the main column of the 5-14-5 grid. Below sm the row collapses and the fields stack, which is the wanted mobile behaviour.
The main search field stays full width: it is the primary control and should dominate; the horizontal rows visually mark the rest as secondary.
Solved along the way: .form-control[size] broke the input group
The buttons kept wrapping onto a second line below the input. The cause was a rule in the theme's own style.css:
Specificity is 0,2,0 — one class plus one attribute — exactly the same as Tabler's .input-group > .form-control { flex: 1 1 auto; width: 1%; min-width: 0 }. On a tie the later stylesheet wins, and style.css loads last. The input then claimed the full container width and flex-wrap: wrap pushed the buttons down.
The rule was also redundant: Tabler's base .form-control already sets width: 100%, and an HTML size attribute is only a presentational hint that any CSS width overrides. Deleting it fixed the form.
Rendering: the card_header style
Both search modes map to one reusable style in theme.php:
card_header renders a Tabler card with a .card-header, unlike the default case which prints a bare .card-body. It is also reachable from any layout via {SETSTYLE=card_header}.
The caption is assembled by core:
giving Results 1 - 10 of 42 in News. With no matches $res_display is empty, so it reads Results in News — awkward, and with a double space in the source, but it still identifies the area, which is the point.
Result rows are CSS-only
The form is fully templatable. The results are not — each row is built as a fixed string in search_class.php, wrapped in a list by search.php:
All three are direct children of the <li>, which lets the theme target them precisely without touching anything inside the summary:
Two notes on the details:
Tabler sizes
h4at0.875remfor dashboard use. A result title needs to read as the primary element of its row, so the theme raises it to1rem.The date span is emitted inline and has to be promoted to
display: block.<mark>needs nothing — Tabler styles it through--tblr-highlight-bg/--tblr-highlight-colorand ships separate dark-mode values.
Still open
Pagination. search.php wraps {NEXTPREV} in <div class="nextprev search form-inline"> — form-inline is a Bootstrap 3 / 4 class that no longer exists — and requests the default template prefix with a hardcoded value. Restyling it means overriding the default_* keys in THEME/templates/nextprev_template.php, which changes pagination site-wide, not just on search. Decision pending.
The navbar search box. $SEARCH_TEMPLATE['shortcode'] drives the {SEARCH} shortcode used in the header. It is a separate batch (e107_core/shortcodes/batch/search_shortcodes.php) whose {SEARCH_INPUT} and {SEARCH_BUTTON} do accept a class parm, so Tabler markup is straightforward there. Not yet written.
Security notes
Never interpolate
$_GET['q']into the template. The field value must come from{SEARCH_MAIN_SEARCHFIELD}(or{SEARCH_MAIN}), which runs it through$tp->post_toForm(). The same applies to the enhanced fields —sc_search_enhanced()escapes each value itself.$modereaches aclassattribute. Intablestyle()the mode is used as a CSS class on the card. It comes from thetablerender()caller, which is plugin code rather than user input, but nothing guarantees a plugin did not build it from a request, so it is sanitised before use:The caption needs no escaping. It is built from language constants and from
search_info[$key]['qtype'], which each plugin declares in itse_search.php. The one path that touches$_GETuses the request value as an array key into a plugin-defined list, so no request data reaches the output. Escaping it withtoHTML()would also break plugins that put legitimate markup inqtype.
Upstream findings
Working through this page surfaced eight issues in core search, from the missing Bootstrap 5 template to path constants leaking into result summaries and a mislabelled <label for> in the advanced form. They were written up as a single report for the e107 maintainers rather than filed as individual issues.
Last updated