Skip to content

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?”

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.

PathWhat it does
wp-content/plugins/carboncalc/includes/Admin.php:9Registers the whole menu (manage_options on every screen)
wp-content/plugins/carboncalc/includes/AdminUserLookupPage.phpPer-user gate replay + effective permissions (903 lines)
wp-content/plugins/carboncalc/includes/AdminCompanyLookupPage.phpPer-CRN DB row vs live CRM (421 lines)
wp-content/plugins/carboncalc/includes/AdminLoginLogPage.phpRenders carboncalc-login-denied.log
wp-content/plugins/carboncalc/includes/AdminSettingsPage.phpFeature flags, CRN gate, site banner
wp-content/plugins/carboncalc/includes/AdminDebug.phpAd-hoc debug screen (1,897 lines)
wp-content/plugins/carboncalc/includes/DebugSession.php:80Front-end console.group() payload in wp_footer
wp-content/plugins/carboncalc/includes/SiteBanner.php:84The beta banner
wp-content/themes/carboncalculator/functions.php:1651carboncalculator_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.

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 live user_details payload, 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.

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).

Two independent mechanisms:

  1. DebugSession::print_footer_debug() — gated on FeatureSettings::is_debug_mode() and get_template() === 'carboncalculator'. Inlines window.carbonCalcDebugSession plus the contents of assets/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.
  2. carboncalculator_render_lms_debug_panel() in the theme, gated on carboncalculator_lms_debug_enabled() — which reads the CARBONCALC_LMS_DEBUG constant. Renders a visible panel, not console output.

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.

All in wp-content/, JSON one object per line, gitignored via *.log:

FileWritten by
carboncalc-login-denied.logLoginAudit — access denials and “company not linked” dashboard hits
carboncalc-companieshouse.logCompaniesHouse::log() and logExternal() (also used for SIC lookups)
carboncalc-crm.logMoodle CRM insert failures during supplier upload
carboncalc-supplier-upload.logPer-row supplier upload decisions, keyed by debug_id

LoginAudit::read_tail() caps reads at 500 lines; clear_log() unlinks.

Option / constantEffect
carboncalc_feature_debug_modeFront-end console payload
CARBONCALC_LMS_DEBUGThe theme’s visible LMS debug panel
carboncalc_feature_sic_lookup_indicatorSIC provenance in the supplier table
carboncalc_site_banner_enabled / _messageThe banner
CARBONCALC_DEV_BYPASSBypasses the company gate entirely, returns company id 1
CARBONCALC_SKIP_AUTHDisables 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).

  • 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_BYPASS grants access as company id 1. It must never be defined outside local development. It is checked in five places in LMS.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. Passing true from 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.php is 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.
  • New admin screen: add the class, require_once it in carboncalc.php (the file is one long explicit require list, ordered by dependency), and add an add_submenu_page() call in Admin::register_menu().
  • New diagnostics belong in LMS::carbon_company_gate_steps() or RolePermissions::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 through get_carbon_option(), a field in AdminSettingsPage, and — if it affects the front end — a line in DebugSession::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: DebugSession inlines 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 into wp_enqueue_script.

None.

Related: [[carbon-access-gates]], [[company-lookup-integrations]], [[reporting-year-config-and-importer]], [[supply-chain-emissions]].