Skip to main content

The downloads page & product materials

Audience: developers & AI agents · Scope: the public downloads matrix and the per-product materials behind it · Last reviewed: 2026-07-20

TL;DR — A public matrix of products × material types. Some materials are uploaded files, some are generated PDFs, and a couple are pulled from other admin lists. Two things to know before relying on it: the per-material "login required" flag protects nothing, and a product can only ever expose one material per type.

The matrix

Rows are products, columns are material types, plus an "All" column that zips a row together.

  • Products shown are published and not discontinued.
  • Customer-group gating applies to the product list: a visitor sees only products assigned to their customer group — unless they are a quote maker, who sees everything.
  • Two types are removed from the columns entirely: generic Regular files and the Short Specsheet. They can exist as materials but never get a column here.
  • Five columns are visible by default (full spec sheet, diagrams, brochures, images, installation guides); the rest are hidden and toggled by filter chips.

Material types

TypeColumn labelWhere the bytes come from
specsheet_full / specsheet_shortFull / Short Specsheetthe cached spec-sheet PDF — see the hub
diagramsDimensional Diagramsgenerated PDF, re-rendered every download
candela_curvesIES/LDTgenerated PDF of curve images — see the warning below
performance_dataPerformance Datagenerated PDF
filesRegular filesuploaded per material
bim_filesBIM filesuploaded
install_instructionsInstallation Guidesuploaded
global_fileGlobal Fileuploaded once globally and reused across products
brochuresBrochuresthe published, download-type brochures (admin-guide/website-content/share-files-and-brochures.md) list
imagesProduct Imagesthe product gallery, at 1920px

"IES/LDT" is a misnomer. That material delivers a PDF of candela-curve images, not .ies/.ldt photometric files. Anyone downloading it expecting photometric data gets pictures.

"Declare" is not a type. A Declare label is just a files or global_file material whose title says so. The same is true of anything else not in the table above.

Delivery

  • One material → streamed straight from the controller. PDFs go inline, everything else attachment.
  • "All" → the JS collects the visible material ids in that row and asks the server to prepare them, so what you get depends on which columns are currently toggled on. Multiple files are zipped.
  • PDFs take a different path: the "prepare" step copies the PDF into the public web root under docroot/cache/configbox/temp_downloads/<random>/<filename> and returns a plain URL.

⚠️ That temp directory is never cleaned. The garbage-collection CLI does not touch it and nothing else does either, so it grows without bound — and its contents are world-readable at a guessable-ish URL.

Gating — weaker than it looks

There are two mechanisms and neither does much:

1. login_required per material — decorative. It is enforced only in JavaScript, and that handler is broken: it calls e.preventDefault() while taking no event argument, so the ReferenceError aborts the handler and the plain <a href> navigates anyway. Even if the JS worked, the controller never checks the flag at all — the download URL is a guessable task=getMaterial&materialId=N. Treat login_required as a UI hint with no security value.

Note the flag deliberately does not apply to spec sheets — a comment in the model says spec sheets cannot be login-protected.

2. Customer-group gating — partial. It applies to the product list on this page, and inside the "prepare" step for brochures only. Every other material type ignores customer groups entirely.

A whole-page "agents only" gate exists in the view but is commented out, leaving its template orphaned.

If any of this material is genuinely meant to be restricted, it currently isn't. Recorded in docs/_known-issues.md.

Data model

TableHolds
#__configbox_external_product_materialsone row per product × material type: the type, uploaded files, ordering, published, login_required
#__configbox_external_global_product_materialsfiles uploaded once and reused across products

Uploaded files live under the public store at data/store/public/custom_media/product_material_files/{materialId}/ (and …/globalmaterials for global ones) — public, unlike the private store used for product HTML embeds.

One material per type, per product. The lookup is keyed [productId][materialType], so a second published material of the same type is silently dropped — which one survives depends on query order.

Admin settings

Per material (product form → Materials): the type, the files, an optional global material, ordering, published, and login required (not offered for spec sheets).

No global settings.

Deployment runbook (manual steps)

No manual steps — the page ships with the code. To make a product's downloads appear:

  1. Add materials to the product (product form → Materials), one per type.
  2. For generated types (spec sheet, diagrams, candela curves, performance data) there is nothing to upload — just add the material row of that type.
  3. Confirm the product is published, not discontinued, and assigned to the right customer groups.
  4. Smoke test as a signed-out visitor and as an agent — the product list differs by customer group.

Gotchas & caveats

  • A bot filter checks for &amp; in the request URI on the single-material path and returns 403. It is not applied to the "prepare" path.
  • The "All" download is only as complete as the visible columns — a user who toggled columns off gets a smaller zip with no warning.
  • Generated PDFs are rebuilt on demand, so an "All" download of a large product renders several PDFs in one request.
  • Two of the three generated PDFs have broken caching — see the hub.