Carbon Calculator admin and debug tools
The Carbon Calculator ships a large wp-admin surface — 15 submenu screens — plus front-end debug output, a configurable site banner, and three log files. Most of it exists to answer one recurring support question: “why can this user not get in, or why is their number wrong?”
Why it exists
Section titled “Why it exists”The calculator’s data lives partly in Moodle, partly in the CRM, partly in
cc_* tables on one specific subsite, and access depends on a chain of five
gates and two feature flags. Reproducing a user’s state by hand is slow and
error-prone, so the diagnostics are built in: the lookup screens replay the
gates read-only and show the live Moodle payload next to the stored row.
Entry points
Section titled “Entry points”| Path | What it does |
|---|---|
wp-content/plugins/carboncalc/includes/Admin.php:9 | Registers the whole menu (manage_options on every screen) |
wp-content/plugins/carboncalc/includes/AdminUserLookupPage.php | Per-user gate replay + effective permissions (903 lines) |
wp-content/plugins/carboncalc/includes/AdminCompanyLookupPage.php | Per-CRN DB row vs live CRM (421 lines) |
wp-content/plugins/carboncalc/includes/AdminLoginLogPage.php | Renders carboncalc-login-denied.log |
wp-content/plugins/carboncalc/includes/AdminSettingsPage.php | Feature flags, CRN gate, site banner |
wp-content/plugins/carboncalc/includes/AdminDebug.php | Ad-hoc debug screen (1,897 lines) |
wp-content/plugins/carboncalc/includes/DebugSession.php:80 | Front-end console.group() payload in wp_footer |
wp-content/plugins/carboncalc/includes/SiteBanner.php:84 | The beta banner |
wp-content/themes/carboncalculator/functions.php:1651 | carboncalculator_render_lms_debug_panel() |
Menu, in registration order (Admin.php): Reporting Years, Companies,
Company Lookup, User Lookup, Metrics, Conversion Factors, Steps, Fields,
Categories, Importer, Import CSV, SIC Codes, Database Migrations, Carbon
access log, Settings.
How it works
Section titled “How it works”User Lookup
Section titled “User Lookup”The most useful screen. Given a user, it renders:
LMS::carbon_company_gate_steps($lms_user_id, false)(plugins/carboncalc/includes/LMS.php:652) — one pass/fail row per gate with the reason, with writes disabled so looking does not heal.LMS::carbon_full_login_gate_summary()(:614) — the combined verdict, the synced roles, both allowlists, and which feature flags are on.LMS::moodle_payload_gate_hints()(:738) — a read-only comparison against the liveuser_detailspayload, including whether Moodle’s roles would match the allowlists and an explicit warning when the roles array is empty (“sync would delete all cc_user_roles”).RolePermissions::render_user_effective_permissions_table()(:682) — the nine permission rows with a “why” for each.
Company Lookup
Section titled “Company Lookup”Given a CRN: the stored cc_company row, the live CRM payload, the CC
Partner snapshot (RolePermissions::cc_partner_db_snapshot()), and the S3
partner snapshot including which source answered
(profile_json, crm_live, crm_live_cached).
Front-end debug
Section titled “Front-end debug”Two independent mechanisms:
DebugSession::print_footer_debug()— gated onFeatureSettings::is_debug_mode()andget_template() === 'carboncalculator'. Inlineswindow.carbonCalcDebugSessionplus the contents ofassets/js/carboncalc-debug.js(with a heredoc fallback if that file is unreadable,DebugSession.php:117). Logs the user’s email, LMS id, company id, synced roles, all four role allowlists, and every computed permission to the browser console.carboncalculator_render_lms_debug_panel()in the theme, gated oncarboncalculator_lms_debug_enabled()— which reads theCARBONCALC_LMS_DEBUGconstant. Renders a visible panel, not console output.
Site banner
Section titled “Site banner”SiteBanner (SiteBanner.php) renders a dismissible-looking (but static)
notice on Carbon pages via partials/site-banner.php. Message is stored HTML,
sanitised with wp_kses_post() on both save and output. If no message is
stored it falls back to a hard-coded default announcing the beta and
pointing at the legacy tool at carbon.sustainabilitytool.com
(SiteBanner.php:21), including a “full version due end of July 2026” date.
Log files
Section titled “Log files”All in wp-content/, JSON one object per line, gitignored via *.log:
| File | Written by |
|---|---|
carboncalc-login-denied.log | LoginAudit — access denials and “company not linked” dashboard hits |
carboncalc-companieshouse.log | CompaniesHouse::log() and logExternal() (also used for SIC lookups) |
carboncalc-crm.log | Moodle CRM insert failures during supplier upload |
carboncalc-supplier-upload.log | Per-row supplier upload decisions, keyed by debug_id |
LoginAudit::read_tail() caps reads at 500 lines; clear_log() unlinks.
Configuration
Section titled “Configuration”| Option / constant | Effect |
|---|---|
carboncalc_feature_debug_mode | Front-end console payload |
CARBONCALC_LMS_DEBUG | The theme’s visible LMS debug panel |
carboncalc_feature_sic_lookup_indicator | SIC provenance in the supplier table |
carboncalc_site_banner_enabled / _message | The banner |
CARBONCALC_DEV_BYPASS | Bypasses the company gate entirely, returns company id 1 |
CARBONCALC_SKIP_AUTH | Disables theme-side auth checks |
FeatureSettings reads flags from the Carbon subsite’s options table,
switching blogs to do so, with a fallback to the current blog for legacy
saves (FeatureSettings.php:35-60, :166).
Invariants and gotchas
Section titled “Invariants and gotchas”- Debug mode leaks the logged-in user’s email, role set and permissions into the browser console on every Carbon page. It is a settings toggle, not a constant, so it can be left on in production by accident. Check it before investigating “why is this data visible”.
CARBONCALC_DEV_BYPASSgrants access as company id 1. It must never be defined outside local development. It is checked in five places inLMS.php(:68,:132,:453,:492) and bypasses the gate wholesale.- Lookup screens must pass
$allow_writes = false. The gate function syncs roles and inserts stub company rows as a side effect. Passingtruefrom a diagnostic screen changes the state you are trying to observe. - Every admin screen is
manage_options. There is no read-only diagnostic capability, so support work needs a full network/site admin. - Debug output is gated on
get_template() === 'carboncalculator', so the Carbon theme must be the active template — on a subsite running the main theme, nothing appears even with the flag on. - The banner’s default message contains dated content (a July 2026 go-live and a link to the legacy tool). It renders whenever the toggle is on and no message has been saved, so it will keep announcing that date until someone either saves a message or turns the banner off.
AdminDebug.phpis 1,897 lines of ad-hoc tooling with no menu-level separation of concerns. Treat it as a scratchpad, not an API.- Log files grow unbounded. Nothing rotates them. The supplier upload log in particular writes several lines per CSV row.
Changing it safely
Section titled “Changing it safely”- New admin screen: add the class,
require_onceit incarboncalc.php(the file is one long explicit require list, ordered by dependency), and add anadd_submenu_page()call inAdmin::register_menu(). - New diagnostics belong in
LMS::carbon_company_gate_steps()orRolePermissions::get_effective_permissions_for_user()so all the screens pick them up, rather than in a single page’s render method. - New feature flag: add the constant to
FeatureSettings, an accessor that goes throughget_carbon_option(), a field inAdminSettingsPage, and — if it affects the front end — a line inDebugSession::build_frontend_payload(). - Never log a credential or a full Moodle payload containing one. The existing loggers log CRNs, emails and HTTP status codes; keep to that.
- Verify by hand: enable debug mode, load
/my-company-emissions/, check the console group appears; then run a User Lookup for the same account and confirm the two agree. Disable debug mode afterwards. - Deliberately not abstracted:
DebugSessioninlines the JS file’s contents rather than enqueueing it, so the payload and its consumer land in one<script>and cannot race. Do not “fix” this intowp_enqueue_script.
None.
Related: [[carbon-access-gates]], [[company-lookup-integrations]], [[reporting-year-config-and-importer]], [[supply-chain-emissions]].