Select your language

How It Works (Technical Overview)

Audience: developers, integrators and reviewers who want to understand the data flow and security model.

Overview

mod_bearsampp_stats is a Joomla site module that reads a Markdown dashboard from a GitHub repository, renders it in the browser and injects it into the page. The server side is deliberately thin: it computes URLs and passes a small JSON options object to JavaScript. All fetching, Markdown parsing and DOM manipulation happen client side.

Files

FileRole
mod_bearsampp_stats.xmlManifest: metadata, dependencies (PHP 8.1, Joomla 5.0), parameters, update server.
mod_bearsampp_stats.phpEntry point: loads the helper, reads params, loads the layout.
helper.phpBuilds the list of modules and the raw URLs.
tmpl/default.phpRenders the grid or the single-article container and registers web assets.
media/js/mod_bearsampp_stats.jsFetches, renders, sanitizes, rewrites URLs and manages cache.
media/css/style.cssCard grid and modal styling.
language/en-GB/*.iniTranslatable strings.
updates.xmlJoomla Update System feed.

Server side

ModBearsamppStatsHelper::getModules() (helper.php) does the following for each entry in the Modules list:

  1. Splits the list on commas, newlines, semicolons and whitespace, trims and de-duplicates.
  2. Treats each entry as the literal repository name (no auto-prefixing).
  3. Derives a display name by stripping module-, replacing separators with spaces and title-casing (for example mod_bearsampp_stats → Mod Bearsampp Stats), with a small fallback map that keeps common names nicely cased.
  4. Builds the raw URL:
    https://raw.githubusercontent.com/<owner>/<repo>/<branch>/<stats-folder>/dashboard.md

The getOwner() helper normalises the Repository owner value so that Bearsampp, github.com/Bearsampp and https://github.com/Bearsampp all resolve to Bearsampp. The stats folder is sanitised to a single path segment (letters, digits, ., _, -).

tmpl/default.php registers the stylesheet, Marked.js and DOMPurify through the Joomla Web Asset Manager, then emits either:

  • a single .bearsampp-stats-inline container (single-article layout, exactly one repository), or
  • a .bearsampp-stats-grid of .bearsampp-stats-card buttons plus one Bootstrap 5 modal.

Each element carries data-rawurl, data-module, data-slug and data-display. Runtime options (branch, TTL, grid columns and translated strings) are passed via Joomla.getOptions('mod_bearsampp_stats').

Client side

media/js/mod_bearsampp_stats.js runs on DOMContentLoaded:

  1. Reads options and, if a modal exists, initialises bootstrap.Modal.
  2. On card click (or immediately for the inline case) calls fetchAndRender().

fetchAndRender():

  1. Derives baseUrl from the raw URL and builds a cache key bearsampp-stats:v3:<module>:<slug>:<branch>.
  2. Returns cached HTML from localStorage if present and not expired.
  3. Otherwise fetches the raw URL with cache: 'no-cache' and checks response.ok.
  4. Normalises the heading: the first Markdown # heading is replaced with # <slug> Statistics, and the link text in the "Release asset download totals for [...]" line is retargeted at the actual slug.
  5. Verifies Marked.js and DOMPurify are available, then renders and sanitizes:
    const html = window.DOMPurify.sanitize(window.marked.parse(markdown));
  6. Stores the sanitized HTML in cache and calls renderContent().

renderContent():

  1. Sets innerHTML on the target content element.
  2. For every img[src], a[href] and source[src] with a relative value, resolves it against baseUrl with new URL(value, baseUrl). Absolute URLs (Shields.io badges, full raw.githubusercontent.com URLs, # anchors and protocol-relative URLs) are left untouched.
  3. Forces external links (a[href^="http"]) to open in a new tab and adds rel="noopener noreferrer".

The dashboard.md file

The module displays whatever Markdown lives at <stats-folder>/dashboard.md. The module never creates that file, so a repository must already publish it.

The recommended way to produce it is Bearsampp's stats-daily-with-chart workflow. That workflow runs the GitHub Downloads Action, which writes downloads.json and the generated SVG charts into the stats folder, and then runs an additional render step - not part of the action - to produce or update dashboard.md. The auto-creation behaviour below is provided by that render step, so it only happens in repositories that use Bearsampp's workflow (or another workflow that adds the same step). A repository that runs the action alone, or uses any other stats tool, must supply its own dashboard.md.

Dashboard auto-creation (Bearsampp workflow only). The render step:

  1. Reads stats/downloads.json.
  2. If stats/dashboard.md is missing, generates a default one from the snapshot - a title, a one-line description, four Shields.io badges (total / day / week / month), the total-trend chart, all generated charts and the data table.
  3. If it exists, refreshes only the ## Data table so the numbers stay in sync with the JSON, leaving the rest of the file untouched.

So a repository that uses Bearsampp's workflow gets a working dashboard automatically on the first run, and you can freely customise dashboard.md afterwards - only the data table is regenerated.

Caching

The browser cache is a localStorage entry per module + slug + branch holding the sanitized HTML and an expiry timestamp. cache_ttl_minutes controls the TTL; 0 disables client caching so every view re-fetches. The server sends cache: 'no-cache' on the fetch so the browser revalidates with GitHub rather than serving a stale HTTP response.

Security model

  • Sanitization: all fetched Markdown is parsed to HTML and then passed through DOMPurify before insertion, so scripts and unsafe markup in a repository's dashboard.md are stripped.
  • CDN integrity: Marked.js and DOMPurify are loaded from jsDelivr via the Web Asset Manager with declared dependencies, so they load before the module script runs.
  • No server-side writes: the module does not write files, query the database beyond normal module parameter storage, or collect visitor data.
  • Link hardening: outbound links open with noopener noreferrer.
  • URL construction: owner, repo and folder components are URL-encoded; the folder is restricted to a single safe path segment.

Failure handling

If the fetch fails, the HTTP status is not OK, or the renderer/sanitizer is unavailable, the module hides the loader, shows a translated "Failed to load stats" alert and (in modal mode) closes the modal. When a repository has no dashboard.md (for example, the stats workflow has not run yet), the browser request returns a 404 and the same graceful error path is used; the translated fallback string is "Stats coming soon for this module."

Distribution and updates

Releases are produced by the repository's packaging workflow. updates.xml advertises the latest version to the Joomla Update System, with a download URL pointing at the release ZIP on GitHub.

Our Supporters

Sorry, this website uses features that your browser doesn't support. Upgrade to a newer version of Firefox, Chrome, Safari, or Edge and you'll be all set.