Tags: ChurchCRM/CRM
Tags
Fix: default DB port to 3306 when blank and log a warning (#8894) ## Summary Some installs have `$dbPort` left blank in `Config.php`. Previously this caused a fatal validation error. This PR makes the loader fall back to the MySQL default (3306) and log a warning so operators can see it happened. ## Changes - Default `$dbPort` to `'3306'` when the value is `null` or empty string before validation runs - Emit a `CONFIG_WARN` log entry when the port is assumed so operators can find and fix it - Remove `$dbPort` from the \"required variables\" check (it now always has a value by the time validation runs) - Consolidate the duplicate `writeErrorLog()` into a single `writeLog(string \$level, string \$message)` helper ## Why Blank DB port is a common configuration oversight on shared hosting and older installs where the port was simply omitted from `Config.php`. A hard failure is worse than a safe default. ## Files Changed - `src/ChurchCRM/Config/ConfigLoader.php` — port defaulting logic + unified log helper ## Testing - PHP syntax validation: ✅ 744 files pass - Biome lint: ✅ no errors - Manually verified: blank `$dbPort` → falls through to `'3306'`, warning written to `churchcrm-YYYY-MM-DD-config-error.log` ## Related Issues Fixes installs silently broken by missing `$dbPort` in `Config.php` --------- Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
Improve GitHub release upload robustness (#8769) ## What Changed Enhanced the GitHub release upload workflow to be more robust by: - Removing the "v" prefix from release tags for consistency - Adding validation to ensure the zip file exists before attempting upload - Improving release detection by querying the GitHub API directly instead of relying on `gh release view` - Using explicit file variable instead of glob pattern to prevent potential issues with multiple matches ## Type - [ ] ✨ Feature - [x] 🐛 Bug fix - [ ] ♻️ Refactor - [x] 🏗️ Build/Infrastructure - [ ] 🔒 Security ## Testing The changes are to the CI/CD workflow and will be validated by the GitHub Actions pipeline on the next release. The improvements add error handling that will catch issues earlier (missing zip files) and use more reliable API queries for release detection. ## Pre-Merge - [x] Tested locally - [x] No new warnings - [x] Build passes - [x] Backward compatible (or migration documented) https://claude.ai/code/session_012dwe1XbZJBsTgR774UaZjd --------- Co-authored-by: Claude <noreply@anthropic.com>
🌍 POEditor Locale Update - 2026-04-25 (#8818) ## 🌍 Automatic Locale Update This PR contains updated translations downloaded from POEditor and missing term reports for all locales. **Details:** - 📅 **Date**: 2026-04-25 - 🏷️ **Version**: 7.3.0 - 🚀 **Trigger**: Manual run **What's included:** - Updated translation files from POEditor - Generated locale JSON files - 📊 Missing terms for all locales (in `locale/terms/missing/`) - Locale audit results **Missing Terms Reports:** For each incomplete locale, batch files under `locale/terms/missing/{locale}/` contain all untranslated terms downloaded directly from POEditor. These can be used with `/locale-translate` or uploaded directly to POEditor. This PR was automatically created by the POEditor workflow. --- **Manual Run Options:** - 🔍 Full download and audit Co-authored-by: DawoudIO <554959+DawoudIO@users.noreply.github.com>
🌍 POEditor Locale Update - 2026-04-23 (#8782) ## 🌍 Automatic Locale Update This PR contains updated translations downloaded from POEditor and missing term reports for all locales. **Details:** - 📅 **Date**: 2026-04-23 - 🏷️ **Version**: 7.2.2 - 🚀 **Trigger**: Manual run **What's included:** - Updated translation files from POEditor - Generated locale JSON files - 📊 Missing terms for all locales (in `locale/terms/missing/`) - Locale audit results **Missing Terms Reports:** For each incomplete locale, batch files under `locale/terms/missing/{locale}/` contain all untranslated terms downloaded directly from POEditor. These can be used with `/locale-translate` or uploaded directly to POEditor. This PR was automatically created by the POEditor workflow. --- **Manual Run Options:** - 🔍 Full download and audit Co-authored-by: DawoudIO <554959+DawoudIO@users.noreply.github.com>
ci: simplify Create Release workflow inputs Drop the run_id input — it was confusing (users don't know where to find a run ID) and the workflow always picking the latest successful master build is the right default. If you need to release an older build, bump the relevant commit back onto master first. Also drop the actions/checkout step: gh CLI works via API with GH_TOKEN and we don't touch any repo files, so the checkout was dead weight. GH_REPO is set from github.repository so gh knows which repo to query.
Fix API SQL injections, null guards, and security bugs (#8601) ## Summary - Fix 7 SQL injection vulnerabilities in `FinancialService.php` by converting to Perpl ORM queries - Add null guards on ORM `findPk()`/`findOne()` results that could crash on invalid IDs - Fix user enumeration in login endpoint (same 401 for all failures) - Fix operator precedence bug, boolean coercion, `(int)` casts, and birthday parse guard - Delete unused `system-locale.php` API route (zero callers) - Add Cypress tests covering the security fixes ## Security Fixes ### Critical: SQL Injection in FinancialService.php (7 locations) All used raw `FunctionsUtils::runQuery()` with string concatenation: - `getMemberByScanString()` — raw SQL → `FamilyQuery::filterByScanCheck()` ORM - `setDeposit()` — raw DELETE → `PledgeQuery` ORM - `getDepositTotal()` — raw SUM → `PledgeQuery` with `withColumn()` - `locateFamilyCheck()` — raw COUNT → `PledgeQuery::count()` - `processCurrencyDenominations()` — `mysqli_real_escape_string` + `(int)` casts (no ORM model) - `getPledgeorPayment()` — raw SELECT → `PledgeQuery` ORM, also removes `extract()` - `getCurrencyTypeOnDeposit()` — `(int)` casts on both parameters (no ORM model) ### High: Null Guards - `FinancialService::deletePayment()` — null check before `delete()` - `FinancialService::getPayments()` — null check on `getPerson()` - `people-groups.php` — null guards on membership and role lookups (3 locations) ### Medium: Bug Fixes - `people-families.php:88` — operator precedence: `!empty(a || !empty(b))` → `!empty(a) || !empty(b)` - `people-groups.php` — `(int)` casts on `roleID`/`groupID`/`userID` from request input - `people-groups.php` — `setActive()`/`setIncludeInEmailExport()` string `'false'` → `filter_var()` boolean - Middleware (`Person`/`Family`/`Group`/`Property`) — `(int)` cast on `findPk()` IDs - `public-register.php` — guard `DateTime::createFromFormat` returning `false` - `public-user.php` — prevent user enumeration (same 401 for "not found" and "wrong password") ### Cleanup - Delete unused `system-locale.php` — zero frontend/backend/test callers - Keep `/api/issues` accessible to all authenticated users (used by Report Issue on every page) ## Test plan - [ ] Verify deposit operations (create, update, close) still work - [ ] Test payment creation with CHECK and CASH methods - [ ] Test scanned check lookup (`/families/byCheckNumber`) - [ ] Verify login returns 401 for invalid users (not 404) - [ ] Test group role update/rename operations - [ ] Test group active/email-export toggle with `'true'`/`'false'` - [ ] Verify "Report an Issue" works for non-admin users - [ ] Run full Cypress E2E suite https://claude.ai/code/session_01XtgSNmxPz4scbhfXtQ8fFf --------- Co-authored-by: Claude <noreply@anthropic.com>
locale: mandate branch + commit + push + upload per locale - Add MANDATORY data loss prevention rules to prevent agent timeouts losing work - Require commit + push after EVERY locale (never accumulate uncommitted translations) - Add per-locale POEditor upload as part of translation workflow (step 3f) - Commit refreshed batch files only if upload succeeded (step 3g) - Skip refreshed file commit if upload fails — continue to next locale gracefully - Create locale-translate-agent-prompt.md: storable, clear script for GitHub agents - Update all skill files (locale-ai-translation.md, locale-cloud-safe-translation.md, locale-workflow-simplified.md, locale-release.md, locale-translate.md) Fixes: Hours of lost translation work from agent timeouts and uncommitted files Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
🌍 POEditor Locale Update - 2026-04-07 (#8545) ## 🌍 Automatic Locale Update This PR contains updated translations downloaded from POEditor and missing term reports for all locales. **Details:** - 📅 **Date**: 2026-04-07 - 🏷️ **Version**: 7.1.2 - 🚀 **Trigger**: Manual run **What's included:** - Updated translation files from POEditor - Generated locale JSON files - 📊 Missing terms for all locales (in `locale/terms/missing/`) - Locale audit results **Missing Terms Reports:** For each incomplete locale, batch files under `locale/terms/missing/{locale}/` contain all untranslated terms downloaded directly from POEditor. These can be used with `/locale-translate` or uploaded directly to POEditor. This PR was automatically created by the POEditor workflow. --- **Manual Run Options:** - 🔍 Full download and audit Co-authored-by: DawoudIO <554959+DawoudIO@users.noreply.github.com>
fix: replace inline onclick with data-* attributes in custom field ed… …itors (#8520) ## Summary - Inline `onclick` attributes on Delete buttons in the three custom field editors (Group, Family, Person) are blocked when CSP is enforced (`script-src` does not include `unsafe-inline`) - Replaces `onclick="confirmDeleteField(...)"` with `data-field-name`, `data-field-id` (and `data-prop-id` for Group) attributes on the button - Adds a delegated `$(document).on('click', '.js-delete-field')` listener inside the **existing** `<script nonce="...">` block — no new script tags - Removes the now-unnecessary `$fieldNameJs`/`$fieldIdJs` PHP intermediary variables ## Why ChurchCRM's `Content-Security-Policy` `script-src` directive does not include `unsafe-inline`. When `bEnforceCSP` is enabled, all inline event handlers are silently blocked. Discovered during review of #8509 (which patched only the HTML-attribute quoting in `GroupPropsFormEditor.php`). This fix makes Delete work under both report-only **and** enforced CSP, and is consistent with the pattern documented in `security-best-practices.md` → _No Inline Event Handlers_. ## Files Changed | File | Change | |------|--------| | `src/GroupPropsFormEditor.php` | `onclick` → `data-field-name/prop-id/field-id` + delegated listener | | `src/FamilyCustomFieldsEditor.php` | `onclick` → `data-field-name/field-id` + delegated listener | | `src/PersonCustomFieldsEditor.php` | `onclick` → `data-field-name/field-id` + delegated listener | | `cypress/e2e/ui/admin/person-custom-fields.spec.js` | Regression tests for Person + Family delete buttons | | `cypress/e2e/ui/groups/standard.group.properties.spec.js` | Regression test for Group props form delete button | ## Testing Cypress regression tests added that verify for all three editors: 1. Delete button has `data-field-name`/`data-field-id` attributes and **no** `onclick` 2. Clicking the button opens the bootbox confirm dialog (event delegation fires) 3. Cancel leaves the field intact 4. Confirm deletes the field and shows a success notification Manual steps: 1. Open Admin → Custom Fields (Person, Family, Group) 2. Click the ⋮ menu on any field → **Delete** — confirm dialog should appear and delete should succeed 3. Enable `bEnforceCSP` in System Settings → repeat above — Delete must still work (no CSP console errors) ## Related Issues - Fixes #8508 - Supersedes #8509 (the `htmlspecialchars(json_encode(...))` workaround is no longer needed since the `onclick` attribute is removed entirely) 🤖 Generated with [Claude Code](https://claude.com/claude-code) --------- Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
PreviousNext