-
Notifications
You must be signed in to change notification settings - Fork 9
Standards and Conventions
This document outlines the coding standards, conventions, and established patterns used in the Proclaim project.
- PSR-12 coding standard with Joomla conventions
- PHP 8.3+ features encouraged (typed properties, union types, match expressions, named arguments)
- Configuration: PHP CS Fixer (
.php-cs-fixer.dist.php) - Run
composer lint:fixbefore committing
Key PSR-12 rules:
- Type casts with space:
(int) $var,(string) $value - 4-space indentation (no tabs)
- Line length should not exceed 120 characters
- Opening brace on same line for control structures, new line for classes/methods
-
ES6+ syntax throughout (
const/let, arrow functions, template literals, destructuring) -
ESLint 10 with flat config (
build/eslint.config.mjs) —js.configs.recommendedbase - Source files in
build/media_source/js/with.es6.jsextension - Run
npm run lint:jsbefore committing -
Jest for unit tests (
tests/js/*.test.js)
- Use the
Cwmprefix for component classes (e.g.,CwmparamsModel,CwmteacherTable) - Entity names follow Joomla singular/plural convention:
- Singular controller (FormController):
CwmserieController,CwmteacherController - Plural controller (AdminController):
CwmseriesController,CwmteachersController - Table:
CwmserieTable,CwmteacherTable - Model (edit):
CwmserieModel/ Model (list):CwmseriesModel
- Singular controller (FormController):
- Administrator:
CWM\Component\Proclaim\Administrator - Site (Frontend):
CWM\Component\Proclaim\Site
Source files live in build/media_source/, the media/ output directory is gitignored, and npm run build generates it.
npm run build # Build all: JS + CSS + assets (images, vendor libs)
npm run build:js # Rollup + Terser JS compilation only
npm run build:css # CSS minification only
npm run build:assets # Copy images and vendor librariesAfter git clone: npm install && npm run build generates the full media/ tree before running composer symlink.
| Deprecated | Replacement |
|---|---|
jimport() |
Composer autoloader / use statements |
getErrorMsg(), getError(), setError()
|
Throw exceptions |
new Input() for request data |
$this->input in controllers, Factory::getApplication()->getInput() elsewhere |
getSession()->get('user') |
$app->getIdentity() or Factory::getApplication()->getIdentity()
|
$this->_db / getDbo()
|
$this->getDatabase() |
addScript(), addStyleSheet(), addScriptDeclaration()
|
Web Asset Manager (WAM) |
HTMLHelper::_('behavior.framework'), formbehavior.chosen, etc. |
Native Bootstrap 5 / Joomla core assets |
Table::getInstance() |
MVCFactory $this->getTable()
|
BaseDatabaseModel::getInstance() |
MVCFactory injection |
-
BaseControllerin Joomla 5 does NOT havegetInput()orgetApplication()methods. Use$this->inputand$this->appdirectly. - Typed event classes (
ContentPrepareEvent, etc.) with->getResult()are NOT available in Joomla 5. Use$app->triggerEvent('onEventName', [...]). -
new Input([...])with array data is OK (typed data bag for method signatures). - Code must run natively on Joomla 6 WITHOUT the backward compatibility plugin.
All JS/CSS loading MUST use the Web Asset Manager via joomla.asset.json.
Asset URI rules:
- Do NOT include
css/orjs/subdirectory in URIs. Joomla auto-resolvescom_proclaim/foo.css->media/com_proclaim/css/foo.css - Vendor assets (e.g.,
media/fancybox/) can't use auto-resolution -- use full literal path withregisterAndUseScript()
Inline patterns:
-
addInlineStyle()for dynamic CSS vars -
addInlineScript()with PHP heredoc for scripts needing PHP data
Use CwmlangHelper::registerAllForJs() in any admin template that needs JS language strings. This parses the component .ini file and calls Text::script() for every key automatically.
use CWM\Component\Proclaim\Administrator\Helper\CwmlangHelper;
CwmlangHelper::registerAllForJs();JS access: Joomla.Text._('JBS_ADM_KEY')
Quirk: Joomla.Text._() returns the raw key string (truthy) when unregistered -- || 'fallback' never fires. Compare result against the key to detect missing registrations.
| Directory | Purpose |
|---|---|
Controller/ |
Request handlers |
Model/ |
Business logic and data access |
View/ |
View classes (HtmlView.php) |
Helper/ |
Utility classes |
Table/ |
Database table classes (admin only) |
Field/ |
Custom form fields (admin only) |
tmpl/ |
PHP template files |
-
AdminController (plural, e.g.,
CwmseriesController): List management -- does NOT have abatch()method -
FormController (singular, e.g.,
CwmserieController): Edit/save -- HASbatch()method
Batch data-submit-task attributes must use the singular controller name with cwm prefix: cwmserie.batch, NOT serie.batch or cwmseries.batch.
The generic cwmadmin-batch-footer.es6.js handles all entity types. It reads the data-submit-task attribute from the button and validates it ends with .batch.
To add batch support for a new entity:
- Add
$batch_commandsproperty to the Model (maps form field names to methods) - Add batch method (e.g.,
batchLocation()) to the Model - Add batch body template with form fields (e.g.,
Cwmhtml::location()) - Use
useScript('com_proclaim.cwmadmin-batch-footer')in the footer template - Set
data-submit-task="cwm{entity}.batch"on the submit button
-
check()method: Use\UnexpectedValueExceptionwithText::_()keys for validation errors - Normalize sentinel values (e.g.,
-1from "Select" dropdowns) toNULLincheck() - Always declare class properties (dynamic properties deprecated in PHP 8.2+)
- Template files in
tmpl/andlayouts/are excluded from linting - Keep business logic out of templates
- Use
$this->escape()for all output - Dark mode: Use Bootstrap 5.3 color-adaptive classes. NEVER use
bg-light(stays white in dark mode). Usebg-body-secondaryorbg-body-tertiaryinstead.
- Always use
$db->quoteName()for identifiers - Use
$query->whereIn(), never rawimplode(',', ...) IN() - Use prepared statements via Joomla's query builder
All 11 admin list models MUST have BOTH:
- Filter dropdown -- UX feature letting users narrow by specific access level
- View-level access check -- security enforcing non-super-admin users only see records in their authorized view levels
$user = $this->getCurrentUser();
// Filter by access level (dropdown)
if ($access = $this->getState('filter.access')) {
$query->where($db->qn('alias.access') . ' = ' . (int) $access);
}
// Restrict non-admin users to their authorised view levels
if (!$user->authorise('core.admin')) {
$query->whereIn($db->qn('alias.access'), $user->getAuthorisedViewLevels());
}Rules:
- Use
$this->getCurrentUser()(fromCurrentUserTrait) in list models - Super admins (
core.admin) bypass view-level filtering - Do NOT remove the view-level check -- it's essential for multi-tenant deployments
Use CwmlocationHelper::getUserLocations() for consistent campus resolution across models, views, and batch operations. The helper resolves accessible location IDs via Joomla view levels.
- Validate and sanitize all user input
- Use
Session::checkToken()(CSRF) on all form submissions - Use
$user->authorise()checks in controllers before operations - Never trust client-side validation alone
- URL form fields use
type="url"+validate="url" - Numeric fields use
filter="int"
- Tests in
tests/unit/mirroring source structure - Base class:
CWM\Component\Proclaim\Tests\ProclaimTestCase - Stubs:
tests/unit/Stubs/JoomlaCmsStubs.php(40+ Joomla CMS class/interface/trait stubs) - Run:
composer test(all),composer test:unit,composer test:integration
- Tests in
tests/js/*.test.js - Run:
npm test,npm run test:watch,npm run test:coverage
- Config:
playwright.config.jsat project root - 4 projects: admin-j5, site-j5, admin-j6, site-j6
- Run:
npx playwright test
Run these before submitting code:
# PHP syntax check
composer lint:syntax
# PHP CS Fixer - check style (dry-run)
composer lint
# PHP CS Fixer - auto-fix style
composer lint:fix
# Run lint + PHP tests
composer check
# Run all checks + all tests (PHP + JS)
composer check:all
# JS linting
npm run lint:js
# JS tests
npm testThe @ operator suppresses error messages from function calls. While generally discouraged, there are cases where it's necessary when the error originates outside the component's logic.
If using @, you MUST:
- Thoroughly explain the need in a comment
- Document why the error cannot be handled through normal means
All project documentation should be maintained in the Proclaim.wiki repository, not in the main Proclaim repository.
| Location | Content |
|---|---|
| Proclaim.wiki | All detailed documentation, guides, release notes, tasks, plans |
| Main repo README.md | Brief overview and quick-start only, link to wiki for details |
| Main repo CLAUDE.md | AI assistant guidance for development |
-
Release Notes: Create a new
Whats-New-X.X.mdfile for each minor version - Tasks: Update Tasks when features are completed
- Keep README minimal: The main repository README should contain only essential quick-start information
- Link don't duplicate: Reference wiki pages rather than duplicating content
The wiki is a separate Git repository:
- Location:
Proclaim.wiki/(sibling to main Proclaim repo) - GitHub: https://github.com/Joomla-Bible-Study/Proclaim/wiki
- Clone:
git clone https://github.com/Joomla-Bible-Study/Proclaim.wiki.git
Check build/versions.json for correct version numbers:
-
next.minor-- Use for@sincetags and migrations -
next.major-- Use for major breaking changes -
current.version-- Last stable release
- Multi-Campus-Admin-Guide
- Multi-Campus-User-Guide
- Template-Customization-Guide
- Print-Friendly-View
- Content-Security-Policy
- Troubleshooting-FAQ
- Setting-up-your-development-environment
- Standards and Conventions
- Contributing-Workflow
- Database-Schema
- Location-API-Reference
- Backward-Compatibility-Breaks-10.1
- Proclaim-Code-Road-map
- Tasks
- Overview
- Admin Center
- Messages
- Media Files
- Servers
- Teachers
- Series
- Podcasts
- Topics
- Locations
- Comments
- Message Types
- Templates
- Utilities